ChangeLog update.
[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);
67 if(fn->sk != NULL)
68 {
69 fn->sk->close = 1;
cab0b442 70 if(fn->sk->data == fn)
71 {
72 fn->sk->data = NULL;
73 putfnetnode(fn);
74 }
d3372da9 75 putsock(fn->sk);
76 fn->sk = NULL;
77 }
78}
79
cab0b442 80void getfnetnode(struct fnetnode *fn)
d3372da9 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
88void 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);
d9c95ea0 107 CBCHAINFREE(fn, fnetpeer_new);
108 CBCHAINFREE(fn, fnetpeer_del);
109 CBCHAINFREE(fn, fnetpeer_chdi);
d3372da9 110 if(fn->fnet->destroy != NULL)
111 fn->fnet->destroy(fn);
3ea7528f 112 while(fn->args != NULL)
113 freewcspair(fn->args, &fn->args);
d3372da9 114 while(fn->peers != NULL)
115 fnetdelpeer(fn->peers);
116 if(fn->mynick != NULL)
117 free(fn->mynick);
d1e8b9fd 118 if(fn->pubid != NULL)
119 free(fn->pubid);
d3372da9 120 if(fn->name != NULL)
121 free(fn->name);
122 if(fn->sk != NULL)
123 putsock(fn->sk);
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 }
174 data->sk = sk;
175 fnetsetstate(data, FNN_HS);
176 socksettos(sk, confgetint("fnet", "fntos"));
177 data->fnet->connect(data);
178 putfnetnode(data);
179}
180
181static void resolvecb(struct sockaddr *addr, int addrlen, struct fnetnode *data)
182{
183 if(addr == NULL)
184 {
185 killfnetnode(data);
186 putfnetnode(data);
187 } else {
cab0b442 188 netcsconn(addr, addrlen, (void (*)(struct socket *, int, void *))conncb, data);
d3372da9 189 }
190}
191
192static struct fnetpeerdatum *finddatum(struct fnetnode *fn, wchar_t *id)
193{
194 struct fnetpeerdatum *datum;
195
196 for(datum = fn->peerdata; datum != NULL; datum = datum->next)
197 {
198 if(!wcscmp(datum->id, id))
199 break;
200 }
201 return(datum);
202}
203
204static struct fnetpeerdatum *adddatum(struct fnetnode *fn, wchar_t *id, int datatype)
205{
206 struct fnetpeerdatum *new;
207
208 new = smalloc(sizeof(*new));
209 new->refcount = 0;
210 new->id = swcsdup(id);
211 new->datatype = datatype;
212 new->prev = NULL;
213 new->next = fn->peerdata;
214 if(fn->peerdata != NULL)
215 fn->peerdata->prev = new;
216 fn->peerdata = new;
217 return(new);
218}
219
d9c95ea0 220static struct fnetpeerdi *difindoradd(struct fnetpeer *peer, struct fnetpeerdatum *datum, int *isnew)
d3372da9 221{
222 int i;
223
224 for(i = 0; i < peer->dinum; i++)
225 {
226 if(peer->peerdi[i].datum == datum)
227 break;
228 }
229 if(i >= peer->dinum)
230 {
231 peer->peerdi = srealloc(peer->peerdi, sizeof(struct fnetpeerdi) * (peer->dinum + 1));
232 memset(&peer->peerdi[peer->dinum], 0, sizeof(struct fnetpeerdi));
233 peer->peerdi[peer->dinum].datum = datum;
234 datum->refcount++;
d9c95ea0 235 if(isnew != NULL)
236 *isnew = 1;
d3372da9 237 return(&peer->peerdi[peer->dinum++]);
238 } else {
d9c95ea0 239 if(isnew != NULL)
240 *isnew = 0;
d3372da9 241 return(&peer->peerdi[i]);
242 }
243}
244
245void fnetpeersetstr(struct fnetpeer *peer, wchar_t *id, wchar_t *value)
246{
247 struct fnetpeerdatum *datum;
248 struct fnetpeerdi *di;
d9c95ea0 249 int changed;
d3372da9 250
251 if((datum = finddatum(peer->fn, id)) == NULL)
252 datum = adddatum(peer->fn, id, FNPD_STR);
d9c95ea0 253 di = difindoradd(peer, datum, &changed);
254 if(di->data.str != NULL) {
378f34d2 255 changed = (changed || wcscmp(value, di->data.str));
d3372da9 256 free(di->data.str);
d9c95ea0 257 } else {
258 changed = 1;
259 }
d3372da9 260 di->data.str = swcsdup(value);
d9c95ea0 261 if(changed)
262 CBCHAINDOCB(peer->fn, fnetpeer_chdi, peer->fn, peer, di);
d3372da9 263}
264
265void fnetpeersetnum(struct fnetpeer *peer, wchar_t *id, int value)
266{
267 struct fnetpeerdatum *datum;
268 struct fnetpeerdi *di;
d9c95ea0 269 int changed;
d3372da9 270
271 if((datum = finddatum(peer->fn, id)) == NULL)
272 datum = adddatum(peer->fn, id, FNPD_INT);
d9c95ea0 273 di = difindoradd(peer, datum, &changed);
274 changed = (changed || (di->data.num != value));
d3372da9 275 di->data.num = value;
d9c95ea0 276 if(changed)
277 CBCHAINDOCB(peer->fn, fnetpeer_chdi, peer->fn, peer, di);
d3372da9 278}
279
280void fnetpeersetlnum(struct fnetpeer *peer, wchar_t *id, long long value)
281{
282 struct fnetpeerdatum *datum;
283 struct fnetpeerdi *di;
d9c95ea0 284 int changed;
d3372da9 285
286 if((datum = finddatum(peer->fn, id)) == NULL)
287 datum = adddatum(peer->fn, id, FNPD_LL);
d9c95ea0 288 di = difindoradd(peer, datum, &changed);
289 changed = (changed || (di->data.lnum != value));
d3372da9 290 di->data.lnum = value;
d9c95ea0 291 if(changed)
292 CBCHAINDOCB(peer->fn, fnetpeer_chdi, peer->fn, peer, di);
d3372da9 293}
294
295static void putdatum(struct fnetpeer *peer, struct fnetpeerdatum *datum)
296{
297 if(--datum->refcount > 0)
298 return;
299 if(datum->next != NULL)
300 datum->next->prev = datum->prev;
301 if(datum->prev != NULL)
302 datum->prev->next = datum->next;
303 if(datum == peer->fn->peerdata)
304 peer->fn->peerdata = datum->next;
305 free(datum->id);
306 free(datum);
307}
308
309void fnetpeerunset(struct fnetpeer *peer, wchar_t *id)
310{
311 int i;
312 struct fnetpeerdatum *datum;
313
314 if((datum = finddatum(peer->fn, id)) == NULL)
315 return;
316 for(i = 0; i < peer->dinum; i++)
317 {
318 if(peer->peerdi[i].datum == datum)
319 break;
320 }
321 if(i >= peer->dinum)
322 return;
323 if((datum->datatype == FNPD_STR) && (peer->peerdi[i].data.str != NULL))
324 free(peer->peerdi[i].data.str);
325 peer->dinum--;
326 memmove(&peer->peerdi[i], &peer->peerdi[i + 1], sizeof(struct fnetpeerdi) * (peer->dinum - i));
327 putdatum(peer, datum);
328}
329
330struct fnetpeer *fnetaddpeer(struct fnetnode *fn, wchar_t *id, wchar_t *nick)
331{
332 struct fnetpeer *new;
333
334 new = smalloc(sizeof(*new));
335 new->fn = fn;
336 new->id = swcsdup(id);
337 new->nick = swcsdup(nick);
338 new->flags.w = 0;
339 new->dinum = 0;
340 new->peerdi = NULL;
341 new->next = fn->peers;
342 new->prev = NULL;
343 if(fn->peers != NULL)
344 fn->peers->prev = new;
345 fn->peers = new;
346 fn->numpeers++;
347 CBCHAINDOCB(fn, fnetnode_ac, fn, L"numpeers");
d9c95ea0 348 CBCHAINDOCB(fn, fnetpeer_new, fn, new);
d3372da9 349 return(new);
350}
351
352void fnetdelpeer(struct fnetpeer *peer)
353{
354 int i;
355
356 if(peer->next != NULL)
357 peer->next->prev = peer->prev;
358 if(peer->prev != NULL)
359 peer->prev->next = peer->next;
360 if(peer->fn->peers == peer)
361 peer->fn->peers = peer->next;
362 peer->fn->numpeers--;
363 CBCHAINDOCB(peer->fn, fnetnode_ac, peer->fn, L"numpeers");
d9c95ea0 364 CBCHAINDOCB(peer->fn, fnetpeer_del, peer->fn, peer);
d3372da9 365 free(peer->id);
366 free(peer->nick);
367 for(i = 0; i < peer->dinum; i++)
368 {
369 if((peer->peerdi[i].datum->datatype == FNPD_STR) && (peer->peerdi[i].data.str != NULL))
370 free(peer->peerdi[i].data.str);
371 putdatum(peer, peer->peerdi[i].datum);
372 }
373 if(peer->peerdi != NULL)
374 free(peer->peerdi);
375 free(peer);
376}
377
378struct fnetpeer *fnetfindpeer(struct fnetnode *fn, wchar_t *id)
379{
380 struct fnetpeer *cur;
381
382 for(cur = fn->peers; (cur != NULL) && wcscmp(cur->id, id); cur = cur->next);
383 return(cur);
384}
385
386int fnetsetnick(struct fnetnode *fn, wchar_t *newnick)
387{
388 int ret;
389
390 if(fn->fnet->setnick != NULL)
391 ret = fn->fnet->setnick(fn, newnick);
392 else
393 ret = 0;
394 if(!ret)
395 {
396 if(fn->mynick != NULL)
397 free(fn->mynick);
398 fn->mynick = swcsdup(newnick);
399 }
400 return(ret);
401}
402
403int fnetsendchat(struct fnetnode *fn, int public, wchar_t *to, wchar_t *string)
404{
405 if(fn->fnet->sendchat == NULL)
406 {
407 errno = ENOTSUP;
408 return(-1);
409 }
410 return(fn->fnet->sendchat(fn, public, to, string));
411}
412
413int fnetsearch(struct fnetnode *fn, struct search *srch, struct srchfnnlist *ln)
414{
415 if(fn->fnet->search == NULL)
416 {
417 errno = ENOTSUP;
418 return(-1);
419 }
420 return(fn->fnet->search(fn, srch, ln));
421}
422
423void fnetsetname(struct fnetnode *fn, wchar_t *newname)
424{
425 if(fn->name != NULL)
426 free(fn->name);
427 fn->name = swcsdup(newname);
428 CBCHAINDOCB(fn, fnetnode_ac, fn, L"name");
429}
430
431void fnetsetstate(struct fnetnode *fn, int newstate)
432{
433 fn->state = newstate;
434 CBCHAINDOCB(fn, fnetnode_ac, fn, L"state");
435}
436
437struct fnet *findfnet(wchar_t *name)
438{
439 struct fnet *fnet;
440
441 for(fnet = networks; fnet != NULL; fnet = fnet->next)
442 {
443 if(!wcscmp(name, fnet->name))
444 break;
445 }
446 return(fnet);
447}
448
a3a4a3a7 449struct fnetnode *fnetinitconnect(wchar_t *name, wchar_t *owner, char *addr, struct wcspair *args)
d3372da9 450{
451 struct fnet *fnet;
452 struct fnetnode *fn;
efc05613 453 struct wcspair *arg;
d3372da9 454
455 if((fnet = findfnet(name)) == NULL)
456 {
457 errno = EPROTONOSUPPORT;
458 return(NULL);
459 }
460 fn = newfn(fnet);
a3a4a3a7 461 fn->owner = swcsdup(owner);
d1e8b9fd 462 fn->pubid = icmbstowcs(addr, NULL);
463 if(fn->pubid == NULL)
464 fn->pubid = swcsdup(L"");
54771774 465 fn->args = args;
efc05613 466 for(arg = fn->args; arg != NULL; arg = arg->next)
467 {
468 if(!wcscmp(arg->key, L"nick"))
469 fnetsetnick(fn, arg->val);
470 }
d3372da9 471 getfnetnode(fn);
472 if(netresolve(addr, (void (*)(struct sockaddr *, int, void *))resolvecb, fn) < 0)
473 return(NULL);
474 return(fn);
475}
476
477void regfnet(struct fnet *fnet)
478{
479 fnet->next = networks;
480 networks = fnet;
481}
482
483/*
484 * Note on the chat string: Must be in UNIX text file format - that
485 * is, LF line endings. The filenet-specific code must see to it that
486 * any other kind of format is converted into that. In the future,
487 * certain control characters and escape sequences will be parsed by
488 * the client. Make sure that any filenet-specific code strips any
489 * such that aren't supposed to be in the protocol.
490 *
491 * Note on "name": This is supposed to be an identifier for the
492 * source. If the chat is a public message, set "public" to non-zero
493 * and "name" to whatever "chat room" name is appropriate for the
494 * fnetnode, but not NULL. If there is a "default" channel in this
495 * filenet, set "name" to the empty string. If the chat is a private
496 * message, name is ignored.
497 */
498void fnethandlechat(struct fnetnode *fn, int public, wchar_t *name, wchar_t *peer, wchar_t *chat)
499{
500 CBCHAINDOCB(fn, fnetnode_chat, fn, public, name, peer, chat);
501}
502
503static struct configvar myvars[] =
504{
d9f89ef5 505 /** The number of seconds to wait between searches. Most hubs
506 * require at least ten seconds, and quite often network lag will
507 * often make searches arrive to the hub more often than sent. It
508 * may be semi-dangerous to specify too low a value, since hubs
509 * will often kick users that search too often (even when the
510 * reason is network lag -- there is no way for the hub to know
511 * this), but it is quite annoying to set too high a value. 15 to
512 * 40 seconds are the recommended range (although the default is
513 * 15 seconds, it is recommended to set to 30 seconds). */
d3372da9 514 {CONF_VAR_INT, "srchwait", {.num = 15}},
d9f89ef5 515 /** The TOS value to use for hub connections (see the TOS VALUES
516 * section). */
d3372da9 517 {CONF_VAR_INT, "fntos", {.num = 0}},
d9f89ef5 518 /** The TOS value to use for peer connections (see the TOS VALUES
519 * section). */
d3372da9 520 {CONF_VAR_INT, "fnptos", {.num = 0}},
d9f89ef5 521 /** Specifies a maximum number of simultaneously connected
522 * hubs. Attempts to connect to new hubs beyond this limit will
523 * return an error. Set to zero to remove the limit. */
cd340dab 524 {CONF_VAR_INT, "maxnodes", {.num = 0}},
d3372da9 525 {CONF_VAR_END}
526};
527
528static struct module me =
529{
530 .conf =
531 {
532 .vars = myvars
533 },
534 .name = "fnet"
535};
536
537MODULE(me)