netcsconn revision
[doldaconnect.git] / daemon / filenet.c
1 /*
2  *  Dolda Connect - Modular multiuser Direct Connect-style client
3  *  Copyright (C) 2004 Fredrik Tolf <fredrik@dolda2000.com>
4  *  
5  *  This program is free software; you can redistribute it and/or modify
6  *  it under the terms of the GNU General Public License as published by
7  *  the Free Software Foundation; either version 2 of the License, or
8  *  (at your option) any later version.
9  *  
10  *  This program is distributed in the hope that it will be useful,
11  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  *  GNU General Public License for more details.
14  *  
15  *  You should have received a copy of the GNU General Public License
16  *  along with this program; if not, write to the Free Software
17  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18 */
19 #include <string.h>
20 #include <wchar.h>
21 #include <sys/socket.h>
22 #include <errno.h>
23
24 #ifdef HAVE_CONFIG_H
25 #include <config.h>
26 #endif
27 #include "filenet.h"
28 #include "search.h"
29 #include "module.h"
30 #include "utils.h"
31 #include "net.h"
32
33 static void freepeer(struct fnetpeer *peer);
34
35 static struct fnet *networks = NULL;
36 struct fnetnode *fnetnodes = NULL;
37 int numfnetnodes = 0;
38 GCBCHAIN(newfncb, struct fnetnode *);
39
40 static struct fnetnode *newfn(struct fnet *fnet)
41 {
42     static int curid = 0;
43     struct fnetnode *new;
44     
45     new = smalloc(sizeof(*new));
46     memset(new, 0, sizeof(*new));
47     new->fnet = fnet;
48     new->refcount = 1;
49     new->id = curid++;
50     new->mynick = swcsdup(confgetstr("cli", "defnick"));
51     new->srchwait = confgetint("fnet", "srchwait");
52     new->state = FNN_SYN;
53     CBCHAININIT(new, fnetnode_ac);
54     CBCHAININIT(new, fnetnode_chat);
55     CBCHAININIT(new, fnetnode_unlink);
56     CBCHAININIT(new, fnetnode_destroy);
57     CBCHAININIT(new, fnetpeer_new);
58     CBCHAININIT(new, fnetpeer_del);
59     CBCHAININIT(new, fnetpeer_chdi);
60     new->next = NULL;
61     new->prev = NULL;
62     numfnetnodes++;
63     return(new);
64 }
65
66 void killfnetnode(struct fnetnode *fn)
67 {
68     fnetsetstate(fn, FNN_DEAD);
69     if(fn->connected)
70         fn->fnet->kill(fn);
71 }
72
73 void getfnetnode(struct fnetnode *fn)
74 {
75     fn->refcount++;
76 #ifdef DEBUG
77     fprintf(stderr, "getfnetnode on id %i at %p, refcount=%i\n", fn->id, fn, fn->refcount);
78 #endif
79 }
80
81 static void freepeers(struct btree *n)
82 {
83     if(n == NULL)
84         return;
85     freepeers(n->l);
86     freepeers(n->r);
87     freepeer(n->d);
88     free(n);
89 }
90
91 void putfnetnode(struct fnetnode *fn)
92 {
93     struct fnetnode *cur;
94     
95 #ifdef DEBUG
96     fprintf(stderr, "putfnetnode on id %i at %p, refcount=%i\n", fn->id, fn, fn->refcount - 1);
97 #endif
98     if(--fn->refcount)
99         return;
100     for(cur = fnetnodes; cur != NULL; cur = cur->next)
101     {
102         if(cur == fn)
103             flog(LOG_CRIT, "BUG: fnetnode reached refcount 0 while still in list - id %i", fn->id);
104     }
105     CBCHAINDOCB(fn, fnetnode_destroy, fn);
106     CBCHAINFREE(fn, fnetnode_ac);
107     CBCHAINFREE(fn, fnetnode_chat);
108     CBCHAINFREE(fn, fnetnode_unlink);
109     CBCHAINFREE(fn, fnetnode_destroy);
110     CBCHAINFREE(fn, fnetpeer_new);
111     CBCHAINFREE(fn, fnetpeer_del);
112     CBCHAINFREE(fn, fnetpeer_chdi);
113     if((fn->fnet->destroy != NULL) && fn->connected)
114         fn->fnet->destroy(fn);
115     while(fn->args != NULL)
116         freewcspair(fn->args, &fn->args);
117     freepeers(fn->peers);
118     if(fn->mynick != NULL)
119         free(fn->mynick);
120     if(fn->pubid != NULL)
121         free(fn->pubid);
122     if(fn->name != NULL)
123         free(fn->name);
124     if(fn->owner != NULL)
125         free(fn->owner);
126     free(fn);
127     numfnetnodes--;
128 }
129
130 struct fnetnode *findfnetnode(int id)
131 {
132     struct fnetnode *fn;
133     
134     for(fn = fnetnodes; (fn != NULL) && (fn->id != id); fn = fn->next);
135     return(fn);
136 }
137
138 void linkfnetnode(struct fnetnode *fn)
139 {
140     if(fn->linked)
141         return;
142     getfnetnode(fn);
143     fn->next = fnetnodes;
144     if(fnetnodes != NULL)
145         fnetnodes->prev = fn;
146     fnetnodes = fn;
147     fn->linked = 1;
148     GCBCHAINDOCB(newfncb, fn);
149 }
150
151 void unlinkfnetnode(struct fnetnode *fn)
152 {
153     if(!fn->linked)
154         return;
155     if(fnetnodes == fn)
156         fnetnodes = fn->next;
157     if(fn->next != NULL)
158         fn->next->prev = fn->prev;
159     if(fn->prev != NULL)
160         fn->prev->next = fn->next;
161     fn->linked = 0;
162     CBCHAINDOCB(fn, fnetnode_unlink, fn);
163     putfnetnode(fn);
164 }
165
166 static void conncb(struct socket *sk, int err, struct fnetnode *data)
167 {
168     if(err != 0)
169     {
170         killfnetnode(data);
171         putfnetnode(data);
172         return;
173     }
174     fnetsetstate(data, FNN_HS);
175     socksettos(sk, confgetint("fnet", "fntos"));
176     data->fnet->connect(data, sk);
177     data->connected = 1;
178     putfnetnode(data);
179 }
180
181 static void resolvecb(struct sockaddr *addr, int addrlen, struct fnetnode *data)
182 {
183     if(addr == NULL)
184     {
185         killfnetnode(data);
186         putfnetnode(data);
187     } else {
188         netcsconn(addr, addrlen, (void (*)(struct socket *, int, void *))conncb, data);
189     }
190 }
191
192 static struct fnetpeerdatum *finddatum(struct fnetnode *fn, wchar_t *id)
193 {
194     struct fnetpeerdatum *datum;
195     
196     for(datum = fn->peerdata; datum != NULL; datum = datum->next)
197     {
198         if(!wcscmp(datum->id, id))
199             break;
200     }
201     return(datum);
202 }
203
204 static struct fnetpeerdatum *adddatum(struct fnetnode *fn, wchar_t *id, int datatype)
205 {
206     struct fnetpeerdatum *new;
207     
208     new = smalloc(sizeof(*new));
209     new->refcount = 0;
210     new->id = swcsdup(id);
211     new->datatype = datatype;
212     new->prev = NULL;
213     new->next = fn->peerdata;
214     if(fn->peerdata != NULL)
215         fn->peerdata->prev = new;
216     fn->peerdata = new;
217     return(new);
218 }
219
220 static struct fnetpeerdi *difindoradd(struct fnetpeer *peer, struct fnetpeerdatum *datum, int *isnew)
221 {
222     int i;
223     
224     for(i = 0; i < peer->dinum; i++)
225     {
226         if(peer->peerdi[i].datum == datum)
227             break;
228     }
229     if(i >= peer->dinum)
230     {
231         peer->peerdi = srealloc(peer->peerdi, sizeof(struct fnetpeerdi) * (peer->dinum + 1));
232         memset(&peer->peerdi[peer->dinum], 0, sizeof(struct fnetpeerdi));
233         peer->peerdi[peer->dinum].datum = datum;
234         datum->refcount++;
235         if(isnew != NULL)
236             *isnew = 1;
237         return(&peer->peerdi[peer->dinum++]);
238     } else {
239         if(isnew != NULL)
240             *isnew = 0;
241         return(&peer->peerdi[i]);
242     }
243 }
244
245 void fnetpeersetstr(struct fnetpeer *peer, wchar_t *id, wchar_t *value)
246 {
247     struct fnetpeerdatum *datum;
248     struct fnetpeerdi *di;
249     int changed;
250     
251     if((datum = finddatum(peer->fn, id)) == NULL)
252         datum = adddatum(peer->fn, id, FNPD_STR);
253     di = difindoradd(peer, datum, &changed);
254     if(di->data.str != NULL) {
255         changed = (changed || wcscmp(value, di->data.str));
256         free(di->data.str);
257     } else {
258         changed = 1;
259     }
260     di->data.str = swcsdup(value);
261     if(changed)
262         CBCHAINDOCB(peer->fn, fnetpeer_chdi, peer->fn, peer, di);
263 }
264
265 void fnetpeersetnum(struct fnetpeer *peer, wchar_t *id, int value)
266 {
267     struct fnetpeerdatum *datum;
268     struct fnetpeerdi *di;
269     int changed;
270     
271     if((datum = finddatum(peer->fn, id)) == NULL)
272         datum = adddatum(peer->fn, id, FNPD_INT);
273     di = difindoradd(peer, datum, &changed);
274     changed = (changed || (di->data.num != value));
275     di->data.num = value;
276     if(changed)
277         CBCHAINDOCB(peer->fn, fnetpeer_chdi, peer->fn, peer, di);
278 }
279
280 void fnetpeersetlnum(struct fnetpeer *peer, wchar_t *id, long long value)
281 {
282     struct fnetpeerdatum *datum;
283     struct fnetpeerdi *di;
284     int changed;
285     
286     if((datum = finddatum(peer->fn, id)) == NULL)
287         datum = adddatum(peer->fn, id, FNPD_LL);
288     di = difindoradd(peer, datum, &changed);
289     changed = (changed || (di->data.lnum != value));
290     di->data.lnum = value;
291     if(changed)
292         CBCHAINDOCB(peer->fn, fnetpeer_chdi, peer->fn, peer, di);
293 }
294
295 static void putdatum(struct fnetpeer *peer, struct fnetpeerdatum *datum)
296 {
297     if(--datum->refcount > 0)
298         return;
299     if(datum->next != NULL)
300         datum->next->prev = datum->prev;
301     if(datum->prev != NULL)
302         datum->prev->next = datum->next;
303     if(datum == peer->fn->peerdata)
304         peer->fn->peerdata = datum->next;
305     free(datum->id);
306     free(datum);
307 }
308
309 void fnetpeerunset(struct fnetpeer *peer, wchar_t *id)
310 {
311     int i;
312     struct fnetpeerdatum *datum;
313     
314     if((datum = finddatum(peer->fn, id)) == NULL)
315         return;
316     for(i = 0; i < peer->dinum; i++)
317     {
318         if(peer->peerdi[i].datum == datum)
319             break;
320     }
321     if(i >= peer->dinum)
322         return;
323     if((datum->datatype == FNPD_STR) && (peer->peerdi[i].data.str != NULL))
324         free(peer->peerdi[i].data.str);
325     peer->dinum--;
326     memmove(&peer->peerdi[i], &peer->peerdi[i + 1], sizeof(struct fnetpeerdi) * (peer->dinum - i));
327     putdatum(peer, datum);
328 }
329
330 static int peercmpid(void *a, void *b)
331 {
332     return(wcscmp(((struct fnetpeer *)a)->id, ((struct fnetpeer *)b)->id));
333 }
334
335 struct fnetpeer *fnetaddpeer(struct fnetnode *fn, wchar_t *id, wchar_t *nick)
336 {
337     struct fnetpeer *new;
338     
339     new = smalloc(sizeof(*new));
340     new->fn = fn;
341     new->id = swcsdup(id);
342     new->nick = swcsdup(nick);
343     new->flags.w = 0;
344     new->dinum = 0;
345     new->peerdi = NULL;
346     bbtreeput(&fn->peers, new, peercmpid);
347     fn->numpeers++;
348     CBCHAINDOCB(fn, fnetnode_ac, fn, L"numpeers");
349     CBCHAINDOCB(fn, fnetpeer_new, fn, new);
350     return(new);
351 }
352
353 static void freepeer(struct fnetpeer *peer)
354 {
355     int i;
356     
357     peer->fn->numpeers--;
358     free(peer->id);
359     free(peer->nick);
360     for(i = 0; i < peer->dinum; i++)
361     {
362         if((peer->peerdi[i].datum->datatype == FNPD_STR) && (peer->peerdi[i].data.str != NULL))
363             free(peer->peerdi[i].data.str);
364         putdatum(peer, peer->peerdi[i].datum);
365     }
366     if(peer->peerdi != NULL)
367         free(peer->peerdi);
368     free(peer);
369 }
370
371 void fnetdelpeer(struct fnetpeer *peer)
372 {
373     bbtreedel(&peer->fn->peers, peer, peercmpid);
374     CBCHAINDOCB(peer->fn, fnetnode_ac, peer->fn, L"numpeers");
375     CBCHAINDOCB(peer->fn, fnetpeer_del, peer->fn, peer);
376     freepeer(peer);
377 }
378
379 void fnetpeerdm(struct fnetnode *fn)
380 {
381     struct btree *new;
382     struct fnetpeer *peer;
383     int intact;
384     
385     new = NULL;
386     intact = 1;
387     for(peer = btreeiter(fn->peers); peer != NULL; peer = btreeiter(NULL)) {
388         if(!peer->flags.b.delete) {
389             bbtreeput(&new, peer, peercmpid);
390         } else {
391             intact = 0;
392             CBCHAINDOCB(peer->fn, fnetpeer_del, peer->fn, peer);
393             freepeer(peer);
394         }
395     }
396     btreefree(fn->peers);
397     fn->peers = new;
398     if(!intact)
399         CBCHAINDOCB(peer->fn, fnetnode_ac, peer->fn, L"numpeers");
400 }
401
402 struct fnetpeer *fnetfindpeer(struct fnetnode *fn, wchar_t *id)
403 {
404     struct fnetpeer key;
405     
406     key.id = id;
407     return(btreeget(fn->peers, &key, peercmpid));
408 }
409
410 int fnetsetnick(struct fnetnode *fn, wchar_t *newnick)
411 {
412     int ret;
413     
414     if((fn->fnet->setnick != NULL) && fn->connected)
415         ret = fn->fnet->setnick(fn, newnick);
416     else
417         ret = 0;
418     if(!ret)
419     {
420         if(fn->mynick != NULL)
421             free(fn->mynick);
422         fn->mynick = swcsdup(newnick);
423     }
424     return(ret);
425 }
426
427 int fnetsendchat(struct fnetnode *fn, int public, wchar_t *to, wchar_t *string)
428 {
429     if((fn->fnet->sendchat == NULL) || !fn->connected)
430     {
431         errno = ENOTSUP;
432         return(-1);
433     }
434     return(fn->fnet->sendchat(fn, public, to, string));
435 }
436
437 int fnetsearch(struct fnetnode *fn, struct search *srch, struct srchfnnlist *ln)
438 {
439     if(fn->fnet->search == NULL)
440     {
441         errno = ENOTSUP;
442         return(-1);
443     }
444     return(fn->fnet->search(fn, srch, ln));
445 }
446
447 void fnetsetname(struct fnetnode *fn, wchar_t *newname)
448 {
449     if(fn->name != NULL)
450         free(fn->name);
451     fn->name = swcsdup(newname);
452     CBCHAINDOCB(fn, fnetnode_ac, fn, L"name");
453 }
454
455 void fnetsetstate(struct fnetnode *fn, int newstate)
456 {
457     fn->state = newstate;
458     CBCHAINDOCB(fn, fnetnode_ac, fn, L"state");
459 }
460
461 wchar_t *fnfilebasename(wchar_t *path)
462 {
463     wchar_t *p;
464     
465     if((p = wcsrchr(path, L'/')) != NULL)
466         return(p + 1);
467     return(path);
468 }
469
470 struct fnet *findfnet(wchar_t *name)
471 {
472     struct fnet *fnet;
473     
474     for(fnet = networks; fnet != NULL; fnet = fnet->next)
475     {
476         if(!wcscmp(name, fnet->name))
477             break;
478     }
479     return(fnet);
480 }
481
482 struct fnetnode *fnetinitconnect(wchar_t *name, wchar_t *owner, char *addr, struct wcspair *args)
483 {
484     struct fnet *fnet;
485     struct fnetnode *fn;
486     struct wcspair *arg;
487     
488     if((fnet = findfnet(name)) == NULL)
489     {
490         errno = EPROTONOSUPPORT;
491         return(NULL);
492     }
493     fn = newfn(fnet);
494     fn->owner = swcsdup(owner);
495     fn->pubid = icmbstowcs(addr, NULL);
496     if(fn->pubid == NULL)
497         fn->pubid = swcsdup(L"");
498     fn->args = args;
499     for(arg = fn->args; arg != NULL; arg = arg->next)
500     {
501         if(!wcscmp(arg->key, L"nick"))
502             fnetsetnick(fn, arg->val);
503     }
504     getfnetnode(fn);
505     if(netresolve(addr, (void (*)(struct sockaddr *, int, void *))resolvecb, fn) < 0)
506         return(NULL);
507     return(fn);
508 }
509
510 void regfnet(struct fnet *fnet)
511 {
512     fnet->next = networks;
513     networks = fnet;
514 }
515
516 /*
517  * Note on the chat string: Must be in UNIX text file format - that
518  * is, LF line endings. The filenet-specific code must see to it that
519  * any other kind of format is converted into that. In the future,
520  * certain control characters and escape sequences will be parsed by
521  * the client. Make sure that any filenet-specific code strips any
522  * such that aren't supposed to be in the protocol.
523  *
524  * Note on "name": This is supposed to be an identifier for the
525  * source. If the chat is a public message, set "public" to non-zero
526  * and "name" to whatever "chat room" name is appropriate for the
527  * fnetnode, but not NULL. If there is a "default" channel in this
528  * filenet, set "name" to the empty string. If the chat is a private
529  * message, name is ignored.
530  */
531 void fnethandlechat(struct fnetnode *fn, int public, wchar_t *name, wchar_t *peer, wchar_t *chat)
532 {
533     CBCHAINDOCB(fn, fnetnode_chat, fn, public, name, peer, chat);
534 }
535
536 static struct configvar myvars[] =
537 {
538     /** The number of seconds to wait between searches. Most hubs
539      * require at least ten seconds, and quite often network lag will
540      * often make searches arrive to the hub more often than sent. It
541      * may be semi-dangerous to specify too low a value, since hubs
542      * will often kick users that search too often (even when the
543      * reason is network lag -- there is no way for the hub to know
544      * this), but it is quite annoying to set too high a value. 15 to
545      * 40 seconds are the recommended range (although the default is
546      * 15 seconds, it is recommended to set to 30 seconds). */
547     {CONF_VAR_INT, "srchwait", {.num = 15}},
548     /** The TOS value to use for hub connections (see the TOS VALUES
549      * section). */
550     {CONF_VAR_INT, "fntos", {.num = 0}},
551     /** The TOS value to use for peer connections (see the TOS VALUES
552      * section). */
553     {CONF_VAR_INT, "fnptos", {.num = 0}},
554     /** Specifies a maximum number of simultaneously connected
555      * hubs. Attempts to connect to new hubs beyond this limit will
556      * return an error. Set to zero to remove the limit. */
557     {CONF_VAR_INT, "maxnodes", {.num = 0}},
558     {CONF_VAR_END}
559 };
560
561 static struct module me =
562 {
563     .conf =
564     {
565         .vars = myvars
566     },
567     .name = "fnet"
568 };
569
570 MODULE(me)