1 /*
2 * dcc.c: Things dealing client to client connections.
3 *
4 * Copyright (c) 1991, 1992 Troy Rollo.
5 * Copyright (c) 1992-1996 Matthew Green.
6 * Copyright 1995, 2015 EPIC Software Labs
7 * All rights reserved.
8 *
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
11 * are met:
12 * 1. Redistributions of source code must retain the above copyright
13 * notice, this list of conditions and the following disclaimer.
14 * 2. Redistributions in binary form must reproduce the above copyright
15 * notices, the above paragraph (the one permitting redistribution),
16 * this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
18 * 3. The names of the author(s) may not be used to endorse or promote
19 * products derived from this software without specific prior written
20 * permission.
21 *
22 * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR
23 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
24 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
25 * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT,
26 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
27 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
28 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
29 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
30 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32 * SUCH DAMAGE.
33 */
34
35 #include "irc.h"
36 #include "sedcrypt.h"
37 #include "ctcp.h"
38 #include "dcc.h"
39 #include "functions.h"
40 #include "hook.h"
41 #include "ircaux.h"
42 #include "lastlog.h"
43 #include "newio.h"
44 #include "output.h"
45 #include "parse.h"
46 #include "server.h"
47 #include "status.h"
48 #include "vars.h"
49 #include "window.h"
50 #include "termx.h"
51 #include "reg.h"
52 #include "alias.h"
53 #include "timer.h"
54
55 #define DCC_BLOCK_SIZE (1<<11)
56
57 /* This should probably be configurable. */
58 #define DCC_RCV_BLOCK_SIZE (1<<16)
59
60 /* These are the settings for ``flags'' */
61 #define DCC_CHAT 0x0001U
62 #define DCC_FILEOFFER 0x0002U
63 #define DCC_FILEREAD 0x0003U
64 #define DCC_RAW 0x0004U
65 #define DCC_RAW_LISTEN 0x0005U
66 #define DCC_TYPES 0x000fU
67 #define DCC_MY_OFFER 0x0010U
68 #define DCC_ACTIVE 0x0020U
69 #define DCC_THEIR_OFFER 0x0040U
70 #define DCC_DELETE 0x0080U
71 #define DCC_TWOCLIENTS 0x0100U
72 #define DCC_REJECTED 0x0200U
73 #define DCC_QUOTED 0x0400U
74 #define DCC_CONNECTING 0x0800U
75 #define DCC_RESUME_REQ 0x1000U
76 #define DCC_STATES 0xfff0U
77
dcc_target(const char * name)78 static char *dcc_target (const char *name)
79 {
80 size_t len;
81 char * ret;
82
83 len = strlen(name);
84 ret = new_malloc(len + 2);
85 snprintf(ret, len + 2, "=%s", name);
86
87 return ret;
88 }
89
90 typedef struct DCC_struct
91 {
92 unsigned flags;
93 int family;
94 int locked; /* XXX - Sigh */
95 int socket;
96 int file;
97 int held;
98 int packet_size;
99 int full_line_buffer;
100 long refnum;
101 intmax_t filesize;
102 char * local_filename;
103 char * description;
104 char * othername;
105 char * user;
106 char * userhost;
107 struct DCC_struct * next;
108
109 SS offer; /* Their offer */
110 SS peer_sockaddr; /* Their saddr */
111 SS local_sockaddr; /* Our saddr */
112 unsigned short want_port; /* HOST ORDER */
113
114 intmax_t bytes_read; /* INTMAX */
115 intmax_t bytes_sent; /* IM */
116 intmax_t bytes_acked; /* IM */
117 intmax_t resume_size; /* IM */
118
119 Timeval lasttime;
120 Timeval starttime;
121 Timeval holdtime;
122 double heldtime;
123
124 int (*open_callback) (struct DCC_struct *);
125 int server;
126 int updates_status;
127 } DCC_list;
128
129 static DCC_list * ClientList = NULL;
130 static char DCC_current_transfer_buffer[256];
131 static int dcc_global_lock = 0;
132 static int dccs_rejected = 0;
133 static int dcc_refnum = 0;
134 static int dcc_updates_status = 1;
135 static char * default_dcc_port = NULL;
136
137 #define DCC_SUBCOMMAND(x) static void x (int argc, char **argv, const char *subargs)
138 static void dcc_chat (char *);
139 DCC_SUBCOMMAND(dcc_chat_subcmd);
140 static void dcc_get (char *);
141 #ifdef MIRC_BROKEN_DCC_RESUME
142 static void dcc_resume (char *);
143 #endif
144 DCC_SUBCOMMAND(dcc_get_subcmd);
145 static void dcc_close (char *);
146 DCC_SUBCOMMAND(dcc_close_subcmd);
147 static void dcc_closeall (char *);
148 DCC_SUBCOMMAND(dcc_closeall_subcmd);
149
150 static void dcc_filesend (char *);
151 static void dcc_send_raw (char *);
152 static void dcc_list (char *args);
153 static void dcc_rename (char *);
154
155 static DCC_list * dcc_searchlist (unsigned, const char *, const char *, const char *, int);
156 static void dcc_erase (DCC_list *);
157 static void dcc_garbage_collect (void);
158 static int dcc_connected (int);
159 static int dcc_connect (DCC_list *);
160 static int dcc_listen (DCC_list *);
161 static void dcc_send_booster_ctcp (DCC_list *dcc);
162
163 static void do_dcc (int fd);
164 static void process_dcc_chat (DCC_list *);
165 static void process_incoming_listen (DCC_list *);
166 static void process_incoming_raw (DCC_list *);
167 static void process_dcc_send (DCC_list *);
168 static void process_incoming_file (DCC_list *);
169
170 static void output_reject_ctcp (int, char *, char *);
171 static void DCC_close_filesend (DCC_list *, const char *, const char *);
172 static void update_transfer_buffer (DCC_list *, const char *format, ...);
173 static char * dcc_urlencode (const char *);
174 static char * dcc_urldecode (const char *);
175 static void fill_in_default_port (DCC_list *dcc);
176
177 #ifdef MIRC_BROKEN_DCC_RESUME
178 static void dcc_getfile_resume_demanded (const char *, char *, char *, char *);
179 static void dcc_getfile_resume_start (const char *, char *, char *, char *);
180 #endif
181
182
183 /*
184 * These are the possible <type> arguments to /DCC
185 */
186 typedef void (*dcc_function) (char *);
187 struct
188 {
189 const char * name;
190 dcc_function function;
191 } dcc_commands[] =
192 {
193 { "CHAT", dcc_chat }, /* DCC_CHAT */
194 { "SEND", dcc_filesend }, /* DCC_FILEOFFER */
195 { "GET", dcc_get }, /* DCC_FILEREAD */
196 { "RAW", dcc_send_raw }, /* DCC_RAW */
197
198 { "CLOSE", dcc_close },
199 { "CLOSEALL", dcc_closeall },
200 { "LIST", dcc_list },
201 { "RENAME", dcc_rename },
202 #ifdef MIRC_BROKEN_DCC_RESUME
203 { "RESUME", dcc_resume },
204 #endif
205 { NULL, (dcc_function) NULL }
206 };
207
208 /*
209 * These are the possible types of DCCs that can be open.
210 */
211 static const char *dcc_types[] =
212 {
213 "<none>",
214 "CHAT", /* DCC_CHAT */
215 "SEND", /* DCC_FILEOFFER */
216 "GET", /* DCC_FILEREAD */
217 "RAW", /* DCC_RAW */
218 "RAW_LISTEN",
219 NULL
220 };
221
get_dcc_by_filedesc(int fd)222 static DCC_list * get_dcc_by_filedesc (int fd)
223 {
224 DCC_list * dcc = NULL;
225
226 for (dcc = ClientList; dcc ; dcc = dcc->next)
227 {
228 /* Never return deleted entries */
229 if (dcc->flags & DCC_DELETE)
230 continue;
231
232 if (dcc->socket == fd)
233 break;
234 }
235
236 return dcc;
237 }
238
get_dcc_by_refnum(int refnum)239 static DCC_list * get_dcc_by_refnum (int refnum)
240 {
241 DCC_list * dcc = NULL;
242
243 for (dcc = ClientList; dcc; dcc = dcc->next)
244 {
245 if (dcc->flags & DCC_DELETE)
246 continue;
247
248 if (dcc->refnum == refnum)
249 break;
250 }
251
252 return dcc;
253 }
254
255
256 /************************************************************************/
257 /*
258 * remove_from_dcc_list: What do you think it does?
259 */
dcc_remove_from_list(DCC_list * erased)260 static void dcc_remove_from_list (DCC_list *erased)
261 {
262 if (x_debug & DEBUG_DCC_XMIT)
263 yell("Removing %p from dcc list", erased);
264
265 if (erased == ClientList)
266 ClientList = erased->next;
267 else
268 {
269 DCC_list *prev = NULL;
270
271 for (prev = ClientList; prev; prev = prev->next)
272 if (prev->next == erased)
273 break;
274 if (prev)
275 prev->next = erased->next;
276 }
277 }
278
279 /*
280 * We use a primitive form of cooperative reference counting to figure out
281 * when it's ok to delete an item. Whenever anyone iterates over ClientList,
282 * they "lock" with NULL to claim a reference on all DCC items. When they
283 * are done, they "unlock" with NULL to release the references to all DCC
284 * items. It is also possible to claim a reference on a particular DCC item.
285 * All references must be released later, naturally. A DCC item is garbage
286 * collected when it is marked as "DELETE"d, it has no reference locks, and
287 * there are no global reference locks.
288 */
dcc_garbage_collect(void)289 static void dcc_garbage_collect (void)
290 {
291 DCC_list *dcc;
292 int need_update = 0;
293
294 if (dcc_global_lock) /* XXX Yea, yea, yea */
295 {
296 if (x_debug & DEBUG_DCC_XMIT)
297 yell("Garbage collection waiting for global lock");
298 return;
299 }
300
301 dcc = ClientList;
302 while (dcc)
303 {
304 if ((dcc->flags & DCC_DELETE) && dcc->locked == 0)
305 {
306 if ((dcc->flags & DCC_TYPES) == DCC_FILEOFFER ||
307 (dcc->flags & DCC_TYPES) == DCC_FILEREAD)
308 need_update = 1;
309
310 if (x_debug & DEBUG_DCC_XMIT)
311 yell("DCC %p being GC'd", dcc);
312 dcc_erase(dcc);
313 dcc = ClientList; /* Start over */
314 }
315 else
316 dcc = dcc->next;
317 }
318
319 if (need_update)
320 update_transfer_buffer(NULL, NULL); /* Whatever */
321 }
322
323 /*
324 * Note that 'erased' does not neccesarily have to be on ClientList.
325 * In fact, it may very well NOT be on ClientList. The handling at the
326 * beginning of the function is only to sanity check that it isnt on
327 * ClientList when we blow it away.
328 */
dcc_erase(DCC_list * erased)329 static void dcc_erase (DCC_list *erased)
330 {
331 if (x_debug & DEBUG_DCC_XMIT)
332 yell("DCC %p being erased", erased);
333
334 dcc_remove_from_list(erased);
335
336 /*
337 * Automatically grok for REJECTs
338 */
339 if (!(erased->flags & DCC_REJECTED))
340 {
341 if (erased->flags & (DCC_MY_OFFER|DCC_THEIR_OFFER))
342 if (get_int_var(DCC_AUTO_SEND_REJECTS_VAR))
343 do
344 {
345 unsigned my_type = erased->flags & DCC_TYPES;
346 char *dummy_ptr = NULL;
347 char *nopath;
348 static time_t last_reject = 0;
349 time_t right_now;
350
351 time(&right_now);
352 if (right_now - last_reject < 2)
353 break; /* Throttle flood attempts */
354
355 last_reject = right_now;
356
357 if (erased->description &&
358 (nopath = strrchr(erased->description, '/')))
359 nopath++;
360 else
361 nopath = erased->description;
362
363 malloc_sprintf(&dummy_ptr, "%s %s %s",
364 erased->user,
365 (my_type == DCC_FILEOFFER ? "GET" :
366 (my_type == DCC_FILEREAD ? "SEND" :
367 dcc_types[my_type])),
368 nopath ? nopath : "<any>");
369
370 erased->flags |= DCC_REJECTED;
371
372 /*
373 * And output it to the user
374 */
375 if (is_server_registered(erased->server))
376 {
377 if (!dead && *(erased->user) != '=')
378 isonbase(erased->server, dummy_ptr, output_reject_ctcp);
379 else
380 output_reject_ctcp(erased->server, dummy_ptr, erased->user);
381 }
382 }
383 while (0);
384 }
385
386 /*
387 * In any event, blow it away.
388 */
389 erased->socket = new_close(erased->socket);
390 close(erased->file);
391 erased->file = -1;
392 new_free(&erased->description); /* Magic check failure here */
393 new_free(&erased->local_filename);
394 new_free(&erased->user);
395 new_free(&erased->othername);
396 new_free(&erased->userhost);
397 new_free((char **)&erased);
398
399 if (x_debug & DEBUG_DCC_XMIT)
400 yell("DCC erased");
401 }
402
403 /*
404 * close_all_dcc: We call this when we create a new process so that
405 * we don't leave any fd's lying around, that won't close when we
406 * want them to..
407 */
close_all_dcc(void)408 void close_all_dcc (void)
409 {
410 DCC_list *dcc;
411 int l;
412
413 dccs_rejected = 0;
414
415 while ((dcc = ClientList))
416 dcc_erase(dcc);
417
418 if (dccs_rejected)
419 {
420 l = message_from(NULL, LEVEL_DCC);
421 say("Waiting for DCC REJECTs to be sent");
422 sleep(1);
423 pop_message_from(l);
424 }
425 }
426
427 /*
428 * Place the dcc on hold. Return 1
429 * (fail) if it was already on hold.
430 */
dcc_hold(DCC_list * dcc)431 static int dcc_hold (DCC_list *dcc)
432 {
433 new_hold_fd(dcc->socket);
434 if (dcc->held)
435 return 1;
436 else {
437 get_time(&dcc->holdtime);
438 dcc->held = 1;
439 return 0;
440 }
441 }
442
443 /*
444 * Remove the hold. Return 1
445 * (fail) if it was already unheld.
446 */
dcc_unhold(DCC_list * dcc)447 static int dcc_unhold (DCC_list *dcc)
448 {
449 Timeval right_now;
450
451 new_unhold_fd(dcc->socket);
452 if (!dcc->held)
453 return 1;
454 else {
455 get_time(&right_now);
456 dcc->heldtime += time_diff(dcc->holdtime, right_now);
457 get_time(&dcc->holdtime);
458 dcc->held = 0;
459 return 0;
460 }
461 }
462
463
lock_dcc(DCC_list * dcc)464 static int lock_dcc (DCC_list *dcc)
465 {
466 if (x_debug & DEBUG_DCC_XMIT)
467 yell("DCC %p being locked", dcc);
468
469 if (dcc)
470 dcc->locked++;
471 dcc_global_lock++;
472 return 0;
473 }
474
unlock_dcc(DCC_list * dcc)475 static int unlock_dcc (DCC_list *dcc)
476 {
477 if (x_debug & DEBUG_DCC_XMIT)
478 yell("DCC %p being unlocked", dcc);
479
480 if (dcc)
481 dcc->locked--;
482 dcc_global_lock--;
483
484 if (dcc_global_lock == 0)
485 dcc_garbage_collect(); /* XXX Maybe unnecessary */
486 return 0;
487 }
488
489
490
491 /************************************************************************/
492 /*
493 * These functions handle important DCC jobs.
494 */
dcc_create(unsigned type,const char * user,const char * description,const char * othername,int family,intmax_t filesize)495 static DCC_list *dcc_create (
496 unsigned type, /* Type of connection we want */
497 const char * user, /* Nick of the remote peer */
498 const char * description, /*
499 * DCC Type specific information,
500 * Usually a full pathname for
501 * SEND/GET, "listen" or "connect"
502 * for RAW, or NULL for others.
503 */
504 const char * othername, /* Alias filename for SEND/GET */
505 int family,
506 intmax_t filesize)
507 {
508 DCC_list *new_client;
509
510 new_client = new_malloc(sizeof(DCC_list));
511 new_client->flags = type;
512 new_client->family = family;
513 new_client->locked = 0;
514 new_client->socket = -1;
515 new_client->file = -1;
516 new_client->filesize = filesize;
517 new_client->held = 0;
518 new_client->local_filename = NULL;
519 new_client->next = ClientList;
520 new_client->user = malloc_strdup(user);
521 new_client->userhost = (FromUserHost && *FromUserHost)
522 ? malloc_strdup(FromUserHost)
523 : malloc_strdup(unknown_userhost);
524 new_client->description = malloc_strdup(description);
525 new_client->othername = malloc_strdup(othername);
526 new_client->bytes_read = 0;
527 new_client->bytes_sent = 0;
528 new_client->bytes_acked = 0;
529 new_client->starttime.tv_sec = 0;
530 new_client->starttime.tv_usec = 0;
531 new_client->holdtime.tv_sec = 0;
532 new_client->holdtime.tv_usec = 0;
533 new_client->heldtime = 0.0;
534 new_client->want_port = 0;
535 new_client->resume_size = 0;
536 new_client->open_callback = NULL;
537 new_client->refnum = dcc_refnum++;
538 new_client->server = from_server;
539 new_client->updates_status = 1;
540 new_client->packet_size = 0;
541 new_client->full_line_buffer = 0;
542 get_time(&new_client->lasttime);
543
544 if (x_debug & DEBUG_DCC_XMIT)
545 yell("DCC %p created", new_client);
546 ClientList = new_client;
547 return new_client;
548 }
549
550 /*
551 * dcc_searchlist searches through the dcc_list and finds the client
552 * with the the flag described in type set. This function should never
553 * return a delete'd entry.
554 */
dcc_searchlist(unsigned type,const char * user,const char * description,const char * othername,int active)555 static DCC_list *dcc_searchlist (
556 unsigned type, /* What kind of connection we want */
557 const char * user, /* Nick of the remote peer */
558 const char * description, /*
559 * DCC Type specific information,
560 * Usually a full pathname for
561 * SEND/GET, "listen" or "connect"
562 * for RAW, or NULL for others.
563 */
564 const char * othername, /* Alias filename for SEND/GET */
565 int active) /* Only get active/non-active? */
566 {
567 DCC_list *client;
568 const char *last = NULL;
569 char *decoded_description;
570
571 decoded_description = description ? dcc_urldecode(description) : NULL;
572
573 if (x_debug & DEBUG_DCC_SEARCH)
574 yell("entering dcc_sl. desc (%s) decdesc (%s) user (%s) "
575 "type(%d) other (%s) active (%d)",
576 description, decoded_description, user, type,
577 othername, active);
578
579 /*
580 * Walk all of the DCC connections that we know about.
581 */
582 lock_dcc(NULL);
583
584 for (client = ClientList; client ; client = client->next)
585 {
586 /* Never return deleted entries */
587 if (client->flags & DCC_DELETE)
588 continue;
589
590 /*
591 * Tell the user if they care
592 */
593 if (x_debug & DEBUG_DCC_SEARCH)
594 {
595 yell("checking against name (%s) user (%s) type (%d) "
596 "flag (%d) other (%s) active (%x)",
597 client->description,
598 client->user,
599 client->flags & DCC_TYPES,
600 client->flags,
601 client->othername,
602 client->flags & DCC_ACTIVE);
603 }
604
605 /*
606 * Ok. first of all, it has to be the right type.
607 * XXX - Doing (unsigned) -1 is a hack.
608 */
609 if (type != (unsigned)-1 &&
610 ((client->flags & DCC_TYPES) != type))
611 continue;
612
613 /*
614 * Its OK if the user matches the client's user
615 */
616 if (user && my_stricmp(user, client->user))
617 continue;
618
619
620 /*
621 * If "name" is NULL, then that acts as a wildcard.
622 * If "description" is NULL, then that also acts as a wildcard.
623 * If "name" is not the same as "description", then it could
624 * be that "description" is a filename. Check to see if
625 * "name" is the last component in "description" and
626 * accept that.
627 * Otherwise, "othername" must exist and be the same.
628 * In all other cases, reject this entry.
629 */
630
631 if (description && client->description &&
632 my_stricmp(description, client->description) &&
633 my_stricmp(decoded_description, client->description))
634 {
635 /*
636 * OK. well, 'name' isnt 'description', try looking
637 * for a last segment. If there isnt one, choke.
638 */
639 if ((last = strrchr(client->description, '/')) == NULL)
640 continue;
641
642 /*
643 * If 'name' isnt 'last' then we'll have to look at
644 * 'othername' to see if that matches.
645 */
646 if (my_stricmp(description, last + 1) && my_stricmp(decoded_description, last + 1))
647 {
648 if (!othername || !client->othername)
649 continue;
650
651 if (my_stricmp(othername, client->othername))
652 continue;
653 }
654 }
655
656 /*
657 * Active == 0 -> Only PENDING unopened connections, please
658 * No deleted entries, either.
659 */
660 if (active == 0)
661 {
662 if (client->flags & DCC_ACTIVE)
663 continue;
664 }
665 /*
666 * Active == 1 -> Only ACTIVE and OPEN connections, please
667 */
668 else if (active == 1)
669 {
670 if ((client->flags & DCC_ACTIVE) == 0)
671 continue;
672 }
673 /*
674 * Active == -1 -> Only NON DELETED entries, please.
675 */
676 else if (active == -1)
677 (void) 0;
678
679 if (x_debug & DEBUG_DCC_SEARCH)
680 yell("We have a winner!");
681
682 /* Looks like we have a winner! */
683 unlock_dcc(NULL);
684 return client;
685 }
686 new_free(&decoded_description);
687 unlock_dcc(NULL);
688
689 return NULL;
690 }
691
692
693 /*
694 * dcc_get_bucket searches through the dcc_list and collects the clients
695 * we can do dcc get on. This function should never return a delete'd entry.
696 */
dcc_get_bucket(Bucket * b,const char * user,const char * fname)697 static int dcc_get_bucket (Bucket *b, const char *user, const char *fname)
698 {
699 DCC_list *client;
700 const char *last = NULL;
701 char *decoded_description;
702 int count = 0;
703
704 decoded_description = fname ? dcc_urldecode(fname) : NULL;
705
706 if (x_debug & DEBUG_DCC_SEARCH)
707 yell("entering dcc_g_b. desc (%s) decdesc (%s) user (%s) ",
708 fname, decoded_description, user);
709
710 /*
711 * Walk all of the DCC connections that we know about.
712 */
713 lock_dcc(NULL);
714
715 for (client = ClientList; client ; client = client->next)
716 {
717 /* Skip deleted entries */
718 if (client->flags & DCC_DELETE)
719 continue;
720
721 /* Skip already ACTIVE connections */
722 if (client->flags & DCC_ACTIVE)
723 continue;
724
725 /* Skip non-DCC GETs */
726 if ((client->flags & DCC_TYPES) != DCC_FILEREAD)
727 continue;
728
729 /* Skip DCC GETs with no filename (!!!) */
730 if (!client->description)
731 continue;
732
733 /* Skip DCCs from other people */
734 if (user && my_stricmp(user, client->user))
735 continue;
736
737 last = strrchr(client->description, '/');
738
739 /*
740 * If "fname" is NULL, then that also acts as a wildcard.
741 * If "fname" is not the same as "description", then it could
742 * be that "description" is a filename. Check to see if
743 * "fname" is the last component in "description" and
744 * accept that.
745 * In all other cases, reject this entry.
746 */
747 #define ACCEPT { if (b) \
748 add_to_bucket(b, empty_string, client); \
749 count++; \
750 continue; }
751 #define CHECKVAL(x) \
752 if (! x ) \
753 ACCEPT \
754 else if (!my_stricmp( x , client->description)) \
755 ACCEPT \
756 else if (last && !my_stricmp( x , last + 1)) \
757 ACCEPT
758
759 CHECKVAL(fname)
760 CHECKVAL(decoded_description)
761 }
762
763 new_free(&decoded_description);
764 unlock_dcc(NULL);
765 return count;
766 }
767
768 /*
769 * Added by Chaos: Is used in edit.c for checking redirect.
770 */
dcc_chat_active(const char * user)771 int dcc_chat_active (const char *user)
772 {
773 int retval;
774 int l;
775
776 l = message_from(user, LEVEL_DCC);
777 retval = dcc_searchlist(DCC_CHAT, user, NULL, NULL, 1) ? 1 : 0;
778 pop_message_from(l);
779 return retval;
780 }
781
782
783 /************************************************************************/
dcc_get_connect_addrs(DCC_list * dcc)784 static int dcc_get_connect_addrs (DCC_list *dcc)
785 {
786 ssize_t c;
787 const char * type;
788 int retval;
789
790 type = dcc_types[dcc->flags & DCC_TYPES];
791
792 #define DGETS(x, y) dgets( x , (char *) & y , sizeof y , -1);
793
794 /* * */
795 /* This is the errno value from getsockopt() */
796 c = DGETS(dcc->socket, retval)
797 if (c < (ssize_t)sizeof(retval) || retval)
798 goto something_broke;
799
800 /* This is the socket error returned by getsockopt() */
801 c = DGETS(dcc->socket, retval)
802 if (c < (ssize_t)sizeof(retval) || retval)
803 goto something_broke;
804
805 /* * */
806 /* This is the errno value from getsockname() */
807 c = DGETS(dcc->socket, retval)
808 if (c < (ssize_t)sizeof(retval) || retval)
809 goto something_broke;
810
811 /* This is the address returned by getsockname() */
812 c = DGETS(dcc->socket, dcc->local_sockaddr)
813 if (c < (ssize_t)sizeof(dcc->local_sockaddr))
814 goto something_broke;
815
816 /* * */
817 /* This is the errno value from getpeername() */
818 c = DGETS(dcc->socket, retval)
819 if (c < (ssize_t)sizeof(retval) || retval)
820 goto something_broke;
821
822 /* This is the address returned by getpeername() */
823 c = DGETS(dcc->socket, dcc->peer_sockaddr)
824 if (c < (ssize_t)sizeof(dcc->peer_sockaddr))
825 goto something_broke;
826
827 return 0;
828
829 something_broke:
830 say("DCC %s connection with %s could not be established: %s",
831 type, dcc->user,
832 retval ? strerror(retval) : "(internal error)");
833 dcc->flags |= DCC_DELETE;
834 return -1;
835 }
836
837 /*
838 * This is called when a client connection completes (successfully).
839 * If the connection failed, we will forcibly eject the dcc.
840 */
dcc_connected(int fd)841 static int dcc_connected (int fd)
842 {
843 DCC_list * dcc;
844 const char * type;
845 int jvs_blah;
846
847 if (!(dcc = get_dcc_by_filedesc(fd)))
848 return -1; /* Don't want it */
849
850 type = dcc_types[dcc->flags & DCC_TYPES];
851
852 /*
853 * Set up the connection to be useful
854 */
855 new_open(dcc->socket, do_dcc, NEWIO_RECV, 1, dcc->server);
856 dcc->flags &= ~DCC_THEIR_OFFER;
857 dcc->flags |= DCC_ACTIVE;
858
859 /*
860 * If this was a two-peer connection, then tell the user
861 * that the connection was successfull.
862 */
863 lock_dcc(dcc);
864 if ((dcc->flags & DCC_TYPES) != DCC_RAW &&
865 (dcc->flags & DCC_TYPES) != DCC_RAW_LISTEN)
866 {
867 char p_addr[256];
868 char p_port[24];
869 char *encoded_description;
870 SA *addr = (SA *)&dcc->peer_sockaddr;
871
872 if (inet_ntostr(addr, p_addr, 256, p_port, 24, NI_NUMERICHOST))
873 yell("Couldn't get host/port for this connection.");
874
875 encoded_description = dcc_urlencode(dcc->description);
876
877 /*
878 * It would be nice if SEND also showed the filename
879 * and size (since it's possible to have multiple
880 * SEND requests queued), so we check for a file size
881 * first.
882 * Actually, that was wrong. We just check the type.
883 * so that it doesnt choke on zero-length files.
884 */
885 if (!strcmp(type, "SEND"))
886 {
887 if ((jvs_blah = do_hook(DCC_CONNECT_LIST,
888 "%s %s %s %s %s " INTMAX_FORMAT,
889 dcc->user, type, p_addr, p_port,
890 encoded_description,
891 dcc->filesize)))
892 /*
893 * Compatability with bitchx
894 */
895 jvs_blah = do_hook(DCC_CONNECT_LIST,
896 "%s GET %s %s %s " INTMAX_FORMAT,
897 dcc->user, p_addr, p_port,
898 encoded_description,
899 dcc->filesize);
900 }
901 else
902 {
903 jvs_blah = do_hook(DCC_CONNECT_LIST,
904 "%s %s %s %s",
905 dcc->user, type, p_addr, p_port);
906 }
907
908 if (jvs_blah)
909 {
910 say("DCC %s connection with %s[%s:%s] established",
911 type, dcc->user, p_addr, p_port);
912 }
913 new_free(&encoded_description);
914 }
915 unlock_dcc(dcc);
916
917 /*
918 * Record the time the connection was started,
919 * Clean up and then return.
920 */
921 get_time(&dcc->starttime);
922 get_time(&dcc->lasttime);
923 if (dcc->open_callback)
924 dcc->open_callback(dcc);
925 return 0; /* Going to keep it. */
926 }
927
do_expire_dcc_connects(void * stuff)928 static int do_expire_dcc_connects (void *stuff)
929 {
930 int old_server = from_server;
931 int seconds;
932 DCC_list *dcc;
933 Timeval right_now;
934 int l;
935
936 /*
937 * Initialize our idea of what is going on.
938 */
939 if (from_server == NOSERV)
940 from_server = get_window_server(0);
941
942 get_time(&right_now);
943 if ((seconds = get_int_var(DCC_CONNECT_TIMEOUT_VAR)) == 0)
944 return 0; /* Do not time out if == 0 */
945
946 lock_dcc(NULL);
947 for (dcc = ClientList ; dcc != NULL ; dcc = dcc->next)
948 {
949 if (!(dcc->flags & DCC_CONNECTING))
950 continue;
951
952 if (time_diff(dcc->lasttime, right_now) >= seconds)
953 {
954 unsigned my_type = dcc->flags & DCC_TYPES;
955 char *encoded_description;
956
957 encoded_description = dcc_urlencode(dcc->description);
958 l = message_from(dcc->user, LEVEL_DCC);
959 if (do_hook(DCC_LOST_LIST,"%s %s %s CONNECT TIMED OUT",
960 dcc->user,
961 dcc_types[my_type],
962 encoded_description ? encoded_description : "<any>"))
963 say("DCC %s:%s to %s -- connection timed out",
964 dcc_types[my_type],
965 dcc->description ? dcc->description : "<any>",
966 dcc->user);
967 dcc->flags |= DCC_DELETE;
968 pop_message_from(l);
969 }
970 }
971 unlock_dcc(NULL);
972
973 dcc_garbage_collect();
974 from_server = old_server;
975 return 0;
976 }
977
978 /*
979 * Whenever a DCC changes state from WAITING->ACTIVE, it calls this function
980 * to initiate the internet connection for the transaction.
981 */
dcc_connect(DCC_list * dcc)982 static int dcc_connect (DCC_list *dcc)
983 {
984 int old_server = from_server;
985 int retval = 0;
986 SS local;
987 socklen_t locallen;
988 int seconds;
989
990 /*
991 * Initialize our idea of what is going on.
992 */
993 if (from_server == NOSERV)
994 from_server = get_window_server(0);
995
996 lock_dcc(dcc);
997 do
998 {
999 if (!(dcc->flags & DCC_THEIR_OFFER))
1000 {
1001 say("Can't connect on a dcc that was not offered [%s]", dcc->user);
1002 dcc->flags |= DCC_DELETE;
1003 retval = -1;
1004 break;
1005 }
1006
1007 /* inet_vhostsockaddr doesn't usually return an error */
1008 if (inet_vhostsockaddr(FAMILY(dcc->offer), -1, NULL,
1009 &local, &locallen) < 0)
1010 {
1011 say("Can't figure out your virtual host. "
1012 "Use /HOSTNAME to reset it and try again.");
1013 retval = -1;
1014 break;
1015 }
1016
1017 dcc->socket = client_connect((SA *)&local, locallen,
1018 (SA *)&dcc->offer, sizeof(dcc->offer));
1019
1020 if (dcc->socket < 0)
1021 {
1022 char *encoded_description = dcc_urlencode(dcc->description);
1023
1024 /* XXX Error message may need to be tuned here. */
1025 if (do_hook(DCC_LOST_LIST,"%s %s %s ERROR",
1026 dcc->user,
1027 dcc_types[dcc->flags&DCC_TYPES],
1028 encoded_description ?
1029 encoded_description :
1030 "<any>"))
1031 say("Unable to create connection: (%d)", dcc->socket);
1032
1033 if (encoded_description)
1034 new_free(&encoded_description);
1035
1036 dcc->flags |= DCC_DELETE;
1037 retval = -1;
1038 break;
1039 }
1040
1041 dcc->flags |= DCC_CONNECTING;
1042 new_open(dcc->socket, do_dcc, NEWIO_CONNECT, 0, dcc->server);
1043
1044 if ((seconds = get_int_var(DCC_CONNECT_TIMEOUT_VAR)) > 0)
1045 {
1046 /*
1047 say("A non-blocking connect() for your DCC has been initiated."
1048 " It could take a while to complete."
1049 " I'll check on it in %d seconds", seconds);
1050 */
1051 add_timer(0, empty_string, seconds, 1,
1052 do_expire_dcc_connects, NULL, NULL,
1053 GENERAL_TIMER, -1, 0, 0);
1054 }
1055 from_server = old_server;
1056 get_time(&dcc->lasttime);
1057 break;
1058 }
1059 while (0);
1060
1061 unlock_dcc(dcc);
1062 from_server = old_server;
1063 return retval;
1064 }
1065
1066
1067 /*
1068 * Make an offer to another peer that they can't refuse.
1069 */
dcc_listen(DCC_list * dcc)1070 static int dcc_listen (DCC_list *dcc)
1071 {
1072 int old_server = from_server;
1073 char p_port[12];
1074 int retval = 0;
1075
1076 /*
1077 * Initialize our idea of what is going on.
1078 */
1079 if (from_server == NOSERV)
1080 from_server = get_window_server(0);
1081
1082 do
1083 {
1084 if (dcc->flags & DCC_THEIR_OFFER)
1085 {
1086 dcc->flags |= DCC_DELETE;
1087 say("Mixup: dcc_offer on a remote offer [%d]", dcc->socket);
1088 retval = -1;
1089 break;
1090 }
1091
1092 /*
1093 * Mark that we're waiting for the remote peer to answer,
1094 * and then open up a listen()ing socket for them. If our
1095 * first choice of port fails, try another one. If both
1096 * fail, then we give up. If the user insists on doing
1097 * random ports, then we will fallback to asking the system
1098 * for a port if our random port isnt available.
1099 */
1100 dcc->flags |= DCC_MY_OFFER;
1101
1102 while ((dcc->socket = ip_bindery(dcc->family, dcc->want_port,
1103 &dcc->local_sockaddr)) < 0)
1104 {
1105 /* XXX Maybe this shouldn't be done for $listen()s. */
1106 char *encoded_description = NULL;
1107 int original_port = dcc->want_port;
1108
1109 fill_in_default_port(dcc);
1110
1111 encoded_description = dcc_urlencode(dcc->description);
1112 do_hook(DCC_LOST_LIST,"%s %s %s %ld %d PORT IN USE",
1113 dcc->user, dcc_types[dcc->flags & DCC_TYPES],
1114 encoded_description, dcc->refnum, original_port);
1115 new_free(&encoded_description);
1116
1117 if (dcc->want_port == original_port)
1118 {
1119 dcc->flags |= DCC_DELETE;
1120 say("Unable to create connection on fd [%d] "
1121 "binding local port [%d] for inbound connection.",
1122 dcc->socket, dcc->want_port);
1123 retval = -1;
1124 goto dcc_listen_bail;
1125 }
1126 }
1127
1128 #ifdef MIRC_BROKEN_DCC_RESUME
1129 /*
1130 * For stupid MIRC dcc resumes, we need to stash the
1131 * local port number, because the remote client will send
1132 * back that port number as its ID of what file it wants
1133 * to resume (rather than the filename. ick.)
1134 */
1135 inet_ntostr((SA *)&dcc->local_sockaddr, NULL, 0, p_port, 12, 0);
1136 malloc_strcpy(&dcc->othername, p_port);
1137 #endif
1138 new_open(dcc->socket, do_dcc, NEWIO_ACCEPT, 1, dcc->server);
1139
1140 /*
1141 * If this is to be a 2-peer connection, then we need to
1142 * send the remote peer a CTCP request. I suppose we should
1143 * do an ISON request first, but thats another project.
1144 */
1145 if (dcc->flags & DCC_TWOCLIENTS)
1146 dcc_send_booster_ctcp(dcc);
1147 get_time(&dcc->lasttime);
1148 }
1149 while (0);
1150
1151 dcc_listen_bail:
1152 from_server = old_server;
1153 return retval;
1154 }
1155
1156
1157 /*
1158 * send_booster_ctcp: This is called by dcc_open and also by dcc_filesend
1159 * to send a CTCP handshake message to a remote peer. The reason its a
1160 * function is because its a rather large chunk of code, and it needs to be
1161 * done basically identically by both places. Whatever.
1162 *
1163 * XXX This function is not really protocol independant.
1164 */
dcc_send_booster_ctcp(DCC_list * dcc)1165 static void dcc_send_booster_ctcp (DCC_list *dcc)
1166 {
1167 SS my_sockaddr;
1168 char p_host[128];
1169 char p_port[24];
1170 char * nopath;
1171 const char * type = dcc_types[dcc->flags & DCC_TYPES];
1172 int family;
1173 int server = from_server < 0 ? primary_server : from_server;
1174
1175 if (!is_server_registered(server))
1176 {
1177 yell("You cannot use DCC while not connected to a server.");
1178 return;
1179 }
1180
1181 family = FAMILY(dcc->local_sockaddr);
1182
1183 /*
1184 * Use the external gateway address (visible to the server) if the
1185 * user wants us to use that. If the user is NOT using a vhost,
1186 * then use the address we have for ourselves connecting to the
1187 * server. If the user IS using a vhost, then obviously use that.
1188 */
1189 if (get_int_var(DCC_USE_GATEWAY_ADDR_VAR))
1190 my_sockaddr = get_server_uh_addr(server);
1191 else if (family == AF_INET && V4ADDR(dcc->local_sockaddr).s_addr ==
1192 htonl(INADDR_ANY))
1193 my_sockaddr = get_server_local_addr(server);
1194 #ifdef INET6
1195 else if (family == AF_INET6 && memcmp(&V6ADDR(dcc->local_sockaddr),
1196 &in6addr_any,
1197 sizeof(in6addr_any)) == 0)
1198 my_sockaddr = get_server_local_addr(server);
1199 #endif
1200 else
1201 my_sockaddr = dcc->local_sockaddr;
1202
1203 /*
1204 * If the family the user asked for is not the family of the address
1205 * we want to put in the handshake, something is very wrong. Tell
1206 * the user about it and give up.
1207 */
1208 if (family != FAMILY(my_sockaddr))
1209 {
1210 if (get_int_var(DCC_USE_GATEWAY_ADDR_VAR))
1211 yell("When using /SET DCC_USE_GATEWAY_ADDR ON, I can only "
1212 "support DCC in the same address family (IPv4 or IPv6) "
1213 "as you are using to connect to the server.");
1214 else if (family == AF_INET)
1215 yell("I do not know what your IPv4 address is. You can tell "
1216 "me your IPv4 hostname with /HOSTNAME and retry the /DCC");
1217 #ifdef INET6
1218 else if (family == AF_INET6)
1219 yell("I do not know what your IPv6 address is. You can tell "
1220 "me your IPv6 hostname with /HOSTNAME and retry the /DCC");
1221 #endif
1222 else
1223 yell("I do not know what your address is because you asked "
1224 "me for an address family that I don't support.");
1225
1226 dcc->flags |= DCC_DELETE;
1227 return;
1228 }
1229
1230 if (family == AF_INET)
1231 V4PORT(my_sockaddr) = V4PORT(dcc->local_sockaddr);
1232 #ifdef INET6
1233 else if (family == AF_INET6)
1234 V6PORT(my_sockaddr) = V6PORT(dcc->local_sockaddr);
1235 #endif
1236 else
1237 {
1238 yell("Could not send a CTCP handshake becuase the address "
1239 "family is not supported.");
1240 dcc->flags |= DCC_DELETE;
1241 return;
1242 }
1243
1244 if (inet_ntostr((SA *)&my_sockaddr, p_host, 128, p_port, 24,
1245 GNI_INTEGER | NI_NUMERICHOST))
1246 {
1247 yell("dcc_send_booster_ctcp: I couldn't figure out your hostname (or the port you tried to use was invalid). I have to delete this DCC");
1248 if (get_int_var(DCC_USE_GATEWAY_ADDR_VAR))
1249 yell("dcc_send_booster_ctcp: You have /SET DCC_USE_GATEWAY_ADDR ON, which uses the hostname the irc server shows to the network. If you're using a fake hostname, that would cause this problem. Use /SET DCC_USE_GATEWAY_ADDR OFF if you have a fake hostname on irc (but that still won't work behind a NAT router");
1250 dcc->flags |= DCC_DELETE;
1251 return;
1252 }
1253
1254 /*
1255 * If this is to be a 2-peer connection, then we need to
1256 * send the remote peer a CTCP request. I suppose we should
1257 * do an ISON request first, but thats another project.
1258 */
1259 if (!(dcc->flags & DCC_TWOCLIENTS))
1260 return;
1261
1262 get_time(&dcc->starttime);
1263 get_time(&dcc->lasttime);
1264
1265 if (((dcc->flags & DCC_TYPES) == DCC_FILEOFFER) &&
1266 (nopath = strrchr(dcc->description, '/')))
1267 nopath++;
1268 else
1269 nopath = dcc->description;
1270
1271 /*
1272 * If this is a DCC SEND...
1273 */
1274 lock_dcc(dcc);
1275 if ((dcc->flags & DCC_TYPES) == DCC_FILEOFFER)
1276 {
1277 char * url_name = dcc_urlencode(nopath);
1278
1279 /*
1280 * Dont bother with the checksum.
1281 */
1282 send_ctcp(1, dcc->user, "DCC", "%s %s %s %s "INTMAX_FORMAT,
1283 type, url_name, p_host, p_port,
1284 dcc->filesize);
1285
1286 /*
1287 * Tell the user we sent out the request
1288 */
1289 if (do_hook(DCC_OFFER_LIST, "%s %s %s "INTMAX_FORMAT,
1290 dcc->user, type, url_name, dcc->filesize))
1291 say("Sent DCC %s request (%s "INTMAX_FORMAT") to %s",
1292 type, nopath, dcc->filesize, dcc->user);
1293
1294 new_free(&url_name);
1295 }
1296
1297 /*
1298 * Otherwise its a DCC CHAT request
1299 */
1300 else
1301 {
1302 /*
1303 * Send out the handshake
1304 */
1305 send_ctcp(1, dcc->user, "DCC", "%s %s %s %s",
1306 type, nopath, p_host, p_port);
1307
1308 /*
1309 * And tell the user
1310 */
1311 if (do_hook(DCC_OFFER_LIST, "%s %s", dcc->user, type))
1312 say("Sent DCC %s request to %s", type, dcc->user);
1313 }
1314 unlock_dcc(dcc);
1315 }
1316
1317
1318 /************************************************************************/
1319 /*
1320 * This allows you to send (via /msg or /query) a message to a remote
1321 * dcc target. The two types of targets you may send to are a DCC CHAT
1322 * connection (you specify a nickname) or a DCC RAW connection (you specify
1323 * the file descriptor). Of course, since DCC RAW connections use digits
1324 * and DCC CHATs dont, sending a message to =<digits> then is presumed to
1325 * be a message to a DCC RAW.
1326 *
1327 * If ``noisy'' is 1, then we tell the user that we send the message, and
1328 * allow them to hook it. If ``noisy'' is 0, then we're doing this silently
1329 * (as in /redirect, or /ctcp) and we dont want to tell the user about it.
1330 *
1331 * XXXX This needs to be broken up into CHAT and RAW cases.
1332 */
dcc_message_transmit(int type,char * user,char * desc,char * text,const char * text_display,int noisy,const char * cmd)1333 static void dcc_message_transmit (
1334 int type, /* What type of DCC to send over */
1335 char *user, /* Who to send it to */
1336 char *desc, /* THe 'desc' field in DCC_List */
1337 char *text, /* What to send them */
1338 const char *text_display, /* What to tell the user we sent */
1339 int noisy, /* Do we tell the user? */
1340 const char *cmd) /*
1341 * For /CTCP's, is this a PRIVMSG
1342 * or a NOTICE?
1343 */
1344 {
1345 DCC_list *dcc;
1346 char tmp[DCC_BLOCK_SIZE+1];
1347 char thing = '\0';
1348 int list = 0;
1349 int old_from_server = from_server;
1350 int writeval;
1351 char *target = NULL;
1352 const char *utf8_text = NULL;
1353 char *extra = NULL;
1354
1355 tmp[0] = 0;
1356
1357 switch (type)
1358 {
1359 case DCC_CHAT:
1360 {
1361 thing = '=';
1362 list = SEND_DCC_CHAT_LIST;
1363 break;
1364 }
1365 case DCC_RAW:
1366 {
1367 noisy = 0;
1368 break;
1369 }
1370 }
1371
1372 if (!(dcc = dcc_searchlist(type, user, desc, NULL, -1)))
1373 {
1374 say("No active DCC %s:%s connection for %s",
1375 dcc_types[type], desc, user);
1376 return;
1377 }
1378
1379
1380 /*
1381 * Check for CTCPs... whee.
1382 * Dont tag outbound ACTIONs, which break mirc. Blah.
1383 */
1384 if (type == DCC_CHAT && *text == CTCP_DELIM_CHAR
1385 && strncmp(text + 1, "ACTION", 6))
1386 {
1387 if (!cmd || !strcmp(cmd, "PRIVMSG"))
1388 strlcpy(tmp, "CTCP_MESSAGE ", sizeof tmp);
1389 else
1390 strlcpy(tmp, "CTCP_REPLY ", sizeof tmp);
1391 }
1392
1393 if (x_debug & DEBUG_OUTBOUND)
1394 yell("-> [%s] [%s]", desc, text);
1395
1396 if (dcc->flags & DCC_QUOTED)
1397 {
1398 char * dest;
1399 size_t destlen;
1400
1401 if (!(dest = transform_string_dyn("-CTCP", text, 0, &destlen)))
1402 {
1403 yell("DMSG: Could not CTCP-dequote [%s]", text);
1404 dest = malloc_strdup(text);
1405 }
1406 writeval = write(dcc->socket, dest, destlen);
1407 new_free(&dest);
1408 }
1409 else
1410 {
1411 strlcat(tmp, text, sizeof tmp);
1412 strlcat(tmp, "\n", sizeof tmp);
1413
1414 writeval = write(dcc->socket, tmp, strlen(tmp));
1415 }
1416
1417
1418 if (writeval == -1)
1419 {
1420 dcc->flags |= DCC_DELETE;
1421 say("Outbound write() failed: %s", strerror(errno));
1422
1423 from_server = old_from_server;
1424 return;
1425 }
1426
1427 dcc->bytes_sent += writeval;
1428
1429 if (noisy)
1430 {
1431 lock_dcc(dcc);
1432 if (do_hook(list, "%s %s", dcc->user, text_display))
1433 put_it("=> %c%s%c %s",
1434 thing, dcc->user, thing, text_display);
1435 unlock_dcc(dcc);
1436 }
1437
1438 get_time(&dcc->lasttime);
1439 return;
1440 }
1441
1442 /*
1443 * This is used to send a message to a remote DCC peer. This is called
1444 * by send_text.
1445 */
dcc_chat_transmit(char * user,char * text,const char * orig,const char * type,int noisy)1446 void dcc_chat_transmit (char *user, char *text, const char *orig, const char *type, int noisy)
1447 {
1448 int fd;
1449 int l = -1;
1450 char * target = NULL;
1451
1452 do
1453 {
1454 /*
1455 * This converts a message being sent to a number into whatever
1456 * its local port is (which is what we think the nickname is).
1457 * Its just a 15 minute hack. dont read too much into it.
1458 */
1459 if (is_number(user) && (fd = atol(user)))
1460 {
1461 DCC_list * dcc;
1462 if (!(dcc = get_dcc_by_filedesc(fd)))
1463 {
1464 l = message_from(NULL, LEVEL_DCC);
1465 put_it("Descriptor %d is not an open DCC RAW", fd);
1466 break;
1467 }
1468
1469 target = dcc_target(dcc->user);
1470 l = message_from(target, LEVEL_DCC);
1471 dcc_message_transmit(DCC_RAW, dcc->user, dcc->description,
1472 text, orig, noisy, type);
1473 get_time(&dcc->lasttime);
1474 }
1475 else
1476 {
1477 target = dcc_target(user);
1478 l = message_from(target, LEVEL_DCC);
1479 dcc_message_transmit(DCC_CHAT, user, NULL,
1480 text, orig, noisy, type);
1481 }
1482 }
1483 while (0);
1484
1485 dcc_garbage_collect();
1486 pop_message_from(l);
1487 new_free(&target);
1488 }
1489
1490
1491
1492
1493 /*
1494 *
1495 * All these functions are user-generated -- that is, they are all called
1496 * when the user does /DCC <command> or one of the DCC-oriented built in
1497 * functions.
1498 *
1499 */
1500
1501
1502 /*
1503 * The /DCC command. Delegates the work to other functions.
1504 */
BUILT_IN_COMMAND(dcc_cmd)1505 BUILT_IN_COMMAND(dcc_cmd)
1506 {
1507 const char *cmd;
1508 int i, l;
1509
1510 if (!(cmd = next_arg(args, &args)))
1511 cmd = "LIST";
1512
1513 for (i = 0; dcc_commands[i].name != NULL; i++)
1514 {
1515 if (!my_stricmp(dcc_commands[i].name, cmd))
1516 {
1517 l = message_from(NULL, LEVEL_DCC);
1518 lock_dcc(NULL);
1519 dcc_commands[i].function(args);
1520 unlock_dcc(NULL);
1521 pop_message_from(l);
1522
1523 dcc_garbage_collect();
1524 return;
1525 }
1526 }
1527
1528 l = message_from(NULL, LEVEL_DCC);
1529 say("Unknown DCC command: %s", cmd);
1530 pop_message_from(l);
1531 }
1532
1533
1534 /*
1535 * Usage: /DCC CHAT <nick> [-p port]|[-6]|[-4]
1536 */
dcc_chat(char * args)1537 static void dcc_chat (char *args)
1538 {
1539 int argc;
1540 char * argv[10];
1541
1542 argc = split_args(args, argv, 10);
1543 dcc_chat_subcmd(argc, argv, NULL);
1544 }
1545
DCC_SUBCOMMAND(dcc_chat_subcmd)1546 DCC_SUBCOMMAND(dcc_chat_subcmd)
1547 {
1548 char *user = NULL;
1549 DCC_list *dcc;
1550 unsigned short portnum = 0; /* Any port by default */
1551 int family = AF_INET; /* IPv4 by default */
1552 int i;
1553
1554 if (argc == 0)
1555 {
1556 say("Usage: /DCC CHAT <nick> [-p port]|[-6]|[-4]");
1557 return;
1558 }
1559
1560 for (i = 0; i < argc; i++)
1561 {
1562 if (!strcmp(argv[i], "-4"))
1563 family = AF_INET;
1564 #ifdef INET6
1565 else if (!strcmp(argv[i], "-6"))
1566 family = AF_INET6;
1567 #endif
1568 else if (!strcmp(argv[i], "-p"))
1569 {
1570 if (i + 1 == argc)
1571 say("DCC CHAT: Argument to -p missing -- ignored");
1572 else if (!is_number(argv[i + 1]))
1573 say("DCC CHAT: Argument to -p non-numeric -- ignored");
1574 else
1575 {
1576 portnum = my_atol(argv[i + 1]);
1577 i++;
1578 }
1579 }
1580 else if (*argv[i] == '-')
1581 say("DCC CHAT: Option %s not supported", argv[i]);
1582 else if (user)
1583 {
1584 say("DCC CHAT: Opening multiple chats per command not "
1585 "supported yet -- ignoring extra nick %s", argv[i]);
1586 }
1587 else
1588 malloc_strcpy(&user, argv[i]);
1589 }
1590
1591 if ((dcc = dcc_searchlist(DCC_CHAT, user, NULL, NULL, -1)))
1592 {
1593 if ((dcc->flags & DCC_ACTIVE) || (dcc->flags & DCC_MY_OFFER))
1594 {
1595 say("Sending a booster CTCP handshake for "
1596 "an existing DCC CHAT to %s", user);
1597 dcc_send_booster_ctcp(dcc);
1598 return;
1599 }
1600 }
1601 else
1602 dcc = dcc_create(DCC_CHAT, user, "chat", NULL, family, 0);
1603
1604 dcc->flags |= DCC_TWOCLIENTS;
1605 dcc->want_port = portnum;
1606 fill_in_default_port(dcc);
1607
1608 if (dcc->flags & DCC_THEIR_OFFER)
1609 dcc_connect(dcc);
1610 else
1611 dcc_listen(dcc);
1612 }
1613
1614 /*
1615 * Usage: /DCC CLOSE <type> <nick> [<file>]
1616 */
dcc_close(char * args)1617 static void dcc_close (char *args)
1618 {
1619 int argc;
1620 char * argv[10];
1621
1622 argc = split_args(args, argv, 10);
1623 dcc_close_subcmd(argc, argv, NULL);
1624 }
1625
DCC_SUBCOMMAND(dcc_close_subcmd)1626 DCC_SUBCOMMAND(dcc_close_subcmd)
1627 {
1628 DCC_list *dcc;
1629 int type;
1630 char *user = NULL,
1631 *file = NULL;
1632 int count = 0;
1633
1634 if (argc < 2)
1635 {
1636 say("Usage: /DCC CLOSE <type> <nick> [<file>]");
1637 return;
1638 }
1639
1640 if ((!my_stricmp(argv[0], "-all") || !my_stricmp(argv[0], "*")))
1641 type = -1;
1642 else
1643 {
1644 int i;
1645
1646 for (i = 0; dcc_types[i]; i++)
1647 if (!my_stricmp(argv[0], dcc_types[i]))
1648 break;
1649
1650 if (!dcc_types[i])
1651 {
1652 say("DCC CLOSE: Unknown DCC type: %s", argv[0]);
1653 return;
1654 }
1655
1656 type = i;
1657 }
1658
1659 if ((!my_stricmp(argv[1], "-all") || !my_stricmp(argv[1], "*")))
1660 user = NULL;
1661 else
1662 user = argv[1];
1663
1664 if (argc >= 3)
1665 file = argv[2];
1666
1667 while ((dcc = dcc_searchlist(type, user, file, file, -1)))
1668 {
1669 char * encoded_description = NULL;
1670 unsigned my_type = dcc->flags & DCC_TYPES;
1671
1672 count++;
1673 lock_dcc(dcc);
1674
1675 if (dcc->description) {
1676 if (my_type == DCC_FILEOFFER)
1677 encoded_description = dcc_urlencode(dcc->description);
1678 else
1679 /* assume the other end encoded the filename */
1680 encoded_description = malloc_strdup(dcc->description);
1681 }
1682
1683 if (do_hook(DCC_LOST_LIST,"%s %s %s USER ABORTED CONNECTION",
1684 dcc->user,
1685 dcc_types[my_type],
1686 encoded_description ? encoded_description : "<any>"))
1687 say("DCC %s:%s to %s closed",
1688 dcc_types[my_type],
1689 file ? file : "<any>",
1690 dcc->user);
1691
1692 if (encoded_description)
1693 new_free(&encoded_description);
1694
1695 dcc->flags |= DCC_DELETE;
1696 unlock_dcc(dcc);
1697 }
1698
1699 if (!count)
1700 say("No DCC %s:%s to %s found",
1701 (type == -1 ? "<any>" : dcc_types[type]),
1702 (file ? file : "<any>"),
1703 user);
1704 }
1705
1706 /*
1707 * Usage: /DCC CLOSEALL
1708 * It leaves your DCC list very empty
1709 */
dcc_closeall(char * args)1710 static void dcc_closeall (char *args)
1711 {
1712 /* Just a dummy placeholder */
1713 dcc_closeall_subcmd(0, NULL, NULL);
1714 }
1715
DCC_SUBCOMMAND(dcc_closeall_subcmd)1716 DCC_SUBCOMMAND(dcc_closeall_subcmd)
1717 {
1718 char * my_argv[3];
1719
1720 my_argv[0] = LOCAL_COPY("-all");
1721 my_argv[1] = LOCAL_COPY("-all");
1722 dcc_close_subcmd(2, my_argv, NULL);
1723 }
1724
1725 /*
1726 * Usage: /DCC GET <nick> [file|*]
1727 * The '*' file gets all offered files.
1728 */
dcc_get(char * args)1729 static void dcc_get (char *args)
1730 {
1731 int argc;
1732 char * argv[10];
1733
1734 argv[0] = LOCAL_COPY("GET");
1735 argc = split_args(args, &argv[1], 9);
1736 dcc_get_subcmd(argc + 1, argv, NULL);
1737 }
1738
1739 #ifdef MIRC_BROKEN_DCC_RESUME
1740 /*
1741 * Usage: /DCC RESUME <nick> [file|*]
1742 * The '*' file gets all offered files.
1743 */
dcc_resume(char * args)1744 static void dcc_resume (char *args)
1745 {
1746 int argc;
1747 char * argv[10];
1748
1749 argv[0] = LOCAL_COPY("RESUME");
1750 argc = split_args(args, &argv[1], 9);
1751 dcc_get_subcmd(argc + 1, argv, NULL);
1752 }
1753 #endif
1754
handle_invalid_savedir(const char * pathname)1755 static void handle_invalid_savedir (const char *pathname)
1756 {
1757 say("DCC GET: Can't save file because %s is not a valid directory.",
1758 pathname);
1759 say("DCC GET: Check /SET DCC_STORE_PATH and try again.");
1760 }
1761
DCC_SUBCOMMAND(dcc_get_subcmd)1762 DCC_SUBCOMMAND(dcc_get_subcmd)
1763 {
1764 char *user;
1765 char *filename = NULL;
1766 DCC_list *dcc;
1767 Filename default_savedir = "";
1768 Filename fullname = "";
1769 Filename pathname = "";
1770 int savedir_is_invalid = 0;
1771 int file;
1772 char *realfilename = NULL;
1773 int count = 0, i, j;
1774 Stat sb;
1775 int proto;
1776 const char * x = NULL;
1777 int resume;
1778 Bucket * b;
1779
1780 if (argc < 2)
1781 {
1782 say("You must supply a nickname for DCC GET");
1783 return;
1784 }
1785
1786 /* Figure out if this is DCC GET or DCC RESUME */
1787 if (!strcmp(argv[0], "RESUME"))
1788 resume = 1;
1789 else
1790 resume = 0;
1791
1792 /* Figure out whose offer we will accept */
1793 user = argv[1];
1794
1795 lock_dcc(NULL);
1796
1797 /* Handle directory as the last argument */
1798 normalize_filename(argv[argc-1], pathname);
1799 if (isdir(pathname))
1800 {
1801 /* Pretend the user did /set dcc_store_path argv[argc-1] */
1802 x = pathname;
1803 argv[argc-1] = NULL;
1804 argc--;
1805 }
1806
1807 /* Handle a straight rename */
1808 else if (argc == 4 && dcc_get_bucket(NULL, user, argv[2]) == 1 &&
1809 dcc_get_bucket(NULL, user, argv[3]) == 0)
1810 {
1811 /* Pretend the user did /dcc rename get <user> argv[2] argv[3] */
1812 if (!(b = new_bucket()))
1813 {
1814 yell("DCC GET: new_bucket() failed. [1] help!");
1815 return;
1816 }
1817
1818 dcc_get_bucket(b, user, argv[2]);
1819 dcc = b->list[0].stuff;
1820 malloc_strcpy(&dcc->description, argv[3]);
1821 free_bucket(&b);
1822
1823 /* Pretend the user did /dcc get <user> argv[3] */
1824 argv[2] = argv[3];
1825 argc = 3;
1826 }
1827
1828 /* Calculate "default_savedir" */
1829 if (!x)
1830 x = get_string_var(DCC_STORE_PATH_VAR);
1831 if (!x || !*x)
1832 x = "./";
1833
1834 if (normalize_filename(x, default_savedir))
1835 savedir_is_invalid = 1;
1836
1837 if (argc == 2)
1838 {
1839 filename = NULL;
1840 j = 4;
1841 goto jumpstart_get;
1842 }
1843
1844 for (j = 2; j < argc; j++)
1845 {
1846 filename = argv[j];
1847 jumpstart_get:
1848 if (!(b = new_bucket()))
1849 {
1850 yell("DCC GET: new_bucket() failed. [2] Help!");
1851 return;
1852 }
1853 count = dcc_get_bucket(b, user, filename);
1854 for (i = 0; i < count; i++)
1855 {
1856 /* Skip any null pointers. sigh. */
1857 if (!(dcc = b->list[i].stuff))
1858 continue;
1859
1860 lock_dcc(dcc);
1861
1862 /*
1863 * Figure out where we will save this file. If the user has
1864 * /DCC RENAMEd this file to an absolute path, we honor that.
1865 * Otherwise, we use the default directory from above
1866 * (09/24/2003)
1867 */
1868 realfilename = dcc_urldecode(dcc->description);
1869 if (*realfilename == '/')
1870 strlcpy(fullname, realfilename, sizeof(fullname));
1871 else
1872 {
1873 if (savedir_is_invalid == 1)
1874 {
1875 handle_invalid_savedir(x);
1876 savedir_is_invalid++;
1877 new_free(&realfilename);
1878 unlock_dcc(dcc);
1879 continue;
1880 }
1881
1882 strlcpy(fullname, default_savedir, sizeof(fullname));
1883 strlcat(fullname, "/", sizeof(fullname));
1884 strlcat(fullname, realfilename, sizeof(fullname));
1885 }
1886
1887 new_free(&realfilename);
1888 dcc->local_filename = malloc_strdup(fullname);
1889 dcc->open_callback = NULL;
1890
1891 #ifdef MIRC_BROKEN_DCC_RESUME
1892 if (resume && get_int_var(MIRC_BROKEN_DCC_RESUME_VAR) &&
1893 stat(fullname, &sb) != -1)
1894 {
1895 dcc->bytes_sent = 0;
1896 dcc->bytes_read = dcc->resume_size = sb.st_size;
1897
1898 if ((file = open(fullname, O_WRONLY|O_APPEND, 0644)) == -1)
1899 {
1900 say("Unable to open %s: %s", fullname, strerror(errno));
1901 unlock_dcc(dcc);
1902 continue;
1903 }
1904 dcc->file = file;
1905 dcc->flags |= DCC_RESUME_REQ;
1906
1907 if (((SA *)&dcc->offer)->sa_family == AF_INET)
1908 malloc_strcpy(&dcc->othername,
1909 ltoa(ntohs(V4PORT(dcc->offer))));
1910
1911 if (x_debug & DEBUG_DCC_XMIT)
1912 yell("SENDING DCC RESUME to [%s] [%s|%s|%ld]",
1913 user, filename, dcc->othername,
1914 (long)sb.st_size);
1915
1916 /* Just in case we have to fool the protocol enforcement. */
1917 proto = get_server_protocol_state(from_server);
1918 set_server_protocol_state(from_server, 0);
1919 send_ctcp(1, user, "DCC",
1920 #if 1
1921 strchr(dcc->description, ' ')
1922 ? "RESUME \"%s\" %s %ld"
1923 : "RESUME %s %s %ld",
1924 dcc->description,
1925 #else
1926 /* This is for testing mirc compatability */
1927 "RESUME file.ext %s %ld",
1928 #endif
1929 dcc->othername, (long)sb.st_size);
1930 set_server_protocol_state(from_server, proto);
1931 }
1932 else
1933 #endif
1934 {
1935 if ((file = open(fullname, O_WRONLY|O_TRUNC|O_CREAT, 0644))==-1)
1936 {
1937 say("Unable to open %s: %s", fullname, strerror(errno));
1938 unlock_dcc(dcc);
1939 continue;
1940 }
1941
1942 dcc->file = file;
1943 dcc->flags |= DCC_TWOCLIENTS;
1944 dcc_connect(dcc); /* Nonblocking should be ok here */
1945 unlock_dcc(dcc);
1946 }
1947 }
1948 }
1949 unlock_dcc(NULL);
1950
1951 if (!count)
1952 {
1953 if (filename)
1954 say("No file (%s) offered in SEND mode by %s", filename, user);
1955 else
1956 say("No file offered in SEND mode by %s", user);
1957 }
1958 }
1959
1960 /*
1961 * Calculates transfer speed based on size, start time, and current time.
1962 */
calc_speed(intmax_t sofar,Timeval sta,Timeval cur)1963 static char * calc_speed (intmax_t sofar, Timeval sta, Timeval cur)
1964 {
1965 static char buf[7];
1966 double tdiff = time_diff(sta, cur);
1967
1968 if (sofar == 0 || tdiff <= 0.0)
1969 snprintf(buf, sizeof(buf), "N/A");
1970 else
1971 snprintf(buf, sizeof(buf), "%4.1f",
1972 ((sofar / 1024.0) / tdiff));
1973 return buf;
1974 }
1975
1976 /*
1977 * Packs a file size into a smaller representation of Kb, Mb, or Gb.
1978 * I'm sure it can be done less kludgy.
1979 */
calc_size(intmax_t fsize,char * retval,size_t retsize)1980 static char * calc_size (intmax_t fsize, char *retval, size_t retsize)
1981 {
1982 if (fsize < 1 << 10)
1983 snprintf(retval, retsize, INTMAX_FORMAT, fsize);
1984 else if (fsize < 1 << 20)
1985 snprintf(retval, retsize, "%3.1fKb", fsize / (double)(1 << 10));
1986 else if (fsize < 1 << 30)
1987 snprintf(retval, retsize, "%3.1fMb", fsize / (double)(1 << 20));
1988 else
1989 snprintf(retval, retsize, "%3.1fGb", fsize / (double)(1 << 30));
1990
1991 return retval;
1992 }
1993
1994 /*
1995 * Usage: /DCC LIST
1996 */
dcc_list(char * args)1997 static void dcc_list (char *args)
1998 {
1999 DCC_list *dcc;
2000 DCC_list *next;
2001 static const char *format =
2002 "%-7.7s%-3.3s %-9.9s %-9.9s %-20.20s %-6.6s %-5.5s %-6.6s %s";
2003 char *encoded_description;
2004 int l;
2005
2006 l = message_from(NULL, LEVEL_OTHER); /* Gulp */
2007
2008 if (do_hook(DCC_LIST_LIST, "Start * * * * * * *"))
2009 {
2010 put_it(format, "Type", " ", "Nick", "Status", "Start time",
2011 "Size", "Compl", "Kb/s", "Args");
2012 }
2013
2014 lock_dcc(NULL);
2015 for (dcc = ClientList ; dcc != NULL ; dcc = next)
2016 {
2017 unsigned flags = dcc->flags;
2018
2019 next = dcc->next;
2020 lock_dcc(dcc);
2021
2022 if ((flags & DCC_TYPES) == DCC_FILEOFFER)
2023 encoded_description = dcc_urlencode(dcc->description);
2024 else
2025 /* assume the other end encoded the filename */
2026 encoded_description = malloc_strdup(dcc->description);
2027
2028 if (do_hook(DCC_LIST_LIST, "%s %s %s %s " INTMAX_FORMAT " "INTMAX_FORMAT
2029 " "INTMAX_FORMAT" %s",
2030 dcc_types[flags & DCC_TYPES],
2031 zero, /* No encryption */
2032 dcc->user,
2033 flags & DCC_DELETE ? "Closed" :
2034 flags & DCC_ACTIVE ? "Active" :
2035 flags & DCC_MY_OFFER ? "Waiting" :
2036 flags & DCC_THEIR_OFFER ? "Offered" :
2037 "Unknown",
2038 (intmax_t)dcc->starttime.tv_sec,
2039 dcc->filesize,
2040 dcc->bytes_sent ? dcc->bytes_sent
2041 : dcc->bytes_read,
2042 encoded_description))
2043 {
2044 char * filename = strrchr(dcc->description, '/');
2045 char completed[9];
2046 char size[9];
2047 char speed[9];
2048 char buf[23];
2049 const char * time_f;
2050 intmax_t tot_size;
2051 intmax_t act_sent;
2052
2053 /*
2054 * Figure out something sane for the filename
2055 */
2056 if (!filename || get_int_var(DCC_LONG_PATHNAMES_VAR))
2057 filename = dcc->description;
2058 else if (filename && *filename)
2059 filename++;
2060
2061 if (!filename)
2062 filename = LOCAL_COPY(ltoa(get_pending_bytes(dcc->socket)));
2063
2064 /*
2065 * Figure out how many bytes we have sent for *this*
2066 * session, and how many bytes of the file have been
2067 * sent *in total*.
2068 */
2069 if (dcc->bytes_sent)
2070 {
2071 tot_size = dcc->bytes_sent;
2072 act_sent = tot_size - dcc->resume_size;
2073 }
2074 else
2075 {
2076 tot_size = dcc->bytes_read;
2077 act_sent = tot_size - dcc->resume_size;
2078 }
2079
2080 /*
2081 * Figure out something sane for the "completed" and
2082 * "size" fields.
2083 */
2084 if (dcc->filesize)
2085 {
2086 double prop;
2087 long perc;
2088
2089 prop = (double)tot_size / dcc->filesize;
2090 perc = prop * 100;
2091
2092 snprintf(completed, sizeof completed, "%ld%%", perc);
2093 calc_size(dcc->filesize, size, sizeof(size));
2094 }
2095 else
2096 {
2097 calc_size(tot_size, completed, sizeof(completed));
2098 *size = 0;
2099 }
2100
2101
2102 /*
2103 * Figure out something sane for starting time
2104 */
2105 if (dcc->starttime.tv_sec)
2106 {
2107 time_t blech = dcc->starttime.tv_sec;
2108 struct tm *btime = localtime(&blech);
2109
2110 strftime(buf, 22, "%T %b %d %Y", btime);
2111 time_f = buf;
2112 }
2113 else
2114 time_f = empty_string;
2115
2116 /*
2117 * Figure out something sane for the xfer speed.
2118 */
2119 if (act_sent)
2120 {
2121 strlcpy(speed, calc_speed(act_sent,
2122 dcc->starttime, get_time(NULL)), sizeof speed);
2123 }
2124 else
2125 *speed = 0;
2126
2127 /*
2128 * And do the dirty work.
2129 */
2130 put_it(format,
2131 dcc_types[flags&DCC_TYPES],
2132 empty_string, /* No encryption */
2133 dcc->user,
2134 flags & DCC_DELETE ? "Closed" :
2135 flags & DCC_ACTIVE ? "Active" :
2136 flags & DCC_MY_OFFER ? "Waiting" :
2137 flags & DCC_THEIR_OFFER ? "Offered" :
2138 "Unknown",
2139 time_f, size, completed, speed, filename);
2140 }
2141 if (encoded_description)
2142 new_free(&encoded_description);
2143 unlock_dcc(dcc);
2144 }
2145 unlock_dcc(NULL);
2146 do_hook(DCC_LIST_LIST, "End * * * * * * *");
2147 pop_message_from(l);
2148 }
2149
2150
2151 /*
2152 * Usage: /DCC RAW <filedesc> <host> [text]
2153 */
dcc_send_raw(char * args)2154 static void dcc_send_raw (char *args)
2155 {
2156 char *name;
2157 char *host;
2158 int l;
2159
2160 if (!(name = next_arg(args, &args)))
2161 {
2162 say("No name specified for DCC RAW");
2163 return;
2164 }
2165 if (!(host = next_arg(args, &args)))
2166 {
2167 say("No hostname specified for DCC RAW");
2168 return;
2169 }
2170
2171 l = message_from(name, LEVEL_DCC);
2172 dcc_message_transmit(DCC_RAW, name, host, args, args, 1, NULL);
2173 pop_message_from(l);
2174 }
2175
2176 /*
2177 * Usage: /DCC RENAME [-CHAT] <nick> [<oldname>] <newname>
2178 */
dcc_rename(char * args)2179 static void dcc_rename (char *args)
2180 {
2181 DCC_list *dcc;
2182 const char *user;
2183 const char *oldf;
2184 const char *newf;
2185 char *temp;
2186 int type = DCC_FILEREAD;
2187
2188 if (!(user = next_arg(args, &args)) || !(temp = next_arg(args, &args)))
2189 {
2190 say("You must specify a nick and new filename for DCC RENAME");
2191 return;
2192 }
2193
2194 if ((newf = next_arg(args, &args)) != NULL)
2195 oldf = temp;
2196 else
2197 {
2198 newf= temp;
2199 oldf = NULL;
2200 }
2201
2202 if (!my_strnicmp(user, "-CHAT", strlen(user)))
2203 {
2204 if (!oldf || !newf)
2205 {
2206 say("You must specify a new nickname for DCC RENAME -CHAT");
2207 return;
2208 }
2209 user = oldf;
2210 oldf = "chat";
2211 type = DCC_CHAT;
2212 }
2213
2214 if ((dcc = dcc_searchlist(type, user, oldf, NULL, -1)))
2215 {
2216 if (type == DCC_FILEREAD && (dcc->flags & DCC_ACTIVE))
2217 {
2218 say("Too late to rename that file");
2219 return;
2220 }
2221
2222 if (type == DCC_FILEREAD)
2223 {
2224 say("File %s from %s renamed to %s",
2225 dcc->description ? dcc->description : "<any>",
2226 user, newf);
2227 malloc_strcpy(&dcc->description, newf);
2228 }
2229 else
2230 {
2231 if (is_channel(newf))
2232 {
2233 say("I can't permit you to DCC RENAME to "
2234 "something that looks like a channel, "
2235 "sorry.");
2236 return;
2237 }
2238
2239 say("DCC CHAT from %s changed to new nick %s",
2240 user, newf);
2241 malloc_strcpy(&dcc->user, newf);
2242 }
2243 }
2244 else
2245 say("%s has not yet offered you the file %s",
2246 user, oldf ? oldf : "<any>");
2247 }
2248
2249 /*
2250 * Usage: /DCC SEND <nick> <filename> [<filename>]
2251 */
dcc_filesend(char * args)2252 static void dcc_filesend (char *args)
2253 {
2254 char *user,
2255 *this_arg;
2256 Filename fullname;
2257 unsigned short portnum = 0;
2258 int filenames_parsed = 0;
2259 DCC_list *Client;
2260 Stat stat_buf;
2261 int family;
2262
2263 /*
2264 * For sure, at least one argument is needed, the target
2265 */
2266 if ((user = next_arg(args, &args)))
2267 {
2268 family = AF_INET;
2269
2270 while (args && *args)
2271 {
2272 this_arg = new_next_arg(args, &args);
2273
2274 /*
2275 * Check to see if its a flag
2276 */
2277 if (*this_arg == '-')
2278 {
2279 if (this_arg[1] == 'p')
2280 {
2281 if (args && *args)
2282 portnum = my_atol(next_arg(args, &args));
2283 }
2284 #ifdef INET6
2285 else if (this_arg[1] == '6')
2286 family = AF_INET6;
2287 #endif
2288 else if (this_arg[1] == '4')
2289 family = AF_INET;
2290
2291 continue;
2292 }
2293
2294 /*
2295 * Ok. We have a filename they want to send. Check to
2296 * see what kind it is.
2297 */
2298 if (normalize_filename(this_arg, fullname))
2299 {
2300 say("%s is not a valid directory", fullname);
2301 continue;
2302 }
2303
2304 /*
2305 * Make a note that we've seen a filename
2306 */
2307 filenames_parsed++;
2308
2309 #ifdef I_DONT_TRUST_MY_USERS
2310 /*
2311 * Dont allow the user to send a file that is in "/etc" or
2312 * a file that ends in "/passwd". Presumably this is for
2313 * your safety. If you can figure out how to get around this,
2314 * then i guess you dont need this protection.
2315 */
2316 if (!strncmp(fullname, "/etc/", 5) ||
2317 !end_strcmp(fullname, "/passwd", 7))
2318 {
2319 say("Send Request Rejected");
2320 continue;
2321 }
2322 #endif
2323
2324 if (access(fullname, R_OK))
2325 {
2326 say("Cannot send %s because you dont have read permission", fullname);
2327 continue;
2328 }
2329
2330 if (isdir(fullname))
2331 {
2332 say("Cannot send %s because it is a directory", fullname);
2333 continue;
2334 }
2335
2336 stat(fullname, &stat_buf);
2337 if ((Client = dcc_searchlist(DCC_FILEOFFER, user, fullname,
2338 this_arg, -1)))
2339 {
2340 if ((Client->flags & DCC_ACTIVE) ||
2341 (Client->flags & DCC_MY_OFFER))
2342 {
2343 say("Sending a booster CTCP handshake for "
2344 "an existing DCC SEND:%s to %s",
2345 fullname, user);
2346 dcc_send_booster_ctcp(Client);
2347 continue;
2348 }
2349 }
2350 else
2351 Client = dcc_create(DCC_FILEOFFER, user, fullname,
2352 this_arg, family, stat_buf.st_size);
2353
2354 Client->flags |= DCC_TWOCLIENTS;
2355 Client->want_port = portnum;
2356 fill_in_default_port(Client);
2357 dcc_listen(Client);
2358 } /* The WHILE */
2359 } /* The IF */
2360
2361 if (!filenames_parsed)
2362 yell("Usage: /DCC SEND <[=]nick> <file> [<file> ...]");
2363 }
2364
2365
2366 /*
2367 * Usage: $listen(<port> <family>)
2368 */
dcc_raw_listen(int family,unsigned short port)2369 char *dcc_raw_listen (int family, unsigned short port)
2370 {
2371 DCC_list *Client;
2372 const char * PortName = empty_string;
2373 int l;
2374
2375 lock_dcc(NULL);
2376 l = message_from(NULL, LEVEL_DCC);
2377
2378 do
2379 {
2380 if (port && port < 1024)
2381 {
2382 say("May not bind to a privileged port");
2383 break;
2384 }
2385 PortName = LOCAL_COPY(ltoa(port));
2386
2387 if ((Client = dcc_searchlist(DCC_RAW_LISTEN, PortName, NULL,
2388 NULL, -1)))
2389 {
2390 if ((Client->flags & DCC_ACTIVE) ||
2391 (Client->flags & DCC_MY_OFFER))
2392 {
2393 say("A previous DCC RAW_LISTEN on %s exists", PortName);
2394 break;
2395 }
2396 }
2397 else
2398 Client = dcc_create(DCC_RAW_LISTEN, PortName, "raw_listen",
2399 NULL, family, 0);
2400
2401 lock_dcc(Client);
2402 Client->want_port = port;
2403 if (dcc_listen(Client)) /* Not a connect(). */
2404 {
2405 unlock_dcc(Client);
2406 break;
2407 }
2408
2409 get_time(&Client->starttime);
2410 Client->flags |= DCC_ACTIVE;
2411 Client->user = malloc_strdup(ltoa(Client->want_port));
2412 unlock_dcc(Client);
2413 }
2414 while (0);
2415
2416 unlock_dcc(NULL);
2417 dcc_garbage_collect();
2418 pop_message_from(l);
2419 return malloc_strdup(PortName);
2420 }
2421
2422 /*
2423 * Usage: $connect(<hostname> <portnum> <family>)
2424 */
dcc_raw_connect(const char * host,const char * port,int family)2425 char *dcc_raw_connect (const char *host, const char *port, int family)
2426 {
2427 DCC_list * Client = NULL;
2428 SS my_sockaddr;
2429 char retval[12];
2430 int l;
2431
2432 *retval = 0;
2433 lock_dcc(NULL);
2434 l = message_from(NULL, LEVEL_DCC);
2435
2436 do
2437 {
2438 memset(&my_sockaddr, 0, sizeof(my_sockaddr));
2439 FAMILY(my_sockaddr) = family;
2440 if (inet_strton(host, port, (SA *)&my_sockaddr, AI_ADDRCONFIG))
2441 {
2442 say("Unknown host: %s", host);
2443 break;
2444 }
2445
2446 if ((Client = dcc_searchlist(DCC_RAW, port, host, NULL, -1)))
2447 {
2448 if (Client->flags & DCC_ACTIVE)
2449 {
2450 say("A previous DCC RAW to %s on %s exists", host, port);
2451 break;
2452 }
2453 }
2454 else
2455 Client = dcc_create(DCC_RAW, port, host, NULL, family, 0);
2456
2457 lock_dcc(Client);
2458 Client->offer = my_sockaddr;
2459 Client->flags = DCC_THEIR_OFFER | DCC_RAW;
2460 if (dcc_connect(Client)) /* Nonblocking from here */
2461 {
2462 unlock_dcc(Client);
2463 break;
2464 }
2465 unlock_dcc(Client);
2466 snprintf(retval, sizeof retval, "%d", Client->socket);
2467 }
2468 while (0);
2469
2470 unlock_dcc(NULL);
2471 pop_message_from(l);
2472 dcc_garbage_collect();
2473 return malloc_strdup(retval);
2474 }
2475
2476
2477
2478 /*
2479 *
2480 * All the rest of this file is dedicated to automatic replies generated
2481 * by the client in response to external stimuli from DCC connections.
2482 *
2483 */
2484
2485
2486
2487 /*
2488 * When a user does a CTCP DCC, it comes here for preliminary parsing.
2489 *
2490 * XXX This function is not really family independant (but it's close)
2491 */
register_dcc_offer(const char * user,char * type,char * description,char * address,char * port,char * size,char * extra,char * rest)2492 void register_dcc_offer (const char *user, char *type, char *description, char *address, char *port, char *size, char *extra, char *rest)
2493 {
2494 DCC_list * dcc = NULL;
2495 int dtype;
2496 char * c;
2497 unsigned short realport; /* Bleh */
2498 char p_addr[256];
2499 int err;
2500 SS offer;
2501 intmax_t filesize;
2502 int l;
2503
2504 /*
2505 * Ensure that nobody will mess around with us while we're working.
2506 */
2507 lock_dcc(NULL);
2508 l = message_from(NULL, LEVEL_DCC);
2509
2510 do
2511 {
2512 /* If we're debugging, give the user the raw handshake details. */
2513 if (x_debug & DEBUG_DCC_SEARCH)
2514 yell("register_dcc_offer: [%s|%s|%s|%s|%s|%s|%s]",
2515 user, type, description, address, port, size, extra);
2516
2517 /* If they offer us a path with directory, ignore the directory. */
2518 if ((c = strrchr(description, '/')))
2519 description = c + 1;
2520
2521 /* If they offer us a hidden ("dot") file, mangle the dot. */
2522 if (*description == '.')
2523 *description = '_';
2524
2525 /* If they give us a file size, set the global variable */
2526 if (size && *size)
2527 filesize = STRNUM(size);
2528 else
2529 filesize = 0;
2530
2531 /*
2532 * Figure out if it's a type of offer we support.
2533 */
2534 if (!my_stricmp(type, "CHAT"))
2535 dtype = DCC_CHAT;
2536 else if (!my_stricmp(type, "SEND"))
2537 {
2538 if (!size || !*size || !is_number(size))
2539 {
2540 say("DCC SEND (%s) received from %s without a file size",
2541 description, user);
2542 break;
2543 }
2544 dtype = DCC_FILEREAD;
2545 }
2546 #ifdef MIRC_BROKEN_DCC_RESUME
2547 else if (!my_stricmp(type, "RESUME"))
2548 {
2549 /*
2550 * Dont be deceieved by the arguments we're passing it.
2551 * The arguments are "out of order" because MIRC doesnt
2552 * send them in the traditional order. Ugh.
2553 */
2554 if (!port || !*port)
2555 {
2556 say("DCC RESUME received from %s without a resume location",
2557 user);
2558 break;
2559 }
2560 dcc_getfile_resume_demanded(user, description, address, port);
2561 break;
2562 }
2563 else if (!my_stricmp(type, "ACCEPT"))
2564 {
2565 /*
2566 * See the comment above.
2567 */
2568 dcc_getfile_resume_start (user, description, address, port);
2569 break;
2570 }
2571 #endif
2572 else
2573 {
2574 say("Unknown DCC %s (%s) received from %s",
2575 type, description, user);
2576 break;
2577 }
2578
2579 /* CHECK HANDSHAKE ADDRESS FOR VALIDITY */
2580 /*
2581 * Convert the handshake address to a sockaddr. If it cannot be
2582 * figured out, warn the user and punt.
2583 */
2584 memset(&offer, 0, sizeof(offer));
2585 V4FAM(offer) = AF_UNSPEC;
2586 if ((err = inet_strton(address, port, (SA *)&offer, AI_NUMERICHOST)))
2587 {
2588 say("DCC %s (%s) request from %s had mangled return address "
2589 "[%s] (%d)", type, description, user, address, err);
2590 break;
2591 }
2592
2593 /*
2594 * Convert the sockaddr back to a name that we can print.
2595 * What we've got now is the original handshake address, a
2596 * sockaddr, and a p-addr.
2597 */
2598 if (inet_ntostr((SA *)&offer, p_addr, 256, NULL, 0, NI_NUMERICHOST))
2599 {
2600 say("DCC %s (%s) request from %s could not be converted back "
2601 "into a p-addr [%s] [%s]",
2602 type, description, user, address, port);
2603 break;
2604 }
2605
2606 /*
2607 * Check for invalid or illegal IP addresses.
2608 */
2609 if (FAMILY(offer) == AF_INET)
2610 {
2611 if (V4ADDR(offer).s_addr == 0)
2612 {
2613 yell("### DCC handshake from %s ignored becuase it had "
2614 "an null address", user);
2615 break;
2616 }
2617 }
2618 #ifdef INET6
2619 else if (FAMILY(offer) == AF_INET6)
2620 {
2621 /* Reserved for future expansion */
2622 }
2623 #endif
2624
2625 #ifdef HACKED_DCC_WARNING
2626 /*
2627 * Check for hacked (forged) IP addresses. A handshake is considered
2628 * forged if the address in the handshake is not the same address that
2629 * the user is using on irc. This is not turned on by default because
2630 * it can make epic block on dns lookups, which rogue users can use
2631 * to make your life painful, and also because a lot of networks are
2632 * using faked hostnames on irc, which makes this a waste of time.
2633 */
2634 if (FAMILY(offer) == AF_INET)
2635 {
2636 char * fromhost;
2637 SS irc_addr;
2638
2639 if (!(fromhost = strchr(FromUserHost, '@')))
2640 {
2641 yell("### Incoming handshake from a non-user peer!");
2642 break;
2643 }
2644
2645 fromhost++;
2646 FAMILY(irc_addr) = FAMILY(offer);
2647 if (inet_strton(fromhost, port, (SA *)&irc_addr, AI_ADDRCONFIG))
2648 {
2649 yell("### Incoming handshake has an address or port "
2650 "[%s:%s] that could not be figured out!",
2651 fromhost, port);
2652 yell("### Please use caution in deciding whether to "
2653 "accept it or not");
2654 }
2655 else if (FAMILY(offer) == AF_INET)
2656 {
2657 if (V4ADDR(irc_addr).s_addr != V4ADDR(offer).s_addr)
2658 {
2659 say("WARNING: Fake dcc handshake detected! [%x]",
2660 V4ADDR(offer).s_addr);
2661 say("Unless you know where this dcc request is "
2662 "coming from");
2663 say("It is recommended you ignore it!");
2664 }
2665 }
2666 }
2667 #ifdef INET6
2668 else if (FAMILY(offer) == AF_INET6)
2669 {
2670 /* Reserved for future expansion */
2671 }
2672 #endif
2673 #endif
2674
2675 /* CHECK HANDSHAKE PORT FOR VALIDITY */
2676 if ((realport = strtoul(port, NULL, 10)) < 1024)
2677 {
2678 say("DCC %s (%s) request from %s rejected because it "
2679 "specified reserved port number (%hu) [%s]",
2680 type, description, user,
2681 realport, port);
2682 break;
2683 }
2684
2685 /*******************************************************************
2686 * So now we've checked the address, and it's ok, we've checked the
2687 * port, and it's ok, and we've checked the file size, and it's ok.
2688 * We are now ready to get down to business!
2689 *******************************************************************/
2690
2691 /* Get ourselves a new dcc entry. */
2692 if ((dcc = dcc_searchlist(dtype, user, description, NULL, -1)))
2693 {
2694 /*
2695 * If DCC_MY_OFFER is set, that means we have already sent this
2696 * person this same DCC offer they sent us. Now we have a race
2697 * condition. They will try to accept ours and we will try to
2698 * accept theirs. Let's see who wins!
2699 */
2700 if (dcc->flags & DCC_MY_OFFER)
2701 {
2702 /* Revoke the offer I made to them. */
2703 dcc->flags |= DCC_DELETE;
2704
2705 /*
2706 * If they offered us a DCC CHAT, create a new entry for
2707 * this new offer to us.
2708 *
2709 * DCC CHAT collision -- do it automatically.
2710 */
2711 if (dtype == DCC_CHAT)
2712 {
2713 char *copy;
2714
2715 say("DCC CHAT already requested by %s, connecting.", user);
2716 dcc_create(dtype, user, "chat", NULL, FAMILY(offer), filesize);
2717 copy = LOCAL_COPY(user);
2718 dcc_chat(copy);
2719 break;
2720 }
2721
2722 /*
2723 * Otherwise, we're trying to send the same file to each other.
2724 * We just punt here because I don't know what else to do.
2725 */
2726 else
2727 {
2728 say("DCC %s collision for %s:%s", type, user, description);
2729 send_ctcp(0, user, "DCC", "DCC %s collision occured while connecting to %s (%s)",
2730 type, get_server_nickname(from_server), description);
2731 break;
2732 }
2733 }
2734
2735 /*
2736 * If DCC_ACTIVE is set, that means we already have an open
2737 * connection to them, either a file transfer or a dcc chat.
2738 * We definitely can't have another one open, so punt on this
2739 * new one.
2740 */
2741 if (dcc->flags & DCC_ACTIVE)
2742 {
2743 say("Received DCC %s request from %s while previous "
2744 "session still active", type, user);
2745 break;
2746 }
2747 }
2748 else
2749 dcc = dcc_create(dtype, user, description, NULL,
2750 FAMILY(offer), filesize);
2751
2752 /*
2753 * Otherwise, we're good to go! Mark this dcc as being something
2754 * they offered to us, and copy over the port and address.
2755 */
2756 dcc->flags |= DCC_THEIR_OFFER;
2757 memcpy(&dcc->offer, &offer, sizeof offer);
2758
2759 /*
2760 * DCC SEND and CHAT have different arguments, so they can't
2761 * very well use the exact same hooked data. Both now are
2762 * identical for $0-4, and SEND adds filename/size in $5-6
2763 */
2764 lock_dcc(dcc);
2765 if ((dcc->flags & DCC_TYPES) == DCC_FILEREAD)
2766 {
2767 if (do_hook(DCC_REQUEST_LIST, "%s %s %s %s %s %s "INTMAX_FORMAT,
2768 user, type, description, p_addr, port,
2769 dcc->description, dcc->filesize))
2770 goto display_it;
2771 }
2772 else
2773 if (do_hook(DCC_REQUEST_LIST, "%s %s %s %s %s",
2774 user, type, description, p_addr, port))
2775 goto display_it;
2776 unlock_dcc(dcc);
2777
2778 /* All done! */
2779 break;
2780
2781 display_it:
2782 /*
2783 * This is kind of a detour we take because the user didn't grab
2784 * the ONs and we need to output something to tell them about it.
2785 */
2786 unlock_dcc(dcc);
2787
2788 /*
2789 * For DCC SEND offers, if we already have the file, we need to warn
2790 * the user so they don't accidentally delete one of their files!
2791 */
2792 if ((dcc->flags & DCC_TYPES) == DCC_FILEREAD)
2793 {
2794 Stat statit;
2795 char * realfilename;
2796
2797 if (get_string_var(DCC_STORE_PATH_VAR))
2798 realfilename = malloc_sprintf(NULL, "%s/%s",
2799 get_string_var(DCC_STORE_PATH_VAR),
2800 dcc->description);
2801 else
2802 realfilename = malloc_strdup(dcc->description);
2803
2804
2805 if (stat(realfilename, &statit) == 0)
2806 {
2807 int xclose = 0, resume = 0, ren = 0, get = 0;
2808
2809 if ((intmax_t)statit.st_size < dcc->filesize)
2810 {
2811 say("WARNING: File [%s] exists but is smaller than "
2812 "the file offered.", realfilename);
2813 xclose = resume = ren = get = 1;
2814 }
2815 else if ((intmax_t)statit.st_size == dcc->filesize)
2816 {
2817 say("WARNING: File [%s] exists, and its the same size.",
2818 realfilename);
2819 xclose = ren = get = 1;
2820 }
2821 else
2822 {
2823 say("WARNING: File [%s] already exists.", realfilename);
2824 xclose = ren = get = 1;
2825 }
2826
2827 if (xclose)
2828 say("Use /DCC CLOSE GET %s %s to not get the file.",
2829 user, dcc->description);
2830 #ifdef MIRC_BROKEN_DCC_RESUME
2831 if (resume)
2832 say("Use /DCC RESUME %s %s to continue the "
2833 "copy where it left off.",
2834 user, dcc->description);
2835 #endif
2836 if (get)
2837 say("Use /DCC GET %s %s to overwrite the "
2838 "existing file.",
2839 user, dcc->description);
2840 if (ren)
2841 say("Use /DCC RENAME %s %s newname to save it to a "
2842 "different filename.",
2843 user, dcc->description);
2844 }
2845 new_free(&realfilename);
2846 }
2847
2848 /* Thanks, Tychy! (lherron@imageek.york.cuny.edu) */
2849 if ((dcc->flags & DCC_TYPES) == DCC_FILEREAD)
2850 say("DCC %s (%s "INTMAX_FORMAT") request received "
2851 "from %s!%s [%s (%s)]",
2852 type, description, dcc->filesize, user,
2853 FromUserHost, p_addr, port);
2854 else
2855 say("DCC %s (%s) request received from %s!%s [%s (%s)]",
2856 type, description, user, FromUserHost, p_addr, port);
2857 }
2858 while (0);
2859
2860 if (dcc)
2861 {
2862 get_time(&dcc->lasttime);
2863 get_time(&dcc->starttime);
2864 }
2865
2866 unlock_dcc(NULL);
2867 dcc_garbage_collect();
2868 pop_message_from(l);
2869 return;
2870 }
2871
2872 /*
2873 * Check all DCCs for data, and if they have any, perform whatever
2874 * actions are required.
2875 */
do_dcc(int fd)2876 void do_dcc (int fd)
2877 {
2878 DCC_list *Client;
2879 int previous_server;
2880 int l;
2881 int found_it = 0;
2882
2883 /* Sanity */
2884 if (fd < 0)
2885 return;
2886
2887 /* Whats with all this double-pointer chicanery anyhow? */
2888 lock_dcc(NULL);
2889
2890 for (Client = ClientList; Client; Client = Client->next)
2891 {
2892 if (Client->socket == fd)
2893 {
2894 found_it = 1;
2895 previous_server = from_server;
2896 from_server = FROMSERV;
2897
2898 l = message_from(NULL, LEVEL_DCC);
2899
2900 if (Client->flags & DCC_DELETE)
2901 {
2902 say("DCC fd %d is ready, client is deleted. Closing.", fd);
2903 Client->socket = new_close(Client->socket);
2904 }
2905 else switch (Client->flags & DCC_TYPES)
2906 {
2907 case DCC_CHAT:
2908 process_dcc_chat(Client);
2909 break;
2910 case DCC_RAW_LISTEN:
2911 process_incoming_listen(Client);
2912 break;
2913 case DCC_RAW:
2914 process_incoming_raw(Client);
2915 break;
2916 case DCC_FILEOFFER:
2917 process_dcc_send(Client);
2918 break;
2919 case DCC_FILEREAD:
2920 process_incoming_file(Client);
2921 break;
2922 }
2923 get_time(&Client->lasttime);
2924 pop_message_from(l);
2925
2926 from_server = previous_server;
2927 }
2928
2929 /*
2930 * Don't time out raw_listen sockets.
2931 */
2932 else if ((Client->flags & DCC_TYPES) == DCC_RAW_LISTEN)
2933 /* nothing */(void)0;
2934
2935 /*
2936 * This shouldnt be neccesary any more, but what the hey,
2937 * I'm still paranoid.
2938 */
2939 if (!Client)
2940 break;
2941 }
2942
2943 if (!found_it)
2944 {
2945 yell("DCC callback for fd %d but it doesn't exist any more! "
2946 "Closing it. Wish me luck!", fd);
2947 new_close(fd);
2948 }
2949
2950 unlock_dcc(NULL);
2951 dcc_garbage_collect();
2952 }
2953
2954
2955
2956 /*********************************** DCC CHAT *******************************/
2957 /*
2958 * This is a "unix_accept" callback, which is invoked when a remote user
2959 * accepts our dcc chat offer. unix_accept sends us the file descriptor
2960 * (from accept(2)) and the getpeeraddr() from that socket.
2961 *
2962 * At this point, we can close our listen(2) socket (in Client->socket) and
2963 * use the new accept(2) socket instead, and start the connection.
2964 */
process_dcc_chat_connection(DCC_list * Client)2965 static void process_dcc_chat_connection (DCC_list *Client)
2966 {
2967 char p_addr[256];
2968 char p_port[24];
2969 SA * addr;
2970 int fd;
2971 int c1, c2;
2972
2973 c1 = DGETS(Client->socket, fd)
2974 c2 = DGETS(Client->socket, Client->peer_sockaddr)
2975 if (c1 != sizeof(fd) || c2 != sizeof(Client->peer_sockaddr))
2976 {
2977 Client->flags |= DCC_DELETE;
2978 yell("### DCC Error: accept() failed.");
2979 return;
2980 }
2981
2982 Client->socket = new_close(Client->socket);
2983 if ((Client->socket = fd) > 0)
2984 new_open(Client->socket, do_dcc, NEWIO_RECV, 1, Client->server);
2985 else
2986 {
2987 Client->flags |= DCC_DELETE;
2988 yell("### DCC Error: accept() failed. Punting.");
2989 return;
2990 }
2991
2992 Client->flags &= ~DCC_MY_OFFER;
2993 Client->flags |= DCC_ACTIVE;
2994
2995 addr = (SA *)&Client->peer_sockaddr;
2996 inet_ntostr(addr, p_addr, 256, p_port, 24, NI_NUMERICHOST);
2997
2998 lock_dcc(Client);
2999 if (do_hook(DCC_CONNECT_LIST, "%s CHAT %s %s",
3000 Client->user, p_addr, p_port))
3001 say("DCC chat connection to %s[%s:%s] established",
3002 Client->user, p_addr, p_port);
3003 unlock_dcc(Client);
3004
3005 get_time(&Client->starttime);
3006 }
3007
process_dcc_chat_error(DCC_list * Client)3008 static void process_dcc_chat_error (DCC_list *Client)
3009 {
3010 lock_dcc(Client);
3011 if (do_hook(DCC_LOST_LIST, "%s CHAT ERROR", Client->user))
3012 say("DCC CHAT connection to %s lost", Client->user);
3013 Client->flags |= DCC_DELETE;
3014 unlock_dcc(Client);
3015 return;
3016 }
3017
process_dcc_chat_ctcps(DCC_list * Client,char * tmp)3018 static char * process_dcc_chat_ctcps (DCC_list *Client, char *tmp)
3019 {
3020 char equal_nickname[80];
3021 int ctcp_request = 0, ctcp_reply = 0;
3022 int l;
3023 char *target = NULL, *extra = NULL;
3024
3025 #define CTCP_MESSAGE "CTCP_MESSAGE "
3026 #define CTCP_REPLY "CTCP_REPLY "
3027
3028 if (*tmp == CTCP_DELIM_CHAR)
3029 ctcp_request = 1;
3030 else if (!strncmp(tmp, CTCP_MESSAGE, strlen(CTCP_MESSAGE)))
3031 {
3032 ov_strcpy(tmp, tmp + strlen(CTCP_MESSAGE));
3033 ctcp_request = 1;
3034 }
3035 else if (!strncmp(tmp, CTCP_REPLY, strlen(CTCP_REPLY)))
3036 {
3037 ov_strcpy(tmp, tmp + strlen(CTCP_REPLY));
3038 ctcp_reply = 1;
3039 }
3040
3041 if (ctcp_request == 1 || ctcp_reply == 1)
3042 {
3043 const char *OFUH = FromUserHost;
3044
3045 if (Client->userhost && *Client->userhost)
3046 FromUserHost = Client->userhost;
3047 else
3048 FromUserHost = unknown_userhost;
3049
3050 /*
3051 * So 'inbound_recode' will decode 'tmp' in place,
3052 * UNLESS 'tmp' isn't big enough to hold the new text.
3053 * In that case, 'extra' is malloc()ed and it will
3054 * hold the new text.
3055 * So if 'extra' is null, 'tmp' already holds it.
3056 * If 'extra' is not null, 'tmp' needs to point to it.
3057 */
3058 target = dcc_target(Client->user);
3059 inbound_recode(target, -1, empty_string, tmp, &extra);
3060 if (extra)
3061 tmp = extra;
3062
3063 l = message_from(target, LEVEL_CTCP);
3064 if (ctcp_request == 1)
3065 tmp = do_ctcp(1, target, nickname, tmp);
3066 else
3067 tmp = do_ctcp(0, target, nickname, tmp);
3068 pop_message_from(l);
3069
3070 FromUserHost = OFUH;
3071
3072 new_free(&target);
3073 new_free(&extra);
3074 }
3075
3076 if (!tmp || !*tmp)
3077 return NULL;
3078
3079 return tmp;
3080 }
3081
process_dcc_chat_data(DCC_list * Client)3082 static void process_dcc_chat_data (DCC_list *Client)
3083 {
3084 char tmp[IO_BUFFER_SIZE + 1];
3085 ssize_t bytesread;
3086 int l;
3087 const char * OFUH = FromUserHost;
3088 char * target;
3089 const char * utf8_text = NULL;
3090 char * extra = NULL;
3091
3092 /* Get a new line via dgets. */
3093 bytesread = dgets(Client->socket, tmp, IO_BUFFER_SIZE, 1);
3094
3095 /*
3096 * bytesread == 0 means there was new data, but it was an incomplete
3097 * line. Since we allow dgets() to buffer for us, we just ignore
3098 * this and wait for the rest of the line later.
3099 */
3100 if (bytesread == 0)
3101 return;
3102
3103 /*
3104 * bytesread == -1 means the connection just totaly died.
3105 */
3106 if (bytesread == -1)
3107 {
3108 process_dcc_chat_error(Client);
3109 return;
3110 }
3111
3112 /*
3113 * Otherwise, handle a new DCC CHAT message.
3114 */
3115 Client->bytes_read += bytesread;
3116
3117 chomp(tmp);
3118
3119 /* Tell the user... */
3120 if (x_debug & DEBUG_INBOUND)
3121 yell("DCC: [%d] <- [%s]", Client->socket, tmp);
3122
3123 /* Handle any CTCPs... */
3124 /* If the message is empty, ignore it... */
3125 if (!process_dcc_chat_ctcps(Client, tmp) || !*tmp)
3126 return;
3127
3128 /* Otherwise throw the message to the user. */
3129 target = dcc_target(Client->user);
3130 utf8_text = inbound_recode(target, -1, empty_string, tmp, &extra);
3131
3132 l = message_from(target, LEVEL_DCC);
3133 lock_dcc(Client);
3134 if (Client->userhost && *Client->userhost)
3135 FromUserHost = Client->userhost;
3136 else
3137 FromUserHost = unknown_userhost;
3138
3139 if (do_hook(DCC_CHAT_LIST, "%s %s", Client->user, utf8_text))
3140 {
3141 char timestr[256];
3142
3143 *timestr = 0;
3144 if (get_server_away(NOSERV))
3145 snprintf(timestr, sizeof(timestr), " <%s>", my_ctime(time(NULL)));
3146
3147 put_it("=%s= %s%s", Client->user, utf8_text, timestr);
3148 }
3149
3150 FromUserHost = OFUH;
3151 unlock_dcc(Client);
3152 pop_message_from(l);
3153
3154 new_free(&target);
3155 new_free(&extra);
3156 }
3157
3158 /*
3159 * This is a unix_connect() callback which is invoked when a connect(2)ing
3160 * socket is ready to write (which means the connection is complete or failed)
3161 * This is used when we connect to someone else's offer
3162 */
process_dcc_chat_connected(DCC_list * dcc)3163 static void process_dcc_chat_connected (DCC_list *dcc)
3164 {
3165 lock_dcc(dcc);
3166 if (x_debug & DEBUG_SERVER_CONNECT)
3167 yell("process_dcc_chat_connected: dcc [%s] now ready to write",
3168 dcc->user);
3169
3170 if (dcc_get_connect_addrs(dcc))
3171 {
3172 if (do_hook(DCC_LOST_LIST, "%s CHAT ERROR", dcc->user))
3173 say("DCC CHAT connection to %s lost", dcc->user);
3174 dcc->flags |= DCC_DELETE;
3175 unlock_dcc(dcc);
3176 return;
3177 }
3178
3179 dcc_connected(dcc->socket);
3180 dcc->flags &= ~DCC_CONNECTING;
3181 unlock_dcc(dcc);
3182 }
3183
process_dcc_chat(DCC_list * Client)3184 static void process_dcc_chat (DCC_list *Client)
3185 {
3186 if (Client->flags & DCC_MY_OFFER)
3187 process_dcc_chat_connection(Client);
3188 else if (Client->flags & DCC_CONNECTING)
3189 process_dcc_chat_connected(Client);
3190 else
3191 process_dcc_chat_data(Client);
3192 }
3193
3194
3195 /****************************** DCC RAW *************************************/
3196 /*
3197 * This is a unix_accept() callback invoked whenever we accept(2) an incoming
3198 * connection to our listen(2)ing socket. This creates a new DCC RAW, and
3199 * the original DCC RAW listen is unchanged.
3200 */
process_incoming_listen(DCC_list * Client)3201 static void process_incoming_listen (DCC_list *Client)
3202 {
3203 SS remaddr;
3204 int new_socket;
3205 char fdstr[10];
3206 DCC_list *NewClient;
3207 char host[1025];
3208 socklen_t len;
3209 char p_port[24];
3210 char l_port[24];
3211 char trash[1025] = "";
3212 int c1, c2;
3213
3214 c1 = DGETS(Client->socket, new_socket)
3215 c2 = DGETS(Client->socket, remaddr)
3216 if (c1 != sizeof(new_socket) || c2 != sizeof(remaddr))
3217 {
3218 Client->flags |= DCC_DELETE;
3219 yell("### DCC Error: accept() failed.");
3220 return;
3221 }
3222
3223 if (new_socket < 0)
3224 {
3225 yell("### DCC Error: accept() failed. Punting.");
3226 return;
3227 }
3228
3229 *host = 0;
3230 inet_ntostr((SA *)&remaddr, host, sizeof(host),
3231 p_port, sizeof(p_port), 0);
3232
3233 strlcpy(fdstr, ltoa(new_socket), sizeof fdstr);
3234 if (!(NewClient = dcc_searchlist(DCC_RAW, fdstr, host, NULL, 0)))
3235 NewClient = dcc_create(DCC_RAW, fdstr, host, NULL, 0, 0);
3236
3237 NewClient->socket = new_socket;
3238 NewClient->peer_sockaddr = remaddr;
3239 NewClient->offer = remaddr;
3240
3241 len = sizeof(NewClient->local_sockaddr);
3242 getsockname(NewClient->socket, (SA *)&NewClient->local_sockaddr, &len);
3243 inet_ntostr((SA *)&Client->local_sockaddr, trash, sizeof(trash),
3244 l_port, sizeof(l_port), 0);
3245
3246 NewClient->flags |= DCC_ACTIVE;
3247 NewClient->flags |= DCC_QUOTED & Client->flags;
3248 NewClient->bytes_read = NewClient->bytes_sent = 0;
3249 get_time(&NewClient->starttime);
3250 new_open(NewClient->socket, do_dcc, NEWIO_RECV, 1, NewClient->server);
3251
3252 lock_dcc(Client);
3253 if (do_hook(DCC_RAW_LIST, "%s %s N %s",
3254 NewClient->user, NewClient->description, l_port))
3255 if (do_hook(DCC_CONNECT_LIST,"%s RAW %s %s",
3256 NewClient->user, NewClient->description, l_port))
3257 say("DCC RAW connection to %s on %s via %s established",
3258 NewClient->description, NewClient->user, p_port);
3259 unlock_dcc(Client);
3260 }
3261
3262
3263 /*
3264 * This handles when someone sends you a line of info over a DCC RAW
3265 * connection (that was established with a $listen() or $connect()).
3266 */
process_dcc_raw_data(DCC_list * Client)3267 static void process_dcc_raw_data (DCC_list *Client)
3268 {
3269 char tmp[IO_BUFFER_SIZE + 1];
3270 char *bufptr;
3271 char * freeme = NULL;
3272 ssize_t bytesread;
3273
3274 bufptr = tmp;
3275 if (Client->flags & DCC_QUOTED)
3276 bytesread = dgets(Client->socket, bufptr, IO_BUFFER_SIZE, -1);
3277 else if (Client->packet_size > 0)
3278 bytesread = dgets(Client->socket, bufptr, Client->packet_size, 2);
3279 else if (Client->full_line_buffer)
3280 bytesread = dgets(Client->socket, bufptr, IO_BUFFER_SIZE, 1);
3281 else
3282 bytesread = dgets(Client->socket, bufptr, IO_BUFFER_SIZE, 0);
3283
3284 switch (bytesread)
3285 {
3286 CLOSE:
3287 case -1:
3288 {
3289 lock_dcc(Client);
3290 if (do_hook(DCC_RAW_LIST, "%s %s C",
3291 Client->user, Client->description))
3292 if (do_hook(DCC_LOST_LIST,"%s RAW %s",
3293 Client->user, Client->description))
3294 say("DCC RAW connection to %s on %s lost",
3295 Client->user, Client->description);
3296 Client->flags |= DCC_DELETE;
3297 unlock_dcc(Client);
3298 break;
3299 }
3300 case 0:
3301 {
3302 if (Client->flags & DCC_QUOTED)
3303 goto CLOSE;
3304 /* FALLTHROUGH */
3305 __attribute__((fallthrough));
3306 }
3307 default:
3308 {
3309 from_server = primary_server; /* Colten asked for it */
3310 if (Client->flags & DCC_QUOTED)
3311 {
3312 char * dest;
3313 if (!(dest = transform_string_dyn("+CTCP", bufptr,
3314 bytesread, NULL)))
3315 yell("DCC RAW: Could not CTCP enquote [%s]",
3316 bufptr);
3317 else
3318 freeme = bufptr = dest;
3319 }
3320 else if (bytesread > 0 && tmp[strlen(tmp) - 1] == '\n')
3321 tmp[strlen(tmp) - 1] = '\0';
3322 Client->bytes_read += bytesread;
3323
3324 lock_dcc(Client);
3325 if (do_hook(DCC_RAW_LIST, "%s %s D %s",
3326 Client->user, Client->description, bufptr))
3327 say("Raw data on %s from %s: %s",
3328 Client->user, Client->description, bufptr);
3329 unlock_dcc(Client);
3330 }
3331 }
3332 new_free(&freeme);
3333 return;
3334 }
3335
3336 /*
3337 * This is a unix_connect() callback called when your $connect() is writable
3338 * either connect()ed or it failed.
3339 */
process_dcc_raw_connected(DCC_list * dcc)3340 static void process_dcc_raw_connected (DCC_list *dcc)
3341 {
3342 lock_dcc(dcc);
3343 if (x_debug & DEBUG_SERVER_CONNECT)
3344 yell("process_dcc_raw_connected: dcc [%s] now ready to write",
3345 dcc->user);
3346
3347 if (dcc_get_connect_addrs(dcc))
3348 {
3349 if (do_hook(DCC_LOST_LIST, "%s RAW ERROR", dcc->user))
3350 say("DCC RAW connection to %s lost", dcc->user);
3351 dcc->flags |= DCC_DELETE;
3352 unlock_dcc(dcc);
3353 return;
3354 }
3355
3356 if (((SA *)&dcc->peer_sockaddr)->sa_family == AF_INET)
3357 malloc_strcpy(&dcc->othername,
3358 ltoa(ntohs(V4PORT(dcc->peer_sockaddr))));
3359 #ifdef INET6
3360 else if (((SA *)&dcc->peer_sockaddr)->sa_family == AF_INET6)
3361 malloc_strcpy(&dcc->othername,
3362 ltoa(ntohs(V6PORT(dcc->peer_sockaddr))));
3363 #endif
3364 else
3365 malloc_strcpy(&dcc->othername, "<any>");
3366
3367 dcc->user = malloc_strdup(ltoa(dcc->socket));
3368 do_hook(DCC_RAW_LIST, "%s %s E %s", dcc->user, dcc->description,
3369 dcc->othername);
3370
3371 dcc_connected(dcc->socket);
3372 dcc->flags &= ~DCC_CONNECTING;
3373 unlock_dcc(dcc);
3374 }
3375
3376
process_incoming_raw(DCC_list * Client)3377 static void process_incoming_raw (DCC_list *Client)
3378 {
3379 if (Client->flags & DCC_CONNECTING)
3380 process_dcc_raw_connected(Client);
3381 else
3382 process_dcc_raw_data(Client);
3383 }
3384
3385 /****************************** DCC SEND ************************************/
3386 /*
3387 * When youre sending a file, and your peer sends an ACK, this handles
3388 * whether or not to send the next packet.
3389 */
process_dcc_send_connection(DCC_list * dcc)3390 static void process_dcc_send_connection (DCC_list *dcc)
3391 {
3392 int new_fd;
3393 #ifdef HAVE_SO_SNDLOWAT
3394 int size;
3395 #endif
3396 SA * addr;
3397 char p_addr[256];
3398 char p_port[24];
3399 char *encoded_description;
3400 int c1, c2;
3401
3402 c1 = DGETS(dcc->socket, new_fd)
3403 c2 = DGETS(dcc->socket, dcc->peer_sockaddr)
3404 if (c1 != sizeof(new_fd) || c2 != sizeof(dcc->peer_sockaddr))
3405 {
3406 dcc->flags |= DCC_DELETE;
3407 yell("### DCC Error: accept() failed.");
3408 return;
3409 }
3410
3411 dcc->socket = new_close(dcc->socket);
3412 if ((dcc->socket = new_fd) < 0)
3413 {
3414 dcc->flags |= DCC_DELETE;
3415 yell("### DCC Error: accept() failed. Punting.");
3416 return;
3417 }
3418 new_open(dcc->socket, do_dcc, NEWIO_RECV, 1, dcc->server);
3419 dcc->flags &= ~DCC_MY_OFFER;
3420 dcc->flags |= DCC_ACTIVE;
3421 get_time(&dcc->starttime);
3422
3423 #ifdef HAVE_SO_SNDLOWAT
3424 /*
3425 * Give a hint to the OS how many bytes we need to send
3426 * for each write()
3427 */
3428 size = DCC_BLOCK_SIZE;
3429 if (setsockopt(dcc->socket, SOL_SOCKET, SO_SNDLOWAT,
3430 &size, sizeof(size)) < 0)
3431 say("setsockopt failed: %s", strerror(errno));
3432 #endif
3433
3434 addr = (SA *)&dcc->peer_sockaddr;
3435 inet_ntostr(addr, p_addr, 256, p_port, 24, NI_NUMERICHOST);
3436
3437 /*
3438 * Tell the user
3439 */
3440 lock_dcc(dcc);
3441 encoded_description = dcc_urlencode(dcc->description);
3442 if (do_hook(DCC_CONNECT_LIST, "%s SEND %s %s %s "INTMAX_FORMAT,
3443 dcc->user, p_addr, p_port,
3444 dcc->description, dcc->filesize))
3445 say("DCC SEND connection to %s[%s:%s] established",
3446 dcc->user, p_addr, p_port);
3447 new_free(&encoded_description);
3448 unlock_dcc(dcc);
3449
3450
3451 /*
3452 * Open up the file to be sent
3453 */
3454 if ((dcc->file = open(dcc->description, O_RDONLY)) == -1)
3455 {
3456 dcc->flags |= DCC_DELETE;
3457 say("Unable to open %s: %s", dcc->description, strerror(errno));
3458 return;
3459 }
3460
3461 /*
3462 * Set up any DCC RESUME as needed
3463 */
3464 if (dcc->bytes_sent)
3465 {
3466 if (x_debug & DEBUG_DCC_XMIT)
3467 yell("Resuming at address ("INTMAX_FORMAT")",
3468 dcc->bytes_sent);
3469 lseek(dcc->file, dcc->bytes_sent, SEEK_SET);
3470 }
3471 }
3472
process_dcc_send_handle_ack(DCC_list * dcc)3473 static void process_dcc_send_handle_ack (DCC_list *dcc)
3474 {
3475 char * encoded_description;
3476 u_32int_t bytes;
3477 intmax_t provisional_bytes;
3478
3479 if (x_debug & DEBUG_DCC_XMIT)
3480 yell("Reading a packet from [%s:%s("INTMAX_FORMAT")]",
3481 dcc->user, dcc->othername, dcc->bytes_acked);
3482
3483 /*
3484 * The acknowledgement is /ALWAYS/ a 32 bit integers in network
3485 * order, no matter what. Even if we've sent more than 2^32 bits,
3486 * the value wraps around to 0 again. ugh. bleh.
3487 */
3488 if (dgets(dcc->socket, (char *)&bytes, sizeof(bytes), -1) <
3489 (ssize_t)sizeof(bytes))
3490 {
3491 lock_dcc(dcc);
3492 encoded_description = dcc_urlencode(dcc->description);
3493 if (do_hook(DCC_LOST_LIST,"%s SEND %s CONNECTION LOST",
3494 dcc->user, encoded_description))
3495 say("DCC SEND:%s connection to %s lost",
3496 dcc->description, dcc->user);
3497 new_free(&encoded_description);
3498 dcc->flags |= DCC_DELETE;
3499 unlock_dcc(dcc);
3500 return;
3501 }
3502 bytes = ntohl(bytes);
3503
3504 /*
3505 * XXX Ok. Rollover logic is atrocious, but I wrote it in a hurry
3506 * and if you want to "fix" it, be my guest.
3507 *
3508 * Acknowledgements are always 32 bits. This is de facto the right
3509 * 32 bits in the number of bytes transferred. The number of bytes
3510 * transferred might exceed 32 bits, so we need to extrapolate what
3511 * the extra bits will be.
3512 *
3513 * Normally, we will substitute the right 32 bits of the last value
3514 * of "bytes_acked" with the new bytes value. This is fine and
3515 * dandy as long as there is no overflow.
3516 *
3517 * But if there is overflow, then the extended "bytes" will be less
3518 * than the previous value of "bytes_acked". In this case, we will
3519 * substitute the right 32 bits of the bytes SENT, since this would
3520 * be in theory a value rolled over from the bytes ACKed. If this
3521 * resulting value is reasonable, then we run with it, and this
3522 * effects the rollover.
3523 *
3524 * Ugh. Why am I wasting time supporting dcc sends > 2gb anyways?
3525 */
3526 provisional_bytes = dcc->bytes_acked;
3527 provisional_bytes = provisional_bytes >> 32;
3528 provisional_bytes = provisional_bytes << 32;
3529 provisional_bytes += bytes;
3530
3531 if (provisional_bytes < dcc->bytes_acked)
3532 {
3533 provisional_bytes = dcc->bytes_sent;
3534 provisional_bytes = provisional_bytes >> 32;
3535 provisional_bytes = provisional_bytes << 32;
3536 provisional_bytes += bytes;
3537 }
3538
3539 if (provisional_bytes > dcc->bytes_sent)
3540 {
3541 yell("### WARNING! The other peer claims to have recieved more bytes than");
3542 yell("### I have actually sent so far. Please report this to ");
3543 yell("### problems@epicsol.org or #epic on EFNet right away.");
3544 yell("### Ask the person who you sent the file to look for garbage at the");
3545 yell("### end of the file you just sent them. Please enclose that ");
3546 yell("### information as well as the following:");
3547 yell("###");
3548 yell("### bytesrecvd [%ld ("INTMAX_FORMAT")]", (long)bytes, provisional_bytes);
3549 yell("### dcc->bytes_sent ["INTMAX_FORMAT"]", dcc->bytes_sent);
3550 yell("### dcc->filesize ["INTMAX_FORMAT"]", dcc->filesize);
3551 yell("### dcc->bytes_acked ["INTMAX_FORMAT"]", dcc->bytes_acked);
3552
3553 /* And just cope with it to avoid whining */
3554 dcc->bytes_sent = provisional_bytes;
3555 }
3556
3557 if (provisional_bytes >= dcc->bytes_acked)
3558 {
3559 if (x_debug & DEBUG_DCC_XMIT)
3560 yell("Bytes to %ld ACKed", (long)bytes);
3561 dcc->bytes_acked = provisional_bytes;
3562 }
3563
3564 /*
3565 * If we've sent the whole file already...
3566 */
3567 if (dcc->bytes_sent >= dcc->filesize)
3568 {
3569 /*
3570 * If theyve ACKed the last packet, we close
3571 * the connection.
3572 */
3573 if (provisional_bytes >= dcc->filesize)
3574 DCC_close_filesend(dcc, "SEND", "TRANSFER COMPLETE");
3575
3576 /*
3577 * Either way there's nothing more to do.
3578 */
3579 return;
3580 }
3581 }
3582
3583
process_dcc_send_data(DCC_list * dcc)3584 static void process_dcc_send_data (DCC_list *dcc)
3585 {
3586 intmax_t fill_window;
3587 ssize_t bytesread;
3588 char tmp[DCC_BLOCK_SIZE+1];
3589 int old_from_server = from_server;
3590 char bytes_sent[10];
3591 char filesize[10];
3592
3593 /*
3594 * We use a nonblocking sliding window algorithm. We send as many
3595 * packets as we can *without blocking*, up to but never more than
3596 * the value of /SET DCC_SLIDING_WINDOW. Whenever we recieve some
3597 * stimulus (like from an ACK) we re-fill the window. We always do
3598 * a my_iswritable() before we write() to make sure that it wont block.
3599 */
3600 fill_window = get_int_var(DCC_SLIDING_WINDOW_VAR) * DCC_BLOCK_SIZE;
3601 if (fill_window < DCC_BLOCK_SIZE)
3602 fill_window = DCC_BLOCK_SIZE; /* Sanity */
3603
3604 while (dcc->bytes_sent - dcc->bytes_acked < fill_window)
3605 {
3606 /*
3607 * Check to make sure the write won't block.
3608 */
3609 if (my_iswritable(dcc->socket, 0) <= 0)
3610 break;
3611
3612 /*
3613 * Grab some more file. If this chokes, dont sweat it.
3614 */
3615 if ((bytesread = read(dcc->file, tmp, DCC_BLOCK_SIZE)) <= 0)
3616 break;
3617
3618 /*
3619 * Bug the user
3620 */
3621 if (x_debug & DEBUG_DCC_XMIT)
3622 yell("Sending packet [%s [%s] (packet XXX) (%ld bytes)]",
3623 dcc->user, dcc->othername, bytesread);
3624
3625 /*
3626 * Attempt to write the file. If it chokes, whine.
3627 */
3628 if (write(dcc->socket, tmp, bytesread) < bytesread)
3629 {
3630 dcc->flags |= DCC_DELETE;
3631 say("Outbound write() failed: %s", strerror(errno));
3632 from_server = old_from_server;
3633 return;
3634 }
3635
3636 dcc->bytes_sent += bytesread;
3637
3638 /* XXX When should this ever be possible? */
3639 if (!dcc->filesize)
3640 continue;
3641
3642 calc_size(dcc->bytes_sent, bytes_sent, sizeof(bytes_sent));
3643 calc_size(dcc->filesize, filesize, sizeof(filesize));
3644
3645 update_transfer_buffer(dcc, "(to %10s: %s of %s: %d%%)",
3646 dcc->user,
3647 bytes_sent, filesize,
3648 (int)(dcc->bytes_sent * 100.0 / dcc->filesize));
3649 }
3650 }
3651
process_dcc_send(DCC_list * dcc)3652 static void process_dcc_send (DCC_list *dcc)
3653 {
3654 if (dcc->flags & DCC_MY_OFFER)
3655 process_dcc_send_connection(dcc);
3656 else
3657 process_dcc_send_handle_ack(dcc);
3658
3659 process_dcc_send_data(dcc);
3660 }
3661
3662 /****************************** DCC GET ************************************/
3663 /*
3664 * When youre recieving a DCC GET file, this is called when the sender
3665 * sends you a portion of the file.
3666 *
3667 * There's no need to stick to the packet size here. If we have more than
3668 * one in the buffer, there's no benefit in sending out acks for all.
3669 * It's probably more polite to hold back on clobbering the sender with
3670 * redundant useless acks too.
3671 */
process_dcc_get_data(DCC_list * dcc)3672 static void process_dcc_get_data (DCC_list *dcc)
3673 {
3674 char tmp[DCC_RCV_BLOCK_SIZE+1];
3675 intmax_t provisional_bytesread;
3676 u_32int_t bytestemp;
3677 ssize_t bytesread;
3678 char bytes_read[10];
3679 char filesize[10];
3680
3681 /* Sanity check -- file size is not permitted to be omitted! */
3682 if (!dcc->filesize)
3683 {
3684 say("DCC GET from %s lost -- Filesize is 0, no data to xfer",
3685 dcc->user);
3686 DCC_close_filesend(dcc, "GET", "TRANSFER COMPLETE");
3687 return;
3688 }
3689
3690 /* Read the next chunk of the file from the remote peer */
3691 if ((bytesread = dgets(dcc->socket, tmp, sizeof(tmp), -1)) <= 0)
3692 {
3693 if (dcc->bytes_read < dcc->filesize)
3694 {
3695 say("DCC GET to %s lost -- Remote peer closed connection",
3696 dcc->user);
3697 DCC_close_filesend(dcc, "GET",
3698 "REMOTE PEER CLOSED CONNECTION");
3699 }
3700 else
3701 DCC_close_filesend(dcc, "GET", "TRANSFER COMPLETE");
3702
3703 return;
3704 }
3705
3706 /* Save the chunk to the local file */
3707 if ((write(dcc->file, tmp, bytesread)) == -1)
3708 {
3709 dcc->flags |= DCC_DELETE;
3710 say("Write to local file [%d] failed: %s",
3711 dcc->file, strerror(errno));
3712 return;
3713 }
3714
3715 /* Acknowledge receipt of the chunk */
3716 dcc->bytes_read += bytesread;
3717 provisional_bytesread = dcc->bytes_read;
3718 provisional_bytesread = provisional_bytesread >> 32;
3719 provisional_bytesread = provisional_bytesread << 32;
3720 bytestemp = (u_32int_t)(dcc->bytes_read - provisional_bytesread);
3721 bytestemp = htonl(bytestemp);
3722 if (write(dcc->socket, (char *)&bytestemp, sizeof(u_32int_t)) == -1)
3723 {
3724 dcc->flags |= DCC_DELETE;
3725 yell("### Writing DCC GET checksum back to %s failed. "
3726 "Giving up.", dcc->user);
3727 return;
3728 }
3729
3730 /* TAKE THIS OUT IF IT CAUSES PROBLEMS */
3731 if (dcc->bytes_read > dcc->filesize)
3732 {
3733 dcc->flags |= DCC_DELETE;
3734 yell("### DCC GET WARNING: incoming file is larger then the "
3735 "handshake said");
3736 yell("### DCC GET: Closing connection");
3737 return;
3738 }
3739
3740 /* Tell the user about it */
3741 calc_size(dcc->bytes_read, bytes_read, sizeof(bytes_read));
3742 calc_size(dcc->filesize, filesize, sizeof(filesize));
3743 update_transfer_buffer(dcc, "(to %10s: %s of %s: %d%%)",
3744 dcc->user,
3745 bytes_read, filesize,
3746 (int)(dcc->bytes_read * 100.0 / dcc->filesize));
3747 }
3748
process_dcc_get_connected(DCC_list * dcc)3749 static void process_dcc_get_connected (DCC_list *dcc)
3750 {
3751 lock_dcc(dcc);
3752 if (x_debug & DEBUG_SERVER_CONNECT)
3753 yell("process_dcc_get_connected: dcc [%s] now ready to write",
3754 dcc->user);
3755
3756 if (dcc_get_connect_addrs(dcc))
3757 {
3758 char *edesc = dcc_urlencode(dcc->description);
3759
3760 if (do_hook(DCC_LOST_LIST, "%s GET %s ERROR", dcc->user, edesc))
3761 say("DCC GET connection to %s lost", dcc->user);
3762 new_free(&edesc);
3763 dcc->flags |= DCC_DELETE;
3764 unlock_dcc(dcc);
3765 return;
3766 }
3767
3768 dcc_connected(dcc->socket);
3769 dcc->flags &= ~DCC_CONNECTING;
3770 unlock_dcc(dcc);
3771 }
3772
process_incoming_file(DCC_list * dcc)3773 static void process_incoming_file (DCC_list *dcc)
3774 {
3775 if (dcc->flags & DCC_CONNECTING)
3776 process_dcc_get_connected(dcc);
3777 else
3778 process_dcc_get_data(dcc);
3779 }
3780
3781
3782 /***************************************************************************/
3783 /***************************************************************************/
3784
3785
3786 /*
3787 * This is a callback. When we want to do a CTCP DCC REJECT, we do
3788 * a WHOIS to make sure theyre still on irc, no sense sending it to
3789 * nobody in particular. When this gets called back, that means the
3790 * peer is indeed on irc, so we send them the REJECT.
3791 *
3792 * -- Changed this to be quasi-reentrant. yuck.
3793 */
output_reject_ctcp(int refnum,char * original,char * received)3794 static void output_reject_ctcp (int refnum, char *original, char *received)
3795 {
3796 /*char *nickname_requested;*/
3797 char *nickname_recieved;
3798 char *type;
3799 char *description;
3800 int old_fs;
3801
3802 /*
3803 * XXX This is, of course, a monsterous hack.
3804 */
3805 /*nickname_requested = */next_arg(original, &original);
3806 type = next_arg(original, &original);
3807 description = next_arg(original, &original);
3808 nickname_recieved = next_arg(received, &received);
3809
3810 if (nickname_recieved && *nickname_recieved)
3811 {
3812 /* XXX -- Ok, whatever. */
3813 dccs_rejected++;
3814
3815 old_fs = from_server;
3816 from_server = refnum;
3817 send_ctcp(0, nickname_recieved, "DCC", "REJECT %s %s", type, description);
3818 from_server = old_fs;
3819 }
3820
3821 }
3822
3823
3824 /*
3825 * This is called when someone sends you a CTCP DCC REJECT.
3826 */
dcc_reject(const char * from,char * type,char * args)3827 void dcc_reject (const char *from, char *type, char *args)
3828 {
3829 DCC_list * Client;
3830 char * description;
3831 int CType;
3832 int l;
3833
3834 for (CType = 0; dcc_types[CType] != NULL; CType++)
3835 if (!my_stricmp(type, dcc_types[CType]))
3836 break;
3837
3838 if (!dcc_types[CType])
3839 return;
3840
3841 l = message_from(from, LEVEL_DCC);
3842 description = next_arg(args, &args);
3843
3844 if ((Client = dcc_searchlist(CType, from, description,
3845 description, -1)))
3846 {
3847 lock_dcc(Client);
3848
3849 if (do_hook(DCC_LOST_LIST,"%s %s %s REJECTED", from, type,
3850 description ? description : "<any>"))
3851 say("DCC %s:%s rejected by %s: closing", type,
3852 description ? description : "<any>", from);
3853 Client->flags |= DCC_REJECTED;
3854 Client->flags |= DCC_DELETE;
3855 unlock_dcc(Client);
3856 }
3857
3858 dcc_garbage_collect();
3859 pop_message_from(l);
3860 }
3861
3862
DCC_close_filesend(DCC_list * Client,const char * info,const char * errormsg)3863 static void DCC_close_filesend (DCC_list *Client, const char *info,
3864 const char *errormsg)
3865 {
3866 char lame_ultrix[13]; /* should be plenty */
3867 char lame_ultrix2[13];
3868 char lame_ultrix3[13];
3869 double xtime, xfer;
3870 char *encoded_description;
3871
3872 /* XXX - Can't we do this by calling calc_speed? */
3873 xtime = time_diff(Client->starttime, get_time(NULL));
3874 xtime -= Client->heldtime;
3875 if (Client->bytes_sent)
3876 xfer = Client->bytes_sent - Client->resume_size;
3877 else
3878 xfer = Client->bytes_read - Client->resume_size;
3879 snprintf(lame_ultrix, sizeof(lame_ultrix),
3880 "%2.4g", (xfer / 1024.0 / xtime));
3881
3882 /* Cant pass %g to put_it (lame ultrix/dgux), fix suggested by sheik. */
3883 if (xfer <= 0)
3884 xfer = 1;
3885 snprintf(lame_ultrix2, sizeof(lame_ultrix2), "%2.4g", xfer / 1024.0);
3886
3887 if (xtime <= 0)
3888 xtime = 1;
3889 snprintf(lame_ultrix3, sizeof(lame_ultrix3), "%2.6g", xtime);
3890
3891 lock_dcc(Client);
3892
3893 if ((Client->flags & DCC_TYPES) == DCC_FILEOFFER)
3894 encoded_description = dcc_urlencode(Client->description);
3895 else
3896 /* assume the other end encoded the filename */
3897 encoded_description = malloc_strdup(Client->description);
3898
3899 if (do_hook(DCC_LOST_LIST,"%s %s %s %s %s",
3900 Client->user, info, encoded_description, lame_ultrix,
3901 errormsg))
3902 say("DCC %s:%s [%skb] with %s completed in %s sec (%s kb/sec)",
3903 info, Client->description, lame_ultrix2, Client->user,
3904 lame_ultrix3, lame_ultrix);
3905
3906 new_free(&encoded_description);
3907 Client->flags |= DCC_DELETE;
3908 unlock_dcc(Client);
3909 }
3910
3911
3912
3913 /*
3914 * Looks for the dcc transfer that is "current" (last recieved data)
3915 * and returns information for it
3916 */
DCC_get_current_transfer(void)3917 char * DCC_get_current_transfer (void)
3918 {
3919 return DCC_current_transfer_buffer;
3920 }
3921
3922
update_transfer_buffer(DCC_list * dcc,const char * format,...)3923 static void update_transfer_buffer (DCC_list *dcc, const char *format, ...)
3924 {
3925 if (!dcc_updates_status || (dcc && !dcc->updates_status))
3926 return;
3927
3928 if (format)
3929 {
3930 va_list args;
3931 va_start(args, format);
3932 vsnprintf(DCC_current_transfer_buffer,
3933 sizeof DCC_current_transfer_buffer,
3934 format, args);
3935 va_end(args);
3936 }
3937 else
3938 *DCC_current_transfer_buffer = 0;
3939
3940 if (do_hook(DCC_ACTIVITY_LIST, "%ld", dcc ? dcc->refnum : -1))
3941 update_all_status();
3942 }
3943
3944
dcc_urlencode(const char * s)3945 static char * dcc_urlencode (const char *s)
3946 {
3947 const char * p1;
3948 char * p2;
3949 char * src;
3950 size_t srclen;
3951 char * dest;
3952 size_t destsize;
3953
3954 src = LOCAL_COPY(s);
3955 for (p1 = s, p2 = src; *p1; p1++)
3956 {
3957 if (*p1 == '\\')
3958 continue;
3959 *p2++ = *p1;
3960 }
3961 *p2 = 0;
3962
3963 srclen = strlen(src);
3964 destsize = srclen * 3 + 2;
3965 dest = new_malloc(destsize);
3966 transform_string(URL_xform, XFORM_ENCODE, NULL,
3967 src, srclen, dest, destsize);
3968 return dest;
3969 }
3970
dcc_urldecode(const char * s)3971 static char * dcc_urldecode (const char *s)
3972 {
3973 char * p1;
3974 size_t srclen;
3975 char * dest;
3976 size_t destsize;
3977
3978 srclen = strlen(s);
3979 destsize = srclen + 2;
3980 dest = new_malloc(destsize);
3981 transform_string(URL_xform, XFORM_DECODE, NULL,
3982 s, srclen,
3983 dest, destsize);
3984
3985 for (p1 = dest; *p1; p1++)
3986 {
3987 if (*p1 != '.')
3988 break;
3989 *p1 = '_';
3990 }
3991
3992 return dest;
3993 }
3994
wait_for_dcc(const char * descriptor)3995 int wait_for_dcc (const char *descriptor)
3996 {
3997 DCC_list *dcc;
3998 char reason[1024];
3999 int fd;
4000
4001 if (!is_number(descriptor))
4002 {
4003 yell("File descriptor (%s) should be a number", descriptor);
4004 return -1;
4005 }
4006
4007 fd = atol(descriptor);
4008 if (!(dcc = get_dcc_by_filedesc(fd)))
4009 return -1;
4010
4011 if (!(dcc->flags & DCC_CONNECTING))
4012 return 0;
4013
4014 snprintf(reason, 1024, "WAIT on DCC %s", descriptor);
4015 lock_stack_frame();
4016 while (dcc->flags & DCC_CONNECTING)
4017 io(reason);
4018 unlock_stack_frame();
4019 return 0;
4020 }
4021
fill_in_default_port(DCC_list * dcc)4022 static void fill_in_default_port (DCC_list *dcc)
4023 {
4024 if (default_dcc_port)
4025 {
4026 char dccref[10];
4027 char *newport = NULL;
4028
4029 snprintf(dccref, sizeof(dccref), "%ld", dcc->refnum);
4030 newport = expand_alias(default_dcc_port, dccref);
4031 dcc->want_port = (unsigned short)my_atol(newport);
4032 new_free(&newport);
4033 }
4034 }
4035
4036
4037 /*
4038 * This stuff doesnt conform to the protocol.
4039 * Thanks mirc for disregarding the protocol.
4040 */
4041 #ifdef MIRC_BROKEN_DCC_RESUME
4042
4043 /*
4044 * When the peer demands DCC RESUME
4045 * We send out a DCC ACCEPT
4046 */
dcc_getfile_resume_demanded(const char * user,char * filename,char * port,char * offset)4047 static void dcc_getfile_resume_demanded (const char *user, char *filename, char *port, char *offset)
4048 {
4049 DCC_list *Client;
4050 int proto;
4051 char *realfilename = filename;
4052
4053 if (!get_int_var(MIRC_BROKEN_DCC_RESUME_VAR))
4054 return;
4055
4056 if (x_debug & DEBUG_DCC_XMIT)
4057 yell("GOT DCC RESUME REQUEST from [%s] [%s|%s|%s]", user, filename, port, offset);
4058
4059 if (!strcmp(filename, "file.ext"))
4060 filename = NULL;
4061
4062 if (!(Client = dcc_searchlist(DCC_FILEOFFER, user, filename, port, 0)))
4063 {
4064 if (x_debug & DEBUG_DCC_XMIT)
4065 yell("Resume request that doesnt exist. Hmmm.");
4066 return; /* Its a fake. */
4067 }
4068
4069 if (!offset)
4070 return; /* Its a fake */
4071
4072 Client->bytes_acked = Client->bytes_sent = Client->resume_size = STR2INT(offset);
4073 Client->bytes_read = 0;
4074
4075 /* Just in case we have to fool the protocol enforcement. */
4076 proto = get_server_protocol_state(from_server);
4077 set_server_protocol_state(from_server, 0);
4078
4079 send_ctcp(1, user, "DCC", "ACCEPT %s %s %s",
4080 realfilename, port, offset);
4081
4082 set_server_protocol_state(from_server, proto);
4083 /* Wait for them to open the connection */
4084 }
4085
4086
4087 /*
4088 * When we get the DCC ACCEPT
4089 * We start the connection
4090 */
dcc_getfile_resume_start(const char * nick,char * filename,char * port,char * offset)4091 static void dcc_getfile_resume_start (const char *nick, char *filename, char *port, char *offset)
4092 {
4093 DCC_list *Client;
4094
4095 if (!get_int_var(MIRC_BROKEN_DCC_RESUME_VAR))
4096 return;
4097
4098 if (x_debug & DEBUG_DCC_XMIT)
4099 yell("GOT CONFIRMATION FOR DCC RESUME from [%s] [%s]", nick, filename);
4100
4101 if (!strcmp(filename, "file.ext"))
4102 filename = NULL;
4103
4104 if (!(Client = dcc_searchlist(DCC_FILEREAD, nick, filename, port, 0)))
4105 return; /* Its fake. */
4106
4107 if (!(Client->flags & DCC_RESUME_REQ))
4108 {
4109 if (x_debug & DEBUG_DCC_XMIT)
4110 yell("Unsolicited DCC ACCEPT from [%s] [%s]", nick, filename);
4111 return;
4112 }
4113
4114 Client->flags |= DCC_TWOCLIENTS;
4115 dcc_connect(Client);
4116 }
4117
4118 #endif
4119
dccctl(char * input)4120 char * dccctl (char *input)
4121 {
4122 long ref;
4123 char * listc;
4124 int len;
4125 DCC_list * client;
4126 char * retval = NULL;
4127 size_t clue = 0;
4128
4129 GET_FUNC_ARG(listc, input);
4130 len = strlen(listc);
4131 if (!my_strnicmp(listc, "REFNUMS", len)) {
4132 for (client = ClientList; client; client = client->next)
4133 malloc_strcat_word_c(&retval, space, ltoa(client->refnum), DWORD_DWORDS, &clue);
4134 } else if (!my_strnicmp(listc, "REFBASE", len)) {
4135 int oldref = dcc_refnum;
4136 if (input && *input)
4137 GET_INT_ARG(dcc_refnum, input);
4138 RETURN_INT(oldref);
4139 } else if (!my_strnicmp(listc, "GET", len)) {
4140 GET_INT_ARG(ref, input);
4141
4142 if (!(client = get_dcc_by_refnum(ref)))
4143 RETURN_EMPTY;
4144
4145 GET_FUNC_ARG(listc, input);
4146 len = strlen(listc);
4147 if (!my_strnicmp(listc, "REFNUM", len)) {
4148 RETURN_INT(client->refnum);
4149 } else if (!my_strnicmp(listc, "TYPE", len)) {
4150 RETURN_STR(dcc_types[client->flags & DCC_TYPES]);
4151 } else if (!my_strnicmp(listc, "DESCRIPTION", len)) {
4152 RETURN_STR(client->description);
4153 } else if (!my_strnicmp(listc, "FILENAME", len)) {
4154 RETURN_STR(client->local_filename);
4155 } else if (!my_strnicmp(listc, "USER", len)) {
4156 RETURN_STR(client->user);
4157 } else if (!my_strnicmp(listc, "USERHOST", len)) {
4158 RETURN_STR(client->userhost);
4159 } else if (!my_strnicmp(listc, "OTHERNAME", len)) {
4160 RETURN_STR(client->othername);
4161 } else if (!my_strnicmp(listc, "SIZE", len)) {
4162 return INT2STR(client->filesize);
4163 } else if (!my_strnicmp(listc, "FILESIZE", len)) { /* DEPRECATED */
4164 return INT2STR(client->filesize);
4165 } else if (!my_strnicmp(listc, "RESUMESIZE", len)) {
4166 return INT2STR(client->resume_size);
4167 } else if (!my_strnicmp(listc, "READBYTES", len)) {
4168 return INT2STR(client->bytes_read);
4169 } else if (!my_strnicmp(listc, "SENTBYTES", len)) {
4170 return INT2STR(client->bytes_sent);
4171 } else if (!my_strnicmp(listc, "SERVER", len)) {
4172 RETURN_INT(client->server);
4173 } else if (!my_strnicmp(listc, "LOCKED", len)) {
4174 RETURN_INT(client->locked);
4175 } else if (!my_strnicmp(listc, "HELD", len)) {
4176 RETURN_INT(client->held);
4177 } else if (!my_strnicmp(listc, "HELDTIME", len)) {
4178 RETURN_FLOAT(client->heldtime);
4179 } else if (!my_strnicmp(listc, "QUOTED", len)) {
4180 RETURN_INT(client->flags & DCC_QUOTED && 1);
4181 } else if (!my_strnicmp(listc, "PACKET_SIZE", len)) {
4182 RETURN_INT(client->packet_size);
4183 } else if (!my_strnicmp(listc, "FULL_LINE_BUFFER", len)) {
4184 RETURN_INT(client->full_line_buffer);
4185 } else if (!my_strnicmp(listc, "FLAGS", len)) {
4186 /* This is pretty much a crock. */
4187 RETURN_INT(client->flags);
4188 } else if (!my_strnicmp(listc, "LASTTIME", len)) {
4189 malloc_strcat_word_c(&retval, space, ltoa(client->lasttime.tv_sec), DWORD_NO, &clue);
4190 malloc_strcat_word_c(&retval, space, ltoa(client->lasttime.tv_usec), DWORD_NO, &clue);
4191 } else if (!my_strnicmp(listc, "STARTTIME", len)) {
4192 malloc_strcat_word_c(&retval, space, ltoa(client->starttime.tv_sec), DWORD_NO, &clue);
4193 malloc_strcat_word_c(&retval, space, ltoa(client->starttime.tv_usec), DWORD_NO, &clue);
4194 } else if (!my_strnicmp(listc, "HOLDTIME", len)) {
4195 malloc_strcat_word_c(&retval, space, ltoa(client->holdtime.tv_sec), DWORD_NO, &clue);
4196 malloc_strcat_word_c(&retval, space, ltoa(client->holdtime.tv_usec), DWORD_NO, &clue);
4197 } else if (!my_strnicmp(listc, "OFFERADDR", len)) {
4198 char host[1025], port[25];
4199 if (inet_ntostr((SA *)&client->offer,
4200 host, sizeof(host),
4201 port, sizeof(port), NI_NUMERICHOST))
4202 RETURN_EMPTY;
4203 malloc_strcat_word_c(&retval, space, host, DWORD_NO, &clue);
4204 malloc_strcat_word_c(&retval, space, port, DWORD_NO, &clue);
4205 } else if (!my_strnicmp(listc, "REMADDR", len)) {
4206 char host[1025], port[25];
4207 if (inet_ntostr((SA *)&client->peer_sockaddr,
4208 host, sizeof(host),
4209 port, sizeof(port), NI_NUMERICHOST))
4210 RETURN_EMPTY;
4211 malloc_strcat_word_c(&retval, space, host, DWORD_NO, &clue);
4212 malloc_strcat_word_c(&retval, space, port, DWORD_NO, &clue);
4213 } else if (!my_strnicmp(listc, "LOCADDR", len)) {
4214 char host[1025], port[25];
4215 if (inet_ntostr((SA *)&client->local_sockaddr,
4216 host, sizeof(host),
4217 port, sizeof(port), NI_NUMERICHOST))
4218 RETURN_EMPTY;
4219 malloc_strcat_word_c(&retval, space, host, DWORD_NO, &clue);
4220 malloc_strcat_word_c(&retval, space, port, DWORD_NO, &clue);
4221 } else if (!my_strnicmp(listc, "READABLE", len)) {
4222 int retint;
4223 retint = my_isreadable(client->socket, 0) > 0;
4224 RETURN_INT(retint);
4225 } else if (!my_strnicmp(listc, "WRITABLE", len)) {
4226 int retint;
4227 retint = my_iswritable(client->socket, 0) > 0;
4228 RETURN_INT(retint);
4229 } else if (!my_strnicmp(listc, "UPDATES_STATUS", len)) {
4230 RETURN_INT(client->updates_status);
4231 } else if (!my_strnicmp(listc, "WANT_PORT", len)) {
4232 RETURN_INT(client->want_port);
4233 } else {
4234 RETURN_EMPTY;
4235 }
4236 } else if (!my_strnicmp(listc, "SET", len)) {
4237 GET_INT_ARG(ref, input);
4238
4239 if (!(client = get_dcc_by_refnum(ref)))
4240 RETURN_EMPTY;
4241
4242 GET_FUNC_ARG(listc, input);
4243 len = strlen(listc);
4244 if (!my_strnicmp(listc, "REFNUM", len)) {
4245 long newref;
4246
4247 GET_INT_ARG(newref, input);
4248 client->refnum = newref;
4249
4250 RETURN_INT(1);
4251 } else if (!my_strnicmp(listc, "DESCRIPTION", len)) {
4252 malloc_strcpy(&client->description, input);
4253 } else if (!my_strnicmp(listc, "FILENAME", len)) {
4254 malloc_strcpy(&client->local_filename, input);
4255 } else if (!my_strnicmp(listc, "USER", len)) {
4256 malloc_strcpy(&client->user, input);
4257 } else if (!my_strnicmp(listc, "USERHOST", len)) {
4258 malloc_strcpy(&client->userhost, input);
4259 } else if (!my_strnicmp(listc, "OTHERNAME", len)) {
4260 malloc_strcpy(&client->othername, input);
4261 } else if (!my_strnicmp(listc, "WANT_PORT", len)) {
4262 long newref;
4263 GET_INT_ARG(newref, input);
4264 client->want_port = (unsigned short)newref;
4265 RETURN_INT(1);
4266 } else if (!my_strnicmp(listc, "HELD", len)) {
4267 long hold, held;
4268
4269 GET_INT_ARG(hold, input);
4270 if (hold)
4271 held = dcc_hold(client);
4272 else
4273 held = dcc_unhold(client);
4274
4275 RETURN_INT(held);
4276 } else if (!my_strnicmp(listc, "QUOTED", len)) {
4277 long quoted;
4278
4279 GET_INT_ARG(quoted, input);
4280 if (quoted)
4281 client->flags |= DCC_QUOTED;
4282 else
4283 client->flags &= ~DCC_QUOTED;
4284 } else if (!my_strnicmp(listc, "FULL_LINE_BUFFER", len)) {
4285 long buffered;
4286
4287 GET_INT_ARG(buffered, input);
4288 if (buffered)
4289 client->full_line_buffer = 1;
4290 else
4291 client->full_line_buffer = 0;
4292 } else if (!my_strnicmp(listc, "PACKET_SIZE", len)) {
4293 long packet_size;
4294
4295 /* Validation? */
4296 GET_INT_ARG(packet_size, input);
4297 client->packet_size = packet_size;
4298 } else if (!my_strnicmp(listc, "OFFERADDR", len)) {
4299 char *host, *port;
4300 SS a;
4301
4302 GET_FUNC_ARG(host, input);
4303 GET_FUNC_ARG(port, input);
4304
4305 V4FAM(a) = AF_UNSPEC;
4306 if ((client->flags & DCC_ACTIVE) ||
4307 inet_strton(host, port, (SA *)&a, AI_ADDRCONFIG))
4308 RETURN_EMPTY;
4309
4310 memcpy(&client->offer, &a, sizeof client->offer);
4311 RETURN_INT(1);
4312 } else if (!my_strnicmp(listc, "UPDATES_STATUS", len)) {
4313 long updates;
4314
4315 GET_INT_ARG(updates, input);
4316 client->updates_status = updates;
4317 RETURN_INT(1);
4318 } else {
4319 RETURN_EMPTY;
4320 }
4321 RETURN_INT(1);
4322 } else if (!my_strnicmp(listc, "TYPEMATCH", len)) {
4323 for (client = ClientList; client; client = client->next)
4324 if (wild_match(input, dcc_types[client->flags & DCC_TYPES]))
4325 malloc_strcat_word_c(&retval, space, ltoa(client->refnum), DWORD_NO, &clue);
4326 } else if (!my_strnicmp(listc, "DESCMATCH", len)) {
4327 for (client = ClientList; client; client = client->next)
4328 if (wild_match(input, client->description ? client->description : EMPTY))
4329 malloc_strcat_word_c(&retval, space, ltoa(client->refnum), DWORD_NO, &clue);
4330 } else if (!my_strnicmp(listc, "FILEMATCH", len)) {
4331 for (client = ClientList; client; client = client->next)
4332 if (wild_match(input, client->local_filename ? client->local_filename : EMPTY))
4333 malloc_strcat_word_c(&retval, space, ltoa(client->refnum), DWORD_NO, &clue);
4334 } else if (!my_strnicmp(listc, "USERMATCH", len)) {
4335 for (client = ClientList; client; client = client->next)
4336 if (wild_match(input, client->user ? client->user : EMPTY))
4337 malloc_strcat_word_c(&retval, space, ltoa(client->refnum), DWORD_NO, &clue);
4338 } else if (!my_strnicmp(listc, "USERHOSTMATCH", len)) {
4339 for (client = ClientList; client; client = client->next)
4340 if (wild_match(input, client->userhost ? client->userhost : EMPTY))
4341 malloc_strcat_word_c(&retval, space, ltoa(client->refnum), DWORD_NO, &clue);
4342 } else if (!my_strnicmp(listc, "OTHERMATCH", len)) {
4343 for (client = ClientList; client; client = client->next)
4344 if (wild_match(input, client->othername ? client->othername : EMPTY))
4345 malloc_strcat_word_c(&retval, space, ltoa(client->refnum), DWORD_NO, &clue);
4346 } else if (!my_strnicmp(listc, "LOCKED", len)) {
4347 for (client = ClientList; client; client = client->next)
4348 if (client->locked)
4349 malloc_strcat_word_c(&retval, space, ltoa(client->refnum), DWORD_NO, &clue);
4350 } else if (!my_strnicmp(listc, "HELD", len)) {
4351 for (client = ClientList; client; client = client->next)
4352 if (client->held)
4353 malloc_strcat_word_c(&retval, space, ltoa(client->refnum), DWORD_NO, &clue);
4354 } else if (!my_strnicmp(listc, "UNHELD", len)) {
4355 for (client = ClientList; client; client = client->next)
4356 if (!client->held)
4357 malloc_strcat_word_c(&retval, space, ltoa(client->refnum), DWORD_NO, &clue);
4358 } else if (!my_strnicmp(listc, "READABLES", len)) {
4359 for (client = ClientList; client; client = client->next)
4360 {
4361 if (my_isreadable(client->socket, 0))
4362 malloc_strcat_word_c(&retval, space,
4363 ltoa(client->refnum), DWORD_NO, &clue);
4364 }
4365 } else if (!my_strnicmp(listc, "WRITABLES", len)) {
4366 for (client = ClientList; client; client = client->next)
4367 {
4368 if (my_iswritable(client->socket, 0))
4369 malloc_strcat_word_c(&retval, space,
4370 ltoa(client->refnum), DWORD_NO, &clue);
4371 }
4372 } else if (!my_strnicmp(listc, "UPDATES_STATUS", len)) {
4373 int oldval;
4374
4375 oldval = dcc_updates_status;
4376 if (input && *input) {
4377 int newval;
4378 GET_INT_ARG(newval, input);
4379 dcc_updates_status = newval;
4380 }
4381 RETURN_INT(oldval);
4382 } else if (!my_strnicmp(listc, "DEFAULT_PORT", len)) {
4383 if (empty(input))
4384 RETURN_STR(default_dcc_port);
4385 else
4386 malloc_strcpy(&default_dcc_port, input);
4387 RETURN_INT(1);
4388 } else if (!my_strnicmp(listc, "FD_TO_REFNUM", len)) {
4389 int fd;
4390 GET_INT_ARG(fd, input);
4391 for (client = ClientList; client; client = client->next) {
4392 if (client->socket == fd)
4393 RETURN_INT(client->refnum);
4394 }
4395 RETURN_INT(-1);
4396 } else
4397 RETURN_EMPTY;
4398
4399 RETURN_MSTR(retval);
4400 }
4401
4402 #if 0
4403 void help_topics_dcc (FILE *f)
4404 {
4405 int x;
4406
4407 for (x = 0; dcc_commands[x].name; x++)
4408 fprintf(f, "dcc %s\n", dcc_commands[x].name);
4409 }
4410 #endif
4411
4412