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