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