+
+/* -}{----------------------------------------------------------------------- */
+
+#include <kernelapi.h>
+#include <notification.h>
+
+/* -}{----------------------------------------------------------------------- */
+
+extern void ensure_self_sub(ni_event* evq);
+
+/* -}{----------------------------------------------------------------------- */
+
+#define TMPBUFSIZE 4096
+static char tmpbuf[TMPBUFSIZE];
+
+/* -}{----------------------------------------------------------------------- */
+
+static void cache_read(char*, char*, char*, int, k_stat, void*);
+static void entity_written(char*, char*, char*, int, k_stat, void*);
+static void write_headers(char* path, ni_resource* res);
+static void headers_written(char*, char*, char*, int, k_stat, void*);
+
+
+/* -}{----------------------------------------------------------------------- */
+
+char* make_cache_path(char* uri)
+{
+ char* path;
+ size_t l=strlen(uri);
+ if(l>100){ k_log_out("URI too long to save: %s", uri); return 0; }
+ if(*(uri+l-1)!='/'){
+ path=k_strdup(uri);
+ }
+ else{
+ path=k_malloc(l+1+10);
+ sprintf(path, "%sindex.html", uri);
+ }
+ return path;
+}
+
+void look_in_file_cache(ni_event* evq)
+{
+ k_hashtable* sub=evq->ent_head;
+ char* uri=k_hashtable_get(sub, "Sub-To:");
+ size_t l=strlen(uri);
+
+ if(l>100){ ensure_self_sub(evq); return; }
+
+ char hdr[128];
+ if(*(uri+l-1)!='/') sprintf(hdr, "%s.hdr", uri);
+ else sprintf(hdr, "%sindex.html.hdr", uri);
+
+ if(strstr(hdr, "..")){ ensure_self_sub(evq); return; }
+ if(strchr(hdr, '?' )){ ensure_self_sub(evq); return; }
+
+ char* c=strchr(hdr, ':');
+ char* e=strchr(hdr, '/');
+ if(c && c< e) *c='-';
+
+ k_file_read(".", k_strdup(hdr), USE_MALL, 0, cache_read, evq);
+}
+
+void save_in_file_cache(ni_resource* res)
+{
+ char* uri =res->uri;
+ char* path =make_cache_path(uri); if(!path) return;
+ k_hashtable* ent_head=res->ent_head;
+ void* data =res->entity;
+ int partial =k_hashtable_is( ent_head, "Status:", "206");
+ size_t size =k_hashtable_get_int(ent_head, "Content-Length:");
+ char* conrange=k_hashtable_get( ent_head, "Content-Range:");
+ int constant=k_hashtable_is( ent_head, "CUX:", "C");
+
+ if(!data){
+ write_headers(path, res);
+ }
+ else
+ if(constant){
+ int ok=k_file_sync(data, size);
+ if(!ok){
+ k_log_err("Failed to write (sync mmap): %s", uri);
+ k_free(path);
+ return;
+ }
+ write_headers(path, res);
+ }
+ else{
+ if(partial){
+ k_log_out("save_in_file_cache partial %s", conrange);
+ if(!conrange || strncmp(conrange, "0-", 2)){
+ k_log_err("not saving partial");
+ k_free(path);
+ return;
+ }
+ size=atoi(conrange+2);
+ }
+ k_log_out("save_in_file_cache updateable: %d bytes", size);
+ k_file_write(".", path, data, size, entity_written, res);
+ }
+}
+
+/* -}{----------------------------------------------------------------------- */
+
+void entity_written(char* basedir,
+ char* path,
+ char* data,
+ int usedmmap,
+ k_stat kstat,
+ void* context)
+{
+ if(data) k_log_out("File cache entity written: %s", path);
+ else{
+ k_log_err("Failed to write entity: %s", path);
+ k_free(path);
+ return;
+ }
+ ni_resource* res=context;
+ write_headers(path, res);
+}
+
+void write_headers(char* path, ni_resource* res)
+{
+ snprintf(tmpbuf, TMPBUFSIZE, "%s.hdr", path);
+ k_free(path); path=k_strdup(tmpbuf);
+ k_hashtable_snprintf(res->ent_head, tmpbuf, TMPBUFSIZE);
+ k_file_write(".", path, tmpbuf, strlen(tmpbuf), headers_written, 0);
+}
+
+void headers_written(char* basedir,
+ char* path,
+ char* data,
+ int usedmmap,
+ k_stat kstat,
+ void* context)
+{
+ if(data) k_log_out("File cache headers written: %s", path);
+ else k_log_err("Failed to write headers: %s", path);
+ k_free(path);
+}
+
+void cache_read(char* basedir,
+ char* path,
+ char* data,
+ int usedmmap,
+ k_stat kstat,
+ void* context)
+{
+ if(!data) k_log_out("no cache of %s", path);
+ else k_log_out("file cache read: %d %s", kstat.size, path);
+ char* e=strstr(path, ".hdr");
+ if(e){
+ ni_event* evq=context;
+ if(!data){ k_free(path); ensure_self_sub(evq); return; }
+
+ char* header=data;
+ *(header+kstat.size-2)=0;
+ k_hashtable* ent_head=k_hashtable_new("nHeaders/cache_read", 1);
+ k_hashtable_put(ent_head, "", header);
+ if(!ni_get_headers(header, 0, ent_head)){
+ k_hashtable_delete(ent_head);
+ k_free(path);
+ ensure_self_sub(evq);
+ return;
+ }
+ ni_event_delete(evq);
+ *e=0;
+ ni_event* evt=ni_event_new(0, 0, ent_head, 0);
+ int constant=k_hashtable_is(ent_head, "CUX:", "C");
+ size_t m=constant? USE_MMAP: USE_MALL;
+ k_file_read(".", path, m, 0, cache_read, evt);
+ return;
+ }
+ ni_event* evt=context;
+ k_free(path);
+ evt->entity=data;
+ ni_event_show(evt, "File Cache Found:");
+ k_event_post("on", evt);
+}
+
+/* -}{----------------------------------------------------------------------- */
+
+