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 struct fnet *networks = NULL;
34 struct fnetnode *fnetnodes = NULL;
36 GCBCHAIN(newfncb, struct fnetnode *);
38 static struct fnetnode *newfn(struct fnet *fnet)
43 new = smalloc(sizeof(*new));
44 memset(new, 0, sizeof(*new));
48 new->mynick = swcsdup(confgetstr("cli", "defnick"));
49 new->srchwait = confgetint("fnet", "srchwait");
51 CBCHAININIT(new, fnetnode_ac);
52 CBCHAININIT(new, fnetnode_chat);
53 CBCHAININIT(new, fnetnode_unlink);
54 CBCHAININIT(new, fnetnode_destroy);
55 CBCHAININIT(new, fnetpeer_new);
56 CBCHAININIT(new, fnetpeer_del);
57 CBCHAININIT(new, fnetpeer_chdi);
64 void killfnetnode(struct fnetnode *fn)
66 fnetsetstate(fn, FNN_DEAD);
70 if(fn->sk->data == fn)
80 void getfnetnode(struct fnetnode *fn)
84 fprintf(stderr, "getfnetnode on id %i at %p, refcount=%i\n", fn->id, fn, fn->refcount);
88 void putfnetnode(struct fnetnode *fn)
93 fprintf(stderr, "putfnetnode on id %i at %p, refcount=%i\n", fn->id, fn, fn->refcount - 1);
97 for(cur = fnetnodes; cur != NULL; cur = cur->next)
100 flog(LOG_CRIT, "BUG: fnetnode reached refcount 0 while still in list - id %i", fn->id);
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);
107 CBCHAINFREE(fn, fnetpeer_new);
108 CBCHAINFREE(fn, fnetpeer_del);
109 CBCHAINFREE(fn, fnetpeer_chdi);
110 if(fn->fnet->destroy != NULL)
111 fn->fnet->destroy(fn);
112 while(fn->args != NULL)
113 freewcspair(fn->args, &fn->args);
114 while(fn->peers != NULL)
115 fnetdelpeer(fn->peers);
116 if(fn->mynick != NULL)
118 if(fn->pubid != NULL)
128 struct fnetnode *findfnetnode(int id)
132 for(fn = fnetnodes; (fn != NULL) && (fn->id != id); fn = fn->next);
136 void linkfnetnode(struct fnetnode *fn)
141 fn->next = fnetnodes;
142 if(fnetnodes != NULL)
143 fnetnodes->prev = fn;
146 GCBCHAINDOCB(newfncb, fn);
149 void unlinkfnetnode(struct fnetnode *fn)
154 fnetnodes = fn->next;
156 fn->next->prev = fn->prev;
158 fn->prev->next = fn->next;
160 CBCHAINDOCB(fn, fnetnode_unlink, fn);
164 static void conncb(struct socket *sk, int err, struct fnetnode *data)
173 fnetsetstate(data, FNN_HS);
174 socksettos(sk, confgetint("fnet", "fntos"));
175 data->fnet->connect(data);
179 static void resolvecb(struct sockaddr *addr, int addrlen, struct fnetnode *data)
186 netcsconn(addr, addrlen, (void (*)(struct socket *, int, void *))conncb, data);
190 static struct fnetpeerdatum *finddatum(struct fnetnode *fn, wchar_t *id)
192 struct fnetpeerdatum *datum;
194 for(datum = fn->peerdata; datum != NULL; datum = datum->next)
196 if(!wcscmp(datum->id, id))
202 static struct fnetpeerdatum *adddatum(struct fnetnode *fn, wchar_t *id, int datatype)
204 struct fnetpeerdatum *new;
206 new = smalloc(sizeof(*new));
208 new->id = swcsdup(id);
209 new->datatype = datatype;
211 new->next = fn->peerdata;
212 if(fn->peerdata != NULL)
213 fn->peerdata->prev = new;
218 static struct fnetpeerdi *difindoradd(struct fnetpeer *peer, struct fnetpeerdatum *datum, int *isnew)
222 for(i = 0; i < peer->dinum; i++)
224 if(peer->peerdi[i].datum == datum)
229 peer->peerdi = srealloc(peer->peerdi, sizeof(struct fnetpeerdi) * (peer->dinum + 1));
230 memset(&peer->peerdi[peer->dinum], 0, sizeof(struct fnetpeerdi));
231 peer->peerdi[peer->dinum].datum = datum;
235 return(&peer->peerdi[peer->dinum++]);
239 return(&peer->peerdi[i]);
243 void fnetpeersetstr(struct fnetpeer *peer, wchar_t *id, wchar_t *value)
245 struct fnetpeerdatum *datum;
246 struct fnetpeerdi *di;
249 if((datum = finddatum(peer->fn, id)) == NULL)
250 datum = adddatum(peer->fn, id, FNPD_STR);
251 di = difindoradd(peer, datum, &changed);
252 if(di->data.str != NULL) {
253 changed = (changed || wcscmp(value, di->data.str));
258 di->data.str = swcsdup(value);
260 CBCHAINDOCB(peer->fn, fnetpeer_chdi, peer->fn, peer, di);
263 void fnetpeersetnum(struct fnetpeer *peer, wchar_t *id, int value)
265 struct fnetpeerdatum *datum;
266 struct fnetpeerdi *di;
269 if((datum = finddatum(peer->fn, id)) == NULL)
270 datum = adddatum(peer->fn, id, FNPD_INT);
271 di = difindoradd(peer, datum, &changed);
272 changed = (changed || (di->data.num != value));
273 di->data.num = value;
275 CBCHAINDOCB(peer->fn, fnetpeer_chdi, peer->fn, peer, di);
278 void fnetpeersetlnum(struct fnetpeer *peer, wchar_t *id, long long value)
280 struct fnetpeerdatum *datum;
281 struct fnetpeerdi *di;
284 if((datum = finddatum(peer->fn, id)) == NULL)
285 datum = adddatum(peer->fn, id, FNPD_LL);
286 di = difindoradd(peer, datum, &changed);
287 changed = (changed || (di->data.lnum != value));
288 di->data.lnum = value;
290 CBCHAINDOCB(peer->fn, fnetpeer_chdi, peer->fn, peer, di);
293 static void putdatum(struct fnetpeer *peer, struct fnetpeerdatum *datum)
295 if(--datum->refcount > 0)
297 if(datum->next != NULL)
298 datum->next->prev = datum->prev;
299 if(datum->prev != NULL)
300 datum->prev->next = datum->next;
301 if(datum == peer->fn->peerdata)
302 peer->fn->peerdata = datum->next;
307 void fnetpeerunset(struct fnetpeer *peer, wchar_t *id)
310 struct fnetpeerdatum *datum;
312 if((datum = finddatum(peer->fn, id)) == NULL)
314 for(i = 0; i < peer->dinum; i++)
316 if(peer->peerdi[i].datum == datum)
321 if((datum->datatype == FNPD_STR) && (peer->peerdi[i].data.str != NULL))
322 free(peer->peerdi[i].data.str);
324 memmove(&peer->peerdi[i], &peer->peerdi[i + 1], sizeof(struct fnetpeerdi) * (peer->dinum - i));
325 putdatum(peer, datum);
328 struct fnetpeer *fnetaddpeer(struct fnetnode *fn, wchar_t *id, wchar_t *nick)
330 struct fnetpeer *new;
332 new = smalloc(sizeof(*new));
334 new->id = swcsdup(id);
335 new->nick = swcsdup(nick);
339 new->next = fn->peers;
341 if(fn->peers != NULL)
342 fn->peers->prev = new;
345 CBCHAINDOCB(fn, fnetnode_ac, fn, L"numpeers");
346 CBCHAINDOCB(fn, fnetpeer_new, fn, new);
350 void fnetdelpeer(struct fnetpeer *peer)
354 if(peer->next != NULL)
355 peer->next->prev = peer->prev;
356 if(peer->prev != NULL)
357 peer->prev->next = peer->next;
358 if(peer->fn->peers == peer)
359 peer->fn->peers = peer->next;
360 peer->fn->numpeers--;
361 CBCHAINDOCB(peer->fn, fnetnode_ac, peer->fn, L"numpeers");
362 CBCHAINDOCB(peer->fn, fnetpeer_del, peer->fn, peer);
365 for(i = 0; i < peer->dinum; i++)
367 if((peer->peerdi[i].datum->datatype == FNPD_STR) && (peer->peerdi[i].data.str != NULL))
368 free(peer->peerdi[i].data.str);
369 putdatum(peer, peer->peerdi[i].datum);
371 if(peer->peerdi != NULL)
376 struct fnetpeer *fnetfindpeer(struct fnetnode *fn, wchar_t *id)
378 struct fnetpeer *cur;
380 for(cur = fn->peers; (cur != NULL) && wcscmp(cur->id, id); cur = cur->next);
384 int fnetsetnick(struct fnetnode *fn, wchar_t *newnick)
388 if(fn->fnet->setnick != NULL)
389 ret = fn->fnet->setnick(fn, newnick);
394 if(fn->mynick != NULL)
396 fn->mynick = swcsdup(newnick);
401 int fnetsendchat(struct fnetnode *fn, int public, wchar_t *to, wchar_t *string)
403 if(fn->fnet->sendchat == NULL)
408 return(fn->fnet->sendchat(fn, public, to, string));
411 int fnetsearch(struct fnetnode *fn, struct search *srch, struct srchfnnlist *ln)
413 if(fn->fnet->search == NULL)
418 return(fn->fnet->search(fn, srch, ln));
421 void fnetsetname(struct fnetnode *fn, wchar_t *newname)
425 fn->name = swcsdup(newname);
426 CBCHAINDOCB(fn, fnetnode_ac, fn, L"name");
429 void fnetsetstate(struct fnetnode *fn, int newstate)
431 fn->state = newstate;
432 CBCHAINDOCB(fn, fnetnode_ac, fn, L"state");
435 struct fnet *findfnet(wchar_t *name)
439 for(fnet = networks; fnet != NULL; fnet = fnet->next)
441 if(!wcscmp(name, fnet->name))
447 struct fnetnode *fnetinitconnect(wchar_t *name, char *addr, struct wcspair *args)
453 if((fnet = findfnet(name)) == NULL)
455 errno = EPROTONOSUPPORT;
459 fn->pubid = icmbstowcs(addr, NULL);
460 if(fn->pubid == NULL)
461 fn->pubid = swcsdup(L"");
463 for(arg = fn->args; arg != NULL; arg = arg->next)
465 if(!wcscmp(arg->key, L"nick"))
466 fnetsetnick(fn, arg->val);
469 if(netresolve(addr, (void (*)(struct sockaddr *, int, void *))resolvecb, fn) < 0)
474 void regfnet(struct fnet *fnet)
476 fnet->next = networks;
481 * Note on the chat string: Must be in UNIX text file format - that
482 * is, LF line endings. The filenet-specific code must see to it that
483 * any other kind of format is converted into that. In the future,
484 * certain control characters and escape sequences will be parsed by
485 * the client. Make sure that any filenet-specific code strips any
486 * such that aren't supposed to be in the protocol.
488 * Note on "name": This is supposed to be an identifier for the
489 * source. If the chat is a public message, set "public" to non-zero
490 * and "name" to whatever "chat room" name is appropriate for the
491 * fnetnode, but not NULL. If there is a "default" channel in this
492 * filenet, set "name" to the empty string. If the chat is a private
493 * message, name is ignored.
495 void fnethandlechat(struct fnetnode *fn, int public, wchar_t *name, wchar_t *peer, wchar_t *chat)
497 CBCHAINDOCB(fn, fnetnode_chat, fn, public, name, peer, chat);
500 static struct configvar myvars[] =
502 {CONF_VAR_INT, "srchwait", {.num = 15}},
503 {CONF_VAR_INT, "fntos", {.num = 0}},
504 {CONF_VAR_INT, "fnptos", {.num = 0}},
505 {CONF_VAR_INT, "maxnodes", {.num = 0}},
509 static struct module me =