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