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

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

Revert "webif: Simplify template loading and checking."

This reverts commit r8346. The subdir logic is used to have different
template directories for different paths comming from http request.

For more info see:

http://www.streamboard.tv/wbb2/thread.php?threadid=37378

File size: 17.7 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!*/
232char *tpl_getUnparsedTpl(const char* name, int8_t removeHeader, const char* subdir) {
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, subdir, name, ".tpl", path, 255)) > 0 && file_exists(path))
248 || (strlen(subdir) > 0
249#ifdef TOUCH
250 && strcmp(subdir, TOUCH_SUBDIR)
251#endif
252 && strlen(tpl_getFilePathInSubdir(cfg.http_tpl, "" , name, ".tpl", path, 255)) > 0 && file_exists(path)))
253 {
254 FILE *fp;
255 char buffer[1025];
256 memset(buffer, 0, sizeof(buffer));
257 int32_t readen, allocated = 1025, offset, size = 0;
258 if (!cs_malloc(&result, allocated)) return NULL;
259 if ((fp = fopen(path,"r"))!=NULL) {
260 // Use as read size sizeof(buffer) - 1 to ensure that buffer is
261 // zero terminated otherwise strstr can segfault!
262 while((readen = fread(buffer, 1, sizeof(buffer) - 1, fp)) > 0) {
263 offset = 0;
264 if (size == 0 && removeHeader) {
265 /* Remove version string from output and check if it is valid for output */
266 char *pch1 = strstr(buffer,"<!--OSCam");
267 if (pch1 != NULL) {
268 char *pch2 = strstr(pch1,"-->");
269 if (pch2 != NULL) {
270 offset = pch2 - buffer + 4;
271 readen -= offset;
272 pch2[0] = '\0';
273 } // if
274 } // if
275 } // if
276 if (allocated < size + readen + 1) {
277 allocated += size + 1024;
278 if (!cs_realloc(&result, allocated)) return NULL;
279 }
280 memcpy(result + size, buffer + offset, readen);
281 size += readen;
282 } // while
283 result[size] = '\0';
284 fclose (fp);
285 return result;
286 } // if
287 } // if
288 } // if
289
290 if (tpl) {
291 if (!cs_malloc(&result, tpl->tpl_data_len + 1)) return NULL; // +1 to accomodate \0 at the end
292 memcpy(result, tpl->tpl_data, tpl->tpl_data_len);
293 } else {
294 if (!cs_malloc(&result, 1)) return NULL; // Return empty string
295 }
296 return result;
297}
298
299/* Returns the specified template with all variables/other templates replaced or an
300 empty string if the template doesn't exist. Do not free the result yourself, it
301 will get automatically cleaned up! */
302char *tpl_getTpl(struct templatevars *vars, const char* name) {
303 char *tplorg = tpl_getUnparsedTpl(name, 1, tpl_getVar(vars, "SUBDIR"));
304 if (!tplorg) return "";
305 char *tplend = tplorg + strlen(tplorg);
306 char *pch, *pch2, *tpl=tplorg;
307 char varname[33];
308
309 int32_t tmp,respos = 0;
310 int32_t allocated = 2 * strlen(tpl) + 1;
311 char *result;
312 if (!cs_malloc(&result, allocated)) return "";
313
314 while(tpl < tplend) {
315 if (tpl[0] == '#' && tpl[1] == '#' && tpl[2] != '#') {
316 pch2 = tpl;
317 pch = tpl + 2;
318 while(pch[0] != '\0' && (pch[0] != '#' || pch[1] != '#')) ++pch;
319 if (pch - pch2 < 32 && pch[0] == '#' && pch[1] == '#') {
320 memcpy(varname, pch2 + 2, pch - pch2 - 2);
321 varname[pch - pch2 - 2] = '\0';
322 if (strncmp(varname, "TPL", 3) == 0) {
323 if ((*vars).messages > 0 || strncmp(varname, "TPLMESSAGE", 10) != 0)
324 pch2 = tpl_getTpl(vars, varname + 3);
325 else pch2 = "";
326 } else {
327 pch2 = tpl_getVar(vars, varname);
328 }
329 tmp = strlen(pch2);
330 if (tmp + respos + 2 >= allocated) {
331 allocated = tmp + respos + 256;
332 if (!cs_realloc(&result, allocated)) return "";
333 }
334 memcpy(result + respos, pch2, tmp);
335 respos += tmp;
336 tpl = pch + 2;
337 }
338 } else {
339 if (respos + 2 >= allocated) {
340 allocated = respos + 256;
341 if (!cs_realloc(&result, allocated)) return "";
342 }
343 result[respos] = tpl[0];
344 ++respos;
345 ++tpl;
346 }
347 }
348 free(tplorg);
349 result[respos] = '\0';
350 tpl_addTmp(vars, result);
351 return result;
352}
353
354/* Saves all templates to the specified paths. Existing files will be overwritten! */
355int32_t tpl_saveIncludedTpls(const char *path) {
356 int32_t i, cnt = 0;
357 char tmp[256];
358 FILE *fp;
359 for (i = 0; i < tpls_count; ++i) {
360 const struct tpl *tpl = &tpls[i];
361 if (strlen(tpl_getTplPath(tpl->tpl_name, path, tmp, 256)) > 0 && (fp = fopen(tmp,"w")) != NULL) {
362 if (strncmp(tpl->tpl_name, "IC", 2) != 0) {
363 fprintf(fp, "<!--OSCam;%lu;%s;%s-->\n", crc32(0L, (unsigned char *)tpl->tpl_data, tpl->tpl_data_len), CS_VERSION, CS_SVN_VERSION);
364 }
365 fwrite(tpl->tpl_data, tpl->tpl_data_len, 1, fp);
366 fclose (fp);
367 ++cnt;
368 }
369 }
370 return cnt;
371}
372
373/* Checks all disk templates in a directory if they are still current or may need upgrade! */
374void tpl_checkOneDirDiskRevisions(const char* subdir) {
375 char dirpath[255] = "\0";
376 snprintf(dirpath, 255, "%s%s", cfg.http_tpl ? cfg.http_tpl : "", subdir);
377 int32_t i;
378 char path[255];
379 for(i = 0; i < tpls_count; ++i) {
380 const struct tpl *tpl = &tpls[i];
381 if (strncmp(tpl->tpl_name, "IC", 2) != 0 && strlen(tpl_getTplPath(tpl->tpl_name, dirpath, path, 255)) > 0 && file_exists(path)) {
382 int8_t error = 1;
383 char *tplorg = tpl_getUnparsedTpl(tpl->tpl_name, 0, subdir);
384 unsigned long checksum = 0, curchecksum = crc32(0L, (unsigned char*)tpl->tpl_data, tpl->tpl_data_len);
385 char *pch1 = strstr(tplorg,"<!--OSCam");
386 if (pch1 != NULL) {
387 char *version = "?", *revision = "?";
388 char *pch2 = strstr(pch1,"-->");
389 if (pch2 != NULL) {
390 pch2[0] = '\0';
391 int32_t j;
392 char *ptr1, *saveptr1 = NULL;
393 for (j = 0, ptr1 = strtok_r(pch1 + 10, ";", &saveptr1); (ptr1) && j < 4 ; ptr1 = strtok_r(NULL, ";", &saveptr1), j++) {
394 if (j == 0) checksum = strtoul(ptr1, NULL, 10);
395 else if (j == 1) version = ptr1;
396 else if (j == 2) revision = ptr1;
397 }
398 }
399 if (checksum != curchecksum) {
400 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);
401 } else error = 0;
402 } else cs_log("WARNING: Your http disk template %s is in the old template format without revision info. Please consider upgrading it!", path);
403 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);
404 free(tplorg);
405 }
406 }
407}
408
409/* Checks whether disk templates need upgrade - including sub-directories */
410void tpl_checkDiskRevisions(void) {
411 char subdir[255];
412 char dirpath[255];
413 if (cfg.http_tpl) {
414 tpl_checkOneDirDiskRevisions("");
415 DIR *hdir;
416 struct dirent entry;
417 struct dirent *result;
418 struct stat s;
419 if ((hdir = opendir(cfg.http_tpl)) != NULL) {
420 while(cs_readdir_r(hdir, &entry, &result) == 0 && result != NULL) {
421 if (strcmp(".", entry.d_name) == 0 || strcmp("..", entry.d_name) == 0) {
422 continue;
423 }
424 snprintf(dirpath, 255, "%s%s", cfg.http_tpl, entry.d_name);
425 if (stat(dirpath, &s) == 0) {
426 if (s.st_mode & S_IFDIR) {
427 snprintf(subdir, 255,
428 #ifdef WIN32
429 "%s\\"
430 #else
431 "%s/"
432 #endif
433 , entry.d_name);
434 tpl_checkOneDirDiskRevisions(subdir);
435 }
436 }
437 }
438 closedir(hdir);
439 }
440 }
441}
442
443/* Helper function for urldecode.*/
444static int32_t x2i(int32_t i) {
445 i = toupper(i);
446 i = i - '0';
447 if (i > 9) i = i - 'A' + '9' + 1;
448 return i;
449}
450
451/* Decodes values in a http url. Note: The original value is modified! */
452void urldecode(char *s) {
453 int32_t c, c1, n;
454 char *t;
455 t = s;
456 n = strlen(s);
457 while(n >0) {
458 c = *s++;
459 if (c == '+') c = ' ';
460 else if (c == '%' && n > 2) {
461 c = *s++;
462 c1 = c;
463 c = *s++;
464 c = 16*x2i(c1) + x2i(c);
465 n -= 2;
466 }
467 *t++ = c;
468 n--;
469 }
470 *t = 0;
471}
472
473/* Encode values in a http url. Do not call free() or realloc on the returned reference or you will get memory corruption! */
474char *urlencode(struct templatevars *vars, char *str) {
475 char *buf;
476 if (!cs_malloc(&buf, strlen(str) * 3 + 1)) return "";
477 char *pstr = str, *pbuf = buf;
478 while (*pstr) {
479 if (isalnum(*pstr) || *pstr == '-' || *pstr == '_' || *pstr == '.' || *pstr == '~') *pbuf++ = *pstr;
480 else if (*pstr == ' ') *pbuf++ = '+';
481 else {
482 *pbuf++ = '%';
483 *pbuf++ = to_hex(*pstr >> 4);
484 *pbuf++ = to_hex(*pstr & 15);
485 }
486 ++pstr;
487 }
488 *pbuf = '\0';
489 /* Allocate the needed memory size and store it in the templatevars */
490 if (!cs_realloc(&buf, strlen(buf) + 1)) return "";
491 return tpl_addTmp(vars, buf);
492}
493
494/* XML-Escapes a char array. The returned reference will be automatically cleaned through the templatevars-mechanism tpl_clear().
495 Do not call free() or realloc on the returned reference or you will get memory corruption! */
496char *xml_encode(struct templatevars *vars, char *chartoencode) {
497 if (!chartoencode) return "";
498 int32_t i, pos = 0, len = strlen(chartoencode);
499 char *encoded;
500 char buffer[7];
501 /* In worst case, every character could get converted to 6 chars (we only support ASCII, for Unicode it would be 7)*/
502 if (!cs_malloc(&encoded, len * 6 + 1)) return "";
503 for (i = 0; i < len; ++i) {
504 unsigned char tmp = chartoencode[i];
505 switch(tmp) {
506 case '&' : memcpy(encoded + pos, "&amp;", 5); pos += 5; break;
507 case '<' : memcpy(encoded + pos, "&lt;", 4); pos += 4; break;
508 case '>' : memcpy(encoded + pos, "&gt;", 4); pos += 4; break;
509 case '"' : memcpy(encoded + pos, "&quot;", 6); pos += 6; break;
510 case '\'': memcpy(encoded + pos, "&#39;", 5); pos += 5; break; // &apos; not supported on older IE
511 case '\n': memcpy(encoded + pos, "\n", 1); pos += 1; break;
512 default:
513 if (tmp < 32 || (cs_http_use_utf8 != 1 && tmp > 127)) {
514 snprintf(buffer, 7, "&#%d;", tmp);
515 memcpy(encoded + pos, buffer, strlen(buffer));
516 pos += strlen(buffer);
517 } else {
518 encoded[pos] = tmp;
519 ++pos;
520 }
521 }
522 }
523 /* Reduce to the really needed memory size and store it in the templatevars */
524 if (!cs_realloc(&encoded, pos + 1)) return "";
525 encoded[pos] = '\0';
526 return tpl_addTmp(vars, encoded);
527}
528
529/* Format a seconds integer to hh:mm:ss or dd hh:mm:ss depending hrs >24 */
530char *sec2timeformat(struct templatevars *vars, int32_t seconds) {
531 char *value;
532 if (seconds <= 0)
533 return "00:00:00";
534 if (!cs_malloc(&value, 16))
535 return "00:00:00";
536 int32_t secs = 0, fullmins = 0, mins = 0, fullhours = 0, hours = 0, days = 0;
537 secs = seconds % 60;
538 if (seconds >= 60) {
539 fullmins = seconds / 60;
540 mins = fullmins % 60;
541 if (fullmins >= 60) {
542 fullhours = fullmins / 60;
543 hours = fullhours % 24;
544 days = fullhours / 24;
545 }
546 }
547 if (days == 0)
548 snprintf(value, 16, "%02d:%02d:%02d", hours, mins, secs);
549 else
550 snprintf(value, 16, "%02dd %02d:%02d:%02d", days, hours, mins, secs);
551 return tpl_addTmp(vars, value);
552}
553
554#endif
Note: See TracBrowser for help on using the repository browser.