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