Optimized hubread.
[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
0e227c1d
FT
33static void freepeer(struct fnetpeer *peer);
34
d3372da9 35static struct fnet *networks = NULL;
36struct fnetnode *fnetnodes = NULL;
37int numfnetnodes = 0;
38GCBCHAIN(newfncb, struct fnetnode *);
39
40static struct fnetnode *newfn(struct fnet *fnet)
41{
42 static int curid = 0;
43 struct fnetnode *new;
44
45 new = smalloc(sizeof(*new));
46 memset(new, 0, sizeof(*new));
47 new->fnet = fnet;
48 new->refcount = 1;
49 new->id = curid++;
50 new->mynick = swcsdup(confgetstr("cli", "defnick"));
51 new->srchwait = confgetint("fnet", "srchwait");
52 new->state = FNN_SYN;
53 CBCHAININIT(new, fnetnode_ac);
54 CBCHAININIT(new, fnetnode_chat);
55 CBCHAININIT(new, fnetnode_unlink);
56 CBCHAININIT(new, fnetnode_destroy);
d9c95ea0 57 CBCHAININIT(new, fnetpeer_new);
58 CBCHAININIT(new, fnetpeer_del);
59 CBCHAININIT(new, fnetpeer_chdi);
d3372da9 60 new->next = NULL;
61 new->prev = NULL;
62 numfnetnodes++;
63 return(new);
64}
65
66void killfnetnode(struct fnetnode *fn)
67{
68 fnetsetstate(fn, FNN_DEAD);
ffa81d5f
FT
69 if(fn->connected)
70 fn->fnet->kill(fn);
d3372da9 71}
72
cab0b442 73void getfnetnode(struct fnetnode *fn)
d3372da9 74{
75 fn->refcount++;
76#ifdef DEBUG
77 fprintf(stderr, "getfnetnode on id %i at %p, refcount=%i\n", fn->id, fn, fn->refcount);
78#endif
79}
80
0e227c1d
FT
81static void freepeers(struct btree *n)
82{
83 if(n == NULL)
84 return;
85 freepeers(n->l);
86 freepeers(n->r);
87 freepeer(n->d);
88 free(n);
89}
90
d3372da9 91void putfnetnode(struct fnetnode *fn)
92{
93 struct fnetnode *cur;
94
95#ifdef DEBUG
96 fprintf(stderr, "putfnetnode on id %i at %p, refcount=%i\n", fn->id, fn, fn->refcount - 1);
97#endif
98 if(--fn->refcount)
99 return;
100 for(cur = fnetnodes; cur != NULL; cur = cur->next)
101 {
102 if(cur == fn)
103 flog(LOG_CRIT, "BUG: fnetnode reached refcount 0 while still in list - id %i", fn->id);
104 }
105 CBCHAINDOCB(fn, fnetnode_destroy, fn);
106 CBCHAINFREE(fn, fnetnode_ac);
107 CBCHAINFREE(fn, fnetnode_chat);
108 CBCHAINFREE(fn, fnetnode_unlink);
109 CBCHAINFREE(fn, fnetnode_destroy);
d9c95ea0 110 CBCHAINFREE(fn, fnetpeer_new);
111 CBCHAINFREE(fn, fnetpeer_del);
112 CBCHAINFREE(fn, fnetpeer_chdi);
ffa81d5f 113 if((fn->fnet->destroy != NULL) && fn->connected)
d3372da9 114 fn->fnet->destroy(fn);
3ea7528f 115 while(fn->args != NULL)
116 freewcspair(fn->args, &fn->args);
0e227c1d 117 freepeers(fn->peers);
d3372da9 118 if(fn->mynick != NULL)
119 free(fn->mynick);
d1e8b9fd 120 if(fn->pubid != NULL)
121 free(fn->pubid);
d3372da9 122 if(fn->name != NULL)
123 free(fn->name);
a3a4a3a7 124 if(fn->owner != NULL)
125 free(fn->owner);
d3372da9 126 free(fn);
127 numfnetnodes--;
128}
129
130struct fnetnode *findfnetnode(int id)
131{
132 struct fnetnode *fn;
133
134 for(fn = fnetnodes; (fn != NULL) && (fn->id != id); fn = fn->next);
135 return(fn);
136}
137
138void linkfnetnode(struct fnetnode *fn)
139{
140 if(fn->linked)
141 return;
142 getfnetnode(fn);
143 fn->next = fnetnodes;
144 if(fnetnodes != NULL)
145 fnetnodes->prev = fn;
146 fnetnodes = fn;
147 fn->linked = 1;
148 GCBCHAINDOCB(newfncb, fn);
149}
150
151void unlinkfnetnode(struct fnetnode *fn)
152{
153 if(!fn->linked)
154 return;
155 if(fnetnodes == fn)
156 fnetnodes = fn->next;
157 if(fn->next != NULL)
158 fn->next->prev = fn->prev;
159 if(fn->prev != NULL)
160 fn->prev->next = fn->next;
161 fn->linked = 0;
162 CBCHAINDOCB(fn, fnetnode_unlink, fn);
163 putfnetnode(fn);
164}
165
cab0b442 166static void conncb(struct socket *sk, int err, struct fnetnode *data)
d3372da9 167{
168 if(err != 0)
169 {
170 killfnetnode(data);
171 putfnetnode(data);
cab0b442 172 return;
d3372da9 173 }
d3372da9 174 fnetsetstate(data, FNN_HS);
175 socksettos(sk, confgetint("fnet", "fntos"));
ffa81d5f
FT
176 data->fnet->connect(data, sk);
177 data->connected = 1;
d3372da9 178 putfnetnode(data);
ffa81d5f 179 putsock(sk);
d3372da9 180}
181
182static void resolvecb(struct sockaddr *addr, int addrlen, struct fnetnode *data)
183{
184 if(addr == NULL)
185 {
186 killfnetnode(data);
187 putfnetnode(data);
188 } else {
cab0b442 189 netcsconn(addr, addrlen, (void (*)(struct socket *, int, void *))conncb, data);
d3372da9 190 }
191}
192
193static struct fnetpeerdatum *finddatum(struct fnetnode *fn, wchar_t *id)
194{
195 struct fnetpeerdatum *datum;
196
197 for(datum = fn->peerdata; datum != NULL; datum = datum->next)
198 {
199 if(!wcscmp(datum->id, id))
200 break;
201 }
202 return(datum);
203}
204
205static struct fnetpeerdatum *adddatum(struct fnetnode *fn, wchar_t *id, int datatype)
206{
207 struct fnetpeerdatum *new;
208
209 new = smalloc(sizeof(*new));
210 new->refcount = 0;
211 new->id = swcsdup(id);
212 new->datatype = datatype;
213 new->prev = NULL;
214 new->next = fn->peerdata;
215 if(fn->peerdata != NULL)
216 fn->peerdata->prev = new;
217 fn->peerdata = new;
218 return(new);
219}
220
d9c95ea0 221static struct fnetpeerdi *difindoradd(struct fnetpeer *peer, struct fnetpeerdatum *datum, int *isnew)
d3372da9 222{
223 int i;
224
225 for(i = 0; i < peer->dinum; i++)
226 {
227 if(peer->peerdi[i].datum == datum)
228 break;
229 }
230 if(i >= peer->dinum)
231 {
232 peer->peerdi = srealloc(peer->peerdi, sizeof(struct fnetpeerdi) * (peer->dinum + 1));
233 memset(&peer->peerdi[peer->dinum], 0, sizeof(struct fnetpeerdi));
234 peer->peerdi[peer->dinum].datum = datum;
235 datum->refcount++;
d9c95ea0 236 if(isnew != NULL)
237 *isnew = 1;
d3372da9 238 return(&peer->peerdi[peer->dinum++]);
239 } else {
d9c95ea0 240 if(isnew != NULL)
241 *isnew = 0;
d3372da9 242 return(&peer->peerdi[i]);
243 }
244}
245
246void fnetpeersetstr(struct fnetpeer *peer, wchar_t *id, wchar_t *value)
247{
248 struct fnetpeerdatum *datum;
249 struct fnetpeerdi *di;
d9c95ea0 250 int changed;
d3372da9 251
252 if((datum = finddatum(peer->fn, id)) == NULL)
253 datum = adddatum(peer->fn, id, FNPD_STR);
d9c95ea0 254 di = difindoradd(peer, datum, &changed);
255 if(di->data.str != NULL) {
378f34d2 256 changed = (changed || wcscmp(value, di->data.str));
d3372da9 257 free(di->data.str);
d9c95ea0 258 } else {
259 changed = 1;
260 }
d3372da9 261 di->data.str = swcsdup(value);
d9c95ea0 262 if(changed)
263 CBCHAINDOCB(peer->fn, fnetpeer_chdi, peer->fn, peer, di);
d3372da9 264}
265
266void fnetpeersetnum(struct fnetpeer *peer, wchar_t *id, int value)
267{
268 struct fnetpeerdatum *datum;
269 struct fnetpeerdi *di;
d9c95ea0 270 int changed;
d3372da9 271
272 if((datum = finddatum(peer->fn, id)) == NULL)
273 datum = adddatum(peer->fn, id, FNPD_INT);
d9c95ea0 274 di = difindoradd(peer, datum, &changed);
275 changed = (changed || (di->data.num != value));
d3372da9 276 di->data.num = value;
d9c95ea0 277 if(changed)
278 CBCHAINDOCB(peer->fn, fnetpeer_chdi, peer->fn, peer, di);
d3372da9 279}
280
281void fnetpeersetlnum(struct fnetpeer *peer, wchar_t *id, long long value)
282{
283 struct fnetpeerdatum *datum;
284 struct fnetpeerdi *di;
d9c95ea0 285 int changed;
d3372da9 286
287 if((datum = finddatum(peer->fn, id)) == NULL)
288 datum = adddatum(peer->fn, id, FNPD_LL);
d9c95ea0 289 di = difindoradd(peer, datum, &changed);
290 changed = (changed || (di->data.lnum != value));
d3372da9 291 di->data.lnum = value;
d9c95ea0 292 if(changed)
293 CBCHAINDOCB(peer->fn, fnetpeer_chdi, peer->fn, peer, di);
d3372da9 294}
295
296static void putdatum(struct fnetpeer *peer, struct fnetpeerdatum *datum)
297{
298 if(--datum->refcount > 0)
299 return;
300 if(datum->next != NULL)
301 datum->next->prev = datum->prev;
302 if(datum->prev != NULL)
303 datum->prev->next = datum->next;
304 if(datum == peer->fn->peerdata)
305 peer->fn->peerdata = datum->next;
306 free(datum->id);
307 free(datum);
308}
309
310void fnetpeerunset(struct fnetpeer *peer, wchar_t *id)
311{
312 int i;
313 struct fnetpeerdatum *datum;
314
315 if((datum = finddatum(peer->fn, id)) == NULL)
316 return;
317 for(i = 0; i < peer->dinum; i++)
318 {
319 if(peer->peerdi[i].datum == datum)
320 break;
321 }
322 if(i >= peer->dinum)
323 return;
324 if((datum->datatype == FNPD_STR) && (peer->peerdi[i].data.str != NULL))
325 free(peer->peerdi[i].data.str);
326 peer->dinum--;
327 memmove(&peer->peerdi[i], &peer->peerdi[i + 1], sizeof(struct fnetpeerdi) * (peer->dinum - i));
328 putdatum(peer, datum);
329}
330
0e227c1d
FT
331static int peercmpid(void *a, void *b)
332{
333 return(wcscmp(((struct fnetpeer *)a)->id, ((struct fnetpeer *)b)->id));
334}
335
d3372da9 336struct fnetpeer *fnetaddpeer(struct fnetnode *fn, wchar_t *id, wchar_t *nick)
337{
338 struct fnetpeer *new;
339
340 new = smalloc(sizeof(*new));
341 new->fn = fn;
342 new->id = swcsdup(id);
343 new->nick = swcsdup(nick);
344 new->flags.w = 0;
345 new->dinum = 0;
346 new->peerdi = NULL;
0e227c1d 347 bbtreeput(&fn->peers, new, peercmpid);
d3372da9 348 fn->numpeers++;
349 CBCHAINDOCB(fn, fnetnode_ac, fn, L"numpeers");
d9c95ea0 350 CBCHAINDOCB(fn, fnetpeer_new, fn, new);
d3372da9 351 return(new);
352}
353
0e227c1d 354static void freepeer(struct fnetpeer *peer)
d3372da9 355{
356 int i;
357
d3372da9 358 peer->fn->numpeers--;
d3372da9 359 free(peer->id);
360 free(peer->nick);
361 for(i = 0; i < peer->dinum; i++)
362 {
363 if((peer->peerdi[i].datum->datatype == FNPD_STR) && (peer->peerdi[i].data.str != NULL))
364 free(peer->peerdi[i].data.str);
365 putdatum(peer, peer->peerdi[i].datum);
366 }
367 if(peer->peerdi != NULL)
368 free(peer->peerdi);
369 free(peer);
370}
371
0e227c1d
FT
372void fnetdelpeer(struct fnetpeer *peer)
373{
374 bbtreedel(&peer->fn->peers, peer, peercmpid);
375 CBCHAINDOCB(peer->fn, fnetnode_ac, peer->fn, L"numpeers");
376 CBCHAINDOCB(peer->fn, fnetpeer_del, peer->fn, peer);
377 freepeer(peer);
378}
379
380void fnetpeerdm(struct fnetnode *fn)
381{
382 struct btree *new;
383 struct fnetpeer *peer;
384 int intact;
385
386 new = NULL;
387 intact = 1;
388 for(peer = btreeiter(fn->peers); peer != NULL; peer = btreeiter(NULL)) {
389 if(!peer->flags.b.delete) {
390 bbtreeput(&new, peer, peercmpid);
391 } else {
392 intact = 0;
393 CBCHAINDOCB(peer->fn, fnetpeer_del, peer->fn, peer);
394 freepeer(peer);
395 }
396 }
397 btreefree(fn->peers);
398 fn->peers = new;
399 if(!intact)
400 CBCHAINDOCB(peer->fn, fnetnode_ac, peer->fn, L"numpeers");
401}
402
d3372da9 403struct fnetpeer *fnetfindpeer(struct fnetnode *fn, wchar_t *id)
404{
0e227c1d 405 struct fnetpeer key;
d3372da9 406
0e227c1d
FT
407 key.id = id;
408 return(btreeget(fn->peers, &key, peercmpid));
d3372da9 409}
410
411int fnetsetnick(struct fnetnode *fn, wchar_t *newnick)
412{
413 int ret;
414
ffa81d5f 415 if((fn->fnet->setnick != NULL) && fn->connected)
d3372da9 416 ret = fn->fnet->setnick(fn, newnick);
417 else
418 ret = 0;
419 if(!ret)
420 {
421 if(fn->mynick != NULL)
422 free(fn->mynick);
423 fn->mynick = swcsdup(newnick);
424 }
425 return(ret);
426}
427
428int fnetsendchat(struct fnetnode *fn, int public, wchar_t *to, wchar_t *string)
429{
ffa81d5f 430 if((fn->fnet->sendchat == NULL) || !fn->connected)
d3372da9 431 {
432 errno = ENOTSUP;
433 return(-1);
434 }
435 return(fn->fnet->sendchat(fn, public, to, string));
436}
437
438int fnetsearch(struct fnetnode *fn, struct search *srch, struct srchfnnlist *ln)
439{
440 if(fn->fnet->search == NULL)
441 {
442 errno = ENOTSUP;
443 return(-1);
444 }
445 return(fn->fnet->search(fn, srch, ln));
446}
447
448void fnetsetname(struct fnetnode *fn, wchar_t *newname)
449{
450 if(fn->name != NULL)
451 free(fn->name);
452 fn->name = swcsdup(newname);
453 CBCHAINDOCB(fn, fnetnode_ac, fn, L"name");
454}
455
456void fnetsetstate(struct fnetnode *fn, int newstate)
457{
458 fn->state = newstate;
459 CBCHAINDOCB(fn, fnetnode_ac, fn, L"state");
460}
461
462struct fnet *findfnet(wchar_t *name)
463{
464 struct fnet *fnet;
465
466 for(fnet = networks; fnet != NULL; fnet = fnet->next)
467 {
468 if(!wcscmp(name, fnet->name))
469 break;
470 }
471 return(fnet);
472}
473
a3a4a3a7 474struct fnetnode *fnetinitconnect(wchar_t *name, wchar_t *owner, char *addr, struct wcspair *args)
d3372da9 475{
476 struct fnet *fnet;
477 struct fnetnode *fn;
efc05613 478 struct wcspair *arg;
d3372da9 479
480 if((fnet = findfnet(name)) == NULL)
481 {
482 errno = EPROTONOSUPPORT;
483 return(NULL);
484 }
485 fn = newfn(fnet);
a3a4a3a7 486 fn->owner = swcsdup(owner);
d1e8b9fd 487 fn->pubid = icmbstowcs(addr, NULL);
488 if(fn->pubid == NULL)
489 fn->pubid = swcsdup(L"");
54771774 490 fn->args = args;
efc05613 491 for(arg = fn->args; arg != NULL; arg = arg->next)
492 {
493 if(!wcscmp(arg->key, L"nick"))
494 fnetsetnick(fn, arg->val);
495 }
d3372da9 496 getfnetnode(fn);
497 if(netresolve(addr, (void (*)(struct sockaddr *, int, void *))resolvecb, fn) < 0)
498 return(NULL);
499 return(fn);
500}
501
502void regfnet(struct fnet *fnet)
503{
504 fnet->next = networks;
505 networks = fnet;
506}
507
508/*
509 * Note on the chat string: Must be in UNIX text file format - that
510 * is, LF line endings. The filenet-specific code must see to it that
511 * any other kind of format is converted into that. In the future,
512 * certain control characters and escape sequences will be parsed by
513 * the client. Make sure that any filenet-specific code strips any
514 * such that aren't supposed to be in the protocol.
515 *
516 * Note on "name": This is supposed to be an identifier for the
517 * source. If the chat is a public message, set "public" to non-zero
518 * and "name" to whatever "chat room" name is appropriate for the
519 * fnetnode, but not NULL. If there is a "default" channel in this
520 * filenet, set "name" to the empty string. If the chat is a private
521 * message, name is ignored.
522 */
523void fnethandlechat(struct fnetnode *fn, int public, wchar_t *name, wchar_t *peer, wchar_t *chat)
524{
525 CBCHAINDOCB(fn, fnetnode_chat, fn, public, name, peer, chat);
526}
527
528static struct configvar myvars[] =
529{
d9f89ef5 530 /** The number of seconds to wait between searches. Most hubs
531 * require at least ten seconds, and quite often network lag will
532 * often make searches arrive to the hub more often than sent. It
533 * may be semi-dangerous to specify too low a value, since hubs
534 * will often kick users that search too often (even when the
535 * reason is network lag -- there is no way for the hub to know
536 * this), but it is quite annoying to set too high a value. 15 to
537 * 40 seconds are the recommended range (although the default is
538 * 15 seconds, it is recommended to set to 30 seconds). */
d3372da9 539 {CONF_VAR_INT, "srchwait", {.num = 15}},
d9f89ef5 540 /** The TOS value to use for hub connections (see the TOS VALUES
541 * section). */
d3372da9 542 {CONF_VAR_INT, "fntos", {.num = 0}},
d9f89ef5 543 /** The TOS value to use for peer connections (see the TOS VALUES
544 * section). */
d3372da9 545 {CONF_VAR_INT, "fnptos", {.num = 0}},
d9f89ef5 546 /** Specifies a maximum number of simultaneously connected
547 * hubs. Attempts to connect to new hubs beyond this limit will
548 * return an error. Set to zero to remove the limit. */
cd340dab 549 {CONF_VAR_INT, "maxnodes", {.num = 0}},
d3372da9 550 {CONF_VAR_END}
551};
552
553static struct module me =
554{
555 .conf =
556 {
557 .vars = myvars
558 },
559 .name = "fnet"
560};
561
562MODULE(me)