#define MODULE_LOG_PREFIX "webif" #include "globals.h" #ifdef WEBIF #include "webif/pages.h" #include "module-webif-tpl.h" #include "oscam-files.h" #include "oscam-string.h" #ifdef COMPRESSED_TEMPLATES #include "minilzo/minilzo.h" #endif /* struct template templates[] that comes from webif/pages.c is recreated as struct tpl tpls[] because we need to add additional fields such as tpl_name_hash and possibly preprocess templates[] struct before using it. */ struct tpl { uint32_t tpl_name_hash; const char *tpl_name; const char *tpl_data; const char *tpl_deps; char *extra_data; uint32_t tpl_data_len; uint8_t tpl_type; }; static struct tpl *tpls; static char *tpls_data; static int tpls_count; static void tpl_init_base64(struct tpl *tpl) { // The rest of OSCam expects images to be base64 encoded and contain mime type. if(!template_is_image(tpl->tpl_type)) { return; } size_t b64_buf_len = 32 + BASE64_LENGTH(tpl->tpl_data_len); // Enough for base64 and 32 for header (data:XXX;base64,) char *b64_buf; if(!cs_malloc(&b64_buf, b64_buf_len)) { tpl->tpl_data = ""; tpl->tpl_data_len = 0; return; } int hdr_len = snprintf(b64_buf, b64_buf_len, "data:%s;base64,", template_get_mimetype(tpl->tpl_type)); base64_encode(tpl->tpl_data, tpl->tpl_data_len, b64_buf + hdr_len, b64_buf_len - hdr_len); tpl->tpl_data = tpl->extra_data = b64_buf; tpl->tpl_data_len = cs_strlen(b64_buf); } void webif_tpls_prepare(void) { int i; const struct template *templates = templates_get(); tpls_count = templates_count(); if(!cs_malloc(&tpls, tpls_count * sizeof(struct tpl))) { tpls_count = 0; return; } #ifdef COMPRESSED_TEMPLATES const char *templates_cdata; size_t tpls_data_len, tpls_data_olen; templates_get_data(&templates_cdata, &tpls_data_len, &tpls_data_olen); if(!cs_malloc(&tpls_data, tpls_data_olen)) { tpls_count = 0; return; } lzo_uint new_len = tpls_data_olen; int r = lzo1x_decompress_safe((uint8_t *)templates_cdata, tpls_data_len, (uint8_t *)tpls_data, &new_len, NULL); if(r == LZO_E_OK && new_len == tpls_data_olen) { cs_log("webif: decompressed %zu bytes back into %zu bytes", tpls_data_len, tpls_data_olen); } else { /* this should NEVER happen */ cs_log("internal error - decompression failed: %d\n", r); NULLFREE(tpls); tpls_count = 0; } for(i = 0; i < tpls_count; ++i) { tpls[i].tpl_name = tpls_data + templates[i].tpl_name_ofs; tpls[i].tpl_data = tpls_data + templates[i].tpl_data_ofs; tpls[i].tpl_deps = tpls_data + templates[i].tpl_deps_ofs; tpls[i].tpl_data_len = templates[i].tpl_data_len; tpls[i].tpl_type = templates[i].tpl_type; tpls[i].tpl_name_hash = jhash(tpls[i].tpl_name, cs_strlen(tpls[i].tpl_name)); tpl_init_base64(&tpls[i]); } #else for(i = 0; i < tpls_count; ++i) { tpls[i].tpl_name_hash = jhash(templates[i].tpl_name, cs_strlen(templates[i].tpl_name)); tpls[i].tpl_name = templates[i].tpl_name; tpls[i].tpl_data = templates[i].tpl_data; tpls[i].tpl_deps = templates[i].tpl_deps; tpls[i].tpl_data_len = templates[i].tpl_data_len; tpls[i].tpl_type = templates[i].tpl_type; tpl_init_base64(&tpls[i]); } #endif } void webif_tpls_free(void) { int32_t i, tmp; tmp = tpls_count; tpls_count = 0; for(i = 0; i < tmp; ++i) { NULLFREE(tpls[i].extra_data); } NULLFREE(tpls_data); NULLFREE(tpls); } /* Adds a name->value-mapping or appends to it. You will get a reference back which you may freely use (but you should not call free/realloc on this!)*/ void tpl_addVar(struct templatevars *vars, uint8_t addmode, const char *name, const char *value) { if(name == NULL) { return; } if(value == NULL) { value = ""; } int32_t i; char *tmp = NULL, *result = NULL; for(i = (*vars).varscnt - 1; i >= 0; --i) { if(strcmp((*vars).names[i], name) == 0) { result = (*vars).values[i]; break; } } if(result == NULL) { if((*vars).varsalloc <= (*vars).varscnt) { if(!cs_realloc(&(*vars).names, (*vars).varsalloc * 2 * sizeof(char **))) { return; } if(!cs_realloc(&(*vars).values, (*vars).varsalloc * 2 * sizeof(char **))) { return; } if(!cs_realloc(&(*vars).vartypes, (*vars).varsalloc * 2 * sizeof(uint8_t *))) { return; } (*vars).varsalloc = (*vars).varscnt * 2; } int32_t len = cs_strlen(name) + 1; if(!cs_malloc(&tmp, len)) { return; } memcpy(tmp, name, len); (*vars).names[(*vars).varscnt] = tmp; len = cs_strlen(value) + 1; if(!cs_malloc(&tmp, len)) { NULLFREE((*vars).names[(*vars).varscnt]); return; } memcpy(tmp, value, len); (*vars).values[(*vars).varscnt] = tmp; (*vars).vartypes[(*vars).varscnt] = addmode; (*vars).varscnt++; } else { int32_t oldlen = 0, newlen = cs_strlen(value); if(addmode == TPLAPPEND || addmode == TPLAPPENDONCE) { oldlen = cs_strlen((*vars).values[i]); } if(!cs_realloc(&((*vars).values[i]), oldlen + newlen + 1)) { return; } memcpy((*vars).values[i] + oldlen, value, newlen + 1); (*vars).vartypes[i] = addmode; } return; } /* Adds a message to be output on the page using the TPLMESSAGE template. */ void tpl_addMsg(struct templatevars *vars, const char *value) { tpl_addVar(vars, TPLADDONCE, "MESSAGE", value); (*vars).messages++; tpl_addVar(vars, TPLAPPEND, "MESSAGES", tpl_getTpl(vars, "MESSAGEBIT")); } /* Allows to add a char array which has been allocated by malloc. It will automatically get freed when calling tpl_clear(). Please do NOT free the memory yourself or realloc it after having added the array here! */ static char *tpl_addTmp(struct templatevars *vars, char *value) { if(value == NULL) { return ""; } if((*vars).tmpalloc <= (*vars).tmpcnt) { if(!cs_realloc(&(*vars).tmp, (*vars).tmpalloc * 2 * sizeof(char **))) { return value; } (*vars).tmpalloc = (*vars).tmpcnt * 2; } (*vars).tmp[(*vars).tmpcnt] = value; (*vars).tmpcnt++; return value; } /* Allows to do a dynamic printf without knowing and defining the needed memory size. If you specify varname, the printf-result will be added/appended to the varlist, if varname=NULL it will only be returned. In either case you will always get a reference back which you may freely use (but you should not call free/realloc on this as it will be automatically cleaned!)*/ void tpl_printf(struct templatevars *vars, uint8_t addmode, const char *varname, const char *fmtstring, ...) { uint32_t needed; char test[1]; va_list argptr; va_start(argptr, fmtstring); needed = vsnprintf(test, 1, fmtstring, argptr); va_end(argptr); char *result; if(!cs_malloc(&result, needed + 1)) { return; } va_start(argptr, fmtstring); vsnprintf(result, needed + 1, fmtstring, argptr); va_end(argptr); if(varname == NULL) { tpl_addTmp(vars, result); } else { tpl_addVar(vars, addmode, varname, result); free(result); } return; } /* Returns the value for a name or an empty string if nothing was found. */ char *tpl_getVar(struct templatevars *vars, const char *name) { int32_t i; char *result = NULL; for(i = (*vars).varscnt - 1; i >= 0; --i) { if(strcmp((*vars).names[i], name) == 0) { result = (*vars).values[i]; break; } } if(result == NULL) { return ""; } else { if((*vars).vartypes[i] == TPLADDONCE || (*vars).vartypes[i] == TPLAPPENDONCE) { // This is a one-time-use variable which gets cleaned up automatically after retrieving it if(!cs_malloc(&(*vars).values[i], 1)) { (*vars).values[i] = result; result[0] = '\0'; return result; } else { (*vars).values[i][0] = '\0'; return tpl_addTmp(vars, result); } } else { return result; } } } /* Initializes all variables for a templatevar-structure and returns a pointer to it. Make sure to call tpl_clear() when you are finished or you'll run into a memory leak! */ struct templatevars *tpl_create(void) { struct templatevars *vars; if(!cs_malloc(&vars, sizeof(struct templatevars))) { return NULL; } (*vars).varsalloc = 64; (*vars).varscnt = 0; (*vars).tmpalloc = 64; (*vars).tmpcnt = 0; if(!cs_malloc(&(*vars).names, (*vars).varsalloc * sizeof(char **))) { NULLFREE(vars); return NULL; } if(!cs_malloc(&(*vars).values, (*vars).varsalloc * sizeof(char **))) { NULLFREE((*vars).names); NULLFREE(vars); return NULL; } if(!cs_malloc(&(*vars).vartypes, (*vars).varsalloc * sizeof(uint8_t *))) { NULLFREE((*vars).names); NULLFREE((*vars).values); NULLFREE(vars); return NULL; } if(!cs_malloc(&(*vars).tmp, (*vars).tmpalloc * sizeof(char **))) { NULLFREE((*vars).names); NULLFREE((*vars).values); NULLFREE((*vars).vartypes); NULLFREE(vars); return NULL; } return vars; } /* Clears all allocated memory for the specified templatevar-structure. */ void tpl_clear(struct templatevars *vars) { int32_t i; for(i = (*vars).varscnt - 1; i >= 0; --i) { NULLFREE((*vars).names[i]); NULLFREE((*vars).values[i]); } NULLFREE((*vars).names); NULLFREE((*vars).values); NULLFREE((*vars).vartypes); for(i = (*vars).tmpcnt - 1; i >= 0; --i) { NULLFREE((*vars).tmp[i]); } NULLFREE((*vars).tmp); NULLFREE(vars); } /* Creates a path to a template file. You need to set the resultsize to the correct size of result. */ char *tpl_getFilePathInSubdir(const char *path, const char *subdir, const char *name, const char *ext, char *result, uint32_t resultsize) { int path_len = cs_strlen(path); const char *path_fixup = ""; if(path_len && path[path_len - 1] != '/') { path_fixup = "/"; } if(path_len + cs_strlen(path_fixup) + cs_strlen(name) + cs_strlen(subdir) + cs_strlen(ext) < resultsize) { snprintf(result, resultsize, "%s%s%s%s%s", path, path_fixup, subdir, name, ext); } else { result[0] = '\0'; } return result; } char *tpl_getTplPath(const char *name, const char *path, char *result, uint32_t resultsize) { return tpl_getFilePathInSubdir(path, "", name, ".tpl", result, resultsize); } #define check_conf(CONFIG_VAR, text) \ if (config_enabled(CONFIG_VAR) && strncmp(#CONFIG_VAR, text, len) == 0) { ok = 1; break; } /* Returns an unparsed template either from disk or from internal templates. Note: You must free() the result after using it and you may get NULL if an error occured!*/ char *tpl_getUnparsedTpl(const char *name, int8_t removeHeader, const char *subdir) { int32_t i; char *result; char *tpl_path; tpl_path = (cfg.http_piconpath && cs_strlen(name) > 3 && name[0] == 'I' && name[1] == 'C' && name[2] == '_') ? cfg.http_piconpath : cfg.http_tpl; if(tpl_path) { char path[255]; if((cs_strlen(tpl_getFilePathInSubdir(tpl_path, subdir, name, ".tpl", path, 255)) > 0 && file_exists(path)) || (cs_strlen(subdir) > 0 && cs_strlen(tpl_getFilePathInSubdir(tpl_path, "" , name, ".tpl", path, 255)) > 0 && file_exists(path))) { FILE *fp; char buffer[1025]; memset(buffer, 0, sizeof(buffer)); int32_t readen, allocated = 1025, offset, size = 0; if(!cs_malloc(&result, allocated)) { return NULL; } if((fp = fopen(path, "r")) != NULL) { // Use as read size sizeof(buffer) - 1 to ensure that buffer is // zero terminated otherwise strstr can segfault! while((readen = fread(buffer, 1, sizeof(buffer) - 1, fp)) > 0) { offset = 0; if(size == 0 && removeHeader) { /* Remove version string from output and check if it is valid for output */ char *pch1 = strstr(buffer, ""); if(pch2 != NULL) { offset = pch2 - buffer + 4; readen -= offset; pch2[0] = '\0'; char *ptr1, *ptr2, *saveptr1 = NULL, *saveptr2 = NULL; for(i = 0, ptr1 = strtok_r(pch1 + 10, ";", &saveptr1); (ptr1) && i < 4 ; ptr1 = strtok_r(NULL, ";", &saveptr1), i++) { if(i == 3 && cs_strlen(ptr1) > 2) { int8_t ok = 0; for(ptr2 = strtok_r(ptr1, ",", &saveptr2); (ptr2) && ok == 0 ; ptr2 = strtok_r(NULL, ",", &saveptr2)) { size_t len = cs_strlen(ptr2); check_conf(WITH_CARDREADER, ptr2); check_conf(CARDREADER_PHOENIX, ptr2); check_conf(CARDREADER_DRECAS, ptr2); check_conf(CARDREADER_INTERNAL_AZBOX, ptr2); check_conf(CARDREADER_INTERNAL_COOLAPI, ptr2); check_conf(CARDREADER_INTERNAL_AMSMC, ptr2); check_conf(CARDREADER_INTERNAL_SCI, ptr2); check_conf(CARDREADER_SC8IN1, ptr2); check_conf(CARDREADER_MP35, ptr2); check_conf(CARDREADER_SMARGO, ptr2); check_conf(CARDREADER_PCSC, ptr2); check_conf(CARDREADER_SMART, ptr2); check_conf(CARDREADER_DB2COM, ptr2); check_conf(CARDREADER_STAPI, ptr2); check_conf(CARDREADER_STAPI5, ptr2); check_conf(WEBIF_LIVELOG, ptr2); check_conf(WEBIF_JQUERY, ptr2); check_conf(WITH_COMPRESS_WEBIF, ptr2); check_conf(CS_ANTICASC, ptr2); check_conf(CS_CACHEEX, ptr2); check_conf(CS_CACHEEX_AIO, ptr2); check_conf(HAVE_DVBAPI, ptr2); check_conf(WITH_EXTENDED_CW, ptr2); check_conf(WITH_NEUTRINO, ptr2); check_conf(READ_SDT_CHARSETS, ptr2); check_conf(CLOCKFIX, ptr2); check_conf(IPV6SUPPORT, ptr2); check_conf(LCDSUPPORT, ptr2); check_conf(LEDSUPPORT, ptr2); check_conf(MODULE_CAMD33, ptr2); check_conf(MODULE_CAMD35, ptr2); check_conf(MODULE_CAMD35_TCP, ptr2); check_conf(MODULE_CCCAM, ptr2); check_conf(MODULE_CCCSHARE, ptr2); check_conf(MODULE_CONSTCW, ptr2); check_conf(MODULE_GBOX, ptr2); check_conf(MODULE_GHTTP, ptr2); check_conf(MODULE_MONITOR, ptr2); check_conf(MODULE_NEWCAMD, ptr2); check_conf(MODULE_PANDORA, ptr2); check_conf(MODULE_RADEGAST, ptr2); check_conf(MODULE_SERIAL, ptr2); check_conf(MODULE_CW_CYCLE_CHECK, ptr2); check_conf(READER_BULCRYPT, ptr2); check_conf(READER_CONAX, ptr2); check_conf(READER_CRYPTOWORKS, ptr2); check_conf(READER_GRIFFIN, ptr2); check_conf(READER_DGCRYPT, ptr2); check_conf(READER_DRE, ptr2); check_conf(READER_IRDETO, ptr2); check_conf(READER_NAGRA, ptr2); check_conf(READER_NAGRA_MERLIN, ptr2); check_conf(READER_SECA, ptr2); check_conf(READER_TONGFANG, ptr2); check_conf(READER_VIACCESS, ptr2); check_conf(READER_VIDEOGUARD, ptr2); check_conf(WITH_CARDREADER, ptr2); check_conf(WITH_DEBUG, ptr2); check_conf(WITH_LB, ptr2); check_conf(WITH_LIBCRYPTO, ptr2); check_conf(WITH_SSL, ptr2); check_conf(WITH_STAPI, ptr2); check_conf(WITH_STAPI5, ptr2); } // for if(ok == 0) { fclose(fp); return result; } break; } // if } // for } // if } // if } // if if(allocated < size + readen + 1) { allocated += size + 1024; if(!cs_realloc(&result, allocated)) { fclose(fp); return NULL; } } memcpy(result + size, buffer + offset, readen); size += readen; } // while result[size] = '\0'; fclose(fp); return result; } // if } // if } // if bool found = 0; uint32_t name_hash = jhash(name, cs_strlen(name)); for(i = 0; i < tpls_count; i++) { if(tpls[i].tpl_name_hash == name_hash) { found = 1; break; } } if(found) { const struct tpl *tpl = &tpls[i]; if(!cs_malloc(&result, tpl->tpl_data_len + 1)) { return NULL; } // +1 to accomodate \0 at the end memcpy(result, tpl->tpl_data, tpl->tpl_data_len); } else { if(!cs_malloc(&result, 1)) { return NULL; } // Return empty string } return result; } /* Returns the specified template with all variables/other templates replaced or an empty string if the template doesn't exist. Do not free the result yourself, it will get automatically cleaned up! */ char *tpl_getTpl(struct templatevars *vars, const char *name) { char *tplorg = tpl_getUnparsedTpl(name, 1, tpl_getVar(vars, "SUBDIR")); if(!tplorg) { return ""; } char *tplend = tplorg + cs_strlen(tplorg); char *pch, *pch2, *tpl = tplorg; char varname[33]; int32_t tmp, respos = 0; int32_t allocated = 2 * cs_strlen(tpl) + 1; char *result; if(!cs_malloc(&result, allocated)) { return ""; } while(tpl < tplend) { if(tpl[0] == '#' && tpl[1] == '#' && tpl[2] != '#') { pch2 = tpl; pch = tpl + 2; while(pch[0] != '\0' && (pch[0] != '#' || pch[1] != '#')) { ++pch; } if(pch - pch2 < 32 && pch[0] == '#' && pch[1] == '#') { memcpy(varname, pch2 + 2, pch - pch2 - 2); varname[pch - pch2 - 2] = '\0'; if(strncmp(varname, "TPL", 3) == 0) { if((*vars).messages > 0 || strncmp(varname, "TPLMESSAGE", 10) != 0) { pch2 = tpl_getTpl(vars, varname + 3); } else { pch2 = ""; } } else { pch2 = tpl_getVar(vars, varname); } tmp = cs_strlen(pch2); if(tmp + respos + 2 >= allocated) { allocated = tmp + respos + 256; if(!cs_realloc(&result, allocated)) { return ""; } } memcpy(result + respos, pch2, tmp); respos += tmp; tpl = pch + 2; } } else { if(respos + 2 >= allocated) { allocated = respos + 256; if(!cs_realloc(&result, allocated)) { return ""; } } result[respos] = tpl[0]; ++respos; ++tpl; } } NULLFREE(tplorg); result[respos] = '\0'; tpl_addTmp(vars, result); return result; } /* Saves all templates to the specified paths. Existing files will be overwritten! */ int32_t tpl_saveIncludedTpls(const char *path) { int32_t i, cnt = 0; char tmp[256]; FILE *fp; for(i = 0; i < tpls_count; ++i) { const struct tpl *tpl = &tpls[i]; if(cs_strlen(tpl_getTplPath(tpl->tpl_name, path, tmp, 256)) > 0 && (fp = fopen(tmp, "w")) != NULL) { if(strncmp(tpl->tpl_name, "IC", 2) != 0) { fprintf(fp, "\n", crc32(0, (uint8_t *)tpl->tpl_data, tpl->tpl_data_len), CS_VERSION, tpl->tpl_deps); } fwrite(tpl->tpl_data, tpl->tpl_data_len, 1, fp); fclose(fp); ++cnt; } } return cnt; } /* Checks all disk templates in a directory if they are still current or may need upgrade! */ void tpl_checkOneDirDiskRevisions(const char *subdir) { char dirpath[255] = "\0"; snprintf(dirpath, 255, "%s%s", cfg.http_tpl ? cfg.http_tpl : "", subdir); int32_t i; char path[255]; for(i = 0; i < tpls_count; ++i) { const struct tpl *tpl = &tpls[i]; if(strncmp(tpl->tpl_name, "IC", 2) != 0 && cs_strlen(tpl_getTplPath(tpl->tpl_name, dirpath, path, 255)) > 0 && file_exists(path)) { int8_t error = 1; char *tplorg = tpl_getUnparsedTpl(tpl->tpl_name, 0, subdir); unsigned long checksum = 0, curchecksum = crc32(0L, (uint8_t *)tpl->tpl_data, tpl->tpl_data_len); char *ifdefs = "", *pch1 = strstr(tplorg, ""); if(pch2 != NULL) { pch2[0] = '\0'; int32_t j; char *ptr1, *saveptr1 = NULL; for(j = 0, ptr1 = strtok_r(pch1 + 10, ";", &saveptr1); (ptr1) && j < 4 ; ptr1 = strtok_r(NULL, ";", &saveptr1), j++) { if(j == 0) { checksum = strtoul(ptr1, NULL, 10); } else if(j == 1) { version = ptr1; } else if(j == 2) { revision = ptr1; } else if(j == 3) { ifdefs = ptr1; } } } if(checksum != curchecksum) { 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); } else { error = 0; } } else { cs_log("WARNING: Your http disk template %s is in the old template format without revision info. Please consider upgrading it!", path); } 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: ", curchecksum, CS_VERSION, ifdefs); } NULLFREE(tplorg); } } } /* Checks whether disk templates need upgrade - including sub-directories */ void tpl_checkDiskRevisions(void) { char subdir[255]; char dirpath[255]; int n; if(cfg.http_tpl) { tpl_checkOneDirDiskRevisions(""); struct stat s; struct dirent **entries; n = scandir(cfg.http_tpl, &entries, NULL, NULL); if(n==-1) return; while(n--) { if(strcmp(".", entries[n]->d_name) == 0 || strcmp("..", entries[n]->d_name) == 0) { free(entries[n]); continue; } snprintf(dirpath, 255, "%.31s%.31s", cfg.http_tpl, entries[n]->d_name); if(stat(dirpath, &s) == 0) { if(s.st_mode & S_IFDIR) { snprintf(subdir, 255, #ifdef WIN32 "%s\\" #else "%.253s/" #endif , entries[n]->d_name); tpl_checkOneDirDiskRevisions(subdir); } } free(entries[n]); } free(entries); } } /* Helper function for urldecode.*/ static int32_t x2i(int32_t i) { i = toupper(i); i = i - '0'; if(i > 9) { i = i - 'A' + '9' + 1; } return i; } /* Decodes values in a http url. Note: The original value is modified! */ void urldecode(char *s) { int32_t c, c1, n; char *t; t = s; n = cs_strlen(s); while(n > 0) { c = *s++; if(c == '+') { c = ' '; } else if(c == '%' && n > 2) { c = *s++; c1 = c; c = *s++; c = 16 * x2i(c1) + x2i(c); n -= 2; } *t++ = c; n--; } *t = 0; } /* Encode values in a http url. Do not call free() or realloc on the returned reference or you will get memory corruption! */ char *urlencode(struct templatevars *vars, const char *str) { char *buf; if(!cs_malloc(&buf, cs_strlen(str) * 3 + 1)) { return ""; } const char *pstr = str; char *pbuf = buf; while(*pstr) { if(isalnum((uint8_t)*pstr) || *pstr == '-' || *pstr == '_' || *pstr == '.' || *pstr == '~') { *pbuf++ = *pstr; } else if(*pstr == ' ') { *pbuf++ = '+'; } else { *pbuf++ = '%'; *pbuf++ = to_hex(*pstr >> 4); *pbuf++ = to_hex(*pstr & 15); } ++pstr; } *pbuf = '\0'; /* Allocate the needed memory size and store it in the templatevars */ if(!cs_realloc(&buf, cs_strlen(buf) + 1)) { return ""; } return tpl_addTmp(vars, buf); } /* XML-Escapes a char array. The returned reference will be automatically cleaned through the templatevars-mechanism tpl_clear(). Do not call free() or realloc on the returned reference or you will get memory corruption! */ char *xml_encode(struct templatevars *vars, const char *chartoencode) { if(!chartoencode) { return ""; } int32_t i, pos = 0, len = cs_strlen(chartoencode); char *encoded; char buffer[7]; /* In worst case, every character could get converted to 6 chars (we only support ASCII, for Unicode it would be 7)*/ if(!cs_malloc(&encoded, len * 6 + 1)) { return ""; } for(i = 0; i < len; ++i) { uint8_t tmp = chartoencode[i]; switch(tmp) { case '&' : memcpy(encoded + pos, "&", 5); pos += 5; break; case '<' : memcpy(encoded + pos, "<", 4); pos += 4; break; case '>' : memcpy(encoded + pos, ">", 4); pos += 4; break; case '"' : memcpy(encoded + pos, """, 6); pos += 6; break; case '\'': memcpy(encoded + pos, "'", 6); pos += 6; break; case '\n': memcpy(encoded + pos, "\n", 1); pos += 1; break; default: if(tmp < 32) { snprintf(buffer, 7, "&#%d;", tmp); memcpy(encoded + pos, buffer, cs_strlen(buffer)); pos += cs_strlen(buffer); } else { encoded[pos] = tmp; ++pos; } } } /* Reduce to the really needed memory size and store it in the templatevars */ if(!cs_realloc(&encoded, pos + 1)) { return ""; } encoded[pos] = '\0'; return tpl_addTmp(vars, encoded); } char *json_encode(struct templatevars *vars, const char *chartoencode) { if(!chartoencode) { return ""; } int32_t i, pos = 0, len = cs_strlen(chartoencode); char *encoded; char buffer[7]; /* In worst case, every character could get converted to 6 chars (\uXXXX) */ if(!cs_malloc(&encoded, len * 6 + 1)) { return ""; } for(i = 0; i < len; ++i) { uint8_t tmp = chartoencode[i]; switch(tmp) { case '"' : memcpy(encoded + pos, "\\\"", 2); pos += 2; break; case '\\': memcpy(encoded + pos, "\\\\", 2); pos += 2; break; case '\n': memcpy(encoded + pos, "\\n", 2); pos += 2; break; case '\r': memcpy(encoded + pos, "\\r", 2); pos += 2; break; case '\t': memcpy(encoded + pos, "\\t", 2); pos += 2; break; default: if(tmp < 32) { snprintf(buffer, 7, "\\u%04x", tmp); memcpy(encoded + pos, buffer, 6); pos += 6; } else { encoded[pos] = tmp; ++pos; } } } /* Reduce to the really needed memory size and store it in the templatevars */ if(!cs_realloc(&encoded, pos + 1)) { return ""; } encoded[pos] = '\0'; return tpl_addTmp(vars, encoded); } /* Format a seconds integer to hh:mm:ss or dd hh:mm:ss depending hrs >24 */ char *sec2timeformat(struct templatevars *vars, int32_t seconds) { char *value; if(seconds <= 0) { return "00:00:00"; } if(!cs_malloc(&value, 16)) { return "00:00:00"; } int32_t secs = 0, fullmins = 0, mins = 0, fullhours = 0, hours = 0, days = 0; secs = seconds % 60; if(seconds >= 60) { fullmins = seconds / 60; mins = fullmins % 60; if(fullmins >= 60) { fullhours = fullmins / 60; hours = fullhours % 24; days = fullhours / 24; } } if(days == 0) { snprintf(value, 16, "%02d:%02d:%02d", hours, mins, secs); } else { snprintf(value, 16, "%02dd %02d:%02d:%02d", days, hours, mins, secs); } return tpl_addTmp(vars, value); } #endif