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