Fix some potential issues when freeing running cbchains.
[doldaconnect.git] / daemon / filenet.c
CommitLineData
d3372da9 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
33static struct fnet *networks = NULL;
34struct fnetnode *fnetnodes = NULL;
35int numfnetnodes = 0;
36GCBCHAIN(newfncb, struct fnetnode *);
37
38static 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);
d9c95ea0 55 CBCHAININIT(new, fnetpeer_new);
56 CBCHAININIT(new, fnetpeer_del);
57 CBCHAININIT(new, fnetpeer_chdi);
d3372da9 58 new->next = NULL;
59 new->prev = NULL;
60 numfnetnodes++;
61 return(new);
62}
63
64void killfnetnode(struct fnetnode *fn)
65{
66 fnetsetstate(fn, FNN_DEAD);
67 if(fn->sk != NULL)
68 {
69 fn->sk->close = 1;
d3372da9 70 putsock(fn->sk);
71 fn->sk = NULL;
72 }
73}
74
5ec60e42 75struct fnetnode *getfnetnode(struct fnetnode *fn)
d3372da9 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
5ec60e42 81 return(fn);
d3372da9 82}
83
84void 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);
d9c95ea0 103 CBCHAINFREE(fn, fnetpeer_new);
104 CBCHAINFREE(fn, fnetpeer_del);
105 CBCHAINFREE(fn, fnetpeer_chdi);
d3372da9 106 if(fn->fnet->destroy != NULL)
107 fn->fnet->destroy(fn);
3ea7528f 108 while(fn->args != NULL)
109 freewcspair(fn->args, &fn->args);
d3372da9 110 while(fn->peers != NULL)
111 fnetdelpeer(fn->peers);
112 if(fn->mynick != NULL)
113 free(fn->mynick);
d1e8b9fd 114 if(fn->pubid != NULL)
115 free(fn->pubid);
d3372da9 116 if(fn->name != NULL)
117 free(fn->name);
118 if(fn->sk != NULL)
119 putsock(fn->sk);
a3a4a3a7 120 if(fn->owner != NULL)
121 free(fn->owner);
d3372da9 122 free(fn);
123 numfnetnodes--;
124}
125
126struct 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
134void 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
147void 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
5ec60e42 162static int conncb(struct socket *sk, int err, struct fnetnode *data)
d3372da9 163{
164 if(err != 0)
165 {
166 killfnetnode(data);
167 putfnetnode(data);
5ec60e42 168 return(1);
d3372da9 169 }
170 data->sk = sk;
171 fnetsetstate(data, FNN_HS);
172 socksettos(sk, confgetint("fnet", "fntos"));
173 data->fnet->connect(data);
174 putfnetnode(data);
5ec60e42 175 return(1);
d3372da9 176}
177
178static void resolvecb(struct sockaddr *addr, int addrlen, struct fnetnode *data)
179{
180 if(addr == NULL)
181 {
182 killfnetnode(data);
183 putfnetnode(data);
184 } else {
5ec60e42 185 netcsconn(addr, addrlen, (int (*)(struct socket *, int, void *))conncb, data);
d3372da9 186 }
187}
188
189static 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
201static 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
d9c95ea0 217static struct fnetpeerdi *difindoradd(struct fnetpeer *peer, struct fnetpeerdatum *datum, int *isnew)
d3372da9 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++;
d9c95ea0 232 if(isnew != NULL)
233 *isnew = 1;
d3372da9 234 return(&peer->peerdi[peer->dinum++]);
235 } else {
d9c95ea0 236 if(isnew != NULL)
237 *isnew = 0;
d3372da9 238 return(&peer->peerdi[i]);
239 }
240}
241
242void fnetpeersetstr(struct fnetpeer *peer, wchar_t *id, wchar_t *value)
243{
244 struct fnetpeerdatum *datum;
245 struct fnetpeerdi *di;
d9c95ea0 246 int changed;
d3372da9 247
248 if((datum = finddatum(peer->fn, id)) == NULL)
249 datum = adddatum(peer->fn, id, FNPD_STR);
d9c95ea0 250 di = difindoradd(peer, datum, &changed);
251 if(di->data.str != NULL) {
378f34d2 252 changed = (changed || wcscmp(value, di->data.str));
d3372da9 253 free(di->data.str);
d9c95ea0 254 } else {
255 changed = 1;
256 }
d3372da9 257 di->data.str = swcsdup(value);
d9c95ea0 258 if(changed)
259 CBCHAINDOCB(peer->fn, fnetpeer_chdi, peer->fn, peer, di);
d3372da9 260}
261
262void fnetpeersetnum(struct fnetpeer *peer, wchar_t *id, int value)
263{
264 struct fnetpeerdatum *datum;
265 struct fnetpeerdi *di;
d9c95ea0 266 int changed;
d3372da9 267
268 if((datum = finddatum(peer->fn, id)) == NULL)
269 datum = adddatum(peer->fn, id, FNPD_INT);
d9c95ea0 270 di = difindoradd(peer, datum, &changed);
271 changed = (changed || (di->data.num != value));
d3372da9 272 di->data.num = value;
d9c95ea0 273 if(changed)
274 CBCHAINDOCB(peer->fn, fnetpeer_chdi, peer->fn, peer, di);
d3372da9 275}
276
277void fnetpeersetlnum(struct fnetpeer *peer, wchar_t *id, long long value)
278{
279 struct fnetpeerdatum *datum;
280 struct fnetpeerdi *di;
d9c95ea0 281 int changed;
d3372da9 282
283 if((datum = finddatum(peer->fn, id)) == NULL)
284 datum = adddatum(peer->fn, id, FNPD_LL);
d9c95ea0 285 di = difindoradd(peer, datum, &changed);
286 changed = (changed || (di->data.lnum != value));
d3372da9 287 di->data.lnum = value;
d9c95ea0 288 if(changed)
289 CBCHAINDOCB(peer->fn, fnetpeer_chdi, peer->fn, peer, di);
d3372da9 290}
291
292static 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
306void 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
327struct 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");
d9c95ea0 345 CBCHAINDOCB(fn, fnetpeer_new, fn, new);
d3372da9 346 return(new);
347}
348
349void 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");
d9c95ea0 361 CBCHAINDOCB(peer->fn, fnetpeer_del, peer->fn, peer);
d3372da9 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
375struct 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
383int 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
400int 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
410int 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
420void 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
428void fnetsetstate(struct fnetnode *fn, int newstate)
429{
430 fn->state = newstate;
431 CBCHAINDOCB(fn, fnetnode_ac, fn, L"state");
432}
433
434struct 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
a3a4a3a7 446struct fnetnode *fnetinitconnect(wchar_t *name, wchar_t *owner, char *addr, struct wcspair *args)
d3372da9 447{
448 struct fnet *fnet;
449 struct fnetnode *fn;
efc05613 450 struct wcspair *arg;
d3372da9 451
452 if((fnet = findfnet(name)) == NULL)
453 {
454 errno = EPROTONOSUPPORT;
455 return(NULL);
456 }
457 fn = newfn(fnet);
a3a4a3a7 458 fn->owner = swcsdup(owner);
d1e8b9fd 459 fn->pubid = icmbstowcs(addr, NULL);
460 if(fn->pubid == NULL)
461 fn->pubid = swcsdup(L"");
54771774 462 fn->args = args;
efc05613 463 for(arg = fn->args; arg != NULL; arg = arg->next)
464 {
465 if(!wcscmp(arg->key, L"nick"))
466 fnetsetnick(fn, arg->val);
467 }
d3372da9 468 getfnetnode(fn);
469 if(netresolve(addr, (void (*)(struct sockaddr *, int, void *))resolvecb, fn) < 0)
470 return(NULL);
471 return(fn);
472}
473
474void 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 */
495void 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
500static 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}},
cd340dab 505 {CONF_VAR_INT, "maxnodes", {.num = 0}},
d3372da9 506 {CONF_VAR_END}
507};
508
509static struct module me =
510{
511 .conf =
512 {
513 .vars = myvars
514 },
515 .name = "fnet"
516};
517
518MODULE(me)