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