Add Unix authentication method.
[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);
700 }
701
702 static struct dc_fnetnode *newfn(void)
703 {
704     struct dc_fnetnode *fn;
705     
706     fn = smalloc(sizeof(*fn));
707     memset(fn, 0, sizeof(*fn));
708     fn->id = -1;
709     fn->name = NULL;
710     fn->fnet = NULL;
711     fn->state = fn->numusers = fn->found = 0;
712     fn->destroycb = NULL;
713     fn->udata = NULL;
714     fn->next = dc_fnetnodes;
715     fn->prev = NULL;
716     if(dc_fnetnodes != NULL)
717         dc_fnetnodes->prev = fn;
718     dc_fnetnodes = fn;
719     return(fn);
720 }
721
722 static void freefn(struct dc_fnetnode *fn)
723 {
724     if(fn->next != NULL)
725         fn->next->prev = fn->prev;
726     if(fn->prev != NULL)
727         fn->prev->next = fn->next;
728     if(fn == dc_fnetnodes)
729         dc_fnetnodes = fn->next;
730     if(fn->destroycb != NULL)
731         fn->destroycb(fn);
732     while(fn->peers != NULL)
733         delpeer(fn->peers);
734     while(fn->peerdata != NULL)
735     {
736         fn->peerdata->refcount = 0;
737         putdatum(fn, fn->peerdata);
738     }
739     if(fn->name != NULL)
740         free(fn->name);
741     if(fn->fnet != NULL)
742         free(fn->fnet);
743     free(fn);
744 }
745
746 struct dc_fnetnode *dc_findfnetnode(int id)
747 {
748     struct dc_fnetnode *fn;
749     
750     for(fn = dc_fnetnodes; fn != NULL; fn = fn->next)
751     {
752         if(fn->id == id)
753             break;
754     }
755     return(fn);
756 }
757
758 static struct dc_transfer *newtransfer(void)
759 {
760     struct dc_transfer *transfer;
761     
762     transfer = smalloc(sizeof(*transfer));
763     memset(transfer, 0, sizeof(*transfer));
764     transfer->id = -1;
765     transfer->peerid = transfer->peernick = transfer->path = NULL;
766     transfer->state = DC_TRNS_WAITING;
767     transfer->dir = DC_TRNSD_UNKNOWN;
768     transfer->size = -1;
769     transfer->curpos = -1;
770     transfer->destroycb = NULL;
771     transfer->udata = NULL;
772     transfer->next = dc_transfers;
773     transfer->prev = NULL;
774     if(dc_transfers != NULL)
775         dc_transfers->prev = transfer;
776     dc_transfers = transfer;
777     return(transfer);
778 }
779
780 static void freetransfer(struct dc_transfer *transfer)
781 {
782     if(transfer->next != NULL)
783         transfer->next->prev = transfer->prev;
784     if(transfer->prev != NULL)
785         transfer->prev->next = transfer->next;
786     if(transfer == dc_transfers)
787         dc_transfers = transfer->next;
788     if(transfer->destroycb != NULL)
789         transfer->destroycb(transfer);
790     if(transfer->peerid != NULL)
791         free(transfer->peerid);
792     if(transfer->peernick != NULL)
793         free(transfer->peernick);
794     if(transfer->path != NULL)
795         free(transfer->path);
796     free(transfer);
797 }
798
799 struct dc_transfer *dc_findtransfer(int id)
800 {
801     struct dc_transfer *transfer;
802     
803     for(transfer = dc_transfers; transfer != NULL; transfer = transfer->next)
804     {
805         if(transfer->id == id)
806             break;
807     }
808     return(transfer);
809 }
810
811 static int getfnlistcallback(struct dc_response *resp)
812 {
813     struct dc_intresp *ires;
814     struct gencbdata *data;
815     struct dc_fnetnode *fn, *next;
816     
817     data = resp->data;
818     if(resp->code == 200)
819     {
820         for(fn = dc_fnetnodes; fn != NULL; fn = fn->next)
821             fn->found = 0;
822         while((ires = dc_interpret(resp)) != NULL)
823         {
824             if((fn = dc_findfnetnode(ires->argv[0].val.num)) != NULL)
825             {
826                 fn->found = 1;
827                 if(fn->fnet != NULL)
828                     free(fn->fnet);
829                 fn->fnet = swcsdup(ires->argv[1].val.str);
830                 if(fn->name != NULL)
831                     free(fn->name);
832                 fn->name = swcsdup(ires->argv[2].val.str);
833                 fn->numusers = ires->argv[3].val.num;
834                 fn->state = ires->argv[4].val.num;
835                 if(fn->pubid != NULL)
836                     free(fn->pubid);
837                 fn->pubid = swcsdup(ires->argv[5].val.str);
838             } else {
839                 fn = newfn();
840                 fn->id = ires->argv[0].val.num;
841                 fn->fnet = swcsdup(ires->argv[1].val.str);
842                 fn->name = swcsdup(ires->argv[2].val.str);
843                 fn->numusers = ires->argv[3].val.num;
844                 fn->state = ires->argv[4].val.num;
845                 fn->pubid = swcsdup(ires->argv[5].val.str);
846                 fn->found = 1;
847             }
848             dc_freeires(ires);
849         }
850         for(fn = dc_fnetnodes; fn != NULL; fn = next)
851         {
852             next = fn->next;
853             if(!fn->found)
854                 freefn(fn);
855         }
856         data->callback(200, data->data);
857         free(resp->data);
858     } else if(resp->code == 201) {
859         while(dc_fnetnodes != NULL)
860             freefn(dc_fnetnodes);
861         data->callback(201, data->data);
862         free(resp->data);
863     } else if(resp->code == 502) {
864         while(dc_fnetnodes != NULL)
865             freefn(dc_fnetnodes);
866         data->callback(502, data->data);
867         free(resp->data);
868     }
869     return(1);
870 }
871
872 static int gettrlistcallback(struct dc_response *resp)
873 {
874     struct dc_intresp *ires;
875     struct gencbdata *data;
876     struct dc_transfer *transfer, *next;
877     
878     data = resp->data;
879     if(resp->code == 200)
880     {
881         for(transfer = dc_transfers; transfer != NULL; transfer = transfer->next)
882             transfer->found = 0;
883         while((ires = dc_interpret(resp)) != NULL)
884         {
885             if((transfer = dc_findtransfer(ires->argv[0].val.num)) != NULL)
886             {
887                 transfer->found = 1;
888                 if((transfer->path == NULL) || wcscmp(transfer->path, ires->argv[5].val.str))
889                 {
890                     if(transfer->path != NULL)
891                         free(transfer->path);
892                     transfer->path = swcsdup(ires->argv[5].val.str);
893                 }
894                 if((transfer->peerid == NULL) || wcscmp(transfer->peerid, ires->argv[3].val.str))
895                 {
896                     if(transfer->peerid != NULL)
897                         free(transfer->peerid);
898                     transfer->peerid = swcsdup(ires->argv[3].val.str);
899                 }
900                 if((transfer->peernick == NULL) || wcscmp(transfer->peernick, ires->argv[4].val.str))
901                 {
902                     if(transfer->peernick != NULL)
903                         free(transfer->peernick);
904                     transfer->peernick = swcsdup(ires->argv[4].val.str);
905                 }
906                 transfer->dir = ires->argv[1].val.num;
907                 transfer->state = ires->argv[2].val.num;
908                 transfer->size = ires->argv[6].val.num;
909                 transfer->curpos = ires->argv[7].val.num;
910                 if(transfer->hash != NULL)
911                 {
912                     free(transfer->hash);
913                     transfer->hash = NULL;
914                 }
915                 if(wcslen(ires->argv[8].val.str) > 0)
916                     transfer->hash = swcsdup(ires->argv[8].val.str);
917             } else {
918                 transfer = newtransfer();
919                 transfer->id = ires->argv[0].val.num;
920                 transfer->dir = ires->argv[1].val.num;
921                 transfer->state = ires->argv[2].val.num;
922                 transfer->peerid = swcsdup(ires->argv[3].val.str);
923                 transfer->peernick = swcsdup(ires->argv[4].val.str);
924                 transfer->path = swcsdup(ires->argv[5].val.str);
925                 transfer->size = ires->argv[6].val.num;
926                 transfer->curpos = ires->argv[7].val.num;
927                 if(wcslen(ires->argv[8].val.str) > 0)
928                     transfer->hash = swcsdup(ires->argv[8].val.str);
929                 transfer->found = 1;
930             }
931             dc_freeires(ires);
932         }
933         for(transfer = dc_transfers; transfer != NULL; transfer = next)
934         {
935             next = transfer->next;
936             if(!transfer->found)
937                 freetransfer(transfer);
938         }
939         data->callback(200, data->data);
940         free(data);
941     } else if(resp->code == 201) {
942         while(dc_transfers != NULL)
943             freetransfer(dc_transfers);
944         data->callback(201, data->data);
945         free(data);
946     } else if(resp->code == 502) {
947         while(dc_transfers != NULL)
948             freetransfer(dc_transfers);
949         data->callback(502, data->data);
950         free(data);
951     }
952     return(1);
953 }
954
955 static int sortlist1(const struct dc_respline *l1, const struct dc_respline *l2)
956 {
957     return(wcscmp(l1->argv[1], l2->argv[1]));
958 }
959
960 static int sortlist2(const struct dc_fnetpeer **p1, const struct dc_fnetpeer **p2)
961 {
962     return(wcscmp((*p1)->id, (*p2)->id));
963 }
964
965 static void fillpeer(struct dc_fnetpeer *peer, struct dc_respline *r)
966 {
967     int i;
968     struct dc_fnetpeerdatum *datum;
969     
970     for(i = 3; i < r->argc; i += 2)
971     {
972         if((datum = finddatum(peer->fn, r->argv[i])) != NULL)
973         {
974             switch(datum->dt)
975             {
976             case DC_FNPD_INT:
977                 peersetnum(peer, datum->id, wcstol(r->argv[i + 1], NULL, 10));
978                 break;
979             case DC_FNPD_LL:
980                 peersetlnum(peer, datum->id, wcstoll(r->argv[i + 1], NULL, 10));
981                 break;
982             case DC_FNPD_STR:
983                 peersetstr(peer, datum->id, r->argv[i + 1]);
984                 break;
985             }
986         }
987     }
988 }
989
990 static int getpeerlistcallback(struct dc_response *resp)
991 {
992     int i, o, c;
993     struct dc_fnetnode *fn;
994     struct fnetcbdata *data;
995     struct dc_fnetpeer *peer;
996     struct dc_fnetpeer **plist;
997     size_t plistsize, plistdata;
998     
999     data = resp->data;
1000     if((fn = dc_findfnetnode(data->fnid)) == NULL)
1001     {
1002         data->callback(NULL, -1, data->data);
1003         free(data);
1004         return(1);
1005     }
1006     if(resp->code == 200)
1007     {
1008         qsort(resp->rlines, resp->numlines, sizeof(*resp->rlines), (int (*)(const void *, const void *))sortlist1);
1009         plist = NULL;
1010         plistsize = plistdata = 0;
1011         for(i = 0, peer = fn->peers; peer != NULL; peer = peer->next)
1012             addtobuf(plist, peer);
1013         qsort(plist, plistdata, sizeof(*plist), (int (*)(const void *, const void *))sortlist2);
1014         i = o = 0;
1015         while(1)
1016         {
1017             if((i < resp->numlines) && (o < plistdata))
1018             {
1019                 c = wcscmp(resp->rlines[i].argv[1], plist[o]->id);
1020                 if(c < 0)
1021                 {
1022                     peer = addpeer(fn, resp->rlines[i].argv[1], resp->rlines[i].argv[2]);
1023                     fillpeer(peer, resp->rlines + i);
1024                     i++;
1025                 } else if(c > 0) {
1026                     delpeer(plist[o]);
1027                     o++;
1028                 } else {
1029                     fillpeer(plist[o], resp->rlines + i);
1030                     i++;
1031                     o++;
1032                 }
1033             } else if(i < resp->numlines) {
1034                 peer = addpeer(fn, resp->rlines[i].argv[1], resp->rlines[i].argv[2]);
1035                 fillpeer(peer, resp->rlines + i);
1036                 i++;
1037             } else if(o < plistdata) {
1038                 delpeer(plist[o]);
1039                 o++;
1040             } else {
1041                 break;
1042             }
1043         }
1044         free(plist);
1045     } else if(resp->code == 201) {
1046         while(fn->peers != NULL)
1047             delpeer(fn->peers);
1048     }
1049     data->callback(fn, resp->code, data->data);
1050     free(data);
1051     return(1);
1052 }
1053
1054 static int getpalistcallback(struct dc_response *resp)
1055 {
1056     struct dc_fnetnode *fn;
1057     struct dc_intresp *ires;
1058     struct fnetcbdata *data;
1059     
1060     data = resp->data;
1061     if((fn = dc_findfnetnode(data->fnid)) == NULL)
1062     {
1063         data->callback(NULL, -1, data->data);
1064         free(data);
1065         return(1);
1066     }
1067     if(resp->code == 200)
1068     {
1069         while((ires = dc_interpret(resp)) != NULL)
1070         {
1071             adddatum(fn, ires->argv[0].val.str, ires->argv[1].val.num);
1072             dc_freeires(ires);
1073         }
1074         dc_queuecmd(getpeerlistcallback, data, L"lspeers", L"%%i", fn->id, NULL);
1075     } else if(resp->code == 201) {
1076         dc_queuecmd(getpeerlistcallback, data, L"lspeers", L"%%i", fn->id, NULL);
1077     } else {
1078         data->callback(fn, resp->code, data->data);
1079         free(data);
1080     }
1081     return(1);
1082 }
1083
1084 void dc_getfnlistasync(void (*callback)(int, void *), void *udata)
1085 {
1086     struct gencbdata *data;
1087     
1088     data = smalloc(sizeof(*data));
1089     data->callback = callback;
1090     data->data = udata;
1091     dc_queuecmd(getfnlistcallback, data, L"lsnodes", NULL);
1092 }
1093
1094 void dc_gettrlistasync(void (*callback)(int, void *), void *udata)
1095 {
1096     struct gencbdata *data;
1097     
1098     data = smalloc(sizeof(*data));
1099     data->callback = callback;
1100     data->data = udata;
1101     dc_queuecmd(gettrlistcallback, data, L"lstrans", NULL);
1102 }
1103
1104 void dc_getpeerlistasync(struct dc_fnetnode *fn, void (*callback)(struct dc_fnetnode *, int, void *), void *udata)
1105 {
1106     struct fnetcbdata *data;
1107     
1108     data = smalloc(sizeof(*data));
1109     data->callback = callback;
1110     data->fnid = fn->id;
1111     data->data = udata;
1112     dc_queuecmd(getpalistcallback, data, L"lspa", L"%%i", fn->id, NULL);
1113 }
1114
1115 void dc_uimisc_disconnected(void)
1116 {
1117     while(dc_fnetnodes != NULL)
1118         freefn(dc_fnetnodes);
1119     while(dc_transfers != NULL)
1120         freetransfer(dc_transfers);
1121 }
1122
1123 void dc_uimisc_handlenotify(struct dc_response *resp)
1124 {
1125     int i;
1126     struct dc_fnetnode *fn;
1127     struct dc_transfer *transfer;
1128     struct dc_fnetpeer *peer;
1129     struct dc_intresp *ires;
1130     
1131     if((ires = dc_interpret(resp)) == NULL)
1132         return;
1133     switch(resp->code)
1134     {
1135     case 601:
1136         if((fn = dc_findfnetnode(ires->argv[0].val.num)) != NULL)
1137             fn->state = ires->argv[1].val.num;
1138         break;
1139     case 602:
1140         if((fn = dc_findfnetnode(ires->argv[0].val.num)) != NULL)
1141         {
1142             if(fn->name != NULL)
1143                 free(fn->name);
1144             fn->name = swcsdup(ires->argv[1].val.str);
1145         }
1146         break;
1147     case 603:
1148         if((fn = dc_findfnetnode(ires->argv[0].val.num)) != NULL)
1149             freefn(fn);
1150         break;
1151     case 604:
1152         fn = newfn();
1153         fn->id = ires->argv[0].val.num;
1154         if(fn->fnet != NULL)
1155             free(fn->fnet);
1156         fn->fnet = swcsdup(ires->argv[1].val.str);
1157         fn->state = DC_FNN_STATE_SYN;
1158         fn->numusers = 0;
1159         break;
1160     case 605:
1161         if((fn = dc_findfnetnode(ires->argv[0].val.num)) != NULL)
1162             fn->numusers = ires->argv[1].val.num;
1163         break;
1164     case 610:
1165         transfer = newtransfer();
1166         transfer->id = ires->argv[0].val.num;
1167         transfer->dir = ires->argv[1].val.num;
1168         if(transfer->dir == DC_TRNSD_UP)
1169             transfer->state = DC_TRNS_HS;
1170         transfer->peerid = swcsdup(ires->argv[2].val.str);
1171         if(ires->argv[3].val.str[0])
1172             transfer->path = swcsdup(ires->argv[3].val.str);
1173         break;
1174     case 611:
1175         if((transfer = dc_findtransfer(ires->argv[0].val.num)) != NULL)
1176             transfer->state = ires->argv[1].val.num;
1177         break;
1178     case 612:
1179         if((transfer = dc_findtransfer(ires->argv[0].val.num)) != NULL)
1180         {
1181             if(transfer->peernick != NULL)
1182                 free(transfer->peernick);
1183             transfer->peernick = swcsdup(ires->argv[1].val.str);
1184         }
1185         break;
1186     case 613:
1187         if((transfer = dc_findtransfer(ires->argv[0].val.num)) != NULL)
1188             transfer->size = ires->argv[1].val.num;
1189         break;
1190     case 614:
1191         if((transfer = dc_findtransfer(ires->argv[0].val.num)) != NULL)
1192         {
1193             transfer->error = ires->argv[1].val.num;
1194             time(&transfer->errortime);
1195         }
1196         break;
1197     case 615:
1198         if((transfer = dc_findtransfer(ires->argv[0].val.num)) != NULL)
1199             transfer->curpos = ires->argv[1].val.num;
1200         break;
1201     case 616:
1202         if((transfer = dc_findtransfer(ires->argv[0].val.num)) != NULL)
1203         {
1204             if(transfer->path != NULL)
1205                 free(transfer->path);
1206             transfer->path = swcsdup(ires->argv[1].val.str);
1207         }
1208         break;
1209     case 617:
1210         if((transfer = dc_findtransfer(ires->argv[0].val.num)) != NULL)
1211             freetransfer(transfer);
1212         break;
1213     case 618:
1214         if((transfer = dc_findtransfer(ires->argv[0].val.num)) != NULL)
1215         {
1216             if(transfer->hash != NULL)
1217             {
1218                 free(transfer->hash);
1219                 transfer->hash = NULL;
1220             }
1221             if(wcslen(ires->argv[1].val.str) > 0)
1222                 transfer->hash = swcsdup(ires->argv[1].val.str);
1223         }
1224         break;
1225     case 630:
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                 peer = addpeer(fn, ires->argv[1].val.str, ires->argv[2].val.str);
1231                 if(fn->newpeercb != NULL)
1232                     fn->newpeercb(peer);
1233             }
1234         }
1235         break;
1236     case 631:
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(fn->delpeercb != NULL)
1242                     fn->delpeercb(peer);
1243                 delpeer(peer);
1244             }
1245         }
1246         break;
1247     case 632:
1248         if((fn = dc_findfnetnode(ires->argv[0].val.num)) != NULL)
1249         {
1250             if((peer = dc_fnetfindpeer(fn, ires->argv[1].val.str)) != NULL)
1251             {
1252                 if(wcscmp(ires->argv[2].val.str, peer->nick))
1253                 {
1254                     free(peer->nick);
1255                     peer->nick = swcsdup(ires->argv[2].val.str);
1256                 }
1257                 for(i = 4; i < resp->rlines[0].argc; i += 3)
1258                 {
1259                     switch(wcstol(resp->rlines[0].argv[i + 1], NULL, 10))
1260                     {
1261                     case DC_FNPD_INT:
1262                         peersetnum(peer, resp->rlines[0].argv[i], wcstol(resp->rlines[0].argv[i + 2], NULL, 10));
1263                         break;
1264                     case DC_FNPD_LL:
1265                         peersetlnum(peer, resp->rlines[0].argv[i], wcstoll(resp->rlines[0].argv[i + 2], NULL, 10));
1266                         break;
1267                     case DC_FNPD_STR:
1268                         peersetstr(peer, resp->rlines[0].argv[i], resp->rlines[0].argv[i + 2]);
1269                         break;
1270                     }
1271                 }
1272                 if(fn->chpeercb != NULL)
1273                     fn->chpeercb(peer);
1274             }
1275         }
1276         break;
1277     default:
1278         break;
1279     }
1280     dc_freeires(ires);
1281     resp->curline = 0;
1282 }
1283
1284 /* Note the backspace handling - it's not as elegant as possible, but
1285  * it helps avoid the "box-of-toothpicks" syndrome when writing search
1286  * expressions manually. */
1287 wchar_t **dc_lexsexpr(wchar_t *sexpr)
1288 {
1289     wchar_t **ret;
1290     wchar_t *buf;
1291     size_t retsize, retdata, bufsize, bufdata;
1292     int state;
1293     
1294     ret = NULL;
1295     buf = NULL;
1296     retsize = retdata = bufsize = bufdata = 0;
1297     state = 0;
1298     while(*sexpr != L'\0')
1299     {
1300         switch(state)
1301         {
1302         case 0:
1303             if(!iswspace(*sexpr))
1304                 state = 1;
1305             else
1306                 sexpr++;
1307             break;
1308         case 1:
1309             if(iswspace(*sexpr))
1310             {
1311                 if(buf != NULL)
1312                 {
1313                     addtobuf(buf, L'\0');
1314                     addtobuf(ret, buf);
1315                     buf = NULL;
1316                     bufsize = bufdata = 0;
1317                 }
1318                 state = 0;
1319             } else if((*sexpr == L'(') ||
1320                       (*sexpr == L')') ||
1321                       (*sexpr == L'&') ||
1322                       (*sexpr == L'|') ||
1323                       (*sexpr == L'!')) {
1324                 if(buf != NULL)
1325                 {
1326                     addtobuf(buf, L'\0');
1327                     addtobuf(ret, buf);
1328                     buf = NULL;
1329                     bufsize = bufdata = 0;
1330                 }
1331                 addtobuf(buf, *sexpr);
1332                 addtobuf(buf, L'\0');
1333                 addtobuf(ret, buf);
1334                 buf = NULL;
1335                 bufsize = bufdata = 0;
1336                 sexpr++;
1337             } else if(*sexpr == L'\"') {
1338                 sexpr++;
1339                 state = 2;
1340             } else if(*sexpr == L'\\') {
1341                 sexpr++;
1342                 if(*sexpr == L'\0')
1343                 {
1344                     addtobuf(buf, *sexpr);
1345                 } else if((*sexpr == L'\\') || (*sexpr == L'\"')) {
1346                     addtobuf(buf, *sexpr);
1347                     sexpr++;
1348                 } else {
1349                     addtobuf(buf, L'\\');
1350                     addtobuf(buf, *sexpr);
1351                     sexpr++;
1352                 }
1353             } else {
1354                 addtobuf(buf, *(sexpr++));
1355             }
1356             break;
1357         case 2:
1358             if(*sexpr == L'\\')
1359             {
1360                 sexpr++;
1361                 if(*sexpr == L'\0')
1362                 {
1363                     addtobuf(buf, *sexpr);
1364                 } else if((*sexpr == L'\\') || (*sexpr == L'\"')) {
1365                     addtobuf(buf, *sexpr);
1366                     sexpr++;
1367                 } else {
1368                     addtobuf(buf, L'\\');
1369                     addtobuf(buf, *sexpr);
1370                     sexpr++;
1371                 }
1372             } else if(*sexpr == L'\"') {
1373                 state = 1;
1374                 sexpr++;
1375             } else {
1376                 addtobuf(buf, *(sexpr++));
1377             }
1378             break;
1379         }
1380     }
1381     if(buf != NULL)
1382     {
1383         addtobuf(buf, L'\0');
1384         addtobuf(ret, buf);
1385     }
1386     addtobuf(ret, NULL);
1387     return(ret);
1388 }
1389
1390 void dc_freewcsarr(wchar_t **arr)
1391 {
1392     wchar_t **buf;
1393     
1394     if(arr == NULL)
1395         return;
1396     for(buf = arr; *buf != NULL; buf++)
1397         free(*buf);
1398     free(arr);
1399 }