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