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