Add small sanity assignment.
[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         if(fn->sk->data == fn)
71         {
72             fn->sk->data = NULL;
73             putfnetnode(fn);
74         }
75         putsock(fn->sk);
76         fn->sk = NULL;
77     }
78 }
79
80 void getfnetnode(struct fnetnode *fn)
81 {
82     fn->refcount++;
83 #ifdef DEBUG
84     fprintf(stderr, "getfnetnode on id %i at %p, refcount=%i\n", fn->id, fn, fn->refcount);
85 #endif
86 }
87
88 void putfnetnode(struct fnetnode *fn)
89 {
90     struct fnetnode *cur;
91     
92 #ifdef DEBUG
93     fprintf(stderr, "putfnetnode on id %i at %p, refcount=%i\n", fn->id, fn, fn->refcount - 1);
94 #endif
95     if(--fn->refcount)
96         return;
97     for(cur = fnetnodes; cur != NULL; cur = cur->next)
98     {
99         if(cur == fn)
100             flog(LOG_CRIT, "BUG: fnetnode reached refcount 0 while still in list - id %i", fn->id);
101     }
102     CBCHAINDOCB(fn, fnetnode_destroy, fn);
103     CBCHAINFREE(fn, fnetnode_ac);
104     CBCHAINFREE(fn, fnetnode_chat);
105     CBCHAINFREE(fn, fnetnode_unlink);
106     CBCHAINFREE(fn, fnetnode_destroy);
107     CBCHAINFREE(fn, fnetpeer_new);
108     CBCHAINFREE(fn, fnetpeer_del);
109     CBCHAINFREE(fn, fnetpeer_chdi);
110     if(fn->fnet->destroy != NULL)
111         fn->fnet->destroy(fn);
112     while(fn->args != NULL)
113         freewcspair(fn->args, &fn->args);
114     while(fn->peers != NULL)
115         fnetdelpeer(fn->peers);
116     if(fn->mynick != NULL)
117         free(fn->mynick);
118     if(fn->pubid != NULL)
119         free(fn->pubid);
120     if(fn->name != NULL)
121         free(fn->name);
122     if(fn->sk != NULL)
123         putsock(fn->sk);
124     free(fn);
125     numfnetnodes--;
126 }
127
128 struct fnetnode *findfnetnode(int id)
129 {
130     struct fnetnode *fn;
131     
132     for(fn = fnetnodes; (fn != NULL) && (fn->id != id); fn = fn->next);
133     return(fn);
134 }
135
136 void linkfnetnode(struct fnetnode *fn)
137 {
138     if(fn->linked)
139         return;
140     getfnetnode(fn);
141     fn->next = fnetnodes;
142     if(fnetnodes != NULL)
143         fnetnodes->prev = fn;
144     fnetnodes = fn;
145     fn->linked = 1;
146     GCBCHAINDOCB(newfncb, fn);
147 }
148
149 void unlinkfnetnode(struct fnetnode *fn)
150 {
151     if(!fn->linked)
152         return;
153     if(fnetnodes == fn)
154         fnetnodes = fn->next;
155     if(fn->next != NULL)
156         fn->next->prev = fn->prev;
157     if(fn->prev != NULL)
158         fn->prev->next = fn->next;
159     fn->linked = 0;
160     CBCHAINDOCB(fn, fnetnode_unlink, fn);
161     putfnetnode(fn);
162 }
163
164 static void conncb(struct socket *sk, int err, struct fnetnode *data)
165 {
166     if(err != 0)
167     {
168         killfnetnode(data);
169         putfnetnode(data);
170         return;
171     }
172     data->sk = sk;
173     fnetsetstate(data, FNN_HS);
174     socksettos(sk, confgetint("fnet", "fntos"));
175     data->fnet->connect(data);
176     putfnetnode(data);
177 }
178
179 static void resolvecb(struct sockaddr *addr, int addrlen, struct fnetnode *data)
180 {
181     if(addr == NULL)
182     {
183         killfnetnode(data);
184         putfnetnode(data);
185     } else {
186         netcsconn(addr, addrlen, (void (*)(struct socket *, int, void *))conncb, data);
187     }
188 }
189
190 static struct fnetpeerdatum *finddatum(struct fnetnode *fn, wchar_t *id)
191 {
192     struct fnetpeerdatum *datum;
193     
194     for(datum = fn->peerdata; datum != NULL; datum = datum->next)
195     {
196         if(!wcscmp(datum->id, id))
197             break;
198     }
199     return(datum);
200 }
201
202 static struct fnetpeerdatum *adddatum(struct fnetnode *fn, wchar_t *id, int datatype)
203 {
204     struct fnetpeerdatum *new;
205     
206     new = smalloc(sizeof(*new));
207     new->refcount = 0;
208     new->id = swcsdup(id);
209     new->datatype = datatype;
210     new->prev = NULL;
211     new->next = fn->peerdata;
212     if(fn->peerdata != NULL)
213         fn->peerdata->prev = new;
214     fn->peerdata = new;
215     return(new);
216 }
217
218 static struct fnetpeerdi *difindoradd(struct fnetpeer *peer, struct fnetpeerdatum *datum, int *isnew)
219 {
220     int i;
221     
222     for(i = 0; i < peer->dinum; i++)
223     {
224         if(peer->peerdi[i].datum == datum)
225             break;
226     }
227     if(i >= peer->dinum)
228     {
229         peer->peerdi = srealloc(peer->peerdi, sizeof(struct fnetpeerdi) * (peer->dinum + 1));
230         memset(&peer->peerdi[peer->dinum], 0, sizeof(struct fnetpeerdi));
231         peer->peerdi[peer->dinum].datum = datum;
232         datum->refcount++;
233         if(isnew != NULL)
234             *isnew = 1;
235         return(&peer->peerdi[peer->dinum++]);
236     } else {
237         if(isnew != NULL)
238             *isnew = 0;
239         return(&peer->peerdi[i]);
240     }
241 }
242
243 void fnetpeersetstr(struct fnetpeer *peer, wchar_t *id, wchar_t *value)
244 {
245     struct fnetpeerdatum *datum;
246     struct fnetpeerdi *di;
247     int changed;
248     
249     if((datum = finddatum(peer->fn, id)) == NULL)
250         datum = adddatum(peer->fn, id, FNPD_STR);
251     di = difindoradd(peer, datum, &changed);
252     if(di->data.str != NULL) {
253         changed = (changed || wcscmp(value, di->data.str));
254         free(di->data.str);
255     } else {
256         changed = 1;
257     }
258     di->data.str = swcsdup(value);
259     if(changed)
260         CBCHAINDOCB(peer->fn, fnetpeer_chdi, peer->fn, peer, di);
261 }
262
263 void fnetpeersetnum(struct fnetpeer *peer, wchar_t *id, int value)
264 {
265     struct fnetpeerdatum *datum;
266     struct fnetpeerdi *di;
267     int changed;
268     
269     if((datum = finddatum(peer->fn, id)) == NULL)
270         datum = adddatum(peer->fn, id, FNPD_INT);
271     di = difindoradd(peer, datum, &changed);
272     changed = (changed || (di->data.num != value));
273     di->data.num = value;
274     if(changed)
275         CBCHAINDOCB(peer->fn, fnetpeer_chdi, peer->fn, peer, di);
276 }
277
278 void fnetpeersetlnum(struct fnetpeer *peer, wchar_t *id, long long value)
279 {
280     struct fnetpeerdatum *datum;
281     struct fnetpeerdi *di;
282     int changed;
283     
284     if((datum = finddatum(peer->fn, id)) == NULL)
285         datum = adddatum(peer->fn, id, FNPD_LL);
286     di = difindoradd(peer, datum, &changed);
287     changed = (changed || (di->data.lnum != value));
288     di->data.lnum = value;
289     if(changed)
290         CBCHAINDOCB(peer->fn, fnetpeer_chdi, peer->fn, peer, di);
291 }
292
293 static void putdatum(struct fnetpeer *peer, struct fnetpeerdatum *datum)
294 {
295     if(--datum->refcount > 0)
296         return;
297     if(datum->next != NULL)
298         datum->next->prev = datum->prev;
299     if(datum->prev != NULL)
300         datum->prev->next = datum->next;
301     if(datum == peer->fn->peerdata)
302         peer->fn->peerdata = datum->next;
303     free(datum->id);
304     free(datum);
305 }
306
307 void fnetpeerunset(struct fnetpeer *peer, wchar_t *id)
308 {
309     int i;
310     struct fnetpeerdatum *datum;
311     
312     if((datum = finddatum(peer->fn, id)) == NULL)
313         return;
314     for(i = 0; i < peer->dinum; i++)
315     {
316         if(peer->peerdi[i].datum == datum)
317             break;
318     }
319     if(i >= peer->dinum)
320         return;
321     if((datum->datatype == FNPD_STR) && (peer->peerdi[i].data.str != NULL))
322         free(peer->peerdi[i].data.str);
323     peer->dinum--;
324     memmove(&peer->peerdi[i], &peer->peerdi[i + 1], sizeof(struct fnetpeerdi) * (peer->dinum - i));
325     putdatum(peer, datum);
326 }
327
328 struct fnetpeer *fnetaddpeer(struct fnetnode *fn, wchar_t *id, wchar_t *nick)
329 {
330     struct fnetpeer *new;
331     
332     new = smalloc(sizeof(*new));
333     new->fn = fn;
334     new->id = swcsdup(id);
335     new->nick = swcsdup(nick);
336     new->flags.w = 0;
337     new->dinum = 0;
338     new->peerdi = NULL;
339     new->next = fn->peers;
340     new->prev = NULL;
341     if(fn->peers != NULL)
342         fn->peers->prev = new;
343     fn->peers = new;
344     fn->numpeers++;
345     CBCHAINDOCB(fn, fnetnode_ac, fn, L"numpeers");
346     CBCHAINDOCB(fn, fnetpeer_new, fn, new);
347     return(new);
348 }
349
350 void fnetdelpeer(struct fnetpeer *peer)
351 {
352     int i;
353     
354     if(peer->next != NULL)
355         peer->next->prev = peer->prev;
356     if(peer->prev != NULL)
357         peer->prev->next = peer->next;
358     if(peer->fn->peers == peer)
359         peer->fn->peers = peer->next;
360     peer->fn->numpeers--;
361     CBCHAINDOCB(peer->fn, fnetnode_ac, peer->fn, L"numpeers");
362     CBCHAINDOCB(peer->fn, fnetpeer_del, peer->fn, peer);
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 struct fnetpeer *fnetfindpeer(struct fnetnode *fn, wchar_t *id)
377 {
378     struct fnetpeer *cur;
379     
380     for(cur = fn->peers; (cur != NULL) && wcscmp(cur->id, id); cur = cur->next);
381     return(cur);
382 }
383
384 int fnetsetnick(struct fnetnode *fn, wchar_t *newnick)
385 {
386     int ret;
387     
388     if(fn->fnet->setnick != NULL)
389         ret = fn->fnet->setnick(fn, newnick);
390     else
391         ret = 0;
392     if(!ret)
393     {
394         if(fn->mynick != NULL)
395             free(fn->mynick);
396         fn->mynick = swcsdup(newnick);
397     }
398     return(ret);
399 }
400
401 int fnetsendchat(struct fnetnode *fn, int public, wchar_t *to, wchar_t *string)
402 {
403     if(fn->fnet->sendchat == NULL)
404     {
405         errno = ENOTSUP;
406         return(-1);
407     }
408     return(fn->fnet->sendchat(fn, public, to, string));
409 }
410
411 int fnetsearch(struct fnetnode *fn, struct search *srch, struct srchfnnlist *ln)
412 {
413     if(fn->fnet->search == NULL)
414     {
415         errno = ENOTSUP;
416         return(-1);
417     }
418     return(fn->fnet->search(fn, srch, ln));
419 }
420
421 void fnetsetname(struct fnetnode *fn, wchar_t *newname)
422 {
423     if(fn->name != NULL)
424         free(fn->name);
425     fn->name = swcsdup(newname);
426     CBCHAINDOCB(fn, fnetnode_ac, fn, L"name");
427 }
428
429 void fnetsetstate(struct fnetnode *fn, int newstate)
430 {
431     fn->state = newstate;
432     CBCHAINDOCB(fn, fnetnode_ac, fn, L"state");
433 }
434
435 struct fnet *findfnet(wchar_t *name)
436 {
437     struct fnet *fnet;
438     
439     for(fnet = networks; fnet != NULL; fnet = fnet->next)
440     {
441         if(!wcscmp(name, fnet->name))
442             break;
443     }
444     return(fnet);
445 }
446
447 struct fnetnode *fnetinitconnect(wchar_t *name, char *addr, struct wcspair *args)
448 {
449     struct fnet *fnet;
450     struct fnetnode *fn;
451     struct wcspair *arg;
452     
453     if((fnet = findfnet(name)) == NULL)
454     {
455         errno = EPROTONOSUPPORT;
456         return(NULL);
457     }
458     fn = newfn(fnet);
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_END}
506 };
507
508 static struct module me =
509 {
510     .conf =
511     {
512         .vars = myvars
513     },
514     .name = "fnet"
515 };
516
517 MODULE(me)