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