Store transfer hashes.
[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                 if(transfer->hash != NULL)
782                 {
783                     free(transfer->hash);
784                     transfer->hash = NULL;
785                 }
786                 if(wcslen(ires->argv[8].val.str) > 0)
787                     transfer->hash = swcsdup(ires->argv[8].val.str);
788             } else {
789                 transfer = newtransfer();
790                 transfer->id = ires->argv[0].val.num;
791                 transfer->dir = ires->argv[1].val.num;
792                 transfer->state = ires->argv[2].val.num;
793                 transfer->peerid = swcsdup(ires->argv[3].val.str);
794                 transfer->peernick = swcsdup(ires->argv[4].val.str);
795                 transfer->path = swcsdup(ires->argv[5].val.str);
796                 transfer->size = ires->argv[6].val.num;
797                 transfer->curpos = ires->argv[7].val.num;
798                 if(wcslen(ires->argv[8].val.str) > 0)
799                     transfer->hash = swcsdup(ires->argv[8].val.str);
800                 transfer->found = 1;
801             }
802             dc_freeires(ires);
803         }
804         for(transfer = dc_transfers; transfer != NULL; transfer = next)
805         {
806             next = transfer->next;
807             if(!transfer->found)
808                 freetransfer(transfer);
809         }
810         data->callback(200, data->data);
811         free(data);
812     } else if(resp->code == 201) {
813         while(dc_transfers != NULL)
814             freetransfer(dc_transfers);
815         data->callback(201, data->data);
816         free(data);
817     } else if(resp->code == 502) {
818         while(dc_transfers != NULL)
819             freetransfer(dc_transfers);
820         data->callback(502, data->data);
821         free(data);
822     }
823     return(1);
824 }
825
826 void dc_getfnlistasync(void (*callback)(int, void *), void *udata)
827 {
828     struct gencbdata *data;
829     
830     data = smalloc(sizeof(*data));
831     data->callback = callback;
832     data->data = udata;
833     dc_queuecmd(getfnlistcallback, data, L"lsnodes", NULL);
834 }
835
836 void dc_gettrlistasync(void (*callback)(int, void *), void *udata)
837 {
838     struct gencbdata *data;
839     
840     data = smalloc(sizeof(*data));
841     data->callback = callback;
842     data->data = udata;
843     dc_queuecmd(gettrlistcallback, data, L"lstrans", NULL);
844 }
845
846 void dc_uimisc_disconnected(void)
847 {
848     while(dc_fnetnodes != NULL)
849         freefn(dc_fnetnodes);
850     while(dc_transfers != NULL)
851         freetransfer(dc_transfers);
852 }
853
854 void dc_uimisc_handlenotify(struct dc_response *resp)
855 {
856     struct dc_fnetnode *fn;
857     struct dc_transfer *transfer;
858     struct dc_intresp *ires;
859     
860     if((ires = dc_interpret(resp)) == NULL)
861         return;
862     switch(resp->code)
863     {
864     case 601:
865         if((fn = dc_findfnetnode(ires->argv[0].val.num)) != NULL)
866             fn->state = ires->argv[1].val.num;
867         break;
868     case 602:
869         if((fn = dc_findfnetnode(ires->argv[0].val.num)) != NULL)
870         {
871             if(fn->name != NULL)
872                 free(fn->name);
873             fn->name = swcsdup(ires->argv[1].val.str);
874         }
875         break;
876     case 603:
877         if((fn = dc_findfnetnode(ires->argv[0].val.num)) != NULL)
878             freefn(fn);
879         break;
880     case 604:
881         fn = newfn();
882         fn->id = ires->argv[0].val.num;
883         if(fn->fnet != NULL)
884             free(fn->fnet);
885         fn->fnet = swcsdup(ires->argv[1].val.str);
886         fn->state = DC_FNN_STATE_SYN;
887         fn->numusers = 0;
888         break;
889     case 605:
890         if((fn = dc_findfnetnode(ires->argv[0].val.num)) != NULL)
891             fn->numusers = ires->argv[1].val.num;
892         break;
893     case 610:
894         transfer = newtransfer();
895         transfer->id = ires->argv[0].val.num;
896         transfer->dir = ires->argv[1].val.num;
897         if(transfer->dir == DC_TRNSD_UP)
898             transfer->state = DC_TRNS_HS;
899         transfer->peerid = swcsdup(ires->argv[2].val.str);
900         if(ires->argv[3].val.str[0])
901             transfer->path = swcsdup(ires->argv[3].val.str);
902         break;
903     case 611:
904         if((transfer = dc_findtransfer(ires->argv[0].val.num)) != NULL)
905             transfer->state = ires->argv[1].val.num;
906         break;
907     case 612:
908         if((transfer = dc_findtransfer(ires->argv[0].val.num)) != NULL)
909         {
910             if(transfer->peernick != NULL)
911                 free(transfer->peernick);
912             transfer->peernick = swcsdup(ires->argv[1].val.str);
913         }
914         break;
915     case 613:
916         if((transfer = dc_findtransfer(ires->argv[0].val.num)) != NULL)
917             transfer->size = ires->argv[1].val.num;
918         break;
919     case 614:
920         if((transfer = dc_findtransfer(ires->argv[0].val.num)) != NULL)
921         {
922             transfer->error = ires->argv[1].val.num;
923             time(&transfer->errortime);
924         }
925         break;
926     case 615:
927         if((transfer = dc_findtransfer(ires->argv[0].val.num)) != NULL)
928             transfer->curpos = ires->argv[1].val.num;
929         break;
930     case 616:
931         if((transfer = dc_findtransfer(ires->argv[0].val.num)) != NULL)
932         {
933             if(transfer->path != NULL)
934                 free(transfer->path);
935             transfer->path = swcsdup(ires->argv[1].val.str);
936         }
937         break;
938     case 617:
939         if((transfer = dc_findtransfer(ires->argv[0].val.num)) != NULL)
940             freetransfer(transfer);
941         break;
942     case 618:
943         if((transfer = dc_findtransfer(ires->argv[0].val.num)) != NULL)
944         {
945             if(transfer->hash != NULL)
946             {
947                 free(transfer->hash);
948                 transfer->hash = NULL;
949             }
950             if(wcslen(ires->argv[1].val.str) > 0)
951                 transfer->hash = swcsdup(ires->argv[1].val.str);
952         }
953         break;
954     default:
955         break;
956     }
957     dc_freeires(ires);
958     resp->curline = 0;
959 }
960
961 /* Note the backspace handling - it's not as elegant as possible, but
962  * it helps avoid the "box-of-toothpicks" syndrome when writing search
963  * expressions manually. */
964 wchar_t **dc_lexsexpr(wchar_t *sexpr)
965 {
966     wchar_t **ret;
967     wchar_t *buf;
968     size_t retsize, retdata, bufsize, bufdata;
969     int state;
970     
971     ret = NULL;
972     buf = NULL;
973     retsize = retdata = bufsize = bufdata = 0;
974     state = 0;
975     while(*sexpr != L'\0')
976     {
977         switch(state)
978         {
979         case 0:
980             if(!iswspace(*sexpr))
981                 state = 1;
982             else
983                 sexpr++;
984             break;
985         case 1:
986             if(iswspace(*sexpr))
987             {
988                 if(buf != NULL)
989                 {
990                     addtobuf(buf, L'\0');
991                     addtobuf(ret, buf);
992                     buf = NULL;
993                     bufsize = bufdata = 0;
994                 }
995                 state = 0;
996             } else if((*sexpr == L'(') ||
997                       (*sexpr == L')') ||
998                       (*sexpr == L'&') ||
999                       (*sexpr == L'|') ||
1000                       (*sexpr == L'!')) {
1001                 if(buf != NULL)
1002                 {
1003                     addtobuf(buf, L'\0');
1004                     addtobuf(ret, buf);
1005                     buf = NULL;
1006                     bufsize = bufdata = 0;
1007                 }
1008                 addtobuf(buf, *sexpr);
1009                 addtobuf(buf, L'\0');
1010                 addtobuf(ret, buf);
1011                 buf = NULL;
1012                 bufsize = bufdata = 0;
1013                 sexpr++;
1014             } else if(*sexpr == L'\"') {
1015                 sexpr++;
1016                 state = 2;
1017             } else if(*sexpr == L'\\') {
1018                 sexpr++;
1019                 if(*sexpr == L'\0')
1020                 {
1021                     addtobuf(buf, *sexpr);
1022                 } else if((*sexpr == L'\\') || (*sexpr == L'\"')) {
1023                     addtobuf(buf, *sexpr);
1024                     sexpr++;
1025                 } else {
1026                     addtobuf(buf, L'\\');
1027                     addtobuf(buf, *sexpr);
1028                     sexpr++;
1029                 }
1030             } else {
1031                 addtobuf(buf, *(sexpr++));
1032             }
1033             break;
1034         case 2:
1035             if(*sexpr == L'\\')
1036             {
1037                 sexpr++;
1038                 if(*sexpr == L'\0')
1039                 {
1040                     addtobuf(buf, *sexpr);
1041                 } else if((*sexpr == L'\\') || (*sexpr == L'\"')) {
1042                     addtobuf(buf, *sexpr);
1043                     sexpr++;
1044                 } else {
1045                     addtobuf(buf, L'\\');
1046                     addtobuf(buf, *sexpr);
1047                     sexpr++;
1048                 }
1049             } else if(*sexpr == L'\"') {
1050                 state = 1;
1051                 sexpr++;
1052             } else {
1053                 addtobuf(buf, *(sexpr++));
1054             }
1055             break;
1056         }
1057     }
1058     if(buf != NULL)
1059     {
1060         addtobuf(buf, L'\0');
1061         addtobuf(ret, buf);
1062     }
1063     addtobuf(ret, NULL);
1064     return(ret);
1065 }
1066
1067 void dc_freewcsarr(wchar_t **arr)
1068 {
1069     wchar_t **buf;
1070     
1071     if(arr == NULL)
1072         return;
1073     for(buf = arr; *buf != NULL; buf++)
1074         free(*buf);
1075     free(arr);
1076 }