Return hashes in SRs.
[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 <wchar.h>
22 #include <wctype.h>
23 #include <pwd.h>
24 #include <string.h>
25 #include <malloc.h>
26 #include <stdio.h>
27
28 #ifdef HAVE_CONFIG_H
29 #include <config.h>
30 #endif
31 #include <doldaconnect/uilib.h>
32 #include <doldaconnect/uimisc.h>
33 #include <doldaconnect/utils.h>
34
35 #ifdef HAVE_KRB5
36 #include <krb5.h>
37 #include <com_err.h>
38 #endif
39
40 struct logindata;
41
42 struct authmech
43 {
44     wchar_t *name;
45     void (*process)(struct dc_response *resp, struct logindata *data);
46     int (*init)(struct logindata *data);
47     void (*release)(struct logindata *data);
48 };
49
50 struct logindata
51 {
52     int (*conv)(int type, wchar_t *text, char **resp, void *data);
53     void (*callback)(int err, wchar_t *reason, void *data);
54     char *username;
55     int freeusername;
56     int useauthless;
57     void *data;
58     void *mechdata;
59     struct authmech *mech;
60 };
61
62 struct gencbdata
63 {
64     void (*callback)(int resp, void *data);
65     void *data;
66 };
67
68 struct dc_fnetnode *dc_fnetnodes = NULL;
69 struct dc_transfer *dc_transfers = NULL;
70
71 static void freelogindata(struct logindata *data)
72 {
73     if((data->mech != NULL) && (data->mech->release != NULL))
74         data->mech->release(data);
75     if(data->freeusername)
76         free(data->username);
77     free(data);
78 }
79
80 static int logincallback(struct dc_response *resp);
81
82 static void process_authless(struct dc_response *resp, struct logindata *data)
83 {
84     switch(resp->code)
85     {
86     case 200:
87         data->callback(DC_LOGIN_ERR_SUCCESS, NULL, data->data);
88         freelogindata(data);
89         break;
90 /*
91     case 303:
92     case 304:
93         if((ires = dc_interpret(resp)) != NULL)
94         {
95             buf = NULL;
96             if(data->conv((resp->code == 303)?DC_LOGIN_CONV_INFO:DC_LOGIN_CONV_ERROR, ires->argv[0].val.str, &buf, data->data))
97             {
98                 data->callback(DC_LOGIN_ERR_CONV, NULL, data->data);
99                 freelogindata(data);
100             } else {
101                 dc_queuecmd(logincallback, data, L"pass", L"", NULL);
102             }
103             if(buf != NULL)
104             {
105                 memset(buf, 0, strlen(buf));
106                 free(buf);
107             }
108             dc_freeires(ires);
109         }
110         break;
111 */
112     case 505:
113         data->callback(DC_LOGIN_ERR_SERVER, NULL, data->data);
114         freelogindata(data);
115         break;
116     case 506:
117         data->callback(DC_LOGIN_ERR_AUTHFAIL, NULL, data->data);
118         freelogindata(data);
119         break;
120     default:
121         data->callback(DC_LOGIN_ERR_USER, NULL, data->data);
122         freelogindata(data);
123         break;
124     }
125 }
126
127 static void process_pam(struct dc_response *resp, struct logindata *data)
128 {
129     struct dc_intresp *ires;
130     int convtype;
131     char *buf;
132     
133     switch(resp->code)
134     {
135     case 200:
136         data->callback(DC_LOGIN_ERR_SUCCESS, NULL, data->data);
137         freelogindata(data);
138         break;
139     case 301:
140     case 302:
141     case 303:
142     case 304:
143         if(resp->code == 301)
144             convtype = DC_LOGIN_CONV_NOECHO;
145         else if(resp->code == 302)
146             convtype = DC_LOGIN_CONV_ECHO;
147         else if(resp->code == 303)
148             convtype = DC_LOGIN_CONV_INFO;
149         else if(resp->code == 304)
150             convtype = DC_LOGIN_CONV_ERROR;
151         if((ires = dc_interpret(resp)) != NULL)
152         {
153             buf = NULL;
154             if(data->conv(convtype, ires->argv[0].val.str, &buf, data->data))
155             {
156                 data->callback(DC_LOGIN_ERR_CONV, NULL, data->data);
157                 freelogindata(data);
158             } else {
159                 dc_queuecmd(logincallback, data, L"pass", L"%%s", buf, NULL);
160             }
161             if(buf != NULL)
162             {
163                 memset(buf, 0, strlen(buf));
164                 free(buf);
165             }
166             dc_freeires(ires);
167         }
168         break;
169     case 505:
170         data->callback(DC_LOGIN_ERR_SERVER, NULL, data->data);
171         freelogindata(data);
172         break;
173     case 506:
174         data->callback(DC_LOGIN_ERR_AUTHFAIL, NULL, data->data);
175         freelogindata(data);
176         break;
177     default:
178         data->callback(DC_LOGIN_ERR_USER, NULL, data->data);
179         freelogindata(data);
180         break;
181     }
182 }
183
184 #ifdef HAVE_KRB5
185 struct krb5data
186 {
187     int state;
188     krb5_context context;
189     krb5_principal sprinc, myprinc;
190     krb5_ccache ccache;
191     krb5_auth_context authcon;
192     krb5_data reqbuf;
193     krb5_creds *servcreds;
194     int valid, fwd, fwded;
195 };
196
197 static char *hexencode(char *data, size_t datalen)
198 {
199     char *buf, this;
200     size_t bufsize, bufdata;
201     int dig;
202     
203     buf = NULL;
204     bufsize = bufdata = 0;
205     for(; datalen > 0; datalen--, data++)
206     {
207         dig = (*data & 0xF0) >> 4;
208         if(dig > 9)
209             this = 'A' + dig - 10;
210         else
211             this = dig + '0';
212         addtobuf(buf, this);
213         dig = *data & 0x0F;
214         if(dig > 9)
215             this = 'A' + dig - 10;
216         else
217             this = dig + '0';
218         addtobuf(buf, this);
219     }
220     addtobuf(buf, 0);
221     return(buf);
222 }
223
224 static char *hexdecode(char *data, size_t *len)
225 {
226     char *buf, this;
227     size_t bufsize, bufdata;
228     
229     buf = NULL;
230     bufsize = bufdata = 0;
231     for(; *data; data++)
232     {
233         if((*data >= 'A') && (*data <= 'F'))
234         {
235             this = (this & 0x0F) | ((*data - 'A' + 10) << 4);
236         } else if((*data >= '0') && (*data <= '9')) {
237             this = (this & 0x0F) | ((*data - '0') << 4);
238         } else {
239             if(buf != NULL)
240                 free(buf);
241             return(NULL);
242         }
243         data++;
244         if(!*data)
245         {
246             if(buf != NULL)
247                 free(buf);
248             return(NULL);
249         }
250         if((*data >= 'A') && (*data <= 'F'))
251         {
252             this = (this & 0xF0) | (*data - 'A' + 10);
253         } else if((*data >= '0') && (*data <= '9')) {
254             this = (this & 0xF0) | (*data - '0');
255         } else {
256             if(buf != NULL)
257                 free(buf);
258             return(NULL);
259         }
260         addtobuf(buf, this);
261     }
262     addtobuf(buf, 0);
263     if(len != NULL)
264         *len = bufdata - 1;
265     return(buf);
266 }
267
268 static void process_krb5(struct dc_response *resp, struct logindata *data)
269 {
270     int ret;
271     struct dc_intresp *ires;
272     struct krb5data *krb;
273     krb5_data k5d;
274     krb5_ap_rep_enc_part *repl;
275     char *buf;
276     
277     krb = data->mechdata;
278     switch(resp->code)
279     {
280     case 200:
281         data->callback(DC_LOGIN_ERR_SUCCESS, NULL, data->data);
282         freelogindata(data);
283         break;
284     case 300:
285         switch(krb->state)
286         {
287         case 0:
288             buf = hexencode(krb->reqbuf.data, krb->reqbuf.length);
289             dc_queuecmd(logincallback, data, L"pass", L"%%s", buf, NULL);
290             free(buf);
291             krb->state = 1;
292             break;
293         case 1:
294             if((ires = dc_interpret(resp)) != NULL)
295             {
296                 k5d.data = hexdecode(icswcstombs(ires->argv[0].val.str, NULL, NULL), &k5d.length);
297                 if(!krb->valid)
298                 {
299                     if((ret = krb5_rd_rep(krb->context, krb->authcon, &k5d, &repl)) != 0)
300                     {
301                         data->callback(DC_LOGIN_ERR_SERVER, NULL, data->data);
302                         freelogindata(data);
303                         break;
304                     }
305                     /* XXX: Do I need to do something with this? */
306                     krb->valid = 1;
307                     krb5_free_ap_rep_enc_part(krb->context, repl);
308                 }
309                 if(krb->fwd && !krb->fwded)
310                 {
311                     if(krb->reqbuf.data != NULL)
312                         free(krb->reqbuf.data);
313                     krb->reqbuf.data = NULL;
314                     if((ret = krb5_fwd_tgt_creds(krb->context, krb->authcon, NULL, krb->servcreds->client, krb->servcreds->server, 0, 1, &krb->reqbuf)) != 0)
315                     {
316                         fprintf(stderr, "krb5_fwd_tgt_creds reported an error: %s\n", error_message(ret));
317                         dc_queuecmd(logincallback, data, L"pass", L"31", NULL);
318                         krb->fwd = 0;
319                         krb->state = 2;
320                     } else {
321                         dc_queuecmd(logincallback, data, L"pass", L"32", NULL);
322                         krb->state = 0;
323                         krb->fwded = 1;
324                     }
325                 } else {
326                     dc_queuecmd(logincallback, data, L"pass", L"31", NULL);
327                     krb->state = 2;
328                 }
329                 free(k5d.data);
330                 dc_freeires(ires);
331             }
332             break;
333         default:
334             data->callback(DC_LOGIN_ERR_USER, NULL, data->data);
335             freelogindata(data);
336             break;
337         }
338         break;
339     case 505:
340         data->callback(DC_LOGIN_ERR_SERVER, NULL, data->data);
341         freelogindata(data);
342         break;
343     case 506:
344         data->callback(DC_LOGIN_ERR_AUTHFAIL, NULL, data->data);
345         freelogindata(data);
346         break;
347     default:
348         data->callback(DC_LOGIN_ERR_USER, NULL, data->data);
349         freelogindata(data);
350         break;
351     }
352 }
353
354 static int init_krb5(struct logindata *data)
355 {
356     int ret;
357     struct krb5data *krb;
358     krb5_data cksum;
359     krb5_creds creds;
360     
361     krb = smalloc(sizeof(*krb));
362     memset(krb, 0, sizeof(*krb));
363     krb->fwd = 1;
364     krb->fwded = 0;
365     data->mechdata = krb;
366     if((ret = krb5_init_context(&krb->context)) != 0)
367     {
368         fprintf(stderr, "krb5_init_context reported an error: %s\n", error_message(ret));
369         return(1);
370     }
371     if((ret = krb5_auth_con_init(krb->context, &krb->authcon)) != 0)
372     {
373         fprintf(stderr, "krb5_auth_con_init reported an error: %s\n", error_message(ret));
374         return(1);
375     }
376     krb5_auth_con_setflags(krb->context, krb->authcon, KRB5_AUTH_CONTEXT_DO_SEQUENCE);
377     if((ret = krb5_sname_to_principal(krb->context, dc_gethostname(), "doldacond", KRB5_NT_SRV_HST, &krb->sprinc)) != 0)
378     {
379         fprintf(stderr, "krb5_sname_to_principal reported an error: %s\n", error_message(ret));
380         return(1);
381     }
382     if((ret = krb5_cc_default(krb->context, &krb->ccache)) != 0)
383     {
384         fprintf(stderr, "krb5_cc_default reported an error: %s\n", error_message(ret));
385         return(1);
386     }
387     if((ret = krb5_cc_get_principal(krb->context, krb->ccache, &krb->myprinc)) != 0)
388     {
389         fprintf(stderr, "krb5_cc_default reported an error: %s\n", error_message(ret));
390         return(1);
391     }
392     memset(&creds, 0, sizeof(creds));
393     creds.client = krb->myprinc;
394     creds.server = krb->sprinc;
395     if((ret = krb5_get_credentials(krb->context, 0, krb->ccache, &creds, &krb->servcreds)) != 0)
396     {
397         fprintf(stderr, "krb5_get_credentials reported an error: %s\n", error_message(ret));
398         return(1);
399     }
400     /* WTF is this checksum stuff?! The Krb docs don't say a word about it! */
401     cksum.data = sstrdup(dc_gethostname());
402     cksum.length = strlen(cksum.data);
403     if((ret = krb5_mk_req_extended(krb->context, &krb->authcon, AP_OPTS_MUTUAL_REQUIRED, &cksum, krb->servcreds, &krb->reqbuf)) != 0)
404     {
405         fprintf(stderr, "krb5_mk_req_extended reported an error: %s\n", error_message(ret));
406         return(1);
407     }
408     free(cksum.data);
409     krb->state = 0;
410     return(0);
411 }
412
413 static void release_krb5(struct logindata *data)
414 {
415     struct krb5data *krb;
416     
417     if((krb = data->mechdata) == NULL)
418         return;
419     if(krb->servcreds != NULL)
420         krb5_free_creds(krb->context, krb->servcreds);
421     if(krb->reqbuf.data != NULL)
422         free(krb->reqbuf.data);
423     if(krb->sprinc != NULL)
424         krb5_free_principal(krb->context, krb->sprinc);
425     if(krb->myprinc != NULL)
426         krb5_free_principal(krb->context, krb->myprinc);
427     if(krb->ccache != NULL)
428         krb5_cc_close(krb->context, krb->ccache);
429     if(krb->authcon != NULL)
430         krb5_auth_con_free(krb->context, krb->authcon);
431     if(krb->context != NULL)
432         krb5_free_context(krb->context);
433     free(krb);
434 }
435 #endif
436
437 /* Arranged in order of priority */
438 static struct authmech authmechs[] =
439 {
440 #ifdef HAVE_KRB5
441     {
442         .name = L"krb5",
443         .process = process_krb5,
444         .init = init_krb5,
445         .release = release_krb5
446     },
447 #endif
448     {
449         .name = L"authless",
450         .process = process_authless,
451         .init = NULL,
452         .release = NULL
453     },
454     {
455         .name = L"pam",
456         .process = process_pam,
457         .init = NULL,
458         .release = NULL
459     },
460     {
461         .name = NULL
462     }
463 };
464
465 static int builtinconv(int type, wchar_t *text, char **resp, void *data)
466 {
467     char *buf, *pass;
468     
469     if(isatty(0))
470     {
471         if((buf = icwcstombs(text, NULL)) == NULL)
472             return(1);
473         pass = getpass(buf);
474         free(buf);
475         *resp = sstrdup(pass);
476         memset(pass, 0, strlen(pass));
477         return(0);
478     }
479     return(1);
480 }
481
482 static int logincallback(struct dc_response *resp)
483 {
484     int i;
485     struct dc_intresp *ires;
486     struct logindata *data;
487     int mech;
488     char *username;
489     struct passwd *pwent;
490     void *odata, *ndata;
491     
492     data = resp->data;
493     if(!wcscmp(resp->cmdname, L"lsauth"))
494     {
495         if(resp->code == 201)
496         {
497             data->callback(DC_LOGIN_ERR_NOLOGIN, NULL, data->data);
498             freelogindata(data);
499         } else {
500             mech = -1;
501             while((ires = dc_interpret(resp)) != NULL)
502             {
503                 if(!data->useauthless && !wcscmp(ires->argv[0].val.str, L"authless"))
504                 {
505                     dc_freeires(ires);
506                     continue;
507                 }
508                 for(i = 0; authmechs[i].name != NULL; i++)
509                 {
510                     if(!wcscmp(authmechs[i].name, ires->argv[0].val.str) && ((i < mech) || (mech == -1)))
511                     {
512                         odata = data->mechdata;
513                         data->mechdata = NULL;
514                         if((authmechs[i].init != NULL) && authmechs[i].init(data))
515                         {
516                             if(authmechs[i].release != NULL)
517                                 authmechs[i].release(data);
518                             data->mechdata = odata;
519                             fprintf(stderr, "authentication mechanism %ls failed, trying further...\n", authmechs[i].name);
520                         } else {
521                             if((data->mech != NULL) && data->mech->release != NULL)
522                             {
523                                 ndata = data->mechdata;
524                                 data->mechdata = odata;
525                                 data->mech->release(data);
526                                 data->mechdata = ndata;
527                             }
528                             mech = i;
529                             data->mech = authmechs + i;
530                         }
531                         break;
532                     }
533                 }
534                 dc_freeires(ires);
535             }
536             if(mech == -1)
537             {
538                 data->callback(DC_LOGIN_ERR_NOLOGIN, NULL, data->data);
539                 freelogindata(data);
540             } else {
541                 if((username = data->username) == NULL)
542                 {
543                     if((pwent = getpwuid(getuid())) == NULL)
544                     {
545                         data->callback(DC_LOGIN_ERR_USER, NULL, data->data);
546                         freelogindata(data);
547                         return(1);
548                     }
549                     username = pwent->pw_name;
550                 }
551                 dc_queuecmd(logincallback, data, L"login", data->mech->name, L"%%s", username, NULL);
552             }
553         }
554     } else if(!wcscmp(resp->cmdname, L"login") || !wcscmp(resp->cmdname, L"pass")) {
555         data->mech->process(resp, data);
556     }
557     return(1);
558 }
559
560 void dc_loginasync(char *username, int useauthless, int (*conv)(int, wchar_t *, char **, void *), void (*callback)(int, wchar_t *, void *), void *udata)
561 {
562     struct logindata *data;
563     
564     data = smalloc(sizeof(*data));
565     if(conv == NULL)
566         conv = builtinconv;
567     data->conv = conv;
568     data->mech = NULL;
569     data->data = udata;
570     data->mechdata = NULL;
571     data->callback = callback;
572     data->useauthless = useauthless;
573     data->freeusername = 0;
574     if(username == NULL)
575     {
576         data->username = NULL;
577     } else {
578         data->username = sstrdup(username);
579         data->freeusername = 1;
580     }
581     dc_queuecmd(logincallback, data, L"lsauth", NULL);
582 }
583
584 static struct dc_fnetnode *newfn(void)
585 {
586     struct dc_fnetnode *fn;
587     
588     fn = smalloc(sizeof(*fn));
589     memset(fn, 0, sizeof(*fn));
590     fn->id = -1;
591     fn->name = NULL;
592     fn->fnet = NULL;
593     fn->state = fn->numusers = fn->found = 0;
594     fn->destroycb = NULL;
595     fn->udata = NULL;
596     fn->next = dc_fnetnodes;
597     fn->prev = NULL;
598     if(dc_fnetnodes != NULL)
599         dc_fnetnodes->prev = fn;
600     dc_fnetnodes = fn;
601     return(fn);
602 }
603
604 static void freefn(struct dc_fnetnode *fn)
605 {
606     if(fn->next != NULL)
607         fn->next->prev = fn->prev;
608     if(fn->prev != NULL)
609         fn->prev->next = fn->next;
610     if(fn == dc_fnetnodes)
611         dc_fnetnodes = fn->next;
612     if(fn->destroycb != NULL)
613         fn->destroycb(fn);
614     if(fn->name != NULL)
615         free(fn->name);
616     if(fn->fnet != NULL)
617         free(fn->fnet);
618     free(fn);
619 }
620
621 struct dc_fnetnode *dc_findfnetnode(int id)
622 {
623     struct dc_fnetnode *fn;
624     
625     for(fn = dc_fnetnodes; fn != NULL; fn = fn->next)
626     {
627         if(fn->id == id)
628             break;
629     }
630     return(fn);
631 }
632
633 static struct dc_transfer *newtransfer(void)
634 {
635     struct dc_transfer *transfer;
636     
637     transfer = smalloc(sizeof(*transfer));
638     memset(transfer, 0, sizeof(*transfer));
639     transfer->id = -1;
640     transfer->peerid = transfer->peernick = transfer->path = NULL;
641     transfer->state = DC_TRNS_WAITING;
642     transfer->dir = DC_TRNSD_UNKNOWN;
643     transfer->size = -1;
644     transfer->curpos = -1;
645     transfer->destroycb = NULL;
646     transfer->udata = NULL;
647     transfer->next = dc_transfers;
648     transfer->prev = NULL;
649     if(dc_transfers != NULL)
650         dc_transfers->prev = transfer;
651     dc_transfers = transfer;
652     return(transfer);
653 }
654
655 static void freetransfer(struct dc_transfer *transfer)
656 {
657     if(transfer->next != NULL)
658         transfer->next->prev = transfer->prev;
659     if(transfer->prev != NULL)
660         transfer->prev->next = transfer->next;
661     if(transfer == dc_transfers)
662         dc_transfers = transfer->next;
663     if(transfer->destroycb != NULL)
664         transfer->destroycb(transfer);
665     if(transfer->peerid != NULL)
666         free(transfer->peerid);
667     if(transfer->peernick != NULL)
668         free(transfer->peernick);
669     if(transfer->path != NULL)
670         free(transfer->path);
671     free(transfer);
672 }
673
674 struct dc_transfer *dc_findtransfer(int id)
675 {
676     struct dc_transfer *transfer;
677     
678     for(transfer = dc_transfers; transfer != NULL; transfer = transfer->next)
679     {
680         if(transfer->id == id)
681             break;
682     }
683     return(transfer);
684 }
685
686 static int getfnlistcallback(struct dc_response *resp)
687 {
688     struct dc_intresp *ires;
689     struct gencbdata *data;
690     struct dc_fnetnode *fn, *next;
691     
692     data = resp->data;
693     if(resp->code == 200)
694     {
695         for(fn = dc_fnetnodes; fn != NULL; fn = fn->next)
696             fn->found = 0;
697         while((ires = dc_interpret(resp)) != NULL)
698         {
699             if((fn = dc_findfnetnode(ires->argv[0].val.num)) != NULL)
700             {
701                 fn->found = 1;
702                 if(fn->fnet != NULL)
703                     free(fn->fnet);
704                 fn->fnet = swcsdup(ires->argv[1].val.str);
705                 if(fn->name != NULL)
706                     free(fn->name);
707                 fn->name = swcsdup(ires->argv[2].val.str);
708                 fn->numusers = ires->argv[3].val.num;
709                 fn->state = ires->argv[4].val.num;
710             } else {
711                 fn = newfn();
712                 fn->id = ires->argv[0].val.num;
713                 fn->fnet = swcsdup(ires->argv[1].val.str);
714                 fn->name = swcsdup(ires->argv[2].val.str);
715                 fn->numusers = ires->argv[3].val.num;
716                 fn->state = ires->argv[4].val.num;
717                 fn->found = 1;
718             }
719             dc_freeires(ires);
720         }
721         for(fn = dc_fnetnodes; fn != NULL; fn = next)
722         {
723             next = fn->next;
724             if(!fn->found)
725                 freefn(fn);
726         }
727         data->callback(200, data->data);
728         free(resp->data);
729     } else if(resp->code == 201) {
730         while(dc_fnetnodes != NULL)
731             freefn(dc_fnetnodes);
732         data->callback(201, data->data);
733         free(resp->data);
734     } else if(resp->code == 502) {
735         while(dc_fnetnodes != NULL)
736             freefn(dc_fnetnodes);
737         data->callback(502, data->data);
738         free(resp->data);
739     }
740     return(1);
741 }
742
743 static int gettrlistcallback(struct dc_response *resp)
744 {
745     struct dc_intresp *ires;
746     struct gencbdata *data;
747     struct dc_transfer *transfer, *next;
748     
749     data = resp->data;
750     if(resp->code == 200)
751     {
752         for(transfer = dc_transfers; transfer != NULL; transfer = transfer->next)
753             transfer->found = 0;
754         while((ires = dc_interpret(resp)) != NULL)
755         {
756             if((transfer = dc_findtransfer(ires->argv[0].val.num)) != NULL)
757             {
758                 transfer->found = 1;
759                 if((transfer->path == NULL) || wcscmp(transfer->path, ires->argv[5].val.str))
760                 {
761                     if(transfer->path != NULL)
762                         free(transfer->path);
763                     transfer->path = swcsdup(ires->argv[5].val.str);
764                 }
765                 if((transfer->peerid == NULL) || wcscmp(transfer->peerid, ires->argv[3].val.str))
766                 {
767                     if(transfer->peerid != NULL)
768                         free(transfer->peerid);
769                     transfer->peerid = swcsdup(ires->argv[3].val.str);
770                 }
771                 if((transfer->peernick == NULL) || wcscmp(transfer->peernick, ires->argv[4].val.str))
772                 {
773                     if(transfer->peernick != NULL)
774                         free(transfer->peernick);
775                     transfer->peernick = swcsdup(ires->argv[4].val.str);
776                 }
777                 transfer->dir = ires->argv[1].val.num;
778                 transfer->state = ires->argv[2].val.num;
779                 transfer->size = ires->argv[6].val.num;
780                 transfer->curpos = ires->argv[7].val.num;
781             } else {
782                 transfer = newtransfer();
783                 transfer->id = ires->argv[0].val.num;
784                 transfer->dir = ires->argv[1].val.num;
785                 transfer->state = ires->argv[2].val.num;
786                 transfer->peerid = swcsdup(ires->argv[3].val.str);
787                 transfer->peernick = swcsdup(ires->argv[4].val.str);
788                 transfer->path = swcsdup(ires->argv[5].val.str);
789                 transfer->size = ires->argv[6].val.num;
790                 transfer->curpos = ires->argv[7].val.num;
791                 transfer->found = 1;
792             }
793             dc_freeires(ires);
794         }
795         for(transfer = dc_transfers; transfer != NULL; transfer = next)
796         {
797             next = transfer->next;
798             if(!transfer->found)
799                 freetransfer(transfer);
800         }
801         data->callback(200, data->data);
802         free(data);
803     } else if(resp->code == 201) {
804         while(dc_transfers != NULL)
805             freetransfer(dc_transfers);
806         data->callback(201, data->data);
807         free(data);
808     } else if(resp->code == 502) {
809         while(dc_transfers != NULL)
810             freetransfer(dc_transfers);
811         data->callback(502, data->data);
812         free(data);
813     }
814     return(1);
815 }
816
817 void dc_getfnlistasync(void (*callback)(int, void *), void *udata)
818 {
819     struct gencbdata *data;
820     
821     data = smalloc(sizeof(*data));
822     data->callback = callback;
823     data->data = udata;
824     dc_queuecmd(getfnlistcallback, data, L"lsnodes", NULL);
825 }
826
827 void dc_gettrlistasync(void (*callback)(int, void *), void *udata)
828 {
829     struct gencbdata *data;
830     
831     data = smalloc(sizeof(*data));
832     data->callback = callback;
833     data->data = udata;
834     dc_queuecmd(gettrlistcallback, data, L"lstrans", NULL);
835 }
836
837 void dc_uimisc_disconnected(void)
838 {
839     while(dc_fnetnodes != NULL)
840         freefn(dc_fnetnodes);
841     while(dc_transfers != NULL)
842         freetransfer(dc_transfers);
843 }
844
845 void dc_uimisc_handlenotify(struct dc_response *resp)
846 {
847     struct dc_fnetnode *fn;
848     struct dc_transfer *transfer;
849     struct dc_intresp *ires;
850     
851     if((ires = dc_interpret(resp)) == NULL)
852         return;
853     switch(resp->code)
854     {
855     case 601:
856         if((fn = dc_findfnetnode(ires->argv[0].val.num)) != NULL)
857             fn->state = ires->argv[1].val.num;
858         break;
859     case 602:
860         if((fn = dc_findfnetnode(ires->argv[0].val.num)) != NULL)
861         {
862             if(fn->name != NULL)
863                 free(fn->name);
864             fn->name = swcsdup(ires->argv[1].val.str);
865         }
866         break;
867     case 603:
868         if((fn = dc_findfnetnode(ires->argv[0].val.num)) != NULL)
869             freefn(fn);
870         break;
871     case 604:
872         fn = newfn();
873         fn->id = ires->argv[0].val.num;
874         if(fn->fnet != NULL)
875             free(fn->fnet);
876         fn->fnet = swcsdup(ires->argv[1].val.str);
877         fn->state = DC_FNN_STATE_SYN;
878         fn->numusers = 0;
879         break;
880     case 605:
881         if((fn = dc_findfnetnode(ires->argv[0].val.num)) != NULL)
882             fn->numusers = ires->argv[1].val.num;
883         break;
884     case 610:
885         transfer = newtransfer();
886         transfer->id = ires->argv[0].val.num;
887         transfer->dir = ires->argv[1].val.num;
888         if(transfer->dir == DC_TRNSD_UP)
889             transfer->state = DC_TRNS_HS;
890         transfer->peerid = swcsdup(ires->argv[2].val.str);
891         if(ires->argv[3].val.str[0])
892             transfer->path = swcsdup(ires->argv[3].val.str);
893         break;
894     case 611:
895         if((transfer = dc_findtransfer(ires->argv[0].val.num)) != NULL)
896             transfer->state = ires->argv[1].val.num;
897         break;
898     case 612:
899         if((transfer = dc_findtransfer(ires->argv[0].val.num)) != NULL)
900         {
901             if(transfer->peernick != NULL)
902                 free(transfer->peernick);
903             transfer->peernick = swcsdup(ires->argv[1].val.str);
904         }
905         break;
906     case 613:
907         if((transfer = dc_findtransfer(ires->argv[0].val.num)) != NULL)
908             transfer->size = ires->argv[1].val.num;
909         break;
910     case 614:
911         if((transfer = dc_findtransfer(ires->argv[0].val.num)) != NULL)
912         {
913             transfer->error = ires->argv[1].val.num;
914             time(&transfer->errortime);
915         }
916         break;
917     case 615:
918         if((transfer = dc_findtransfer(ires->argv[0].val.num)) != NULL)
919             transfer->curpos = ires->argv[1].val.num;
920         break;
921     case 616:
922         if((transfer = dc_findtransfer(ires->argv[0].val.num)) != NULL)
923         {
924             if(transfer->path != NULL)
925                 free(transfer->path);
926             transfer->path = swcsdup(ires->argv[1].val.str);
927         }
928         break;
929     case 617:
930         if((transfer = dc_findtransfer(ires->argv[0].val.num)) != NULL)
931             freetransfer(transfer);
932         break;
933     default:
934         break;
935     }
936     dc_freeires(ires);
937     resp->curline = 0;
938 }
939
940 /* Note the backspace handling - it's not as elegant as possible, but
941  * it helps avoid the "box-of-toothpicks" syndrome when writing search
942  * expressions manually. */
943 wchar_t **dc_lexsexpr(wchar_t *sexpr)
944 {
945     wchar_t **ret;
946     wchar_t *buf;
947     size_t retsize, retdata, bufsize, bufdata;
948     int state;
949     
950     ret = NULL;
951     buf = NULL;
952     retsize = retdata = bufsize = bufdata = 0;
953     state = 0;
954     while(*sexpr != L'\0')
955     {
956         switch(state)
957         {
958         case 0:
959             if(!iswspace(*sexpr))
960                 state = 1;
961             else
962                 sexpr++;
963             break;
964         case 1:
965             if(iswspace(*sexpr))
966             {
967                 if(buf != NULL)
968                 {
969                     addtobuf(buf, L'\0');
970                     addtobuf(ret, buf);
971                     buf = NULL;
972                     bufsize = bufdata = 0;
973                 }
974                 state = 0;
975             } else if((*sexpr == L'(') ||
976                       (*sexpr == L')') ||
977                       (*sexpr == L'&') ||
978                       (*sexpr == L'|') ||
979                       (*sexpr == L'!')) {
980                 if(buf != NULL)
981                 {
982                     addtobuf(buf, L'\0');
983                     addtobuf(ret, buf);
984                     buf = NULL;
985                     bufsize = bufdata = 0;
986                 }
987                 addtobuf(buf, *sexpr);
988                 addtobuf(buf, L'\0');
989                 addtobuf(ret, buf);
990                 buf = NULL;
991                 bufsize = bufdata = 0;
992                 sexpr++;
993             } else if(*sexpr == L'\"') {
994                 sexpr++;
995                 state = 2;
996             } else if(*sexpr == L'\\') {
997                 sexpr++;
998                 if(*sexpr == L'\0')
999                 {
1000                     addtobuf(buf, *sexpr);
1001                 } else if((*sexpr == L'\\') || (*sexpr == L'\"')) {
1002                     addtobuf(buf, *sexpr);
1003                     sexpr++;
1004                 } else {
1005                     addtobuf(buf, L'\\');
1006                     addtobuf(buf, *sexpr);
1007                     sexpr++;
1008                 }
1009             } else {
1010                 addtobuf(buf, *(sexpr++));
1011             }
1012             break;
1013         case 2:
1014             if(*sexpr == L'\\')
1015             {
1016                 sexpr++;
1017                 if(*sexpr == L'\0')
1018                 {
1019                     addtobuf(buf, *sexpr);
1020                 } else if((*sexpr == L'\\') || (*sexpr == L'\"')) {
1021                     addtobuf(buf, *sexpr);
1022                     sexpr++;
1023                 } else {
1024                     addtobuf(buf, L'\\');
1025                     addtobuf(buf, *sexpr);
1026                     sexpr++;
1027                 }
1028             } else if(*sexpr == L'\"') {
1029                 state = 1;
1030                 sexpr++;
1031             } else {
1032                 addtobuf(buf, *(sexpr++));
1033             }
1034             break;
1035         }
1036     }
1037     if(buf != NULL)
1038     {
1039         addtobuf(buf, L'\0');
1040         addtobuf(ret, buf);
1041     }
1042     addtobuf(ret, NULL);
1043     return(ret);
1044 }
1045
1046 void dc_freewcsarr(wchar_t **arr)
1047 {
1048     wchar_t **buf;
1049     
1050     if(arr == NULL)
1051         return;
1052     for(buf = arr; *buf != NULL; buf++)
1053         free(*buf);
1054     free(arr);
1055 }