master uint/parados / server / json.c
  1#include <stdbool.h>
  2#include <stdio.h>
  3#include <stdlib.h>
  4#include <string.h>
  5
  6#include "config.h"
  7#include "json.h"
  8#include "log.h"
  9
 10static const char* json_basename(const char* path);
 11static int json_grow(struct json* j, size_t need);
 12static int json_hex64(struct json* j, uint64_t v);
 13static const char* json_kind_from_type(const char* type);
 14static int json_putc(struct json* j, const char c);
 15static int json_putn(struct json* j, const char* s, size_t n);
 16static int json_puts(struct json* j, const char* s);
 17static int json_string(struct json* j, const char* s);
 18
 19/**
 20 * @brief Extract basename from path
 21 *
 22 * @param path Input path
 23 *
 24 * @return Ptr to basename
 25 */
 26static const char* json_basename(const char* path)
 27{
 28	if (!path)
 29		return "";
 30
 31	const char* p = strrchr(path, '/');
 32	return p ? (p + 1) : path;
 33}
 34
 35/**
 36 * @brief Grow JSON buffer capacity
 37 *
 38 * @param j JSON buffer
 39 * @param need Additional bytes needed
 40 *
 41 * @return 0=Success, -1=Failure
 42 */
 43static int json_grow(struct json* j, size_t need)
 44{
 45	if (j->len + need <= j->cap)
 46		return 0;
 47
 48	size_t ncap = j->cap ? j->cap : 1024; /* initial buffer */
 49	while (ncap < j->len + need)
 50		ncap *= 2;
 51
 52	char* nb = realloc(j->buf, ncap);
 53	if (!nb) {
 54		LOG(verbose_log, "JSON", "realloc FAILED     OOM");
 55		return -1;
 56	}
 57
 58	j->buf = nb;
 59	j->cap = ncap;
 60
 61	return 0;
 62}
 63
 64/**
 65 * @brief Encode uint64 as 16-digit hex string
 66 *
 67 * @param j Output JSON buffer
 68 * @param v Input value
 69 *
 70 * @return 0=Success, -1=Failure
 71 */
 72static int json_hex64(struct json* j, uint64_t v)
 73{
 74	char hex[17];
 75	static const char* b16 = "0123456789abcdef";
 76
 77	for (int i = 15; i >= 0; i--) {
 78		hex[i] = b16[v & 0xf];
 79		v >>= 4;
 80	}
 81	hex[16] = '\0';
 82
 83	return json_string(j, hex);
 84}
 85
 86/**
 87 * @brief Map MIME type to item kind
 88 *
 89 * @param type MIME type string
 90 *
 91 * @return Item kind string
 92 */
 93static const char* json_kind_from_type(const char* type)
 94{
 95	if (!type || type[0] == '\0')
 96		return "other";
 97
 98	if (strncmp(type, "audio/", 6) == 0)
 99		return "audio";
100	if (strncmp(type, "video/", 6) == 0)
101		return "video";
102	if (strncmp(type, "image/", 6) == 0)
103		return "image";
104
105	return "other";
106}
107
108/**
109 * @brief Append single byte to JSON buffer
110 *
111 * @param j Output JSON buffer
112 * @param c Byte to append
113 *
114 * @return 0=Success, -1=Failure
115 */
116static int json_putc(struct json* j, char c)
117{
118	return json_putn(j, &c, 1);
119}
120
121/**
122 * @brief Append byte range to JSON buffer
123 *
124 * @param j Output JSON buffer
125 * @param s Input bytes
126 * @param n Number of bytes to append
127 *
128 * @return 0=Success, -1=Failure
129 */
130static int json_putn(struct json* j, const char* s, size_t n)
131{
132	if (json_grow(j, n + 1) < 0)
133		return -1;
134
135	memcpy(j->buf + j->len, s, n);
136	j->len += n;
137	j->buf[j->len] = '\0';
138
139	return 0;
140}
141
142/**
143 * @brief Append string to JSON buffer
144 *
145 * @param j Output JSON buffer
146 * @param s Input string
147 *
148 * @return 0=Success, -1=Failure
149 */
150static int json_puts(struct json* j, const char* s)
151{
152	return json_putn(j, s, strlen(s));
153}
154
155/**
156 * @brief Append JSON string with escaping
157 *
158 * @param j Output JSON buffer
159 * @param s Input string
160 *
161 * @return 0=Success, -1=Failure
162 */
163static int json_string(struct json* j, const char* s)
164{
165	/* opening quote */
166	if (json_putc(j, '"') < 0)
167		return -1;
168
169	/* emit string body */
170	for (; *s; s++) {
171		unsigned char c = (unsigned char)(*s);
172
173		/* escape quote and backslash */
174		if (c == '"' || c == '\\') {
175			if (json_putc(j, '\\') < 0)
176				return -1;
177			if (json_putc(j, (char)c) < 0)
178				return -1;
179			continue;
180		}
181
182		/* common escapes */
183		if (c == '\n') {
184			if (json_puts(j, "\\n") < 0)
185				return -1;
186			continue;
187		}
188		if (c == '\r') {
189			if (json_puts(j, "\\r") < 0)
190				return -1;
191			continue;
192		}
193		if (c == '\t') {
194			if (json_puts(j, "\\t") < 0)
195				return -1;
196			continue;
197		}
198
199		/* other control chars->\uxxxx */
200		if (c < 0x20) {
201			char esc[7];
202			snprintf(esc, sizeof(esc), "\\u%04x", (unsigned)c);
203			if (json_puts(j, esc) < 0)
204				return -1;
205			continue;
206		}
207
208		/* literal byte */
209		if (json_putc(j, (char)c) < 0)
210			return -1;
211	}
212
213	/* closing quote */
214	if (json_putc(j, '"') < 0)
215		return -1;
216
217	return 0;
218}
219
220void json_free(struct json* j)
221{
222	if (!j)
223		return;
224
225	free(j->buf);
226	j->buf = NULL;
227	j->len = 0;
228	j->cap = 0;
229}
230
231int json_library(struct json* j, const struct library* l)
232{
233	memset(j, 0, sizeof(*j));
234
235	/* header */
236	if (json_puts(j, "{\"proto\":1,\"items\":[") < 0)
237		goto fail;
238
239	/* items[] */
240	for (size_t i = 0; i < l->len; i++) {
241		/* item separator */
242		if (i > 0) {
243			if (json_putc(j, ',') < 0)
244				goto fail;
245		}
246
247		/* object open */
248		if (json_puts(j, "{\"id\":") < 0)
249			goto fail;
250		if (json_hex64(j, l->items[i].id) < 0)
251			goto fail;
252
253		/* path */
254		if (json_puts(j, ",\"path\":") < 0)
255			goto fail;
256		if (json_string(j, l->items[i].path) < 0)
257			goto fail;
258
259		/* object close */
260		if (json_putc(j, '}') < 0)
261			goto fail;
262	}
263
264	/* footer */
265	if (json_puts(j, "]}") < 0)
266		goto fail;
267
268	return 0;
269
270fail:
271	json_free(j);
272	return -1;
273}
274
275int json_meta(struct json* j, const struct item* it, size_t size, long mtime, const char* type)
276{
277	memset(j, 0, sizeof(*j));
278
279	if (json_puts(j, "{\"proto\":1,\"id\":") < 0)
280		goto fail;
281	if (json_hex64(j, it->id) < 0)
282		goto fail;
283
284	if (json_puts(j, ",\"path\":") < 0)
285		goto fail;
286	if (json_string(j, it->path) < 0)
287		goto fail;
288
289	/* basename */
290	if (json_puts(j, ",\"name\":") < 0)
291		goto fail;
292	if (json_string(j, json_basename(it->path)) < 0)
293		goto fail;
294
295	if (json_puts(j, ",\"size\":") < 0)
296		goto fail;
297
298	/* decimal size */
299	char tmp[32];
300	snprintf(tmp, sizeof(tmp), "%zu", size);
301	if (json_puts(j, tmp) < 0)
302		goto fail;
303
304
305	/* mtime (epoch seconds) */
306	if (json_puts(j, ",\"mtime\":") < 0)
307		goto fail;
308	snprintf(tmp, sizeof(tmp), "%ld", mtime);
309	if (json_puts(j, tmp) < 0)
310		goto fail;
311
312	/* formats */
313	if (json_puts(j, ",\"type\":") < 0)
314		goto fail;
315	if (json_string(j, type) < 0)
316		goto fail;
317
318	if (json_puts(j, ",\"kind\":") < 0)
319		goto fail;
320	if (json_string(j, json_kind_from_type(type)) < 0)
321		goto fail;
322
323	if (json_putc(j, '}') < 0)
324		goto fail;
325
326	return 0;
327
328fail:
329	json_free(j);
330	return -1;
331}
332