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

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

webif: Remove the need to save config info in struct templates.

Configuration data for templates was introduced in commit r7221
as a way to fix ticket #2665.

Basically we kept the configuration deps in the template and when we
loaded the template from file we checked if the config items are enabled.
If they weren't we ignorred the template like it didn't exist.

But now there is a better way. If config item was disabled when the
binary was built then the template does not exist in struct templates
at all.

So we can just check if the requested template exists in struct templates,
and if it doesn't exists we just don't try to load it from file, so the
problem in ticket #2665 is still solved but we remove lots of code (one
less place place that knows about configuration items, yay!) and lower
the binary size by ~1.4k.

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.