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