exchange

Base system with REST service to issue digital coins, run by the payment service provider
Log | Files | Refs | Submodules | README | LICENSE

mustach-wrap.c (11474B)


      1 /*
      2  Author: José Bollo <jobol@nonadev.net>
      3 
      4  https://gitlab.com/jobol/mustach
      5 
      6  SPDX-License-Identifier: ISC
      7 */
      8 
      9 #ifndef _GNU_SOURCE
     10 #define _GNU_SOURCE
     11 #endif
     12 
     13 #include <stdlib.h>
     14 #include <stdio.h>
     15 #include <string.h>
     16 #ifdef _WIN32
     17 #include <malloc.h>
     18 #endif
     19 
     20 #include "mustach.h"
     21 #include "mustach-wrap.h"
     22 
     23 /*
     24 * It was stated that allowing to include files
     25 * through template is not safe when the mustache
     26 * template is open to any value because it could
     27 * create leaks (example: {{>/etc/passwd}}).
     28 */
     29 #if MUSTACH_SAFE
     30 # undef MUSTACH_LOAD_TEMPLATE
     31 #elif !defined(MUSTACH_LOAD_TEMPLATE)
     32 # define MUSTACH_LOAD_TEMPLATE 1
     33 #endif
     34 
     35 #if !defined(INCLUDE_PARTIAL_EXTENSION)
     36 # define INCLUDE_PARTIAL_EXTENSION ".mustache"
     37 #endif
     38 
     39 /* global hook for partials */
     40 int (*mustach_wrap_get_partial)(const char *name, struct mustach_sbuf *sbuf) = NULL;
     41 
     42 /* internal structure for wrapping */
     43 struct wrap {
     44 	/* original interface */
     45 	const struct mustach_wrap_itf *itf;
     46 
     47 	/* original closure */
     48 	void *closure;
     49 
     50 	/* flags */
     51 	int flags;
     52 
     53 	/* emiter callback */
     54 	mustach_emit_cb_t *emitcb;
     55 
     56 	/* write callback */
     57 	mustach_write_cb_t *writecb;
     58 };
     59 
     60 /* length given by masking with 3 */
     61 enum comp {
     62 	C_no = 0,
     63 	C_eq = 1,
     64 	C_lt = 5,
     65 	C_le = 6,
     66 	C_gt = 9,
     67 	C_ge = 10
     68 };
     69 
     70 enum sel {
     71 	S_none = 0,
     72 	S_ok = 1,
     73 	S_objiter = 2,
     74 	S_ok_or_objiter = S_ok | S_objiter
     75 };
     76 
     77 static enum comp getcomp(char *head, int sflags)
     78 {
     79 	return (head[0] == '=' && (sflags & Mustach_With_Equal)) ? C_eq
     80 		: (head[0] == '<' && (sflags & Mustach_With_Compare)) ? (head[1] == '=' ? C_le : C_lt)
     81 		: (head[0] == '>' && (sflags & Mustach_With_Compare)) ? (head[1] == '=' ? C_ge : C_gt)
     82 		: C_no;
     83 }
     84 
     85 static char *keyval(char *head, int sflags, enum comp *comp)
     86 {
     87 	char *w, car, escaped;
     88 	enum comp k;
     89 
     90 	k = C_no;
     91 	w = head;
     92 	car = *head;
     93 	escaped = (sflags & Mustach_With_EscFirstCmp) && (getcomp(head, sflags) != C_no);
     94 	while (car && (escaped || (k = getcomp(head, sflags)) == C_no)) {
     95 		if (escaped)
     96 			escaped = 0;
     97 		else
     98 			escaped = ((sflags & Mustach_With_JsonPointer) ? car == '~' : car == '\\')
     99 			    && (getcomp(head + 1, sflags) != C_no);
    100 		if (!escaped)
    101 			*w++ = car;
    102 		head++;
    103 		car = *head;
    104 	}
    105 	*w = 0;
    106 	*comp = k;
    107 	return k == C_no ? NULL : &head[k & 3];
    108 }
    109 
    110 static char *getkey(char **head, int sflags)
    111 {
    112 	char *result, *iter, *write, car;
    113 
    114 	car = *(iter = *head);
    115 	if (!car)
    116 		result = NULL;
    117 	else {
    118 		result = write = iter;
    119 		if (sflags & Mustach_With_JsonPointer)
    120 		{
    121 			while (car && car != '/') {
    122 				if (car == '~')
    123 					switch (iter[1]) {
    124 					case '1': car = '/'; /*@fallthrough@*/
    125 					case '0': iter++;
    126 					}
    127 				*write++ = car;
    128 				car = *++iter;
    129 			}
    130 			*write = 0;
    131 			while (car == '/')
    132 				car = *++iter;
    133 		}
    134 		else
    135 		{
    136 			while (car && car != '.') {
    137 				if (car == '\\' && (iter[1] == '.' || iter[1] == '\\'))
    138 					car = *++iter;
    139 				*write++ = car;
    140 				car = *++iter;
    141 			}
    142 			*write = 0;
    143 			while (car == '.')
    144 				car = *++iter;
    145 		}
    146 		*head = iter;
    147 	}
    148 	return result;
    149 }
    150 
    151 static enum sel sel(struct wrap *w, const char *name)
    152 {
    153 	enum sel result;
    154 	int i, j, sflags, scmp;
    155 	char *key, *value;
    156 	enum comp k;
    157 
    158 	/* make a local writeable copy */
    159 	size_t lenname = 1 + strlen(name);
    160 	char buffer[lenname];
    161 	char *copy = buffer;
    162 	memcpy(copy, name, lenname);
    163 
    164 	/* check if matches json pointer selection */
    165 	sflags = w->flags;
    166 	if (sflags & Mustach_With_JsonPointer) {
    167 		if (copy[0] == '/')
    168 			copy++;
    169 		else
    170 			sflags ^= Mustach_With_JsonPointer;
    171 	}
    172 
    173 	/* extract the value, translate the key and get the comparator */
    174 	if (sflags & (Mustach_With_Equal | Mustach_With_Compare))
    175 		value = keyval(copy, sflags, &k);
    176 	else {
    177 		k = C_no;
    178 		value = NULL;
    179 	}
    180 
    181 	/* case of . alone if Mustach_With_SingleDot? */
    182 	if (copy[0] == '.' && copy[1] == 0 /*&& (sflags & Mustach_With_SingleDot)*/)
    183 		/* yes, select current */
    184 		result = w->itf->sel(w->closure, NULL) ? S_ok : S_none;
    185 	else
    186 	{
    187 		/* not the single dot, extract the first key */
    188 		key = getkey(&copy, sflags);
    189 		if (key == NULL)
    190 			return 0;
    191 
    192 		/* select the root item */
    193 		if (w->itf->sel(w->closure, key))
    194 			result = S_ok;
    195 		else if (key[0] == '*'
    196 		      && !key[1]
    197 		      && !value
    198 		      && !*copy
    199 		      && (w->flags & Mustach_With_ObjectIter)
    200 		      && w->itf->sel(w->closure, NULL))
    201 			result = S_ok_or_objiter;
    202 		else
    203 			result = S_none;
    204 		if (result == S_ok) {
    205 			/* iterate the selection of sub items */
    206 			key = getkey(&copy, sflags);
    207 			while(result == S_ok && key) {
    208 				if (w->itf->subsel(w->closure, key))
    209 					/* nothing */;
    210 				else if (key[0] == '*'
    211 				      && !key[1]
    212 				      && !value
    213 				      && !*copy
    214 				      && (w->flags & Mustach_With_ObjectIter))
    215 					result = S_objiter;
    216 				else
    217 					result = S_none;
    218 				key = getkey(&copy, sflags);
    219 			}
    220 		}
    221 	}
    222 	/* should it be compared? */
    223 	if (result == S_ok && value) {
    224 		if (!w->itf->compare)
    225 			result = S_none;
    226 		else {
    227 			i = value[0] == '!';
    228 			scmp = w->itf->compare(w->closure, &value[i]);
    229 			switch (k) {
    230 			case C_eq: j = scmp == 0; break;
    231 			case C_lt: j = scmp < 0; break;
    232 			case C_le: j = scmp <= 0; break;
    233 			case C_gt: j = scmp > 0; break;
    234 			case C_ge: j = scmp >= 0; break;
    235 			default: j = i; break;
    236 			}
    237 			if (i == j)
    238 				result = S_none;
    239 		}
    240 	}
    241 	return result;
    242 }
    243 
    244 static int start_callback(void *closure)
    245 {
    246 	struct wrap *w = closure;
    247 	return w->itf->start ? w->itf->start(w->closure) : MUSTACH_OK;
    248 }
    249 
    250 static void stop_callback(void *closure, int status)
    251 {
    252 	struct wrap *w = closure;
    253 	if (w->itf->stop)
    254 		w->itf->stop(w->closure, status);
    255 }
    256 
    257 static int emit(struct wrap *w, const char *buffer, size_t size, FILE *file)
    258 {
    259 	int r;
    260 
    261 	if (w->writecb)
    262 		r = w->writecb(file, buffer, size);
    263 	else
    264 		r = fwrite(buffer, 1, size, file) == size ? MUSTACH_OK : MUSTACH_ERROR_SYSTEM;
    265 	return r;
    266 }
    267 
    268 static int emit_callback(void *closure, const char *buffer, size_t size, int escape, FILE *file)
    269 {
    270 	struct wrap *w = closure;
    271 	int r;
    272 	size_t s, i;
    273 	char car;
    274 
    275 	if (w->emitcb)
    276 		r = w->emitcb(file, buffer, size, escape);
    277 	else if (!escape)
    278 		r = emit(w, buffer, size, file);
    279 	else {
    280 		i = 0;
    281 		r = MUSTACH_OK;
    282 		while(i < size && r == MUSTACH_OK) {
    283 			s = i;
    284 			while (i < size && (car = buffer[i]) != '<' && car != '>' && car != '&' && car != '"')
    285 				i++;
    286 			if (i != s)
    287 				r = emit(w, &buffer[s], i - s, file);
    288 			if (i < size && r == MUSTACH_OK) {
    289 				switch(car) {
    290 				case '<': r = emit(w, "&lt;", 4, file); break;
    291 				case '>': r = emit(w, "&gt;", 4, file); break;
    292 				case '&': r = emit(w, "&amp;", 5, file); break;
    293 				case '"': r = emit(w, "&quot;", 6, file); break;
    294 				}
    295 				i++;
    296 			}
    297 		}
    298 	}
    299 	return r;
    300 }
    301 
    302 static int enter_callback(void *closure, const char *name)
    303 {
    304 	struct wrap *w = closure;
    305 	enum sel s = sel(w, name);
    306 	return s == S_none ? 0 : w->itf->enter(w->closure, s & S_objiter);
    307 }
    308 
    309 static int next_callback(void *closure)
    310 {
    311 	struct wrap *w = closure;
    312 	return w->itf->next(w->closure);
    313 }
    314 
    315 static int leave_callback(void *closure)
    316 {
    317 	struct wrap *w = closure;
    318 	return w->itf->leave(w->closure);
    319 }
    320 
    321 static int getoptional(struct wrap *w, const char *name, struct mustach_sbuf *sbuf)
    322 {
    323 	enum sel s = sel(w, name);
    324 	if (!(s & S_ok))
    325 		return 0;
    326 	return w->itf->get(w->closure, sbuf, s & S_objiter);
    327 }
    328 
    329 static int get_callback(void *closure, const char *name, struct mustach_sbuf *sbuf)
    330 {
    331 	struct wrap *w = closure;
    332 	if (getoptional(w, name, sbuf) <= 0) {
    333 		if (w->flags & Mustach_With_ErrorUndefined)
    334 			return MUSTACH_ERROR_UNDEFINED_TAG;
    335 		sbuf->value = "";
    336 	}
    337 	return MUSTACH_OK;
    338 }
    339 
    340 #if MUSTACH_LOAD_TEMPLATE
    341 static int get_partial_from_file(const char *name, struct mustach_sbuf *sbuf)
    342 {
    343 	static char extension[] = INCLUDE_PARTIAL_EXTENSION;
    344 	size_t s;
    345 	long pos;
    346 	FILE *file;
    347 	char *path, *buffer;
    348 
    349 	/* allocate path */
    350 	s = strlen(name);
    351 	path = malloc(s + sizeof extension);
    352 	if (path == NULL)
    353 		return MUSTACH_ERROR_SYSTEM;
    354 
    355 	/* try without extension first */
    356 	memcpy(path, name, s + 1);
    357 	file = fopen(path, "r");
    358 	if (file == NULL) {
    359 		memcpy(&path[s], extension, sizeof extension);
    360 		file = fopen(path, "r");
    361 	}
    362 	free(path);
    363 
    364 	/* if file opened */
    365 	if (file == NULL)
    366 		return MUSTACH_ERROR_PARTIAL_NOT_FOUND;
    367 
    368 	/* compute file size */
    369 	if (fseek(file, 0, SEEK_END) >= 0
    370 	 && (pos = ftell(file)) >= 0
    371 	 && fseek(file, 0, SEEK_SET) >= 0) {
    372 		/* allocate value */
    373 		s = (size_t)pos;
    374 		buffer = malloc(s + 1);
    375 		if (buffer != NULL) {
    376 			/* read value */
    377 			if (1 == fread(buffer, s, 1, file)) {
    378 				/* force zero at end */
    379 				sbuf->value = buffer;
    380 				buffer[s] = 0;
    381 				sbuf->freecb = free;
    382 				fclose(file);
    383 				return MUSTACH_OK;
    384 			}
    385 			free(buffer);
    386 		}
    387 	}
    388 	fclose(file);
    389 	return MUSTACH_ERROR_SYSTEM;
    390 }
    391 #endif
    392 
    393 static int partial_callback(void *closure, const char *name, struct mustach_sbuf *sbuf)
    394 {
    395 	struct wrap *w = closure;
    396 	int rc;
    397 	if (mustach_wrap_get_partial != NULL) {
    398 		rc = mustach_wrap_get_partial(name, sbuf);
    399 		if (rc != MUSTACH_ERROR_PARTIAL_NOT_FOUND) {
    400 			if (rc != MUSTACH_OK)
    401 				sbuf->value = "";
    402 			return rc;
    403 		}
    404 	}
    405 #if MUSTACH_LOAD_TEMPLATE
    406 	if (w->flags & Mustach_With_PartialDataFirst) {
    407 		if (getoptional(w, name, sbuf) > 0)
    408 			rc = MUSTACH_OK;
    409 		else
    410 			rc = get_partial_from_file(name, sbuf);
    411 	}
    412 	else {
    413 		rc = get_partial_from_file(name, sbuf);
    414 		if (rc != MUSTACH_OK &&  getoptional(w, name, sbuf) > 0)
    415 			rc = MUSTACH_OK;
    416 	}
    417 #else
    418 	rc = getoptional(w, name, sbuf) > 0 ?  MUSTACH_OK : MUSTACH_ERROR_PARTIAL_NOT_FOUND;
    419 #endif
    420 	if (rc != MUSTACH_OK)
    421 		sbuf->value = "";
    422 	return MUSTACH_OK;
    423 }
    424 
    425 const struct mustach_itf mustach_wrap_itf = {
    426 	.start = start_callback,
    427 	.put = NULL,
    428 	.enter = enter_callback,
    429 	.next = next_callback,
    430 	.leave = leave_callback,
    431 	.partial = partial_callback,
    432 	.get = get_callback,
    433 	.emit = emit_callback,
    434 	.stop = stop_callback
    435 };
    436 
    437 static void wrap_init(struct wrap *wrap, const struct mustach_wrap_itf *itf, void *closure, int flags, mustach_emit_cb_t *emitcb, mustach_write_cb_t *writecb)
    438 {
    439 	if (flags & Mustach_With_Compare)
    440 		flags |= Mustach_With_Equal;
    441 	wrap->closure = closure;
    442 	wrap->itf = itf;
    443 	wrap->flags = flags;
    444 	wrap->emitcb = emitcb;
    445 	wrap->writecb = writecb;
    446 }
    447 
    448 int mustach_wrap_file(const char *template, size_t length, const struct mustach_wrap_itf *itf, void *closure, int flags, FILE *file)
    449 {
    450 	struct wrap w;
    451 	wrap_init(&w, itf, closure, flags, NULL, NULL);
    452 	return mustach_file(template, length, &mustach_wrap_itf, &w, flags, file);
    453 }
    454 
    455 int mustach_wrap_fd(const char *template, size_t length, const struct mustach_wrap_itf *itf, void *closure, int flags, int fd)
    456 {
    457 	struct wrap w;
    458 	wrap_init(&w, itf, closure, flags, NULL, NULL);
    459 	return mustach_fd(template, length, &mustach_wrap_itf, &w, flags, fd);
    460 }
    461 
    462 int mustach_wrap_mem(const char *template, size_t length, const struct mustach_wrap_itf *itf, void *closure, int flags, char **result, size_t *size)
    463 {
    464 	struct wrap w;
    465 	wrap_init(&w, itf, closure, flags, NULL, NULL);
    466 	return mustach_mem(template, length, &mustach_wrap_itf, &w, flags, result, size);
    467 }
    468 
    469 int mustach_wrap_write(const char *template, size_t length, const struct mustach_wrap_itf *itf, void *closure, int flags, mustach_write_cb_t *writecb, void *writeclosure)
    470 {
    471 	struct wrap w;
    472 	wrap_init(&w, itf, closure, flags, NULL, writecb);
    473 	return mustach_file(template, length, &mustach_wrap_itf, &w, flags, writeclosure);
    474 }
    475 
    476 int mustach_wrap_emit(const char *template, size_t length, const struct mustach_wrap_itf *itf, void *closure, int flags, mustach_emit_cb_t *emitcb, void *emitclosure)
    477 {
    478 	struct wrap w;
    479 	wrap_init(&w, itf, closure, flags, emitcb, NULL);
    480 	return mustach_file(template, length, &mustach_wrap_itf, &w, flags, emitclosure);
    481 }
    482