Add public IDs for fnetnodes.
[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     CBCHAININIT(new, fnetpeer_new);
56     CBCHAININIT(new, fnetpeer_del);
57     CBCHAININIT(new, fnetpeer_chdi);
58     new->next = NULL;
59     new->prev = NULL;
60     numfnetnodes++;
61     return(new);
62 }
63
64 void 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
77 void 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
85 void 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);
104     CBCHAINFREE(fn, fnetpeer_new);
105     CBCHAINFREE(fn, fnetpeer_del);
106     CBCHAINFREE(fn, fnetpeer_chdi);
107     if(fn->fnet->destroy != NULL)
108         fn->fnet->destroy(fn);
109     while(fn->args != NULL)
110         freewcspair(fn->args, &fn->args);
111     while(fn->peers != NULL)
112         fnetdelpeer(fn->peers);
113     if(fn->mynick != NULL)
114         free(fn->mynick);
115     if(fn->pubid != NULL)
116         free(fn->pubid);
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
125 struct 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
133 void 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
146 void 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
161 static 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
176 static 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
187 static 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
199 static 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
215 static struct fnetpeerdi *difindoradd(struct fnetpeer *peer, struct fnetpeerdatum *datum, int *isnew)
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++;
230         if(isnew != NULL)
231             *isnew = 1;
232         return(&peer->peerdi[peer->dinum++]);
233     } else {
234         if(isnew != NULL)
235             *isnew = 0;
236         return(&peer->peerdi[i]);
237     }
238 }
239
240 void fnetpeersetstr(struct fnetpeer *peer, wchar_t *id, wchar_t *value)
241 {
242     struct fnetpeerdatum *datum;
243     struct fnetpeerdi *di;
244     int changed;
245     
246     if((datum = finddatum(peer->fn, id)) == NULL)
247         datum = adddatum(peer->fn, id, FNPD_STR);
248     di = difindoradd(peer, datum, &changed);
249     if(di->data.str != NULL) {
250         changed = (changed || wcscmp(value, di->data.str));
251         free(di->data.str);
252     } else {
253         changed = 1;
254     }
255     di->data.str = swcsdup(value);
256     if(changed)
257         CBCHAINDOCB(peer->fn, fnetpeer_chdi, peer->fn, peer, di);
258 }
259
260 void fnetpeersetnum(struct fnetpeer *peer, wchar_t *id, int value)
261 {
262     struct fnetpeerdatum *datum;
263     struct fnetpeerdi *di;
264     int changed;
265     
266     if((datum = finddatum(peer->fn, id)) == NULL)
267         datum = adddatum(peer->fn, id, FNPD_INT);
268     di = difindoradd(peer, datum, &changed);
269     changed = (changed || (di->data.num != value));
270     di->data.num = value;
271     if(changed)
272         CBCHAINDOCB(peer->fn, fnetpeer_chdi, peer->fn, peer, di);
273 }
274
275 void fnetpeersetlnum(struct fnetpeer *peer, wchar_t *id, long long value)
276 {
277     struct fnetpeerdatum *datum;
278     struct fnetpeerdi *di;
279     int changed;
280     
281     if((datum = finddatum(peer->fn, id)) == NULL)
282         datum = adddatum(peer->fn, id, FNPD_LL);
283     di = difindoradd(peer, datum, &changed);
284     changed = (changed || (di->data.lnum != value));
285     di->data.lnum = value;
286     if(changed)
287         CBCHAINDOCB(peer->fn, fnetpeer_chdi, peer->fn, peer, di);
288 }
289
290 static 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
304 void 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
325 struct 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");
343     CBCHAINDOCB(fn, fnetpeer_new, fn, new);
344     return(new);
345 }
346
347 void 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");
359     CBCHAINDOCB(peer->fn, fnetpeer_del, peer->fn, peer);
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
373 struct 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
381 int 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
398 int 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
408 int 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
418 void 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
426 void fnetsetstate(struct fnetnode *fn, int newstate)
427 {
428     fn->state = newstate;
429     CBCHAINDOCB(fn, fnetnode_ac, fn, L"state");
430 }
431
432 struct 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
444 struct fnetnode *fnetinitconnect(wchar_t *name, char *addr, struct wcspair *args)
445 {
446     struct fnet *fnet;
447     struct fnetnode *fn;
448     struct wcspair *arg;
449     
450     if((fnet = findfnet(name)) == NULL)
451     {
452         errno = EPROTONOSUPPORT;
453         return(NULL);
454     }
455     fn = newfn(fnet);
456     fn->pubid = icmbstowcs(addr, NULL);
457     if(fn->pubid == NULL)
458         fn->pubid = swcsdup(L"");
459     fn->args = args;
460     for(arg = fn->args; arg != NULL; arg = arg->next)
461     {
462         if(!wcscmp(arg->key, L"nick"))
463             fnetsetnick(fn, arg->val);
464     }
465     getfnetnode(fn);
466     if(netresolve(addr, (void (*)(struct sockaddr *, int, void *))resolvecb, fn) < 0)
467         return(NULL);
468     return(fn);
469 }
470
471 void 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  */
492 void 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
497 static 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
505 static struct module me =
506 {
507     .conf =
508     {
509         .vars = myvars
510     },
511     .name = "fnet"
512 };
513
514 MODULE(me)