Handle fnet peer lists in the library.
[doldaconnect.git] / lib / uimisc.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
20 #include <unistd.h>
21 /* I'm very unsure about this, but for now it defines wcstoll (which
22  * should be defined anyway) and doesn't break anything... let's keep
23  * two eyes wide open, though. */
24 #define __USE_ISOC99
25 #include <wchar.h>
26 #include <wctype.h>
27 #include <pwd.h>
28 #include <string.h>
29 #include <malloc.h>
30 #include <stdio.h>
31
32 #ifdef HAVE_CONFIG_H
33 #include <config.h>
34 #endif
35 #include <doldaconnect/uilib.h>
36 #include <doldaconnect/uimisc.h>
37 #include <doldaconnect/utils.h>
38
39 #ifdef HAVE_KRB5
40 #include <krb5.h>
41 #include <com_err.h>
42 #endif
43
44 struct logindata;
45
46 struct authmech
47 {
48     wchar_t *name;
49     void (*process)(struct dc_response *resp, struct logindata *data);
50     int (*init)(struct logindata *data);
51     void (*release)(struct logindata *data);
52 };
53
54 struct logindata
55 {
56     int (*conv)(int type, wchar_t *text, char **resp, void *data);
57     void (*callback)(int err, wchar_t *reason, void *data);
58     char *username;
59     int freeusername;
60     int useauthless;
61     void *data;
62     void *mechdata;
63     struct authmech *mech;
64 };
65
66 struct gencbdata
67 {
68     void (*callback)(int resp, void *data);
69     void *data;
70 };
71
72 struct fnetcbdata
73 {
74     void (*callback)(struct dc_fnetnode *fn, int resp, void *data);
75     int fnid;
76     void *data;
77 };
78
79 struct dc_fnetnode *dc_fnetnodes = NULL;
80 struct dc_transfer *dc_transfers = NULL;
81
82 static void freelogindata(struct logindata *data)
83 {
84     if((data->mech != NULL) && (data->mech->release != NULL))
85         data->mech->release(data);
86     if(data->freeusername)
87         free(data->username);
88     free(data);
89 }
90
91 static int logincallback(struct dc_response *resp);
92
93 static void process_authless(struct dc_response *resp, struct logindata *data)
94 {
95     switch(resp->code)
96     {
97     case 200:
98         data->callback(DC_LOGIN_ERR_SUCCESS, NULL, data->data);
99         freelogindata(data);
100         break;
101 /*
102     case 303:
103     case 304:
104         if((ires = dc_interpret(resp)) != NULL)
105         {
106             buf = NULL;
107             if(data->conv((resp->code == 303)?DC_LOGIN_CONV_INFO:DC_LOGIN_CONV_ERROR, ires->argv[0].val.str, &buf, data->data))
108             {
109                 data->callback(DC_LOGIN_ERR_CONV, NULL, data->data);
110                 freelogindata(data);
111             } else {
112                 dc_queuecmd(logincallback, data, L"pass", L"", NULL);
113             }
114             if(buf != NULL)
115             {
116                 memset(buf, 0, strlen(buf));
117                 free(buf);
118             }
119             dc_freeires(ires);
120         }
121         break;
122 */
123     case 505:
124         data->callback(DC_LOGIN_ERR_SERVER, NULL, data->data);
125         freelogindata(data);
126         break;
127     case 506:
128         data->callback(DC_LOGIN_ERR_AUTHFAIL, NULL, data->data);
129         freelogindata(data);
130         break;
131     default:
132         data->callback(DC_LOGIN_ERR_USER, NULL, data->data);
133         freelogindata(data);
134         break;
135     }
136 }
137
138 static void process_pam(struct dc_response *resp, struct logindata *data)
139 {
140     struct dc_intresp *ires;
141     int convtype;
142     char *buf;
143     
144     switch(resp->code)
145     {
146     case 200:
147         data->callback(DC_LOGIN_ERR_SUCCESS, NULL, data->data);
148         freelogindata(data);
149         break;
150     case 301:
151     case 302:
152     case 303:
153     case 304:
154         if(resp->code == 301)
155             convtype = DC_LOGIN_CONV_NOECHO;
156         else if(resp->code == 302)
157             convtype = DC_LOGIN_CONV_ECHO;
158         else if(resp->code == 303)
159             convtype = DC_LOGIN_CONV_INFO;
160         else if(resp->code == 304)
161             convtype = DC_LOGIN_CONV_ERROR;
162         if((ires = dc_interpret(resp)) != NULL)
163         {
164             buf = NULL;
165             if(data->conv(convtype, ires->argv[0].val.str, &buf, data->data))
166             {
167                 data->callback(DC_LOGIN_ERR_CONV, NULL, data->data);
168                 freelogindata(data);
169             } else {
170                 dc_queuecmd(logincallback, data, L"pass", L"%%s", buf, NULL);
171             }
172             if(buf != NULL)
173             {
174                 memset(buf, 0, strlen(buf));
175                 free(buf);
176             }
177             dc_freeires(ires);
178         }
179         break;
180     case 505:
181         data->callback(DC_LOGIN_ERR_SERVER, NULL, data->data);
182         freelogindata(data);
183         break;
184     case 506:
185         data->callback(DC_LOGIN_ERR_AUTHFAIL, NULL, data->data);
186         freelogindata(data);
187         break;
188     default:
189         data->callback(DC_LOGIN_ERR_USER, NULL, data->data);
190         freelogindata(data);
191         break;
192     }
193 }
194
195 #ifdef HAVE_KRB5
196 struct krb5data
197 {
198     int state;
199     krb5_context context;
200     krb5_principal sprinc, myprinc;
201     krb5_ccache ccache;
202     krb5_auth_context authcon;
203     krb5_data reqbuf;
204     krb5_creds *servcreds;
205     int valid, fwd, fwded;
206 };
207
208 static char *hexencode(char *data, size_t datalen)
209 {
210     char *buf, this;
211     size_t bufsize, bufdata;
212     int dig;
213     
214     buf = NULL;
215     bufsize = bufdata = 0;
216     for(; datalen > 0; datalen--, data++)
217     {
218         dig = (*data & 0xF0) >> 4;
219         if(dig > 9)
220             this = 'A' + dig - 10;
221         else
222             this = dig + '0';
223         addtobuf(buf, this);
224         dig = *data & 0x0F;
225         if(dig > 9)
226             this = 'A' + dig - 10;
227         else
228             this = dig + '0';
229         addtobuf(buf, this);
230     }
231     addtobuf(buf, 0);
232     return(buf);
233 }
234
235 static char *hexdecode(char *data, size_t *len)
236 {
237     char *buf, this;
238     size_t bufsize, bufdata;
239     
240     buf = NULL;
241     bufsize = bufdata = 0;
242     for(; *data; data++)
243     {
244         if((*data >= 'A') && (*data <= 'F'))
245         {
246             this = (this & 0x0F) | ((*data - 'A' + 10) << 4);
247         } else if((*data >= '0') && (*data <= '9')) {
248             this = (this & 0x0F) | ((*data - '0') << 4);
249         } else {
250             if(buf != NULL)
251                 free(buf);
252             return(NULL);
253         }
254         data++;
255         if(!*data)
256         {
257             if(buf != NULL)
258                 free(buf);
259             return(NULL);
260         }
261         if((*data >= 'A') && (*data <= 'F'))
262         {
263             this = (this & 0xF0) | (*data - 'A' + 10);
264         } else if((*data >= '0') && (*data <= '9')) {
265             this = (this & 0xF0) | (*data - '0');
266         } else {
267             if(buf != NULL)
268                 free(buf);
269             return(NULL);
270         }
271         addtobuf(buf, this);
272     }
273     addtobuf(buf, 0);
274     if(len != NULL)
275         *len = bufdata - 1;
276     return(buf);
277 }
278
279 static void process_krb5(struct dc_response *resp, struct logindata *data)
280 {
281     int ret;
282     struct dc_intresp *ires;
283     struct krb5data *krb;
284     krb5_data k5d;
285     krb5_ap_rep_enc_part *repl;
286     char *buf;
287     
288     krb = data->mechdata;
289     switch(resp->code)
290     {
291     case 200:
292         data->callback(DC_LOGIN_ERR_SUCCESS, NULL, data->data);
293         freelogindata(data);
294         break;
295     case 300:
296         switch(krb->state)
297         {
298         case 0:
299             buf = hexencode(krb->reqbuf.data, krb->reqbuf.length);
300             dc_queuecmd(logincallback, data, L"pass", L"%%s", buf, NULL);
301             free(buf);
302             krb->state = 1;
303             break;
304         case 1:
305             if((ires = dc_interpret(resp)) != NULL)
306             {
307                 k5d.data = hexdecode(icswcstombs(ires->argv[0].val.str, NULL, NULL), &k5d.length);
308                 if(!krb->valid)
309                 {
310                     if((ret = krb5_rd_rep(krb->context, krb->authcon, &k5d, &repl)) != 0)
311                     {
312                         data->callback(DC_LOGIN_ERR_SERVER, NULL, data->data);
313                         freelogindata(data);
314                         break;
315                     }
316                     /* XXX: Do I need to do something with this? */
317                     krb->valid = 1;
318                     krb5_free_ap_rep_enc_part(krb->context, repl);
319                 }
320                 if(krb->fwd && !krb->fwded)
321                 {
322                     if(krb->reqbuf.data != NULL)
323                         free(krb->reqbuf.data);
324                     krb->reqbuf.data = NULL;
325                     if((ret = krb5_fwd_tgt_creds(krb->context, krb->authcon, NULL, krb->servcreds->client, krb->servcreds->server, 0, 1, &krb->reqbuf)) != 0)
326                     {
327                         fprintf(stderr, "krb5_fwd_tgt_creds reported an error: %s\n", error_message(ret));
328                         dc_queuecmd(logincallback, data, L"pass", L"31", NULL);
329                         krb->fwd = 0;
330                         krb->state = 2;
331                     } else {
332                         dc_queuecmd(logincallback, data, L"pass", L"32", NULL);
333                         krb->state = 0;
334                         krb->fwded = 1;
335                     }
336                 } else {
337                     dc_queuecmd(logincallback, data, L"pass", L"31", NULL);
338                     krb->state = 2;
339                 }
340                 free(k5d.data);
341                 dc_freeires(ires);
342             }
343             break;
344         default:
345             data->callback(DC_LOGIN_ERR_USER, NULL, data->data);
346             freelogindata(data);
347             break;
348         }
349         break;
350     case 505:
351         data->callback(DC_LOGIN_ERR_SERVER, NULL, data->data);
352         freelogindata(data);
353         break;
354     case 506:
355         data->callback(DC_LOGIN_ERR_AUTHFAIL, NULL, data->data);
356         freelogindata(data);
357         break;
358     default:
359         data->callback(DC_LOGIN_ERR_USER, NULL, data->data);
360         freelogindata(data);
361         break;
362     }
363 }
364
365 static int init_krb5(struct logindata *data)
366 {
367     int ret;
368     struct krb5data *krb;
369     krb5_data cksum;
370     krb5_creds creds;
371     
372     krb = smalloc(sizeof(*krb));
373     memset(krb, 0, sizeof(*krb));
374     krb->fwd = 1;
375     krb->fwded = 0;
376     data->mechdata = krb;
377     if((ret = krb5_init_context(&krb->context)) != 0)
378     {
379         fprintf(stderr, "krb5_init_context reported an error: %s\n", error_message(ret));
380         return(1);
381     }
382     if((ret = krb5_auth_con_init(krb->context, &krb->authcon)) != 0)
383     {
384         fprintf(stderr, "krb5_auth_con_init reported an error: %s\n", error_message(ret));
385         return(1);
386     }
387     krb5_auth_con_setflags(krb->context, krb->authcon, KRB5_AUTH_CONTEXT_DO_SEQUENCE);
388     if((ret = krb5_sname_to_principal(krb->context, dc_gethostname(), "doldacond", KRB5_NT_SRV_HST, &krb->sprinc)) != 0)
389     {
390         fprintf(stderr, "krb5_sname_to_principal reported an error: %s\n", error_message(ret));
391         return(1);
392     }
393     if((ret = krb5_cc_default(krb->context, &krb->ccache)) != 0)
394     {
395         fprintf(stderr, "krb5_cc_default reported an error: %s\n", error_message(ret));
396         return(1);
397     }
398     if((ret = krb5_cc_get_principal(krb->context, krb->ccache, &krb->myprinc)) != 0)
399     {
400         fprintf(stderr, "krb5_cc_default reported an error: %s\n", error_message(ret));
401         return(1);
402     }
403     memset(&creds, 0, sizeof(creds));
404     creds.client = krb->myprinc;
405     creds.server = krb->sprinc;
406     if((ret = krb5_get_credentials(krb->context, 0, krb->ccache, &creds, &krb->servcreds)) != 0)
407     {
408         fprintf(stderr, "krb5_get_credentials reported an error: %s\n", error_message(ret));
409         return(1);
410     }
411     /* WTF is this checksum stuff?! The Krb docs don't say a word about it! */
412     cksum.data = sstrdup(dc_gethostname());
413     cksum.length = strlen(cksum.data);
414     if((ret = krb5_mk_req_extended(krb->context, &krb->authcon, AP_OPTS_MUTUAL_REQUIRED, &cksum, krb->servcreds, &krb->reqbuf)) != 0)
415     {
416         fprintf(stderr, "krb5_mk_req_extended reported an error: %s\n", error_message(ret));
417         return(1);
418     }
419     free(cksum.data);
420     krb->state = 0;
421     return(0);
422 }
423
424 static void release_krb5(struct logindata *data)
425 {
426     struct krb5data *krb;
427     
428     if((krb = data->mechdata) == NULL)
429         return;
430     if(krb->servcreds != NULL)
431         krb5_free_creds(krb->context, krb->servcreds);
432     if(krb->reqbuf.data != NULL)
433         free(krb->reqbuf.data);
434     if(krb->sprinc != NULL)
435         krb5_free_principal(krb->context, krb->sprinc);
436     if(krb->myprinc != NULL)
437         krb5_free_principal(krb->context, krb->myprinc);
438     if(krb->ccache != NULL)
439         krb5_cc_close(krb->context, krb->ccache);
440     if(krb->authcon != NULL)
441         krb5_auth_con_free(krb->context, krb->authcon);
442     if(krb->context != NULL)
443         krb5_free_context(krb->context);
444     free(krb);
445 }
446 #endif
447
448 /* Arranged in order of priority */
449 static struct authmech authmechs[] =
450 {
451 #ifdef HAVE_KRB5
452     {
453         .name = L"krb5",
454         .process = process_krb5,
455         .init = init_krb5,
456         .release = release_krb5
457     },
458 #endif
459     {
460         .name = L"authless",
461         .process = process_authless,
462         .init = NULL,
463         .release = NULL
464     },
465     {
466         .name = L"pam",
467         .process = process_pam,
468         .init = NULL,
469         .release = NULL
470     },
471     {
472         .name = NULL
473     }
474 };
475
476 static int builtinconv(int type, wchar_t *text, char **resp, void *data)
477 {
478     char *buf, *pass;
479     
480     if(isatty(0))
481     {
482         if((buf = icwcstombs(text, NULL)) == NULL)
483             return(1);
484         pass = getpass(buf);
485         free(buf);
486         *resp = sstrdup(pass);
487         memset(pass, 0, strlen(pass));
488         return(0);
489     }
490     return(1);
491 }
492
493 static int logincallback(struct dc_response *resp)
494 {
495     int i;
496     struct dc_intresp *ires;
497     struct logindata *data;
498     int mech;
499     char *username;
500     struct passwd *pwent;
501     void *odata, *ndata;
502     
503     data = resp->data;
504     if(!wcscmp(resp->cmdname, L"lsauth"))
505     {
506         if(resp->code == 201)
507         {
508             data->callback(DC_LOGIN_ERR_NOLOGIN, NULL, data->data);
509             freelogindata(data);
510         } else {
511             mech = -1;
512             while((ires = dc_interpret(resp)) != NULL)
513             {
514                 if(!data->useauthless && !wcscmp(ires->argv[0].val.str, L"authless"))
515                 {
516                     dc_freeires(ires);
517                     continue;
518                 }
519                 for(i = 0; authmechs[i].name != NULL; i++)
520                 {
521                     if(!wcscmp(authmechs[i].name, ires->argv[0].val.str) && ((i < mech) || (mech == -1)))
522                     {
523                         odata = data->mechdata;
524                         data->mechdata = NULL;
525                         if((authmechs[i].init != NULL) && authmechs[i].init(data))
526                         {
527                             if(authmechs[i].release != NULL)
528                                 authmechs[i].release(data);
529                             data->mechdata = odata;
530                             fprintf(stderr, "authentication mechanism %ls failed, trying further...\n", authmechs[i].name);
531                         } else {
532                             if((data->mech != NULL) && data->mech->release != NULL)
533                             {
534                                 ndata = data->mechdata;
535                                 data->mechdata = odata;
536                                 data->mech->release(data);
537                                 data->mechdata = ndata;
538                             }
539                             mech = i;
540                             data->mech = authmechs + i;
541                         }
542                         break;
543                     }
544                 }
545                 dc_freeires(ires);
546             }
547             if(mech == -1)
548             {
549                 data->callback(DC_LOGIN_ERR_NOLOGIN, NULL, data->data);
550                 freelogindata(data);
551             } else {
552                 if((username = data->username) == NULL)
553                 {
554                     if((pwent = getpwuid(getuid())) == NULL)
555                     {
556                         data->callback(DC_LOGIN_ERR_USER, NULL, data->data);
557                         freelogindata(data);
558                         return(1);
559                     }
560                     username = pwent->pw_name;
561                 }
562                 dc_queuecmd(logincallback, data, L"login", data->mech->name, L"%%s", username, NULL);
563             }
564         }
565     } else if(!wcscmp(resp->cmdname, L"login") || !wcscmp(resp->cmdname, L"pass")) {
566         data->mech->process(resp, data);
567     }
568     return(1);
569 }
570
571 void dc_loginasync(char *username, int useauthless, int (*conv)(int, wchar_t *, char **, void *), void (*callback)(int, wchar_t *, void *), void *udata)
572 {
573     struct logindata *data;
574     
575     data = smalloc(sizeof(*data));
576     if(conv == NULL)
577         conv = builtinconv;
578     data->conv = conv;
579     data->mech = NULL;
580     data->data = udata;
581     data->mechdata = NULL;
582     data->callback = callback;
583     data->useauthless = useauthless;
584     data->freeusername = 0;
585     if(username == NULL)
586     {
587         data->username = NULL;
588     } else {
589         data->username = sstrdup(username);
590         data->freeusername = 1;
591     }
592     dc_queuecmd(logincallback, data, L"lsauth", NULL);
593 }
594
595 static struct dc_fnetpeerdatum *finddatum(struct dc_fnetnode *fn, wchar_t *id)
596 {
597     struct dc_fnetpeerdatum *datum;
598     
599     for(datum = fn->peerdata; datum != NULL; datum = datum->next)
600     {
601         if(!wcscmp(datum->id, id))
602             break;
603     }
604     return(datum);
605 }
606
607 static struct dc_fnetpeerdatum *adddatum(struct dc_fnetnode *fn, wchar_t *id, int dt)
608 {
609     struct dc_fnetpeerdatum *datum;
610     
611     datum = smalloc(sizeof(*datum));
612     memset(datum, 0, sizeof(*datum));
613     datum->refcount = 0;
614     datum->dt = dt;
615     datum->id = swcsdup(id);
616     datum->prev = NULL;
617     datum->next = fn->peerdata;
618     if(fn->peerdata != NULL)
619         fn->peerdata->prev = datum;
620     fn->peerdata = datum;
621     return(datum);
622 }
623
624 static struct dc_fnetpeerdi *difindoradd(struct dc_fnetpeer *peer, struct dc_fnetpeerdatum *datum)
625 {
626     int i;
627     
628     for(i = 0; i < peer->dinum; i++)
629     {
630         if(peer->di[i].datum == datum)
631             return(&peer->di[i]);
632     }
633     peer->di = srealloc(peer->di, sizeof(struct dc_fnetpeerdi) * ++(peer->dinum));
634     memset(&peer->di[i], 0, sizeof(struct dc_fnetpeerdi));
635     peer->di[i].datum = datum;
636     datum->refcount++;
637     return(&peer->di[i]);
638 }
639
640 static void putdatum(struct dc_fnetnode *fn, struct dc_fnetpeerdatum *datum)
641 {
642     if(--datum->refcount > 0)
643         return;
644     if(datum->next != NULL)
645         datum->next->prev = datum->prev;
646     if(datum->prev != NULL)
647         datum->prev->next = datum->next;
648     if(fn->peerdata == datum)
649         fn->peerdata = datum->next;
650     free(datum->id);
651     free(datum);
652 }
653
654 static void peersetnum(struct dc_fnetpeer *peer, wchar_t *id, int value)
655 {
656     struct dc_fnetpeerdatum *datum;
657     struct dc_fnetpeerdi *di;
658     
659     if((datum = finddatum(peer->fn, id)) == NULL)
660         datum = adddatum(peer->fn, id, DC_FNPD_INT);
661     di = difindoradd(peer, datum);
662     di->d.num = value;
663 }
664
665 static void peersetlnum(struct dc_fnetpeer *peer, wchar_t *id, long long value)
666 {
667     struct dc_fnetpeerdatum *datum;
668     struct dc_fnetpeerdi *di;
669     
670     if((datum = finddatum(peer->fn, id)) == NULL)
671         datum = adddatum(peer->fn, id, DC_FNPD_INT);
672     di = difindoradd(peer, datum);
673     di->d.lnum = value;
674 }
675
676 static void peersetstr(struct dc_fnetpeer *peer, wchar_t *id, wchar_t *value)
677 {
678     struct dc_fnetpeerdatum *datum;
679     struct dc_fnetpeerdi *di;
680     
681     if((datum = finddatum(peer->fn, id)) == NULL)
682         datum = adddatum(peer->fn, id, DC_FNPD_INT);
683     di = difindoradd(peer, datum);
684     if(di->d.str != NULL)
685         free(di->d.str);
686     di->d.str = swcsdup(value);
687 }
688
689 struct dc_fnetpeer *dc_fnetfindpeer(struct dc_fnetnode *fn, wchar_t *id)
690 {
691     struct dc_fnetpeer *peer;
692     
693     for(peer = fn->peers; peer != NULL; peer = peer->next)
694     {
695         if(!wcscmp(peer->id, id))
696             break;
697     }
698     return(peer);
699 }
700
701 static struct dc_fnetpeer *addpeer(struct dc_fnetnode *fn, wchar_t *id, wchar_t *nick)
702 {
703     struct dc_fnetpeer *peer;
704     
705     peer = smalloc(sizeof(*peer));
706     memset(peer, 0, sizeof(*peer));
707     peer->fn = fn;
708     peer->id = swcsdup(id);
709     peer->nick = swcsdup(nick);
710     peer->next = fn->peers;
711     peer->prev = NULL;
712     if(fn->peers != NULL)
713         fn->peers->prev = peer;
714     fn->peers = peer;
715     return(peer);
716 }
717
718 static void delpeer(struct dc_fnetpeer *peer)
719 {
720     int i;
721     
722     if(peer->next != NULL)
723         peer->next->prev = peer->prev;
724     if(peer->prev != NULL)
725         peer->prev->next = peer->next;
726     if(peer->fn->peers == peer)
727         peer->fn->peers = peer->next;
728     free(peer->id);
729     free(peer->nick);
730     for(i = 0; i < peer->dinum; i++)
731     {
732         if((peer->di[i].datum->dt == DC_FNPD_STR) && (peer->di[i].d.str != NULL))
733             free(peer->di[i].d.str);
734         putdatum(peer->fn, peer->di[i].datum);
735     }
736     free(peer);
737 }
738
739 static struct dc_fnetnode *newfn(void)
740 {
741     struct dc_fnetnode *fn;
742     
743     fn = smalloc(sizeof(*fn));
744     memset(fn, 0, sizeof(*fn));
745     fn->id = -1;
746     fn->name = NULL;
747     fn->fnet = NULL;
748     fn->state = fn->numusers = fn->found = 0;
749     fn->destroycb = NULL;
750     fn->udata = NULL;
751     fn->next = dc_fnetnodes;
752     fn->prev = NULL;
753     if(dc_fnetnodes != NULL)
754         dc_fnetnodes->prev = fn;
755     dc_fnetnodes = fn;
756     return(fn);
757 }
758
759 static void freefn(struct dc_fnetnode *fn)
760 {
761     if(fn->next != NULL)
762         fn->next->prev = fn->prev;
763     if(fn->prev != NULL)
764         fn->prev->next = fn->next;
765     if(fn == dc_fnetnodes)
766         dc_fnetnodes = fn->next;
767     if(fn->destroycb != NULL)
768         fn->destroycb(fn);
769     while(fn->peers != NULL)
770         delpeer(fn->peers);
771     while(fn->peerdata != NULL)
772     {
773         fn->peerdata->refcount = 0;
774         putdatum(fn, fn->peerdata);
775     }
776     if(fn->name != NULL)
777         free(fn->name);
778     if(fn->fnet != NULL)
779         free(fn->fnet);
780     free(fn);
781 }
782
783 struct dc_fnetnode *dc_findfnetnode(int id)
784 {
785     struct dc_fnetnode *fn;
786     
787     for(fn = dc_fnetnodes; fn != NULL; fn = fn->next)
788     {
789         if(fn->id == id)
790             break;
791     }
792     return(fn);
793 }
794
795 static struct dc_transfer *newtransfer(void)
796 {
797     struct dc_transfer *transfer;
798     
799     transfer = smalloc(sizeof(*transfer));
800     memset(transfer, 0, sizeof(*transfer));
801     transfer->id = -1;
802     transfer->peerid = transfer->peernick = transfer->path = NULL;
803     transfer->state = DC_TRNS_WAITING;
804     transfer->dir = DC_TRNSD_UNKNOWN;
805     transfer->size = -1;
806     transfer->curpos = -1;
807     transfer->destroycb = NULL;
808     transfer->udata = NULL;
809     transfer->next = dc_transfers;
810     transfer->prev = NULL;
811     if(dc_transfers != NULL)
812         dc_transfers->prev = transfer;
813     dc_transfers = transfer;
814     return(transfer);
815 }
816
817 static void freetransfer(struct dc_transfer *transfer)
818 {
819     if(transfer->next != NULL)
820         transfer->next->prev = transfer->prev;
821     if(transfer->prev != NULL)
822         transfer->prev->next = transfer->next;
823     if(transfer == dc_transfers)
824         dc_transfers = transfer->next;
825     if(transfer->destroycb != NULL)
826         transfer->destroycb(transfer);
827     if(transfer->peerid != NULL)
828         free(transfer->peerid);
829     if(transfer->peernick != NULL)
830         free(transfer->peernick);
831     if(transfer->path != NULL)
832         free(transfer->path);
833     free(transfer);
834 }
835
836 struct dc_transfer *dc_findtransfer(int id)
837 {
838     struct dc_transfer *transfer;
839     
840     for(transfer = dc_transfers; transfer != NULL; transfer = transfer->next)
841     {
842         if(transfer->id == id)
843             break;
844     }
845     return(transfer);
846 }
847
848 static int getfnlistcallback(struct dc_response *resp)
849 {
850     struct dc_intresp *ires;
851     struct gencbdata *data;
852     struct dc_fnetnode *fn, *next;
853     
854     data = resp->data;
855     if(resp->code == 200)
856     {
857         for(fn = dc_fnetnodes; fn != NULL; fn = fn->next)
858             fn->found = 0;
859         while((ires = dc_interpret(resp)) != NULL)
860         {
861             if((fn = dc_findfnetnode(ires->argv[0].val.num)) != NULL)
862             {
863                 fn->found = 1;
864                 if(fn->fnet != NULL)
865                     free(fn->fnet);
866                 fn->fnet = swcsdup(ires->argv[1].val.str);
867                 if(fn->name != NULL)
868                     free(fn->name);
869                 fn->name = swcsdup(ires->argv[2].val.str);
870                 fn->numusers = ires->argv[3].val.num;
871                 fn->state = ires->argv[4].val.num;
872             } else {
873                 fn = newfn();
874                 fn->id = ires->argv[0].val.num;
875                 fn->fnet = swcsdup(ires->argv[1].val.str);
876                 fn->name = swcsdup(ires->argv[2].val.str);
877                 fn->numusers = ires->argv[3].val.num;
878                 fn->state = ires->argv[4].val.num;
879                 fn->found = 1;
880             }
881             dc_freeires(ires);
882         }
883         for(fn = dc_fnetnodes; fn != NULL; fn = next)
884         {
885             next = fn->next;
886             if(!fn->found)
887                 freefn(fn);
888         }
889         data->callback(200, data->data);
890         free(resp->data);
891     } else if(resp->code == 201) {
892         while(dc_fnetnodes != NULL)
893             freefn(dc_fnetnodes);
894         data->callback(201, data->data);
895         free(resp->data);
896     } else if(resp->code == 502) {
897         while(dc_fnetnodes != NULL)
898             freefn(dc_fnetnodes);
899         data->callback(502, data->data);
900         free(resp->data);
901     }
902     return(1);
903 }
904
905 static int gettrlistcallback(struct dc_response *resp)
906 {
907     struct dc_intresp *ires;
908     struct gencbdata *data;
909     struct dc_transfer *transfer, *next;
910     
911     data = resp->data;
912     if(resp->code == 200)
913     {
914         for(transfer = dc_transfers; transfer != NULL; transfer = transfer->next)
915             transfer->found = 0;
916         while((ires = dc_interpret(resp)) != NULL)
917         {
918             if((transfer = dc_findtransfer(ires->argv[0].val.num)) != NULL)
919             {
920                 transfer->found = 1;
921                 if((transfer->path == NULL) || wcscmp(transfer->path, ires->argv[5].val.str))
922                 {
923                     if(transfer->path != NULL)
924                         free(transfer->path);
925                     transfer->path = swcsdup(ires->argv[5].val.str);
926                 }
927                 if((transfer->peerid == NULL) || wcscmp(transfer->peerid, ires->argv[3].val.str))
928                 {
929                     if(transfer->peerid != NULL)
930                         free(transfer->peerid);
931                     transfer->peerid = swcsdup(ires->argv[3].val.str);
932                 }
933                 if((transfer->peernick == NULL) || wcscmp(transfer->peernick, ires->argv[4].val.str))
934                 {
935                     if(transfer->peernick != NULL)
936                         free(transfer->peernick);
937                     transfer->peernick = swcsdup(ires->argv[4].val.str);
938                 }
939                 transfer->dir = ires->argv[1].val.num;
940                 transfer->state = ires->argv[2].val.num;
941                 transfer->size = ires->argv[6].val.num;
942                 transfer->curpos = ires->argv[7].val.num;
943                 if(transfer->hash != NULL)
944                 {
945                     free(transfer->hash);
946                     transfer->hash = NULL;
947                 }
948                 if(wcslen(ires->argv[8].val.str) > 0)
949                     transfer->hash = swcsdup(ires->argv[8].val.str);
950             } else {
951                 transfer = newtransfer();
952                 transfer->id = ires->argv[0].val.num;
953                 transfer->dir = ires->argv[1].val.num;
954                 transfer->state = ires->argv[2].val.num;
955                 transfer->peerid = swcsdup(ires->argv[3].val.str);
956                 transfer->peernick = swcsdup(ires->argv[4].val.str);
957                 transfer->path = swcsdup(ires->argv[5].val.str);
958                 transfer->size = ires->argv[6].val.num;
959                 transfer->curpos = ires->argv[7].val.num;
960                 if(wcslen(ires->argv[8].val.str) > 0)
961                     transfer->hash = swcsdup(ires->argv[8].val.str);
962                 transfer->found = 1;
963             }
964             dc_freeires(ires);
965         }
966         for(transfer = dc_transfers; transfer != NULL; transfer = next)
967         {
968             next = transfer->next;
969             if(!transfer->found)
970                 freetransfer(transfer);
971         }
972         data->callback(200, data->data);
973         free(data);
974     } else if(resp->code == 201) {
975         while(dc_transfers != NULL)
976             freetransfer(dc_transfers);
977         data->callback(201, data->data);
978         free(data);
979     } else if(resp->code == 502) {
980         while(dc_transfers != NULL)
981             freetransfer(dc_transfers);
982         data->callback(502, data->data);
983         free(data);
984     }
985     return(1);
986 }
987
988 static int getpeerlistcallback(struct dc_response *resp)
989 {
990     int i, o;
991     struct dc_fnetnode *fn;
992     struct fnetcbdata *data;
993     struct dc_fnetpeer *peer, *next;
994     struct dc_fnetpeerdatum *datum;
995     
996     data = resp->data;
997     if((fn = dc_findfnetnode(data->fnid)) == NULL)
998     {
999         data->callback(NULL, -1, data->data);
1000         free(data);
1001         return(1);
1002     }
1003     if(resp->code == 200)
1004     {
1005         for(peer = fn->peers; peer != NULL; peer = peer->next)
1006             peer->found = 0;
1007         for(i = 0; i < resp->numlines; i++)
1008         {
1009             if((peer = dc_fnetfindpeer(fn, resp->rlines[i].argv[1])) == NULL)
1010                 peer = addpeer(fn, resp->rlines[i].argv[1], resp->rlines[i].argv[2]);
1011             peer->found = 1;
1012             for(o = 3; o < resp->rlines[i].argc; o += 2)
1013             {
1014                 if((datum = finddatum(fn, resp->rlines[i].argv[o])) != NULL)
1015                 {
1016                     switch(datum->dt)
1017                     {
1018                     case DC_FNPD_INT:
1019                         peersetnum(peer, datum->id, wcstol(resp->rlines[i].argv[o + 1], NULL, 10));
1020                         break;
1021                     case DC_FNPD_LL:
1022                         peersetlnum(peer, datum->id, wcstoll(resp->rlines[i].argv[o + 1], NULL, 10));
1023                         break;
1024                     case DC_FNPD_STR:
1025                         peersetstr(peer, datum->id, resp->rlines[i].argv[o + 1]);
1026                         break;
1027                     }
1028                 }
1029             }
1030         }
1031         for(peer = fn->peers; peer != NULL; peer = next)
1032         {
1033             next = peer->next;
1034             if(!peer->found)
1035                 delpeer(peer);
1036         }
1037         fn->trackpeers = 1;
1038     } else if(resp->code == 201) {
1039         while(fn->peers != NULL)
1040             delpeer(fn->peers);
1041         fn->trackpeers = 1;
1042     }
1043     data->callback(fn, resp->code, data->data);
1044     free(data);
1045     return(1);
1046 }
1047
1048 static int getpalistcallback(struct dc_response *resp)
1049 {
1050     struct dc_fnetnode *fn;
1051     struct dc_intresp *ires;
1052     struct fnetcbdata *data;
1053     
1054     data = resp->data;
1055     if((fn = dc_findfnetnode(data->fnid)) == NULL)
1056     {
1057         data->callback(NULL, -1, data->data);
1058         free(data);
1059         return(1);
1060     }
1061     if(resp->code == 200)
1062     {
1063         while((ires = dc_interpret(resp)) != NULL)
1064         {
1065             adddatum(fn, ires->argv[0].val.str, ires->argv[1].val.num);
1066             dc_freeires(ires);
1067         }
1068         dc_queuecmd(getpeerlistcallback, data, L"lspeers", L"%%i", fn->id, NULL);
1069     } else if(resp->code == 201) {
1070         dc_queuecmd(getpeerlistcallback, data, L"lspeers", L"%%i", fn->id, NULL);
1071     } else {
1072         data->callback(fn, resp->code, data->data);
1073         free(data);
1074     }
1075     return(1);
1076 }
1077
1078 void dc_getfnlistasync(void (*callback)(int, void *), void *udata)
1079 {
1080     struct gencbdata *data;
1081     
1082     data = smalloc(sizeof(*data));
1083     data->callback = callback;
1084     data->data = udata;
1085     dc_queuecmd(getfnlistcallback, data, L"lsnodes", NULL);
1086 }
1087
1088 void dc_gettrlistasync(void (*callback)(int, void *), void *udata)
1089 {
1090     struct gencbdata *data;
1091     
1092     data = smalloc(sizeof(*data));
1093     data->callback = callback;
1094     data->data = udata;
1095     dc_queuecmd(gettrlistcallback, data, L"lstrans", NULL);
1096 }
1097
1098 void dc_getpeerlistasync(struct dc_fnetnode *fn, void (*callback)(struct dc_fnetnode *, int, void *), void *udata)
1099 {
1100     struct fnetcbdata *data;
1101     
1102     data = smalloc(sizeof(*data));
1103     data->callback = callback;
1104     data->fnid = fn->id;
1105     data->data = udata;
1106     dc_queuecmd(getpalistcallback, data, L"lspa", L"%%i", fn->id, NULL);
1107 }
1108
1109 void dc_uimisc_disconnected(void)
1110 {
1111     while(dc_fnetnodes != NULL)
1112         freefn(dc_fnetnodes);
1113     while(dc_transfers != NULL)
1114         freetransfer(dc_transfers);
1115 }
1116
1117 void dc_uimisc_handlenotify(struct dc_response *resp)
1118 {
1119     int i;
1120     struct dc_fnetnode *fn;
1121     struct dc_transfer *transfer;
1122     struct dc_fnetpeer *peer;
1123     struct dc_intresp *ires;
1124     
1125     if((ires = dc_interpret(resp)) == NULL)
1126         return;
1127     switch(resp->code)
1128     {
1129     case 601:
1130         if((fn = dc_findfnetnode(ires->argv[0].val.num)) != NULL)
1131             fn->state = ires->argv[1].val.num;
1132         break;
1133     case 602:
1134         if((fn = dc_findfnetnode(ires->argv[0].val.num)) != NULL)
1135         {
1136             if(fn->name != NULL)
1137                 free(fn->name);
1138             fn->name = swcsdup(ires->argv[1].val.str);
1139         }
1140         break;
1141     case 603:
1142         if((fn = dc_findfnetnode(ires->argv[0].val.num)) != NULL)
1143             freefn(fn);
1144         break;
1145     case 604:
1146         fn = newfn();
1147         fn->id = ires->argv[0].val.num;
1148         if(fn->fnet != NULL)
1149             free(fn->fnet);
1150         fn->fnet = swcsdup(ires->argv[1].val.str);
1151         fn->state = DC_FNN_STATE_SYN;
1152         fn->numusers = 0;
1153         break;
1154     case 605:
1155         if((fn = dc_findfnetnode(ires->argv[0].val.num)) != NULL)
1156             fn->numusers = ires->argv[1].val.num;
1157         break;
1158     case 610:
1159         transfer = newtransfer();
1160         transfer->id = ires->argv[0].val.num;
1161         transfer->dir = ires->argv[1].val.num;
1162         if(transfer->dir == DC_TRNSD_UP)
1163             transfer->state = DC_TRNS_HS;
1164         transfer->peerid = swcsdup(ires->argv[2].val.str);
1165         if(ires->argv[3].val.str[0])
1166             transfer->path = swcsdup(ires->argv[3].val.str);
1167         break;
1168     case 611:
1169         if((transfer = dc_findtransfer(ires->argv[0].val.num)) != NULL)
1170             transfer->state = ires->argv[1].val.num;
1171         break;
1172     case 612:
1173         if((transfer = dc_findtransfer(ires->argv[0].val.num)) != NULL)
1174         {
1175             if(transfer->peernick != NULL)
1176                 free(transfer->peernick);
1177             transfer->peernick = swcsdup(ires->argv[1].val.str);
1178         }
1179         break;
1180     case 613:
1181         if((transfer = dc_findtransfer(ires->argv[0].val.num)) != NULL)
1182             transfer->size = ires->argv[1].val.num;
1183         break;
1184     case 614:
1185         if((transfer = dc_findtransfer(ires->argv[0].val.num)) != NULL)
1186         {
1187             transfer->error = ires->argv[1].val.num;
1188             time(&transfer->errortime);
1189         }
1190         break;
1191     case 615:
1192         if((transfer = dc_findtransfer(ires->argv[0].val.num)) != NULL)
1193             transfer->curpos = ires->argv[1].val.num;
1194         break;
1195     case 616:
1196         if((transfer = dc_findtransfer(ires->argv[0].val.num)) != NULL)
1197         {
1198             if(transfer->path != NULL)
1199                 free(transfer->path);
1200             transfer->path = swcsdup(ires->argv[1].val.str);
1201         }
1202         break;
1203     case 617:
1204         if((transfer = dc_findtransfer(ires->argv[0].val.num)) != NULL)
1205             freetransfer(transfer);
1206         break;
1207     case 618:
1208         if((transfer = dc_findtransfer(ires->argv[0].val.num)) != NULL)
1209         {
1210             if(transfer->hash != NULL)
1211             {
1212                 free(transfer->hash);
1213                 transfer->hash = NULL;
1214             }
1215             if(wcslen(ires->argv[1].val.str) > 0)
1216                 transfer->hash = swcsdup(ires->argv[1].val.str);
1217         }
1218         break;
1219     case 630:
1220         if(((fn = dc_findfnetnode(ires->argv[0].val.num)) != NULL) && fn->trackpeers)
1221         {
1222             if((peer = dc_fnetfindpeer(fn, ires->argv[1].val.str)) == NULL)
1223                 addpeer(fn, ires->argv[1].val.str, ires->argv[2].val.str);
1224         }
1225         break;
1226     case 631:
1227         if(((fn = dc_findfnetnode(ires->argv[0].val.num)) != NULL) && fn->trackpeers)
1228         {
1229             if((peer = dc_fnetfindpeer(fn, ires->argv[1].val.str)) != NULL)
1230                 delpeer(peer);
1231         }
1232         break;
1233     case 632:
1234         if(((fn = dc_findfnetnode(ires->argv[0].val.num)) != NULL) && fn->trackpeers)
1235         {
1236             if((peer = dc_fnetfindpeer(fn, ires->argv[1].val.str)) != NULL)
1237             {
1238                 if(wcscmp(ires->argv[2].val.str, peer->nick))
1239                 {
1240                     free(peer->nick);
1241                     peer->nick = swcsdup(ires->argv[2].val.str);
1242                 }
1243                 for(i = 3; i < resp->rlines[0].argc; i += 3)
1244                 {
1245                     switch(wcstol(resp->rlines[0].argv[i + 1], NULL, 10))
1246                     {
1247                     case DC_FNPD_INT:
1248                         peersetnum(peer, resp->rlines[0].argv[i], wcstol(resp->rlines[0].argv[i + 2], NULL, 10));
1249                         break;
1250                     case DC_FNPD_LL:
1251                         peersetlnum(peer, resp->rlines[0].argv[i], wcstoll(resp->rlines[0].argv[i + 2], NULL, 10));
1252                         break;
1253                     case DC_FNPD_STR:
1254                         peersetstr(peer, resp->rlines[0].argv[i], resp->rlines[0].argv[i + 2]);
1255                         break;
1256                     }
1257                 }
1258             }
1259         }
1260         break;
1261     default:
1262         break;
1263     }
1264     dc_freeires(ires);
1265     resp->curline = 0;
1266 }
1267
1268 /* Note the backspace handling - it's not as elegant as possible, but
1269  * it helps avoid the "box-of-toothpicks" syndrome when writing search
1270  * expressions manually. */
1271 wchar_t **dc_lexsexpr(wchar_t *sexpr)
1272 {
1273     wchar_t **ret;
1274     wchar_t *buf;
1275     size_t retsize, retdata, bufsize, bufdata;
1276     int state;
1277     
1278     ret = NULL;
1279     buf = NULL;
1280     retsize = retdata = bufsize = bufdata = 0;
1281     state = 0;
1282     while(*sexpr != L'\0')
1283     {
1284         switch(state)
1285         {
1286         case 0:
1287             if(!iswspace(*sexpr))
1288                 state = 1;
1289             else
1290                 sexpr++;
1291             break;
1292         case 1:
1293             if(iswspace(*sexpr))
1294             {
1295                 if(buf != NULL)
1296                 {
1297                     addtobuf(buf, L'\0');
1298                     addtobuf(ret, buf);
1299                     buf = NULL;
1300                     bufsize = bufdata = 0;
1301                 }
1302                 state = 0;
1303             } else if((*sexpr == L'(') ||
1304                       (*sexpr == L')') ||
1305                       (*sexpr == L'&') ||
1306                       (*sexpr == L'|') ||
1307                       (*sexpr == L'!')) {
1308                 if(buf != NULL)
1309                 {
1310                     addtobuf(buf, L'\0');
1311                     addtobuf(ret, buf);
1312                     buf = NULL;
1313                     bufsize = bufdata = 0;
1314                 }
1315                 addtobuf(buf, *sexpr);
1316                 addtobuf(buf, L'\0');
1317                 addtobuf(ret, buf);
1318                 buf = NULL;
1319                 bufsize = bufdata = 0;
1320                 sexpr++;
1321             } else if(*sexpr == L'\"') {
1322                 sexpr++;
1323                 state = 2;
1324             } else if(*sexpr == L'\\') {
1325                 sexpr++;
1326                 if(*sexpr == L'\0')
1327                 {
1328                     addtobuf(buf, *sexpr);
1329                 } else if((*sexpr == L'\\') || (*sexpr == L'\"')) {
1330                     addtobuf(buf, *sexpr);
1331                     sexpr++;
1332                 } else {
1333                     addtobuf(buf, L'\\');
1334                     addtobuf(buf, *sexpr);
1335                     sexpr++;
1336                 }
1337             } else {
1338                 addtobuf(buf, *(sexpr++));
1339             }
1340             break;
1341         case 2:
1342             if(*sexpr == L'\\')
1343             {
1344                 sexpr++;
1345                 if(*sexpr == L'\0')
1346                 {
1347                     addtobuf(buf, *sexpr);
1348                 } else if((*sexpr == L'\\') || (*sexpr == L'\"')) {
1349                     addtobuf(buf, *sexpr);
1350                     sexpr++;
1351                 } else {
1352                     addtobuf(buf, L'\\');
1353                     addtobuf(buf, *sexpr);
1354                     sexpr++;
1355                 }
1356             } else if(*sexpr == L'\"') {
1357                 state = 1;
1358                 sexpr++;
1359             } else {
1360                 addtobuf(buf, *(sexpr++));
1361             }
1362             break;
1363         }
1364     }
1365     if(buf != NULL)
1366     {
1367         addtobuf(buf, L'\0');
1368         addtobuf(ret, buf);
1369     }
1370     addtobuf(ret, NULL);
1371     return(ret);
1372 }
1373
1374 void dc_freewcsarr(wchar_t **arr)
1375 {
1376     wchar_t **buf;
1377     
1378     if(arr == NULL)
1379         return;
1380     for(buf = arr; *buf != NULL; buf++)
1381         free(*buf);
1382     free(arr);
1383 }