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