• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2001-2004 Brandon Long
3  * All Rights Reserved.
4  *
5  * ClearSilver Templating System
6  *
7  * This code is made available under the terms of the ClearSilver License.
8  * http://www.clearsilver.net/license.hdf
9  *
10  */
11 
12 #include "cs_config.h"
13 
14 #include <unistd.h>
15 #include <ctype.h>
16 #include <stdlib.h>
17 #include <stdio.h>
18 #include <string.h>
19 #include <stdarg.h>
20 #include <regex.h>
21 #include "neo_misc.h"
22 #include "neo_err.h"
23 #include "neo_str.h"
24 #include "ulist.h"
25 
26 #ifndef va_copy
27 #ifdef __va_copy
28 # define va_copy(dest,src) __va_copy(dest,src)
29 #else
30 # define va_copy(dest,src) ((dest) = (src))
31 #endif
32 #endif
33 
neos_strip(char * s)34 char *neos_strip (char *s)
35 {
36   int x;
37 
38   x = strlen(s) - 1;
39   while (x>=0 && isspace(s[x])) s[x--] = '\0';
40 
41   while (*s && isspace(*s)) s++;
42 
43   return s;
44 }
45 
neos_rstrip(char * s)46 char *neos_rstrip (char *s)
47 {
48   int n = strlen (s)-1;
49 
50   while (n >= 0 && isspace(s[n]))
51   {
52     s[n] = '\0';
53     n--;
54   }
55 
56   return s;
57 }
58 
neos_lower(char * s)59 void neos_lower(char *s)
60 {
61   while(*s != 0) {
62     *s = tolower(*s);
63     s++;
64   }
65 }
66 
67 
string_init(STRING * str)68 void string_init (STRING *str)
69 {
70   str->buf = NULL;
71   str->len = 0;
72   str->max = 0;
73 }
74 
string_clear(STRING * str)75 void string_clear (STRING *str)
76 {
77   if (str->buf != NULL)
78     free(str->buf);
79   string_init(str);
80 }
81 
string_check_length(STRING * str,int l)82 static NEOERR* string_check_length (STRING *str, int l)
83 {
84   if (str->buf == NULL)
85   {
86     if (l * 10 > 256)
87       str->max = l * 10;
88     else
89       str->max = 256;
90     str->buf = (char *) malloc (sizeof(char) * str->max);
91     if (str->buf == NULL)
92       return nerr_raise (NERR_NOMEM, "Unable to allocate render buf of size %d",
93 	  str->max);
94    /*  ne_warn("Creating string %x at %d (%5.2fK)", str, str->max, (str->max / 1024.0)); */
95   }
96   else if (str->len + l >= str->max)
97   {
98     do
99     {
100       str->max *= 2;
101     } while (str->len + l >= str->max);
102     str->buf = (char *) realloc (str->buf, sizeof(char) * str->max);
103     if (str->buf == NULL)
104       return nerr_raise (NERR_NOMEM, "Unable to allocate STRING buf of size %d",
105 	  str->max);
106     /* ne_warn("Growing string %x to %d (%5.2fK)", str, str->max, (str->max / 1024.0)); */
107   }
108   return STATUS_OK;
109 }
110 
string_set(STRING * str,const char * buf)111 NEOERR *string_set (STRING *str, const char *buf)
112 {
113   str->len = 0;
114   return nerr_pass (string_append (str, buf));
115 }
116 
string_append(STRING * str,const char * buf)117 NEOERR *string_append (STRING *str, const char *buf)
118 {
119   NEOERR *err;
120   int l;
121 
122   l = strlen(buf);
123   err = string_check_length (str, l);
124   if (err != STATUS_OK) return nerr_pass (err);
125   strcpy(str->buf + str->len, buf);
126   str->len += l;
127 
128   return STATUS_OK;
129 }
130 
string_appendn(STRING * str,const char * buf,int l)131 NEOERR *string_appendn (STRING *str, const char *buf, int l)
132 {
133   NEOERR *err;
134 
135   err = string_check_length (str, l+1);
136   if (err != STATUS_OK) return nerr_pass (err);
137   memcpy(str->buf + str->len, buf, l);
138   str->len += l;
139   str->buf[str->len] = '\0';
140 
141   return STATUS_OK;
142 }
143 
144 /* this is much more efficient with C99 snprintfs... */
string_appendvf(STRING * str,const char * fmt,va_list ap)145 NEOERR *string_appendvf (STRING *str, const char *fmt, va_list ap)
146 {
147   NEOERR *err;
148   char buf[4096];
149   int bl, size;
150   va_list tmp;
151 
152   va_copy(tmp, ap);
153   /* determine length */
154   size = sizeof (buf);
155   bl = vsnprintf (buf, size, fmt, tmp);
156   if (bl > -1 && bl < size)
157     return string_appendn (str, buf, bl);
158 
159   /* Handle non-C99 snprintfs (requires extra malloc/free and copy) */
160   if (bl == -1)
161   {
162     char *a_buf;
163 
164     va_copy(tmp, ap);
165     a_buf = vnsprintf_alloc(size*2, fmt, tmp);
166     if (a_buf == NULL)
167       return nerr_raise(NERR_NOMEM,
168 	  "Unable to allocate memory for formatted string");
169     err = string_append(str, a_buf);
170     free(a_buf);
171     return nerr_pass(err);
172   }
173 
174   err = string_check_length (str, bl+1);
175   if (err != STATUS_OK) return nerr_pass (err);
176   va_copy(tmp, ap);
177   vsprintf (str->buf + str->len, fmt, tmp);
178   str->len += bl;
179   str->buf[str->len] = '\0';
180 
181   return STATUS_OK;
182 }
183 
string_appendf(STRING * str,const char * fmt,...)184 NEOERR *string_appendf (STRING *str, const char *fmt, ...)
185 {
186   NEOERR *err;
187   va_list ap;
188 
189   va_start (ap, fmt);
190   err = string_appendvf (str, fmt, ap);
191   va_end (ap);
192   return nerr_pass(err);
193 }
194 
string_append_char(STRING * str,char c)195 NEOERR *string_append_char (STRING *str, char c)
196 {
197   NEOERR *err;
198   err = string_check_length (str, 1);
199   if (err != STATUS_OK) return nerr_pass (err);
200   str->buf[str->len] = c;
201   str->buf[str->len + 1] = '\0';
202   str->len += 1;
203 
204   return STATUS_OK;
205 }
206 
string_array_init(STRING_ARRAY * arr)207 void string_array_init (STRING_ARRAY *arr)
208 {
209   arr->entries = NULL;
210   arr->count = 0;
211   arr->max = 0;
212 }
213 
string_array_split(ULIST ** list,char * s,const char * sep,int max)214 NEOERR *string_array_split (ULIST **list, char *s, const char *sep,
215                             int max)
216 {
217   NEOERR *err;
218   char *p, *n, *f;
219   int sl;
220   int x = 0;
221 
222   if (sep[0] == '\0')
223     return nerr_raise (NERR_ASSERT, "separator must be at least one character");
224 
225   err = uListInit (list, 10, 0);
226   if (err) return nerr_pass(err);
227 
228   sl = strlen(sep);
229   p = (sl == 1) ? strchr (s, sep[0]) : strstr (s, sep);
230   f = s;
231   while (p != NULL)
232   {
233     if (x >= max) break;
234     *p = '\0';
235     n = strdup(f);
236     *p = sep[0];
237     if (n) err = uListAppend (*list, n);
238     else err = nerr_raise(NERR_NOMEM,
239 	"Unable to allocate memory to split %s", s);
240     if (err) goto split_err;
241     f = p+sl;
242     p = (sl == 1) ? strchr (f, sep[0]) : strstr (f, sep);
243     x++;
244   }
245   /* Handle remainder */
246   n = strdup(f);
247   if (n) err = uListAppend (*list, n);
248   else err = nerr_raise(NERR_NOMEM,
249       "Unable to allocate memory to split %s", s);
250   if (err) goto split_err;
251   return STATUS_OK;
252 
253 split_err:
254   uListDestroy(list, ULIST_FREE);
255   return err;
256 }
257 
string_array_clear(STRING_ARRAY * arr)258 void string_array_clear (STRING_ARRAY *arr)
259 {
260   int x;
261 
262   for (x = 0; x < arr->count; x++)
263   {
264     if (arr->entries[x] != NULL) free (arr->entries[x]);
265     arr->entries[x] = NULL;
266   }
267   free (arr->entries);
268   arr->entries = NULL;
269   arr->count = 0;
270 }
271 
272 /* Mostly used by vprintf_alloc for non-C99 compliant snprintfs,
273  * this is like vsprintf_alloc except it takes a "suggested" size */
vnisprintf_alloc(char ** buf,int start_size,const char * fmt,va_list ap)274 int vnisprintf_alloc (char **buf, int start_size, const char *fmt, va_list ap)
275 {
276   int bl, size;
277   va_list tmp;
278 
279   *buf = NULL;
280   size = start_size;
281 
282   *buf = (char *) malloc (size * sizeof(char));
283   if (*buf == NULL) return 0;
284   while (1)
285   {
286     va_copy(tmp, ap);
287     bl = vsnprintf (*buf, size, fmt, tmp);
288     if (bl > -1 && bl < size)
289       return bl;
290     if (bl > -1)
291       size = bl + 1;
292     else
293       size *= 2;
294     *buf = (char *) realloc (*buf, size * sizeof(char));
295     if (*buf == NULL) return 0;
296   }
297 }
298 
vnsprintf_alloc(int start_size,const char * fmt,va_list ap)299 char *vnsprintf_alloc (int start_size, const char *fmt, va_list ap)
300 {
301   char *r;
302   vnisprintf_alloc(&r, start_size, fmt, ap);
303   return r;
304 }
305 
306 /* This works better with a C99 compliant vsnprintf, but should work ok
307  * with versions that return a -1 if it overflows the buffer */
visprintf_alloc(char ** buf,const char * fmt,va_list ap)308 int visprintf_alloc (char **buf, const char *fmt, va_list ap)
309 {
310   char ibuf[4096];
311   int bl, size;
312   va_list tmp;
313 
314 /* PPC doesn't like you re-using a va_list... and it might not be
315  * supposed to work at all */
316   va_copy(tmp, ap);
317 
318   size = sizeof (ibuf);
319   bl = vsnprintf (ibuf, sizeof (ibuf), fmt, tmp);
320   if (bl > -1 && bl < size)
321   {
322     *buf = (char *) calloc(bl+1, sizeof(char));
323     if (*buf == NULL) return 0;
324     strncpy(*buf, ibuf, bl);
325     return bl;
326   }
327 
328   if (bl > -1)
329     size = bl + 1;
330   else
331     size *= 2;
332 
333   return vnisprintf_alloc(buf, size, fmt, ap);
334 }
335 
vsprintf_alloc(const char * fmt,va_list ap)336 char *vsprintf_alloc (const char *fmt, va_list ap)
337 {
338   char *r;
339   visprintf_alloc(&r, fmt, ap);
340   return r;
341 }
342 
343 /* technically, sprintf's can have null values, so we need to be able to
344  * return a length also like real sprintf */
isprintf_alloc(char ** buf,const char * fmt,...)345 int isprintf_alloc (char **buf, const char *fmt, ...)
346 {
347   va_list ap;
348   int r;
349 
350   va_start (ap, fmt);
351   r = visprintf_alloc (buf, fmt, ap);
352   va_end (ap);
353   return r;
354 }
355 
sprintf_alloc(const char * fmt,...)356 char *sprintf_alloc (const char *fmt, ...)
357 {
358   va_list ap;
359   char *r;
360 
361   va_start (ap, fmt);
362   r = vsprintf_alloc (fmt, ap);
363   va_end (ap);
364   return r;
365 }
366 
367 /* This is mostly just here for completeness, I doubt anyone would use
368  * this (its more efficient (time-wise) if start_size is bigger than the
369  * resulting string.  Its less efficient than sprintf_alloc if we have a
370  * C99 snprintf and it doesn't fit in start_size.
371  * BTW: If you are really worried about the efficiency of these
372  * functions, maybe you shouldn't be using them in the first place... */
nsprintf_alloc(int start_size,const char * fmt,...)373 char *nsprintf_alloc (int start_size, const char *fmt, ...)
374 {
375   va_list ap;
376   char *r;
377 
378   va_start (ap, fmt);
379   r = vnsprintf_alloc (start_size, fmt, ap);
380   va_end (ap);
381   return r;
382 }
383 
reg_search(const char * re,const char * str)384 BOOL reg_search (const char *re, const char *str)
385 {
386   regex_t search_re;
387   int errcode;
388   char buf[256];
389 
390   if ((errcode = regcomp(&search_re, re, REG_ICASE | REG_EXTENDED | REG_NOSUB)))
391   {
392     regerror (errcode, &search_re, buf, sizeof(buf));
393     ne_warn ("Unable to compile regex %s: %s", re, buf);
394     return FALSE;
395   }
396   errcode = regexec (&search_re, str, 0, NULL, 0);
397   regfree (&search_re);
398   if (errcode == 0)
399     return TRUE;
400   return FALSE;
401 }
402 
string_readline(STRING * str,FILE * fp)403 NEOERR *string_readline (STRING *str, FILE *fp)
404 {
405   NEOERR *err;
406 
407   /* minimum size for a readline is 256 above current position */
408   err = string_check_length (str, str->len + 256);
409   if (err != STATUS_OK) return nerr_pass (err);
410 
411   while (fgets(str->buf + str->len, str->max - str->len, fp) != NULL)
412   {
413     str->len = strlen(str->buf);
414     if (str->buf[str->len-1] == '\n') break;
415     err = string_check_length (str, str->len + 256);
416     if (err != STATUS_OK) return nerr_pass (err);
417   }
418   return STATUS_OK;
419 }
420 
neos_escape(UINT8 * buf,int buflen,char esc_char,const char * escape,char ** esc)421 NEOERR* neos_escape(UINT8 *buf, int buflen, char esc_char, const char *escape,
422                     char **esc)
423 {
424   int nl = 0;
425   int l = 0;
426   int x = 0;
427   char *s;
428   int match = 0;
429 
430   while (l < buflen)
431   {
432     if (buf[l] == esc_char)
433     {
434       nl += 2;
435     }
436     else
437     {
438       x = 0;
439       while (escape[x])
440       {
441 	if (escape[x] == buf[l])
442 	{
443 	  nl +=2;
444 	  break;
445 	}
446 	x++;
447       }
448     }
449     nl++;
450     l++;
451   }
452 
453   s = (char *) malloc (sizeof(char) * (nl + 1));
454   if (s == NULL)
455     return nerr_raise (NERR_NOMEM, "Unable to allocate memory to escape %s",
456 	buf);
457 
458   nl = 0; l = 0;
459   while (l < buflen)
460   {
461     match = 0;
462     if (buf[l] == esc_char)
463     {
464       match = 1;
465     }
466     else
467     {
468       x = 0;
469       while (escape[x])
470       {
471 	if (escape[x] == buf[l])
472 	{
473 	  match = 1;
474 	  break;
475 	}
476 	x++;
477       }
478     }
479     if (match)
480     {
481       s[nl++] = esc_char;
482       s[nl++] = "0123456789ABCDEF"[buf[l] / 16];
483       s[nl++] = "0123456789ABCDEF"[buf[l] % 16];
484       l++;
485     }
486     else
487     {
488       s[nl++] = buf[l++];
489     }
490   }
491   s[nl] = '\0';
492 
493   *esc = s;
494   return STATUS_OK;
495 }
496 
neos_unescape(UINT8 * s,int buflen,char esc_char)497 UINT8 *neos_unescape (UINT8 *s, int buflen, char esc_char)
498 {
499   int i = 0, o = 0;
500 
501   if (s == NULL) return s;
502   while (i < buflen)
503   {
504     if (s[i] == esc_char && (i+2 < buflen) &&
505 	isxdigit(s[i+1]) && isxdigit(s[i+2]))
506     {
507       UINT8 num;
508       num = (s[i+1] >= 'A') ? ((s[i+1] & 0xdf) - 'A') + 10 : (s[i+1] - '0');
509       num *= 16;
510       num += (s[i+2] >= 'A') ? ((s[i+2] & 0xdf) - 'A') + 10 : (s[i+2] - '0');
511       s[o++] = num;
512       i+=3;
513     }
514     else {
515       s[o++] = s[i++];
516     }
517   }
518   if (i && o) s[o] = '\0';
519   return s;
520 }
521 
repr_string_alloc(const char * s)522 char *repr_string_alloc (const char *s)
523 {
524   int l,x,i;
525   int nl = 0;
526   char *rs;
527 
528   if (s == NULL)
529   {
530     return strdup("NULL");
531   }
532 
533   l = strlen(s);
534   for (x = 0; x < l; x++)
535   {
536     if (isprint(s[x]) && s[x] != '"' && s[x] != '\\')
537     {
538       nl++;
539     }
540     else
541     {
542       if (s[x] == '\n' || s[x] == '\t' || s[x] == '\r' || s[x] == '"' ||
543 	  s[x] == '\\')
544       {
545 	nl += 2;
546       }
547       else nl += 4;
548     }
549   }
550 
551   rs = (char *) malloc ((nl+3) * sizeof(char));
552   if (rs == NULL)
553     return NULL;
554 
555   i = 0;
556   rs[i++] = '"';
557   for (x = 0; x < l; x++)
558   {
559     if (isprint(s[x]) && s[x] != '"' && s[x] != '\\')
560     {
561       rs[i++] = s[x];
562     }
563     else
564     {
565       rs[i++] = '\\';
566       switch (s[x])
567       {
568 	case '\n':
569 	  rs[i++] = 'n';
570 	  break;
571 	case '\t':
572 	  rs[i++] = 't';
573 	  break;
574 	case '\r':
575 	  rs[i++] = 'r';
576 	  break;
577 	case '"':
578 	  rs[i++] = '"';
579 	  break;
580 	case '\\':
581 	  rs[i++] = '\\';
582 	  break;
583 	default:
584 	  sprintf(&(rs[i]), "%03o", (s[x] & 0377));
585 	  i += 3;
586 	  break;
587       }
588     }
589   }
590   rs[i++] = '"';
591   rs[i] = '\0';
592   return rs;
593 }
594 
595 // List of all characters that must be escaped
596 // List based on http://www.blooberry.com/indexdot/html/topics/urlencoding.htm
597 static char EscapedChars[] = "$&+,/:;=?@ \"<>#%{}|\\^~[]`'";
598 
599 // Check if a single character needs to be escaped
is_reserved_char(char c)600 static BOOL is_reserved_char(char c)
601 {
602   int i = 0;
603 
604   if (c < 32 || c > 122) {
605     return TRUE;
606   } else {
607     while (EscapedChars[i]) {
608       if (c == EscapedChars[i]) {
609         return TRUE;
610       }
611       ++i;
612     }
613   }
614   return FALSE;
615 }
616 
neos_js_escape(const char * in,char ** esc)617 NEOERR *neos_js_escape (const char *in, char **esc)
618 {
619   int nl = 0;
620   int l = 0;
621   unsigned char *buf = (unsigned char *)in;
622   unsigned char *s;
623 
624   while (buf[l])
625   {
626     if (buf[l] == '/' || buf[l] == '"' || buf[l] == '\'' ||
627         buf[l] == '\\' || buf[l] == '>' || buf[l] == '<' ||
628         buf[l] == '&' || buf[l] == ';' || buf[l] < 32)
629     {
630       nl += 3;
631     }
632     nl++;
633     l++;
634   }
635 
636   s = (unsigned char *) malloc (sizeof(unsigned char) * (nl + 1));
637   if (s == NULL)
638     return nerr_raise (NERR_NOMEM, "Unable to allocate memory to escape %s",
639         buf);
640 
641   nl = 0; l = 0;
642   while (buf[l])
643   {
644     if (buf[l] == '/' || buf[l] == '"' || buf[l] == '\'' ||
645         buf[l] == '\\' || buf[l] == '>' || buf[l] == '<' ||
646         buf[l] == '&' || buf[l] == ';' || buf[l] < 32)
647     {
648       s[nl++] = '\\';
649       s[nl++] = 'x';
650       s[nl++] = "0123456789ABCDEF"[(buf[l] >> 4) & 0xF];
651       s[nl++] = "0123456789ABCDEF"[buf[l] & 0xF];
652       l++;
653     }
654     else
655     {
656       s[nl++] = buf[l++];
657     }
658   }
659   s[nl] = '\0';
660 
661   *esc = (char *)s;
662   return STATUS_OK;
663 }
664 
665 
neos_url_escape(const char * in,char ** esc,const char * other)666 NEOERR *neos_url_escape (const char *in, char **esc,
667                          const char *other)
668 {
669   int nl = 0;
670   int l = 0;
671   int x = 0;
672   unsigned char *buf = (unsigned char *)in;
673   unsigned char *uother = (unsigned char *)other;
674   unsigned char *s;
675   int match = 0;
676 
677   while (buf[l])
678   {
679     if (is_reserved_char(buf[l]))
680     {
681       nl += 2;
682     }
683     else if (uother)
684     {
685       x = 0;
686       while (uother[x])
687       {
688         if (uother[x] == buf[l])
689         {
690           nl +=2;
691           break;
692         }
693         x++;
694       }
695     }
696     nl++;
697     l++;
698   }
699 
700   s = (unsigned char *) malloc (sizeof(unsigned char) * (nl + 1));
701   if (s == NULL)
702     return nerr_raise (NERR_NOMEM, "Unable to allocate memory to escape %s",
703       buf);
704 
705   nl = 0; l = 0;
706   while (buf[l])
707   {
708     match = 0;
709     if (buf[l] == ' ')
710     {
711       s[nl++] = '+';
712       l++;
713     }
714     else
715     {
716       if (is_reserved_char(buf[l]))
717       {
718         match = 1;
719       }
720       else if (uother)
721       {
722         x = 0;
723         while (uother[x])
724         {
725           if (uother[x] == buf[l])
726           {
727             match = 1;
728             break;
729           }
730           x++;
731         }
732       }
733       if (match)
734       {
735         s[nl++] = '%';
736         s[nl++] = "0123456789ABCDEF"[buf[l] / 16];
737         s[nl++] = "0123456789ABCDEF"[buf[l] % 16];
738         l++;
739       }
740       else
741       {
742         s[nl++] = buf[l++];
743       }
744     }
745   }
746   s[nl] = '\0';
747 
748   *esc = (char *)s;
749   return STATUS_OK;
750 }
751 
neos_html_escape(const char * src,int slen,char ** out)752 NEOERR *neos_html_escape (const char *src, int slen,
753                           char **out)
754 {
755   NEOERR *err = STATUS_OK;
756   STRING out_s;
757   int x;
758   char *ptr;
759 
760   string_init(&out_s);
761   err = string_append (&out_s, "");
762   if (err) return nerr_pass (err);
763   *out = NULL;
764 
765   x = 0;
766   while (x < slen)
767   {
768     ptr = strpbrk(src + x, "&<>\"'\r");
769     if (ptr == NULL || (ptr-src >= slen))
770     {
771       err = string_appendn (&out_s, src + x, slen-x);
772       x = slen;
773     }
774     else
775     {
776       err = string_appendn (&out_s, src + x, (ptr - src) - x);
777       if (err != STATUS_OK) break;
778       x = ptr - src;
779       if (src[x] == '&')
780         err = string_append (&out_s, "&amp;");
781       else if (src[x] == '<')
782         err = string_append (&out_s, "&lt;");
783       else if (src[x] == '>')
784         err = string_append (&out_s, "&gt;");
785       else if (src[x] == '"')
786         err = string_append (&out_s, "&quot;");
787       else if (src[x] == '\'')
788         err = string_append (&out_s, "&#39;");
789       else if (src[x] != '\r')
790         err = nerr_raise (NERR_ASSERT, "src[x] == '%c'", src[x]);
791       x++;
792     }
793     if (err != STATUS_OK) break;
794   }
795   if (err)
796   {
797     string_clear (&out_s);
798     return nerr_pass (err);
799   }
800   *out = out_s.buf;
801   return STATUS_OK;
802 }
803 
804 char *URL_PROTOCOLS[] = {"http://", "https://", "ftp://", "mailto:"};
805 
neos_url_validate(const char * in,char ** esc)806 NEOERR *neos_url_validate (const char *in, char **esc)
807 {
808   NEOERR *err = STATUS_OK;
809   STRING out_s;
810   int valid = 0;
811   size_t i;
812   size_t inlen;
813   int num_protocols = sizeof(URL_PROTOCOLS) / sizeof(char*);
814   void* slashpos;
815   void* colonpos;
816 
817   inlen = strlen(in);
818 
819   /*
820    * <a href="//b:80"> or <a href="a/b:80"> are allowed by browsers
821    * and ":" is treated as part of the path, while
822    * <a href="www.google.com:80"> is an invalid url
823    * and ":" is treated as a scheme separator.
824    *
825    * Hence allow for ":" in the path part of a url (after /)
826    */
827   slashpos = memchr(in, '/', inlen);
828   if (slashpos == NULL) {
829     i = inlen;
830   }
831   else {
832     i = (size_t)((char*)slashpos - in);
833   }
834 
835   colonpos = memchr(in, ':', i);
836 
837   if (colonpos == NULL) {
838     // no scheme in 'in': so this is a relative url
839     valid = 1;
840   }
841   else {
842     for (i = 0; i < num_protocols; i++)
843     {
844       if ((inlen >= strlen(URL_PROTOCOLS[i])) &&
845           strncmp(in, URL_PROTOCOLS[i], strlen(URL_PROTOCOLS[i])) == 0) {
846         // 'in' starts with one of the allowed protocols
847         valid = 1;
848         break;
849       }
850 
851     }
852   }
853 
854   if (valid)
855     return neos_html_escape(in, inlen, esc);
856 
857   // 'in' contains an unsupported scheme, replace with '#'
858   string_init(&out_s);
859   err = string_append (&out_s, "#");
860   if (err) return nerr_pass (err);
861 
862   *esc = out_s.buf;
863   return STATUS_OK;
864 
865 }
866 
neos_var_escape(NEOS_ESCAPE context,const char * in,char ** esc)867 NEOERR *neos_var_escape (NEOS_ESCAPE context,
868                          const char *in,
869                          char **esc)
870 {
871 
872   /* Just dup and return if we do nothing. */
873   if (context == NEOS_ESCAPE_NONE ||
874       context == NEOS_ESCAPE_FUNCTION)
875   {
876     *esc = strdup(in);
877     return STATUS_OK;
878   }
879 
880   /* Now we escape based on context. This is the order of precedence:
881    * url > script > style > html
882    */
883   if (context & NEOS_ESCAPE_URL)
884     return nerr_pass(neos_url_escape(in, esc, NULL));
885   else if (context & NEOS_ESCAPE_SCRIPT)
886     return nerr_pass(neos_js_escape(in, esc));
887   else if (context & NEOS_ESCAPE_HTML)
888     return nerr_pass(neos_html_escape(in, strlen(in), esc));
889 
890   return nerr_raise(NERR_ASSERT, "unknown escape context supplied: %d",
891     context);
892 }
893