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