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