first import of old cilux linux platform code
[cilux] / src / ni / headers.c
1
2 /* -}{----------------------------------------------------------------------- */
3
4 #include <kernelapi.h>
5 #undef  PUBLIC
6 #define PUBLIC EXPORT
7 #include <ni.h>
8
9 /* -}{---- ------------------------------------------------------------------ */
10
11 static k_hashtable* entity_headers;
12
13 /* -}{---- ------------------------------------------------------------------ */
14
15 #define TMPBUFSIZE  4096
16 static char         tmpbuf[TMPBUFSIZE];
17
18 /* -}{----------------------------------------------------------------------- */
19
20 static char* get_request( char* header, k_hashtable*, k_hashtable*);
21 static char* get_response(char* header, k_hashtable*, k_hashtable*);
22 static char* get_val(char** atp);
23 static char* end_of_val(char* at);
24 static void  fix_keepalive(k_hashtable* evt_head);
25 static void  fix_cache_control(k_hashtable* ent_head);
26 static void  fix_uri(k_hashtable* ent_head);
27 static void  fix_subscribe(k_hashtable*, k_hashtable*);
28
29 /* -}{----------------------------------------------------------------------- */
30
31 #define WHITESPACEH  " \t"
32 #define WHITESPACEV  "\012\015"
33 #define WHITESPACE   WHITESPACEH WHITESPACEV
34
35 EXPORT ni_event* ni_get_request_headers(char* header)
36 {
37         k_hashtable* evt_head=k_hashtable_new("vHeaders/get_req_hdrs", 1);
38         k_hashtable* ent_head=k_hashtable_new("nHeaders/get_req_hdrs", 1);
39         k_hashtable_put(ent_head, "", header);
40
41         char* h=get_request(header, evt_head, ent_head);
42         if(!h){
43                 k_hashtable_delete(evt_head);
44                 k_hashtable_delete(ent_head);
45                 return 0;
46         }
47
48         h=ni_get_headers(h, evt_head, ent_head);
49         if(!h){
50                 k_hashtable_delete(evt_head);
51                 k_hashtable_delete(ent_head);
52                 return 0;
53         }
54
55         fix_keepalive(evt_head);
56         fix_cache_control(ent_head);
57         fix_uri(ent_head);
58         fix_subscribe(evt_head, ent_head);
59
60         ni_event* evq=ni_event_new(0, evt_head, ent_head, 0);
61
62         return evq;
63 }
64
65 EXPORT ni_event* ni_get_response_headers(char* header)
66 {
67         k_hashtable* evt_head=k_hashtable_new("vHeaders/get_resp_hdrs", 1);
68         k_hashtable* ent_head=k_hashtable_new("nHeaders/get_resp_hdrs", 1);
69         k_hashtable_put(ent_head, "", header);
70
71         char* h=get_response(header, evt_head, ent_head);
72         if(!h){
73                 k_hashtable_delete(evt_head);
74                 k_hashtable_delete(ent_head);
75                 return 0;
76         }
77
78         h=ni_get_headers(h, evt_head, ent_head);
79         if(!h){
80                 k_hashtable_delete(evt_head);
81                 k_hashtable_delete(ent_head);
82                 return 0;
83         }
84
85         ni_event* evt=ni_event_new(0, evt_head, ent_head, 0);
86
87         return evt;
88 }
89
90 EXPORT char* ni_get_headers(char*        header,
91                              k_hashtable* evt_head,
92                              k_hashtable* ent_head)
93 {
94         char* at=header;
95         char* tag;
96         char* val;
97         while(*at){
98
99                 tag=at;
100                 at=strpbrk(at, WHITESPACE);
101                 if(!at || at==tag) return 0;
102                 char* e=at;
103                 val=get_val(&at);
104                 *e=0;
105
106                 if(strlen(val) > 2048) return 0;
107
108                 k_hashtable* h;
109                 h=k_hashtable_get(entity_headers, tag)? ent_head: evt_head;
110                 if(h){
111                         char* old=k_hashtable_get(h, tag);
112                         if(!old)  k_hashtable_set(h, tag, val);
113                 }
114         }
115         return at;
116 }
117
118 EXPORT void ni_fix_http_headers(k_hashtable* ent_head)
119 {
120         if(k_hashtable_is(ent_head, "Status:", "260")){
121                 char* crn=k_hashtable_get(ent_head,  "Content-Range:");
122                 char* clg=k_hashtable_get(ent_head,  "Content-Length-Given:");
123                 if(crn || clg){
124                         k_hashtable_set(ent_head, "Status:", "206");
125                         k_hashtable_set(ent_head, "Status-Text:", "Partial Content");
126                 }
127                 else{
128                         k_hashtable_set(ent_head, "Status:", "200");
129                         k_hashtable_set(ent_head, "Status-Text:", "OK");
130                 }
131         }
132         k_hashtable_remove(ent_head, "URI:");
133         k_hashtable_remove(ent_head, "Method:");
134         k_hashtable_remove(ent_head, "From:");
135         k_hashtable_remove(ent_head, "Content-Length-Given:");
136         k_hashtable_remove(ent_head, "Last-Modified-Epoch:");
137
138         char* cc=k_hashtable_get(ent_head, "Cache-Control:");
139         if(cc && strstr(cc, "no-cache")){
140                 k_hashtable_set(ent_head, "Pragma:", "no-cache");
141         }
142 }
143
144 EXPORT void ni_fix_ni_headers(k_hashtable* ent_head, int methead)
145 {
146         if(methead && (k_hashtable_is(ent_head, "Status:", "200") ||
147                        k_hashtable_is(ent_head, "Status:", "206")   )){
148
149                 k_hashtable_set(ent_head, "Status:", "260");
150                 k_hashtable_set(ent_head, "Status-Text:", "Headers Only");
151         }
152         k_hashtable_remove(ent_head, "Method:");
153         k_hashtable_remove(ent_head, "Sub-To:");
154         k_hashtable_remove(ent_head, "Via:");
155         k_hashtable_set(   ent_head, "From:", ni_hostname());
156
157         char* uri=k_hashtable_get(ent_head, "URI:");
158         if(*uri=='.'){
159                 snprintf(tmpbuf, TMPBUFSIZE, "%s/%s", ni_hostname(), uri+2);
160                 k_hashtable_put_dup(ent_head, "URI:", tmpbuf);
161         }
162 }
163
164 EXPORT void ni_response(ni_event* evt,
165                          char*      to,
166                          char*      method,
167                          char*      protocol,
168                          char*      connection,
169                          k_channel* chan)
170 {
171         int L=0;
172         snprintf(tmpbuf, TMPBUFSIZE, "%s", k_time_to_rfc_relative(0));
173         k_hashtable_put_dup(evt->evt_head, "Date:",       tmpbuf);
174         k_hashtable_set(    evt->evt_head, "Server:",     k_version);
175         if(connection)
176         k_hashtable_put_dup(evt->evt_head, "Connection:", connection);
177
178         int   status    =k_hashtable_get_int(evt->ent_head, "Status:");
179         char* statustext=k_hashtable_get(    evt->ent_head, "Status-Text:");
180         int   datalength=k_hashtable_get_int(evt->ent_head, "Content-Length:");
181         int   constant  =k_hashtable_is(     evt->ent_head, "CUX:", "C");
182
183         char* buf    =tmpbuf;
184         int   bufsize=TMPBUFSIZE;
185         int   ln=0;
186
187         ln+=snprintf(buf+ln, bufsize-ln, "%s %d %s" CRLF, 
188                                           protocol, status, statustext);
189         if(ln>=bufsize) return;
190
191         static char* exheaders[]={ "Status:", "Status-Text:",
192                                    "Protocol:", "CUX:", 0 };
193
194         ln+=k_hashtable_snprintf_x(evt->evt_head, buf+ln,bufsize-ln, exheaders);
195         if(ln>=bufsize) return;
196
197         ln+=k_hashtable_snprintf_x(evt->ent_head, buf+ln,bufsize-ln, exheaders);
198         if(ln>=bufsize) return;
199
200         ln+=snprintf(buf+ln, bufsize-ln, CRLF);
201         if(ln>=bufsize) return;
202
203         char* head=k_strdup(buf);
204         if(L) k_log_out("Actual response headers:\n%s", head);
205         k_channel_send(chan, head, ln, FREE_ON_SENT);
206
207         if(evt->entity && datalength){
208                 k_channel_send(chan, evt->entity, datalength, !constant);
209         }
210         else{
211                 datalength=0;
212                 if(!constant) k_free(evt->entity);
213         }
214         k_log_out("%s %s %s %d %d", to, method, evt->uri, status, datalength);
215 }
216
217 EXPORT void ni_request(ni_event* evt, char* to, char* method, k_channel* chan)
218 {
219         k_hashtable* sub=evt->ent_head;
220
221         int ln=0;
222         int bufsize=TMPBUFSIZE;
223
224         ln+=snprintf(tmpbuf+ln, bufsize-ln, "%s //%s ni/0.5" CRLF, method, to);
225         if(ln>=bufsize) return;
226
227         ln+=k_hashtable_snprintf(sub, tmpbuf+ln, bufsize-ln);
228         if(ln>=bufsize) return;
229
230         ln+=snprintf(tmpbuf+ln, bufsize-ln, CRLF);
231         if(ln>=bufsize) return;
232
233         char* head=k_strdup(tmpbuf);
234         if(0) k_log_out("Actual request headers:\n%s", head);
235         k_channel_send(chan, head, ln, FREE_ON_SENT);
236 }
237
238 /* -}{----------------------------------------------------------------------- */
239
240 char* get_request(char* header, k_hashtable* evt_head, k_hashtable* ent_head)
241 {
242         char* at=header;
243         char* method=at;
244         at=strpbrk(at, WHITESPACEH);
245         if(!at) return 0;
246         *at++=0;
247
248         k_hashtable_set(ent_head, "Method:", method);
249
250         char* file=0;
251         char* host=0;
252         if(strcmp(method, "PING")){
253
254                 at+=strspn(at, WHITESPACEH);
255                 file=at;
256                 at=strpbrk(at, WHITESPACEH);
257                 if(!at) return 0;
258                 *at++=0;
259                 if(strlen(file) > 1024 ) return 0;
260
261                 if(!strncmp(file, "http://", 7)){
262                         char* s=file+7;
263                         if(!*s || *s=='/') return 0;
264                         char* e=strchr(s, '/');
265                         if(!e) return 0;
266                         host=s;
267                         *e++=0;
268                         file=e;
269                 }
270                 else
271                 if(*file=='/') file++;
272                 else return 0;
273         
274                 if(*file=='/'){
275                         char* s=file+1;
276                         if(!*s || *s=='/') return 0;
277                         char* e=strchr(s, '/');
278                         if(!e) return 0;
279                         host=s;
280                         *e++=0;
281                         file=e;
282                 }
283         }
284         if(file) k_hashtable_set(evt_head, "File:", file);
285         if(host) k_hashtable_set(evt_head, "Host:", host);
286
287         at+=strspn(at, WHITESPACEH);
288         char* protocol=at;
289         at=strpbrk(at, WHITESPACE);
290         if(!at) at=protocol+strlen(protocol);
291         else   *at++=0;
292
293         k_hashtable_set(evt_head, "Protocol:", protocol);
294
295         at+=strspn(at, WHITESPACE);
296         return at;
297 }
298
299 char* get_response(char* header, k_hashtable* evt_head, k_hashtable* ent_head)
300 {
301         char* at=header;
302         char* protocol=at;
303         at=strpbrk(at, WHITESPACEH);
304         if(!at) return 0;
305         *at++=0;
306
307         k_hashtable_set(evt_head, "Protocol:", protocol);
308
309         at+=strspn(at, WHITESPACEH);
310         char* status=at;
311         at=strpbrk(at, WHITESPACEH);
312         if(!at) return 0;
313         *at++=0;
314
315         at+=strspn(at, WHITESPACEH);
316         char* statustext=at;
317         at=strpbrk(at, WHITESPACEV);
318         if(!at) return 0;
319         *at++=0;
320
321         k_hashtable_set(ent_head, "Status:",      status);
322         k_hashtable_set(ent_head, "Status-Text:", statustext);
323
324         at+=strspn(at, WHITESPACE);
325         return at;
326 }
327
328 char* get_val(char** atp)
329 {
330         char* at=*atp;
331         char* val=at;
332
333         val+=strspn(val, WHITESPACEH);
334         val+=strspn(val, WHITESPACEV);
335
336         char* eov=end_of_val(at);
337         if(eov<val) val=eov;
338         if(*eov) *eov++=0;
339
340         *atp=eov+strspn(eov, WHITESPACEV);
341         return val;
342 }
343
344 char* end_of_val(char* at)
345 {
346         char* eov;
347         do{
348                 char* v=strpbrk(at, WHITESPACEV);
349                 if(!v) return at+strlen(at);
350                 eov=v;
351                 at=v+strspn(v, WHITESPACEV);
352
353         }while(strspn(at, WHITESPACEH));
354
355         return eov;
356 }
357
358 void fix_keepalive(k_hashtable* evt_head)
359 {
360         int keepalive=0;
361         int is11=k_hashtable_is( evt_head, "Protocol:",   "HTTP/1.1");
362         int isps=k_hashtable_isn(evt_head, "Protocol:",   "ni/", 4);
363         int iska=k_hashtable_isi(evt_head, "Connection:", "Keep-Alive");
364         int iscl=k_hashtable_isi(evt_head, "Connection:", "close");
365         if((!is11 && iska) || (is11 && !iscl) || isps){
366                 keepalive=1;
367         }
368         if(0) k_log_out("is11=%d iska=%d iscl=%d isps=%d ka=%d",
369                          is11,   iska,   iscl,   isps,   keepalive);
370         k_hashtable_set(evt_head, "Connection:", 
371                                   keepalive? "Keep-Alive": "close");
372 }
373
374 void fix_cache_control(k_hashtable* ent_head)
375 {
376         char* cachec=k_hashtable_get(    ent_head, "Cache-Control:");
377         char* pragma=k_hashtable_extract(ent_head, "Pragma:");
378         if(!cachec && pragma && strstr(pragma, "no-cache")){
379                 k_hashtable_set(         ent_head, "Cache-Control:","no-cache");
380         }
381 }
382
383 void fix_uri(k_hashtable* ent_head)
384 {
385         char* uri=k_hashtable_get(ent_head, "URI:");
386         if(!uri) return;
387         char* nihostname=ni_hostname();
388         int l=strlen(nihostname);
389         if(strncmp(uri, nihostname, l)) return;
390         snprintf(tmpbuf, TMPBUFSIZE, ".%s", uri+l);
391         k_hashtable_put_dup(ent_head, "URI:", tmpbuf);
392 }
393
394 void fix_subscribe(k_hashtable* evt_head, k_hashtable* ent_head)
395 {
396         char* host=k_hashtable_extract(evt_head, "Host:");
397         char* file=k_hashtable_extract(evt_head, "File:");
398         if(host){
399                 k_string_url_decode(host);
400                 int localhostdns=!strcmp( host, "localhost") ||
401                                  !strncmp(host, "localhost:", 10);
402                 int localhostni=!strcmp(host, ni_hostname());
403                 char* lastdot=strrchr(host, '.');
404                 int dotdotnumber=lastdot && atoi(lastdot+1) >0;
405                 if(localhostdns || localhostni || dotdotnumber){
406                         host=".";
407                 }
408         }
409         else{
410                 host=".";
411         }
412         if(file){
413                 k_string_url_decode(file);
414                 snprintf(tmpbuf, TMPBUFSIZE, "%s/%s", host, file);
415                 k_hashtable_put_dup(ent_head, "Sub-To:", tmpbuf);
416                 k_hashtable_set(    ent_head, "Sub-Type:", "Original");
417         }
418 }
419
420 void init_headers(void)
421 {
422         entity_headers  =k_hashtable_new("Entity Headers", 1);
423
424         k_hashtable_set(entity_headers, "URI:", (void*)1);
425         k_hashtable_set(entity_headers, "From:", (void*)1);
426         k_hashtable_set(entity_headers, "To:", (void*)1);
427         k_hashtable_set(entity_headers, "Via:", (void*)1);
428         k_hashtable_set(entity_headers, "Sub-To:", (void*)1);
429         k_hashtable_set(entity_headers, "Sub-Type:", (void*)1);
430         k_hashtable_set(entity_headers, "Method:", (void*)1);
431         k_hashtable_set(entity_headers, "Status:", (void*)1);
432         k_hashtable_set(entity_headers, "Status-Text:", (void*)1);
433         k_hashtable_set(entity_headers, "Last-Modified-Epoch:", (void*)1);
434         k_hashtable_set(entity_headers, "CUX:", (void*)1);
435
436         k_hashtable_set(entity_headers, "Range:", (void*)1);
437         k_hashtable_set(entity_headers, "If-Modified-Since:", (void*)1);
438         k_hashtable_set(entity_headers, "If-None-Match:", (void*)1);
439         k_hashtable_set(entity_headers, "Cache-Control:", (void*)1);
440         k_hashtable_set(entity_headers, "Pragma:", (void*)1);
441
442         k_hashtable_set(entity_headers, "Content-Length:", (void*)1);
443         k_hashtable_set(entity_headers, "Content-Type:", (void*)1);
444         k_hashtable_set(entity_headers, "Content-Encoding:", (void*)1);
445         k_hashtable_set(entity_headers, "Content-Location:", (void*)1);
446         k_hashtable_set(entity_headers, "Content-MD5:", (void*)1);
447         k_hashtable_set(entity_headers, "Content-Language:", (void*)1);
448         k_hashtable_set(entity_headers, "Content-Range:", (void*)1);
449         k_hashtable_set(entity_headers, "Last-Modified:", (void*)1);
450         k_hashtable_set(entity_headers, "ETag:", (void*)1);
451         k_hashtable_set(entity_headers, "Expires:", (void*)1);
452
453         k_hashtable_set(entity_headers, "Allow:", (void*)1);
454 }
455
456 void drop_entity_headers(k_hashtable* ent_head)
457 {
458         k_hashtable_remove(ent_head, "Content-Length:");
459         k_hashtable_remove(ent_head, "Content-Range:");
460         k_hashtable_remove(ent_head, "Content-Type:");
461         k_hashtable_remove(ent_head, "Content-Encoding:");
462         k_hashtable_remove(ent_head, "Content-Location:");
463         k_hashtable_remove(ent_head, "Last-Modified:");
464 #ifdef DO_THESE_ONES_TOO
465         k_hashtable_remove(ent_head, "Allow:");
466         k_hashtable_remove(ent_head, "Content-Language:");
467         k_hashtable_remove(ent_head, "Content-MD5:");
468         k_hashtable_remove(ent_head, "Expires:");
469 #endif
470 }
471
472 void fill_headers(k_hashtable* ent_head,
473                   char*  status,
474                   char*  statustext,
475                   char*  uri,
476                   time_t modifitime,
477                   int    datalength,
478                   char*  mimetype,
479                   char*  encoding,
480                   int    nocache)
481 {
482         if(status){
483                 k_hashtable_put_dup(ent_head, "Status:",      status);
484                 k_hashtable_put_dup(ent_head, "Status-Text:", statustext);
485         }
486         if(uri){
487                 k_hashtable_put_dup(ent_head, "URI:", uri);
488         }
489         if(modifitime>=0){
490                 snprintf(tmpbuf, TMPBUFSIZE, "%s", k_time_to_rfc(modifitime));
491                 k_hashtable_put_dup(ent_head, "Last-Modified:", tmpbuf);
492         }
493         if(datalength>=0){
494                 snprintf(tmpbuf, TMPBUFSIZE, "%d", datalength);
495                 k_hashtable_put_dup(ent_head, "Content-Length:", tmpbuf);
496         }
497         if(mimetype){
498                 k_hashtable_put_dup(ent_head, "Content-Type:",   mimetype);
499         }
500         if(encoding){
501                 k_hashtable_put_dup(ent_head, "Content-Encoding:", encoding);
502         }
503         if(nocache==1){
504                 k_hashtable_put_dup(ent_head, "Cache-Control:",
505                                               "no-cache,no-store");
506         }
507         else
508         if(nocache==0){
509                 long maxage=31449600;
510                 snprintf(tmpbuf, TMPBUFSIZE, "max-age=%ld", maxage);
511                 char* maxages=k_time_to_rfc_relative(maxage);
512                 k_hashtable_put_dup(ent_head, "Cache-Control:", tmpbuf);
513                 k_hashtable_put_dup(ent_head, "Expires:", maxages);
514         }
515         else
516         if(nocache== -1){
517                 k_hashtable_remove(ent_head, "Cache-Control:");
518                 k_hashtable_remove(ent_head, "Expires:");
519         }
520 }
521
522 /* -}{---- ------------------------------------------------------------------ */
523