object web, etc
[cilux] / src / on / notification.c
1
2 /* -}{----------------------------------------------------------------------- */
3
4 #include <kernelapi.h>
5 #undef  PUBLIC
6 #define PUBLIC EXPORT
7 #include <notification.h>
8
9 /* -}{---- ------------------------------------------------------------------ */
10
11 EXPORT void n_register_driver(char*            name,
12                               n_handles_object handles_object,
13                               n_view_event     view_event,
14                               n_cast_event     cast_event,
15                               n_sync_object    sync_object )
16 {
17 }
18
19 EXPORT n_object* n_object_new(char* s)
20 {
21     return 0;
22 }
23
24 EXPORT void n_commit(n_object* o)
25 {
26 }
27
28 EXPORT n_object* n_view(n_object* o, char* uid)
29 {
30     return 0;
31 }
32
33 EXPORT void n_open(n_object* o, char* uid, char* options)
34 {
35 }
36
37 EXPORT char* n_to_string(n_object* o)
38 {
39     return 0;
40 }
41
42 EXPORT char* n_uid(n_object* o)
43 {
44     return 0;
45 }
46
47 EXPORT int n_uid_is(n_object* o, char* uid)
48 {
49     return 0;
50 }
51
52 EXPORT char* n_header(n_object* o, char* name)
53 {
54     return 0;
55 }
56
57 EXPORT k_hashtable* n_headers(n_object* o)
58 {
59     return 0;
60 }
61
62 EXPORT k_hashtable* n_content(n_object* o)
63 {
64     return 0;
65 }
66
67 EXPORT void n_dispatch(n_event* event)
68 {
69     /* one callback per view or cast */
70     /* must delete the event */
71 }
72
73 /* -}{---- ------------------------------------------------------------------ */
74
75 #define TMPBUFSIZE  4096
76 static char         tmpbuf[TMPBUFSIZE];
77 static char*        hostname;
78 static k_hashtable* resources;
79
80 /* -}{---- ------------------------------------------------------------------ */
81
82 typedef struct ni_driver{
83         char*                name;
84         ni_handles_resource handles_resource;
85         ni_sync_resource    sync_resource;
86 } ni_driver;
87
88 /* -}{---- From headers.c --------------------------------------------------- */
89
90 extern void  init_headers(void);
91 extern void  drop_entity_headers(k_hashtable* ent_head);
92 extern void  fill_headers(k_hashtable* ent_head,
93                           char*  status,
94                           char*  statustext,
95                           char*  uri,
96                           time_t modifitime,
97                           int    datalength,
98                           char*  mimetype,
99                           char*  encoding,
100                           int    nocache);
101
102 /* -}{---- ------------------------------------------------------------------ */
103
104 static void          incoming_request(ni_event* evq);
105 static ni_resource* ensure_res(char* pub);
106 static void          ensure_sub_entry(k_hashtable* ent_head, k_hashtable* sub);
107 static void          ensure_pub_entry(k_hashtable* ent_head, k_hashtable* sub);
108 static k_hashtable* get_this(k_hashtable*, char*, k_hashtable*);
109 static k_hashtable* get_using(k_hashtable* curr, char* tag, char* val);
110 static void          post_to_driver(char* pub, ni_event* evq);
111 static void          incoming_resource(ni_event* evt);
112 static void    test_pub_tos(ni_resource* res, ni_event* evt, int entityok);
113 static int     satisfiable(k_hashtable*, ni_resource*, ni_event*, int);
114 static void    update_pubcache(int, k_hashtable**, k_hashtable*, k_hashtable*);
115 static int     sub_less(k_hashtable* sub1, k_hashtable* sub2);
116 static void    fix_via_subs(k_hashtable* evteh, k_hashtable* reseh);
117 static int     sub_ok(k_hashtable* sub);
118 static int     range_ok(char* range, char* conrange);
119 static void    merge_non_entity_headers(k_hashtable* reseh, k_hashtable* evteh);
120 static void    merge_entity_range(ni_resource* res, ni_event* evt);
121 static void    respond(   k_hashtable* sub, ni_resource* res, ni_event* evt);
122 static void    respond_ok(k_hashtable* sub, ni_event* evv);
123 static void    respond_nf(k_hashtable* sub, ni_event* evv);
124 static void*   entity_to_octets(k_hashtable* ent_head, void* entity);
125 static char*       handled_by(char* pub);
126 static void        call_sync_resource(ni_resource* res);
127 static ni_driver* ni_driver_new(char*                name,
128                                   ni_handles_resource handles_resource,
129                                   ni_sync_resource    sync_resource);
130
131 /* -}{---- ------------------------------------------------------------------ */
132
133 EXPORT int on_module_loaded(void)
134 {
135         resources=k_hashtable_new("Resources", 0);
136
137         init_headers();
138
139         k_log_out("ON initialised");
140         return 1;
141 }
142
143 EXPORT void on_module_tick(void)
144 {
145 }
146
147 EXPORT int on_module_event(void* data)
148 {
149         ni_event* evt=data;
150
151         if(!k_hashtable_get(evt->ent_head, "Status:")){
152
153                 incoming_request(evt);
154         }
155         else{
156                 incoming_resource(evt);
157         }
158         return 1;
159 }
160
161 /* -}{---- ------------------------------------------------------------------ */
162
163 void incoming_request(ni_event* evq)
164 {
165         int L=1;
166         if(L) ni_event_show(evq, "ON got request event");
167
168         k_hashtable* sub=evq->ent_head;
169         char* uri  =k_hashtable_get(sub, "URI:");
170         char* pub  =k_hashtable_get(sub, "Sub-To:"); if(!pub) return;
171         int   styor=k_hashtable_isi(sub, "Sub-Type:", "Original");
172         char* via  =k_hashtable_get(sub, "Via:");
173         char* from =k_hashtable_get(sub, "From:");
174         if(L) k_log_out("------------------ %s", pub);
175
176         int nocache=k_hashtable_isi(sub, "Cache-Control:", "no-cache");
177         if(nocache){
178                 k_hashtable_remove( sub, "Cache-Control:");
179                 k_hashtable_set(    sub, "If-Modified-Since:", "0");
180         }
181
182         if(!from){
183                 ni_resource* ses=k_hashtable_get(resources, uri);
184                 if(ses) ensure_sub_entry(ses->ent_head, sub);
185                 if(ses) if(L) ni_resource_show(ses, "Subscribing Resource:");
186                 if(styor && via){
187                         post_to_driver(pub, evq);
188                         return;
189                 }
190         }
191
192         ni_resource* res=ensure_res(pub);
193         if(L) ni_resource_show(res, "Publishing Resource:");
194
195         k_hashtable* pubcache=0;
196         int sat=satisfiable(sub, res, 0, 0);
197         update_pubcache(sat, &pubcache, res->ent_head, sub);
198         if(sat){
199                 if(L) k_log_out("Memory cache hit for %s", pub);
200                 respond(k_hashtable_dup(sub), res, 0);
201                 ni_event_delete(evq);
202         }
203         else{
204                 ensure_pub_entry(res->ent_head, sub);
205                 if(L) ni_resource_show(res, "Pending Resource:");
206                 if(pubcache){
207                         evq->ent_head=pubcache;
208                         post_to_driver(pub, evq);
209                         k_hashtable_delete(sub);
210                 }
211                 else{
212                         if(L) k_log_out("In-progress Pub-Cache sufficient");
213                         ni_event_delete(evq);
214                 }
215         }
216 }
217
218 ni_resource* ensure_res(char* pub)
219 {
220         ni_resource* res=k_hashtable_get(resources, pub);
221         if(!res){
222                 res=ni_resource_new(pub, k_hashtable_new("resHeaders", 1), 0);
223                 k_hashtable_set(resources, pub, res);
224         }
225         return res;
226 }
227
228 void ensure_sub_entry(k_hashtable* ent_head, k_hashtable* sub)
229 {
230         char* tag="Sub-To:";
231         k_hashtable* sup=get_this(ent_head, tag, sub);
232         if(!sup){
233                 k_hashtable* sup=k_hashtable_dup(sub);
234                 char* uri=k_hashtable_extract(sup, "Sub-To:");
235                 k_hashtable_put(              sup, "URI:", uri);
236                 k_hashtable_remove(           sup, "Sub-Type:");
237                 k_hashtable_sub(ent_head, tag, sup);
238         }
239         else{
240                 k_hashtable_remove(sup, "Range:");
241                 k_hashtable_remove(sup, "Content-Range:");
242                 k_hashtable_remove(sup, "Status:");
243                 k_hashtable_remove(sup, "Status-Cache:");
244                 char* mth=k_strdup(k_hashtable_get(sub, "Method:"));
245                 char* via=k_strdup(k_hashtable_get(sub, "Via:"));
246                 char* ims=k_strdup(k_hashtable_get(sub, "If-Modified-Since:"));
247                 char* rng=k_strdup(k_hashtable_get(sub, "Range:"));
248                 if(mth) k_hashtable_put(sup, "Method:",            mth);
249                 if(via) k_hashtable_put(sup, "Via:",               via);
250                 if(ims) k_hashtable_put(sup, "If-Modified-Since:", ims);
251                 if(rng) k_hashtable_put(sup, "Range:",             rng);
252         }
253 }
254
255 void ensure_pub_entry(k_hashtable* ent_head, k_hashtable* sub)
256 {
257         char* tag="Pub-To:";
258         k_hashtable* sup=get_this(ent_head, tag, sub);
259         if(!sup){
260                 k_hashtable* sup=k_hashtable_dup(sub);
261                 k_hashtable_remove(sup, "Sub-To:");
262                 k_hashtable_remove(sup, "Sub-Type:");
263                 k_hashtable_sub(ent_head, tag, sup);
264         }
265         else {
266                 k_hashtable_remove(sup, "Method:");
267                 k_hashtable_remove(sup, "If-Modified-Since:");
268                 k_hashtable_remove(sup, "Cache-Control:");
269                 k_hashtable_remove(sup, "Update:");
270                 char* mth=k_strdup(k_hashtable_get(sub, "Method:"));
271                 char* ims=k_strdup(k_hashtable_get(sub, "If-Modified-Since:"));
272                 char* ccn=k_strdup(k_hashtable_get(sub, "Cache-Control:"));
273                 char* upd=k_strdup(k_hashtable_get(sub, "Update:"));
274                 if(mth) k_hashtable_put(sup, "Method:",            mth);
275                 if(ims) k_hashtable_put(sup, "If-Modified-Since:", ims);
276                 if(ccn) k_hashtable_put(sup, "Cache-Control:",     ccn);
277                 if(upd) k_hashtable_put(sup, "Update:",            upd);
278         }
279 }
280
281 k_hashtable* get_this(k_hashtable* ent_head, char* pubsub, k_hashtable* try)
282 {
283         k_hashtable* curr=k_hashtable_get(ent_head, pubsub);
284         if(!curr) return 0;
285
286         char* tag;
287         char* val;
288
289         tag="From:";
290         val=k_hashtable_get(try, tag);
291         if(val) return get_using(curr, tag, val);
292
293         tag="URI:";
294         val=k_hashtable_get(try, tag);
295         if(val) return get_using(curr, tag, val);
296
297         return 0;
298 }
299
300 k_hashtable* get_using(k_hashtable* curr, char* tag, char* val)
301 {
302         k_hashtable* c;
303         for(c=curr; c; c=c->next) if(k_hashtable_is(c, tag, val)) return c;
304         return c;
305 }
306
307 /* -}{---- ------------------------------------------------------------------ */
308
309 void incoming_resource(ni_event* evt)
310 {
311         int L=0;
312         if(L) ni_event_show(evt, "ON got resource incoming event");
313
314         ni_resource* res=k_hashtable_get(resources, evt->uri);
315         int fullconst=res && k_hashtable_is(res->ent_head, "Status:", "200") &&
316                              k_hashtable_is(res->ent_head, "CUX:",    "C");
317
318         if(fullconst){
319                 k_log_err("Resource is complete and Constant");
320                 ni_event_delete(evt);
321                 return;
322         }
323         if(!res) res=ensure_res(evt->uri);
324
325         k_hashtable* reseh=res->ent_head;
326         k_hashtable* evteh=evt->ent_head;
327         int okfull  =k_hashtable_is(evteh, "Status:", "200");
328         int partial =k_hashtable_is(evteh, "Status:", "206");
329         int headonly=k_hashtable_is(evteh, "Status:", "260");/*
330         int updated =k_hashtable_is(evteh, "Status:", "266");*/
331         int notmod  =k_hashtable_is(evteh, "Status:", "304");
332         int notfound=k_hashtable_is(evteh, "Status:", "404");
333
334         int entityok= (okfull || partial || notmod);
335         int entityev=!(headonly || notmod || notfound);
336         int entityin=!!evt->entity;
337         int entityon=!!res->entity;
338
339         if(!entityev){
340         }
341         if(okfull || !entityon){
342                 k_hashtable_merge(reseh, evteh);
343                 if(okfull) k_hashtable_remove(reseh, "Content-Range:");
344         }
345         else{
346                 merge_non_entity_headers(reseh, evteh);
347         }
348
349         fix_via_subs(evteh, reseh);
350
351         if(entityin){
352                 if(okfull || (partial && !entityon)){
353                         if(res->entity!=evt->entity){
354                                 if(!k_hashtable_is(reseh, "CUX:", "C")){
355                                         k_free(res->entity);
356                                 }
357                                 res->entity=evt->entity;
358                         }
359                 }
360                 else
361                 if(partial && entityon){
362                         merge_entity_range(res, evt);
363                 }
364         }
365
366         if(L) ni_resource_show(res, "updated resource - sync and try pubs:");
367
368         call_sync_resource(res);
369
370         test_pub_tos(res, evt, entityok);
371
372         evt->entity=0;
373         ni_event_delete(evt);
374
375         if(L) ni_resource_show(res, "resource after ON:");
376 }
377
378 void fix_via_subs(k_hashtable* evteh, k_hashtable* reseh)
379 {
380         char* from=k_hashtable_get(evteh, "From:");
381         if(!from) return;
382         k_hashtable* subs=k_hashtable_get(reseh, "Sub-To:");
383         k_hashtable* sub;
384         for(sub=subs; sub; sub=sub->next){
385                 if(!k_hashtable_is(sub, "Via:", from)) continue;
386                 int   st=k_hashtable_get_int(evteh, "Status:");
387                 char* cr=k_hashtable_get_dup(evteh, "Content-Range:");
388                 if(st!=304) k_hashtable_put_int(sub, "Status:", st);
389                 if(st==200) k_hashtable_remove( sub, "Content-Range:");
390                 else
391                 if(cr)          k_hashtable_put(sub, "Content-Range:", cr);
392                 if(sub_ok(sub)) k_hashtable_set(sub, "Status-Cache:", "OK");
393         }
394 }
395
396 void test_pub_tos(ni_resource* res, ni_event* evt, int entityok)
397 {
398         k_hashtable* keeps=0;
399         k_hashtable* sub=k_hashtable_extract(res->ent_head, "Pub-To:");
400         while(sub){
401                 k_hashtable* subnext=sub->next;
402                 sub->next=0;
403                 int keep=1;
404                 int sat=satisfiable(sub, res, evt, entityok);
405                 update_pubcache(sat, 0, res->ent_head, sub);
406                 if(sat){
407                         respond(k_hashtable_dup(sub), res, evt);
408                         if(!k_hashtable_get(sub, "Update:")) keep=0;
409                 }
410                 if(keep){
411                         sub->next=keeps;
412                         keeps=sub;
413                 }
414                 else{
415                         k_hashtable_delete(sub);
416                 }
417                 sub=subnext;
418         }
419         sub=keeps;
420         while(sub){
421                 k_hashtable* subnext=sub->next;
422                 k_hashtable_sub(res->ent_head, "Pub-To:", sub);
423                 sub=subnext;
424         }
425 }
426
427 int satisfiable(k_hashtable*  sub,
428                 ni_resource* res,
429                 ni_event*    evt,
430                 int           entityok)
431 {
432         int L=0;
433         int   get  =k_hashtable_is( sub, "Method:", "GET");
434         int   head =k_hashtable_is( sub, "Method:", "HEAD");
435         int   dosub=k_hashtable_is( sub, "Method:", "SUB");
436         int   unsub=k_hashtable_is( sub, "Method:", "UNSUB");
437         int   full=!k_hashtable_isi(sub, "Cache-Control:", "no-full");
438         int   updt =k_hashtable_isi(sub, "Update:", "changes");
439         char* ims  =k_hashtable_get(sub, "If-Modified-Since:");
440         char* range=k_hashtable_get(sub, "Range:");
441
442         int getsub  =get || dosub;
443         int getrange=getsub && range;
444
445         k_hashtable* reseh=res->ent_head;
446         int   gotall=    k_hashtable_is( reseh, "Status:", "200");
447         int   gotrange=  k_hashtable_is( reseh, "Status:", "206");
448         int   gothead=   k_hashtable_is( reseh, "Status:", "260");
449         int   gotupdated=evt && 
450                          k_hashtable_is(evt->ent_head, "Status:", "266");
451         int   notfound=  k_hashtable_is( reseh, "Status:", "404");
452         int   constant=  k_hashtable_is( reseh, "CUX:",    "C"  );
453         char* conrange=  k_hashtable_get(reseh, "Content-Range:");
454
455         if(unsub)    return 0;
456         if(notfound) return 1;
457
458         int gotany=(gothead || gotrange || gotall);
459         int snapok=(constant || !ims || entityok);
460
461         int sat=
462            (full && 
463             ((getsub   && gotall                                && snapok) ||
464              (getrange && gotrange && range_ok(range, conrange) && snapok) ||
465              (head     && gotany                                         )   ))
466              ||
467            (updt &&
468             ((dosub    && gotupdated)));
469
470         if(L) k_log_out("satisfiable %d", sat);
471         return sat;
472 }
473
474 void update_pubcache(int           sat,
475                      k_hashtable** pubcachep,
476                      k_hashtable*  reseh,
477                      k_hashtable*  sub)
478 {
479         if(!sat){ if(pubcachep){
480                 k_hashtable* pubcache;
481                 pubcache=k_hashtable_get(reseh, "Pub-Cache:");
482                 if(!pubcache || sub_less(pubcache, sub)){
483                         if(!pubcache){
484                                 pubcache=k_hashtable_dup(sub);
485                                 k_hashtable_sub(reseh, "Pub-Cache:", pubcache);
486                         }
487                         else{
488                                 k_hashtable_merge(pubcache, sub);
489                         }
490                         k_hashtable_remove(pubcache, "URI:");
491                         k_hashtable_remove(pubcache, "Sub-Type:");
492                         *pubcachep=k_hashtable_dup(pubcache);
493                         k_hashtable_set(*pubcachep, "Sub-Type:", "Cache");
494                 }
495         }}
496         else{ if(!pubcachep){
497                 k_hashtable* pubcache;
498                 pubcache=k_hashtable_get(reseh, "Pub-Cache:");
499                 if(!sub_less(sub, pubcache)){
500                         k_hashtable_remove(pubcache, "Method:");
501                         k_hashtable_remove(pubcache, "If-Modified-Since:");
502                 }
503         }}
504 }
505
506 int sub_ok(k_hashtable* sub)
507 {
508         char* gotresp =k_hashtable_get(sub, "Status:");
509         if(!gotresp) return 0;
510         int   notfound=k_hashtable_is( sub, "Status:", "404");
511         if(notfound) return 0;
512         int   gotall  =k_hashtable_is( sub, "Status:", "200");
513         int   gotrange=k_hashtable_is( sub, "Status:", "206");
514         int   gothead= k_hashtable_is( sub, "Status:", "260");
515         int   doget   =k_hashtable_is( sub, "Method:", "GET");
516         int   dohead  =k_hashtable_is( sub, "Method:", "HEAD");
517         int   dosub   =k_hashtable_is( sub, "Method:", "SUB");
518         char* range   =k_hashtable_get(sub, "Range:");
519         char* conrange=k_hashtable_get(sub, "Content-Range:");
520
521         int dogetsub=doget || dosub;
522         int dogetrange=dogetsub && range;
523         int gotany  =(gothead || gotrange || gotall);
524
525         return (dogetsub   &&  gotall                               ) ||
526                (dogetrange &&  gotrange && range_ok(range, conrange)) ||
527                (dohead     &&  gotany                               );
528 }
529
530 int sub_less(k_hashtable* sub1, k_hashtable* sub2)
531 {
532         int L=0;
533         if(L) k_log_out("sub_less sub1:");
534         if(L) k_hashtable_show_chars(sub1);
535         if(L) k_log_out("sub_less sub2:");
536         if(L) k_hashtable_show_chars(sub2);
537         char* mth1 =k_hashtable_get(sub1, "Method:");
538         int   head1=k_hashtable_is( sub1, "Method:", "HEAD");
539         int   get2 =k_hashtable_is( sub2, "Method:", "GET");
540         char* ims1 =k_hashtable_get(sub1, "If-Modified-Since:");
541         char* ims2 =k_hashtable_get(sub2, "If-Modified-Since:");
542         int   full=!k_hashtable_isi(sub2, "Cache-Control:", "no-full");
543         return full && ((!mth1) || (head1 && get2) || (!ims1 && ims2));
544 }
545
546 int range_ok(char* range, char* conrange)
547 {
548         k_log_out("range_ok not implemented: %s vs %s", range, conrange);
549         return 0;
550 }
551
552 void merge_non_entity_headers(k_hashtable* reseh, k_hashtable* evteh)
553 {
554 }
555
556 void merge_entity_range(ni_resource* res, ni_event* evt)
557 {
558         k_log_out("merge_entity_range not implemented yet");
559 }
560
561 /* -}{---- ------------------------------------------------------------------ */
562
563 void respond(k_hashtable* sub, ni_resource* res, ni_event* evt)
564 {
565         int updated=evt && k_hashtable_is(evt->ent_head, "Status:", "266");
566         ni_event* evv=updated? ni_event_dup(evt): 
567                                 ni_res_to_evt(res);
568         int L=0;
569         if(L) ni_event_show(evv, "respond");
570
571         k_hashtable_remove(evv->ent_head, "Permit:");
572         k_hashtable_remove(evv->ent_head, "Sub-To:");
573         k_hashtable_remove(evv->ent_head, "Pub-To:");
574         k_hashtable_remove(evv->ent_head, "Pub-Cache:");
575         k_hashtable_sub(   evv->ent_head, "Pub-To:", sub);
576
577         int nf;
578         nf=k_hashtable_is( evv->ent_head, "Status:", "404");
579         if(!nf) respond_ok(sub, evv);
580         else    respond_nf(sub, evv);
581 }
582
583 void respond_ok(k_hashtable* sub, ni_event* evv)
584 {
585         char* status=0;
586         char* statustext=0;
587         int   datalength= -1;
588         int   nocache=0;
589         k_hashtable* ent_head=evv->ent_head;
590
591         time_t modifitime=k_hashtable_get_int(ent_head, "Last-Modified-Epoch:");
592         char*  modistring=k_hashtable_get(    ent_head, "Last-Modified:");
593         if(!modifitime){
594                 modifitime=k_time_from_rfc(modistring);
595         }
596         char*  imss=k_hashtable_get(sub, "If-Modified-Since:");
597         time_t imst=k_time_from_rfc(imss);
598
599         if(modifitime== -1 || imst== -1 || modifitime > imst){
600                 if(k_hashtable_is(sub, "Method:", "HEAD")){
601                         evv->entity=0;
602                 }
603                 if(!k_hashtable_is(ent_head, "CUX:", "C")){
604                         evv->entity=entity_to_octets(ent_head, evv->entity);
605                         nocache=1;
606                 }
607         }
608         else{
609                 status="304";
610                 statustext="Not Modified";
611                 modifitime= -1;
612                 nocache= -1;
613                 evv->entity=0;
614                 drop_entity_headers(ent_head);
615         }
616
617         fill_headers(ent_head, status, statustext, 0,
618                      modistring? -1: modifitime,
619                      datalength, 0, 0, nocache);
620
621         char*  pub =k_hashtable_get(sub, "URI:");
622         int L=0;
623         if(L) ni_event_show(evv, "respond_ok");
624         post_to_driver(pub, evv);
625 }
626
627 void respond_nf(k_hashtable* sub, ni_event* evv)
628 {
629         fill_headers(evv->ent_head, "404", "File Not Found", 0, -1, 0, 0, 0, 1);
630         char*  pub =k_hashtable_get(sub, "URI:");
631         post_to_driver(pub, evv);
632 }
633
634 void post_to_driver(char* pub, ni_event* evq)
635 {
636         char* driver=handled_by(pub);
637         if(0) k_log_out("Passing %s on to %s", pub, driver);
638         if(0) ni_event_show(evq, "Post to Driver:");
639         k_event_post(driver, evq);
640 }
641
642 void* entity_to_octets(k_hashtable* ent_head, void* entity)
643 {
644         if(!entity) return 0;
645         size_t size=k_hashtable_get_int(ent_head, "Content-Length:");
646         return k_memdup(entity, size);
647 }
648
649 /* -}{---- ------------------------------------------------------------------ */
650
651 EXPORT char* ni_hostname()
652 {
653         return hostname? hostname: "not set";
654 }
655
656 EXPORT void ni_hostname_set(char* name)
657 {
658         k_free(hostname);
659         hostname=k_strdup(name);
660 }
661
662 /* -}{---- ------------------------------------------------------------------ */
663
664 static ni_driver* opdriver;
665 static ni_driver* moddriver;
666
667 char* handled_by(char* pub)
668 {
669         if(moddriver->handles_resource(pub)) return moddriver->name;
670         return "op";
671 }
672
673 void call_sync_resource(ni_resource* res)
674 {
675         if(moddriver->handles_resource(res->uri)) moddriver->sync_resource(res);
676         else                                      opdriver->sync_resource(res);
677 }
678
679 ni_driver* ni_driver_new(char*                name, 
680                            ni_handles_resource handles_resource,
681                            ni_sync_resource    sync_resource)
682 {
683         ni_driver* driver=k_malloc(sizeof(ni_driver));
684         driver->name            =name;
685         driver->handles_resource=handles_resource;
686         driver->sync_resource   =sync_resource;
687         return driver;
688 }
689
690 EXPORT void ni_register_driver(char*                name,
691                                 ni_handles_resource handles_resource,
692                                 ni_sync_resource    sync_resource)
693 {
694         ni_driver* d=ni_driver_new(name, handles_resource, sync_resource);
695         if(!strcmp(name, "op")) opdriver=d;
696         else                    moddriver=d;
697 }
698
699 /* -}{---- ------------------------------------------------------------------ */
700
701 EXPORT ni_resource* ni_resource_new(char*        uri,
702                                       k_hashtable* ent_head,
703                                       char*        entity)
704 {
705         char* urih=k_hashtable_get(ent_head, "URI:");
706         if(urih) uri=urih;
707         else
708         if(uri) k_hashtable_put_dup(ent_head, "URI:", uri);
709
710         ni_resource* res=k_malloc(sizeof(ni_resource));
711         res->uri     =(uri? k_strdup(uri): 0);
712         res->ent_head=ent_head;
713         res->entity  =entity;
714         return res;
715 }
716
717 EXPORT ni_resource* ni_resource_dup(ni_resource* res)
718 {
719         ni_resource* rep=k_malloc(sizeof(ni_resource));
720         rep->uri     =k_strdup(       res->uri);
721         rep->ent_head=k_hashtable_dup(res->ent_head);
722         rep->entity  =                res->entity;
723         return rep;
724 }
725
726 EXPORT void ni_resource_delete(ni_resource* res)
727 {
728         if(!res) return;
729         if(res->entity && res->ent_head){
730                 int constant=k_hashtable_is(res->ent_head, "CUX:", "C");
731                 if(!constant) k_free(res->entity);
732         }
733         k_hashtable_delete(res->ent_head);
734         k_free(            res->uri);
735         k_free(            res);
736 }
737
738 static char* excto[]={ "Permit:", "Sub-To:", "Pub-To:", 0 };
739 static char* perto[]={ "Permit:", 0 };
740 static char* subto[]={ "Sub-To:",    0 };
741 static char* pubto[]={ "Pub-To:",    0 };
742
743 EXPORT void ni_resource_show(ni_resource* res, char* text)
744 {
745         if(!res){
746                 k_log_out("\n---%s--------\n------------\n\n----------",
747                                 text);
748         }
749         else{
750                 char*  b=tmpbuf;
751                 size_t s=TMPBUFSIZE;
752                 size_t l=0;
753                 l+=k_hashtable_snprintf_x(res->ent_head, b+l, s-l, excto);
754                 l+=k_hashtable_snprintf_i(res->ent_head, b+l, s-l, perto);
755                 l+=k_hashtable_snprintf_i(res->ent_head, b+l, s-l, subto);
756                 l+=k_hashtable_snprintf_i(res->ent_head, b+l, s-l, pubto);
757                 k_log_out("\n---%s----%s--\n%s----------\n%p\n----------", 
758                                 text, res->uri, tmpbuf, res->entity);
759         }
760 }
761
762 /* -}{---- ------------------------------------------------------------------ */
763
764 EXPORT ni_resource* ni_resource_get(char* uri)
765 {
766         return k_hashtable_get(resources, uri);
767 }
768
769 /* -}{---- ------------------------------------------------------------------ */
770
771 EXPORT ni_event* ni_res_to_evt(ni_resource* res)
772 {
773         ni_event* evp=k_malloc(sizeof(ni_event));
774         evp->uri     =k_strdup(       res->uri);
775         evp->evt_head=k_hashtable_new("vHeaders/ni_res_to_evt", 1);
776         evp->ent_head=k_hashtable_dup(res->ent_head);
777         evp->entity  =                res->entity;
778         return evp;
779 }
780
781 /* -}{---- ------------------------------------------------------------------ */
782
783 EXPORT ni_event* ni_event_new(char*        uri,
784                                 k_hashtable* evt_head,
785                                 k_hashtable* ent_head,
786                                 char*        entity)
787 {
788         char* urih=k_hashtable_get(ent_head, "URI:");
789         if(urih) uri=urih;
790         else
791         if(uri) k_hashtable_put_dup(ent_head, "URI:", uri);
792
793         ni_event* evt=k_malloc(sizeof(ni_event));
794         evt->uri     =(uri? k_strdup(uri): 0);
795         evt->evt_head=evt_head;
796         evt->ent_head=ent_head;
797         evt->entity  =entity;
798         return evt;
799 }
800
801 EXPORT ni_event* ni_event_dup(ni_event* evt)
802 {
803         ni_event* evp=k_malloc(sizeof(ni_event));
804         evp->uri     =k_strdup(       evt->uri);
805         evp->evt_head=k_hashtable_dup(evt->evt_head);
806         evp->ent_head=k_hashtable_dup(evt->ent_head);
807         evp->entity  =                evt->entity;
808         return evp;
809 }
810
811 EXPORT void ni_event_delete(ni_event* evt)
812 {
813         if(!evt) return;
814         if(evt->entity && evt->ent_head){
815                 int constant=k_hashtable_is(evt->ent_head, "CUX:", "C");
816                 if(!constant) k_free(evt->entity);
817         }
818         k_hashtable_delete(evt->evt_head);
819         k_hashtable_delete(evt->ent_head);
820         k_free(            evt->uri);
821         k_free(            evt);
822 }
823
824 EXPORT void ni_event_show(ni_event* evt, char* text)
825 {
826         if(!evt){
827                 k_log_out("\n---%s--------\n------------\n\n----------",
828                                 text);
829         }
830         else{
831                 char* buf=tmpbuf;
832                 int   siz=TMPBUFSIZE;
833                 int n=k_hashtable_snprintf(evt->evt_head, buf,   siz)+1;
834                 ;     k_hashtable_snprintf(evt->ent_head, buf+n, siz-n);
835                 k_log_out("\n---%s----%s--\n"
836                           "%s----------\n"
837                           "%s----------\n"
838                           "%p\n----------", 
839                                 text, evt->uri, buf, buf+n, evt->entity);
840         }
841 }
842
843 /* -}{----------------------------------------------------------------------- */
844
845
846
847