Big renaming
[cilux] / src / on / headers.c
diff --git a/src/on/headers.c b/src/on/headers.c
new file mode 100644 (file)
index 0000000..13fc3e0
--- /dev/null
@@ -0,0 +1,523 @@
+
+/* -}{----------------------------------------------------------------------- */
+
+#include <kernelapi.h>
+#undef  PUBLIC
+#define PUBLIC EXPORT
+#include <notification.h>
+
+/* -}{---- ------------------------------------------------------------------ */
+
+static k_hashtable* entity_headers;
+
+/* -}{---- ------------------------------------------------------------------ */
+
+#define TMPBUFSIZE  4096
+static char         tmpbuf[TMPBUFSIZE];
+
+/* -}{----------------------------------------------------------------------- */
+
+static char* get_request( char* header, k_hashtable*, k_hashtable*);
+static char* get_response(char* header, k_hashtable*, k_hashtable*);
+static char* get_val(char** atp);
+static char* end_of_val(char* at);
+static void  fix_keepalive(k_hashtable* evt_head);
+static void  fix_cache_control(k_hashtable* ent_head);
+static void  fix_uri(k_hashtable* ent_head);
+static void  fix_subscribe(k_hashtable*, k_hashtable*);
+
+/* -}{----------------------------------------------------------------------- */
+
+#define WHITESPACEH  " \t"
+#define WHITESPACEV  "\012\015"
+#define WHITESPACE   WHITESPACEH WHITESPACEV
+
+EXPORT ni_event* ni_get_request_headers(char* header)
+{
+       k_hashtable* evt_head=k_hashtable_new("vHeaders/get_req_hdrs", 1);
+       k_hashtable* ent_head=k_hashtable_new("nHeaders/get_req_hdrs", 1);
+       k_hashtable_put(ent_head, "", header);
+
+       char* h=get_request(header, evt_head, ent_head);
+       if(!h){
+               k_hashtable_delete(evt_head);
+               k_hashtable_delete(ent_head);
+               return 0;
+       }
+
+       h=ni_get_headers(h, evt_head, ent_head);
+       if(!h){
+               k_hashtable_delete(evt_head);
+               k_hashtable_delete(ent_head);
+               return 0;
+       }
+
+       fix_keepalive(evt_head);
+       fix_cache_control(ent_head);
+       fix_uri(ent_head);
+       fix_subscribe(evt_head, ent_head);
+
+       ni_event* evq=ni_event_new(0, evt_head, ent_head, 0);
+
+       return evq;
+}
+
+EXPORT ni_event* ni_get_response_headers(char* header)
+{
+       k_hashtable* evt_head=k_hashtable_new("vHeaders/get_resp_hdrs", 1);
+       k_hashtable* ent_head=k_hashtable_new("nHeaders/get_resp_hdrs", 1);
+       k_hashtable_put(ent_head, "", header);
+
+       char* h=get_response(header, evt_head, ent_head);
+       if(!h){
+               k_hashtable_delete(evt_head);
+               k_hashtable_delete(ent_head);
+               return 0;
+       }
+
+       h=ni_get_headers(h, evt_head, ent_head);
+       if(!h){
+               k_hashtable_delete(evt_head);
+               k_hashtable_delete(ent_head);
+               return 0;
+       }
+
+       ni_event* evt=ni_event_new(0, evt_head, ent_head, 0);
+
+       return evt;
+}
+
+EXPORT char* ni_get_headers(char*        header,
+                             k_hashtable* evt_head,
+                             k_hashtable* ent_head)
+{
+       char* at=header;
+       char* tag;
+       char* val;
+       while(*at){
+
+               tag=at;
+               at=strpbrk(at, WHITESPACE);
+               if(!at || at==tag) return 0;
+               char* e=at;
+               val=get_val(&at);
+               *e=0;
+
+               if(strlen(val) > 2048) return 0;
+
+               k_hashtable* h;
+               h=k_hashtable_get(entity_headers, tag)? ent_head: evt_head;
+               if(h){
+                       char* old=k_hashtable_get(h, tag);
+                       if(!old)  k_hashtable_set(h, tag, val);
+               }
+       }
+       return at;
+}
+
+EXPORT void ni_fix_http_headers(k_hashtable* ent_head)
+{
+       if(k_hashtable_is(ent_head, "Status:", "260")){
+               char* crn=k_hashtable_get(ent_head,  "Content-Range:");
+               char* clg=k_hashtable_get(ent_head,  "Content-Length-Given:");
+               if(crn || clg){
+                       k_hashtable_set(ent_head, "Status:", "206");
+                       k_hashtable_set(ent_head, "Status-Text:", "Partial Content");
+               }
+               else{
+                       k_hashtable_set(ent_head, "Status:", "200");
+                       k_hashtable_set(ent_head, "Status-Text:", "OK");
+               }
+       }
+       k_hashtable_remove(ent_head, "URI:");
+       k_hashtable_remove(ent_head, "Method:");
+       k_hashtable_remove(ent_head, "From:");
+       k_hashtable_remove(ent_head, "Content-Length-Given:");
+       k_hashtable_remove(ent_head, "Last-Modified-Epoch:");
+
+       char* cc=k_hashtable_get(ent_head, "Cache-Control:");
+       if(cc && strstr(cc, "no-cache")){
+               k_hashtable_set(ent_head, "Pragma:", "no-cache");
+       }
+}
+
+EXPORT void ni_fix_ni_headers(k_hashtable* ent_head, int methead)
+{
+       if(methead && (k_hashtable_is(ent_head, "Status:", "200") ||
+                      k_hashtable_is(ent_head, "Status:", "206")   )){
+
+               k_hashtable_set(ent_head, "Status:", "260");
+               k_hashtable_set(ent_head, "Status-Text:", "Headers Only");
+       }
+       k_hashtable_remove(ent_head, "Method:");
+       k_hashtable_remove(ent_head, "Sub-To:");
+       k_hashtable_remove(ent_head, "Via:");
+       k_hashtable_set(   ent_head, "From:", ni_hostname());
+
+       char* uri=k_hashtable_get(ent_head, "URI:");
+       if(*uri=='.'){
+               snprintf(tmpbuf, TMPBUFSIZE, "%s/%s", ni_hostname(), uri+2);
+               k_hashtable_put_dup(ent_head, "URI:", tmpbuf);
+       }
+}
+
+EXPORT void ni_response(ni_event* evt,
+                         char*      to,
+                         char*      method,
+                         char*      protocol,
+                         char*      connection,
+                         k_channel* chan)
+{
+       int L=0;
+       snprintf(tmpbuf, TMPBUFSIZE, "%s", k_time_to_rfc_relative(0));
+       k_hashtable_put_dup(evt->evt_head, "Date:",       tmpbuf);
+       k_hashtable_set(    evt->evt_head, "Server:",     k_version);
+       if(connection)
+       k_hashtable_put_dup(evt->evt_head, "Connection:", connection);
+
+       int   status    =k_hashtable_get_int(evt->ent_head, "Status:");
+       char* statustext=k_hashtable_get(    evt->ent_head, "Status-Text:");
+       int   datalength=k_hashtable_get_int(evt->ent_head, "Content-Length:");
+       int   constant  =k_hashtable_is(     evt->ent_head, "CUX:", "C");
+
+       char* buf    =tmpbuf;
+       int   bufsize=TMPBUFSIZE;
+       int   ln=0;
+
+       ln+=snprintf(buf+ln, bufsize-ln, "%s %d %s" CRLF, 
+                                         protocol, status, statustext);
+       if(ln>=bufsize) return;
+
+       static char* exheaders[]={ "Status:", "Status-Text:",
+                                  "Protocol:", "CUX:", 0 };
+
+       ln+=k_hashtable_snprintf_x(evt->evt_head, buf+ln,bufsize-ln, exheaders);
+       if(ln>=bufsize) return;
+
+       ln+=k_hashtable_snprintf_x(evt->ent_head, buf+ln,bufsize-ln, exheaders);
+       if(ln>=bufsize) return;
+
+       ln+=snprintf(buf+ln, bufsize-ln, CRLF);
+       if(ln>=bufsize) return;
+
+       char* head=k_strdup(buf);
+       if(L) k_log_out("Actual response headers:\n%s", head);
+       k_channel_send(chan, head, ln, FREE_ON_SENT);
+
+       if(evt->entity && datalength){
+               k_channel_send(chan, evt->entity, datalength, !constant);
+       }
+       else{
+               datalength=0;
+               if(!constant) k_free(evt->entity);
+       }
+       k_log_out("%s %s %s %d %d", to, method, evt->uri, status, datalength);
+}
+
+EXPORT void ni_request(ni_event* evt, char* to, char* method, k_channel* chan)
+{
+       k_hashtable* sub=evt->ent_head;
+
+       int ln=0;
+       int bufsize=TMPBUFSIZE;
+
+       ln+=snprintf(tmpbuf+ln, bufsize-ln, "%s //%s OP/0.5" CRLF, method, to);
+       if(ln>=bufsize) return;
+
+       ln+=k_hashtable_snprintf(sub, tmpbuf+ln, bufsize-ln);
+       if(ln>=bufsize) return;
+
+       ln+=snprintf(tmpbuf+ln, bufsize-ln, CRLF);
+       if(ln>=bufsize) return;
+
+       char* head=k_strdup(tmpbuf);
+       if(0) k_log_out("Actual request headers:\n%s", head);
+       k_channel_send(chan, head, ln, FREE_ON_SENT);
+}
+
+/* -}{----------------------------------------------------------------------- */
+
+char* get_request(char* header, k_hashtable* evt_head, k_hashtable* ent_head)
+{
+       char* at=header;
+       char* method=at;
+       at=strpbrk(at, WHITESPACEH);
+       if(!at) return 0;
+       *at++=0;
+
+       k_hashtable_set(ent_head, "Method:", method);
+
+       char* file=0;
+       char* host=0;
+       if(strcmp(method, "PING")){
+
+               at+=strspn(at, WHITESPACEH);
+               file=at;
+               at=strpbrk(at, WHITESPACEH);
+               if(!at) return 0;
+               *at++=0;
+               if(strlen(file) > 1024 ) return 0;
+
+               if(!strncmp(file, "http://", 7)){
+                       char* s=file+7;
+                       if(!*s || *s=='/') return 0;
+                       char* e=strchr(s, '/');
+                       if(!e) return 0;
+                       host=s;
+                       *e++=0;
+                       file=e;
+               }
+               else
+               if(*file=='/') file++;
+               else return 0;
+       
+               if(*file=='/'){
+                       char* s=file+1;
+                       if(!*s || *s=='/') return 0;
+                       char* e=strchr(s, '/');
+                       if(!e) return 0;
+                       host=s;
+                       *e++=0;
+                       file=e;
+               }
+       }
+       if(file) k_hashtable_set(evt_head, "File:", file);
+       if(host) k_hashtable_set(evt_head, "Host:", host);
+
+       at+=strspn(at, WHITESPACEH);
+       char* protocol=at;
+       at=strpbrk(at, WHITESPACE);
+       if(!at) at=protocol+strlen(protocol);
+       else   *at++=0;
+
+       k_hashtable_set(evt_head, "Protocol:", protocol);
+
+       at+=strspn(at, WHITESPACE);
+       return at;
+}
+
+char* get_response(char* header, k_hashtable* evt_head, k_hashtable* ent_head)
+{
+       char* at=header;
+       char* protocol=at;
+       at=strpbrk(at, WHITESPACEH);
+       if(!at) return 0;
+       *at++=0;
+
+       k_hashtable_set(evt_head, "Protocol:", protocol);
+
+       at+=strspn(at, WHITESPACEH);
+       char* status=at;
+       at=strpbrk(at, WHITESPACEH);
+       if(!at) return 0;
+       *at++=0;
+
+       at+=strspn(at, WHITESPACEH);
+       char* statustext=at;
+       at=strpbrk(at, WHITESPACEV);
+       if(!at) return 0;
+       *at++=0;
+
+       k_hashtable_set(ent_head, "Status:",      status);
+       k_hashtable_set(ent_head, "Status-Text:", statustext);
+
+       at+=strspn(at, WHITESPACE);
+       return at;
+}
+
+char* get_val(char** atp)
+{
+       char* at=*atp;
+       char* val=at;
+
+       val+=strspn(val, WHITESPACEH);
+       val+=strspn(val, WHITESPACEV);
+
+       char* eov=end_of_val(at);
+       if(eov<val) val=eov;
+       if(*eov) *eov++=0;
+
+       *atp=eov+strspn(eov, WHITESPACEV);
+       return val;
+}
+
+char* end_of_val(char* at)
+{
+       char* eov;
+       do{
+               char* v=strpbrk(at, WHITESPACEV);
+               if(!v) return at+strlen(at);
+               eov=v;
+               at=v+strspn(v, WHITESPACEV);
+
+       }while(strspn(at, WHITESPACEH));
+
+       return eov;
+}
+
+void fix_keepalive(k_hashtable* evt_head)
+{
+       int keepalive=0;
+       int is11=k_hashtable_is( evt_head, "Protocol:",   "HTTP/1.1");
+       int isps=k_hashtable_isn(evt_head, "Protocol:",   "OP/", 4);
+       int iska=k_hashtable_isi(evt_head, "Connection:", "Keep-Alive");
+       int iscl=k_hashtable_isi(evt_head, "Connection:", "close");
+       if((!is11 && iska) || (is11 && !iscl) || isps){
+               keepalive=1;
+       }
+       if(0) k_log_out("is11=%d iska=%d iscl=%d isps=%d ka=%d",
+                        is11,   iska,   iscl,   isps,   keepalive);
+       k_hashtable_set(evt_head, "Connection:", 
+                                 keepalive? "Keep-Alive": "close");
+}
+
+void fix_cache_control(k_hashtable* ent_head)
+{
+       char* cachec=k_hashtable_get(    ent_head, "Cache-Control:");
+       char* pragma=k_hashtable_extract(ent_head, "Pragma:");
+       if(!cachec && pragma && strstr(pragma, "no-cache")){
+               k_hashtable_set(         ent_head, "Cache-Control:","no-cache");
+       }
+}
+
+void fix_uri(k_hashtable* ent_head)
+{
+       char* uri=k_hashtable_get(ent_head, "URI:");
+       if(!uri) return;
+       char* nihostname=ni_hostname();
+       int l=strlen(nihostname);
+       if(strncmp(uri, nihostname, l)) return;
+       snprintf(tmpbuf, TMPBUFSIZE, ".%s", uri+l);
+       k_hashtable_put_dup(ent_head, "URI:", tmpbuf);
+}
+
+void fix_subscribe(k_hashtable* evt_head, k_hashtable* ent_head)
+{
+       char* host=k_hashtable_extract(evt_head, "Host:");
+       char* file=k_hashtable_extract(evt_head, "File:");
+       if(host){
+               k_string_url_decode(host);
+               int localhostdns=!strcmp( host, "localhost") ||
+                                !strncmp(host, "localhost:", 10);
+               int localhostni=!strcmp(host, ni_hostname());
+               char* lastdot=strrchr(host, '.');
+               int dotdotnumber=lastdot && atoi(lastdot+1) >0;
+               if(localhostdns || localhostni || dotdotnumber){
+                       host=".";
+               }
+       }
+       else{
+               host=".";
+       }
+       if(file){
+               k_string_url_decode(file);
+               snprintf(tmpbuf, TMPBUFSIZE, "%s/%s", host, file);
+               k_hashtable_put_dup(ent_head, "Sub-To:", tmpbuf);
+               k_hashtable_set(    ent_head, "Sub-Type:", "Original");
+       }
+}
+
+void init_headers(void)
+{
+       entity_headers  =k_hashtable_new("Entity Headers", 1);
+
+       k_hashtable_set(entity_headers, "URI:", (void*)1);
+       k_hashtable_set(entity_headers, "From:", (void*)1);
+       k_hashtable_set(entity_headers, "To:", (void*)1);
+       k_hashtable_set(entity_headers, "Via:", (void*)1);
+       k_hashtable_set(entity_headers, "Sub-To:", (void*)1);
+       k_hashtable_set(entity_headers, "Sub-Type:", (void*)1);
+       k_hashtable_set(entity_headers, "Method:", (void*)1);
+       k_hashtable_set(entity_headers, "Status:", (void*)1);
+       k_hashtable_set(entity_headers, "Status-Text:", (void*)1);
+       k_hashtable_set(entity_headers, "Last-Modified-Epoch:", (void*)1);
+       k_hashtable_set(entity_headers, "CUX:", (void*)1);
+
+       k_hashtable_set(entity_headers, "Range:", (void*)1);
+       k_hashtable_set(entity_headers, "If-Modified-Since:", (void*)1);
+       k_hashtable_set(entity_headers, "If-None-Match:", (void*)1);
+       k_hashtable_set(entity_headers, "Cache-Control:", (void*)1);
+       k_hashtable_set(entity_headers, "Pragma:", (void*)1);
+
+       k_hashtable_set(entity_headers, "Content-Length:", (void*)1);
+       k_hashtable_set(entity_headers, "Content-Type:", (void*)1);
+       k_hashtable_set(entity_headers, "Content-Encoding:", (void*)1);
+       k_hashtable_set(entity_headers, "Content-Location:", (void*)1);
+       k_hashtable_set(entity_headers, "Content-MD5:", (void*)1);
+       k_hashtable_set(entity_headers, "Content-Language:", (void*)1);
+       k_hashtable_set(entity_headers, "Content-Range:", (void*)1);
+       k_hashtable_set(entity_headers, "Last-Modified:", (void*)1);
+       k_hashtable_set(entity_headers, "ETag:", (void*)1);
+       k_hashtable_set(entity_headers, "Expires:", (void*)1);
+
+       k_hashtable_set(entity_headers, "Allow:", (void*)1);
+}
+
+void drop_entity_headers(k_hashtable* ent_head)
+{
+       k_hashtable_remove(ent_head, "Content-Length:");
+       k_hashtable_remove(ent_head, "Content-Range:");
+       k_hashtable_remove(ent_head, "Content-Type:");
+       k_hashtable_remove(ent_head, "Content-Encoding:");
+       k_hashtable_remove(ent_head, "Content-Location:");
+       k_hashtable_remove(ent_head, "Last-Modified:");
+#ifdef DO_THESE_ONES_TOO
+       k_hashtable_remove(ent_head, "Allow:");
+       k_hashtable_remove(ent_head, "Content-Language:");
+       k_hashtable_remove(ent_head, "Content-MD5:");
+       k_hashtable_remove(ent_head, "Expires:");
+#endif
+}
+
+void fill_headers(k_hashtable* ent_head,
+                  char*  status,
+                  char*  statustext,
+                  char*  uri,
+                  time_t modifitime,
+                  int    datalength,
+                  char*  mimetype,
+                  char*  encoding,
+                  int    nocache)
+{
+       if(status){
+               k_hashtable_put_dup(ent_head, "Status:",      status);
+               k_hashtable_put_dup(ent_head, "Status-Text:", statustext);
+       }
+       if(uri){
+               k_hashtable_put_dup(ent_head, "URI:", uri);
+       }
+       if(modifitime>=0){
+               snprintf(tmpbuf, TMPBUFSIZE, "%s", k_time_to_rfc(modifitime));
+               k_hashtable_put_dup(ent_head, "Last-Modified:", tmpbuf);
+       }
+       if(datalength>=0){
+               snprintf(tmpbuf, TMPBUFSIZE, "%d", datalength);
+               k_hashtable_put_dup(ent_head, "Content-Length:", tmpbuf);
+       }
+       if(mimetype){
+               k_hashtable_put_dup(ent_head, "Content-Type:",   mimetype);
+       }
+       if(encoding){
+               k_hashtable_put_dup(ent_head, "Content-Encoding:", encoding);
+       }
+       if(nocache==1){
+               k_hashtable_put_dup(ent_head, "Cache-Control:",
+                                             "no-cache,no-store");
+       }
+       else
+       if(nocache==0){
+               long maxage=31449600;
+               snprintf(tmpbuf, TMPBUFSIZE, "max-age=%ld", maxage);
+               char* maxages=k_time_to_rfc_relative(maxage);
+               k_hashtable_put_dup(ent_head, "Cache-Control:", tmpbuf);
+               k_hashtable_put_dup(ent_head, "Expires:", maxages);
+       }
+       else
+       if(nocache== -1){
+               k_hashtable_remove(ent_head, "Cache-Control:");
+               k_hashtable_remove(ent_head, "Expires:");
+       }
+}
+
+/* -}{---- ------------------------------------------------------------------ */
+