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