2 * Dolda Connect - Modular multiuser Direct Connect-style client
3 * Copyright (C) 2004 Fredrik Tolf <fredrik@dolda2000.com>
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.
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.
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
21 #include <sys/socket.h>
33 static void freepeer(struct fnetpeer *peer);
35 static struct fnet *networks = NULL;
36 struct fnetnode *fnetnodes = NULL;
38 GCBCHAIN(newfncb, struct fnetnode *);
40 static struct fnetnode *newfn(struct fnet *fnet)
45 new = smalloc(sizeof(*new));
46 memset(new, 0, sizeof(*new));
50 new->mynick = swcsdup(confgetstr("cli", "defnick"));
51 new->srchwait = confgetint("fnet", "srchwait");
53 CBCHAININIT(new, fnetnode_ac);
54 CBCHAININIT(new, fnetnode_chat);
55 CBCHAININIT(new, fnetnode_unlink);
56 CBCHAININIT(new, fnetnode_destroy);
57 CBCHAININIT(new, fnetpeer_new);
58 CBCHAININIT(new, fnetpeer_del);
59 CBCHAININIT(new, fnetpeer_chdi);
66 void killfnetnode(struct fnetnode *fn)
68 fnetsetstate(fn, FNN_DEAD);
73 void getfnetnode(struct fnetnode *fn)
77 fprintf(stderr, "getfnetnode on id %i at %p, refcount=%i\n", fn->id, fn, fn->refcount);
81 static void freepeers(struct btree *n)
91 void putfnetnode(struct fnetnode *fn)
96 fprintf(stderr, "putfnetnode on id %i at %p, refcount=%i\n", fn->id, fn, fn->refcount - 1);
100 for(cur = fnetnodes; cur != NULL; cur = cur->next)
103 flog(LOG_CRIT, "BUG: fnetnode reached refcount 0 while still in list - id %i", fn->id);
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);
110 CBCHAINFREE(fn, fnetpeer_new);
111 CBCHAINFREE(fn, fnetpeer_del);
112 CBCHAINFREE(fn, fnetpeer_chdi);
113 if((fn->fnet->destroy != NULL) && fn->connected)
114 fn->fnet->destroy(fn);
115 while(fn->args != NULL)
116 freewcspair(fn->args, &fn->args);
117 freepeers(fn->peers);
118 if(fn->mynick != NULL)
120 if(fn->pubid != NULL)
124 if(fn->owner != NULL)
130 struct fnetnode *findfnetnode(int id)
134 for(fn = fnetnodes; (fn != NULL) && (fn->id != id); fn = fn->next);
138 void linkfnetnode(struct fnetnode *fn)
143 fn->next = fnetnodes;
144 if(fnetnodes != NULL)
145 fnetnodes->prev = fn;
148 GCBCHAINDOCB(newfncb, fn);
151 void unlinkfnetnode(struct fnetnode *fn)
156 fnetnodes = fn->next;
158 fn->next->prev = fn->prev;
160 fn->prev->next = fn->next;
162 CBCHAINDOCB(fn, fnetnode_unlink, fn);
166 static void conncb(struct socket *sk, int err, struct fnetnode *data)
174 fnetsetstate(data, FNN_HS);
175 socksettos(sk, confgetint("fnet", "fntos"));
176 data->fnet->connect(data, sk);
182 static void resolvecb(struct sockaddr *addr, int addrlen, struct fnetnode *data)
189 netcsconn(addr, addrlen, (void (*)(struct socket *, int, void *))conncb, data);
193 static struct fnetpeerdatum *finddatum(struct fnetnode *fn, wchar_t *id)
195 struct fnetpeerdatum *datum;
197 for(datum = fn->peerdata; datum != NULL; datum = datum->next)
199 if(!wcscmp(datum->id, id))
205 static struct fnetpeerdatum *adddatum(struct fnetnode *fn, wchar_t *id, int datatype)
207 struct fnetpeerdatum *new;
209 new = smalloc(sizeof(*new));
211 new->id = swcsdup(id);
212 new->datatype = datatype;
214 new->next = fn->peerdata;
215 if(fn->peerdata != NULL)
216 fn->peerdata->prev = new;
221 static struct fnetpeerdi *difindoradd(struct fnetpeer *peer, struct fnetpeerdatum *datum, int *isnew)
225 for(i = 0; i < peer->dinum; i++)
227 if(peer->peerdi[i].datum == datum)
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;
238 return(&peer->peerdi[peer->dinum++]);
242 return(&peer->peerdi[i]);
246 void fnetpeersetstr(struct fnetpeer *peer, wchar_t *id, wchar_t *value)
248 struct fnetpeerdatum *datum;
249 struct fnetpeerdi *di;
252 if((datum = finddatum(peer->fn, id)) == NULL)
253 datum = adddatum(peer->fn, id, FNPD_STR);
254 di = difindoradd(peer, datum, &changed);
255 if(di->data.str != NULL) {
256 changed = (changed || wcscmp(value, di->data.str));
261 di->data.str = swcsdup(value);
263 CBCHAINDOCB(peer->fn, fnetpeer_chdi, peer->fn, peer, di);
266 void fnetpeersetnum(struct fnetpeer *peer, wchar_t *id, int value)
268 struct fnetpeerdatum *datum;
269 struct fnetpeerdi *di;
272 if((datum = finddatum(peer->fn, id)) == NULL)
273 datum = adddatum(peer->fn, id, FNPD_INT);
274 di = difindoradd(peer, datum, &changed);
275 changed = (changed || (di->data.num != value));
276 di->data.num = value;
278 CBCHAINDOCB(peer->fn, fnetpeer_chdi, peer->fn, peer, di);
281 void fnetpeersetlnum(struct fnetpeer *peer, wchar_t *id, long long value)
283 struct fnetpeerdatum *datum;
284 struct fnetpeerdi *di;
287 if((datum = finddatum(peer->fn, id)) == NULL)
288 datum = adddatum(peer->fn, id, FNPD_LL);
289 di = difindoradd(peer, datum, &changed);
290 changed = (changed || (di->data.lnum != value));
291 di->data.lnum = value;
293 CBCHAINDOCB(peer->fn, fnetpeer_chdi, peer->fn, peer, di);
296 static void putdatum(struct fnetpeer *peer, struct fnetpeerdatum *datum)
298 if(--datum->refcount > 0)
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;
310 void fnetpeerunset(struct fnetpeer *peer, wchar_t *id)
313 struct fnetpeerdatum *datum;
315 if((datum = finddatum(peer->fn, id)) == NULL)
317 for(i = 0; i < peer->dinum; i++)
319 if(peer->peerdi[i].datum == datum)
324 if((datum->datatype == FNPD_STR) && (peer->peerdi[i].data.str != NULL))
325 free(peer->peerdi[i].data.str);
327 memmove(&peer->peerdi[i], &peer->peerdi[i + 1], sizeof(struct fnetpeerdi) * (peer->dinum - i));
328 putdatum(peer, datum);
331 static int peercmpid(void *a, void *b)
333 return(wcscmp(((struct fnetpeer *)a)->id, ((struct fnetpeer *)b)->id));
336 struct fnetpeer *fnetaddpeer(struct fnetnode *fn, wchar_t *id, wchar_t *nick)
338 struct fnetpeer *new;
340 new = smalloc(sizeof(*new));
342 new->id = swcsdup(id);
343 new->nick = swcsdup(nick);
347 bbtreeput(&fn->peers, new, peercmpid);
349 CBCHAINDOCB(fn, fnetnode_ac, fn, L"numpeers");
350 CBCHAINDOCB(fn, fnetpeer_new, fn, new);
354 static void freepeer(struct fnetpeer *peer)
358 peer->fn->numpeers--;
361 for(i = 0; i < peer->dinum; i++)
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);
367 if(peer->peerdi != NULL)
372 void fnetdelpeer(struct fnetpeer *peer)
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);
380 void fnetpeerdm(struct fnetnode *fn)
383 struct fnetpeer *peer;
388 for(peer = btreeiter(fn->peers); peer != NULL; peer = btreeiter(NULL)) {
389 if(!peer->flags.b.delete) {
390 bbtreeput(&new, peer, peercmpid);
393 CBCHAINDOCB(peer->fn, fnetpeer_del, peer->fn, peer);
397 btreefree(fn->peers);
400 CBCHAINDOCB(peer->fn, fnetnode_ac, peer->fn, L"numpeers");
403 struct fnetpeer *fnetfindpeer(struct fnetnode *fn, wchar_t *id)
408 return(btreeget(fn->peers, &key, peercmpid));
411 int fnetsetnick(struct fnetnode *fn, wchar_t *newnick)
415 if((fn->fnet->setnick != NULL) && fn->connected)
416 ret = fn->fnet->setnick(fn, newnick);
421 if(fn->mynick != NULL)
423 fn->mynick = swcsdup(newnick);
428 int fnetsendchat(struct fnetnode *fn, int public, wchar_t *to, wchar_t *string)
430 if((fn->fnet->sendchat == NULL) || !fn->connected)
435 return(fn->fnet->sendchat(fn, public, to, string));
438 int fnetsearch(struct fnetnode *fn, struct search *srch, struct srchfnnlist *ln)
440 if(fn->fnet->search == NULL)
445 return(fn->fnet->search(fn, srch, ln));
448 void fnetsetname(struct fnetnode *fn, wchar_t *newname)
452 fn->name = swcsdup(newname);
453 CBCHAINDOCB(fn, fnetnode_ac, fn, L"name");
456 void fnetsetstate(struct fnetnode *fn, int newstate)
458 fn->state = newstate;
459 CBCHAINDOCB(fn, fnetnode_ac, fn, L"state");
462 wchar_t *fnfilebasename(wchar_t *path)
466 if((p = wcsrchr(path, L'/')) != NULL)
471 struct fnet *findfnet(wchar_t *name)
475 for(fnet = networks; fnet != NULL; fnet = fnet->next)
477 if(!wcscmp(name, fnet->name))
483 struct fnetnode *fnetinitconnect(wchar_t *name, wchar_t *owner, char *addr, struct wcspair *args)
489 if((fnet = findfnet(name)) == NULL)
491 errno = EPROTONOSUPPORT;
495 fn->owner = swcsdup(owner);
496 fn->pubid = icmbstowcs(addr, NULL);
497 if(fn->pubid == NULL)
498 fn->pubid = swcsdup(L"");
500 for(arg = fn->args; arg != NULL; arg = arg->next)
502 if(!wcscmp(arg->key, L"nick"))
503 fnetsetnick(fn, arg->val);
506 if(netresolve(addr, (void (*)(struct sockaddr *, int, void *))resolvecb, fn) < 0)
511 void regfnet(struct fnet *fnet)
513 fnet->next = networks;
518 * Note on the chat string: Must be in UNIX text file format - that
519 * is, LF line endings. The filenet-specific code must see to it that
520 * any other kind of format is converted into that. In the future,
521 * certain control characters and escape sequences will be parsed by
522 * the client. Make sure that any filenet-specific code strips any
523 * such that aren't supposed to be in the protocol.
525 * Note on "name": This is supposed to be an identifier for the
526 * source. If the chat is a public message, set "public" to non-zero
527 * and "name" to whatever "chat room" name is appropriate for the
528 * fnetnode, but not NULL. If there is a "default" channel in this
529 * filenet, set "name" to the empty string. If the chat is a private
530 * message, name is ignored.
532 void fnethandlechat(struct fnetnode *fn, int public, wchar_t *name, wchar_t *peer, wchar_t *chat)
534 CBCHAINDOCB(fn, fnetnode_chat, fn, public, name, peer, chat);
537 static struct configvar myvars[] =
539 /** The number of seconds to wait between searches. Most hubs
540 * require at least ten seconds, and quite often network lag will
541 * often make searches arrive to the hub more often than sent. It
542 * may be semi-dangerous to specify too low a value, since hubs
543 * will often kick users that search too often (even when the
544 * reason is network lag -- there is no way for the hub to know
545 * this), but it is quite annoying to set too high a value. 15 to
546 * 40 seconds are the recommended range (although the default is
547 * 15 seconds, it is recommended to set to 30 seconds). */
548 {CONF_VAR_INT, "srchwait", {.num = 15}},
549 /** The TOS value to use for hub connections (see the TOS VALUES
551 {CONF_VAR_INT, "fntos", {.num = 0}},
552 /** The TOS value to use for peer connections (see the TOS VALUES
554 {CONF_VAR_INT, "fnptos", {.num = 0}},
555 /** Specifies a maximum number of simultaneously connected
556 * hubs. Attempts to connect to new hubs beyond this limit will
557 * return an error. Set to zero to remove the limit. */
558 {CONF_VAR_INT, "maxnodes", {.num = 0}},
562 static struct module me =