Add trivial HM decoder
[doldaconnect.git] / lib / uimisc.c
CommitLineData
d3372da9 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 <wchar.h>
22#include <wctype.h>
23#include <pwd.h>
24#include <string.h>
25#include <malloc.h>
26#include <stdio.h>
27
28#ifdef HAVE_CONFIG_H
29#include <config.h>
30#endif
31#include <doldaconnect/uilib.h>
32#include <doldaconnect/uimisc.h>
33#include <doldaconnect/utils.h>
34
35#ifdef HAVE_KRB5
36#include <krb5.h>
37#include <com_err.h>
38#endif
39
40struct logindata;
41
42struct authmech
43{
44 wchar_t *name;
45 void (*process)(struct dc_response *resp, struct logindata *data);
46 int (*init)(struct logindata *data);
47 void (*release)(struct logindata *data);
48};
49
50struct logindata
51{
52 int (*conv)(int type, wchar_t *text, char **resp, void *data);
53 void (*callback)(int err, wchar_t *reason, void *data);
54 char *username;
55 int freeusername;
56 int useauthless;
57 void *data;
58 void *mechdata;
59 struct authmech *mech;
60};
61
62struct gencbdata
63{
64 void (*callback)(int resp, void *data);
65 void *data;
66};
67
68struct dc_fnetnode *dc_fnetnodes = NULL;
69struct dc_transfer *dc_transfers = NULL;
70
71static void freelogindata(struct logindata *data)
72{
73 if((data->mech != NULL) && (data->mech->release != NULL))
74 data->mech->release(data);
75 if(data->freeusername)
76 free(data->username);
77 free(data);
78}
79
80static int logincallback(struct dc_response *resp);
81
82static void process_authless(struct dc_response *resp, struct logindata *data)
83{
84 switch(resp->code)
85 {
86 case 200:
87 data->callback(DC_LOGIN_ERR_SUCCESS, NULL, data->data);
88 freelogindata(data);
89 break;
90/*
91 case 303:
92 case 304:
93 if((ires = dc_interpret(resp)) != NULL)
94 {
95 buf = NULL;
96 if(data->conv((resp->code == 303)?DC_LOGIN_CONV_INFO:DC_LOGIN_CONV_ERROR, ires->argv[0].val.str, &buf, data->data))
97 {
98 data->callback(DC_LOGIN_ERR_CONV, NULL, data->data);
99 freelogindata(data);
100 } else {
101 dc_queuecmd(logincallback, data, L"pass", L"", NULL);
102 }
103 if(buf != NULL)
104 {
105 memset(buf, 0, strlen(buf));
106 free(buf);
107 }
108 dc_freeires(ires);
109 }
110 break;
111*/
112 case 505:
113 data->callback(DC_LOGIN_ERR_SERVER, NULL, data->data);
114 freelogindata(data);
115 break;
116 case 506:
117 data->callback(DC_LOGIN_ERR_AUTHFAIL, NULL, data->data);
118 freelogindata(data);
119 break;
120 default:
121 data->callback(DC_LOGIN_ERR_USER, NULL, data->data);
122 freelogindata(data);
123 break;
124 }
125}
126
127static void process_pam(struct dc_response *resp, struct logindata *data)
128{
129 struct dc_intresp *ires;
130 int convtype;
131 char *buf;
132
133 switch(resp->code)
134 {
135 case 200:
136 data->callback(DC_LOGIN_ERR_SUCCESS, NULL, data->data);
137 freelogindata(data);
138 break;
139 case 301:
140 case 302:
141 case 303:
142 case 304:
143 if(resp->code == 301)
144 convtype = DC_LOGIN_CONV_NOECHO;
145 else if(resp->code == 302)
146 convtype = DC_LOGIN_CONV_ECHO;
147 else if(resp->code == 303)
148 convtype = DC_LOGIN_CONV_INFO;
149 else if(resp->code == 304)
150 convtype = DC_LOGIN_CONV_ERROR;
151 if((ires = dc_interpret(resp)) != NULL)
152 {
153 buf = NULL;
154 if(data->conv(convtype, ires->argv[0].val.str, &buf, data->data))
155 {
156 data->callback(DC_LOGIN_ERR_CONV, NULL, data->data);
157 freelogindata(data);
158 } else {
159 dc_queuecmd(logincallback, data, L"pass", L"%%s", buf, NULL);
160 }
161 if(buf != NULL)
162 {
163 memset(buf, 0, strlen(buf));
164 free(buf);
165 }
166 dc_freeires(ires);
167 }
168 break;
169 case 505:
170 data->callback(DC_LOGIN_ERR_SERVER, NULL, data->data);
171 freelogindata(data);
172 break;
173 case 506:
174 data->callback(DC_LOGIN_ERR_AUTHFAIL, NULL, data->data);
175 freelogindata(data);
176 break;
177 default:
178 data->callback(DC_LOGIN_ERR_USER, NULL, data->data);
179 freelogindata(data);
180 break;
181 }
182}
183
184#ifdef HAVE_KRB5
185struct krb5data
186{
187 int state;
188 krb5_context context;
189 krb5_principal sprinc, myprinc;
190 krb5_ccache ccache;
191 krb5_auth_context authcon;
192 krb5_data reqbuf;
193 krb5_creds *servcreds;
194 int valid, fwd, fwded;
195};
196
197static char *hexencode(char *data, size_t datalen)
198{
199 char *buf, this;
200 size_t bufsize, bufdata;
201 int dig;
202
203 buf = NULL;
204 bufsize = bufdata = 0;
205 for(; datalen > 0; datalen--, data++)
206 {
207 dig = (*data & 0xF0) >> 4;
208 if(dig > 9)
209 this = 'A' + dig - 10;
210 else
211 this = dig + '0';
212 addtobuf(buf, this);
213 dig = *data & 0x0F;
214 if(dig > 9)
215 this = 'A' + dig - 10;
216 else
217 this = dig + '0';
218 addtobuf(buf, this);
219 }
220 addtobuf(buf, 0);
221 return(buf);
222}
223
224static char *hexdecode(char *data, size_t *len)
225{
226 char *buf, this;
227 size_t bufsize, bufdata;
228
229 buf = NULL;
230 bufsize = bufdata = 0;
231 for(; *data; data++)
232 {
233 if((*data >= 'A') && (*data <= 'F'))
234 {
235 this = (this & 0x0F) | ((*data - 'A' + 10) << 4);
236 } else if((*data >= '0') && (*data <= '9')) {
237 this = (this & 0x0F) | ((*data - '0') << 4);
238 } else {
239 if(buf != NULL)
240 free(buf);
241 return(NULL);
242 }
243 data++;
244 if(!*data)
245 {
246 if(buf != NULL)
247 free(buf);
248 return(NULL);
249 }
250 if((*data >= 'A') && (*data <= 'F'))
251 {
252 this = (this & 0xF0) | (*data - 'A' + 10);
253 } else if((*data >= '0') && (*data <= '9')) {
254 this = (this & 0xF0) | (*data - '0');
255 } else {
256 if(buf != NULL)
257 free(buf);
258 return(NULL);
259 }
260 addtobuf(buf, this);
261 }
262 addtobuf(buf, 0);
263 if(len != NULL)
264 *len = bufdata - 1;
265 return(buf);
266}
267
268static void process_krb5(struct dc_response *resp, struct logindata *data)
269{
270 int ret;
271 struct dc_intresp *ires;
272 struct krb5data *krb;
273 krb5_data k5d;
274 krb5_ap_rep_enc_part *repl;
275 char *buf;
276
277 krb = data->mechdata;
278 switch(resp->code)
279 {
280 case 200:
281 data->callback(DC_LOGIN_ERR_SUCCESS, NULL, data->data);
282 freelogindata(data);
283 break;
284 case 300:
285 switch(krb->state)
286 {
287 case 0:
288 buf = hexencode(krb->reqbuf.data, krb->reqbuf.length);
289 dc_queuecmd(logincallback, data, L"pass", L"%%s", buf, NULL);
290 free(buf);
291 krb->state = 1;
292 break;
293 case 1:
294 if((ires = dc_interpret(resp)) != NULL)
295 {
296 k5d.data = hexdecode(icswcstombs(ires->argv[0].val.str, NULL, NULL), &k5d.length);
297 if(!krb->valid)
298 {
299 if((ret = krb5_rd_rep(krb->context, krb->authcon, &k5d, &repl)) != 0)
300 {
301 data->callback(DC_LOGIN_ERR_SERVER, NULL, data->data);
302 freelogindata(data);
303 break;
304 }
305 /* XXX: Do I need to do something with this? */
306 krb->valid = 1;
307 krb5_free_ap_rep_enc_part(krb->context, repl);
308 }
309 if(krb->fwd && !krb->fwded)
310 {
311 if(krb->reqbuf.data != NULL)
312 free(krb->reqbuf.data);
313 krb->reqbuf.data = NULL;
314 if((ret = krb5_fwd_tgt_creds(krb->context, krb->authcon, NULL, krb->servcreds->client, krb->servcreds->server, 0, 1, &krb->reqbuf)) != 0)
315 {
316 fprintf(stderr, "krb5_fwd_tgt_creds reported an error: %s\n", error_message(ret));
317 dc_queuecmd(logincallback, data, L"pass", L"31", NULL);
318 krb->fwd = 0;
319 krb->state = 2;
320 } else {
321 dc_queuecmd(logincallback, data, L"pass", L"32", NULL);
322 krb->state = 0;
323 krb->fwded = 1;
324 }
325 } else {
326 dc_queuecmd(logincallback, data, L"pass", L"31", NULL);
327 krb->state = 2;
328 }
329 free(k5d.data);
330 dc_freeires(ires);
331 }
332 break;
333 default:
334 data->callback(DC_LOGIN_ERR_USER, NULL, data->data);
335 freelogindata(data);
336 break;
337 }
338 break;
339 case 505:
340 data->callback(DC_LOGIN_ERR_SERVER, NULL, data->data);
341 freelogindata(data);
342 break;
343 case 506:
344 data->callback(DC_LOGIN_ERR_AUTHFAIL, NULL, data->data);
345 freelogindata(data);
346 break;
347 default:
348 data->callback(DC_LOGIN_ERR_USER, NULL, data->data);
349 freelogindata(data);
350 break;
351 }
352}
353
354static int init_krb5(struct logindata *data)
355{
356 int ret;
357 struct krb5data *krb;
358 krb5_data cksum;
359 krb5_creds creds;
360
361 krb = smalloc(sizeof(*krb));
362 memset(krb, 0, sizeof(*krb));
363 krb->fwd = 1;
364 krb->fwded = 0;
365 data->mechdata = krb;
366 if((ret = krb5_init_context(&krb->context)) != 0)
367 {
368 fprintf(stderr, "krb5_init_context reported an error: %s\n", error_message(ret));
369 return(1);
370 }
371 if((ret = krb5_auth_con_init(krb->context, &krb->authcon)) != 0)
372 {
373 fprintf(stderr, "krb5_auth_con_init reported an error: %s\n", error_message(ret));
374 return(1);
375 }
376 krb5_auth_con_setflags(krb->context, krb->authcon, KRB5_AUTH_CONTEXT_DO_SEQUENCE);
377 if((ret = krb5_sname_to_principal(krb->context, dc_gethostname(), "doldacond", KRB5_NT_SRV_HST, &krb->sprinc)) != 0)
378 {
379 fprintf(stderr, "krb5_sname_to_principal reported an error: %s\n", error_message(ret));
380 return(1);
381 }
382 if((ret = krb5_cc_default(krb->context, &krb->ccache)) != 0)
383 {
384 fprintf(stderr, "krb5_cc_default reported an error: %s\n", error_message(ret));
385 return(1);
386 }
387 if((ret = krb5_cc_get_principal(krb->context, krb->ccache, &krb->myprinc)) != 0)
388 {
389 fprintf(stderr, "krb5_cc_default reported an error: %s\n", error_message(ret));
390 return(1);
391 }
392 memset(&creds, 0, sizeof(creds));
393 creds.client = krb->myprinc;
394 creds.server = krb->sprinc;
395 if((ret = krb5_get_credentials(krb->context, 0, krb->ccache, &creds, &krb->servcreds)) != 0)
396 {
397 fprintf(stderr, "krb5_get_credentials reported an error: %s\n", error_message(ret));
398 return(1);
399 }
400 /* WTF is this checksum stuff?! The Krb docs don't say a word about it! */
401 cksum.data = sstrdup(dc_gethostname());
402 cksum.length = strlen(cksum.data);
403 if((ret = krb5_mk_req_extended(krb->context, &krb->authcon, AP_OPTS_MUTUAL_REQUIRED, &cksum, krb->servcreds, &krb->reqbuf)) != 0)
404 {
405 fprintf(stderr, "krb5_mk_req_extended reported an error: %s\n", error_message(ret));
406 return(1);
407 }
408 free(cksum.data);
409 krb->state = 0;
410 return(0);
411}
412
413static void release_krb5(struct logindata *data)
414{
415 struct krb5data *krb;
416
417 if((krb = data->mechdata) == NULL)
418 return;
419 if(krb->servcreds != NULL)
420 krb5_free_creds(krb->context, krb->servcreds);
421 if(krb->reqbuf.data != NULL)
422 free(krb->reqbuf.data);
423 if(krb->sprinc != NULL)
424 krb5_free_principal(krb->context, krb->sprinc);
425 if(krb->myprinc != NULL)
426 krb5_free_principal(krb->context, krb->myprinc);
427 if(krb->ccache != NULL)
428 krb5_cc_close(krb->context, krb->ccache);
429 if(krb->authcon != NULL)
430 krb5_auth_con_free(krb->context, krb->authcon);
431 if(krb->context != NULL)
432 krb5_free_context(krb->context);
433 free(krb);
434}
435#endif
436
437/* Arranged in order of priority */
438static struct authmech authmechs[] =
439{
440#ifdef HAVE_KRB5
441 {
442 .name = L"krb5",
443 .process = process_krb5,
444 .init = init_krb5,
445 .release = release_krb5
446 },
447#endif
448 {
449 .name = L"authless",
450 .process = process_authless,
451 .init = NULL,
452 .release = NULL
453 },
454 {
455 .name = L"pam",
456 .process = process_pam,
457 .init = NULL,
458 .release = NULL
459 },
460 {
461 .name = NULL
462 }
463};
464
465static int builtinconv(int type, wchar_t *text, char **resp, void *data)
466{
467 char *buf, *pass;
468
469 if(isatty(0))
470 {
471 if((buf = icwcstombs(text, NULL)) == NULL)
472 return(1);
473 pass = getpass(buf);
474 free(buf);
475 *resp = sstrdup(pass);
476 memset(pass, 0, strlen(pass));
477 return(0);
478 }
479 return(1);
480}
481
482static int logincallback(struct dc_response *resp)
483{
484 int i;
485 struct dc_intresp *ires;
486 struct logindata *data;
487 int mech;
488 char *username;
489 struct passwd *pwent;
490 void *odata, *ndata;
491
492 data = resp->data;
493 if(!wcscmp(resp->cmdname, L"lsauth"))
494 {
495 if(resp->code == 201)
496 {
497 data->callback(DC_LOGIN_ERR_NOLOGIN, NULL, data->data);
498 freelogindata(data);
499 } else {
500 mech = -1;
501 while((ires = dc_interpret(resp)) != NULL)
502 {
503 if(!data->useauthless && !wcscmp(ires->argv[0].val.str, L"authless"))
504 {
505 dc_freeires(ires);
506 continue;
507 }
508 for(i = 0; authmechs[i].name != NULL; i++)
509 {
510 if(!wcscmp(authmechs[i].name, ires->argv[0].val.str) && ((i < mech) || (mech == -1)))
511 {
512 odata = data->mechdata;
513 data->mechdata = NULL;
514 if((authmechs[i].init != NULL) && authmechs[i].init(data))
515 {
516 if(authmechs[i].release != NULL)
517 authmechs[i].release(data);
518 data->mechdata = odata;
519 fprintf(stderr, "authentication mechanism %ls failed, trying further...\n", authmechs[i].name);
520 } else {
521 if((data->mech != NULL) && data->mech->release != NULL)
522 {
523 ndata = data->mechdata;
524 data->mechdata = odata;
525 data->mech->release(data);
526 data->mechdata = ndata;
527 }
528 mech = i;
529 data->mech = authmechs + i;
530 }
531 break;
532 }
533 }
534 dc_freeires(ires);
535 }
536 if(mech == -1)
537 {
538 data->callback(DC_LOGIN_ERR_NOLOGIN, NULL, data->data);
539 freelogindata(data);
540 } else {
541 if((username = data->username) == NULL)
542 {
543 if((pwent = getpwuid(getuid())) == NULL)
544 {
545 data->callback(DC_LOGIN_ERR_USER, NULL, data->data);
546 freelogindata(data);
547 return(1);
548 }
549 username = pwent->pw_name;
550 }
551 dc_queuecmd(logincallback, data, L"login", data->mech->name, L"%%s", username, NULL);
552 }
553 }
554 } else if(!wcscmp(resp->cmdname, L"login") || !wcscmp(resp->cmdname, L"pass")) {
555 data->mech->process(resp, data);
556 }
557 return(1);
558}
559
560void dc_loginasync(char *username, int useauthless, int (*conv)(int, wchar_t *, char **, void *), void (*callback)(int, wchar_t *, void *), void *udata)
561{
562 struct logindata *data;
563
564 data = smalloc(sizeof(*data));
565 if(conv == NULL)
566 conv = builtinconv;
567 data->conv = conv;
568 data->mech = NULL;
569 data->data = udata;
570 data->mechdata = NULL;
571 data->callback = callback;
572 data->useauthless = useauthless;
573 data->freeusername = 0;
574 if(username == NULL)
575 {
576 data->username = NULL;
577 } else {
578 data->username = sstrdup(username);
579 data->freeusername = 1;
580 }
581 dc_queuecmd(logincallback, data, L"lsauth", NULL);
582}
583
584static struct dc_fnetnode *newfn(void)
585{
586 struct dc_fnetnode *fn;
587
588 fn = smalloc(sizeof(*fn));
589 memset(fn, 0, sizeof(*fn));
590 fn->id = -1;
591 fn->name = NULL;
592 fn->fnet = NULL;
593 fn->state = fn->numusers = fn->found = 0;
594 fn->destroycb = NULL;
595 fn->udata = NULL;
596 fn->next = dc_fnetnodes;
597 fn->prev = NULL;
598 if(dc_fnetnodes != NULL)
599 dc_fnetnodes->prev = fn;
600 dc_fnetnodes = fn;
601 return(fn);
602}
603
604static void freefn(struct dc_fnetnode *fn)
605{
606 if(fn->next != NULL)
607 fn->next->prev = fn->prev;
608 if(fn->prev != NULL)
609 fn->prev->next = fn->next;
610 if(fn == dc_fnetnodes)
611 dc_fnetnodes = fn->next;
612 if(fn->destroycb != NULL)
613 fn->destroycb(fn);
614 if(fn->name != NULL)
615 free(fn->name);
616 if(fn->fnet != NULL)
617 free(fn->fnet);
618 free(fn);
619}
620
621struct dc_fnetnode *dc_findfnetnode(int id)
622{
623 struct dc_fnetnode *fn;
624
625 for(fn = dc_fnetnodes; fn != NULL; fn = fn->next)
626 {
627 if(fn->id == id)
628 break;
629 }
630 return(fn);
631}
632
633static struct dc_transfer *newtransfer(void)
634{
635 struct dc_transfer *transfer;
636
637 transfer = smalloc(sizeof(*transfer));
638 memset(transfer, 0, sizeof(*transfer));
639 transfer->id = -1;
640 transfer->peerid = transfer->peernick = transfer->path = NULL;
641 transfer->state = DC_TRNS_WAITING;
642 transfer->dir = DC_TRNSD_UNKNOWN;
643 transfer->size = -1;
644 transfer->curpos = -1;
645 transfer->destroycb = NULL;
646 transfer->udata = NULL;
647 transfer->next = dc_transfers;
648 transfer->prev = NULL;
649 if(dc_transfers != NULL)
650 dc_transfers->prev = transfer;
651 dc_transfers = transfer;
652 return(transfer);
653}
654
655static void freetransfer(struct dc_transfer *transfer)
656{
657 if(transfer->next != NULL)
658 transfer->next->prev = transfer->prev;
659 if(transfer->prev != NULL)
660 transfer->prev->next = transfer->next;
661 if(transfer == dc_transfers)
662 dc_transfers = transfer->next;
663 if(transfer->destroycb != NULL)
664 transfer->destroycb(transfer);
665 if(transfer->peerid != NULL)
666 free(transfer->peerid);
667 if(transfer->peernick != NULL)
668 free(transfer->peernick);
669 if(transfer->path != NULL)
670 free(transfer->path);
671 free(transfer);
672}
673
674struct dc_transfer *dc_findtransfer(int id)
675{
676 struct dc_transfer *transfer;
677
678 for(transfer = dc_transfers; transfer != NULL; transfer = transfer->next)
679 {
680 if(transfer->id == id)
681 break;
682 }
683 return(transfer);
684}
685
686static int getfnlistcallback(struct dc_response *resp)
687{
688 struct dc_intresp *ires;
689 struct gencbdata *data;
690 struct dc_fnetnode *fn, *next;
691
692 data = resp->data;
693 if(resp->code == 200)
694 {
695 for(fn = dc_fnetnodes; fn != NULL; fn = fn->next)
696 fn->found = 0;
697 while((ires = dc_interpret(resp)) != NULL)
698 {
699 if((fn = dc_findfnetnode(ires->argv[0].val.num)) != NULL)
700 {
701 fn->found = 1;
702 if(fn->fnet != NULL)
703 free(fn->fnet);
704 fn->fnet = swcsdup(ires->argv[1].val.str);
705 if(fn->name != NULL)
706 free(fn->name);
707 fn->name = swcsdup(ires->argv[2].val.str);
708 fn->numusers = ires->argv[3].val.num;
709 fn->state = ires->argv[4].val.num;
710 } else {
711 fn = newfn();
712 fn->id = ires->argv[0].val.num;
713 fn->fnet = swcsdup(ires->argv[1].val.str);
714 fn->name = swcsdup(ires->argv[2].val.str);
715 fn->numusers = ires->argv[3].val.num;
716 fn->state = ires->argv[4].val.num;
717 fn->found = 1;
718 }
719 dc_freeires(ires);
720 }
721 for(fn = dc_fnetnodes; fn != NULL; fn = next)
722 {
723 next = fn->next;
724 if(!fn->found)
725 freefn(fn);
726 }
727 data->callback(200, data->data);
728 free(resp->data);
729 } else if(resp->code == 201) {
730 while(dc_fnetnodes != NULL)
731 freefn(dc_fnetnodes);
732 data->callback(201, data->data);
733 free(resp->data);
734 } else if(resp->code == 502) {
735 while(dc_fnetnodes != NULL)
736 freefn(dc_fnetnodes);
737 data->callback(502, data->data);
738 free(resp->data);
739 }
740 return(1);
741}
742
743static int gettrlistcallback(struct dc_response *resp)
744{
745 struct dc_intresp *ires;
746 struct gencbdata *data;
747 struct dc_transfer *transfer, *next;
748
749 data = resp->data;
750 if(resp->code == 200)
751 {
752 for(transfer = dc_transfers; transfer != NULL; transfer = transfer->next)
753 transfer->found = 0;
754 while((ires = dc_interpret(resp)) != NULL)
755 {
756 if((transfer = dc_findtransfer(ires->argv[0].val.num)) != NULL)
757 {
758 transfer->found = 1;
759 if((transfer->path == NULL) || wcscmp(transfer->path, ires->argv[5].val.str))
760 {
761 if(transfer->path != NULL)
762 free(transfer->path);
763 transfer->path = swcsdup(ires->argv[5].val.str);
764 }
765 if((transfer->peerid == NULL) || wcscmp(transfer->peerid, ires->argv[3].val.str))
766 {
767 if(transfer->peerid != NULL)
768 free(transfer->peerid);
769 transfer->peerid = swcsdup(ires->argv[3].val.str);
770 }
771 if((transfer->peernick == NULL) || wcscmp(transfer->peernick, ires->argv[4].val.str))
772 {
773 if(transfer->peernick != NULL)
774 free(transfer->peernick);
775 transfer->peernick = swcsdup(ires->argv[4].val.str);
776 }
777 transfer->dir = ires->argv[1].val.num;
778 transfer->state = ires->argv[2].val.num;
779 transfer->size = ires->argv[6].val.num;
780 transfer->curpos = ires->argv[7].val.num;
781 } else {
782 transfer = newtransfer();
783 transfer->id = ires->argv[0].val.num;
784 transfer->dir = ires->argv[1].val.num;
785 transfer->state = ires->argv[2].val.num;
786 transfer->peerid = swcsdup(ires->argv[3].val.str);
787 transfer->peernick = swcsdup(ires->argv[4].val.str);
788 transfer->path = swcsdup(ires->argv[5].val.str);
789 transfer->size = ires->argv[6].val.num;
790 transfer->curpos = ires->argv[7].val.num;
791 transfer->found = 1;
792 }
793 dc_freeires(ires);
794 }
795 for(transfer = dc_transfers; transfer != NULL; transfer = next)
796 {
797 next = transfer->next;
798 if(!transfer->found)
799 freetransfer(transfer);
800 }
801 data->callback(200, data->data);
802 free(data);
803 } else if(resp->code == 201) {
804 while(dc_transfers != NULL)
805 freetransfer(dc_transfers);
806 data->callback(201, data->data);
807 free(data);
808 } else if(resp->code == 502) {
809 while(dc_transfers != NULL)
810 freetransfer(dc_transfers);
811 data->callback(502, data->data);
812 free(data);
813 }
814 return(1);
815}
816
817void dc_getfnlistasync(void (*callback)(int, void *), void *udata)
818{
819 struct gencbdata *data;
820
821 data = smalloc(sizeof(*data));
822 data->callback = callback;
823 data->data = udata;
824 dc_queuecmd(getfnlistcallback, data, L"lsnodes", NULL);
825}
826
827void dc_gettrlistasync(void (*callback)(int, void *), void *udata)
828{
829 struct gencbdata *data;
830
831 data = smalloc(sizeof(*data));
832 data->callback = callback;
833 data->data = udata;
834 dc_queuecmd(gettrlistcallback, data, L"lstrans", NULL);
835}
836
837void dc_uimisc_disconnected(void)
838{
839 while(dc_fnetnodes != NULL)
840 freefn(dc_fnetnodes);
841 while(dc_transfers != NULL)
842 freetransfer(dc_transfers);
843}
844
845void dc_uimisc_handlenotify(struct dc_response *resp)
846{
847 struct dc_fnetnode *fn;
848 struct dc_transfer *transfer;
849 struct dc_intresp *ires;
850
851 if((ires = dc_interpret(resp)) == NULL)
852 return;
853 switch(resp->code)
854 {
855 case 601:
856 if((fn = dc_findfnetnode(ires->argv[0].val.num)) != NULL)
857 fn->state = ires->argv[1].val.num;
858 break;
859 case 602:
860 if((fn = dc_findfnetnode(ires->argv[0].val.num)) != NULL)
861 {
862 if(fn->name != NULL)
863 free(fn->name);
864 fn->name = swcsdup(ires->argv[1].val.str);
865 }
866 break;
867 case 603:
868 if((fn = dc_findfnetnode(ires->argv[0].val.num)) != NULL)
869 freefn(fn);
870 break;
871 case 604:
872 fn = newfn();
873 fn->id = ires->argv[0].val.num;
874 if(fn->fnet != NULL)
875 free(fn->fnet);
876 fn->fnet = swcsdup(ires->argv[1].val.str);
877 fn->state = DC_FNN_STATE_SYN;
878 fn->numusers = 0;
879 break;
880 case 605:
881 if((fn = dc_findfnetnode(ires->argv[0].val.num)) != NULL)
882 fn->numusers = ires->argv[1].val.num;
883 break;
884 case 610:
885 transfer = newtransfer();
886 transfer->id = ires->argv[0].val.num;
887 transfer->dir = ires->argv[1].val.num;
888 if(transfer->dir == DC_TRNSD_UP)
889 transfer->state = DC_TRNS_HS;
890 transfer->peerid = swcsdup(ires->argv[2].val.str);
891 if(ires->argv[3].val.str[0])
892 transfer->path = swcsdup(ires->argv[3].val.str);
893 break;
894 case 611:
895 if((transfer = dc_findtransfer(ires->argv[0].val.num)) != NULL)
896 transfer->state = ires->argv[1].val.num;
897 break;
898 case 612:
899 if((transfer = dc_findtransfer(ires->argv[0].val.num)) != NULL)
900 {
901 if(transfer->peernick != NULL)
902 free(transfer->peernick);
903 transfer->peernick = swcsdup(ires->argv[1].val.str);
904 }
905 break;
906 case 613:
907 if((transfer = dc_findtransfer(ires->argv[0].val.num)) != NULL)
908 transfer->size = ires->argv[1].val.num;
909 break;
910 case 614:
911 if((transfer = dc_findtransfer(ires->argv[0].val.num)) != NULL)
912 {
913 transfer->error = ires->argv[1].val.num;
914 time(&transfer->errortime);
915 }
916 break;
917 case 615:
918 if((transfer = dc_findtransfer(ires->argv[0].val.num)) != NULL)
919 transfer->curpos = ires->argv[1].val.num;
920 break;
921 case 616:
922 if((transfer = dc_findtransfer(ires->argv[0].val.num)) != NULL)
923 {
924 if(transfer->path != NULL)
925 free(transfer->path);
926 transfer->path = swcsdup(ires->argv[1].val.str);
927 }
928 break;
929 case 617:
930 if((transfer = dc_findtransfer(ires->argv[0].val.num)) != NULL)
931 freetransfer(transfer);
932 break;
933 default:
934 break;
935 }
936 dc_freeires(ires);
937 resp->curline = 0;
938}
939
940/* Note the backspace handling - it's not as elegant as possible, but
941 * it helps avoid the "box-of-toothpicks" syndrome when writing search
942 * expressions manually. */
943wchar_t **dc_lexsexpr(wchar_t *sexpr)
944{
945 wchar_t **ret;
946 wchar_t *buf;
947 size_t retsize, retdata, bufsize, bufdata;
948 int state;
949
950 ret = NULL;
951 buf = NULL;
952 retsize = retdata = bufsize = bufdata = 0;
953 state = 0;
954 while(*sexpr != L'\0')
955 {
956 switch(state)
957 {
958 case 0:
959 if(!iswspace(*sexpr))
960 state = 1;
961 else
962 sexpr++;
963 break;
964 case 1:
965 if(iswspace(*sexpr))
966 {
967 if(buf != NULL)
968 {
969 addtobuf(buf, L'\0');
970 addtobuf(ret, buf);
971 buf = NULL;
972 bufsize = bufdata = 0;
973 }
974 state = 0;
975 } else if((*sexpr == L'(') ||
976 (*sexpr == L')') ||
977 (*sexpr == L'&') ||
978 (*sexpr == L'|') ||
979 (*sexpr == L'!')) {
980 if(buf != NULL)
981 {
982 addtobuf(buf, L'\0');
983 addtobuf(ret, buf);
984 buf = NULL;
985 bufsize = bufdata = 0;
986 }
987 addtobuf(buf, *sexpr);
988 addtobuf(buf, L'\0');
989 addtobuf(ret, buf);
990 buf = NULL;
991 bufsize = bufdata = 0;
992 sexpr++;
993 } else if(*sexpr == L'\"') {
994 sexpr++;
995 state = 2;
996 } else if(*sexpr == L'\\') {
997 sexpr++;
998 if(*sexpr == L'\0')
999 {
1000 addtobuf(buf, *sexpr);
1001 } else if((*sexpr == L'\\') || (*sexpr == L'\"')) {
1002 addtobuf(buf, *sexpr);
1003 sexpr++;
1004 } else {
1005 addtobuf(buf, L'\\');
1006 addtobuf(buf, *sexpr);
1007 sexpr++;
1008 }
1009 } else {
1010 addtobuf(buf, *(sexpr++));
1011 }
1012 break;
1013 case 2:
1014 if(*sexpr == L'\\')
1015 {
1016 sexpr++;
1017 if(*sexpr == L'\0')
1018 {
1019 addtobuf(buf, *sexpr);
1020 } else if((*sexpr == L'\\') || (*sexpr == L'\"')) {
1021 addtobuf(buf, *sexpr);
1022 sexpr++;
1023 } else {
1024 addtobuf(buf, L'\\');
1025 addtobuf(buf, *sexpr);
1026 sexpr++;
1027 }
1028 } else if(*sexpr == L'\"') {
1029 state = 1;
1030 sexpr++;
1031 } else {
1032 addtobuf(buf, *(sexpr++));
1033 }
1034 break;
1035 }
1036 }
1037 if(buf != NULL)
1038 {
1039 addtobuf(buf, L'\0');
1040 addtobuf(ret, buf);
1041 }
1042 addtobuf(ret, NULL);
1043 return(ret);
1044}
1045
1046void dc_freewcsarr(wchar_t **arr)
1047{
1048 wchar_t **buf;
1049
1050 if(arr == NULL)
1051 return;
1052 for(buf = arr; *buf != NULL; buf++)
1053 free(*buf);
1054 free(arr);
1055}