source: trunk/oscam-http-helpers.c @ 5375

Last change on this file since 5375 was 5342, checked in by Admin, 8 years ago

Add anticascader to monitor_get_proto function. Fix a compiler warning.

File size: 28.8 KB
Line 
1//FIXME Not checked on threadsafety yet; after checking please remove this line
2#include "globals.h"
3#ifdef WEBIF
4#ifdef WITH_SSL
5#include <openssl/crypto.h>
6#include <openssl/ssl.h>
7#include <openssl/err.h>
8#endif
9#include "oscam-http.h"
10
11/* Adds a name->value-mapping or appends to it. You will get a reference back which you may freely
12   use (but you should not call free/realloc on this!)*/
13char *tpl_addVar(struct templatevars *vars, uint8_t addmode, char *name, char *value){ 
14    if(name == NULL || value == NULL) return "";
15    int32_t i;
16    char *tmp,*result = NULL;
17    for(i = (*vars).varscnt-1; i >= 0; --i){
18        if(strcmp((*vars).names[i], name) == 0){
19            result = (*vars).values[i];
20            break;
21        }
22    }
23    if(result == NULL){
24        if((*vars).varsalloc <= (*vars).varscnt){
25            if(!cs_realloc(&(*vars).names, (*vars).varsalloc * 2 * sizeof(char**), -1)) return "";
26            if(!cs_realloc(&(*vars).values, (*vars).varsalloc * 2 * sizeof(char**), -1)) return "";
27            if(!cs_realloc(&(*vars).vartypes, (*vars).varsalloc * 2 * sizeof(uint8_t*), -1)) return "";
28            (*vars).varsalloc = (*vars).varscnt * 2;
29        }
30        int32_t len = strlen(name) + 1;
31        if(!cs_malloc(&tmp, len * sizeof(char), -1)) return "";
32        memcpy(tmp, name, len);
33        (*vars).names[(*vars).varscnt] = tmp;
34       
35        len = strlen(value) + 1;
36        if(!cs_malloc(&tmp, len * sizeof(char), -1)){
37            free((*vars).names[(*vars).varscnt]);
38            return "";
39        }
40        memcpy(tmp, value, len);
41        (*vars).values[(*vars).varscnt] = tmp;
42        (*vars).vartypes[(*vars).varscnt] = addmode;
43        (*vars).varscnt++;
44    } else {
45        int32_t oldlen = 0, newlen = strlen(value);
46        if(addmode == TPLAPPEND || addmode == TPLAPPENDONCE) oldlen = strlen((*vars).values[i]);
47        if(!cs_realloc(&((*vars).values[i]), (oldlen + newlen + 1) * sizeof(char), -1)) return value;
48        memcpy((*vars).values[i] + oldlen, value, newlen + 1);
49        (*vars).vartypes[i] = addmode;
50    }
51    return tmp;
52}
53
54/* Allows to add a char array which has been allocated by malloc. It will automatically get
55  freed when calling tpl_clear(). Please do NOT free the memory yourself or realloc
56  it after having added the array here! */
57char *tpl_addTmp(struct templatevars *vars, char *value){
58    if(value == NULL) return "";
59    if((*vars).tmpalloc <= (*vars).tmpcnt){     
60        if(!cs_realloc (&(*vars).tmp, (*vars).tmpalloc * 2 * sizeof(char**), -1)) return value;
61        (*vars).tmpalloc = (*vars).tmpcnt * 2;
62    }
63    (*vars).tmp[(*vars).tmpcnt] = value;
64    (*vars).tmpcnt++;
65    return value;
66}
67
68/* Allows to do a dynamic printf without knowing and defining the needed memory size. If you specify
69   varname, the printf-result will be added/appended to the varlist, if varname=NULL it will only be returned.
70   In either case you will always get a reference back which you may freely use (but you should not call
71   free/realloc on this as it will be automatically cleaned!)*/
72char *tpl_printf(struct templatevars *vars, uint8_t addmode, char *varname, char *fmtstring, ...){
73    uint32_t needed;
74    char test[1];
75    va_list argptr;
76
77    va_start(argptr,fmtstring);
78    needed = vsnprintf(test, 1, fmtstring, argptr);
79    va_end(argptr);
80   
81    char *result;
82    if(!cs_malloc(&result, (needed + 1) * sizeof(char), -1)) return "";
83    va_start(argptr,fmtstring);
84    vsnprintf(result, needed + 1, fmtstring, argptr);
85    va_end(argptr);
86
87    if(varname == NULL) tpl_addTmp(vars, result);
88    else {
89        char *tmp = tpl_addVar(vars, addmode, varname, result);
90        free(result);
91        result = tmp;
92    }
93    return result;
94}
95
96/* Returns the value for a name or an empty string if nothing was found. */
97char *tpl_getVar(struct templatevars *vars, char *name){
98    int32_t i;
99    char *result = NULL;
100    for(i = (*vars).varscnt-1; i >= 0; --i){
101        if(strcmp((*vars).names[i], name) == 0){
102            result = (*vars).values[i];
103            break;
104        }
105    }
106    if(result == NULL) return "";
107    else {
108        if((*vars).vartypes[i] == TPLADDONCE || (*vars).vartypes[i] == TPLAPPENDONCE){
109            // This is a one-time-use variable which gets cleaned up automatically after retrieving it
110            if(!cs_malloc(&(*vars).values[i], 1 * sizeof(char), -1)){
111                (*vars).values[i] = result;
112                result[0] = '\0';
113                return result;
114            } else {
115                (*vars).values[i][0] = '\0';
116                return tpl_addTmp(vars, result);
117            }
118        } else return result;
119    }
120}
121
122/* Initializes all variables for a templatevar-structure and returns a pointer to it. Make
123   sure to call tpl_clear() when you are finished or you'll run into a memory leak! */
124struct templatevars *tpl_create(){
125    struct templatevars *vars;
126    if(!cs_malloc(&vars, sizeof(struct templatevars), -1)) return NULL;
127    (*vars).varsalloc = 64;
128    (*vars).varscnt = 0;
129    (*vars).tmpalloc = 64;
130    (*vars).tmpcnt = 0;
131    if(!cs_malloc(&(*vars).names, (*vars).varsalloc * sizeof(char**), -1)){
132        free(vars);
133        return NULL;
134    }
135    if(!cs_malloc(&(*vars).values, (*vars).varsalloc * sizeof(char**), -1)){
136        free((*vars).names);
137        free(vars);
138        return NULL;
139    };
140    if(!cs_malloc(&(*vars).vartypes, (*vars).varsalloc * sizeof(uint8_t*), -1)){
141        free((*vars).names);
142        free((*vars).values);
143        free(vars);
144        return NULL;
145    };
146    if(!cs_malloc(&(*vars).tmp, (*vars).tmpalloc * sizeof(char**), -1)){
147        free((*vars).names);
148        free((*vars).values);
149        free((*vars).vartypes);
150        free(vars);
151        return NULL;
152    };
153    return vars;
154}
155
156/* Clears all allocated memory for the specified templatevar-structure. */
157void tpl_clear(struct templatevars *vars){
158    int32_t i;
159    for(i = (*vars).varscnt-1; i >= 0; --i){
160        free((*vars).names[i]);
161        free((*vars).values[i]);
162    }
163    free((*vars).names);
164    free((*vars).values);
165    free((*vars).vartypes);
166    for(i = (*vars).tmpcnt-1; i >= 0; --i){
167        free((*vars).tmp[i]);
168    }
169    free((*vars).tmp);
170    free(vars);
171}
172
173/* Creates a path to a template file. You need to set the resultsize to the correct size of result. */
174char *tpl_getTplPath(const char *name, const char *path, char *result, uint32_t resultsize){
175    char *pch;
176    if((strlen(path) + strlen(name) + 6) <= resultsize){
177        snprintf(result, resultsize, "%s%s.tpl", path, name);
178        for(pch = result + strlen(path); pch[0] != '\0'; ++pch){
179            if(pch[0] == '/' || pch[0] == '\\') pch[0] = ' ';
180        }
181    } else result[0] = '\0';
182    return result;
183}
184
185/* Returns an unparsed template either from disk or from internal templates.
186   Note: You must free() the result after using it and you may get NULL if an error occured!*/
187char *tpl_getUnparsedTpl(const char* name){
188  int32_t i;
189  int32_t tplcnt = sizeof(tpl)/sizeof(char *);
190  int32_t tplmapcnt = sizeof(tplmap)/sizeof(char *);
191  char *result;
192
193  for(i = 0; i < tplcnt; ++i){
194    if(strcmp(name, tpl[i]) == 0) break;
195  }
196
197  if(strlen(cfg.http_tpl) > 0){
198    char path[255];
199    if(strlen(tpl_getTplPath(name, cfg.http_tpl, path, 255)) > 0 && file_exists(path)){
200            FILE *fp;
201            char buffer[1024];
202            int32_t read, allocated = 1025, size = 0;
203            if(!cs_malloc(&result, allocated * sizeof(char), -1)) return NULL;
204            if((fp = fopen(path,"r"))!=NULL){
205            while((read = fread(&buffer,sizeof(char),1024,fp)) > 0){
206                if(allocated < size + read + 1) {
207                    allocated += size + 1024;
208                    if(!cs_realloc(&result, allocated * sizeof(char), -1)) return NULL;
209                }
210                memcpy(result + size, buffer, read);
211                size += read;
212            }
213            result[size] = '\0';
214            fclose (fp);
215            return result;
216            }
217      }
218  }
219    if(i >= 0 && i < tplmapcnt){
220        int32_t len = (strlen(tplmap[i])) + 1;
221        if(!cs_malloc(&result, len * sizeof(char), -1)) return NULL;
222        memcpy(result, tplmap[i], len);
223    } else {
224        if(!cs_malloc(&result, 1 * sizeof(char), -1)) return NULL;
225        result[0] = '\0';
226  }
227    return result;
228}
229
230/* Returns the specified template with all variables/other templates replaced or an
231   empty string if the template doesn't exist. Do not free the result yourself, it
232   will get automatically cleaned up! */
233char *tpl_getTpl(struct templatevars *vars, const char* name){
234    char *tplorg = tpl_getUnparsedTpl(name);
235    if(!tplorg) return "";
236    char *tplend = tplorg + strlen(tplorg);
237    char *pch, *pch2, *tpl=tplorg;
238    char varname[33];
239
240    int32_t tmp,respos = 0;
241    int32_t allocated = 2 * strlen(tpl) + 1;
242    char *result;
243    if(!cs_malloc(&result, allocated * sizeof(char), -1)) return "";
244
245    while(tpl < tplend){
246        if(tpl[0] == '#' && tpl[1] == '#' && tpl[2] != '#'){
247            pch2 = tpl;
248            pch = tpl + 2;
249            while(pch[0] != '\0' && (pch[0] != '#' || pch[1] != '#')) ++pch;
250            if(pch - pch2 < 32 && pch[0] == '#' && pch[1] == '#'){
251                memcpy(varname, pch2 + 2, pch - pch2 - 2);
252                varname[pch - pch2 - 2] = '\0';
253                if(strncmp(varname, "TPL", 3) == 0){
254                    pch2 = tpl_getTpl(vars, varname + 3);
255                } else {
256                    pch2 = tpl_getVar(vars, varname);
257                }
258                tmp = strlen(pch2);
259                if(tmp + respos + 2 >= allocated){
260                    allocated = tmp + respos + 256;
261                    if(!cs_realloc(&result, allocated * sizeof(char), -1)) return "";
262                }
263                memcpy(result + respos, pch2, tmp);
264                respos += tmp;
265                tpl = pch + 2;
266            }
267        } else {
268            if(respos + 2 >= allocated){
269                allocated = respos + 256;
270                if(!cs_realloc(&result, allocated * sizeof(char), -1)) return "";
271            }
272            result[respos] = tpl[0];
273            ++respos;
274            ++tpl;
275        }
276    }
277    free(tplorg);
278    result[respos] = '\0';
279    tpl_addTmp(vars, result);
280    return result;
281}
282
283/* Saves all templates to the specified paths. Existing files will be overwritten! */
284int32_t tpl_saveIncludedTpls(const char *path){
285    int32_t tplcnt = sizeof(tpl)/sizeof(char *);
286  int32_t tplmapcnt = sizeof(tplmap)/sizeof(char *);
287  int32_t i, cnt = 0;
288  char tmp[256];
289  FILE *fp;
290  for(i = 0; i < tplcnt && i < tplmapcnt; ++i){
291    if(strlen(tpl_getTplPath(tpl[i], path, tmp, 256)) > 0 && (fp = fopen(tmp,"w")) != NULL){
292            fwrite(tplmap[i], sizeof(char), strlen(tplmap[i]), fp);
293            fclose (fp);
294            ++cnt;
295        }
296    }
297    return cnt;
298}
299
300/* Parses a value in an authentication string by removing all quotes/whitespace. Note that the original array is modified. */
301char *parse_auth_value(char *value){
302    char *pch = value;
303    char *pch2;
304    value = strstr(value, "=");
305    if(value != NULL){
306        do{
307            ++value;
308        } while (value[0] == ' ' || value[0] == '"');
309        pch = value;
310        for(pch2 = value + strlen(value) - 1; pch2 >= value && (pch2[0] == ' ' || pch2[0] == '"' || pch2[0] == '\r' || pch2[0] == '\n'); --pch2) pch2[0] = '\0';
311    }
312    return pch;
313}
314
315/* Parses the date out of a "If-Modified-Since"-header. Note that the original string is modified. */
316time_t parse_modifiedsince(char * value){
317    int32_t day = -1, month = -1, year = -1, hour = -1, minutes = -1, seconds = -1;
318    char months[12][4] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"};
319    char *str, *saveptr1 = NULL;
320    time_t modifiedheader = 0;
321    value += 18;
322    // Parse over weekday at beginning...
323    while(value[0] == ' ' && value[0] != '\0') ++value;
324    while(value[0] != ' ' && value[0] != '\0') ++value;
325    // According to http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.3.1 three different timeformats are allowed so we need a bit logic to parse all of them...
326    if(value[0] != '\0'){
327        ++value;
328        for(month = 0; month < 12; ++month){
329            if(strstr(value, months[month])) break;
330        }
331        if(month > 11) month = -1;
332        for (str=strtok_r(value, " ", &saveptr1); str; str=strtok_r(NULL, " ", &saveptr1)){
333            switch(strlen(str)){
334                case 1:
335                case 2:
336                    day = atoi(str);
337                    break;
338               
339                case 4:
340                    if(str[0] != 'G')
341                        year = atoi(str);
342                    break;
343                   
344                case 8:
345                    if(str[2] == ':' && str[5] == ':'){
346                        hour = atoi(str);
347                        minutes = atoi(str + 3);
348                        seconds = atoi(str + 6);
349                    }
350                    break;
351                   
352                case 9:
353                    if(str[2] == '-' && str[6] == '-'){
354                        day = atoi(str);
355                        year = atoi(str + 7) + 2000;
356                    }
357                    break;
358            }
359        }
360        if(day > 0 && day < 32 && month > 0 && year > 0 && year < 9999 && hour > -1 && hour < 24 && minutes > -1 && minutes < 60 && seconds > -1 && seconds < 60){
361            struct tm timeinfo;
362            memset(&timeinfo, 0, sizeof(timeinfo));
363            timeinfo.tm_mday = day;
364            timeinfo.tm_mon = month;
365            timeinfo.tm_year = year - 1900;
366            timeinfo.tm_hour = hour;
367            timeinfo.tm_min = minutes;
368            timeinfo.tm_sec = seconds;
369            modifiedheader = timegm(&timeinfo);
370        }
371    }   
372    return modifiedheader;
373}
374
375/* Calculates the currently valid nonce value and copies it to result. Please note that result needs to be at least (MD5_DIGEST_LENGTH * 2) + 1 large. */
376void calculate_nonce(char *result){
377  char noncetmp[128];
378  unsigned char md5tmp[MD5_DIGEST_LENGTH];
379  snprintf(noncetmp, sizeof(noncetmp), "%d:%s", (int)time(NULL)/AUTHNONCEVALIDSECS, noncekey);
380  char_to_hex(MD5((unsigned char*)noncetmp, strlen(noncetmp), md5tmp), MD5_DIGEST_LENGTH, (unsigned char*)result);
381}
382
383/* Checks if authentication is correct. Returns -1 if not correct, 1 if correct and 2 if nonce isn't valid anymore.
384   Note that authstring will be modified. */
385int32_t check_auth(char *authstring, char *method, char *path, char *expectednonce){
386    int32_t authok = 0, uriok = 0;
387    char *authnonce = "";
388    char *authnc = "";
389    char *authcnonce = "";
390    char *authresponse = "";
391    char *uri = "";
392    char *username = "";
393    char *expectedPassword = cfg.http_pwd;
394    char *pch = authstring + 22;
395    char *pch2;
396    char *saveptr1=NULL;
397
398    for(pch = strtok_r (pch, ",", &saveptr1); pch; pch = strtok_r (NULL, ",", &saveptr1)){
399        pch2 = pch;
400      while(pch2[0] == ' ' && pch2[0] != '\0') ++pch2;
401      if(strncmp(pch2, "nonce", 5) == 0){
402        authnonce=parse_auth_value(pch2);
403      } else if (strncmp(pch2, "nc", 2) == 0){
404        authnc=parse_auth_value(pch2);
405      } else if (strncmp(pch2, "cnonce", 6) == 0){
406        authcnonce=parse_auth_value(pch2);
407      } else if (strncmp(pch2, "response", 8) == 0){
408        authresponse=parse_auth_value(pch2);
409      } else if (strncmp(pch2, "uri", 3) == 0){
410        uri=parse_auth_value(pch2);
411      } else if (strncmp(pch2, "username", 8) == 0){
412        username=parse_auth_value(pch2);
413      }   
414    }
415
416    if(strncmp(uri, path, strlen(path)) == 0) uriok = 1;
417    else {
418        pch2 = uri;
419        for(pch = uri; pch[0] != '\0'; ++pch) {
420            if(pch[0] == '/') pch2 = pch;
421        }
422        if(strncmp(pch2, path, strlen(path)) == 0) uriok = 1;
423    }
424    if(uriok == 1 && strcmp(username, cfg.http_user) == 0){
425        char A1tmp[3 + strlen(username) + strlen(AUTHREALM) + strlen(expectedPassword)];
426        char A1[(MD5_DIGEST_LENGTH * 2) + 1], A2[(MD5_DIGEST_LENGTH * 2) + 1], A3[(MD5_DIGEST_LENGTH * 2) + 1];
427        unsigned char md5tmp[MD5_DIGEST_LENGTH];
428        snprintf(A1tmp, sizeof(A1tmp), "%s:%s:%s", username, AUTHREALM, expectedPassword);
429        char_to_hex(MD5((unsigned char*)A1tmp, strlen(A1tmp), md5tmp), MD5_DIGEST_LENGTH, (unsigned char*)A1);
430       
431        char A2tmp[2 + strlen(method) + strlen(uri)];
432        snprintf(A2tmp, sizeof(A2tmp), "%s:%s", method, uri);       
433        char_to_hex(MD5((unsigned char*)A2tmp, strlen(A2tmp), md5tmp), MD5_DIGEST_LENGTH, (unsigned char*)A2);
434       
435        char A3tmp[10 + strlen(A1) + strlen(A2) + strlen(authnonce) + strlen(authnc) + strlen(authcnonce)];
436        snprintf(A3tmp, sizeof(A3tmp), "%s:%s:%s:%s:auth:%s", A1, authnonce, authnc, authcnonce, A2);
437        char_to_hex(MD5((unsigned char*)A3tmp, strlen(A3tmp), md5tmp), MD5_DIGEST_LENGTH, (unsigned char*)A3);
438       
439        if(strcmp(A3, authresponse) == 0) {
440            if(strcmp(expectednonce, authnonce) == 0) authok = 1;
441            else authok = 2;
442        }
443    }
444    return authok;
445}
446
447int32_t webif_write_raw(char *buf, FILE* f, int32_t len) {
448    errno=0;
449#ifdef WITH_SSL
450    if (ssl_active) {
451        return SSL_write((SSL*)f, buf, len);
452    } else
453#endif
454        return fwrite(buf, 1, len, f);
455}
456
457int32_t webif_write(char *buf, FILE* f) {
458    return webif_write_raw(buf, f, strlen(buf));
459}
460
461int32_t webif_read(char *buf, int32_t num, FILE *f) {
462    errno=0;
463#ifdef WITH_SSL
464    if (ssl_active) {
465        return SSL_read((SSL*)f, buf, num);
466    } else
467#endif
468        return read(fileno(f), buf, num);
469}
470
471void send_headers(FILE *f, int32_t status, char *title, char *extra, char *mime, int32_t cache, int32_t length, char *content, int8_t forcePlain){
472  time_t now;
473  char timebuf[32];
474  char buf[sizeof(PROTOCOL) + sizeof(SERVER) + strlen(title) + (extra == NULL?0:strlen(extra)+2) + (mime == NULL?0:strlen(mime)+2) + 350];
475  char *pos = buf;
476   
477  pos += snprintf(pos, sizeof(buf)-(pos-buf), "%s %d %s\r\n", PROTOCOL, status, title);
478  pos += snprintf(pos, sizeof(buf)-(pos-buf), "Server: %s\r\n", SERVER);
479
480  now = time(NULL);
481  strftime(timebuf, sizeof(timebuf), RFC1123FMT, gmtime(&now));
482  pos += snprintf(pos, sizeof(buf)-(pos-buf), "Date: %s\r\n", timebuf);
483
484    if (extra)
485        pos += snprintf(pos, sizeof(buf)-(pos-buf),"%s\r\n", extra);
486
487    if (mime)
488        pos += snprintf(pos, sizeof(buf)-(pos-buf),"Content-Type: %s\r\n", mime);
489
490    if(status != 304){
491        if(!cache){
492            pos += snprintf(pos, sizeof(buf)-(pos-buf),"Cache-Control: no-store, no-cache, must-revalidate\r\n");
493            pos += snprintf(pos, sizeof(buf)-(pos-buf),"Expires: Sat, 10 Jan 2000 05:00:00 GMT\r\n");
494        } else {
495            pos += snprintf(pos, sizeof(buf)-(pos-buf),"Cache-Control: public, max-age=7200\r\n");
496        }
497        pos += snprintf(pos, sizeof(buf)-(pos-buf),"Content-Length: %d\r\n", length);
498        pos += snprintf(pos, sizeof(buf)-(pos-buf),"Last-Modified: %s\r\n", timebuf);
499        if(content){
500            uint32_t checksum = (uint32_t)crc32(0L, (uchar *)content, length);
501            pos += snprintf(pos, sizeof(buf)-(pos-buf),"ETag: \"%u\"\r\n", checksum==0?1:checksum);
502        }
503    }
504    if(*(int8_t *)pthread_getspecific(getkeepalive))
505        pos += snprintf(pos, sizeof(buf)-(pos-buf), "Connection: Keep-Alive\r\n");
506    else
507        pos += snprintf(pos, sizeof(buf)-(pos-buf), "Connection: close\r\n");
508    pos += snprintf(pos, sizeof(buf)-(pos-buf),"\r\n");
509    if(forcePlain == 1) fwrite(buf, 1, strlen(buf), f);
510    else webif_write(buf, f);
511}
512
513
514void send_error(FILE *f, int32_t status, char *title, char *extra, char *text, int8_t forcePlain){
515    char buf[(2* strlen(title)) + strlen(text) + 128];
516    char *pos = buf;
517    pos += snprintf(pos, sizeof(buf)-(pos-buf), "<HTML><HEAD><TITLE>%d %s</TITLE></HEAD>\r\n", status, title);
518    pos += snprintf(pos, sizeof(buf)-(pos-buf), "<BODY><H4>%d %s</H4>\r\n", status, title);
519    pos += snprintf(pos, sizeof(buf)-(pos-buf), "%s\r\n", text);
520    pos += snprintf(pos, sizeof(buf)-(pos-buf), "</BODY></HTML>\r\n");
521    send_headers(f, status, title, extra, "text/html", 0, strlen(buf), NULL, forcePlain);
522    if(forcePlain == 1) fwrite(buf, 1, strlen(buf), f);
523    else webif_write(buf, f);
524}
525
526void send_error500(FILE *f){
527    send_error(f, 500, "Internal Server Error", NULL, "The server encountered an internal error that prevented it from fulfilling this request.", 0);
528}
529
530void send_header304(FILE *f){
531    send_headers(f, 304, "Not Modified", NULL, NULL, 1, 0, NULL, 0);
532}
533
534/*
535 * function for sending files.
536 */
537void send_file(FILE *f, char *filename, time_t modifiedheader, uint32_t etagheader){
538    int8_t fileno = 0, allocated = 0;
539    int32_t size = 0;
540    char* mimetype = "", *result = "";
541    time_t moddate;
542
543    if (!strcmp(filename, "CSS")){
544        filename = cfg.http_css;
545        mimetype = "text/css";
546        fileno = 1;
547    } else if (!strcmp(filename, "JS")){
548        filename = cfg.http_jscript;
549        mimetype = "text/javascript";
550        fileno = 2;
551    }
552
553    if(strlen(filename) > 0 && file_exists(filename) == 1){
554        struct stat st;     
555        stat(filename, &st);
556        moddate = st.st_mtime;     
557        if(st.st_size > 0){
558            FILE *fp;
559            int32_t read;
560            size = st.st_size;
561            if((fp = fopen(filename, "r"))==NULL) return;
562            if(!cs_malloc(&result, st.st_size + 1, -1)){
563                send_error500(f);
564                fclose(fp);
565                return;
566            }
567            allocated = 1;
568            if((read = fread(result, 1, st.st_size, fp)) != size){
569                result[0] = '\0';
570                size = 0;
571            } else result[read] = '\0';
572            fclose(fp);
573        }
574    } else {
575        moddate = first_client->login;
576        if (fileno == 1){
577            result = CSS;
578        } else if (fileno == 2){
579            result = JSCRIPT;
580        }
581        size = strlen(result);
582    }
583    // We need at least size 1 or keepalive gets problems on some browsers...
584    if(size < 1){
585        if(allocated){
586            free(result);
587            allocated = 0;
588        }
589        result = " ";
590        size = 1;
591    }
592    if((etagheader == 0 && moddate < modifiedheader) || (etagheader > 0 && (uint32_t)crc32(0L, (uchar *)result, size) == etagheader)){
593        send_header304(f);
594    } else {
595        send_headers(f, 200, "OK", NULL, mimetype, 1, size, result, 0);
596        webif_write(result, f);
597    }
598    if(allocated) free(result);
599}
600
601/* Helper function for urldecode.*/
602int32_t x2i(int32_t i){
603    i=toupper(i);
604    i = i - '0';
605    if(i > 9) i = i - 'A' + '9' + 1;
606    return i;
607}
608
609/* Decodes values in a http url. Note: The original value is modified! */
610void urldecode(char *s){
611    int32_t c, c1, n;
612    char *s0,*t;
613    t = s0 = s;
614    n = strlen(s);
615    while(n >0){
616        c = *s++;
617        if(c == '+') c = ' ';
618        else if(c == '%' && n > 2){
619            c = *s++;
620            c1 = c;
621            c = *s++;
622            c = 16*x2i(c1) + x2i(c);
623            n -= 2;
624        }
625        *t++ = c;
626        n--;
627    }
628    *t = 0;
629}
630
631/* Encode values in a http url. Do not call free() or realloc on the returned reference or you will get memory corruption! */
632char *urlencode(struct templatevars *vars, char *str){
633    char buf[strlen(str) * 3 + 1];
634    char *pstr = str, *pbuf = buf;
635    while (*pstr) {
636        if (isalnum(*pstr) || *pstr == '-' || *pstr == '_' || *pstr == '.' || *pstr == '~') *pbuf++ = *pstr;
637        else if (*pstr == ' ') *pbuf++ = '+';
638        else {
639            *pbuf++ = '%';
640            *pbuf++ = to_hex(*pstr >> 4);
641            *pbuf++ = to_hex(*pstr & 15);
642        }
643        ++pstr;
644    }
645    *pbuf = '\0';
646    /* Allocate the needed memory size and store it in the templatevars */
647    if(!cs_malloc(&pbuf, strlen(buf) + 1, -1)) return "";
648    memcpy(pbuf, buf, strlen(buf) + 1);
649    return tpl_addTmp(vars, pbuf);
650}
651
652/* XML-Escapes a char array. The returned reference will be automatically cleaned through the templatevars-mechanism tpl_clear().
653   Do not call free() or realloc on the returned reference or you will get memory corruption! */
654char *xml_encode(struct templatevars *vars, char *chartoencode) {
655    int32_t i, pos = 0, len = strlen(chartoencode);
656    char *result;
657    /* In worst case, every character could get converted to 6 chars (we only support ASCII, for Unicode it would be 7)*/
658    char encoded[len * 6 + 1], buffer[7];
659    for (i = 0; i < len; ++i){
660        switch(chartoencode[i]) {
661            case '&': memcpy(encoded + pos, "&amp;", 5); pos+=5; break;
662            case '<': memcpy(encoded + pos, "&lt;", 4); pos+=4; break;
663            case '>': memcpy(encoded + pos, "&gt;", 4); pos+=4; break;
664            case '"': memcpy(encoded + pos, "&quot;", 6); pos+=6; break;
665            case '\'': memcpy(encoded + pos, "&apos;", 6); pos+=6; break;
666
667            default:
668                if ( (unsigned int)chartoencode[i] < 32 || (unsigned int)chartoencode[i] > 127 ) {
669                    snprintf(buffer, 7, "&#%d;", chartoencode[i] + 256);
670                    memcpy(encoded + pos, buffer, strlen(buffer));
671                    pos+=strlen(buffer);
672
673                } else {
674                    encoded[pos] = chartoencode[i];
675                    ++pos;
676                }
677
678        }
679    }
680    /* Allocate the needed memory size and store it in the templatevars */
681    if(!cs_malloc(&result, pos + 1, -1)) return "";
682    memcpy(result, encoded, pos);
683    result[pos] = '\0';
684    return tpl_addTmp(vars, result);
685}
686
687int32_t b64decode(unsigned char *result){
688    char inalphabet[256], decoder[256];
689    unsigned char alphabet[64] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
690    int32_t i, len = strlen((char *)result), j = 0, bits = 0, char_count = 0;
691   
692    for (i = sizeof(alphabet) - 1; i >= 0; --i) {
693        inalphabet[alphabet[i]] = 1;
694        decoder[alphabet[i]] = i;
695    }
696    for(i = 0; i < len; ++i){
697        if (result[i] == '=') break;
698        if (!inalphabet[result[i]]) continue;
699        bits += decoder[result[i]];
700        ++char_count;
701        if (char_count == 4) {
702            result[j++] = bits >> 16;
703            result[j++] = (bits >> 8) & 0xff;
704            result[j++] = bits & 0xff;
705            bits = 0;
706            char_count = 0;
707        } else {
708            bits <<= 6;
709        }
710    }
711    if (i == len) {
712        if (char_count) {
713            result[j] = '\0';
714            return 0;
715        }
716    } else {
717        switch (char_count) {
718            case 1:
719                result[j] = '\0';
720                return 0;
721            case 2:
722                result[j++] = bits >> 10;
723                result[j] = '\0';
724                break;
725            case 3:
726                result[j++] = bits >> 16;
727                result[j++] = (bits >> 8) & 0xff;
728                result[j] = '\0';
729            break;
730        }
731    }
732    return j;
733}
734
735/* Format a seconds integer to hh:mm:ss or dd hh:mm:ss depending hrs >24 */
736char *sec2timeformat(struct templatevars *vars, int32_t seconds) {
737
738    char *value;
739    if(seconds <= 0)
740        return "00:00:00";
741
742    if(!cs_malloc(&value, 16 * sizeof(char), -1))
743        return "00:00:00";
744
745    int32_t secs = 0, fullmins = 0, mins = 0, fullhours = 0, hours = 0, days = 0;
746
747    secs = seconds % 60;
748    if (seconds > 60) {
749        fullmins = seconds / 60;
750        mins = fullmins % 60;
751        if(fullmins > 60) {
752            fullhours = fullmins / 60;
753            hours = fullhours % 24;
754            days = fullhours / 24;
755        }
756    }
757
758    if(days == 0)
759        snprintf(value, 16, "%02d:%02d:%02d", hours, mins, secs);
760    else
761        snprintf(value, 16, "%02dd %02d:%02d:%02d", days, hours, mins, secs);
762
763    return tpl_addTmp(vars, value);
764}
765
766/* Parse url parameters and save them to params array. The pch pointer is increased to the position where parsing stopped. */
767void parseParams(struct uriparams *params, char *pch) {
768    char *pch2;
769    // parsemode = 1 means parsing next param, parsemode = -1 parsing next
770  //value; pch2 points to the beginning of the currently parsed string, pch is the current position
771    int32_t parsemode = 1;
772
773    pch2=pch;
774    while(pch[0] != '\0') {
775        if((parsemode == 1 && pch[0] == '=') || (parsemode == -1 && pch[0] == '&')) {
776            pch[0] = '\0';
777            urldecode(pch2);
778            if(parsemode == 1) {
779                if(params->paramcount >= MAXGETPARAMS) break;
780                ++params->paramcount;
781                params->params[params->paramcount-1] = pch2;
782            } else {
783                params->values[params->paramcount-1] = pch2;
784            }
785            parsemode = -parsemode;
786            pch2 = pch + 1;
787        }
788        ++pch;
789    }
790    /* last value wasn't processed in the loop yet... */
791    if(parsemode == -1 && params->paramcount <= MAXGETPARAMS) {
792        urldecode(pch2);
793        params->values[params->paramcount-1] = pch2;
794    }
795}
796
797/* Returns the value of the parameter called name or an empty string if it doesn't exist. */
798char *getParam(struct uriparams *params, char *name){
799    int32_t i;
800    for(i=(*params).paramcount-1; i>=0; --i){
801        if(strcmp((*params).params[i], name) == 0) return (*params).values[i];
802    }
803    return "";
804}
805
806struct s_reader *get_reader_by_label(char *lbl){
807    struct s_reader *rdr;
808    LL_ITER itr = ll_iter_create(configured_readers);
809    while((rdr = ll_iter_next(&itr)))
810      if (strcmp(lbl, rdr->label) == 0) break;
811    return rdr;
812}
813
814struct s_client *get_client_by_name(char *name) {
815    struct s_client *cl;
816    for (cl = first_client; cl ; cl = cl->next) {
817        if (strcmp(name, cl->account->usr) == 0)
818            return cl;
819    }
820    return NULL;
821}
822
823struct s_auth *get_account_by_name(char *name) {
824    struct s_auth *account;
825    for (account=cfg.account; (account); account=account->next) {
826        if(strcmp(name, account->usr) == 0)
827            return account;
828    }
829    return NULL;
830}
831
832#ifdef WITH_SSL
833pthread_key_t getssl;
834SSL * cur_ssl(void){
835    return (SSL *) pthread_getspecific(getssl);
836}
837
838/* Locking functions for SSL multithreading */
839static pthread_mutex_t *lock_cs;
840struct CRYPTO_dynlock_value{
841    pthread_mutex_t mutex;
842};
843
844/* function really needs unsigned long to prevent compiler warnings... */
845unsigned long SSL_id_function(void){
846    return ((unsigned long) pthread_self());
847}
848
849void SSL_locking_function(int32_t mode, int32_t type, const char *file, int32_t line){
850    if (mode & CRYPTO_LOCK) {
851        cs_lock(&lock_cs[type]);
852    } else {
853        cs_unlock(&lock_cs[type]);
854    }
855    // just to remove compiler warnings...
856    if(file || line) return;
857}
858
859struct CRYPTO_dynlock_value *SSL_dyn_create_function(const char *file, int32_t line){
860    struct CRYPTO_dynlock_value *l;
861    if(!cs_malloc(&l, sizeof(struct CRYPTO_dynlock_value), -1)) return (NULL);
862        if(pthread_mutex_init(&l->mutex, NULL)) {
863            // Initialization of mutex failed.
864            free(l);
865            return (NULL);
866        }
867    pthread_mutex_init(&l->mutex, NULL);
868    // just to remove compiler warnings...
869        if(file || line) return l;
870    return l;
871}
872
873void SSL_dyn_lock_function(int32_t mode, struct CRYPTO_dynlock_value *l, const char *file, int32_t line){
874    if (mode & CRYPTO_LOCK) {
875        cs_lock(&l->mutex);
876    } else {
877        cs_unlock(&l->mutex);
878    }
879    // just to remove compiler warnings...
880    if(file || line) return;
881}
882
883void SSL_dyn_destroy_function(struct CRYPTO_dynlock_value *l, const char *file, int32_t line){
884    pthread_mutex_destroy(&l->mutex);
885    free(l);
886    // just to remove compiler warnings...
887    if(file || line) return;
888}
889
890/* Init necessary structures for SSL in WebIf*/
891SSL_CTX *SSL_Webif_Init() {
892    SSL_library_init();
893    SSL_load_error_strings();
894    ERR_load_BIO_strings();
895    ERR_load_SSL_strings();
896
897    SSL_CTX *ctx;
898
899    static const char *cs_cert="oscam.pem";
900
901    if (pthread_key_create(&getssl, NULL)) {
902        cs_log("Could not create getssl");
903    }
904   
905    // set locking callbacks for SSL
906    int32_t i, num = CRYPTO_num_locks();
907    lock_cs = (pthread_mutex_t*) OPENSSL_malloc(num * sizeof(pthread_mutex_t));
908   
909    for (i = 0; i < num; ++i) {
910        if(pthread_mutex_init(&lock_cs[i], NULL)){
911            while(--i > 0){
912                pthread_mutex_destroy(&lock_cs[i]);
913                --i;
914            }
915            free(lock_cs);
916            return NULL;
917        };
918    }
919    /* static lock callbacks */ 
920    CRYPTO_set_id_callback(SSL_id_function);
921    CRYPTO_set_locking_callback(SSL_locking_function);
922    /* dynamic lock callbacks */
923    CRYPTO_set_dynlock_create_callback(SSL_dyn_create_function);
924    CRYPTO_set_dynlock_lock_callback(SSL_dyn_lock_function);
925    CRYPTO_set_dynlock_destroy_callback(SSL_dyn_destroy_function); 
926
927    ctx = SSL_CTX_new(SSLv23_server_method());
928
929    char path[128];
930
931    if (cfg.http_cert[0]==0)
932        snprintf(path, sizeof(path), "%s%s", cs_confdir, cs_cert);
933    else
934        cs_strncpy(path, cfg.http_cert, sizeof(path));
935
936    if (!ctx) {
937        ERR_print_errors_fp(stderr);
938        return NULL;
939       }
940
941    if (SSL_CTX_use_certificate_file(ctx, path, SSL_FILETYPE_PEM) <= 0) {
942        ERR_print_errors_fp(stderr);
943        return NULL;
944    }
945
946    if (SSL_CTX_use_PrivateKey_file(ctx, path, SSL_FILETYPE_PEM) <= 0) {
947        ERR_print_errors_fp(stderr);
948        return NULL;
949    }
950
951    if (!SSL_CTX_check_private_key(ctx)) {
952        cs_log("SSL: Private key does not match the certificate public key");
953        return NULL;
954    }
955    cs_log("load ssl certificate file %s", path);
956    return ctx;
957}
958#endif
959#endif
Note: See TracBrowser for help on using the repository browser.