Shut up unless told otherwise.
[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     krb = smalloc(sizeof(*krb));
325     memset(krb, 0, sizeof(*krb));
326     krb->fwd = 1;
327     krb->fwded = 0;
328     data->mechdata = krb;
329     if((ret = krb5_init_context(&krb->context)) != 0)
330     {
331         message("krb5_init_context reported an error: %s\n", error_message(ret));
332         return(1);
333     }
334     if((ret = krb5_auth_con_init(krb->context, &krb->authcon)) != 0)
335     {
336         message("krb5_auth_con_init reported an error: %s\n", error_message(ret));
337         return(1);
338     }
339     krb5_auth_con_setflags(krb->context, krb->authcon, KRB5_AUTH_CONTEXT_DO_SEQUENCE);
340     if((ret = krb5_sname_to_principal(krb->context, dc_gethostname(), "doldacond", KRB5_NT_SRV_HST, &krb->sprinc)) != 0)
341     {
342         message("krb5_sname_to_principal reported an error: %s\n", error_message(ret));
343         return(1);
344     }
345     if((ret = krb5_cc_default(krb->context, &krb->ccache)) != 0)
346     {
347         message("krb5_cc_default reported an error: %s\n", error_message(ret));
348         return(1);
349     }
350     if((ret = krb5_cc_get_principal(krb->context, krb->ccache, &krb->myprinc)) != 0)
351     {
352         message("krb5_cc_default reported an error: %s\n", error_message(ret));
353         return(1);
354     }
355     memset(&creds, 0, sizeof(creds));
356     creds.client = krb->myprinc;
357     creds.server = krb->sprinc;
358     if((ret = krb5_get_credentials(krb->context, 0, krb->ccache, &creds, &krb->servcreds)) != 0)
359     {
360         message("krb5_get_credentials reported an error: %s\n", error_message(ret));
361         return(1);
362     }
363     /* WTF is this checksum stuff?! The Krb docs don't say a word about it! */
364     cksum.data = sstrdup(dc_gethostname());
365     cksum.length = strlen(cksum.data);
366     if((ret = krb5_mk_req_extended(krb->context, &krb->authcon, AP_OPTS_MUTUAL_REQUIRED, &cksum, krb->servcreds, &krb->reqbuf)) != 0)
367     {
368         message("krb5_mk_req_extended reported an error: %s\n", error_message(ret));
369         return(1);
370     }
371     free(cksum.data);
372     krb->state = 0;
373     return(0);
374 }
375
376 static void release_krb5(struct logindata *data)
377 {
378     struct krb5data *krb;
379     
380     if((krb = data->mechdata) == NULL)
381         return;
382     if(krb->servcreds != NULL)
383         krb5_free_creds(krb->context, krb->servcreds);
384     if(krb->reqbuf.data != NULL)
385         free(krb->reqbuf.data);
386     if(krb->sprinc != NULL)
387         krb5_free_principal(krb->context, krb->sprinc);
388     if(krb->myprinc != NULL)
389         krb5_free_principal(krb->context, krb->myprinc);
390     if(krb->ccache != NULL)
391         krb5_cc_close(krb->context, krb->ccache);
392     if(krb->authcon != NULL)
393         krb5_auth_con_free(krb->context, krb->authcon);
394     if(krb->context != NULL)
395         krb5_free_context(krb->context);
396     free(krb);
397 }
398 #endif
399
400 /* Arranged in order of priority */
401 static struct authmech authmechs[] =
402 {
403 #ifdef HAVE_KRB5
404     {
405         .name = L"krb5",
406         .process = process_krb5,
407         .init = init_krb5,
408         .release = release_krb5
409     },
410 #endif
411     {
412         .name = L"authless",
413         .process = process_authless,
414         .init = NULL,
415         .release = NULL
416     },
417     {
418         .name = L"pam",
419         .process = process_pam,
420         .init = NULL,
421         .release = NULL
422     },
423     {
424         .name = NULL
425     }
426 };
427
428 static int builtinconv(int type, wchar_t *text, char **resp, void *data)
429 {
430     char *buf, *pass;
431     
432     if(isatty(0))
433     {
434         if((buf = icwcstombs(text, NULL)) == NULL)
435             return(1);
436         pass = getpass(buf);
437         free(buf);
438         *resp = sstrdup(pass);
439         memset(pass, 0, strlen(pass));
440         return(0);
441     }
442     return(1);
443 }
444
445 static int logincallback(struct dc_response *resp)
446 {
447     int i;
448     struct dc_intresp *ires;
449     struct logindata *data;
450     int mech;
451     char *username;
452     struct passwd *pwent;
453     void *odata, *ndata;
454     
455     data = resp->data;
456     if(!wcscmp(resp->cmdname, L"lsauth"))
457     {
458         if(resp->code == 201)
459         {
460             data->callback(DC_LOGIN_ERR_NOLOGIN, NULL, data->data);
461             freelogindata(data);
462         } else {
463             mech = -1;
464             while((ires = dc_interpret(resp)) != NULL)
465             {
466                 if(!data->useauthless && !wcscmp(ires->argv[0].val.str, L"authless"))
467                 {
468                     dc_freeires(ires);
469                     continue;
470                 }
471                 for(i = 0; authmechs[i].name != NULL; i++)
472                 {
473                     if(!wcscmp(authmechs[i].name, ires->argv[0].val.str) && ((i < mech) || (mech == -1)))
474                     {
475                         odata = data->mechdata;
476                         data->mechdata = NULL;
477                         if((authmechs[i].init != NULL) && authmechs[i].init(data))
478                         {
479                             if(authmechs[i].release != NULL)
480                                 authmechs[i].release(data);
481                             data->mechdata = odata;
482                             message("authentication mechanism %ls failed, trying further...\n", authmechs[i].name);
483                         } else {
484                             if((data->mech != NULL) && data->mech->release != NULL)
485                             {
486                                 ndata = data->mechdata;
487                                 data->mechdata = odata;
488                                 data->mech->release(data);
489                                 data->mechdata = ndata;
490                             }
491                             mech = i;
492                             data->mech = authmechs + i;
493                         }
494                         break;
495                     }
496                 }
497                 dc_freeires(ires);
498             }
499             if(mech == -1)
500             {
501                 data->callback(DC_LOGIN_ERR_NOLOGIN, NULL, data->data);
502                 freelogindata(data);
503             } else {
504                 if((username = data->username) == NULL)
505                 {
506                     if((pwent = getpwuid(getuid())) == NULL)
507                     {
508                         data->callback(DC_LOGIN_ERR_USER, NULL, data->data);
509                         freelogindata(data);
510                         return(1);
511                     }
512                     username = pwent->pw_name;
513                 }
514                 dc_queuecmd(logincallback, data, L"login", data->mech->name, L"%%s", username, NULL);
515             }
516         }
517     } else if(!wcscmp(resp->cmdname, L"login") || !wcscmp(resp->cmdname, L"pass")) {
518         data->mech->process(resp, data);
519     }
520     return(1);
521 }
522
523 void dc_loginasync(char *username, int useauthless, int (*conv)(int, wchar_t *, char **, void *), void (*callback)(int, wchar_t *, void *), void *udata)
524 {
525     struct logindata *data;
526     
527     data = smalloc(sizeof(*data));
528     if(conv == NULL)
529         conv = builtinconv;
530     data->conv = conv;
531     data->mech = NULL;
532     data->data = udata;
533     data->mechdata = NULL;
534     data->callback = callback;
535     data->useauthless = useauthless;
536     data->freeusername = 0;
537     if(username == NULL)
538     {
539         data->username = NULL;
540     } else {
541         data->username = sstrdup(username);
542         data->freeusername = 1;
543     }
544     dc_queuecmd(logincallback, data, L"lsauth", NULL);
545 }
546
547 static struct dc_fnetpeerdatum *finddatum(struct dc_fnetnode *fn, wchar_t *id)
548 {
549     struct dc_fnetpeerdatum *datum;
550     
551     for(datum = fn->peerdata; datum != NULL; datum = datum->next)
552     {
553         if(!wcscmp(datum->id, id))
554             break;
555     }
556     return(datum);
557 }
558
559 static struct dc_fnetpeerdatum *adddatum(struct dc_fnetnode *fn, wchar_t *id, int dt)
560 {
561     struct dc_fnetpeerdatum *datum;
562     
563     datum = smalloc(sizeof(*datum));
564     memset(datum, 0, sizeof(*datum));
565     datum->refcount = 0;
566     datum->dt = dt;
567     datum->id = swcsdup(id);
568     datum->prev = NULL;
569     datum->next = fn->peerdata;
570     if(fn->peerdata != NULL)
571         fn->peerdata->prev = datum;
572     fn->peerdata = datum;
573     return(datum);
574 }
575
576 static struct dc_fnetpeerdi *difindoradd(struct dc_fnetpeer *peer, struct dc_fnetpeerdatum *datum)
577 {
578     int i;
579     
580     for(i = 0; i < peer->dinum; i++)
581     {
582         if(peer->di[i].datum == datum)
583             return(&peer->di[i]);
584     }
585     peer->di = srealloc(peer->di, sizeof(struct dc_fnetpeerdi) * ++(peer->dinum));
586     memset(&peer->di[i], 0, sizeof(struct dc_fnetpeerdi));
587     peer->di[i].datum = datum;
588     datum->refcount++;
589     return(&peer->di[i]);
590 }
591
592 static void putdatum(struct dc_fnetnode *fn, struct dc_fnetpeerdatum *datum)
593 {
594     if(--datum->refcount > 0)
595         return;
596     if(datum->next != NULL)
597         datum->next->prev = datum->prev;
598     if(datum->prev != NULL)
599         datum->prev->next = datum->next;
600     if(fn->peerdata == datum)
601         fn->peerdata = datum->next;
602     free(datum->id);
603     free(datum);
604 }
605
606 static void peersetnum(struct dc_fnetpeer *peer, wchar_t *id, int value)
607 {
608     struct dc_fnetpeerdatum *datum;
609     struct dc_fnetpeerdi *di;
610     
611     if((datum = finddatum(peer->fn, id)) == NULL)
612         datum = adddatum(peer->fn, id, DC_FNPD_INT);
613     di = difindoradd(peer, datum);
614     di->d.num = value;
615 }
616
617 static void peersetlnum(struct dc_fnetpeer *peer, wchar_t *id, long long 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.lnum = value;
626 }
627
628 static void peersetstr(struct dc_fnetpeer *peer, wchar_t *id, wchar_t *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     if(di->d.str != NULL)
637         free(di->d.str);
638     di->d.str = swcsdup(value);
639 }
640
641 struct dc_fnetpeer *dc_fnetfindpeer(struct dc_fnetnode *fn, wchar_t *id)
642 {
643     struct dc_fnetpeer *peer;
644     
645     for(peer = fn->peers; peer != NULL; peer = peer->next)
646     {
647         if(!wcscmp(peer->id, id))
648             break;
649     }
650     return(peer);
651 }
652
653 static struct dc_fnetpeer *addpeer(struct dc_fnetnode *fn, wchar_t *id, wchar_t *nick)
654 {
655     struct dc_fnetpeer *peer;
656     
657     peer = smalloc(sizeof(*peer));
658     memset(peer, 0, sizeof(*peer));
659     peer->fn = fn;
660     peer->id = swcsdup(id);
661     peer->nick = swcsdup(nick);
662     peer->next = fn->peers;
663     peer->prev = NULL;
664     if(fn->peers != NULL)
665         fn->peers->prev = peer;
666     fn->peers = peer;
667     return(peer);
668 }
669
670 static void delpeer(struct dc_fnetpeer *peer)
671 {
672     int i;
673     
674     if(peer->next != NULL)
675         peer->next->prev = peer->prev;
676     if(peer->prev != NULL)
677         peer->prev->next = peer->next;
678     if(peer->fn->peers == peer)
679         peer->fn->peers = peer->next;
680     free(peer->id);
681     free(peer->nick);
682     for(i = 0; i < peer->dinum; i++)
683     {
684         if((peer->di[i].datum->dt == DC_FNPD_STR) && (peer->di[i].d.str != NULL))
685             free(peer->di[i].d.str);
686         putdatum(peer->fn, peer->di[i].datum);
687     }
688     free(peer);
689 }
690
691 static struct dc_fnetnode *newfn(void)
692 {
693     struct dc_fnetnode *fn;
694     
695     fn = smalloc(sizeof(*fn));
696     memset(fn, 0, sizeof(*fn));
697     fn->id = -1;
698     fn->name = NULL;
699     fn->fnet = NULL;
700     fn->state = fn->numusers = fn->found = 0;
701     fn->destroycb = NULL;
702     fn->udata = NULL;
703     fn->next = dc_fnetnodes;
704     fn->prev = NULL;
705     if(dc_fnetnodes != NULL)
706         dc_fnetnodes->prev = fn;
707     dc_fnetnodes = fn;
708     return(fn);
709 }
710
711 static void freefn(struct dc_fnetnode *fn)
712 {
713     if(fn->next != NULL)
714         fn->next->prev = fn->prev;
715     if(fn->prev != NULL)
716         fn->prev->next = fn->next;
717     if(fn == dc_fnetnodes)
718         dc_fnetnodes = fn->next;
719     if(fn->destroycb != NULL)
720         fn->destroycb(fn);
721     while(fn->peers != NULL)
722         delpeer(fn->peers);
723     while(fn->peerdata != NULL)
724     {
725         fn->peerdata->refcount = 0;
726         putdatum(fn, fn->peerdata);
727     }
728     if(fn->name != NULL)
729         free(fn->name);
730     if(fn->fnet != NULL)
731         free(fn->fnet);
732     free(fn);
733 }
734
735 struct dc_fnetnode *dc_findfnetnode(int id)
736 {
737     struct dc_fnetnode *fn;
738     
739     for(fn = dc_fnetnodes; fn != NULL; fn = fn->next)
740     {
741         if(fn->id == id)
742             break;
743     }
744     return(fn);
745 }
746
747 static struct dc_transfer *newtransfer(void)
748 {
749     struct dc_transfer *transfer;
750     
751     transfer = smalloc(sizeof(*transfer));
752     memset(transfer, 0, sizeof(*transfer));
753     transfer->id = -1;
754     transfer->peerid = transfer->peernick = transfer->path = NULL;
755     transfer->state = DC_TRNS_WAITING;
756     transfer->dir = DC_TRNSD_UNKNOWN;
757     transfer->size = -1;
758     transfer->curpos = -1;
759     transfer->destroycb = NULL;
760     transfer->udata = NULL;
761     transfer->next = dc_transfers;
762     transfer->prev = NULL;
763     if(dc_transfers != NULL)
764         dc_transfers->prev = transfer;
765     dc_transfers = transfer;
766     return(transfer);
767 }
768
769 static void freetransfer(struct dc_transfer *transfer)
770 {
771     if(transfer->next != NULL)
772         transfer->next->prev = transfer->prev;
773     if(transfer->prev != NULL)
774         transfer->prev->next = transfer->next;
775     if(transfer == dc_transfers)
776         dc_transfers = transfer->next;
777     if(transfer->destroycb != NULL)
778         transfer->destroycb(transfer);
779     if(transfer->peerid != NULL)
780         free(transfer->peerid);
781     if(transfer->peernick != NULL)
782         free(transfer->peernick);
783     if(transfer->path != NULL)
784         free(transfer->path);
785     free(transfer);
786 }
787
788 struct dc_transfer *dc_findtransfer(int id)
789 {
790     struct dc_transfer *transfer;
791     
792     for(transfer = dc_transfers; transfer != NULL; transfer = transfer->next)
793     {
794         if(transfer->id == id)
795             break;
796     }
797     return(transfer);
798 }
799
800 static int getfnlistcallback(struct dc_response *resp)
801 {
802     struct dc_intresp *ires;
803     struct gencbdata *data;
804     struct dc_fnetnode *fn, *next;
805     
806     data = resp->data;
807     if(resp->code == 200)
808     {
809         for(fn = dc_fnetnodes; fn != NULL; fn = fn->next)
810             fn->found = 0;
811         while((ires = dc_interpret(resp)) != NULL)
812         {
813             if((fn = dc_findfnetnode(ires->argv[0].val.num)) != NULL)
814             {
815                 fn->found = 1;
816                 if(fn->fnet != NULL)
817                     free(fn->fnet);
818                 fn->fnet = swcsdup(ires->argv[1].val.str);
819                 if(fn->name != NULL)
820                     free(fn->name);
821                 fn->name = swcsdup(ires->argv[2].val.str);
822                 fn->numusers = ires->argv[3].val.num;
823                 fn->state = ires->argv[4].val.num;
824                 if(fn->pubid != NULL)
825                     free(fn->pubid);
826                 fn->pubid = swcsdup(ires->argv[5].val.str);
827             } else {
828                 fn = newfn();
829                 fn->id = ires->argv[0].val.num;
830                 fn->fnet = swcsdup(ires->argv[1].val.str);
831                 fn->name = swcsdup(ires->argv[2].val.str);
832                 fn->numusers = ires->argv[3].val.num;
833                 fn->state = ires->argv[4].val.num;
834                 fn->pubid = swcsdup(ires->argv[5].val.str);
835                 fn->found = 1;
836             }
837             dc_freeires(ires);
838         }
839         for(fn = dc_fnetnodes; fn != NULL; fn = next)
840         {
841             next = fn->next;
842             if(!fn->found)
843                 freefn(fn);
844         }
845         data->callback(200, data->data);
846         free(resp->data);
847     } else if(resp->code == 201) {
848         while(dc_fnetnodes != NULL)
849             freefn(dc_fnetnodes);
850         data->callback(201, data->data);
851         free(resp->data);
852     } else if(resp->code == 502) {
853         while(dc_fnetnodes != NULL)
854             freefn(dc_fnetnodes);
855         data->callback(502, data->data);
856         free(resp->data);
857     }
858     return(1);
859 }
860
861 static int gettrlistcallback(struct dc_response *resp)
862 {
863     struct dc_intresp *ires;
864     struct gencbdata *data;
865     struct dc_transfer *transfer, *next;
866     
867     data = resp->data;
868     if(resp->code == 200)
869     {
870         for(transfer = dc_transfers; transfer != NULL; transfer = transfer->next)
871             transfer->found = 0;
872         while((ires = dc_interpret(resp)) != NULL)
873         {
874             if((transfer = dc_findtransfer(ires->argv[0].val.num)) != NULL)
875             {
876                 transfer->found = 1;
877                 if((transfer->path == NULL) || wcscmp(transfer->path, ires->argv[5].val.str))
878                 {
879                     if(transfer->path != NULL)
880                         free(transfer->path);
881                     transfer->path = swcsdup(ires->argv[5].val.str);
882                 }
883                 if((transfer->peerid == NULL) || wcscmp(transfer->peerid, ires->argv[3].val.str))
884                 {
885                     if(transfer->peerid != NULL)
886                         free(transfer->peerid);
887                     transfer->peerid = swcsdup(ires->argv[3].val.str);
888                 }
889                 if((transfer->peernick == NULL) || wcscmp(transfer->peernick, ires->argv[4].val.str))
890                 {
891                     if(transfer->peernick != NULL)
892                         free(transfer->peernick);
893                     transfer->peernick = swcsdup(ires->argv[4].val.str);
894                 }
895                 transfer->dir = ires->argv[1].val.num;
896                 transfer->state = ires->argv[2].val.num;
897                 transfer->size = ires->argv[6].val.num;
898                 transfer->curpos = ires->argv[7].val.num;
899                 if(transfer->hash != NULL)
900                 {
901                     free(transfer->hash);
902                     transfer->hash = NULL;
903                 }
904                 if(wcslen(ires->argv[8].val.str) > 0)
905                     transfer->hash = swcsdup(ires->argv[8].val.str);
906             } else {
907                 transfer = newtransfer();
908                 transfer->id = ires->argv[0].val.num;
909                 transfer->dir = ires->argv[1].val.num;
910                 transfer->state = ires->argv[2].val.num;
911                 transfer->peerid = swcsdup(ires->argv[3].val.str);
912                 transfer->peernick = swcsdup(ires->argv[4].val.str);
913                 transfer->path = swcsdup(ires->argv[5].val.str);
914                 transfer->size = ires->argv[6].val.num;
915                 transfer->curpos = ires->argv[7].val.num;
916                 if(wcslen(ires->argv[8].val.str) > 0)
917                     transfer->hash = swcsdup(ires->argv[8].val.str);
918                 transfer->found = 1;
919             }
920             dc_freeires(ires);
921         }
922         for(transfer = dc_transfers; transfer != NULL; transfer = next)
923         {
924             next = transfer->next;
925             if(!transfer->found)
926                 freetransfer(transfer);
927         }
928         data->callback(200, data->data);
929         free(data);
930     } else if(resp->code == 201) {
931         while(dc_transfers != NULL)
932             freetransfer(dc_transfers);
933         data->callback(201, data->data);
934         free(data);
935     } else if(resp->code == 502) {
936         while(dc_transfers != NULL)
937             freetransfer(dc_transfers);
938         data->callback(502, data->data);
939         free(data);
940     }
941     return(1);
942 }
943
944 static int sortlist1(const struct dc_respline *l1, const struct dc_respline *l2)
945 {
946     return(wcscmp(l1->argv[1], l2->argv[1]));
947 }
948
949 static int sortlist2(const struct dc_fnetpeer **p1, const struct dc_fnetpeer **p2)
950 {
951     return(wcscmp((*p1)->id, (*p2)->id));
952 }
953
954 static void fillpeer(struct dc_fnetpeer *peer, struct dc_respline *r)
955 {
956     int i;
957     struct dc_fnetpeerdatum *datum;
958     
959     for(i = 3; i < r->argc; i += 2)
960     {
961         if((datum = finddatum(peer->fn, r->argv[i])) != NULL)
962         {
963             switch(datum->dt)
964             {
965             case DC_FNPD_INT:
966                 peersetnum(peer, datum->id, wcstol(r->argv[i + 1], NULL, 10));
967                 break;
968             case DC_FNPD_LL:
969                 peersetlnum(peer, datum->id, wcstoll(r->argv[i + 1], NULL, 10));
970                 break;
971             case DC_FNPD_STR:
972                 peersetstr(peer, datum->id, r->argv[i + 1]);
973                 break;
974             }
975         }
976     }
977 }
978
979 static int getpeerlistcallback(struct dc_response *resp)
980 {
981     int i, o, c;
982     struct dc_fnetnode *fn;
983     struct fnetcbdata *data;
984     struct dc_fnetpeer *peer;
985     struct dc_fnetpeer **plist;
986     size_t plistsize, plistdata;
987     
988     data = resp->data;
989     if((fn = dc_findfnetnode(data->fnid)) == NULL)
990     {
991         data->callback(NULL, -1, data->data);
992         free(data);
993         return(1);
994     }
995     if(resp->code == 200)
996     {
997         qsort(resp->rlines, resp->numlines, sizeof(*resp->rlines), (int (*)(const void *, const void *))sortlist1);
998         plist = NULL;
999         plistsize = plistdata = 0;
1000         for(i = 0, peer = fn->peers; peer != NULL; peer = peer->next)
1001             addtobuf(plist, peer);
1002         qsort(plist, plistdata, sizeof(*plist), (int (*)(const void *, const void *))sortlist2);
1003         i = o = 0;
1004         while(1)
1005         {
1006             if((i < resp->numlines) && (o < plistdata))
1007             {
1008                 c = wcscmp(resp->rlines[i].argv[1], plist[o]->id);
1009                 if(c < 0)
1010                 {
1011                     peer = addpeer(fn, resp->rlines[i].argv[1], resp->rlines[i].argv[2]);
1012                     fillpeer(peer, resp->rlines + i);
1013                     i++;
1014                 } else if(c > 0) {
1015                     delpeer(plist[o]);
1016                     o++;
1017                 } else {
1018                     fillpeer(plist[o], resp->rlines + i);
1019                     i++;
1020                     o++;
1021                 }
1022             } else if(i < resp->numlines) {
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(o < plistdata) {
1027                 delpeer(plist[o]);
1028                 o++;
1029             } else {
1030                 break;
1031             }
1032         }
1033         free(plist);
1034     } else if(resp->code == 201) {
1035         while(fn->peers != NULL)
1036             delpeer(fn->peers);
1037     }
1038     data->callback(fn, resp->code, data->data);
1039     free(data);
1040     return(1);
1041 }
1042
1043 static int getpalistcallback(struct dc_response *resp)
1044 {
1045     struct dc_fnetnode *fn;
1046     struct dc_intresp *ires;
1047     struct fnetcbdata *data;
1048     
1049     data = resp->data;
1050     if((fn = dc_findfnetnode(data->fnid)) == NULL)
1051     {
1052         data->callback(NULL, -1, data->data);
1053         free(data);
1054         return(1);
1055     }
1056     if(resp->code == 200)
1057     {
1058         while((ires = dc_interpret(resp)) != NULL)
1059         {
1060             adddatum(fn, ires->argv[0].val.str, ires->argv[1].val.num);
1061             dc_freeires(ires);
1062         }
1063         dc_queuecmd(getpeerlistcallback, data, L"lspeers", L"%%i", fn->id, NULL);
1064     } else if(resp->code == 201) {
1065         dc_queuecmd(getpeerlistcallback, data, L"lspeers", L"%%i", fn->id, NULL);
1066     } else {
1067         data->callback(fn, resp->code, data->data);
1068         free(data);
1069     }
1070     return(1);
1071 }
1072
1073 void dc_getfnlistasync(void (*callback)(int, void *), void *udata)
1074 {
1075     struct gencbdata *data;
1076     
1077     data = smalloc(sizeof(*data));
1078     data->callback = callback;
1079     data->data = udata;
1080     dc_queuecmd(getfnlistcallback, data, L"lsnodes", NULL);
1081 }
1082
1083 void dc_gettrlistasync(void (*callback)(int, void *), void *udata)
1084 {
1085     struct gencbdata *data;
1086     
1087     data = smalloc(sizeof(*data));
1088     data->callback = callback;
1089     data->data = udata;
1090     dc_queuecmd(gettrlistcallback, data, L"lstrans", NULL);
1091 }
1092
1093 void dc_getpeerlistasync(struct dc_fnetnode *fn, void (*callback)(struct dc_fnetnode *, int, void *), void *udata)
1094 {
1095     struct fnetcbdata *data;
1096     
1097     data = smalloc(sizeof(*data));
1098     data->callback = callback;
1099     data->fnid = fn->id;
1100     data->data = udata;
1101     dc_queuecmd(getpalistcallback, data, L"lspa", L"%%i", fn->id, NULL);
1102 }
1103
1104 void dc_uimisc_disconnected(void)
1105 {
1106     while(dc_fnetnodes != NULL)
1107         freefn(dc_fnetnodes);
1108     while(dc_transfers != NULL)
1109         freetransfer(dc_transfers);
1110 }
1111
1112 void dc_uimisc_handlenotify(struct dc_response *resp)
1113 {
1114     int i;
1115     struct dc_fnetnode *fn;
1116     struct dc_transfer *transfer;
1117     struct dc_fnetpeer *peer;
1118     struct dc_intresp *ires;
1119     
1120     if((ires = dc_interpret(resp)) == NULL)
1121         return;
1122     switch(resp->code)
1123     {
1124     case 601:
1125         if((fn = dc_findfnetnode(ires->argv[0].val.num)) != NULL)
1126             fn->state = ires->argv[1].val.num;
1127         break;
1128     case 602:
1129         if((fn = dc_findfnetnode(ires->argv[0].val.num)) != NULL)
1130         {
1131             if(fn->name != NULL)
1132                 free(fn->name);
1133             fn->name = swcsdup(ires->argv[1].val.str);
1134         }
1135         break;
1136     case 603:
1137         if((fn = dc_findfnetnode(ires->argv[0].val.num)) != NULL)
1138             freefn(fn);
1139         break;
1140     case 604:
1141         fn = newfn();
1142         fn->id = ires->argv[0].val.num;
1143         if(fn->fnet != NULL)
1144             free(fn->fnet);
1145         fn->fnet = swcsdup(ires->argv[1].val.str);
1146         fn->state = DC_FNN_STATE_SYN;
1147         fn->numusers = 0;
1148         break;
1149     case 605:
1150         if((fn = dc_findfnetnode(ires->argv[0].val.num)) != NULL)
1151             fn->numusers = ires->argv[1].val.num;
1152         break;
1153     case 610:
1154         transfer = newtransfer();
1155         transfer->id = ires->argv[0].val.num;
1156         transfer->dir = ires->argv[1].val.num;
1157         if(transfer->dir == DC_TRNSD_UP)
1158             transfer->state = DC_TRNS_HS;
1159         transfer->peerid = swcsdup(ires->argv[2].val.str);
1160         if(ires->argv[3].val.str[0])
1161             transfer->path = swcsdup(ires->argv[3].val.str);
1162         break;
1163     case 611:
1164         if((transfer = dc_findtransfer(ires->argv[0].val.num)) != NULL)
1165             transfer->state = ires->argv[1].val.num;
1166         break;
1167     case 612:
1168         if((transfer = dc_findtransfer(ires->argv[0].val.num)) != NULL)
1169         {
1170             if(transfer->peernick != NULL)
1171                 free(transfer->peernick);
1172             transfer->peernick = swcsdup(ires->argv[1].val.str);
1173         }
1174         break;
1175     case 613:
1176         if((transfer = dc_findtransfer(ires->argv[0].val.num)) != NULL)
1177             transfer->size = ires->argv[1].val.num;
1178         break;
1179     case 614:
1180         if((transfer = dc_findtransfer(ires->argv[0].val.num)) != NULL)
1181         {
1182             transfer->error = ires->argv[1].val.num;
1183             time(&transfer->errortime);
1184         }
1185         break;
1186     case 615:
1187         if((transfer = dc_findtransfer(ires->argv[0].val.num)) != NULL)
1188             transfer->curpos = ires->argv[1].val.num;
1189         break;
1190     case 616:
1191         if((transfer = dc_findtransfer(ires->argv[0].val.num)) != NULL)
1192         {
1193             if(transfer->path != NULL)
1194                 free(transfer->path);
1195             transfer->path = swcsdup(ires->argv[1].val.str);
1196         }
1197         break;
1198     case 617:
1199         if((transfer = dc_findtransfer(ires->argv[0].val.num)) != NULL)
1200             freetransfer(transfer);
1201         break;
1202     case 618:
1203         if((transfer = dc_findtransfer(ires->argv[0].val.num)) != NULL)
1204         {
1205             if(transfer->hash != NULL)
1206             {
1207                 free(transfer->hash);
1208                 transfer->hash = NULL;
1209             }
1210             if(wcslen(ires->argv[1].val.str) > 0)
1211                 transfer->hash = swcsdup(ires->argv[1].val.str);
1212         }
1213         break;
1214     case 630:
1215         if((fn = dc_findfnetnode(ires->argv[0].val.num)) != NULL)
1216         {
1217             if((peer = dc_fnetfindpeer(fn, ires->argv[1].val.str)) == NULL)
1218             {
1219                 peer = addpeer(fn, ires->argv[1].val.str, ires->argv[2].val.str);
1220                 if(fn->newpeercb != NULL)
1221                     fn->newpeercb(peer);
1222             }
1223         }
1224         break;
1225     case 631:
1226         if((fn = dc_findfnetnode(ires->argv[0].val.num)) != NULL)
1227         {
1228             if((peer = dc_fnetfindpeer(fn, ires->argv[1].val.str)) != NULL)
1229             {
1230                 if(fn->delpeercb != NULL)
1231                     fn->delpeercb(peer);
1232                 delpeer(peer);
1233             }
1234         }
1235         break;
1236     case 632:
1237         if((fn = dc_findfnetnode(ires->argv[0].val.num)) != NULL)
1238         {
1239             if((peer = dc_fnetfindpeer(fn, ires->argv[1].val.str)) != NULL)
1240             {
1241                 if(wcscmp(ires->argv[2].val.str, peer->nick))
1242                 {
1243                     free(peer->nick);
1244                     peer->nick = swcsdup(ires->argv[2].val.str);
1245                 }
1246                 for(i = 4; i < resp->rlines[0].argc; i += 3)
1247                 {
1248                     switch(wcstol(resp->rlines[0].argv[i + 1], NULL, 10))
1249                     {
1250                     case DC_FNPD_INT:
1251                         peersetnum(peer, resp->rlines[0].argv[i], wcstol(resp->rlines[0].argv[i + 2], NULL, 10));
1252                         break;
1253                     case DC_FNPD_LL:
1254                         peersetlnum(peer, resp->rlines[0].argv[i], wcstoll(resp->rlines[0].argv[i + 2], NULL, 10));
1255                         break;
1256                     case DC_FNPD_STR:
1257                         peersetstr(peer, resp->rlines[0].argv[i], resp->rlines[0].argv[i + 2]);
1258                         break;
1259                     }
1260                 }
1261                 if(fn->chpeercb != NULL)
1262                     fn->chpeercb(peer);
1263             }
1264         }
1265         break;
1266     default:
1267         break;
1268     }
1269     dc_freeires(ires);
1270     resp->curline = 0;
1271 }
1272
1273 /* Note the backspace handling - it's not as elegant as possible, but
1274  * it helps avoid the "box-of-toothpicks" syndrome when writing search
1275  * expressions manually. */
1276 wchar_t **dc_lexsexpr(wchar_t *sexpr)
1277 {
1278     wchar_t **ret;
1279     wchar_t *buf;
1280     size_t retsize, retdata, bufsize, bufdata;
1281     int state;
1282     
1283     ret = NULL;
1284     buf = NULL;
1285     retsize = retdata = bufsize = bufdata = 0;
1286     state = 0;
1287     while(*sexpr != L'\0')
1288     {
1289         switch(state)
1290         {
1291         case 0:
1292             if(!iswspace(*sexpr))
1293                 state = 1;
1294             else
1295                 sexpr++;
1296             break;
1297         case 1:
1298             if(iswspace(*sexpr))
1299             {
1300                 if(buf != NULL)
1301                 {
1302                     addtobuf(buf, L'\0');
1303                     addtobuf(ret, buf);
1304                     buf = NULL;
1305                     bufsize = bufdata = 0;
1306                 }
1307                 state = 0;
1308             } else if((*sexpr == L'(') ||
1309                       (*sexpr == L')') ||
1310                       (*sexpr == L'&') ||
1311                       (*sexpr == L'|') ||
1312                       (*sexpr == L'!')) {
1313                 if(buf != NULL)
1314                 {
1315                     addtobuf(buf, L'\0');
1316                     addtobuf(ret, buf);
1317                     buf = NULL;
1318                     bufsize = bufdata = 0;
1319                 }
1320                 addtobuf(buf, *sexpr);
1321                 addtobuf(buf, L'\0');
1322                 addtobuf(ret, buf);
1323                 buf = NULL;
1324                 bufsize = bufdata = 0;
1325                 sexpr++;
1326             } else if(*sexpr == L'\"') {
1327                 sexpr++;
1328                 state = 2;
1329             } else if(*sexpr == L'\\') {
1330                 sexpr++;
1331                 if(*sexpr == L'\0')
1332                 {
1333                     addtobuf(buf, *sexpr);
1334                 } else if((*sexpr == L'\\') || (*sexpr == L'\"')) {
1335                     addtobuf(buf, *sexpr);
1336                     sexpr++;
1337                 } else {
1338                     addtobuf(buf, L'\\');
1339                     addtobuf(buf, *sexpr);
1340                     sexpr++;
1341                 }
1342             } else {
1343                 addtobuf(buf, *(sexpr++));
1344             }
1345             break;
1346         case 2:
1347             if(*sexpr == L'\\')
1348             {
1349                 sexpr++;
1350                 if(*sexpr == L'\0')
1351                 {
1352                     addtobuf(buf, *sexpr);
1353                 } else if((*sexpr == L'\\') || (*sexpr == L'\"')) {
1354                     addtobuf(buf, *sexpr);
1355                     sexpr++;
1356                 } else {
1357                     addtobuf(buf, L'\\');
1358                     addtobuf(buf, *sexpr);
1359                     sexpr++;
1360                 }
1361             } else if(*sexpr == L'\"') {
1362                 state = 1;
1363                 sexpr++;
1364             } else {
1365                 addtobuf(buf, *(sexpr++));
1366             }
1367             break;
1368         }
1369     }
1370     if(buf != NULL)
1371     {
1372         addtobuf(buf, L'\0');
1373         addtobuf(ret, buf);
1374     }
1375     addtobuf(ret, NULL);
1376     return(ret);
1377 }
1378
1379 void dc_freewcsarr(wchar_t **arr)
1380 {
1381     wchar_t **buf;
1382     
1383     if(arr == NULL)
1384         return;
1385     for(buf = arr; *buf != NULL; buf++)
1386         free(*buf);
1387     free(arr);
1388 }