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