Transfer from CVS at SourceForge
[doldaconnect.git] / daemon / filenet.c
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
33 static struct fnet *networks = NULL;
34 struct fnetnode *fnetnodes = NULL;
35 int numfnetnodes = 0;
36 GCBCHAIN(newfncb, struct fnetnode *);
37
38 static 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
61 void 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
74 void 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
82 void 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
115 struct 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
123 void 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
136 void 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
151 static 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
166 static 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
177 static 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
189 static 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
205 static 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
226 void 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
239 void 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
250 void 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
261 static 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
275 void 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
296 struct 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
317 void 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
342 struct 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
350 int 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
367 int 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
377 int 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
387 void 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
395 void fnetsetstate(struct fnetnode *fn, int newstate)
396 {
397     fn->state = newstate;
398     CBCHAINDOCB(fn, fnetnode_ac, fn, L"state");
399 }
400
401 struct 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
413 struct 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
430 void 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  */
451 void 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
456 static 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
464 static struct module me =
465 {
466     .conf =
467     {
468         .vars = myvars
469     },
470     .name = "fnet"
471 };
472
473 MODULE(me)