mirror of
https://github.com/RfidResearchGroup/proxmark3.git
synced 2025-08-19 21:03:48 -07:00
compiled. not linking....
This commit is contained in:
parent
4fed815b88
commit
f03261be9a
8 changed files with 546 additions and 3 deletions
|
@ -23,10 +23,10 @@ ENV_CFLAGS := $(CFLAGS)
|
|||
VPATH = ../common ../zlib ../uart
|
||||
OBJDIR = obj
|
||||
|
||||
LDLIBS = -L/opt/local/lib -L/usr/local/lib -lreadline -lpthread -lm -ljansson
|
||||
LDLIBS = -L/opt/local/lib -L/usr/local/lib -lreadline -lpthread -lm -L/usr/include
|
||||
LUALIB = ../liblua/liblua.a
|
||||
LDFLAGS = $(ENV_LDFLAGS)
|
||||
INCLUDES_CLIENT = -I. -I../include -I../common -I../common/polarssl -I../zlib -I../uart -I/opt/local/include -I../liblua
|
||||
INCLUDES_CLIENT = -I/usr/include -I. -I../include -I../common -I../common/polarssl -I../zlib -I../uart -I/opt/local/include -I../liblua
|
||||
CFLAGS = $(ENV_CFLAGS) -std=c99 -D_ISOC99_SOURCE -DPRESETS $(INCLUDES_CLIENT) -Wall -g -O3
|
||||
CXXFLAGS = -I../include -Wall -O3
|
||||
|
||||
|
@ -103,6 +103,7 @@ CMDSRCS = crapto1/crapto1.c \
|
|||
crapto1/crypto1.c \
|
||||
mfkey.c \
|
||||
tea.c \
|
||||
jansson/path.c \
|
||||
polarssl/des.c \
|
||||
polarssl/aes.c \
|
||||
polarssl/aes_cmac128.c \
|
||||
|
@ -134,7 +135,6 @@ CMDSRCS = crapto1/crapto1.c \
|
|||
lfdemod.c \
|
||||
emv/crypto_polarssl.c\
|
||||
emv/crypto.c\
|
||||
emv/emvjson.c\
|
||||
emv/emv_pk.c\
|
||||
emv/emv_pki.c\
|
||||
emv/emv_pki_priv.c\
|
||||
|
@ -144,6 +144,7 @@ CMDSRCS = crapto1/crapto1.c \
|
|||
emv/tlv.c \
|
||||
emv/emv_tags.c \
|
||||
emv/dol.c \
|
||||
emv/emvjson.c\
|
||||
emv/emvcore.c \
|
||||
emv/test/crypto_test.c\
|
||||
emv/test/sda_test.c\
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
|
||||
#include <jansson.h>
|
||||
#include "tlv.h"
|
||||
#include "jansson/path.h"
|
||||
|
||||
typedef struct {
|
||||
tlv_tag_t Tag;
|
||||
|
|
176
client/jansson/hashtable.h
Normal file
176
client/jansson/hashtable.h
Normal file
|
@ -0,0 +1,176 @@
|
|||
/*
|
||||
* Copyright (c) 2009-2016 Petri Lehtinen <petri@digip.org>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the MIT license. See LICENSE for details.
|
||||
*/
|
||||
|
||||
#ifndef HASHTABLE_H
|
||||
#define HASHTABLE_H
|
||||
|
||||
#include <stdlib.h>
|
||||
#include "jansson.h"
|
||||
|
||||
struct hashtable_list {
|
||||
struct hashtable_list *prev;
|
||||
struct hashtable_list *next;
|
||||
};
|
||||
|
||||
/* "pair" may be a bit confusing a name, but think of it as a
|
||||
key-value pair. In this case, it just encodes some extra data,
|
||||
too */
|
||||
struct hashtable_pair {
|
||||
struct hashtable_list list;
|
||||
struct hashtable_list ordered_list;
|
||||
size_t hash;
|
||||
json_t *value;
|
||||
char key[1];
|
||||
};
|
||||
|
||||
struct hashtable_bucket {
|
||||
struct hashtable_list *first;
|
||||
struct hashtable_list *last;
|
||||
};
|
||||
|
||||
typedef struct hashtable {
|
||||
size_t size;
|
||||
struct hashtable_bucket *buckets;
|
||||
size_t order; /* hashtable has pow(2, order) buckets */
|
||||
struct hashtable_list list;
|
||||
struct hashtable_list ordered_list;
|
||||
} hashtable_t;
|
||||
|
||||
|
||||
#define hashtable_key_to_iter(key_) \
|
||||
(&(container_of(key_, struct hashtable_pair, key)->ordered_list))
|
||||
|
||||
|
||||
/**
|
||||
* hashtable_init - Initialize a hashtable object
|
||||
*
|
||||
* @hashtable: The (statically allocated) hashtable object
|
||||
*
|
||||
* Initializes a statically allocated hashtable object. The object
|
||||
* should be cleared with hashtable_close when it's no longer used.
|
||||
*
|
||||
* Returns 0 on success, -1 on error (out of memory).
|
||||
*/
|
||||
int hashtable_init(hashtable_t *hashtable);
|
||||
|
||||
/**
|
||||
* hashtable_close - Release all resources used by a hashtable object
|
||||
*
|
||||
* @hashtable: The hashtable
|
||||
*
|
||||
* Destroys a statically allocated hashtable object.
|
||||
*/
|
||||
void hashtable_close(hashtable_t *hashtable);
|
||||
|
||||
/**
|
||||
* hashtable_set - Add/modify value in hashtable
|
||||
*
|
||||
* @hashtable: The hashtable object
|
||||
* @key: The key
|
||||
* @serial: For addition order of keys
|
||||
* @value: The value
|
||||
*
|
||||
* If a value with the given key already exists, its value is replaced
|
||||
* with the new value. Value is "stealed" in the sense that hashtable
|
||||
* doesn't increment its refcount but decreases the refcount when the
|
||||
* value is no longer needed.
|
||||
*
|
||||
* Returns 0 on success, -1 on failure (out of memory).
|
||||
*/
|
||||
int hashtable_set(hashtable_t *hashtable, const char *key, json_t *value);
|
||||
|
||||
/**
|
||||
* hashtable_get - Get a value associated with a key
|
||||
*
|
||||
* @hashtable: The hashtable object
|
||||
* @key: The key
|
||||
*
|
||||
* Returns value if it is found, or NULL otherwise.
|
||||
*/
|
||||
void *hashtable_get(hashtable_t *hashtable, const char *key);
|
||||
|
||||
/**
|
||||
* hashtable_del - Remove a value from the hashtable
|
||||
*
|
||||
* @hashtable: The hashtable object
|
||||
* @key: The key
|
||||
*
|
||||
* Returns 0 on success, or -1 if the key was not found.
|
||||
*/
|
||||
int hashtable_del(hashtable_t *hashtable, const char *key);
|
||||
|
||||
/**
|
||||
* hashtable_clear - Clear hashtable
|
||||
*
|
||||
* @hashtable: The hashtable object
|
||||
*
|
||||
* Removes all items from the hashtable.
|
||||
*/
|
||||
void hashtable_clear(hashtable_t *hashtable);
|
||||
|
||||
/**
|
||||
* hashtable_iter - Iterate over hashtable
|
||||
*
|
||||
* @hashtable: The hashtable object
|
||||
*
|
||||
* Returns an opaque iterator to the first element in the hashtable.
|
||||
* The iterator should be passed to hashtable_iter_* functions.
|
||||
* The hashtable items are not iterated over in any particular order.
|
||||
*
|
||||
* There's no need to free the iterator in any way. The iterator is
|
||||
* valid as long as the item that is referenced by the iterator is not
|
||||
* deleted. Other values may be added or deleted. In particular,
|
||||
* hashtable_iter_next() may be called on an iterator, and after that
|
||||
* the key/value pair pointed by the old iterator may be deleted.
|
||||
*/
|
||||
void *hashtable_iter(hashtable_t *hashtable);
|
||||
|
||||
/**
|
||||
* hashtable_iter_at - Return an iterator at a specific key
|
||||
*
|
||||
* @hashtable: The hashtable object
|
||||
* @key: The key that the iterator should point to
|
||||
*
|
||||
* Like hashtable_iter() but returns an iterator pointing to a
|
||||
* specific key.
|
||||
*/
|
||||
void *hashtable_iter_at(hashtable_t *hashtable, const char *key);
|
||||
|
||||
/**
|
||||
* hashtable_iter_next - Advance an iterator
|
||||
*
|
||||
* @hashtable: The hashtable object
|
||||
* @iter: The iterator
|
||||
*
|
||||
* Returns a new iterator pointing to the next element in the
|
||||
* hashtable or NULL if the whole hastable has been iterated over.
|
||||
*/
|
||||
void *hashtable_iter_next(hashtable_t *hashtable, void *iter);
|
||||
|
||||
/**
|
||||
* hashtable_iter_key - Retrieve the key pointed by an iterator
|
||||
*
|
||||
* @iter: The iterator
|
||||
*/
|
||||
void *hashtable_iter_key(void *iter);
|
||||
|
||||
/**
|
||||
* hashtable_iter_value - Retrieve the value pointed by an iterator
|
||||
*
|
||||
* @iter: The iterator
|
||||
*/
|
||||
void *hashtable_iter_value(void *iter);
|
||||
|
||||
/**
|
||||
* hashtable_iter_set - Set the value pointed by an iterator
|
||||
*
|
||||
* @iter: The iterator
|
||||
* @value: The value to set
|
||||
*/
|
||||
void hashtable_iter_set(void *iter, json_t *value);
|
||||
|
||||
#endif
|
112
client/jansson/jansson_private.h
Normal file
112
client/jansson/jansson_private.h
Normal file
|
@ -0,0 +1,112 @@
|
|||
/*
|
||||
* Copyright (c) 2009-2016 Petri Lehtinen <petri@digip.org>
|
||||
*
|
||||
* Jansson is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the MIT license. See LICENSE for details.
|
||||
*/
|
||||
|
||||
#ifndef JANSSON_PRIVATE_H
|
||||
#define JANSSON_PRIVATE_H
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <jansson_private_config.h>
|
||||
#endif
|
||||
|
||||
#include <stddef.h>
|
||||
#include "jansson.h"
|
||||
#include "hashtable.h"
|
||||
#include "strbuffer.h"
|
||||
|
||||
#define container_of(ptr_, type_, member_) \
|
||||
((type_ *)((char *)ptr_ - offsetof(type_, member_)))
|
||||
|
||||
/* On some platforms, max() may already be defined */
|
||||
#ifndef max
|
||||
#define max(a, b) ((a) > (b) ? (a) : (b))
|
||||
#endif
|
||||
|
||||
/* va_copy is a C99 feature. In C89 implementations, it's sometimes
|
||||
available as __va_copy. If not, memcpy() should do the trick. */
|
||||
#ifndef va_copy
|
||||
#ifdef __va_copy
|
||||
#define va_copy __va_copy
|
||||
#else
|
||||
#define va_copy(a, b) memcpy(&(a), &(b), sizeof(va_list))
|
||||
#endif
|
||||
#endif
|
||||
|
||||
typedef struct {
|
||||
json_t json;
|
||||
hashtable_t hashtable;
|
||||
} json_object_t;
|
||||
|
||||
typedef struct {
|
||||
json_t json;
|
||||
size_t size;
|
||||
size_t entries;
|
||||
json_t **table;
|
||||
} json_array_t;
|
||||
|
||||
typedef struct {
|
||||
json_t json;
|
||||
char *value;
|
||||
size_t length;
|
||||
} json_string_t;
|
||||
|
||||
typedef struct {
|
||||
json_t json;
|
||||
double value;
|
||||
} json_real_t;
|
||||
|
||||
typedef struct {
|
||||
json_t json;
|
||||
json_int_t value;
|
||||
} json_integer_t;
|
||||
|
||||
#define json_to_object(json_) container_of(json_, json_object_t, json)
|
||||
#define json_to_array(json_) container_of(json_, json_array_t, json)
|
||||
#define json_to_string(json_) container_of(json_, json_string_t, json)
|
||||
#define json_to_real(json_) container_of(json_, json_real_t, json)
|
||||
#define json_to_integer(json_) container_of(json_, json_integer_t, json)
|
||||
|
||||
/* Create a string by taking ownership of an existing buffer */
|
||||
json_t *jsonp_stringn_nocheck_own(const char *value, size_t len);
|
||||
|
||||
/* Error message formatting */
|
||||
void jsonp_error_init(json_error_t *error, const char *source);
|
||||
void jsonp_error_set_source(json_error_t *error, const char *source);
|
||||
void jsonp_error_set(json_error_t *error, int line, int column,
|
||||
size_t position, enum json_error_code code,
|
||||
const char *msg, ...);
|
||||
void jsonp_error_vset(json_error_t *error, int line, int column,
|
||||
size_t position, enum json_error_code code,
|
||||
const char *msg, va_list ap);
|
||||
|
||||
/* Locale independent string<->double conversions */
|
||||
int jsonp_strtod(strbuffer_t *strbuffer, double *out);
|
||||
int jsonp_dtostr(char *buffer, size_t size, double value, int prec);
|
||||
|
||||
/* Wrappers for custom memory functions */
|
||||
void* jsonp_malloc(size_t size);
|
||||
void jsonp_free(void *ptr);
|
||||
char *jsonp_strndup(const char *str, size_t length);
|
||||
char *jsonp_strdup(const char *str);
|
||||
char *jsonp_strndup(const char *str, size_t len);
|
||||
|
||||
|
||||
/* Windows compatibility */
|
||||
#if defined(_WIN32) || defined(WIN32)
|
||||
# if defined(_MSC_VER) /* MS compiller */
|
||||
# if (_MSC_VER < 1900) && !defined(snprintf) /* snprintf not defined yet & not introduced */
|
||||
# define snprintf _snprintf
|
||||
# endif
|
||||
# if (_MSC_VER < 1500) && !defined(vsnprintf) /* vsnprintf not defined yet & not introduced */
|
||||
# define vsnprintf(b,c,f,a) _vsnprintf(b,c,f,a)
|
||||
# endif
|
||||
# else /* Other Windows compiller, old definition */
|
||||
# define snprintf _snprintf
|
||||
# define vsnprintf _vsnprintf
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#endif
|
202
client/jansson/path.c
Normal file
202
client/jansson/path.c
Normal file
|
@ -0,0 +1,202 @@
|
|||
/*
|
||||
* Copyright (c) 2012 Rogerz Zhang <rogerz.zhang@gmail.com>
|
||||
*
|
||||
* Jansson is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the MIT license. See LICENSE for details.
|
||||
*
|
||||
* source here https://github.com/rogerz/jansson/blob/json_path/src/path.c
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include <jansson.h>
|
||||
#include "jansson_private.h"
|
||||
|
||||
|
||||
json_t *json_path_get(const json_t *json, const char *path)
|
||||
{
|
||||
static const char root_chr = '$', array_open = '[';
|
||||
static const char *path_delims = ".[", *array_close = "]";
|
||||
const json_t *cursor;
|
||||
char *token, *buf, *peek, *endptr, delim = '\0';
|
||||
const char *expect;
|
||||
|
||||
if (!json || !path || path[0] != root_chr)
|
||||
return NULL;
|
||||
else
|
||||
buf = jsonp_strdup(path);
|
||||
|
||||
peek = buf + 1;
|
||||
cursor = json;
|
||||
token = NULL;
|
||||
expect = path_delims;
|
||||
|
||||
while (peek && *peek && cursor)
|
||||
{
|
||||
char *last_peek = peek;
|
||||
peek = strpbrk(peek, expect);
|
||||
if (peek) {
|
||||
if (!token && peek != last_peek)
|
||||
goto fail;
|
||||
delim = *peek;
|
||||
*peek++ = '\0';
|
||||
} else if (expect != path_delims || !token) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (expect == path_delims) {
|
||||
if (token) {
|
||||
cursor = json_object_get(cursor, token);
|
||||
}
|
||||
expect = (delim == array_open ? array_close : path_delims);
|
||||
token = peek;
|
||||
} else if (expect == array_close) {
|
||||
size_t index = strtol(token, &endptr, 0);
|
||||
if (*endptr)
|
||||
goto fail;
|
||||
cursor = json_array_get(cursor, index);
|
||||
token = NULL;
|
||||
expect = path_delims;
|
||||
} else {
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
|
||||
jsonp_free(buf);
|
||||
return (json_t *)cursor;
|
||||
fail:
|
||||
jsonp_free(buf);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int json_path_set_new(json_t *json, const char *path, json_t *value, size_t flags, json_error_t *error)
|
||||
{
|
||||
static const char root_chr = '$', array_open = '[', object_delim = '.';
|
||||
static const char * const path_delims = ".[", *array_close = "]";
|
||||
|
||||
json_t *cursor, *parent = NULL;
|
||||
char *token, *buf = NULL, *peek, delim = '\0';
|
||||
const char *expect;
|
||||
int index_saved = -1;
|
||||
|
||||
jsonp_error_init(error, "<path>");
|
||||
|
||||
if (!json || !path || flags || !value) {
|
||||
jsonp_error_set(error, -1, -1, 0, json_error_invalid_argument, "invalid argument");
|
||||
goto fail;
|
||||
} else {
|
||||
buf = jsonp_strdup(path);
|
||||
}
|
||||
|
||||
if (buf[0] != root_chr) {
|
||||
jsonp_error_set(error, -1, -1, 0, json_error_invalid_format, "path should start with $");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
peek = buf + 1;
|
||||
cursor = json;
|
||||
token = NULL;
|
||||
expect = path_delims;
|
||||
|
||||
while (peek && *peek && cursor)
|
||||
{
|
||||
char *last_peek = peek;
|
||||
peek = strpbrk(last_peek, expect);
|
||||
|
||||
if (peek) {
|
||||
if (!token && peek != last_peek) {
|
||||
jsonp_error_set(error, -1, -1, last_peek - buf, json_error_invalid_format, "unexpected trailing chars");
|
||||
goto fail;
|
||||
}
|
||||
delim = *peek;
|
||||
*peek++ = '\0';
|
||||
} else { // end of path
|
||||
if (expect == path_delims) {
|
||||
break;
|
||||
} else {
|
||||
jsonp_error_set(error, -1, -1, peek - buf, json_error_invalid_format, "missing ']'?");
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
|
||||
if (expect == path_delims) {
|
||||
if (token) {
|
||||
if (token[0] == '\0') {
|
||||
jsonp_error_set(error, -1, -1, peek - buf, json_error_invalid_format, "empty token");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
parent = cursor;
|
||||
cursor = json_object_get(parent, token);
|
||||
|
||||
if (!cursor) {
|
||||
if (!json_is_object(parent)) {
|
||||
jsonp_error_set(error, -1, -1, peek - buf, json_error_item_not_found, "object expected");
|
||||
goto fail;
|
||||
}
|
||||
if (delim == object_delim) {
|
||||
cursor = json_object();
|
||||
json_object_set_new(parent, token, cursor);
|
||||
} else {
|
||||
jsonp_error_set(error, -1, -1, peek - buf, json_error_item_not_found, "new array is not allowed");
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
}
|
||||
expect = (delim == array_open ? array_close : path_delims);
|
||||
token = peek;
|
||||
} else if (expect == array_close) {
|
||||
char *endptr;
|
||||
size_t index;
|
||||
|
||||
parent = cursor;
|
||||
if (!json_is_array(parent)) {
|
||||
jsonp_error_set(error, -1, -1, peek - buf, json_error_item_not_found, "array expected");
|
||||
goto fail;
|
||||
}
|
||||
index = strtol(token, &endptr, 0);
|
||||
if (*endptr) {
|
||||
jsonp_error_set(error, -1, -1, peek - buf, json_error_item_not_found, "invalid array index");
|
||||
goto fail;
|
||||
}
|
||||
cursor = json_array_get(parent, index);
|
||||
if (!cursor) {
|
||||
jsonp_error_set(error, -1, -1, peek - buf, json_error_item_not_found, "array index out of bound");
|
||||
goto fail;
|
||||
}
|
||||
index_saved = index;
|
||||
token = NULL;
|
||||
expect = path_delims;
|
||||
} else {
|
||||
assert(1);
|
||||
jsonp_error_set(error, -1, -1, peek - buf, json_error_unknown, "unexpected error in path move");
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
|
||||
if (token) {
|
||||
if (json_is_object(cursor)) {
|
||||
json_object_set(cursor, token, value);
|
||||
} else {
|
||||
jsonp_error_set(error, -1, -1, peek - buf, json_error_item_not_found, "object expected");
|
||||
goto fail;
|
||||
}
|
||||
cursor = json_object_get(cursor, token);
|
||||
} else if (index_saved != -1 && json_is_array(parent)) {
|
||||
json_array_set(parent, index_saved, value);
|
||||
cursor = json_array_get(parent, index_saved);
|
||||
} else {
|
||||
jsonp_error_set(error, -1, -1, peek - buf, json_error_item_not_found, "invalid path");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
json_decref(value);
|
||||
jsonp_free(buf);
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
json_decref(value);
|
||||
jsonp_free(buf);
|
||||
return -1;
|
||||
}
|
17
client/jansson/path.h
Normal file
17
client/jansson/path.h
Normal file
|
@ -0,0 +1,17 @@
|
|||
/*
|
||||
* Copyright (c) 2012 Rogerz Zhang <rogerz.zhang@gmail.com>
|
||||
*
|
||||
* Jansson is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the MIT license. See LICENSE for details.
|
||||
*
|
||||
* source here https://github.com/rogerz/jansson/blob/json_path/src/path.c
|
||||
*/
|
||||
|
||||
json_t *json_path_get(const json_t *json, const char *path);
|
||||
int json_path_set_new(json_t *json, const char *path, json_t *value, size_t flags, json_error_t *error);
|
||||
|
||||
static JSON_INLINE
|
||||
int json_path_set(json_t *json, const char *path, json_t *value, size_t flags, json_error_t *error)
|
||||
{
|
||||
return json_path_set_new(json, path, json_incref(value), flags, error);
|
||||
}
|
34
client/jansson/strbuffer.h
Normal file
34
client/jansson/strbuffer.h
Normal file
|
@ -0,0 +1,34 @@
|
|||
/*
|
||||
* Copyright (c) 2009-2016 Petri Lehtinen <petri@digip.org>
|
||||
*
|
||||
* Jansson is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the MIT license. See LICENSE for details.
|
||||
*/
|
||||
|
||||
#ifndef STRBUFFER_H
|
||||
#define STRBUFFER_H
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
typedef struct {
|
||||
char *value;
|
||||
size_t length; /* bytes used */
|
||||
size_t size; /* bytes allocated */
|
||||
} strbuffer_t;
|
||||
|
||||
int strbuffer_init(strbuffer_t *strbuff);
|
||||
void strbuffer_close(strbuffer_t *strbuff);
|
||||
|
||||
void strbuffer_clear(strbuffer_t *strbuff);
|
||||
|
||||
const char *strbuffer_value(const strbuffer_t *strbuff);
|
||||
|
||||
/* Steal the value and close the strbuffer */
|
||||
char *strbuffer_steal_value(strbuffer_t *strbuff);
|
||||
|
||||
int strbuffer_append_byte(strbuffer_t *strbuff, char byte);
|
||||
int strbuffer_append_bytes(strbuffer_t *strbuff, const char *data, size_t size);
|
||||
|
||||
char strbuffer_pop(strbuffer_t *strbuff);
|
||||
|
||||
#endif
|
0
client/obj/jansson/.dummy
Normal file
0
client/obj/jansson/.dummy
Normal file
Loading…
Add table
Add a link
Reference in a new issue