Big renaming
[cilux] / src / on / notification.c
diff --git a/src/on/notification.c b/src/on/notification.c
new file mode 100644 (file)
index 0000000..970b1cb
--- /dev/null
@@ -0,0 +1,824 @@
+
+/* -}{----------------------------------------------------------------------- */
+
+#include <kernelapi.h>
+#undef  PUBLIC
+#define PUBLIC EXPORT
+#include <notification.h>
+
+/* -}{---- ------------------------------------------------------------------ */
+
+EXPORT n_object* n_object_new(char* s)
+{
+    return 0;
+}
+
+EXPORT void n_commit(n_object* o)
+{
+}
+
+EXPORT n_object* n_see(n_object* o, char* uid)
+{
+    return 0;
+}
+
+EXPORT char* n_to_string(n_object* o)
+{
+    return 0;
+}
+
+EXPORT char* n_uid(n_object* o)
+{
+    return 0;
+}
+
+EXPORT char* n_header(n_object* o, char* name)
+{
+    return 0;
+}
+
+EXPORT k_hashtable* n_headers(n_object* o)
+{
+    return 0;
+}
+
+EXPORT k_hashtable* n_content(n_object* o)
+{
+    return 0;
+}
+
+/* -}{---- ------------------------------------------------------------------ */
+
+#define TMPBUFSIZE  4096
+static char         tmpbuf[TMPBUFSIZE];
+static char*        hostname;
+static k_hashtable* resources;
+
+/* -}{---- ------------------------------------------------------------------ */
+
+typedef struct ni_driver{
+       char*                name;
+       ni_handles_resource handles_resource;
+       ni_sync_resource    sync_resource;
+} ni_driver;
+
+/* -}{---- From headers.c --------------------------------------------------- */
+
+extern void  init_headers(void);
+extern void  drop_entity_headers(k_hashtable* ent_head);
+extern void  fill_headers(k_hashtable* ent_head,
+                          char*  status,
+                          char*  statustext,
+                          char*  uri,
+                          time_t modifitime,
+                          int    datalength,
+                          char*  mimetype,
+                          char*  encoding,
+                          int    nocache);
+
+/* -}{---- ------------------------------------------------------------------ */
+
+static void          incoming_request(ni_event* evq);
+static ni_resource* ensure_res(char* pub);
+static void          ensure_sub_entry(k_hashtable* ent_head, k_hashtable* sub);
+static void          ensure_pub_entry(k_hashtable* ent_head, k_hashtable* sub);
+static k_hashtable* get_this(k_hashtable*, char*, k_hashtable*);
+static k_hashtable* get_using(k_hashtable* curr, char* tag, char* val);
+static void          post_to_driver(char* pub, ni_event* evq);
+static void          incoming_resource(ni_event* evt);
+static void    test_pub_tos(ni_resource* res, ni_event* evt, int entityok);
+static int     satisfiable(k_hashtable*, ni_resource*, ni_event*, int);
+static void    update_pubcache(int, k_hashtable**, k_hashtable*, k_hashtable*);
+static int     sub_less(k_hashtable* sub1, k_hashtable* sub2);
+static void    fix_via_subs(k_hashtable* evteh, k_hashtable* reseh);
+static int     sub_ok(k_hashtable* sub);
+static int     range_ok(char* range, char* conrange);
+static void    merge_non_entity_headers(k_hashtable* reseh, k_hashtable* evteh);
+static void    merge_entity_range(ni_resource* res, ni_event* evt);
+static void    respond(   k_hashtable* sub, ni_resource* res, ni_event* evt);
+static void    respond_ok(k_hashtable* sub, ni_event* evv);
+static void    respond_nf(k_hashtable* sub, ni_event* evv);
+static void*   entity_to_octets(k_hashtable* ent_head, void* entity);
+static char*       handled_by(char* pub);
+static void        call_sync_resource(ni_resource* res);
+static ni_driver* ni_driver_new(char*                name,
+                                  ni_handles_resource handles_resource,
+                                  ni_sync_resource    sync_resource);
+
+/* -}{---- ------------------------------------------------------------------ */
+
+EXPORT int on_module_loaded(void)
+{
+       resources=k_hashtable_new("Resources", 0);
+
+       init_headers();
+
+       k_log_out("ON initialised");
+       return 1;
+}
+
+EXPORT void on_module_tick(void)
+{
+}
+
+EXPORT int on_module_event(void* data)
+{
+       ni_event* evt=data;
+
+       if(!k_hashtable_get(evt->ent_head, "Status:")){
+
+               incoming_request(evt);
+       }
+       else{
+               incoming_resource(evt);
+       }
+       return 1;
+}
+
+/* -}{---- ------------------------------------------------------------------ */
+
+void incoming_request(ni_event* evq)
+{
+       int L=1;
+       if(L) ni_event_show(evq, "ON got request event");
+
+       k_hashtable* sub=evq->ent_head;
+       char* uri  =k_hashtable_get(sub, "URI:");
+       char* pub  =k_hashtable_get(sub, "Sub-To:"); if(!pub) return;
+       int   styor=k_hashtable_isi(sub, "Sub-Type:", "Original");
+       char* via  =k_hashtable_get(sub, "Via:");
+       char* from =k_hashtable_get(sub, "From:");
+       if(L) k_log_out("------------------ %s", pub);
+
+       int nocache=k_hashtable_isi(sub, "Cache-Control:", "no-cache");
+       if(nocache){
+               k_hashtable_remove( sub, "Cache-Control:");
+               k_hashtable_set(    sub, "If-Modified-Since:", "0");
+       }
+
+       if(!from){
+               ni_resource* ses=k_hashtable_get(resources, uri);
+               if(ses) ensure_sub_entry(ses->ent_head, sub);
+               if(ses) if(L) ni_resource_show(ses, "Subscribing Resource:");
+               if(styor && via){
+                       post_to_driver(pub, evq);
+                       return;
+               }
+       }
+
+       ni_resource* res=ensure_res(pub);
+       if(L) ni_resource_show(res, "Publishing Resource:");
+
+       k_hashtable* pubcache=0;
+       int sat=satisfiable(sub, res, 0, 0);
+       update_pubcache(sat, &pubcache, res->ent_head, sub);
+       if(sat){
+               if(L) k_log_out("Memory cache hit for %s", pub);
+               respond(k_hashtable_dup(sub), res, 0);
+               ni_event_delete(evq);
+       }
+       else{
+               ensure_pub_entry(res->ent_head, sub);
+               if(L) ni_resource_show(res, "Pending Resource:");
+               if(pubcache){
+                       evq->ent_head=pubcache;
+                       post_to_driver(pub, evq);
+                       k_hashtable_delete(sub);
+               }
+               else{
+                       if(L) k_log_out("In-progress Pub-Cache sufficient");
+                       ni_event_delete(evq);
+               }
+       }
+}
+
+ni_resource* ensure_res(char* pub)
+{
+       ni_resource* res=k_hashtable_get(resources, pub);
+       if(!res){
+               res=ni_resource_new(pub, k_hashtable_new("resHeaders", 1), 0);
+               k_hashtable_set(resources, pub, res);
+       }
+       return res;
+}
+
+void ensure_sub_entry(k_hashtable* ent_head, k_hashtable* sub)
+{
+       char* tag="Sub-To:";
+       k_hashtable* sup=get_this(ent_head, tag, sub);
+       if(!sup){
+               k_hashtable* sup=k_hashtable_dup(sub);
+               char* uri=k_hashtable_extract(sup, "Sub-To:");
+               k_hashtable_put(              sup, "URI:", uri);
+               k_hashtable_remove(           sup, "Sub-Type:");
+               k_hashtable_sub(ent_head, tag, sup);
+       }
+       else{
+               k_hashtable_remove(sup, "Range:");
+               k_hashtable_remove(sup, "Content-Range:");
+               k_hashtable_remove(sup, "Status:");
+               k_hashtable_remove(sup, "Status-Cache:");
+               char* mth=k_strdup(k_hashtable_get(sub, "Method:"));
+               char* via=k_strdup(k_hashtable_get(sub, "Via:"));
+               char* ims=k_strdup(k_hashtable_get(sub, "If-Modified-Since:"));
+               char* rng=k_strdup(k_hashtable_get(sub, "Range:"));
+               if(mth) k_hashtable_put(sup, "Method:",            mth);
+               if(via) k_hashtable_put(sup, "Via:",               via);
+               if(ims) k_hashtable_put(sup, "If-Modified-Since:", ims);
+               if(rng) k_hashtable_put(sup, "Range:",             rng);
+       }
+}
+
+void ensure_pub_entry(k_hashtable* ent_head, k_hashtable* sub)
+{
+       char* tag="Pub-To:";
+       k_hashtable* sup=get_this(ent_head, tag, sub);
+       if(!sup){
+               k_hashtable* sup=k_hashtable_dup(sub);
+               k_hashtable_remove(sup, "Sub-To:");
+               k_hashtable_remove(sup, "Sub-Type:");
+               k_hashtable_sub(ent_head, tag, sup);
+       }
+       else {
+               k_hashtable_remove(sup, "Method:");
+               k_hashtable_remove(sup, "If-Modified-Since:");
+               k_hashtable_remove(sup, "Cache-Control:");
+               k_hashtable_remove(sup, "Update:");
+               char* mth=k_strdup(k_hashtable_get(sub, "Method:"));
+               char* ims=k_strdup(k_hashtable_get(sub, "If-Modified-Since:"));
+               char* ccn=k_strdup(k_hashtable_get(sub, "Cache-Control:"));
+               char* upd=k_strdup(k_hashtable_get(sub, "Update:"));
+               if(mth) k_hashtable_put(sup, "Method:",            mth);
+               if(ims) k_hashtable_put(sup, "If-Modified-Since:", ims);
+               if(ccn) k_hashtable_put(sup, "Cache-Control:",     ccn);
+               if(upd) k_hashtable_put(sup, "Update:",            upd);
+       }
+}
+
+k_hashtable* get_this(k_hashtable* ent_head, char* pubsub, k_hashtable* try)
+{
+       k_hashtable* curr=k_hashtable_get(ent_head, pubsub);
+       if(!curr) return 0;
+
+       char* tag;
+       char* val;
+
+       tag="From:";
+       val=k_hashtable_get(try, tag);
+       if(val) return get_using(curr, tag, val);
+
+       tag="URI:";
+       val=k_hashtable_get(try, tag);
+       if(val) return get_using(curr, tag, val);
+
+       return 0;
+}
+
+k_hashtable* get_using(k_hashtable* curr, char* tag, char* val)
+{
+       k_hashtable* c;
+       for(c=curr; c; c=c->next) if(k_hashtable_is(c, tag, val)) return c;
+       return c;
+}
+
+/* -}{---- ------------------------------------------------------------------ */
+
+void incoming_resource(ni_event* evt)
+{
+       int L=0;
+       if(L) ni_event_show(evt, "ON got resource incoming event");
+
+       ni_resource* res=k_hashtable_get(resources, evt->uri);
+       int fullconst=res && k_hashtable_is(res->ent_head, "Status:", "200") &&
+                            k_hashtable_is(res->ent_head, "CUX:",    "C");
+
+       if(fullconst){
+               k_log_err("Resource is complete and Constant");
+               ni_event_delete(evt);
+               return;
+       }
+       if(!res) res=ensure_res(evt->uri);
+
+       k_hashtable* reseh=res->ent_head;
+       k_hashtable* evteh=evt->ent_head;
+       int okfull  =k_hashtable_is(evteh, "Status:", "200");
+       int partial =k_hashtable_is(evteh, "Status:", "206");
+       int headonly=k_hashtable_is(evteh, "Status:", "260");/*
+       int updated =k_hashtable_is(evteh, "Status:", "266");*/
+       int notmod  =k_hashtable_is(evteh, "Status:", "304");
+       int notfound=k_hashtable_is(evteh, "Status:", "404");
+
+       int entityok= (okfull || partial || notmod);
+       int entityev=!(headonly || notmod || notfound);
+       int entityin=!!evt->entity;
+       int entityon=!!res->entity;
+
+       if(!entityev){
+       }
+       if(okfull || !entityon){
+               k_hashtable_merge(reseh, evteh);
+               if(okfull) k_hashtable_remove(reseh, "Content-Range:");
+       }
+       else{
+               merge_non_entity_headers(reseh, evteh);
+       }
+
+       fix_via_subs(evteh, reseh);
+
+       if(entityin){
+               if(okfull || (partial && !entityon)){
+                       if(res->entity!=evt->entity){
+                               if(!k_hashtable_is(reseh, "CUX:", "C")){
+                                       k_free(res->entity);
+                               }
+                               res->entity=evt->entity;
+                       }
+               }
+               else
+               if(partial && entityon){
+                       merge_entity_range(res, evt);
+               }
+       }
+
+       if(L) ni_resource_show(res, "updated resource - sync and try pubs:");
+
+       call_sync_resource(res);
+
+       test_pub_tos(res, evt, entityok);
+
+       evt->entity=0;
+       ni_event_delete(evt);
+
+       if(L) ni_resource_show(res, "resource after ON:");
+}
+
+void fix_via_subs(k_hashtable* evteh, k_hashtable* reseh)
+{
+       char* from=k_hashtable_get(evteh, "From:");
+       if(!from) return;
+       k_hashtable* subs=k_hashtable_get(reseh, "Sub-To:");
+       k_hashtable* sub;
+       for(sub=subs; sub; sub=sub->next){
+               if(!k_hashtable_is(sub, "Via:", from)) continue;
+               int   st=k_hashtable_get_int(evteh, "Status:");
+               char* cr=k_hashtable_get_dup(evteh, "Content-Range:");
+               if(st!=304) k_hashtable_put_int(sub, "Status:", st);
+               if(st==200) k_hashtable_remove( sub, "Content-Range:");
+               else
+               if(cr)          k_hashtable_put(sub, "Content-Range:", cr);
+               if(sub_ok(sub)) k_hashtable_set(sub, "Status-Cache:", "OK");
+       }
+}
+
+void test_pub_tos(ni_resource* res, ni_event* evt, int entityok)
+{
+       k_hashtable* keeps=0;
+       k_hashtable* sub=k_hashtable_extract(res->ent_head, "Pub-To:");
+       while(sub){
+               k_hashtable* subnext=sub->next;
+               sub->next=0;
+               int keep=1;
+               int sat=satisfiable(sub, res, evt, entityok);
+               update_pubcache(sat, 0, res->ent_head, sub);
+               if(sat){
+                       respond(k_hashtable_dup(sub), res, evt);
+                       if(!k_hashtable_get(sub, "Update:")) keep=0;
+               }
+               if(keep){
+                       sub->next=keeps;
+                       keeps=sub;
+               }
+               else{
+                       k_hashtable_delete(sub);
+               }
+               sub=subnext;
+       }
+       sub=keeps;
+       while(sub){
+               k_hashtable* subnext=sub->next;
+               k_hashtable_sub(res->ent_head, "Pub-To:", sub);
+               sub=subnext;
+       }
+}
+
+int satisfiable(k_hashtable*  sub,
+                ni_resource* res,
+                ni_event*    evt,
+                int           entityok)
+{
+       int L=0;
+       int   get  =k_hashtable_is( sub, "Method:", "GET");
+       int   head =k_hashtable_is( sub, "Method:", "HEAD");
+       int   dosub=k_hashtable_is( sub, "Method:", "SUB");
+       int   unsub=k_hashtable_is( sub, "Method:", "UNSUB");
+       int   full=!k_hashtable_isi(sub, "Cache-Control:", "no-full");
+       int   updt =k_hashtable_isi(sub, "Update:", "changes");
+       char* ims  =k_hashtable_get(sub, "If-Modified-Since:");
+       char* range=k_hashtable_get(sub, "Range:");
+
+       int getsub  =get || dosub;
+       int getrange=getsub && range;
+
+       k_hashtable* reseh=res->ent_head;
+       int   gotall=    k_hashtable_is( reseh, "Status:", "200");
+       int   gotrange=  k_hashtable_is( reseh, "Status:", "206");
+       int   gothead=   k_hashtable_is( reseh, "Status:", "260");
+       int   gotupdated=evt && 
+                        k_hashtable_is(evt->ent_head, "Status:", "266");
+       int   notfound=  k_hashtable_is( reseh, "Status:", "404");
+       int   constant=  k_hashtable_is( reseh, "CUX:",    "C"  );
+       char* conrange=  k_hashtable_get(reseh, "Content-Range:");
+
+       if(unsub)    return 0;
+       if(notfound) return 1;
+
+       int gotany=(gothead || gotrange || gotall);
+       int snapok=(constant || !ims || entityok);
+
+       int sat=
+          (full && 
+           ((getsub   && gotall                                && snapok) ||
+            (getrange && gotrange && range_ok(range, conrange) && snapok) ||
+            (head     && gotany                                         )   ))
+             ||
+           (updt &&
+            ((dosub    && gotupdated)));
+
+       if(L) k_log_out("satisfiable %d", sat);
+       return sat;
+}
+
+void update_pubcache(int           sat,
+                     k_hashtable** pubcachep,
+                     k_hashtable*  reseh,
+                     k_hashtable*  sub)
+{
+       if(!sat){ if(pubcachep){
+               k_hashtable* pubcache;
+               pubcache=k_hashtable_get(reseh, "Pub-Cache:");
+               if(!pubcache || sub_less(pubcache, sub)){
+                       if(!pubcache){
+                               pubcache=k_hashtable_dup(sub);
+                               k_hashtable_sub(reseh, "Pub-Cache:", pubcache);
+                       }
+                       else{
+                               k_hashtable_merge(pubcache, sub);
+                       }
+                       k_hashtable_remove(pubcache, "URI:");
+                       k_hashtable_remove(pubcache, "Sub-Type:");
+                       *pubcachep=k_hashtable_dup(pubcache);
+                       k_hashtable_set(*pubcachep, "Sub-Type:", "Cache");
+               }
+       }}
+       else{ if(!pubcachep){
+               k_hashtable* pubcache;
+               pubcache=k_hashtable_get(reseh, "Pub-Cache:");
+               if(!sub_less(sub, pubcache)){
+                       k_hashtable_remove(pubcache, "Method:");
+                       k_hashtable_remove(pubcache, "If-Modified-Since:");
+               }
+       }}
+}
+
+int sub_ok(k_hashtable* sub)
+{
+       char* gotresp =k_hashtable_get(sub, "Status:");
+       if(!gotresp) return 0;
+       int   notfound=k_hashtable_is( sub, "Status:", "404");
+       if(notfound) return 0;
+       int   gotall  =k_hashtable_is( sub, "Status:", "200");
+       int   gotrange=k_hashtable_is( sub, "Status:", "206");
+       int   gothead= k_hashtable_is( sub, "Status:", "260");
+       int   doget   =k_hashtable_is( sub, "Method:", "GET");
+       int   dohead  =k_hashtable_is( sub, "Method:", "HEAD");
+       int   dosub   =k_hashtable_is( sub, "Method:", "SUB");
+       char* range   =k_hashtable_get(sub, "Range:");
+       char* conrange=k_hashtable_get(sub, "Content-Range:");
+
+       int dogetsub=doget || dosub;
+       int dogetrange=dogetsub && range;
+       int gotany  =(gothead || gotrange || gotall);
+
+       return (dogetsub   &&  gotall                               ) ||
+              (dogetrange &&  gotrange && range_ok(range, conrange)) ||
+              (dohead     &&  gotany                               );
+}
+
+int sub_less(k_hashtable* sub1, k_hashtable* sub2)
+{
+       int L=0;
+       if(L) k_log_out("sub_less sub1:");
+       if(L) k_hashtable_show_chars(sub1);
+       if(L) k_log_out("sub_less sub2:");
+       if(L) k_hashtable_show_chars(sub2);
+       char* mth1 =k_hashtable_get(sub1, "Method:");
+       int   head1=k_hashtable_is( sub1, "Method:", "HEAD");
+       int   get2 =k_hashtable_is( sub2, "Method:", "GET");
+       char* ims1 =k_hashtable_get(sub1, "If-Modified-Since:");
+       char* ims2 =k_hashtable_get(sub2, "If-Modified-Since:");
+       int   full=!k_hashtable_isi(sub2, "Cache-Control:", "no-full");
+       return full && ((!mth1) || (head1 && get2) || (!ims1 && ims2));
+}
+
+int range_ok(char* range, char* conrange)
+{
+       k_log_out("range_ok not implemented: %s vs %s", range, conrange);
+       return 0;
+}
+
+void merge_non_entity_headers(k_hashtable* reseh, k_hashtable* evteh)
+{
+}
+
+void merge_entity_range(ni_resource* res, ni_event* evt)
+{
+       k_log_out("merge_entity_range not implemented yet");
+}
+
+/* -}{---- ------------------------------------------------------------------ */
+
+void respond(k_hashtable* sub, ni_resource* res, ni_event* evt)
+{
+       int updated=evt && k_hashtable_is(evt->ent_head, "Status:", "266");
+       ni_event* evv=updated? ni_event_dup(evt): 
+                               ni_res_to_evt(res);
+       int L=0;
+       if(L) ni_event_show(evv, "respond");
+
+       k_hashtable_remove(evv->ent_head, "Permit:");
+       k_hashtable_remove(evv->ent_head, "Sub-To:");
+       k_hashtable_remove(evv->ent_head, "Pub-To:");
+       k_hashtable_remove(evv->ent_head, "Pub-Cache:");
+       k_hashtable_sub(   evv->ent_head, "Pub-To:", sub);
+
+       int nf;
+       nf=k_hashtable_is( evv->ent_head, "Status:", "404");
+       if(!nf) respond_ok(sub, evv);
+       else    respond_nf(sub, evv);
+}
+
+void respond_ok(k_hashtable* sub, ni_event* evv)
+{
+       char* status=0;
+       char* statustext=0;
+       int   datalength= -1;
+       int   nocache=0;
+       k_hashtable* ent_head=evv->ent_head;
+
+       time_t modifitime=k_hashtable_get_int(ent_head, "Last-Modified-Epoch:");
+       char*  modistring=k_hashtable_get(    ent_head, "Last-Modified:");
+       if(!modifitime){
+               modifitime=k_time_from_rfc(modistring);
+       }
+       char*  imss=k_hashtable_get(sub, "If-Modified-Since:");
+       time_t imst=k_time_from_rfc(imss);
+
+       if(modifitime== -1 || imst== -1 || modifitime > imst){
+               if(k_hashtable_is(sub, "Method:", "HEAD")){
+                       evv->entity=0;
+               }
+               if(!k_hashtable_is(ent_head, "CUX:", "C")){
+                       evv->entity=entity_to_octets(ent_head, evv->entity);
+                       nocache=1;
+               }
+       }
+       else{
+               status="304";
+               statustext="Not Modified";
+               modifitime= -1;
+               nocache= -1;
+               evv->entity=0;
+               drop_entity_headers(ent_head);
+       }
+
+       fill_headers(ent_head, status, statustext, 0,
+                    modistring? -1: modifitime,
+                    datalength, 0, 0, nocache);
+
+       char*  pub =k_hashtable_get(sub, "URI:");
+       int L=0;
+       if(L) ni_event_show(evv, "respond_ok");
+       post_to_driver(pub, evv);
+}
+
+void respond_nf(k_hashtable* sub, ni_event* evv)
+{
+       fill_headers(evv->ent_head, "404", "File Not Found", 0, -1, 0, 0, 0, 1);
+       char*  pub =k_hashtable_get(sub, "URI:");
+       post_to_driver(pub, evv);
+}
+
+void post_to_driver(char* pub, ni_event* evq)
+{
+       char* driver=handled_by(pub);
+       if(0) k_log_out("Passing %s on to %s", pub, driver);
+       if(0) ni_event_show(evq, "Post to Driver:");
+       k_event_post(driver, evq);
+}
+
+void* entity_to_octets(k_hashtable* ent_head, void* entity)
+{
+       if(!entity) return 0;
+       size_t size=k_hashtable_get_int(ent_head, "Content-Length:");
+       return k_memdup(entity, size);
+}
+
+/* -}{---- ------------------------------------------------------------------ */
+
+EXPORT char* ni_hostname()
+{
+       return hostname? hostname: "not set";
+}
+
+EXPORT void ni_hostname_set(char* name)
+{
+       k_free(hostname);
+       hostname=k_strdup(name);
+}
+
+/* -}{---- ------------------------------------------------------------------ */
+
+static ni_driver* opdriver;
+static ni_driver* moddriver;
+
+char* handled_by(char* pub)
+{
+       if(moddriver->handles_resource(pub)) return moddriver->name;
+       return "op";
+}
+
+void call_sync_resource(ni_resource* res)
+{
+       if(moddriver->handles_resource(res->uri)) moddriver->sync_resource(res);
+       else                                      opdriver->sync_resource(res);
+}
+
+ni_driver* ni_driver_new(char*                name, 
+                           ni_handles_resource handles_resource,
+                           ni_sync_resource    sync_resource)
+{
+       ni_driver* driver=k_malloc(sizeof(ni_driver));
+       driver->name            =name;
+       driver->handles_resource=handles_resource;
+       driver->sync_resource   =sync_resource;
+       return driver;
+}
+
+EXPORT void ni_register_driver(char*                name,
+                                ni_handles_resource handles_resource,
+                                ni_sync_resource    sync_resource)
+{
+       ni_driver* d=ni_driver_new(name, handles_resource, sync_resource);
+       if(!strcmp(name, "op")) opdriver=d;
+       else                    moddriver=d;
+}
+
+/* -}{---- ------------------------------------------------------------------ */
+
+EXPORT ni_resource* ni_resource_new(char*        uri,
+                                      k_hashtable* ent_head,
+                                      char*        entity)
+{
+       char* urih=k_hashtable_get(ent_head, "URI:");
+       if(urih) uri=urih;
+       else
+       if(uri) k_hashtable_put_dup(ent_head, "URI:", uri);
+
+       ni_resource* res=k_malloc(sizeof(ni_resource));
+       res->uri     =(uri? k_strdup(uri): 0);
+       res->ent_head=ent_head;
+       res->entity  =entity;
+       return res;
+}
+
+EXPORT ni_resource* ni_resource_dup(ni_resource* res)
+{
+       ni_resource* rep=k_malloc(sizeof(ni_resource));
+       rep->uri     =k_strdup(       res->uri);
+       rep->ent_head=k_hashtable_dup(res->ent_head);
+       rep->entity  =                res->entity;
+       return rep;
+}
+
+EXPORT void ni_resource_delete(ni_resource* res)
+{
+       if(!res) return;
+       if(res->entity && res->ent_head){
+               int constant=k_hashtable_is(res->ent_head, "CUX:", "C");
+               if(!constant) k_free(res->entity);
+       }
+       k_hashtable_delete(res->ent_head);
+       k_free(            res->uri);
+       k_free(            res);
+}
+
+static char* excto[]={ "Permit:", "Sub-To:", "Pub-To:", 0 };
+static char* perto[]={ "Permit:", 0 };
+static char* subto[]={ "Sub-To:",    0 };
+static char* pubto[]={ "Pub-To:",    0 };
+
+EXPORT void ni_resource_show(ni_resource* res, char* text)
+{
+       if(!res){
+               k_log_out("\n---%s--------\n------------\n\n----------",
+                               text);
+       }
+       else{
+               char*  b=tmpbuf;
+               size_t s=TMPBUFSIZE;
+               size_t l=0;
+               l+=k_hashtable_snprintf_x(res->ent_head, b+l, s-l, excto);
+               l+=k_hashtable_snprintf_i(res->ent_head, b+l, s-l, perto);
+               l+=k_hashtable_snprintf_i(res->ent_head, b+l, s-l, subto);
+               l+=k_hashtable_snprintf_i(res->ent_head, b+l, s-l, pubto);
+               k_log_out("\n---%s----%s--\n%s----------\n%p\n----------", 
+                               text, res->uri, tmpbuf, res->entity);
+       }
+}
+
+/* -}{---- ------------------------------------------------------------------ */
+
+EXPORT ni_resource* ni_resource_get(char* uri)
+{
+       return k_hashtable_get(resources, uri);
+}
+
+/* -}{---- ------------------------------------------------------------------ */
+
+EXPORT ni_event* ni_res_to_evt(ni_resource* res)
+{
+       ni_event* evp=k_malloc(sizeof(ni_event));
+       evp->uri     =k_strdup(       res->uri);
+       evp->evt_head=k_hashtable_new("vHeaders/ni_res_to_evt", 1);
+       evp->ent_head=k_hashtable_dup(res->ent_head);
+       evp->entity  =                res->entity;
+       return evp;
+}
+
+/* -}{---- ------------------------------------------------------------------ */
+
+EXPORT ni_event* ni_event_new(char*        uri,
+                                k_hashtable* evt_head,
+                                k_hashtable* ent_head,
+                                char*        entity)
+{
+       char* urih=k_hashtable_get(ent_head, "URI:");
+       if(urih) uri=urih;
+       else
+       if(uri) k_hashtable_put_dup(ent_head, "URI:", uri);
+
+       ni_event* evt=k_malloc(sizeof(ni_event));
+       evt->uri     =(uri? k_strdup(uri): 0);
+       evt->evt_head=evt_head;
+       evt->ent_head=ent_head;
+       evt->entity  =entity;
+       return evt;
+}
+
+EXPORT ni_event* ni_event_dup(ni_event* evt)
+{
+       ni_event* evp=k_malloc(sizeof(ni_event));
+       evp->uri     =k_strdup(       evt->uri);
+       evp->evt_head=k_hashtable_dup(evt->evt_head);
+       evp->ent_head=k_hashtable_dup(evt->ent_head);
+       evp->entity  =                evt->entity;
+       return evp;
+}
+
+EXPORT void ni_event_delete(ni_event* evt)
+{
+       if(!evt) return;
+       if(evt->entity && evt->ent_head){
+               int constant=k_hashtable_is(evt->ent_head, "CUX:", "C");
+               if(!constant) k_free(evt->entity);
+       }
+       k_hashtable_delete(evt->evt_head);
+       k_hashtable_delete(evt->ent_head);
+       k_free(            evt->uri);
+       k_free(            evt);
+}
+
+EXPORT void ni_event_show(ni_event* evt, char* text)
+{
+       if(!evt){
+               k_log_out("\n---%s--------\n------------\n\n----------",
+                               text);
+       }
+       else{
+               char* buf=tmpbuf;
+               int   siz=TMPBUFSIZE;
+               int n=k_hashtable_snprintf(evt->evt_head, buf,   siz)+1;
+               ;     k_hashtable_snprintf(evt->ent_head, buf+n, siz-n);
+               k_log_out("\n---%s----%s--\n"
+                         "%s----------\n"
+                         "%s----------\n"
+                         "%p\n----------", 
+                               text, evt->uri, buf, buf+n, evt->entity);
+       }
+}
+
+/* -}{----------------------------------------------------------------------- */
+
+
+
+