source: trunk/module-webif-tpl.c@ 8436

Last change on this file since 8436 was 8436, checked in by gf, 9 years ago

webif: Simplify template loading and checking.

The subdir logic seems to complicate the code for no good reason and
as far as I can work out it does nothing. It looks like a leftover
that can be removed.

File size: 16.6 KB
Line 
1#include "globals.h"
2
3#ifdef WEBIF
4#include "webif/pages.h"
5#include "module-webif-tpl.h"
6#include "oscam-files.h"
7#include "oscam-string.h"
8
9extern uint8_t cs_http_use_utf8;
10
11/* struct template templates[] that comes from webif/pages.c is recreated as
12 struct tpl tpls[] because we need to add additional fields such as tpl_name_hash
13 and possibly preprocess templates[] struct before using it. */
14
15struct tpl {
16 uint32_t tpl_name_hash;
17 const char *tpl_name;
18 const char *tpl_data;
19 uint32_t tpl_data_len;
20};
21
22static struct tpl *tpls;
23static int tpls_count;
24
25void webif_tpls_prepare(void) {
26 int i;
27 extern const struct template templates[];
28 tpls_count = templates_count();
29 if (!cs_malloc(&tpls, tpls_count * sizeof(struct tpl))) {
30 tpls_count = 0;
31 return;
32 }
33 for(i = 0; i < tpls_count; ++i) {
34 tpls[i].tpl_name_hash = jhash(templates[i].tpl_name, strlen(templates[i].tpl_name));
35 tpls[i].tpl_name = templates[i].tpl_name;
36 tpls[i].tpl_data = templates[i].tpl_data;
37 tpls[i].tpl_data_len = templates[i].tpl_data_len;
38 }
39}
40
41void webif_tpls_free(void) {
42 free(tpls);
43}
44
45/* Adds a name->value-mapping or appends to it. You will get a reference back which you may freely
46 use (but you should not call free/realloc on this!)*/
47char *tpl_addVar(struct templatevars *vars, uint8_t addmode, char *name, char *value){
48 if (name == NULL) return "";
49 if (value == NULL) value = "";
50 int32_t i;
51 char *tmp = NULL, *result = NULL;
52 for(i = (*vars).varscnt-1; i >= 0; --i) {
53 if (strcmp((*vars).names[i], name) == 0) {
54 result = (*vars).values[i];
55 break;
56 }
57 }
58 if (result == NULL) {
59 if ((*vars).varsalloc <= (*vars).varscnt) {
60 if (!cs_realloc(&(*vars).names, (*vars).varsalloc * 2 * sizeof(char**))) return "";
61 if (!cs_realloc(&(*vars).values, (*vars).varsalloc * 2 * sizeof(char**))) return "";
62 if (!cs_realloc(&(*vars).vartypes, (*vars).varsalloc * 2 * sizeof(uint8_t*))) return "";
63 (*vars).varsalloc = (*vars).varscnt * 2;
64 }
65 int32_t len = strlen(name) + 1;
66 if (!cs_malloc(&tmp, len)) return "";
67 memcpy(tmp, name, len);
68 (*vars).names[(*vars).varscnt] = tmp;
69 len = strlen(value) + 1;
70 if (!cs_malloc(&tmp, len)) {
71 free((*vars).names[(*vars).varscnt]);
72 return "";
73 }
74 memcpy(tmp, value, len);
75 (*vars).values[(*vars).varscnt] = tmp;
76 (*vars).vartypes[(*vars).varscnt] = addmode;
77 (*vars).varscnt++;
78 } else {
79 int32_t oldlen = 0, newlen = strlen(value);
80 if (addmode == TPLAPPEND || addmode == TPLAPPENDONCE) oldlen = strlen((*vars).values[i]);
81 if (!cs_realloc(&((*vars).values[i]), oldlen + newlen + 1)) return value;
82 memcpy((*vars).values[i] + oldlen, value, newlen + 1);
83 (*vars).vartypes[i] = addmode;
84 }
85 return tmp;
86}
87
88/* Adds a message to be output on the page using the TPLMESSAGE template. */
89char *tpl_addMsg(struct templatevars *vars, char *value) {
90 tpl_addVar(vars, TPLADDONCE, "MESSAGE", value);
91 (*vars).messages++;
92 return tpl_addVar(vars, TPLAPPEND, "MESSAGES", tpl_getTpl(vars, "MESSAGEBIT"));
93}
94
95/* Allows to add a char array which has been allocated by malloc. It will automatically get
96 freed when calling tpl_clear(). Please do NOT free the memory yourself or realloc
97 it after having added the array here! */
98char *tpl_addTmp(struct templatevars *vars, char *value) {
99 if (value == NULL) return "";
100 if ((*vars).tmpalloc <= (*vars).tmpcnt) {
101 if (!cs_realloc(&(*vars).tmp, (*vars).tmpalloc * 2 * sizeof(char**))) return value;
102 (*vars).tmpalloc = (*vars).tmpcnt * 2;
103 }
104 (*vars).tmp[(*vars).tmpcnt] = value;
105 (*vars).tmpcnt++;
106 return value;
107}
108
109/* Allows to do a dynamic printf without knowing and defining the needed memory size. If you specify
110 varname, the printf-result will be added/appended to the varlist, if varname=NULL it will only be returned.
111 In either case you will always get a reference back which you may freely use (but you should not call
112 free/realloc on this as it will be automatically cleaned!)*/
113char *tpl_printf(struct templatevars *vars, uint8_t addmode, char *varname, char *fmtstring, ...) {
114 uint32_t needed;
115 char test[1];
116 va_list argptr;
117
118 va_start(argptr,fmtstring);
119 needed = vsnprintf(test, 1, fmtstring, argptr);
120 va_end(argptr);
121
122 char *result;
123 if (!cs_malloc(&result, needed + 1)) return "";
124 va_start(argptr,fmtstring);
125 vsnprintf(result, needed + 1, fmtstring, argptr);
126 va_end(argptr);
127
128 if (varname == NULL) tpl_addTmp(vars, result);
129 else {
130 char *tmp = tpl_addVar(vars, addmode, varname, result);
131 free(result);
132 result = tmp;
133 }
134 return result;
135}
136
137/* Returns the value for a name or an empty string if nothing was found. */
138char *tpl_getVar(struct templatevars *vars, char *name) {
139 int32_t i;
140 char *result = NULL;
141 for(i = (*vars).varscnt-1; i >= 0; --i) {
142 if (strcmp((*vars).names[i], name) == 0) {
143 result = (*vars).values[i];
144 break;
145 }
146 }
147 if (result == NULL) return "";
148 else {
149 if ((*vars).vartypes[i] == TPLADDONCE || (*vars).vartypes[i] == TPLAPPENDONCE) {
150 // This is a one-time-use variable which gets cleaned up automatically after retrieving it
151 if (!cs_malloc(&(*vars).values[i], 1)) {
152 (*vars).values[i] = result;
153 result[0] = '\0';
154 return result;
155 } else {
156 (*vars).values[i][0] = '\0';
157 return tpl_addTmp(vars, result);
158 }
159 } else return result;
160 }
161}
162
163/* Initializes all variables for a templatevar-structure and returns a pointer to it. Make
164 sure to call tpl_clear() when you are finished or you'll run into a memory leak! */
165struct templatevars *tpl_create(void) {
166 struct templatevars *vars;
167 if (!cs_malloc(&vars, sizeof(struct templatevars))) return NULL;
168 (*vars).varsalloc = 64;
169 (*vars).varscnt = 0;
170 (*vars).tmpalloc = 64;
171 (*vars).tmpcnt = 0;
172 if (!cs_malloc(&(*vars).names, (*vars).varsalloc * sizeof(char**))) {
173 free(vars);
174 return NULL;
175 }
176 if (!cs_malloc(&(*vars).values, (*vars).varsalloc * sizeof(char**))) {
177 free((*vars).names);
178 free(vars);
179 return NULL;
180 }
181 if (!cs_malloc(&(*vars).vartypes, (*vars).varsalloc * sizeof(uint8_t*))) {
182 free((*vars).names);
183 free((*vars).values);
184 free(vars);
185 return NULL;
186 }
187 if (!cs_malloc(&(*vars).tmp, (*vars).tmpalloc * sizeof(char**))) {
188 free((*vars).names);
189 free((*vars).values);
190 free((*vars).vartypes);
191 free(vars);
192 return NULL;
193 }
194 return vars;
195}
196
197/* Clears all allocated memory for the specified templatevar-structure. */
198void tpl_clear(struct templatevars *vars) {
199 int32_t i;
200 for(i = (*vars).varscnt-1; i >= 0; --i) {
201 free((*vars).names[i]);
202 free((*vars).values[i]);
203 }
204 free((*vars).names);
205 free((*vars).values);
206 free((*vars).vartypes);
207 for(i = (*vars).tmpcnt-1; i >= 0; --i) {
208 free((*vars).tmp[i]);
209 }
210 free((*vars).tmp);
211 free(vars);
212}
213
214/* Creates a path to a template file. You need to set the resultsize to the correct size of result. */
215char *tpl_getFilePathInSubdir(const char *path, const char* subdir, const char *name, const char* ext, char *result, uint32_t resultsize) {
216 int path_len = strlen(path);
217 const char *path_fixup = "";
218 if (path_len && path[path_len - 1] != '/')
219 path_fixup = "/";
220 if (path_len + strlen(path_fixup) + strlen(name) + strlen(subdir) + strlen(ext) < resultsize) {
221 snprintf(result, resultsize, "%s%s%s%s%s", path, path_fixup, subdir, name, ext);
222 } else result[0] = '\0';
223 return result;
224}
225
226char *tpl_getTplPath(const char *name, const char *path, char *result, uint32_t resultsize) {
227 return tpl_getFilePathInSubdir(path, "", name, ".tpl", result, resultsize);
228}
229
230/* Returns an unparsed template either from disk or from internal templates.
231 Note: You must free() the result after using it and you may get NULL if an error occured!*/
232static char *__tpl_getUnparsedTpl(const char* name, bool removeHeader) {
233 int32_t i;
234 const struct tpl *tpl = NULL;
235 char *result;
236
237 uint32_t name_hash = jhash(name, strlen(name));
238 for(i = 0; i < tpls_count; i++) {
239 if (tpls[i].tpl_name_hash == name_hash) {
240 tpl = &tpls[i];
241 break;
242 }
243 }
244
245 if (tpl && cfg.http_tpl) {
246 char path[255];
247 if (strlen(tpl_getFilePathInSubdir(cfg.http_tpl, "", name, ".tpl", path, 255)) > 0 && file_exists(path))
248 {
249 FILE *fp;
250 char buffer[1025];
251 memset(buffer, 0, sizeof(buffer));
252 int32_t readen, allocated = 1025, offset, size = 0;
253 if (!cs_malloc(&result, allocated)) return NULL;
254 if ((fp = fopen(path,"r"))!=NULL) {
255 // Use as read size sizeof(buffer) - 1 to ensure that buffer is
256 // zero terminated otherwise strstr can segfault!
257 while((readen = fread(buffer, 1, sizeof(buffer) - 1, fp)) > 0) {
258 offset = 0;
259 if (size == 0 && removeHeader) {
260 /* Remove version string from output and check if it is valid for output */
261 char *pch1 = strstr(buffer,"<!--OSCam");
262 if (pch1 != NULL) {
263 char *pch2 = strstr(pch1,"-->");
264 if (pch2 != NULL) {
265 offset = pch2 - buffer + 4;
266 readen -= offset;
267 pch2[0] = '\0';
268 } // if
269 } // if
270 } // if
271 if (allocated < size + readen + 1) {
272 allocated += size + 1024;
273 if (!cs_realloc(&result, allocated)) return NULL;
274 }
275 memcpy(result + size, buffer + offset, readen);
276 size += readen;
277 } // while
278 result[size] = '\0';
279 fclose (fp);
280 return result;
281 } // if
282 } // if
283 } // if
284
285 if (tpl) {
286 if (!cs_malloc(&result, tpl->tpl_data_len + 1)) return NULL; // +1 to accomodate \0 at the end
287 memcpy(result, tpl->tpl_data, tpl->tpl_data_len);
288 } else {
289 if (!cs_malloc(&result, 1)) return NULL; // Return empty string
290 }
291 return result;
292}
293
294char *tpl_getUnparsedTpl(const char* name) {
295 return __tpl_getUnparsedTpl(name, true);
296}
297
298/* Returns the specified template with all variables/other templates replaced or an
299 empty string if the template doesn't exist. Do not free the result yourself, it
300 will get automatically cleaned up! */
301char *tpl_getTpl(struct templatevars *vars, const char* name) {
302 char *tplorg = tpl_getUnparsedTpl(name);
303 if (!tplorg) return "";
304 char *tplend = tplorg + strlen(tplorg);
305 char *pch, *pch2, *tpl=tplorg;
306 char varname[33];
307
308 int32_t tmp,respos = 0;
309 int32_t allocated = 2 * strlen(tpl) + 1;
310 char *result;
311 if (!cs_malloc(&result, allocated)) return "";
312
313 while(tpl < tplend) {
314 if (tpl[0] == '#' && tpl[1] == '#' && tpl[2] != '#') {
315 pch2 = tpl;
316 pch = tpl + 2;
317 while(pch[0] != '\0' && (pch[0] != '#' || pch[1] != '#')) ++pch;
318 if (pch - pch2 < 32 && pch[0] == '#' && pch[1] == '#') {
319 memcpy(varname, pch2 + 2, pch - pch2 - 2);
320 varname[pch - pch2 - 2] = '\0';
321 if (strncmp(varname, "TPL", 3) == 0) {
322 if ((*vars).messages > 0 || strncmp(varname, "TPLMESSAGE", 10) != 0)
323 pch2 = tpl_getTpl(vars, varname + 3);
324 else pch2 = "";
325 } else {
326 pch2 = tpl_getVar(vars, varname);
327 }
328 tmp = strlen(pch2);
329 if (tmp + respos + 2 >= allocated) {
330 allocated = tmp + respos + 256;
331 if (!cs_realloc(&result, allocated)) return "";
332 }
333 memcpy(result + respos, pch2, tmp);
334 respos += tmp;
335 tpl = pch + 2;
336 }
337 } else {
338 if (respos + 2 >= allocated) {
339 allocated = respos + 256;
340 if (!cs_realloc(&result, allocated)) return "";
341 }
342 result[respos] = tpl[0];
343 ++respos;
344 ++tpl;
345 }
346 }
347 free(tplorg);
348 result[respos] = '\0';
349 tpl_addTmp(vars, result);
350 return result;
351}
352
353/* Saves all templates to the specified paths. Existing files will be overwritten! */
354int32_t tpl_saveIncludedTpls(const char *path) {
355 int32_t i, cnt = 0;
356 char tmp[256];
357 FILE *fp;
358 for (i = 0; i < tpls_count; ++i) {
359 const struct tpl *tpl = &tpls[i];
360 if (strlen(tpl_getTplPath(tpl->tpl_name, path, tmp, 256)) > 0 && (fp = fopen(tmp,"w")) != NULL) {
361 if (strncmp(tpl->tpl_name, "IC", 2) != 0) {
362 fprintf(fp, "<!--OSCam;%lu;%s;%s-->\n", crc32(0L, (unsigned char *)tpl->tpl_data, tpl->tpl_data_len), CS_VERSION, CS_SVN_VERSION);
363 }
364 fwrite(tpl->tpl_data, tpl->tpl_data_len, 1, fp);
365 fclose (fp);
366 ++cnt;
367 }
368 }
369 return cnt;
370}
371
372/* Checks all disk templates in a directory if they are still current or may need upgrade! */
373void tpl_checkDiskRevisions(void) {
374 if (!cfg.http_tpl)
375 return;
376 int32_t i;
377 char path[255];
378 for(i = 0; i < tpls_count; ++i) {
379 const struct tpl *tpl = &tpls[i];
380 if (strncmp(tpl->tpl_name, "IC", 2) != 0 && strlen(tpl_getTplPath(tpl->tpl_name, cfg.http_tpl, path, 255)) > 0 && file_exists(path)) {
381 int8_t error = 1;
382 char *tplorg = __tpl_getUnparsedTpl(tpl->tpl_name, false); // Do not strip the header
383 unsigned long checksum = 0, curchecksum = crc32(0L, (unsigned char*)tpl->tpl_data, tpl->tpl_data_len);
384 char *pch1 = strstr(tplorg,"<!--OSCam");
385 if (pch1 != NULL) {
386 char *version = "?", *revision = "?";
387 char *pch2 = strstr(pch1,"-->");
388 if (pch2 != NULL) {
389 pch2[0] = '\0';
390 int32_t j;
391 char *ptr1, *saveptr1 = NULL;
392 for (j = 0, ptr1 = strtok_r(pch1 + 10, ";", &saveptr1); (ptr1) && j < 4 ; ptr1 = strtok_r(NULL, ";", &saveptr1), j++) {
393 if (j == 0) checksum = strtoul(ptr1, NULL, 10);
394 else if (j == 1) version = ptr1;
395 else if (j == 2) revision = ptr1;
396 }
397 }
398 if (checksum != curchecksum) {
399 cs_log("WARNING: Your http disk template %s was created for an older revision of OSCam and was changed in original OSCam (%s,r%s). Please consider upgrading it!", path, version, revision);
400 } else error = 0;
401 } else cs_log("WARNING: Your http disk template %s is in the old template format without revision info. Please consider upgrading it!", path);
402 if (error) cs_log("If you are sure that it is current, add the following line at the beginning of the template to suppress this warning: <!--OSCam;%lu;%s;%s-->", curchecksum, CS_VERSION, CS_SVN_VERSION);
403 free(tplorg);
404 }
405 }
406}
407
408/* Helper function for urldecode.*/
409static int32_t x2i(int32_t i) {
410 i = toupper(i);
411 i = i - '0';
412 if (i > 9) i = i - 'A' + '9' + 1;
413 return i;
414}
415
416/* Decodes values in a http url. Note: The original value is modified! */
417void urldecode(char *s) {
418 int32_t c, c1, n;
419 char *t;
420 t = s;
421 n = strlen(s);
422 while(n >0) {
423 c = *s++;
424 if (c == '+') c = ' ';
425 else if (c == '%' && n > 2) {
426 c = *s++;
427 c1 = c;
428 c = *s++;
429 c = 16*x2i(c1) + x2i(c);
430 n -= 2;
431 }
432 *t++ = c;
433 n--;
434 }
435 *t = 0;
436}
437
438/* Encode values in a http url. Do not call free() or realloc on the returned reference or you will get memory corruption! */
439char *urlencode(struct templatevars *vars, char *str) {
440 char *buf;
441 if (!cs_malloc(&buf, strlen(str) * 3 + 1)) return "";
442 char *pstr = str, *pbuf = buf;
443 while (*pstr) {
444 if (isalnum(*pstr) || *pstr == '-' || *pstr == '_' || *pstr == '.' || *pstr == '~') *pbuf++ = *pstr;
445 else if (*pstr == ' ') *pbuf++ = '+';
446 else {
447 *pbuf++ = '%';
448 *pbuf++ = to_hex(*pstr >> 4);
449 *pbuf++ = to_hex(*pstr & 15);
450 }
451 ++pstr;
452 }
453 *pbuf = '\0';
454 /* Allocate the needed memory size and store it in the templatevars */
455 if (!cs_realloc(&buf, strlen(buf) + 1)) return "";
456 return tpl_addTmp(vars, buf);
457}
458
459/* XML-Escapes a char array. The returned reference will be automatically cleaned through the templatevars-mechanism tpl_clear().
460 Do not call free() or realloc on the returned reference or you will get memory corruption! */
461char *xml_encode(struct templatevars *vars, char *chartoencode) {
462 if (!chartoencode) return "";
463 int32_t i, pos = 0, len = strlen(chartoencode);
464 char *encoded;
465 char buffer[7];
466 /* In worst case, every character could get converted to 6 chars (we only support ASCII, for Unicode it would be 7)*/
467 if (!cs_malloc(&encoded, len * 6 + 1)) return "";
468 for (i = 0; i < len; ++i) {
469 unsigned char tmp = chartoencode[i];
470 switch(tmp) {
471 case '&' : memcpy(encoded + pos, "&amp;", 5); pos += 5; break;
472 case '<' : memcpy(encoded + pos, "&lt;", 4); pos += 4; break;
473 case '>' : memcpy(encoded + pos, "&gt;", 4); pos += 4; break;
474 case '"' : memcpy(encoded + pos, "&quot;", 6); pos += 6; break;
475 case '\'': memcpy(encoded + pos, "&#39;", 5); pos += 5; break; // &apos; not supported on older IE
476 case '\n': memcpy(encoded + pos, "\n", 1); pos += 1; break;
477 default:
478 if (tmp < 32 || (cs_http_use_utf8 != 1 && tmp > 127)) {
479 snprintf(buffer, 7, "&#%d;", tmp);
480 memcpy(encoded + pos, buffer, strlen(buffer));
481 pos += strlen(buffer);
482 } else {
483 encoded[pos] = tmp;
484 ++pos;
485 }
486 }
487 }
488 /* Reduce to the really needed memory size and store it in the templatevars */
489 if (!cs_realloc(&encoded, pos + 1)) return "";
490 encoded[pos] = '\0';
491 return tpl_addTmp(vars, encoded);
492}
493
494/* Format a seconds integer to hh:mm:ss or dd hh:mm:ss depending hrs >24 */
495char *sec2timeformat(struct templatevars *vars, int32_t seconds) {
496 char *value;
497 if (seconds <= 0)
498 return "00:00:00";
499 if (!cs_malloc(&value, 16))
500 return "00:00:00";
501 int32_t secs = 0, fullmins = 0, mins = 0, fullhours = 0, hours = 0, days = 0;
502 secs = seconds % 60;
503 if (seconds >= 60) {
504 fullmins = seconds / 60;
505 mins = fullmins % 60;
506 if (fullmins >= 60) {
507 fullhours = fullmins / 60;
508 hours = fullhours % 24;
509 days = fullhours / 24;
510 }
511 }
512 if (days == 0)
513 snprintf(value, 16, "%02d:%02d:%02d", hours, mins, secs);
514 else
515 snprintf(value, 16, "%02dd %02d:%02d:%02d", days, hours, mins, secs);
516 return tpl_addTmp(vars, value);
517}
518
519#endif
Note: See TracBrowser for help on using the repository browser.