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