da83a0914afe06c9bdd91c60a7b1e5762d446a72
[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     putsock(sk);
180 }
181
182 static void resolvecb(struct sockaddr *addr, int addrlen, struct fnetnode *data)
183 {
184     if(addr == NULL)
185     {
186         killfnetnode(data);
187         putfnetnode(data);
188     } else {
189         netcsconn(addr, addrlen, (void (*)(struct socket *, int, void *))conncb, data);
190     }
191 }
192
193 static struct fnetpeerdatum *finddatum(struct fnetnode *fn, wchar_t *id)
194 {
195     struct fnetpeerdatum *datum;
196     
197     for(datum = fn->peerdata; datum != NULL; datum = datum->next)
198     {
199         if(!wcscmp(datum->id, id))
200             break;
201     }
202     return(datum);
203 }
204
205 static struct fnetpeerdatum *adddatum(struct fnetnode *fn, wchar_t *id, int datatype)
206 {
207     struct fnetpeerdatum *new;
208     
209     new = smalloc(sizeof(*new));
210     new->refcount = 0;
211     new->id = swcsdup(id);
212     new->datatype = datatype;
213     new->prev = NULL;
214     new->next = fn->peerdata;
215     if(fn->peerdata != NULL)
216         fn->peerdata->prev = new;
217     fn->peerdata = new;
218     return(new);
219 }
220
221 static struct fnetpeerdi *difindoradd(struct fnetpeer *peer, struct fnetpeerdatum *datum, int *isnew)
222 {
223     int i;
224     
225     for(i = 0; i < peer->dinum; i++)
226     {
227         if(peer->peerdi[i].datum == datum)
228             break;
229     }
230     if(i >= peer->dinum)
231     {
232         peer->peerdi = srealloc(peer->peerdi, sizeof(struct fnetpeerdi) * (peer->dinum + 1));
233         memset(&peer->peerdi[peer->dinum], 0, sizeof(struct fnetpeerdi));
234         peer->peerdi[peer->dinum].datum = datum;
235         datum->refcount++;
236         if(isnew != NULL)
237             *isnew = 1;
238         return(&peer->peerdi[peer->dinum++]);
239     } else {
240         if(isnew != NULL)
241             *isnew = 0;
242         return(&peer->peerdi[i]);
243     }
244 }
245
246 void fnetpeersetstr(struct fnetpeer *peer, wchar_t *id, wchar_t *value)
247 {
248     struct fnetpeerdatum *datum;
249     struct fnetpeerdi *di;
250     int changed;
251     
252     if((datum = finddatum(peer->fn, id)) == NULL)
253         datum = adddatum(peer->fn, id, FNPD_STR);
254     di = difindoradd(peer, datum, &changed);
255     if(di->data.str != NULL) {
256         changed = (changed || wcscmp(value, di->data.str));
257         free(di->data.str);
258     } else {
259         changed = 1;
260     }
261     di->data.str = swcsdup(value);
262     if(changed)
263         CBCHAINDOCB(peer->fn, fnetpeer_chdi, peer->fn, peer, di);
264 }
265
266 void fnetpeersetnum(struct fnetpeer *peer, wchar_t *id, int value)
267 {
268     struct fnetpeerdatum *datum;
269     struct fnetpeerdi *di;
270     int changed;
271     
272     if((datum = finddatum(peer->fn, id)) == NULL)
273         datum = adddatum(peer->fn, id, FNPD_INT);
274     di = difindoradd(peer, datum, &changed);
275     changed = (changed || (di->data.num != value));
276     di->data.num = value;
277     if(changed)
278         CBCHAINDOCB(peer->fn, fnetpeer_chdi, peer->fn, peer, di);
279 }
280
281 void fnetpeersetlnum(struct fnetpeer *peer, wchar_t *id, long long value)
282 {
283     struct fnetpeerdatum *datum;
284     struct fnetpeerdi *di;
285     int changed;
286     
287     if((datum = finddatum(peer->fn, id)) == NULL)
288         datum = adddatum(peer->fn, id, FNPD_LL);
289     di = difindoradd(peer, datum, &changed);
290     changed = (changed || (di->data.lnum != value));
291     di->data.lnum = value;
292     if(changed)
293         CBCHAINDOCB(peer->fn, fnetpeer_chdi, peer->fn, peer, di);
294 }
295
296 static void putdatum(struct fnetpeer *peer, struct fnetpeerdatum *datum)
297 {
298     if(--datum->refcount > 0)
299         return;
300     if(datum->next != NULL)
301         datum->next->prev = datum->prev;
302     if(datum->prev != NULL)
303         datum->prev->next = datum->next;
304     if(datum == peer->fn->peerdata)
305         peer->fn->peerdata = datum->next;
306     free(datum->id);
307     free(datum);
308 }
309
310 void fnetpeerunset(struct fnetpeer *peer, wchar_t *id)
311 {
312     int i;
313     struct fnetpeerdatum *datum;
314     
315     if((datum = finddatum(peer->fn, id)) == NULL)
316         return;
317     for(i = 0; i < peer->dinum; i++)
318     {
319         if(peer->peerdi[i].datum == datum)
320             break;
321     }
322     if(i >= peer->dinum)
323         return;
324     if((datum->datatype == FNPD_STR) && (peer->peerdi[i].data.str != NULL))
325         free(peer->peerdi[i].data.str);
326     peer->dinum--;
327     memmove(&peer->peerdi[i], &peer->peerdi[i + 1], sizeof(struct fnetpeerdi) * (peer->dinum - i));
328     putdatum(peer, datum);
329 }
330
331 static int peercmpid(void *a, void *b)
332 {
333     return(wcscmp(((struct fnetpeer *)a)->id, ((struct fnetpeer *)b)->id));
334 }
335
336 struct fnetpeer *fnetaddpeer(struct fnetnode *fn, wchar_t *id, wchar_t *nick)
337 {
338     struct fnetpeer *new;
339     
340     new = smalloc(sizeof(*new));
341     new->fn = fn;
342     new->id = swcsdup(id);
343     new->nick = swcsdup(nick);
344     new->flags.w = 0;
345     new->dinum = 0;
346     new->peerdi = NULL;
347     bbtreeput(&fn->peers, new, peercmpid);
348     fn->numpeers++;
349     CBCHAINDOCB(fn, fnetnode_ac, fn, L"numpeers");
350     CBCHAINDOCB(fn, fnetpeer_new, fn, new);
351     return(new);
352 }
353
354 static void freepeer(struct fnetpeer *peer)
355 {
356     int i;
357     
358     peer->fn->numpeers--;
359     free(peer->id);
360     free(peer->nick);
361     for(i = 0; i < peer->dinum; i++)
362     {
363         if((peer->peerdi[i].datum->datatype == FNPD_STR) && (peer->peerdi[i].data.str != NULL))
364             free(peer->peerdi[i].data.str);
365         putdatum(peer, peer->peerdi[i].datum);
366     }
367     if(peer->peerdi != NULL)
368         free(peer->peerdi);
369     free(peer);
370 }
371
372 void fnetdelpeer(struct fnetpeer *peer)
373 {
374     bbtreedel(&peer->fn->peers, peer, peercmpid);
375     CBCHAINDOCB(peer->fn, fnetnode_ac, peer->fn, L"numpeers");
376     CBCHAINDOCB(peer->fn, fnetpeer_del, peer->fn, peer);
377     freepeer(peer);
378 }
379
380 void fnetpeerdm(struct fnetnode *fn)
381 {
382     struct btree *new;
383     struct fnetpeer *peer;
384     int intact;
385     
386     new = NULL;
387     intact = 1;
388     for(peer = btreeiter(fn->peers); peer != NULL; peer = btreeiter(NULL)) {
389         if(!peer->flags.b.delete) {
390             bbtreeput(&new, peer, peercmpid);
391         } else {
392             intact = 0;
393             CBCHAINDOCB(peer->fn, fnetpeer_del, peer->fn, peer);
394             freepeer(peer);
395         }
396     }
397     btreefree(fn->peers);
398     fn->peers = new;
399     if(!intact)
400         CBCHAINDOCB(peer->fn, fnetnode_ac, peer->fn, L"numpeers");
401 }
402
403 struct fnetpeer *fnetfindpeer(struct fnetnode *fn, wchar_t *id)
404 {
405     struct fnetpeer key;
406     
407     key.id = id;
408     return(btreeget(fn->peers, &key, peercmpid));
409 }
410
411 int fnetsetnick(struct fnetnode *fn, wchar_t *newnick)
412 {
413     int ret;
414     
415     if((fn->fnet->setnick != NULL) && fn->connected)
416         ret = fn->fnet->setnick(fn, newnick);
417     else
418         ret = 0;
419     if(!ret)
420     {
421         if(fn->mynick != NULL)
422             free(fn->mynick);
423         fn->mynick = swcsdup(newnick);
424     }
425     return(ret);
426 }
427
428 int fnetsendchat(struct fnetnode *fn, int public, wchar_t *to, wchar_t *string)
429 {
430     if((fn->fnet->sendchat == NULL) || !fn->connected)
431     {
432         errno = ENOTSUP;
433         return(-1);
434     }
435     return(fn->fnet->sendchat(fn, public, to, string));
436 }
437
438 int fnetsearch(struct fnetnode *fn, struct search *srch, struct srchfnnlist *ln)
439 {
440     if(fn->fnet->search == NULL)
441     {
442         errno = ENOTSUP;
443         return(-1);
444     }
445     return(fn->fnet->search(fn, srch, ln));
446 }
447
448 void fnetsetname(struct fnetnode *fn, wchar_t *newname)
449 {
450     if(fn->name != NULL)
451         free(fn->name);
452     fn->name = swcsdup(newname);
453     CBCHAINDOCB(fn, fnetnode_ac, fn, L"name");
454 }
455
456 void fnetsetstate(struct fnetnode *fn, int newstate)
457 {
458     fn->state = newstate;
459     CBCHAINDOCB(fn, fnetnode_ac, fn, L"state");
460 }
461
462 wchar_t *fnfilebasename(wchar_t *path)
463 {
464     wchar_t *p;
465     
466     if((p = wcsrchr(path, L'/')) != NULL)
467         return(p + 1);
468     return(path);
469 }
470
471 struct fnet *findfnet(wchar_t *name)
472 {
473     struct fnet *fnet;
474     
475     for(fnet = networks; fnet != NULL; fnet = fnet->next)
476     {
477         if(!wcscmp(name, fnet->name))
478             break;
479     }
480     return(fnet);
481 }
482
483 struct fnetnode *fnetinitconnect(wchar_t *name, wchar_t *owner, char *addr, struct wcspair *args)
484 {
485     struct fnet *fnet;
486     struct fnetnode *fn;
487     struct wcspair *arg;
488     
489     if((fnet = findfnet(name)) == NULL)
490     {
491         errno = EPROTONOSUPPORT;
492         return(NULL);
493     }
494     fn = newfn(fnet);
495     fn->owner = swcsdup(owner);
496     fn->pubid = icmbstowcs(addr, NULL);
497     if(fn->pubid == NULL)
498         fn->pubid = swcsdup(L"");
499     fn->args = args;
500     for(arg = fn->args; arg != NULL; arg = arg->next)
501     {
502         if(!wcscmp(arg->key, L"nick"))
503             fnetsetnick(fn, arg->val);
504     }
505     getfnetnode(fn);
506     if(netresolve(addr, (void (*)(struct sockaddr *, int, void *))resolvecb, fn) < 0)
507         return(NULL);
508     return(fn);
509 }
510
511 void regfnet(struct fnet *fnet)
512 {
513     fnet->next = networks;
514     networks = fnet;
515 }
516
517 /*
518  * Note on the chat string: Must be in UNIX text file format - that
519  * is, LF line endings. The filenet-specific code must see to it that
520  * any other kind of format is converted into that. In the future,
521  * certain control characters and escape sequences will be parsed by
522  * the client. Make sure that any filenet-specific code strips any
523  * such that aren't supposed to be in the protocol.
524  *
525  * Note on "name": This is supposed to be an identifier for the
526  * source. If the chat is a public message, set "public" to non-zero
527  * and "name" to whatever "chat room" name is appropriate for the
528  * fnetnode, but not NULL. If there is a "default" channel in this
529  * filenet, set "name" to the empty string. If the chat is a private
530  * message, name is ignored.
531  */
532 void fnethandlechat(struct fnetnode *fn, int public, wchar_t *name, wchar_t *peer, wchar_t *chat)
533 {
534     CBCHAINDOCB(fn, fnetnode_chat, fn, public, name, peer, chat);
535 }
536
537 static struct configvar myvars[] =
538 {
539     /** The number of seconds to wait between searches. Most hubs
540      * require at least ten seconds, and quite often network lag will
541      * often make searches arrive to the hub more often than sent. It
542      * may be semi-dangerous to specify too low a value, since hubs
543      * will often kick users that search too often (even when the
544      * reason is network lag -- there is no way for the hub to know
545      * this), but it is quite annoying to set too high a value. 15 to
546      * 40 seconds are the recommended range (although the default is
547      * 15 seconds, it is recommended to set to 30 seconds). */
548     {CONF_VAR_INT, "srchwait", {.num = 15}},
549     /** The TOS value to use for hub connections (see the TOS VALUES
550      * section). */
551     {CONF_VAR_INT, "fntos", {.num = 0}},
552     /** The TOS value to use for peer connections (see the TOS VALUES
553      * section). */
554     {CONF_VAR_INT, "fnptos", {.num = 0}},
555     /** Specifies a maximum number of simultaneously connected
556      * hubs. Attempts to connect to new hubs beyond this limit will
557      * return an error. Set to zero to remove the limit. */
558     {CONF_VAR_INT, "maxnodes", {.num = 0}},
559     {CONF_VAR_END}
560 };
561
562 static struct module me =
563 {
564     .conf =
565     {
566         .vars = myvars
567     },
568     .name = "fnet"
569 };
570
571 MODULE(me)