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