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