Added functions for btree iteration.
[doldaconnect.git] / daemon / filenet.c
CommitLineData
d3372da9 1/*
2 * Dolda Connect - Modular multiuser Direct Connect-style client
302a2600 3 * Copyright (C) 2004 Fredrik Tolf <fredrik@dolda2000.com>
d3372da9 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);
ffa81d5f
FT
67 if(fn->connected)
68 fn->fnet->kill(fn);
d3372da9 69}
70
cab0b442 71void getfnetnode(struct fnetnode *fn)
d3372da9 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
79void 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);
d9c95ea0 98 CBCHAINFREE(fn, fnetpeer_new);
99 CBCHAINFREE(fn, fnetpeer_del);
100 CBCHAINFREE(fn, fnetpeer_chdi);
ffa81d5f 101 if((fn->fnet->destroy != NULL) && fn->connected)
d3372da9 102 fn->fnet->destroy(fn);
3ea7528f 103 while(fn->args != NULL)
104 freewcspair(fn->args, &fn->args);
d3372da9 105 while(fn->peers != NULL)
106 fnetdelpeer(fn->peers);
107 if(fn->mynick != NULL)
108 free(fn->mynick);
d1e8b9fd 109 if(fn->pubid != NULL)
110 free(fn->pubid);
d3372da9 111 if(fn->name != NULL)
112 free(fn->name);
a3a4a3a7 113 if(fn->owner != NULL)
114 free(fn->owner);
d3372da9 115 free(fn);
116 numfnetnodes--;
117}
118
119struct 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
127void 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
140void 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
cab0b442 155static void conncb(struct socket *sk, int err, struct fnetnode *data)
d3372da9 156{
157 if(err != 0)
158 {
159 killfnetnode(data);
160 putfnetnode(data);
cab0b442 161 return;
d3372da9 162 }
d3372da9 163 fnetsetstate(data, FNN_HS);
164 socksettos(sk, confgetint("fnet", "fntos"));
ffa81d5f
FT
165 data->fnet->connect(data, sk);
166 data->connected = 1;
d3372da9 167 putfnetnode(data);
ffa81d5f 168 putsock(sk);
d3372da9 169}
170
171static void resolvecb(struct sockaddr *addr, int addrlen, struct fnetnode *data)
172{
173 if(addr == NULL)
174 {
175 killfnetnode(data);
176 putfnetnode(data);
177 } else {
cab0b442 178 netcsconn(addr, addrlen, (void (*)(struct socket *, int, void *))conncb, data);
d3372da9 179 }
180}
181
182static 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
194static 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
d9c95ea0 210static struct fnetpeerdi *difindoradd(struct fnetpeer *peer, struct fnetpeerdatum *datum, int *isnew)
d3372da9 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++;
d9c95ea0 225 if(isnew != NULL)
226 *isnew = 1;
d3372da9 227 return(&peer->peerdi[peer->dinum++]);
228 } else {
d9c95ea0 229 if(isnew != NULL)
230 *isnew = 0;
d3372da9 231 return(&peer->peerdi[i]);
232 }
233}
234
235void fnetpeersetstr(struct fnetpeer *peer, wchar_t *id, wchar_t *value)
236{
237 struct fnetpeerdatum *datum;
238 struct fnetpeerdi *di;
d9c95ea0 239 int changed;
d3372da9 240
241 if((datum = finddatum(peer->fn, id)) == NULL)
242 datum = adddatum(peer->fn, id, FNPD_STR);
d9c95ea0 243 di = difindoradd(peer, datum, &changed);
244 if(di->data.str != NULL) {
378f34d2 245 changed = (changed || wcscmp(value, di->data.str));
d3372da9 246 free(di->data.str);
d9c95ea0 247 } else {
248 changed = 1;
249 }
d3372da9 250 di->data.str = swcsdup(value);
d9c95ea0 251 if(changed)
252 CBCHAINDOCB(peer->fn, fnetpeer_chdi, peer->fn, peer, di);
d3372da9 253}
254
255void fnetpeersetnum(struct fnetpeer *peer, wchar_t *id, int value)
256{
257 struct fnetpeerdatum *datum;
258 struct fnetpeerdi *di;
d9c95ea0 259 int changed;
d3372da9 260
261 if((datum = finddatum(peer->fn, id)) == NULL)
262 datum = adddatum(peer->fn, id, FNPD_INT);
d9c95ea0 263 di = difindoradd(peer, datum, &changed);
264 changed = (changed || (di->data.num != value));
d3372da9 265 di->data.num = value;
d9c95ea0 266 if(changed)
267 CBCHAINDOCB(peer->fn, fnetpeer_chdi, peer->fn, peer, di);
d3372da9 268}
269
270void fnetpeersetlnum(struct fnetpeer *peer, wchar_t *id, long long value)
271{
272 struct fnetpeerdatum *datum;
273 struct fnetpeerdi *di;
d9c95ea0 274 int changed;
d3372da9 275
276 if((datum = finddatum(peer->fn, id)) == NULL)
277 datum = adddatum(peer->fn, id, FNPD_LL);
d9c95ea0 278 di = difindoradd(peer, datum, &changed);
279 changed = (changed || (di->data.lnum != value));
d3372da9 280 di->data.lnum = value;
d9c95ea0 281 if(changed)
282 CBCHAINDOCB(peer->fn, fnetpeer_chdi, peer->fn, peer, di);
d3372da9 283}
284
285static 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
299void 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
320struct 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");
d9c95ea0 338 CBCHAINDOCB(fn, fnetpeer_new, fn, new);
d3372da9 339 return(new);
340}
341
342void 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");
d9c95ea0 354 CBCHAINDOCB(peer->fn, fnetpeer_del, peer->fn, peer);
d3372da9 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
368struct 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
376int fnetsetnick(struct fnetnode *fn, wchar_t *newnick)
377{
378 int ret;
379
ffa81d5f 380 if((fn->fnet->setnick != NULL) && fn->connected)
d3372da9 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
393int fnetsendchat(struct fnetnode *fn, int public, wchar_t *to, wchar_t *string)
394{
ffa81d5f 395 if((fn->fnet->sendchat == NULL) || !fn->connected)
d3372da9 396 {
397 errno = ENOTSUP;
398 return(-1);
399 }
400 return(fn->fnet->sendchat(fn, public, to, string));
401}
402
403int 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
413void 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
421void fnetsetstate(struct fnetnode *fn, int newstate)
422{
423 fn->state = newstate;
424 CBCHAINDOCB(fn, fnetnode_ac, fn, L"state");
425}
426
427struct 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
a3a4a3a7 439struct fnetnode *fnetinitconnect(wchar_t *name, wchar_t *owner, char *addr, struct wcspair *args)
d3372da9 440{
441 struct fnet *fnet;
442 struct fnetnode *fn;
efc05613 443 struct wcspair *arg;
d3372da9 444
445 if((fnet = findfnet(name)) == NULL)
446 {
447 errno = EPROTONOSUPPORT;
448 return(NULL);
449 }
450 fn = newfn(fnet);
a3a4a3a7 451 fn->owner = swcsdup(owner);
d1e8b9fd 452 fn->pubid = icmbstowcs(addr, NULL);
453 if(fn->pubid == NULL)
454 fn->pubid = swcsdup(L"");
54771774 455 fn->args = args;
efc05613 456 for(arg = fn->args; arg != NULL; arg = arg->next)
457 {
458 if(!wcscmp(arg->key, L"nick"))
459 fnetsetnick(fn, arg->val);
460 }
d3372da9 461 getfnetnode(fn);
462 if(netresolve(addr, (void (*)(struct sockaddr *, int, void *))resolvecb, fn) < 0)
463 return(NULL);
464 return(fn);
465}
466
467void 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 */
488void 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
493static struct configvar myvars[] =
494{
d9f89ef5 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). */
d3372da9 504 {CONF_VAR_INT, "srchwait", {.num = 15}},
d9f89ef5 505 /** The TOS value to use for hub connections (see the TOS VALUES
506 * section). */
d3372da9 507 {CONF_VAR_INT, "fntos", {.num = 0}},
d9f89ef5 508 /** The TOS value to use for peer connections (see the TOS VALUES
509 * section). */
d3372da9 510 {CONF_VAR_INT, "fnptos", {.num = 0}},
d9f89ef5 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. */
cd340dab 514 {CONF_VAR_INT, "maxnodes", {.num = 0}},
d3372da9 515 {CONF_VAR_END}
516};
517
518static struct module me =
519{
520 .conf =
521 {
522 .vars = myvars
523 },
524 .name = "fnet"
525};
526
527MODULE(me)