+
+/* -------------------------------------------------------------------------- */
+
+#define PUBLIC EXPORT
+#include <kernelapi.h>
+#include <osapi.h>
+#include "container.h"
+
+/* -------------------------------------------------------------------------- */
+
+#define OTHER_THREAD
+#define CERNDATE "%d/%b/%Y:%H:%M:%S"
+#define RDSIZE 40960
+#define WRSIZE 2
+#define TMPBUFSIZE 40960
+#define WRITEMAX 30000
+#define LOGBUFSIZE 1024
+#define OOM_STRING "Out of memory!\nBailing."
+#define MAX_MODULES 64
+#define MAX_EVENTS_PER_MODULE 3
+#define CHAN_CLOSING(chan) (chan->priv->state!=CHAN_OPEN)
+#define CHAN_CLOSE 0
+#define CHAN_LINGER 1
+#define CHAN_OPEN 2
+#define SETCB_RD 1
+#define SETCB_WR 2
+#define MIN(a,b) (((a) < (b))? (a): (b))
+
+/* -------------------------------------------------------------------------- */
+
+typedef struct wrbuffer{
+ char* base;
+ size_t size;
+ char* free;
+} wrbuffer;
+
+struct k_channel_priv{
+
+ SOCK_T SOCK;
+ int state;
+ int event;
+ int rwmask;
+ char* rdbuffer;
+ size_t rdbufpos;
+ size_t rdbufsize;
+ int rdbufforeign;
+ wrbuffer* wrbuffers;
+ int wrbufpos;
+ int wrbufsize;
+ int lingercount;
+};
+
+typedef struct event{ struct event* next;
+ void* data;
+} event;
+
+typedef struct k_module{
+ char* name;
+ k_module_tick_fn tick_fn;
+ k_module_event_fn event_fn;
+ event* queue;
+} k_module;
+
+typedef struct key_event{ struct key_event* next;
+ unsigned char key;
+ int down;
+} key_event;
+
+/* -------------------------------------------------------------------------- */
+
+static int running=2;
+static k_gl_reshape_event reshape_fn=0;
+static k_gl_draw_event draw_fn=0;
+static k_gl_key_event key_fn=0;
+static k_module modules[MAX_MODULES];
+static int nextmodule=0;
+static key_event* key_consumer=0;
+static key_event* key_producer=0;
+static k_channel* k_channels=0;
+static k_hashtable* connecting_chans;
+static k_hashtable* current_chans;
+static k_hashtable* monthhash;
+static char tmpbuf[TMPBUFSIZE];
+static char logbuf[LOGBUFSIZE];
+static FILEP logfile=0;
+static int log_gmt=0;
+static char dummy_empty_data[0];
+
+/* -------------------------------------------------------------------------- */
+
+static void gen_conn_name(k_channel* chan);
+static void chan_closed(k_channel* chan);
+static SOCK_T make_listen_socket(int listenport);
+static SOCK_T make_connect_socket(void);
+static int listen_socket(SOCK_T s, int listenport);
+static int connect_socket(SOCK_T s, char*, int);
+static void add_module_entry(char*, k_module_tick_fn, k_module_event_fn);
+static int run_queues(void);
+static void show_open_channels(void);
+static k_channel* find_k_channel_for_socket(SOCK_T s);
+static k_channel* register_k_channel(char* name,
+ char* listenhost,
+ int listenport,
+ char* banner,
+ k_channel_event rdcallback,
+ k_channel_event wrcallback,
+ void* context,
+ int type,
+ struct in_addr clientip,
+ SOCK_T s);
+static event** get_module_queue(char* name);
+static k_module_loaded_fn get_module_loaded_fn(char* name, MODULE module);
+static k_module_tick_fn get_module_tick_fn( char* name, MODULE module);
+static k_module_event_fn get_module_event_fn( char* name, MODULE module);
+static int check_i(char* key, char* includes[], int ignorecase);
+static int check_x(char* key, char* excludes[], int ignorecase);
+static unsigned int string_hash(char* p);
+static int set_rdbuffer(k_channel*, char* rdbuffer, size_t size);
+static char* get_rdbuffer(k_channel* chan);
+static void append_to_wrbuffers(k_channel*, char*, size_t, int);
+static char* chop_from_rdbuffer_div(k_channel* chan, char* div);
+static char* chop_from_rdbuffer_len(k_channel* chan, size_t size);
+static void chop_from_wrbuffers( k_channel* chan, size_t size);
+static void got_key(unsigned char key, int down);
+static void init_keys(void);
+static void dir_read(char*, char*, char*, int, k_stat, void*);
+static void load_module(char* name, int dot);
+static void init_modules(void);
+static void log_net_err(char* where, int e);
+static int dir_list(char*, int, char*, char*, char*);
+static int ensure_dir(char* fullname);
+static void next_keys(void);
+static void read_more_and_notify(k_channel* chan);
+static void readable_socket(k_channel* chan);
+static void register_connections(k_channel* chan);
+static void do_regular_things(void);
+static void tick_modules(void);
+static void remove_dead_k_channels(void);
+static void unregister_k_channel(k_channel* chan, int now);
+static void write_more_and_notify(k_channel* chan);
+static char hex_to_int(char c);
+static void write_to_logfile_cern_style(char* text, int error);
+static void writeable_socket(k_channel* chan);
+static void exception_socket(k_channel* chan);
+static SOCK_T make_udp_listen_socket(int listenport);
+static struct sockaddr_in make_sockaddr_in(unsigned long address,
+ unsigned int port);
+static void init_hashtables(void);
+
+/* -------------------------------------------------------------------------- */
+
+#include <osapi.c>
+
+/* -------------------------------------------------------------------------- */
+
+EXPORT void c_init(CONSOLE console,
+ char* version,
+ char* ciux,
+ void (*terminate)(void))
+{
+ k_console =console;
+ k_version =version;
+ k_ciux =ciux;
+ k_terminate=terminate;
+
+ init_hashtables();
+ init_keys();
+
+ if(LOAD_MODULES_EARLY){
+ init_gl();
+ init_net();
+ init_modules();
+ }
+}
+
+EXPORT C_RUN_RV c_run(C_RUN_ARG arg)
+{
+ init_thread();
+
+ if(!LOAD_MODULES_EARLY){
+ init_gl();
+ init_net();
+ init_modules();
+ }
+
+ if(reshape_fn)(*reshape_fn)(640,480);
+ if(draw_fn) (*draw_fn)();
+
+ k_log_out("Cilux U-Web Exchange started");
+ k_log_out("%s", k_version);
+ k_log_out("---------------------------------------------");
+
+ int queue_events_to_do=0;
+ while(1){
+ if(running==2) poller(queue_events_to_do);
+ if(running==1) SLEEP_MS(LOOP_TICK);
+ if(running==0) break;
+
+ if(running==2) queue_events_to_do=run_queues();
+ if(running==1) ;
+ if(running==0) break;
+ }
+
+ k_log_out("Cilux U-Web Exchange terminated");
+
+ return 0;
+}
+
+EXPORT OTHER_THREAD void c_running(int r)
+{
+ running=r;
+}
+
+EXPORT OTHER_THREAD void c_key(unsigned char key, int down)
+{
+ got_key(key, down);
+}
+
+EXPORT OTHER_THREAD void c_signal(int signum)
+{
+ k_log_out("Received signal %d", signum);
+}
+
+/* -------------------------------------------------------------------------- */
+
+EXPORT void k_gl_register_reshape(k_gl_reshape_event callback){
+ reshape_fn=callback;
+}
+
+EXPORT void k_gl_register_draw(k_gl_draw_event callback){
+ draw_fn=callback;
+}
+
+EXPORT void k_gl_register_key(k_gl_key_event callback){
+ key_fn=callback;
+}
+
+/* -------------------------------------------------------------------------- */
+
+void init_hashtables(void)
+{
+ connecting_chans=k_hashtable_new("Connecting Channels", 0);
+ current_chans =k_hashtable_new("Current Channels", 0);
+ monthhash =k_hashtable_new("Months", 1);
+ k_hashtable_set(monthhash, "Jan", (void*)1);
+ k_hashtable_set(monthhash, "Feb", (void*)2);
+ k_hashtable_set(monthhash, "Mar", (void*)3);
+ k_hashtable_set(monthhash, "Apr", (void*)4);
+ k_hashtable_set(monthhash, "May", (void*)5);
+ k_hashtable_set(monthhash, "Jun", (void*)6);
+ k_hashtable_set(monthhash, "Jul", (void*)7);
+ k_hashtable_set(monthhash, "Aug", (void*)8);
+ k_hashtable_set(monthhash, "Sep", (void*)9);
+ k_hashtable_set(monthhash, "Oct", (void*)10);
+ k_hashtable_set(monthhash, "Nov", (void*)11);
+ k_hashtable_set(monthhash, "Dec", (void*)12);
+}
+
+void init_logging(void)
+{
+ snprintf(tmpbuf, TMPBUFSIZE, "%s.log", k_ciux);
+ logfile=FOPEN(tmpbuf, "a");
+ if(!logfile) k_log_err("couldn't open logfile %s", tmpbuf);
+}
+
+void init_modules(void)
+{
+ char* patts[]={ "{*.dll,*.so}", 0};
+ char* temps[]={ "%s:", 0 };
+ k_dir_list(".", ".", patts, temps, dir_read, 0);
+}
+
+void dir_read(char* basedir,
+ char* path,
+ char* data,
+ int usedmmap,
+ k_stat kstat,
+ void* context)
+{
+ int is_np=0;
+ if(data){
+ if(!strncmp(data, MODPRE, strlen(MODPRE))){
+ k_ciux=k_strdup(data+strlen(MODPRE));
+ char* c=strchr(k_ciux, '.');
+ *c=0;
+ }
+ else{
+ is_np=1;
+ k_ciux="np";
+ }
+ k_free(data);
+ }
+
+ init_logging();
+
+ k_log_out("========================================");
+
+ load_module("ni", 0);
+ load_module("np", 0);
+ if(!is_np) load_module(k_ciux, 1);
+
+ k_log_out("========================================");
+}
+
+void load_module(char* name, int dot)
+{
+ k_log_out("------------ %s --------------", name);
+ char dlname[64];
+ snprintf(dlname, 64, "%s%s%s%s", dot? "./": "", MODPRE, name, MODPOST);
+ MODULE module=DLOPEN(dlname);
+ if(module){
+ k_module_loaded_fn loaded_fn=get_module_loaded_fn(name, module);
+ k_module_tick_fn tick_fn =get_module_tick_fn( name, module);
+ k_module_event_fn event_fn =get_module_event_fn( name, module);
+ if(loaded_fn && event_fn){
+ add_module_entry(name, tick_fn, event_fn);
+ (*loaded_fn)();
+ }
+ else k_log_err("Couldn't load module %s (hooks)", dlname);
+ }
+ else k_log_err("Couldn't load module %s (dlopen) %s", dlname, DLERROR);
+}
+
+k_module_loaded_fn get_module_loaded_fn(char* name, MODULE module)
+{
+ snprintf(tmpbuf, TMPBUFSIZE, "%s%s", name, NAME_OF_MODULE_LOADED_FN);
+ k_module_loaded_fn f=(k_module_loaded_fn)DLSYM(module, tmpbuf);
+ if(!f) k_log_err("no %s", tmpbuf);
+ return f;
+}
+
+k_module_tick_fn get_module_tick_fn(char* name, MODULE module)
+{
+ snprintf(tmpbuf, TMPBUFSIZE, "%s%s", name, NAME_OF_MODULE_TICK_FN);
+ k_module_tick_fn f=(k_module_tick_fn)DLSYM(module, tmpbuf);
+ if(!f) k_log_err("no %s", tmpbuf);
+ return f;
+}
+
+k_module_event_fn get_module_event_fn(char* name, MODULE module)
+{
+ snprintf(tmpbuf, TMPBUFSIZE, "%s%s", name, NAME_OF_MODULE_EVENT_FN);
+ k_module_event_fn f=(k_module_event_fn)DLSYM(module, tmpbuf);
+ if(!f) k_log_err("no %s", tmpbuf);
+ return f;
+}
+
+/* -------------------------------------------------------------------------- */
+
+void add_module_entry(char* name,
+ k_module_tick_fn tick_fn,
+ k_module_event_fn event_fn)
+{
+ if(nextmodule==MAX_MODULES){
+ k_fatal("Too many modules! Maximum %d", MAX_MODULES);
+ }
+ modules[nextmodule ].name =name;
+ modules[nextmodule ].tick_fn =tick_fn;
+ modules[nextmodule ].event_fn=event_fn;
+ modules[nextmodule++].queue =0;
+}
+
+event* event_new(void* data)
+{
+ event* ev;
+ ev=k_malloc(sizeof(event));
+ ev->next=0;
+ ev->data=data;
+ return ev;
+}
+
+EXPORT int k_event_post(char* module, void* data)
+{
+ event* ev=event_new(data);
+ event** queue=get_module_queue(module);
+ if(!queue){
+ k_log_err("k_event_post(%s): no such module", module);
+ return 0;
+ }
+ if(!*queue) *queue=ev;
+ else{
+ event* p=*queue;
+ do{
+ if(!p->next){
+ p->next=ev;
+ break;
+ }
+ p=p->next;
+ } while(1);
+ }
+ return 1;
+}
+
+event** get_module_queue(char* name)
+{
+ int i;
+ for(i=0; i< nextmodule; i++){
+ if(!strcmp(modules[i].name, name)) return &modules[i].queue;
+ }
+ return 0;
+}
+
+int run_queues(void)
+{
+ int i,j;
+ for(i=0; i< nextmodule; i++){
+ for(j=0; modules[i].queue && j < MAX_EVENTS_PER_MODULE; j++){
+ event* event=modules[i].queue;
+ modules[i].queue=event->next;
+ event->next=0;
+ (*modules[i].event_fn)(event->data);
+ k_free(event);
+ }
+ }
+ for(i=0; i< nextmodule; i++){
+ if(modules[i].queue) return 1;
+ }
+ return 0;
+}
+
+/* -------------------------------------------------------------------------- */
+
+void init_keys(void)
+{
+ key_event* ke;
+ ke=k_malloc(sizeof(key_event));
+ ke->key=0;
+ ke->down=0;
+ ke->next=0;
+ key_consumer=ke;
+ key_producer=ke;
+}
+
+OTHER_THREAD void got_key(unsigned char key, int down)
+{
+ if(!key_producer) return;
+ key_event* ke;
+ ke=k_malloc(sizeof(key_event));
+ ke->key=key;
+ ke->down=down;
+ ke->next=0;
+ key_producer->next=ke;
+ key_producer=ke;
+}
+
+void next_keys(void)
+{
+ while(key_consumer!=key_producer){
+ key_event* ke=key_consumer->next;
+ if(key_fn) (*key_fn)(ke->key, ke->down);
+ k_free(key_consumer);
+ key_consumer=ke;
+ }
+}
+
+/* -}{----------------------------------------------------------------------- */
+
+EXPORT char* k_channel_name(char* type, char* ip, int port, char* other)
+{
+ int ln=0;
+ int bufsize=TMPBUFSIZE;
+
+ if(type) ln+=snprintf(tmpbuf+ln, bufsize-ln, "|%s|", type);
+ else ln+=snprintf(tmpbuf+ln, bufsize-ln, "|-|");
+ if(ln>=bufsize) return 0;
+
+ if(ip) ln+=snprintf(tmpbuf+ln, bufsize-ln, "%s|", ip);
+ else ln+=snprintf(tmpbuf+ln, bufsize-ln, "-|");
+ if(ln>=bufsize) return 0;
+
+ if(port) ln+=snprintf(tmpbuf+ln, bufsize-ln, "%d|", port);
+ else ln+=snprintf(tmpbuf+ln, bufsize-ln, "-|");
+ if(ln>=bufsize) return 0;
+
+ if(other) ln+=snprintf(tmpbuf+ln, bufsize-ln, "%s|", other);
+ else ln+=snprintf(tmpbuf+ln, bufsize-ln, "-|");
+ if(ln>=bufsize) return 0;
+
+ return k_strdup(tmpbuf);
+}
+
+EXPORT k_channel* k_channel_connect_name(char* chanm,
+ k_channel_event rdcallback,
+ k_channel_event wrcallback)
+{
+ if(k_hashtable_get(connecting_chans, chanm)){
+ if(1) k_log_out("Already connecting to %s", chanm);
+ return 0;
+ }
+ k_hashtable_set(connecting_chans, chanm, chanm);
+
+ k_channel* chan=0;
+ char* t=k_strdup(chanm);
+ char* a=strchr(t+1, '|'); *a++=0;
+ char* p=strchr(a, '|'); *p++=0;
+ char* o=strchr(p, '|'); *o++=0;
+ int pi=atoi(p);
+
+ if(strstr(t, "-client")){
+ chan=k_channel_connect(t, a, pi, rdcallback, wrcallback, 0);
+ if(!chan) k_log_err("couldn't connect to %s:%d", a, pi);
+ }
+ else
+ if(strstr(t, "-server") && *a=='-'){
+ chan=k_channel_listen(t, pi, 0, rdcallback, wrcallback, 0);
+ if(!chan) k_log_err("couldn't listen on port %d", pi);
+ }
+ else
+ if(strstr(t, "-server")){
+ chan=k_channel_connect(t, a, pi, rdcallback, wrcallback, 0);
+ if(!chan) k_log_err("couldn't connect to %s:%d", a, pi);
+ }
+
+ *--a= '|';
+ *--p= '|';
+ *--o= '|';
+
+ if(!chan) k_free(t);
+
+ return chan;
+}
+
+EXPORT k_channel* k_channel_get_name(char* chanm)
+{
+ k_channel* chan=k_hashtable_get(current_chans, chanm);
+ return chan;
+}
+
+EXPORT void k_channel_show_all()
+{
+ k_log_out("----------------------------------------");
+ k_hashtable_show(connecting_chans);
+ k_log_out("----------------------------------------");
+ k_hashtable_show(current_chans);
+ k_log_out("----------------------------------------");
+}
+
+void gen_conn_name(k_channel* chan)
+{
+ char* t=chan->name+1;
+ char* a=strchr(t, '|'); *a++=0;
+ char* p=strchr(a, '|'); *p++=0;
+ char* o=strchr(p, '|'); *o++=0;
+ char* ip=inet_ntoa(chan->clientip);
+ int pi=atoi(p);
+
+ char b[32];
+ static long uniquenum;
+ snprintf(b, 32, "%ld", ++uniquenum);
+ chan->name=k_channel_name(t, ip, pi, b);
+
+ *--a= '|';
+ *--p= '|';
+ *--o= '|';
+}
+
+/* -------------------------------------------------------------------------- */
+
+EXPORT k_channel* k_channel_listen(char* name,
+ int listenport,
+ char* banner,
+ k_channel_event rdcallback,
+ k_channel_event wrcallback,
+ void* context)
+{
+ SOCK_T s=make_listen_socket(listenport);
+ if(!s) return 0;
+
+ struct in_addr noclientip=EMPTY_IN_ADDR;
+ k_channel* chan=register_k_channel(name,
+ 0,
+ listenport,
+ banner,
+ rdcallback,
+ wrcallback,
+ context,
+ CHAN_LISTEN,
+ noclientip,
+ s);
+ int r=listen_socket(s, listenport);
+ if(r== -1){
+ unregister_k_channel(chan,1);
+ return 0;
+ }
+ return chan;
+}
+
+EXPORT k_channel* k_channel_connect(char* name,
+ char* listenhost,
+ int listenport,
+ k_channel_event rdcallback,
+ k_channel_event wrcallback,
+ void* context)
+{
+ SOCK_T s=make_connect_socket();
+ if(!s) return 0;
+
+ struct in_addr noclientip=EMPTY_IN_ADDR;
+ k_channel* chan=register_k_channel(name,
+ listenhost,
+ listenport,
+ 0,
+ rdcallback,
+ wrcallback,
+ context,
+ CHAN_ESTABL,
+ noclientip,
+ s);
+ int r=connect_socket(s, listenhost, listenport);
+ if(r== -1){
+ unregister_k_channel(chan,1);
+ return 0;
+ }
+ return chan;
+}
+
+EXPORT k_channel* k_channel_send(k_channel* chan,
+ char* base,
+ size_t size,
+ int free)
+{
+ append_to_wrbuffers(chan, base, size, free);
+ set_callback(chan, SETCB_WR);
+ return chan;
+}
+
+EXPORT char* k_channel_chop_div(k_channel* chan, char* divider)
+{
+ return chop_from_rdbuffer_div(chan, divider);
+}
+
+EXPORT char* k_channel_chop_len(k_channel* chan, size_t size)
+{
+ return chop_from_rdbuffer_len(chan, size);
+}
+
+EXPORT int k_channel_setbuf(k_channel* chan, char* rdbuffer, size_t size)
+{
+ return set_rdbuffer(chan, rdbuffer, size);
+}
+
+EXPORT char* k_channel_getbuf(k_channel* chan)
+{
+ return get_rdbuffer(chan);
+}
+
+EXPORT void k_channel_close(k_channel* chan)
+{
+ unregister_k_channel(chan,0);
+}
+
+k_channel* register_k_channel(char* name,
+ char* listenhost,
+ int listenport,
+ char* banner,
+ k_channel_event rdcallback,
+ k_channel_event wrcallback,
+ void* context,
+ int type,
+ struct in_addr clientip,
+ SOCK_T s)
+{
+ k_channel* chan;
+ chan=k_malloc(sizeof(k_channel));
+
+ chan->name=name;
+ chan->listenhost=listenhost;
+ chan->listenport=listenport;
+ chan->banner=banner;
+ chan->rdcallback=rdcallback;
+ chan->wrcallback=wrcallback;
+ chan->context=context;
+ chan->type=type;
+ chan->clientip=clientip;
+ chan->linger=0;
+
+ chan->priv=k_malloc(sizeof(k_channel_priv));
+ chan->priv->SOCK=s;
+ chan->priv->state=CHAN_OPEN;
+ chan->priv->event=0; /* what is this for? */
+ chan->priv->rwmask=0; /* what is this for? */
+ chan->priv->rdbuffer=(char*)k_malloc(RDSIZE); /* 40K per connection??!! */
+ chan->priv->rdbufpos=0;
+ chan->priv->rdbufsize=RDSIZE;
+ chan->priv->rdbufforeign=0;
+ chan->priv->wrbuffers=(wrbuffer*)k_malloc(WRSIZE*sizeof(wrbuffer));
+ chan->priv->wrbufpos=0;
+ chan->priv->wrbufsize=WRSIZE;
+ chan->priv->lingercount=LINGER_LOOPS;
+
+ if(1){
+ set_callback(chan, SETCB_RD);
+ }
+ if(chan->listenhost){
+ set_callback(chan, SETCB_WR);
+ }
+
+ chan->next=k_channels;
+ k_channels=chan;
+
+ return chan;
+}
+
+void unregister_k_channel(k_channel* chan, int now)
+{
+ int closing=CHAN_CLOSING(chan);
+ if(closing && !now) return;
+ int linger=chan->linger && !now;
+ chan->priv->state=linger? CHAN_LINGER: CHAN_CLOSE;
+ if(linger) un_set_callback(chan, SETCB_WR);
+ else un_set_callback(chan, SETCB_WR | SETCB_RD);
+ if(closing) return;
+ chan_closed(chan);
+ if(chan->rdcallback)(*chan->rdcallback)(chan, chan->priv->rdbufpos, -1);
+ else
+ if(chan->wrcallback)(*chan->wrcallback)(chan, chan->priv->wrbufpos, -1);
+}
+
+void chan_closed(k_channel* chan)
+{
+ if(0) k_log_out("chan_closed %s", chan->name);
+ k_hashtable_remove(connecting_chans, chan->name);
+ k_hashtable_remove(current_chans, chan->name);
+ if(0) k_channel_show_all();
+}
+
+void do_regular_things(void)
+{
+ tick_modules();
+ remove_dead_k_channels();
+}
+
+void tick_modules(void)
+{
+ int i;
+ for(i=0; i< nextmodule; i++){
+ k_module_tick_fn tf=modules[i].tick_fn;
+ if(tf) (*tf)();
+ }
+}
+
+void remove_dead_k_channels(void)
+{
+ int i;
+ k_channel* cn;
+ k_channel* cp=0;
+ k_channel* cc=k_channels;
+ while(cc){
+ cn=cc->next;
+ if( cc->priv->state==CHAN_CLOSE ||
+ (cc->priv->state==CHAN_LINGER && !cc->priv->lingercount--)){
+ un_set_callback(cc, SETCB_WR | SETCB_RD);
+ if(!cp) k_channels=cn;
+ else cp->next =cn;
+ SOCKET_CLOSE(cc->priv->SOCK);
+ for(i=0; i< cc->priv->wrbufpos; i++){
+ k_free(cc->priv->wrbuffers[i].free);
+ }
+ k_free(cc->priv->wrbuffers);
+ k_free(cc->priv->rdbuffer);
+ k_free(cc->priv);
+ k_free(cc->name);
+ k_free(cc);
+ }
+ else cp=cc;
+ cc=cn;
+ }
+}
+
+k_channel* find_k_channel_for_socket(SOCK_T s)
+{
+ k_channel* chan;
+ for(chan=k_channels; chan; chan=chan->next){
+ if(chan->priv->SOCK==s) break;
+ }
+ return chan;
+}
+
+void show_open_channels(void)
+{
+ k_channel* chan=k_channels;
+ while(chan){
+ if(chan->priv->state==CHAN_OPEN ){
+ if(0) k_log_out("chan open %x", chan->priv->SOCK);
+ }
+ if(chan->priv->state==CHAN_LINGER){
+ if(0) k_log_out("chan lingering %x", chan->priv->SOCK);
+ }
+ if(chan->priv->state==CHAN_CLOSE ){
+ if(0) k_log_out("chan closed %x", chan->priv->SOCK);
+ }
+ chan=chan->next;
+ }
+}
+
+void readable_socket(k_channel* chan)
+{
+ if(chan->type==CHAN_LISTEN){
+ register_connections(chan);
+ }
+ else{
+ read_more_and_notify(chan);
+ }
+}
+
+void writeable_socket(k_channel* chan)
+{
+ write_more_and_notify(chan);
+}
+
+void exception_socket(k_channel* chan)
+{
+ if(0) k_log_out("exception_socket");
+ unregister_k_channel(chan,1);
+}
+
+void register_connections(k_channel* chan)
+{
+ SOCK_T as=chan->priv->SOCK;
+ struct sockaddr_in iaddr;
+ struct sockaddr* iaddrp=(struct sockaddr*)&iaddr;
+ socklen_t size=sizeof(struct sockaddr_in);
+ while(1){
+ SOCK_T s;
+ int r=ACCEPT(as, iaddrp, &size, s);
+ GETERRNO(r);
+ if(r== -1){
+ if(ERRNO==INTERRUPTED) break;
+ if(ISNOTACTIVE(ERRNO)) break;
+ log_net_err("accept", ERRNO);
+ break;
+ }
+ SET_NON_BLOCKING(s);
+ SET_NO_DELAY(s);
+
+ if(chan->banner){
+ SOCKET_WRITE(s, chan->banner, strlen(chan->banner));
+ }
+
+ k_channel* chann=register_k_channel(chan->name,
+ 0,
+ chan->listenport,
+ chan->banner,
+ chan->rdcallback,
+ chan->wrcallback,
+ chan->context,
+ CHAN_ESTABL,
+ iaddr.sin_addr,
+ s);
+ gen_conn_name(chann);
+ k_hashtable_set(current_chans, chann->name, chann);
+ if(chann->rdcallback) (*chann->rdcallback)(chann, 0, 0);
+ }
+}
+
+void read_more_and_notify(k_channel* chan)
+{
+ int pos=chan->priv->rdbufpos;
+ int size=SOCKET_READ(chan->priv->SOCK,
+ chan->priv->rdbuffer +pos,
+ chan->priv->rdbufsize-pos);
+ GETERRNO(size);
+ if(size==0){ size= -1; ERRNO=EPIPE; }
+ if(size== -1){
+ if(ERRNO==INTERRUPTED) return;
+ if(ISNOTACTIVE(ERRNO)) return;
+ log_net_err("socket_read", ERRNO);
+ unregister_k_channel(chan,1);
+ return;
+ }
+ if(CHAN_CLOSING(chan)) return;
+
+ chan->priv->rdbufpos+=size;
+
+ if(chan->rdcallback){
+ (*chan->rdcallback)(chan, chan->priv->rdbufpos, size);
+ }
+
+ if(chan->priv->rdbufpos==chan->priv->rdbufsize){
+ if(chan->priv->rdbufforeign){
+ k_log_err("Run out of room in given buffer");
+ k_log_err("rdbufsize=%d", chan->priv->rdbufsize);
+ unregister_k_channel(chan,1);
+ return;
+ }
+ chan->priv->rdbufsize*=2;
+ chan->priv->rdbuffer=k_realloc(chan->priv->rdbuffer,
+ chan->priv->rdbufsize);
+ if(0) k_log_out("rdbufsize=%d", chan->priv->rdbufsize);
+ // DoS vulnerability unless buffer size limited
+ }
+}
+
+void write_more_and_notify(k_channel* chan)
+{
+ if(!chan->priv->wrbufpos){
+ if(chan->name){
+ k_hashtable_remove(connecting_chans, chan->name);
+ k_hashtable_set( current_chans, chan->name, chan);
+ }
+ if(chan->wrcallback) (*chan->wrcallback)(chan, 0, 0);
+ if(!chan->priv->wrbufpos) un_set_callback(chan, SETCB_WR); // Eerrk?
+ return;
+ }
+
+ int writemax=MIN(WRITEMAX, chan->priv->wrbuffers[0].size);
+ int size=SOCKET_WRITE(chan->priv->SOCK,
+ chan->priv->wrbuffers[0].base,
+ writemax);
+ GETERRNO(size);
+ if(size==0){ size= -1; ERRNO=NOTACTIVE; }
+ if(size== -1){
+ if(ERRNO==INTERRUPTED) return;
+ if(ISNOTACTIVE(ERRNO)) return;
+ log_net_err("socket_write", ERRNO);
+ unregister_k_channel(chan,1);
+ return;
+ }
+
+ chop_from_wrbuffers(chan, size);
+
+ if(chan->wrcallback){
+ (*chan->wrcallback)(chan, chan->priv->wrbufpos, size);
+ }
+
+ if(!chan->priv->wrbufpos) un_set_callback(chan, SETCB_WR);
+}
+
+char* strnstr(char* haystack, size_t size, char* needle) // Errk!
+{
+ if(!needle || !*needle) return haystack;
+ char* h;
+ char* e=haystack+size;
+ for(h=haystack; h < e && *h; h++){
+ char* i=h;
+ char* n=needle;
+ while(i < e && *i && *n && *i==*n){ i++; n++; }
+ if(!*n) return h;
+ }
+ return 0;
+}
+
+char* chop_from_rdbuffer_div(k_channel* chan, char* divider)
+{
+ char* s=strnstr(chan->priv->rdbuffer, chan->priv->rdbufpos, divider);
+ if(!s) return 0;
+ *s=0;
+ size_t size=(s+1 - chan->priv->rdbuffer);
+ s+=strlen(divider);
+ char* chunk=k_memdup(chan->priv->rdbuffer, size);
+ char* e=chan->priv->rdbuffer+chan->priv->rdbufpos;
+ memmove(chan->priv->rdbuffer, s, e-s);
+ chan->priv->rdbufpos=e-s;
+ return chunk;
+}
+
+char* chop_from_rdbuffer_len(k_channel* chan, size_t size)
+{
+ if(!size || size > chan->priv->rdbufpos) return 0;
+ char* chunk=k_memdup(chan->priv->rdbuffer, size);
+ char* s=chan->priv->rdbuffer+size;
+ char* e=chan->priv->rdbuffer+chan->priv->rdbufpos;
+ memmove(chan->priv->rdbuffer, s, e-s);
+ chan->priv->rdbufpos=e-s;
+ return chunk;
+}
+
+int set_rdbuffer(k_channel* chan, char* rdbuffer, size_t size)
+{
+ if(chan->priv->rdbufforeign) return BUFFER_ALREADY_SET;
+ size_t pos=chan->priv->rdbufpos;
+ if(pos>=size){
+ memcpy(rdbuffer, chan->priv->rdbuffer, size);
+ char* s=chan->priv->rdbuffer+size;
+ char* e=chan->priv->rdbuffer+pos;
+ memmove(chan->priv->rdbuffer, s, e-s);
+ chan->priv->rdbufpos=e-s;
+ return BUFFER_FILLED;
+ }
+ memcpy(rdbuffer, chan->priv->rdbuffer, pos);
+ k_free(chan->priv->rdbuffer);
+ chan->priv->rdbuffer=rdbuffer;
+ chan->priv->rdbufsize=size;
+ chan->priv->rdbufforeign=1;
+ return BUFFER_SET;
+}
+
+char* get_rdbuffer(k_channel* chan)
+{
+ if(!chan->priv->rdbufforeign) return 0;
+ char* rdbuffer=chan->priv->rdbuffer;
+ chan->priv->rdbuffer=(char*)k_malloc(RDSIZE);
+ chan->priv->rdbufpos=0;
+ chan->priv->rdbufsize=RDSIZE;
+ chan->priv->rdbufforeign=0;
+ return rdbuffer;
+}
+
+void append_to_wrbuffers(k_channel* chan, char* base, size_t size, int free)
+{
+ if(chan->priv->wrbufpos==chan->priv->wrbufsize){
+ chan->priv->wrbufsize*=2;
+ chan->priv->wrbuffers=k_realloc(chan->priv->wrbuffers,
+ chan->priv->wrbufsize *
+ sizeof(wrbuffer));
+ if(0) k_log_out("wrbufsize=%d", chan->priv->wrbufsize);
+ }
+ chan->priv->wrbuffers[chan->priv->wrbufpos].base=base;
+ chan->priv->wrbuffers[chan->priv->wrbufpos].size=size;
+ chan->priv->wrbuffers[chan->priv->wrbufpos].free=free? base: 0;
+ chan->priv->wrbufpos++;
+}
+
+void chop_from_wrbuffers(k_channel* chan, size_t size)
+{
+ if(chan->priv->wrbuffers[0].size != size){
+ chan->priv->wrbuffers[0].base+=size;
+ chan->priv->wrbuffers[0].size-=size;
+ }
+ else{
+ k_free(chan->priv->wrbuffers[0].free);
+ chan->priv->wrbufpos--;
+ int i;
+ for(i=0; i< chan->priv->wrbufpos; i++){
+ chan->priv->wrbuffers[i]=chan->priv->wrbuffers[i+1];
+ }
+ }
+}
+
+/* -------------------------------------------------------------------------- */
+
+SOCK_T make_listen_socket(int listenport)
+{
+ SOCK_T s;
+ int r=SOCKET(AF_INET, SOCK_STREAM, IPPROTO_TCP, s);
+ GETERRNO(r);
+ if(r== -1){
+ log_net_err("socket", ERRNO);
+ return 0;
+ }
+ SET_NON_BLOCKING(s);
+ SET_REUSEADDR(s);
+
+ struct sockaddr_in iaddr;
+ struct sockaddr* iaddrp=(struct sockaddr*)&iaddr;
+ iaddr=make_sockaddr_in(INADDR_ANY, listenport);
+
+ r=BIND(s, iaddrp, sizeof(iaddr));
+ GETERRNO(r);
+ if(r== -1){
+ log_net_err("bind", ERRNO);
+ SOCKET_CLOSE(s);
+ return 0;
+ }
+
+ return s;
+}
+
+SOCK_T make_connect_socket(void)
+{
+ SOCK_T s;
+ int r=SOCKET(AF_INET, SOCK_STREAM, IPPROTO_TCP, s);
+ GETERRNO(r);
+ if(r== -1){
+ log_net_err("socket", ERRNO);
+ return 0;
+ }
+ SET_NON_BLOCKING(s);
+ SET_NO_DELAY(s);
+ return s;
+}
+
+int listen_socket(SOCK_T s, int listenport)
+{
+ if(0) k_log_out("Listening on port %d", listenport);
+ int r=LISTEN(s, 128);
+ GETERRNO(r);
+ if(r== -1){
+ log_net_err("listen", ERRNO);
+ SOCKET_CLOSE(s);
+ return -1;
+ }
+ return 0;
+}
+
+int connect_socket(SOCK_T s, char* listenhost, int listenport)
+{
+ if(0) k_log_out("Hanging on gethostbyname for %s", listenhost);
+ struct hostent* hostaddr=gethostbyname(listenhost);
+ if(!hostaddr){
+ SOCKET_CLOSE(s);
+ return -1;
+ }
+
+ struct sockaddr_in iaddr;
+ struct sockaddr* iaddrp=(struct sockaddr*)&iaddr;
+ iaddr=make_sockaddr_in(0, listenport);
+ memcpy(&iaddr.sin_addr, hostaddr->h_addr, hostaddr->h_length);
+
+ if(0) k_log_out("Connect: %s(%s):%d ",
+ listenhost, inet_ntoa(iaddr.sin_addr), listenport);
+ do{
+ int r=CONNECT(s, iaddrp, sizeof(iaddr));
+ GETERRNO(r);
+ if(r== -1 && ERRNO==INTERRUPTED) continue;
+ if(r== -1 && !ISCONNECTING(ERRNO)){
+ log_net_err("connect", ERRNO);
+ SOCKET_CLOSE(s);
+ return -1;
+ }
+ break;
+ }while(1);
+
+ return 0;
+}
+
+SOCK_T make_udp_listen_socket(int listenport)
+{
+ SOCK_T s;
+ int r;
+ struct sockaddr_in iaddr;
+ struct sockaddr* iaddrp=(struct sockaddr*)&iaddr;
+
+ r=SOCKET(AF_INET, SOCK_DGRAM, IPPROTO_UDP, s);
+ GETERRNO(r);
+ if(r== -1){
+ log_net_err("socket", ERRNO);
+ return 0;
+ }
+
+ SET_REUSEADDR(s);
+
+ iaddr=make_sockaddr_in(INADDR_ANY, listenport);
+
+ r=BIND(s, iaddrp, sizeof(iaddr));
+ GETERRNO(r);
+ if(r== -1){
+ log_net_err("bind", ERRNO);
+ SOCKET_CLOSE(s);
+ return 0;
+ }
+
+ return s;
+}
+
+EXPORT SOCK_T make_udp_send_socket(char* listenhost, int listenport)
+{
+ SOCK_T s;
+ int r;
+ struct sockaddr_in iaddr;
+ struct sockaddr* iaddrp=(struct sockaddr*)&iaddr;
+
+ r=SOCKET(AF_INET, SOCK_DGRAM, IPPROTO_UDP, s);
+ GETERRNO(r);
+ if(r== -1){
+ log_net_err("socket", ERRNO);
+ return 0;
+ }
+
+ struct hostent* hostaddr=gethostbyname(listenhost);
+ if(!hostaddr){
+ log_net_err("gethostbyname", h_errno);
+ SOCKET_CLOSE(s);
+ return 0;
+ }
+
+ iaddr=make_sockaddr_in(0, listenport);
+ memcpy(&iaddr.sin_addr, hostaddr->h_addr, hostaddr->h_length);
+
+ r=CONNECT(s, iaddrp, sizeof(iaddr));
+ GETERRNO(r);
+ if(r== -1){
+ log_net_err("connect", ERRNO);
+ SOCKET_CLOSE(s);
+ return 0;
+ }
+
+ return s;
+}
+
+struct sockaddr_in make_sockaddr_in(unsigned long address, unsigned int port)
+{
+ struct sockaddr_in iaddr;
+ struct sockaddr* iaddrp=(struct sockaddr*)&iaddr;
+ memset(iaddrp, 0, sizeof(iaddr));
+ iaddr.sin_addr.s_addr=htonl(address);
+ iaddr.sin_port =htons(port);
+ iaddrp->sa_family=AF_INET;
+ return iaddr;
+}
+
+void log_net_err(char* where, int e)
+{
+ if(0) k_log_out("Network (%s): %s (%d)", where, str_error(e), e);
+}
+
+/* -------------------------------------------------------------------------- */
+
+EXPORT void k_file_stat(char* basedir,
+ char* filename,
+ k_file_event callback,
+ void* context)
+{
+ snprintf(tmpbuf, TMPBUFSIZE, "%s/%s", basedir, filename);
+ char* fullname=tmpbuf;
+
+ k_stat kstat={0,0,0,0};
+
+ stat_only(fullname, &kstat);
+ (*callback)(basedir, filename, 0, 0, kstat, context);
+}
+
+EXPORT void k_file_read(char* basedir,
+ char* filename,
+ size_t mmapsize,
+ size_t size,
+ k_file_event callback,
+ void* context)
+{
+ snprintf(tmpbuf, TMPBUFSIZE, "%s/%s", basedir, filename);
+ char* fullname=tmpbuf;
+
+ FILE_T filehandle;
+ k_stat kstat={0,0,0,0};
+
+ int write=!!size;
+ if(write && !ensure_dir(fullname)){
+ (*callback)(basedir, filename, 0, 0, kstat, context);
+ return;
+ }
+
+ stat_only(fullname, &kstat);
+ if(!kstat.type && !write){
+ (*callback)(basedir, filename, 0, 0, kstat, context);
+ return;
+ }
+ filehandle=open_only(fullname, write);
+ if(!filehandle){
+ (*callback)(basedir, filename, 0, 0, kstat, context);
+ return;
+ }
+ if(!kstat.type || (write && kstat.size!=size)){
+ if(ftruncate(filehandle, size)){
+ FCLOSE(filehandle);
+ (*callback)(basedir, filename, 0, 0, kstat, context);
+ return;
+ }
+ }
+ FCLOSE(filehandle);
+
+ if(!write) size=kstat.size;
+ else kstat.size=size;
+
+ if(size>MAX_FILESIZE){
+ (*callback)(basedir, filename, 0, 0, kstat, context);
+ return;
+ }
+ int dommap=(size >= mmapsize);
+
+ int prot=(write? PROT_WRITE: 0)|PROT_READ;
+
+ char* data;
+
+ if(size==0) data=dommap? dummy_empty_data: k_malloc(1);
+ else
+ if(dommap) data=mmap_name( 0, size, prot, MAP_SHARED, fullname, 0);
+ else data=mmap_malloc(0, size, prot, MAP_SHARED, fullname, 0);
+
+ if(data==MAP_FAILED){
+ (*callback)(basedir, filename, 0, 0, kstat, context);
+ return;
+ }
+ (*callback)(basedir, filename, data, dommap, kstat, context);
+}
+
+EXPORT int k_file_sync(char* data, size_t size)
+{
+ int r=msync(data, size, MS_ASYNC);
+ return !r;
+}
+
+EXPORT void k_file_write(char* basedir,
+ char* filename,
+ char* data,
+ size_t datalength,
+ k_file_event callback,
+ void* context)
+{
+ snprintf(tmpbuf, TMPBUFSIZE, "%s/%s", basedir, filename);
+ char* fullname=tmpbuf;
+
+ FILE_T filehandle;
+ k_stat kstat={0,0,0,0};
+
+ if(!ensure_dir(fullname)){
+ (*callback)(basedir, filename, 0, 0, kstat, context);
+ return;
+ }
+
+ filehandle=open_only(fullname, 1);
+ if(!filehandle){
+ (*callback)(basedir, filename, 0, 0, kstat, context);
+ return;
+ }
+ if(datalength!=0) kstat.size=WRITEFILE(filehandle, data, datalength);
+ if(kstat.size== -1){
+ FCLOSE(filehandle);
+ (*callback)(basedir, filename, 0, 0, kstat, context);
+ return;
+ }
+ FCLOSE(filehandle);
+ (*callback)(basedir, filename, data, 0, kstat, context);
+}
+
+EXPORT void k_dir_list(char* basedir,
+ char* dirname,
+ char* pattern[],
+ char* format[],
+ k_file_event callback,
+ void* context)
+{
+ snprintf(tmpbuf, TMPBUFSIZE, "%s/%s", basedir, dirname);
+ char* fullname=k_strdup(tmpbuf);
+
+ k_stat kstat={ STAT_D, 0, 0, 0 };
+ char* buf=tmpbuf;
+ int size=TMPBUFSIZE;
+ int i;
+ for(i=0; format[i]; i++){
+ int n=dir_list(buf, size, fullname, pattern[i], format[i]);
+ if(n== -1){
+ (*callback)(basedir, dirname, 0, 0, kstat, context);
+ k_free(fullname);
+ return;
+ }
+ buf+=n; size-=n;
+ }
+ kstat.size=TMPBUFSIZE-size;
+ (*callback)(basedir, dirname, k_strdup(tmpbuf), 0, kstat, context);
+ k_free(fullname);
+}
+
+int dir_list(char* buf, int size, char* fullname, char* pattern, char* format)
+{
+ DIR* d=opendir(fullname);
+ if(!d) return -1;
+
+ char* b=buf;
+ int s=size;
+ *b=0;
+ struct dirent* de;
+ while((de=readdir(d))){
+
+ char* name=de->d_name;
+ if(*name=='.') continue;
+
+ if(pattern && *pattern &&
+ !k_string_matches_pattern(name, pattern)) continue;
+
+ int n=snprintf(b, s, format, name, name, name, name, name);
+ if(n< 0 || n>=s){ closedir(d); return -1; }
+ b+=n; s-=n;
+ }
+ closedir(d);
+ return size-s;
+}
+
+int ensure_dir(char* fullname)
+{
+ char* e=strrchr(fullname, '/');
+ if(!e) return 1;
+ *e=0;
+ k_stat kstat={0,0,0,0};
+ stat_only(fullname, &kstat);
+ if(kstat.type!=STAT_D){
+ MKDIR(fullname);
+ stat_only(fullname, &kstat);
+ }
+ *e='/';
+ return kstat.type==STAT_D;
+}
+
+/* -------------------------------------------------------------------------- */
+
+EXPORT int k_string_matches_pattern(char* string, char* pattern)
+{
+ char patt[32];
+ strncpy(patt, pattern, 32);
+ patt[31]=0;
+ pattern=patt;
+ if(*pattern=='{'){
+ while(1){
+ pattern++;
+ char* e=strpbrk(pattern, ",}");
+ if(!e) return 0;
+ char x=*e; *e=0;
+ if(k_string_matches_pattern(string, pattern)) return 1;
+ *e=x;
+ pattern=e;
+ }
+ }
+ else
+ if(*pattern=='*'){
+ pattern++;
+ return k_string_ends_with(string, pattern);
+ }
+ return 0;
+}
+
+EXPORT int k_string_ends_with(char* string, char* postfix)
+{
+ char* i=string+strlen(string)-strlen(postfix);
+ if(i<string) return 0;
+ return !strcmp(i, postfix);
+}
+
+EXPORT void k_string_url_decode(char* s)
+{
+ char* t=s;
+ while(*s){
+ if(s[0]=='%' && isxdigit(s[1]) && isxdigit(s[2])){
+ *t=hex_to_int(s[1])*16+hex_to_int(s[2]);
+ s+=3; t++;
+ }
+ else{
+ *t=*s;
+ s++; t++;
+ }
+ }
+ *t=0;
+}
+
+char hex_to_int(char c)
+{
+ return
+ (c >= '0' && c <= '9')? c-'0':
+ (c >= 'A' && c <= 'F')? c-'A'+10:
+ (c >= 'a' && c <= 'f')? c-'a'+10: 0;
+}
+
+/* -------------------------------------------------------------------------- */
+
+EXPORT void k_log_out(char* format, ...)
+{
+ va_list ap;
+ va_start(ap, format);
+ if(LOG_TO_STD){
+ VPRINTFOUT(format,ap);
+ PRINTFOUT("\n");
+ FFLUSH(stdout);
+ }
+ else{
+ vsnprintf(logbuf, LOGBUFSIZE,format,ap);
+ *(logbuf+LOGBUFSIZE-2)='\n';
+ *(logbuf+LOGBUFSIZE-1)=0;
+ write_to_logfile_cern_style(logbuf,0);
+ }
+ va_end(ap);
+}
+
+EXPORT void k_log_err(char* format, ...)
+{
+ va_list ap;
+ va_start(ap, format);
+ if(LOG_TO_STD){
+ VPRINTFERR(format,ap);
+ PRINTFERR("\n");
+ FFLUSH(stderr);
+ }
+ else{
+ vsnprintf(logbuf, LOGBUFSIZE,format,ap);
+ *(logbuf+LOGBUFSIZE-2)='\n';
+ *(logbuf+LOGBUFSIZE-1)=0;
+ write_to_logfile_cern_style(logbuf,1);
+ }
+ va_end(ap);
+}
+
+EXPORT void k_fatal(char* format, ...)
+{
+ va_list ap;
+ va_start(ap, format);
+ k_log_out(format, ap);
+ va_end(ap);
+ k_log_out("Terminating Cilux");
+ k_log_out("---------------------");
+ EXIT(1);
+}
+
+void write_to_logfile_cern_style(char* text, int error)
+{
+ if(!logfile) return;
+
+ time_t now=time(0);
+ struct tm* tm;
+ int z;
+ if(log_gmt){
+ tm=gmtime(&now);
+ z=0;
+ }
+ else{
+ tm=localtime(&now);
+ z=TIMEZONE(tm)/60;
+ }
+ char sgn;
+ if(z >=0){ sgn='+'; }
+ else{ sgn='-'; z= -z; }
+
+ char cern[64];
+ char date[64];
+ strftime(cern, sizeof(cern), CERNDATE, tm);
+#ifdef SHOW_ZONE
+ snprintf(date, sizeof(date), "%s %c%02d%02d", cern, sgn, z/60, z%60);
+#else
+ snprintf(date, sizeof(date), "%s", cern);
+#endif
+ FPRINTF(logfile, "[%s|%s] %s%s\n", date, k_ciux,
+ error? "Warning: ": "", text);
+ FFLUSH(logfile);
+}
+
+/* -------------------------------------------------------------------------- */
+
+#define EXAMPLE_DATE Fri, 04 Feb 2005 12:14:48 GMT
+#define RFC1123STRF "%a, %d %b %Y %H:%M:%S GMT"
+#define RFC1123SSCANF "%3s, %d %3s %4d %2d:%2d:%2d GMT"
+
+static char b[100];
+
+EXPORT char* k_time_to_rfc(time_t t)
+{
+ strftime(b, sizeof(b), RFC1123STRF, gmtime(&t));
+ return b;
+}
+
+EXPORT char* k_time_to_rfc_relative(int plus)
+{
+ time_t t=time(0)+plus;
+ strftime(b, sizeof(b), RFC1123STRF, gmtime(&t));
+ return b;
+}
+
+EXPORT time_t k_time_from_rfc(char* s)
+{
+ if(!s || strlen(s)!=29) return -1;
+ struct tm tm; tm.tm_wday=0; tm.tm_yday=0; tm.tm_isdst=0;
+ char wd[32], mn[32];
+ int r=sscanf(s, RFC1123SSCANF, wd, &tm.tm_mday, mn, &tm.tm_year,
+ &tm.tm_hour, &tm.tm_min, &tm.tm_sec);
+ if(r!=7) return -1;
+ tm.tm_mon=(int)k_hashtable_get(monthhash, mn);
+ if(tm.tm_mon) tm.tm_mon -=1;
+ tm.tm_year-=1900;
+ return MKTIME(&tm);
+}
+
+EXPORT void k_time_get_now(char* buf, size_t size, char* format)
+{
+ time_t now=time(0);
+ struct tm* tm;
+ int gmt=1;
+ if(gmt){
+ tm=gmtime(&now);
+ }
+ else{
+ tm=localtime(&now);
+ }
+ strftime(buf, size, format, tm);
+}
+
+/* -------------------------------------------------------------------------- */
+
+typedef struct hash_item hash_item;
+
+typedef struct hashtable{ struct hashtable* next;
+ char* name;
+ int buckets;
+ int size;
+ hash_item** lists;
+ int ignorecase;
+} hashtable;
+
+struct hash_item{ hash_item* next;
+ char* key;
+ void* val;
+ int free;
+ k_hashtable* sub;
+};
+
+/* -------------------------------------------------------------------------- */
+
+#define STRCMPCASE(tab, listkey, key)\
+((tab)->ignorecase? strcasecmp((listkey), (key)): strcmp((listkey), (key)))
+#define BX 30
+#define WARN_SIZE(h)\
+if((h)->size && !((h)->size % BX)) k_log_out("%s# %d", (h)->name, (h)->size);
+
+EXPORT k_hashtable* k_hashtable_new(char* name, int ignorecase)
+{
+ hashtable* tab;
+ tab=k_malloc(sizeof(hashtable));
+ tab->name=name;
+ tab->buckets=BX;
+ tab->size=0;
+ tab->lists=k_malloc((tab->buckets)*sizeof(hash_item*));
+ tab->ignorecase=ignorecase;
+ tab->next=0;
+ int i; for(i=0; i< tab->buckets; i++) tab->lists[i]=0;
+ return (k_hashtable*)tab;
+}
+
+k_hashtable* k_hashtable_dup2(k_hashtable* tab, int deep)
+{
+ if(!tab) return 0;
+ hashtable* tah=(hashtable*)tab;
+ hashtable* tad;
+ tad=k_malloc(sizeof(hashtable));
+ tad->name=tah->name;
+ tad->buckets=tah->buckets;
+ tad->size=tah->size;
+ tad->lists=k_malloc((tah->buckets)*sizeof(hash_item*));
+ tad->ignorecase=tah->ignorecase;
+ tad->next=0;
+ hash_item** lisp=0;
+ hash_item** lisd=0;
+ int i;
+ for(i=0; i< tah->buckets; i++){
+ for(lisp=&tah->lists[i], lisd=&tad->lists[i];
+ (*lisp);
+ lisp=&(*lisp)->next, lisd=&(*lisd)->next){
+
+ (*lisd)=k_malloc(sizeof(hash_item));
+ (*lisd)->key=k_strdup((*lisp)->key);
+ (*lisd)->val=0;
+ (*lisd)->free=0;
+ (*lisd)->sub=0;
+ if((*lisp)->val){
+ (*lisd)->val=k_strdup((*lisp)->val);
+ (*lisd)->free=1;
+ }
+ if(deep && (*lisp)->sub){
+ (*lisd)->sub=k_hashtable_dup2((*lisp)->sub, 1);
+ }
+ if(!(*lisd)->val && !(*lisd)->sub){
+ (*lisd)->val="...";
+ }
+ }
+ (*lisd)=0;
+ }
+ tad->next=(hashtable*)k_hashtable_dup2(tab->next, deep);
+ return (k_hashtable*)tad;
+}
+
+EXPORT k_hashtable* k_hashtable_dup(k_hashtable* tab)
+{
+ return k_hashtable_dup2(tab, 0);
+}
+
+EXPORT k_hashtable* k_hashtable_deep(k_hashtable* tab)
+{
+ return k_hashtable_dup2(tab, 1);
+}
+
+EXPORT k_hashtable* k_hashtable_dip(k_hashtable* tab, char* includes[])
+{
+ if(!tab) return 0;
+ hashtable* tah=(hashtable*)tab;
+ hashtable* tad;
+ tad=(hashtable*)k_hashtable_new(tah->name, tah->ignorecase);
+ int i;
+ for(i=0; includes[i]; i++){
+ char* in=k_hashtable_get((k_hashtable*)tah, includes[i]);
+ if(!in) continue;
+ in=k_strdup(in);
+ k_hashtable_put((k_hashtable*)tad, includes[i], in);
+ }
+ tad->next=(hashtable*)k_hashtable_dip(tab->next, includes);
+ return (k_hashtable*)tad;
+}
+
+EXPORT void k_hashtable_merge(k_hashtable* tab, k_hashtable* tam)
+{
+ hashtable* tan=(hashtable*)tam;
+ hash_item** lisp;
+ int i;
+ for(i=0; i< tan->buckets; i++){
+ for(lisp=&tan->lists[i]; (*lisp); lisp=&(*lisp)->next){
+ char* key =(*lisp)->key;
+ void* val =(*lisp)->val;
+ int free=(*lisp)->free;
+ k_hashtable* sub =(*lisp)->sub;
+ if(!*key && free) continue;
+ if(val){
+ k_hashtable_put(tab, key, k_strdup(val));
+ }
+ if(sub){
+ k_hashtable_sub(tab, key, k_hashtable_dup(sub));
+ }
+ }
+ }
+}
+
+void k_hashtable_set_put(k_hashtable* tab, char* key, void* val, int free)
+{
+ if(!(tab && key && val)) return;
+ hashtable* tah=(hashtable*)tab;
+ hash_item** lisp;
+ lisp=&tah->lists[string_hash(key) % tah->buckets];
+ while((*lisp) && STRCMPCASE(tah, (*lisp)->key, key)){
+ lisp=&(*lisp)->next;
+ }
+ if(!(*lisp)){
+ (*lisp)=k_malloc(sizeof(hash_item));
+ (*lisp)->key=k_strdup(key);
+ (*lisp)->val=val;
+ (*lisp)->free=free;
+ (*lisp)->sub=0;
+ (*lisp)->next=0;
+ tah->size++;
+ WARN_SIZE(tah);
+ }
+ else{
+ if((*lisp)->free) k_free((*lisp)->val);
+ k_hashtable_delete((*lisp)->sub);
+ (*lisp)->val=val;
+ (*lisp)->free=free;
+ (*lisp)->sub=0;
+ }
+}
+
+EXPORT void k_hashtable_set(k_hashtable* tab, char* key, void* val)
+{
+ k_hashtable_set_put(tab, key, val, 0);
+}
+
+EXPORT void k_hashtable_put_int(k_hashtable* tab, char* key, int val)
+{
+ snprintf(tmpbuf, TMPBUFSIZE, "%d", val);
+ k_hashtable_set_put(tab, key, k_strdup(tmpbuf), 1);
+}
+
+EXPORT void k_hashtable_put(k_hashtable* tab, char* key, void* val)
+{
+ k_hashtable_set_put(tab, key, val, 1);
+}
+
+EXPORT void k_hashtable_put_dup(k_hashtable* tab, char* key, char* val)
+{
+ k_hashtable_set_put(tab, key, k_strdup(val), 1);
+}
+
+EXPORT void k_hashtable_sub(k_hashtable* tab, char* key, k_hashtable* sub)
+{
+ if(!(tab && key && sub)) return;
+ sub->next=0;
+ hashtable* tah=(hashtable*)tab;
+ hash_item** lisp;
+ lisp=&tah->lists[string_hash(key) % tah->buckets];
+ while((*lisp) && STRCMPCASE(tah, (*lisp)->key, key)){
+ lisp=&(*lisp)->next;
+ }
+ if(!(*lisp)){
+ (*lisp)=k_malloc(sizeof(hash_item));
+ (*lisp)->key=k_strdup(key);
+ (*lisp)->val=0;
+ (*lisp)->free=0;
+ (*lisp)->sub=sub;
+ (*lisp)->next=0;
+ tah->size++;
+ WARN_SIZE(tah);
+ }
+ else{
+ k_hashtable* val =(*lisp)->val;
+ k_hashtable* head=(*lisp)->sub;
+ if(!head){
+ if(val) k_log_err("key '%s' in use: no sub!", key);
+ else (*lisp)->sub=sub;
+ }
+ else{
+ while(head->next) head=head->next;
+ head->next=sub;
+ }
+ }
+}
+
+EXPORT void* k_hashtable_get(k_hashtable* tab, char* key)
+{
+ if(!(tab && key)) return 0;
+ hashtable* tah=(hashtable*)tab;
+ hash_item* list;
+ list=tah->lists[string_hash(key) % tah->buckets];
+ while(list && STRCMPCASE(tah, list->key, key)){
+ list=list->next;
+ }
+ return list? (list->val? list->val: list->sub): 0;
+}
+
+EXPORT int k_hashtable_get_int(k_hashtable* tab, char* key)
+{
+ char* val=k_hashtable_get(tab, key);
+ return val? atoi(val): 0;
+}
+
+EXPORT char* k_hashtable_get_dup(k_hashtable* tab, char* key)
+{
+ char* v=k_hashtable_get(tab, key);
+ return k_strdup(v);
+}
+
+EXPORT int k_hashtable_is(k_hashtable* tab, char* key, char* val)
+{
+ char* v=k_hashtable_get(tab, key);
+ return (v && val && !strcmp(v, val));
+}
+
+EXPORT int k_hashtable_isi(k_hashtable* tab, char* key, char* val)
+{
+ char* v=k_hashtable_get(tab, key);
+ return (v && val && !strcasecmp(v, val));
+}
+
+EXPORT int k_hashtable_isn(k_hashtable* tab, char* key, char* val, size_t size)
+{
+ char* v=k_hashtable_get(tab, key);
+ return (v && val && !strncmp(v, val, size));
+}
+
+EXPORT void* k_hashtable_extract(k_hashtable* tab, char* key)
+{
+ hashtable* tah=(hashtable*)tab;
+ hash_item** lisp;
+ hash_item* next;
+ lisp=&tah->lists[string_hash(key) % tah->buckets];
+ while((*lisp) && STRCMPCASE(tah, (*lisp)->key, key)){
+ lisp=&(*lisp)->next;
+ }
+ void* val=0;
+ if((*lisp)){
+ next= (*lisp)->next;
+ k_free((*lisp)->key);
+ val= (*lisp)->val;
+ if(!val) val=(*lisp)->sub;
+ k_free((*lisp));
+ (*lisp)=next;
+ tah->size--;
+ WARN_SIZE(tah);
+ }
+ return val;
+}
+
+EXPORT void k_hashtable_remove(k_hashtable* tab, char* key)
+{
+ hashtable* tah=(hashtable*)tab;
+ hash_item** lisp;
+ hash_item* next;
+ lisp=&tah->lists[string_hash(key) % tah->buckets];
+ while((*lisp) && STRCMPCASE(tah, (*lisp)->key, key)){
+ lisp=&(*lisp)->next;
+ }
+ if((*lisp)){
+ next= (*lisp)->next;
+ k_free((*lisp)->key);
+ if((*lisp)->free) k_free((*lisp)->val);
+ k_hashtable_delete((*lisp)->sub);
+ k_free((*lisp));
+ (*lisp)=next;
+ tah->size--;
+ WARN_SIZE(tah);
+ }
+}
+
+EXPORT void k_hashtable_delete(k_hashtable* tab)
+{
+ if(!tab) return;
+ hashtable* tah=(hashtable*)tab;
+ hash_item* list;
+ hash_item* next;
+ int i;
+ for(i=0; i< tah->buckets; i++){
+ list=tah->lists[i];
+ while(list){
+ next=list->next;
+ k_free(list->key);
+ if(list->free) k_free(list->val);
+ k_hashtable_delete(list->sub);
+ k_free(list);
+ list=next;
+ }
+ }
+ k_free(tah->lists);
+ k_hashtable_delete((k_hashtable*)tah->next);
+ k_free(tah);
+}
+
+EXPORT void k_hashtable_apply(k_hashtable* tab,
+ k_hashtable_apply_fn fn,
+ void* arg)
+{
+ if(!tab) return;
+ hashtable* tah=(hashtable*)tab;
+ hash_item** lisp;
+ int i;
+ for(i=0; i< tah->buckets; i++){
+ for(lisp=&tah->lists[i]; (*lisp); lisp=&(*lisp)->next){
+ if((*lisp)->val){
+ (*fn)(arg, (*lisp)->key, (*lisp)->val);
+ }
+ else{
+ (*fn)(arg, (*lisp)->key, (*lisp)->sub);
+ }
+ }
+ }
+}
+
+void k_hashtable_show2(k_hashtable* tab, int chars)
+{
+ if(!tab) return;
+ hashtable* tah=(hashtable*)tab;
+ if(!chars) k_log_out("%s size=%d", tah->name, tah->size);
+ hash_item** lisp;
+ int i;
+ for(i=0; i< tah->buckets; i++){
+ for(lisp=&tah->lists[i]; (*lisp); lisp=&(*lisp)->next){
+ if(chars){
+ if(*((*lisp)->key)){
+ k_log_out("%s %s",
+ (*lisp)->key, (char*)((*lisp)->val));
+ }
+ }
+ else{
+ k_log_out("buck %d key '%s' val %x sub %x",
+ i, (*lisp)->key, (*lisp)->val, (*lisp)->sub);
+ }
+ }
+ }
+}
+
+EXPORT void k_hashtable_show(k_hashtable* tab)
+{
+ k_hashtable_show2(tab,0);
+}
+
+EXPORT void k_hashtable_show_chars(k_hashtable* tab)
+{
+ k_hashtable_show2(tab,1);
+}
+
+int k_hashtable_snprintf_xi(k_hashtable* tab,
+ char* buf,
+ size_t size,
+ char* includes[],
+ char* excludes[],
+ int indent)
+{
+ if(!tab){ if(size) *buf=0; return 0; }
+ char ind[]=" ";
+ ind[indent]=0;
+ hashtable* tah=(hashtable*)tab;
+ *buf=0;
+ char* at=buf;
+ int ln=size;
+ hash_item** lisp;
+ int i;
+ for(i=0; i< tah->buckets; i++){
+ for(lisp=&tah->lists[i]; (*lisp); lisp=&(*lisp)->next){
+ if(!*((*lisp)->key)) continue;
+ if(check_i((*lisp)->key, includes, tah->ignorecase)) continue;
+ if(check_x((*lisp)->key, excludes, tah->ignorecase)) continue;
+ int n;
+ if((*lisp)->val){
+ n=snprintf(at, ln, "%s%s %s" CRLF, ind, (*lisp)->key,
+ (char*)((*lisp)->val));
+ if(n< 0) return n;
+ if(n>=ln) return size;
+ at+=n; ln-=n;
+ }
+ else{
+ k_hashtable* sub=(*lisp)->sub;
+ do{
+ n=snprintf(at, ln, "%s%s" CRLF,
+ ind, (*lisp)->key);
+ if(n< 0) return n;
+ if(n>=ln) return size;
+ at+=n; ln-=n;
+
+ n=k_hashtable_snprintf_xi(sub, at, ln, 0, 0,
+ indent+8);
+ if(n< 0) return n;
+ if(n>=ln) return size;
+ at+=n; ln-=n;
+
+ }while((sub=sub->next));
+ }
+ }
+ }
+ return size-ln;
+}
+
+EXPORT int k_hashtable_snprintf(k_hashtable* tab, char* buf, size_t size)
+{
+ return k_hashtable_snprintf_xi(tab, buf, size, 0, 0, 0);
+}
+
+EXPORT int k_hashtable_snprintf_i(k_hashtable* tab,
+ char* buf,
+ size_t size,
+ char* includes[])
+{
+ return k_hashtable_snprintf_xi(tab, buf, size, includes, 0, 0);
+}
+
+EXPORT int k_hashtable_snprintf_x(k_hashtable* tab,
+ char* buf,
+ size_t size,
+ char* excludes[])
+{
+ return k_hashtable_snprintf_xi(tab, buf, size, 0, excludes, 0);
+}
+
+int check_i(char* key, char* includes[], int ignorecase)
+{
+ if(!includes) return 0;
+ int e;
+ for(e=0; includes[e]; e++){
+ if(ignorecase? !strcasecmp(key, includes[e]):
+ !strcmp( key, includes[e]) ) break;
+ }
+ return !includes[e];
+}
+
+int check_x(char* key, char* excludes[], int ignorecase)
+{
+ if(!excludes) return 0;
+ int e;
+ for(e=0; excludes[e]; e++){
+ if(ignorecase? !strcasecmp(key, excludes[e]):
+ !strcmp( key, excludes[e]) ) break;
+ }
+ return !!excludes[e];
+}
+
+unsigned int string_hash(char* p)
+{
+ unsigned int h=0;
+ while(*p) h=(h<<5)-h+tolower(*p++);
+ return h;
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+