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