unrealircd

- supernets unrealircd source & configuration
git clone git://git.acid.vegas/unrealircd.git
Log | Files | Refs | Archive | README | LICENSE

dccdeny.c (22831B)

      1 /*
      2  *   IRC - Internet Relay Chat, src/modules/dccdeny.c
      3  *   (C) 2004-2019 The UnrealIRCd Team
      4  *
      5  *   See file AUTHORS in IRC package for additional names of
      6  *   the programmers.
      7  *
      8  *   This program is free software; you can redistribute it and/or modify
      9  *   it under the terms of the GNU General Public License as published by
     10  *   the Free Software Foundation; either version 1, or (at your option)
     11  *   any later version.
     12  *
     13  *   This program is distributed in the hope that it will be useful,
     14  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
     15  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     16  *   GNU General Public License for more details.
     17  *
     18  *   You should have received a copy of the GNU General Public License
     19  *   along with this program; if not, write to the Free Software
     20  *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
     21  */
     22 
     23 #include "unrealircd.h"
     24 
     25 ModuleHeader MOD_HEADER
     26   = {
     27 	"dccdeny",
     28 	"6.0.2",
     29 	"command /dccdeny", 
     30 	"UnrealIRCd Team",
     31 	"unrealircd-6",
     32     };
     33 
     34 /* Variables */
     35 ConfigItem_deny_dcc     *conf_deny_dcc = NULL;
     36 ConfigItem_allow_dcc    *conf_allow_dcc = NULL;
     37 
     38 /* Forward declarions */
     39 int dccdeny_configtest_deny_dcc(ConfigFile *cf, ConfigEntry *ce, int type, int *errs);
     40 int dccdeny_configtest_allow_dcc(ConfigFile *cf, ConfigEntry *ce, int type, int *errs);
     41 int dccdeny_configrun_deny_dcc(ConfigFile *cf, ConfigEntry *ce, int type);
     42 int dccdeny_configrun_allow_dcc(ConfigFile *cf, ConfigEntry *ce, int type);
     43 int dccdeny_stats(Client *client, const char *para);
     44 int dccdeny_dcc_denied(Client *client, const char *target, const char *realfile, const char *displayfile, ConfigItem_deny_dcc *dccdeny);
     45 CMD_FUNC(cmd_dccdeny);
     46 CMD_FUNC(cmd_undccdeny);
     47 CMD_FUNC(cmd_svsfline);
     48 int dccdeny_can_send_to_user(Client *client, Client *target, const char **text, const char **errmsg, SendType sendtype);
     49 int dccdeny_can_send_to_channel(Client *client, Channel *channel, Membership *lp, const char **msg, const char **errmsg, SendType sendtype);
     50 int dccdeny_server_sync(Client *client);
     51 static ConfigItem_deny_dcc *dcc_isforbidden(Client *client, const char *filename);
     52 static ConfigItem_deny_dcc *dcc_isdiscouraged(Client *client, const char *filename);
     53 static void DCCdeny_add(const char *filename, const char *reason, int type, int type2);
     54 static void DCCdeny_del(ConfigItem_deny_dcc *deny);
     55 static void dcc_wipe_services(void);
     56 static const char *get_dcc_filename(const char *text);
     57 static int can_dcc(Client *client, const char *target, Client *targetcli, const char *filename, const char **errmsg);
     58 static int can_dcc_soft(Client *from, Client *to, const char *filename, const char **errmsg);
     59 static void free_dcc_config_blocks(void);
     60 void dccdeny_unload_free_all_conf_deny_dcc(ModData *m);
     61 void dccdeny_unload_free_all_conf_allow_dcc(ModData *m);
     62 ConfigItem_deny_dcc *find_deny_dcc(const char *name);
     63 
     64 MOD_TEST()
     65 {
     66 	HookAdd(modinfo->handle, HOOKTYPE_CONFIGTEST, 0, dccdeny_configtest_deny_dcc);
     67 	HookAdd(modinfo->handle, HOOKTYPE_CONFIGTEST, 0, dccdeny_configtest_allow_dcc);
     68 	return MOD_SUCCESS;
     69 }
     70 
     71 MOD_INIT()
     72 {
     73 	MARK_AS_OFFICIAL_MODULE(modinfo);
     74 	LoadPersistentPointer(modinfo, conf_deny_dcc, dccdeny_unload_free_all_conf_deny_dcc);
     75 	LoadPersistentPointer(modinfo, conf_allow_dcc, dccdeny_unload_free_all_conf_allow_dcc);
     76 	HookAdd(modinfo->handle, HOOKTYPE_CONFIGRUN, 0, dccdeny_configrun_deny_dcc);
     77 	HookAdd(modinfo->handle, HOOKTYPE_CONFIGRUN, 0, dccdeny_configrun_allow_dcc);
     78 	CommandAdd(modinfo->handle, "DCCDENY", cmd_dccdeny, 2, CMD_USER);
     79 	CommandAdd(modinfo->handle, "UNDCCDENY", cmd_undccdeny, MAXPARA, CMD_USER);
     80 	CommandAdd(modinfo->handle, "SVSFLINE", cmd_svsfline, MAXPARA, CMD_SERVER);
     81 	HookAdd(modinfo->handle, HOOKTYPE_STATS, 0, dccdeny_stats);
     82 	HookAdd(modinfo->handle, HOOKTYPE_CAN_SEND_TO_USER, 0, dccdeny_can_send_to_user);
     83 	HookAdd(modinfo->handle, HOOKTYPE_CAN_SEND_TO_CHANNEL, 0, dccdeny_can_send_to_channel);
     84 	HookAdd(modinfo->handle, HOOKTYPE_SERVER_SYNC, 0, dccdeny_server_sync);
     85 	HookAdd(modinfo->handle, HOOKTYPE_DCC_DENIED, 0, dccdeny_dcc_denied);
     86 	return MOD_SUCCESS;
     87 }
     88 
     89 MOD_LOAD()
     90 {
     91 	return MOD_SUCCESS;
     92 }
     93 
     94 MOD_UNLOAD()
     95 {
     96 	free_dcc_config_blocks();
     97 	SavePersistentPointer(modinfo, conf_deny_dcc);
     98 	SavePersistentPointer(modinfo, conf_allow_dcc);
     99 
    100 	return MOD_SUCCESS;
    101 }
    102 
    103 int dccdeny_configtest_deny_dcc(ConfigFile *cf, ConfigEntry *ce, int type, int *errs)
    104 {
    105 	ConfigEntry *cep;
    106 	int errors = 0;
    107 	char has_filename = 0, has_reason = 0, has_soft = 0;
    108 
    109 	/* We are only interested in deny dcc { } */
    110 	if ((type != CONFIG_DENY) || strcmp(ce->value, "dcc"))
    111 		return 0;
    112 
    113 	for (cep = ce->items; cep; cep = cep->next)
    114 	{
    115 		if (config_is_blankorempty(cep, "deny dcc"))
    116 		{
    117 			errors++;
    118 			continue;
    119 		}
    120 		if (!strcmp(cep->name, "filename"))
    121 		{
    122 			if (has_filename)
    123 			{
    124 				config_warn_duplicate(cep->file->filename,
    125 					cep->line_number, "deny dcc::filename");
    126 				continue;
    127 			}
    128 			has_filename = 1;
    129 		}
    130 		else if (!strcmp(cep->name, "reason"))
    131 		{
    132 			if (has_reason)
    133 			{
    134 				config_warn_duplicate(cep->file->filename,
    135 					cep->line_number, "deny dcc::reason");
    136 				continue;
    137 			}
    138 			has_reason = 1;
    139 		}
    140 		else if (!strcmp(cep->name, "soft"))
    141 		{
    142 			if (has_soft)
    143 			{
    144 				config_warn_duplicate(cep->file->filename,
    145 					cep->line_number, "deny dcc::soft");
    146 				continue;
    147 			}
    148 			has_soft = 1;
    149 		}
    150 		else
    151 		{
    152 			config_error_unknown(cep->file->filename,
    153 				cep->line_number, "deny dcc", cep->name);
    154 			errors++;
    155 		}
    156 	}
    157 	if (!has_filename)
    158 	{
    159 		config_error_missing(ce->file->filename, ce->line_number,
    160 			"deny dcc::filename");
    161 		errors++;
    162 	}
    163 	if (!has_reason)
    164 	{
    165 		config_error_missing(ce->file->filename, ce->line_number,
    166 			"deny dcc::reason");
    167 		errors++;
    168 	}
    169 
    170 	*errs = errors;
    171 	return errors ? -1 : 1;
    172 }
    173 
    174 int dccdeny_configtest_allow_dcc(ConfigFile *cf, ConfigEntry *ce, int type, int *errs)
    175 {
    176 	ConfigEntry *cep;
    177 	int errors = 0, has_filename = 0, has_soft = 0;
    178 
    179 	/* We are only interested in allow dcc { } */
    180 	if ((type != CONFIG_ALLOW) || strcmp(ce->value, "dcc"))
    181 		return 0;
    182 
    183 	for (cep = ce->items; cep; cep = cep->next)
    184 	{
    185 		if (config_is_blankorempty(cep, "allow dcc"))
    186 		{
    187 			errors++;
    188 			continue;
    189 		}
    190 		if (!strcmp(cep->name, "filename"))
    191 		{
    192 			if (has_filename)
    193 			{
    194 				config_warn_duplicate(cep->file->filename,
    195 					cep->line_number, "allow dcc::filename");
    196 				continue;
    197 			}
    198 			has_filename = 1;
    199 		}
    200 		else if (!strcmp(cep->name, "soft"))
    201 		{
    202 			if (has_soft)
    203 			{
    204 				config_warn_duplicate(cep->file->filename,
    205 					cep->line_number, "allow dcc::soft");
    206 				continue;
    207 			}
    208 			has_soft = 1;
    209 		}
    210 		else
    211 		{
    212 			config_error_unknown(cep->file->filename, cep->line_number,
    213 				"allow dcc", cep->name);
    214 			errors++;
    215 		}
    216 	}
    217 	if (!has_filename)
    218 	{
    219 		config_error_missing(ce->file->filename, ce->line_number,
    220 			"allow dcc::filename");
    221 		errors++;
    222 	}
    223 
    224 	*errs = errors;
    225 	return errors ? -1 : 1;
    226 }
    227 
    228 int dccdeny_configrun_deny_dcc(ConfigFile *cf, ConfigEntry *ce, int type)
    229 {
    230 	ConfigItem_deny_dcc 	*deny = NULL;
    231 	ConfigEntry 	    	*cep;
    232 
    233 	/* We are only interested in deny dcc { } */
    234 	if ((type != CONFIG_DENY) || strcmp(ce->value, "dcc"))
    235 		return 0;
    236 
    237 	deny = safe_alloc(sizeof(ConfigItem_deny_dcc));
    238 	for (cep = ce->items; cep; cep = cep->next)
    239 	{
    240 		if (!strcmp(cep->name, "filename"))
    241 		{
    242 			safe_strdup(deny->filename, cep->value);
    243 		}
    244 		else if (!strcmp(cep->name, "reason"))
    245 		{
    246 			safe_strdup(deny->reason, cep->value);
    247 		}
    248 		else if (!strcmp(cep->name, "soft"))
    249 		{
    250 			int x = config_checkval(cep->value,CFG_YESNO);
    251 			if (x == 1)
    252 				deny->flag.type = DCCDENY_SOFT;
    253 		}
    254 	}
    255 	if (!deny->reason)
    256 	{
    257 		if (deny->flag.type == DCCDENY_HARD)
    258 			safe_strdup(deny->reason, "Possible infected virus file");
    259 		else
    260 			safe_strdup(deny->reason, "Possible executable content");
    261 	}
    262 	AddListItem(deny, conf_deny_dcc);
    263 	return 0;
    264 }
    265 
    266 int dccdeny_configrun_allow_dcc(ConfigFile *cf, ConfigEntry *ce, int type)
    267 {
    268 	ConfigItem_allow_dcc *allow = NULL;
    269 	ConfigEntry *cep;
    270 
    271 	/* We are only interested in allow dcc { } */
    272 	if ((type != CONFIG_ALLOW) || strcmp(ce->value, "dcc"))
    273 		return 0;
    274 
    275 	allow = safe_alloc(sizeof(ConfigItem_allow_dcc));
    276 
    277 	for (cep = ce->items; cep; cep = cep->next)
    278 	{
    279 		if (!strcmp(cep->name, "filename"))
    280 			safe_strdup(allow->filename, cep->value);
    281 		else if (!strcmp(cep->name, "soft"))
    282 		{
    283 			int x = config_checkval(cep->value,CFG_YESNO);
    284 			if (x)
    285 				allow->flag.type = DCCDENY_SOFT;
    286 		}
    287 	}
    288 	AddListItem(allow, conf_allow_dcc);
    289 	return 1;
    290 }
    291 
    292 /** Free allow dcc { } and deny dcc { } blocks, called on REHASH */
    293 void free_dcc_config_blocks(void)
    294 {
    295 	ConfigItem_deny_dcc *d, *d_next;
    296 	ConfigItem_allow_dcc *a, *a_next;
    297 
    298 	for (d = conf_deny_dcc; d; d = d_next)
    299 	{
    300 		d_next = d->next;
    301 		if (d->flag.type2 == CONF_BAN_TYPE_CONF)
    302 		{
    303 			safe_free(d->filename);
    304 			safe_free(d->reason);
    305 			DelListItem(d, conf_deny_dcc);
    306 			safe_free(d);
    307 		}
    308 	}
    309 	for (a = conf_allow_dcc; a; a = a_next)
    310 	{
    311 		a_next = a->next;
    312 		if (a->flag.type2 == CONF_BAN_TYPE_CONF)
    313 		{
    314 			safe_free(a->filename);
    315 			DelListItem(a, conf_allow_dcc);
    316 			safe_free(a);
    317 		}
    318 	}
    319 }
    320 
    321 /** Free all deny dcc { } blocks - only called on permanent module unload (rare) */
    322 void dccdeny_unload_free_all_conf_deny_dcc(ModData *m)
    323 {
    324 	ConfigItem_deny_dcc *d, *d_next;
    325 
    326 	for (d = conf_deny_dcc; d; d = d_next)
    327 	{
    328 		d_next = d->next;
    329 		safe_free(d->filename);
    330 		safe_free(d->reason);
    331 		DelListItem(d, conf_deny_dcc);
    332 		safe_free(d);
    333 	}
    334 	conf_deny_dcc = NULL;
    335 }
    336 
    337 /** Free all allow dcc { } blocks - only called on permanent module unload (rare) */
    338 void dccdeny_unload_free_all_conf_allow_dcc(ModData *m)
    339 {
    340 	ConfigItem_allow_dcc *a, *a_next;
    341 
    342 	for (a = conf_allow_dcc; a; a = a_next)
    343 	{
    344 		a_next = a->next;
    345 		safe_free(a->filename);
    346 		DelListItem(a, conf_allow_dcc);
    347 		safe_free(a);
    348 	}
    349 	conf_allow_dcc = NULL;
    350 }
    351 
    352 
    353 /* Add a temporary dccdeny line
    354  *
    355  * parv[1] - file
    356  * parv[2] - reason
    357  */
    358 CMD_FUNC(cmd_dccdeny)
    359 {
    360 	if (!MyUser(client))
    361 		return;
    362 
    363 	if (!ValidatePermissionsForPath("server-ban:dccdeny",client,NULL,NULL,NULL))
    364 	{
    365 		sendnumeric(client, ERR_NOPRIVILEGES);
    366 		return;
    367 	}
    368 
    369 	if ((parc < 2) || BadPtr(parv[2]))
    370 	{
    371 		sendnumeric(client, ERR_NEEDMOREPARAMS, "DCCDENY");
    372 		return;
    373 	}
    374 
    375 	if (!find_deny_dcc(parv[1]))
    376 	{
    377 		unreal_log(ULOG_INFO, "dccdeny", "DCCDENY_ADD", client,
    378 		           "[dccdeny] $client added a temporary DCCDENY for $file ($reason)",
    379 		           log_data_string("file", parv[1]),
    380 		           log_data_string("reason", parv[2]));
    381 		DCCdeny_add(parv[1], parv[2], DCCDENY_HARD, CONF_BAN_TYPE_TEMPORARY);
    382 		return;
    383 	} else
    384 	{
    385 		sendnotice(client, "*** %s already has a dccdeny", parv[1]);
    386 	}
    387 }
    388 
    389 /* Remove a temporary dccdeny line
    390  * parv[1] - file/mask
    391  */
    392 CMD_FUNC(cmd_undccdeny)
    393 {
    394 	ConfigItem_deny_dcc *d;
    395 
    396 	if (!MyUser(client))
    397 		return;
    398 
    399 	if (!ValidatePermissionsForPath("server-ban:dccdeny",client,NULL,NULL,NULL))
    400 	{
    401 		sendnumeric(client, ERR_NOPRIVILEGES);
    402 		return;
    403 	}
    404 
    405 	if ((parc < 2) || BadPtr(parv[1]))
    406 	{
    407 		sendnumeric(client, ERR_NEEDMOREPARAMS, "UNDCCDENY");
    408 		return;
    409 	}
    410 
    411 	if ((d = find_deny_dcc(parv[1])) && d->flag.type2 == CONF_BAN_TYPE_TEMPORARY)
    412 	{
    413 		unreal_log(ULOG_INFO, "dccdeny", "DCCDENY_DEL", client,
    414 		           "[dccdeny] $client removed a temporary DCCDENY for $file ($reason)",
    415 		           log_data_string("file", d->filename),
    416 		           log_data_string("reason", d->reason));
    417 		DCCdeny_del(d);
    418 		return;
    419 	} else
    420 	{
    421 		sendnotice(client, "*** Unable to find a temp dccdeny matching %s", parv[1]);
    422 	}
    423 }
    424 
    425 CMD_FUNC(cmd_svsfline)
    426 {
    427 	if (parc < 2)
    428 		return;
    429 
    430 	switch (*parv[1])
    431 	{
    432 		/* Allow non-U-Lines to send ONLY SVSFLINE +, but don't send it out
    433 		 * unless it is from a U-Line -- codemastr
    434 		 */
    435 		case '+':
    436 		{
    437 			if (parc < 4)
    438 				return;
    439 
    440 			if (!find_deny_dcc(parv[2]))
    441 				DCCdeny_add(parv[2], parv[3], DCCDENY_HARD, CONF_BAN_TYPE_AKILL);
    442 
    443 			if (IsULine(client))
    444 			{
    445 				sendto_server(client, 0, 0, NULL, ":%s SVSFLINE + %s :%s",
    446 				    client->id, parv[2], parv[3]);
    447 			}
    448 
    449 			break;
    450 		}
    451 
    452 		case '-':
    453 		{
    454 			ConfigItem_deny_dcc *deny;
    455 
    456 			if (!IsULine(client))
    457 				return;
    458 
    459 			if (parc < 3)
    460 				return;
    461 
    462 			if (!(deny = find_deny_dcc(parv[2])))
    463 				break;
    464 
    465 			DCCdeny_del(deny);
    466 
    467 			sendto_server(client, 0, 0, NULL, ":%s SVSFLINE %s", client->id, parv[2]);
    468 
    469 			break;
    470 		}
    471 
    472 		case '*':
    473 		{
    474 			if (!IsULine(client))
    475 				return;
    476 
    477 			dcc_wipe_services();
    478 
    479 			sendto_server(client, 0, 0, NULL, ":%s SVSFLINE *", client->id);
    480 
    481 			break;
    482 		}
    483 	}
    484 }
    485 
    486 /** Sync the DCC entries on server connect */
    487 int dccdeny_server_sync(Client *client)
    488 {
    489 	ConfigItem_deny_dcc *p;
    490 	for (p = conf_deny_dcc; p; p = p->next)
    491 	{
    492 		if (p->flag.type2 == CONF_BAN_TYPE_AKILL)
    493 			sendto_one(client, NULL, ":%s SVSFLINE + %s :%s", me.id,
    494 			    p->filename, p->reason);
    495 	}
    496 	return 0;
    497 }
    498 
    499 /** Check if a DCC should be blocked (user-to-user) */
    500 int dccdeny_can_send_to_user(Client *client, Client *target, const char **text, const char **errmsg, SendType sendtype)
    501 {
    502 	if (**text == '\001')
    503 	{
    504 		const char *filename = get_dcc_filename(*text);
    505 		if (filename)
    506 		{
    507 			if (MyUser(client) && !can_dcc(client, target->name, target, filename, errmsg))
    508 				return HOOK_DENY;
    509 			if (MyUser(target) && !can_dcc_soft(client, target, filename, errmsg))
    510 				return HOOK_DENY;
    511 		}
    512 	}
    513 
    514 	return HOOK_CONTINUE;
    515 }
    516 
    517 /** Check if a DCC should be blocked (user-to-channel, unusual) */
    518 int dccdeny_can_send_to_channel(Client *client, Channel *channel, Membership *lp, const char **msg, const char **errmsg, SendType sendtype)
    519 {
    520 	static char errbuf[512];
    521 
    522 	if (MyUser(client) && (**msg == '\001'))
    523 	{
    524 		const char *err = NULL;
    525 		const char *filename = get_dcc_filename(*msg);
    526 		if (filename && !can_dcc(client, channel->name, NULL, filename, &err))
    527 		{
    528 			strlcpy(errbuf, err, sizeof(errbuf));
    529 			*errmsg = errbuf;
    530 			return HOOK_DENY;
    531 		}
    532 	}
    533 	return HOOK_CONTINUE;
    534 }
    535 
    536 /* e->flag.type2:
    537  * CONF_BAN_TYPE_AKILL		banned by SVSFLINE ('global')
    538  * CONF_BAN_TYPE_CONF		banned by conf
    539  * CONF_BAN_TYPE_TEMPORARY	banned by /DCCDENY ('temporary')
    540  * e->flag.type:
    541  * DCCDENY_HARD				Fully denied
    542  * DCCDENY_SOFT				Denied, but allowed to override via /DCCALLOW
    543  */
    544 
    545 /** Make a viewable dcc filename.
    546  * This is to protect a bit against tricks like 'flood-it-off-the-buffer'
    547  * and color 1,1 etc...
    548  */
    549 static const char *dcc_displayfile(const char *f)
    550 {
    551 	static char buf[512];
    552 	const char *i;
    553 	char *o = buf;
    554 	size_t n = strlen(f);
    555 
    556 	if (n < 300)
    557 	{
    558 		for (i = f; *i; i++)
    559 			if (*i < 32)
    560 				*o++ = '?';
    561 			else
    562 				*o++ = *i;
    563 		*o = '\0';
    564 		return buf;
    565 	}
    566 
    567 	/* Else, we show it as: [first 256 chars]+"[..TRUNCATED..]"+[last 20 chars] */
    568 	for (i = f; i < f+256; i++)
    569 		if (*i < 32)
    570 			*o++ = '?';
    571 		else
    572 			*o++ = *i;
    573 	strcpy(o, "[..TRUNCATED..]");
    574 	o += sizeof("[..TRUNCATED..]");
    575 	for (i = f+n-20; *i; i++)
    576 		if (*i < 32)
    577 			*o++ = '?';
    578 		else
    579 			*o++ = *i;
    580 	*o = '\0';
    581 	return buf;
    582 }
    583 
    584 static const char *get_dcc_filename(const char *text)
    585 {
    586 	static char filename[BUFSIZE+1];
    587 	char *end;
    588 	int size_string;
    589 
    590 	if (*text != '\001')
    591 		return 0;
    592 
    593 	if (!strncasecmp(text+1, "DCC SEND ", 9))
    594 		text = text + 10;
    595 	else if (!strncasecmp(text+1, "DCC RESUME ", 11))
    596 		text = text + 12;
    597 	else
    598 		return 0;
    599 
    600 	for (; *text == ' '; text++); /* skip leading spaces */
    601 
    602 	if (*text == '"' && *(text+1))
    603 		end = strchr(text+1, '"');
    604 	else
    605 		end = strchr(text, ' ');
    606 
    607 	if (!end || (end < text))
    608 		return 0;
    609 
    610 	size_string = (int)(end - text);
    611 
    612 	if (!size_string || (size_string > (BUFSIZE - 1)))
    613 		return 0;
    614 
    615 	strlcpy(filename, text, size_string+1);
    616 	return filename;
    617 }
    618 
    619 /** Checks if a DCC SEND is allowed.
    620  * @param client      Sending client
    621  * @param target      Target name (user or channel)
    622  * @param targetcli   Target client (NULL in case of channel!)
    623  * @param text        The entire message
    624  * @returns 1 if DCC SEND allowed, 0 if rejected
    625  */
    626 static int can_dcc(Client *client, const char *target, Client *targetcli, const char *filename, const char **errmsg)
    627 {
    628 	ConfigItem_deny_dcc *fl;
    629 	static char errbuf[256];
    630 	int size_string, ret;
    631 
    632 	/* User (IRCOp) may bypass send restrictions */
    633 	if (ValidatePermissionsForPath("immune:dcc",client,targetcli,NULL,NULL))
    634 		return 1;
    635 
    636 	/* User (IRCOp) likes to receive bad dcc's */
    637 	if (targetcli && ValidatePermissionsForPath("self:getbaddcc",targetcli,NULL,NULL,NULL))
    638 		return 1;
    639 
    640 	/* Check if user is already blocked (from the past) */
    641 	if (IsDCCBlock(client))
    642 	{
    643 		*errmsg = "*** You are blocked from sending files as you have tried to "
    644 		          "send a forbidden file - reconnect to regain ability to send";
    645 		return 0;
    646 	}
    647 
    648 	if (match_spamfilter(client, filename, SPAMF_DCC, "PRIVMSG", target, 0, NULL))
    649 	{
    650 		/* Dirty hack, yeah spamfilter already sent the error message :( */
    651 		*errmsg = "";
    652 		return 0;
    653 	}
    654 
    655 	if ((fl = dcc_isforbidden(client, filename)))
    656 	{
    657 		const char *displayfile = dcc_displayfile(filename);
    658 
    659 		RunHook(HOOKTYPE_DCC_DENIED, client, target, filename, displayfile, fl);
    660 
    661 		ircsnprintf(errbuf, sizeof(errbuf), "Cannot DCC SEND file: %s", fl->reason);
    662 		*errmsg = errbuf;
    663 		SetDCCBlock(client);
    664 		return 0;
    665 	}
    666 
    667 	/* Channel dcc (???) and discouraged? just block */
    668 	if (!targetcli && ((fl = dcc_isdiscouraged(client, filename))))
    669 	{
    670 		ircsnprintf(errbuf, sizeof(errbuf), "Cannot DCC SEND file: %s", fl->reason);
    671 		*errmsg = errbuf;
    672 		return 0;
    673 	}
    674 
    675 	/* If we get here, the file is allowed */
    676 	return 1;
    677 }
    678 
    679 /** Checks if a DCC is allowed by DCCALLOW rules (only SOFT bans are checked).
    680  * PARAMETERS:
    681  * from:		the sender client (possibly remote)
    682  * to:			the target client (always local)
    683  * text:		the whole msg
    684  * RETURNS:
    685  * 1:			allowed
    686  * 0:			block
    687  */
    688 static int can_dcc_soft(Client *from, Client *to, const char *filename, const char **errmsg)
    689 {
    690 	ConfigItem_deny_dcc *fl;
    691 	const char *displayfile;
    692 	static char errbuf[256];
    693 
    694 	/* User (IRCOp) may bypass send restrictions */
    695 	if (ValidatePermissionsForPath("immune:dcc",from,to,NULL,NULL))
    696 		return 1;
    697 
    698 	/* User (IRCOp) likes to receive bad dcc's */
    699 	if (ValidatePermissionsForPath("self:getbaddcc",to,NULL,NULL,NULL))
    700 		return 1;
    701 
    702 	/* On the 'soft' blocklist ? */
    703 	if (!(fl = dcc_isdiscouraged(from, filename)))
    704 		return 1; /* No, so is OK */
    705 
    706 	/* If on DCCALLOW list then the user is OK with it */
    707 	if (on_dccallow_list(to, from))
    708 		return 1;
    709 
    710 	/* Soft-blocked */
    711 	displayfile = dcc_displayfile(filename);
    712 
    713 	ircsnprintf(errbuf, sizeof(errbuf), "Cannot DCC SEND file: %s", fl->reason);
    714 	*errmsg = errbuf;
    715 
    716 	/* Inform target ('to') about the /DCCALLOW functionality */
    717 	sendnotice(to, "%s (%s@%s) tried to DCC SEND you a file named '%s', the request has been blocked.",
    718 		from->name, from->user->username, GetHost(from), displayfile);
    719 	if (!IsDCCNotice(to))
    720 	{
    721 		SetDCCNotice(to);
    722 		sendnotice(to, "Files like these might contain malicious content (viruses, trojans). "
    723 			"Therefore, you must explicitly allow anyone that tries to send you such files.");
    724 		sendnotice(to, "If you trust %s, and want him/her to send you this file, you may obtain "
    725 			"more information on using the dccallow system by typing '/DCCALLOW HELP'", from->name);
    726 	}
    727 	return 0;
    728 }
    729 
    730 /** Checks if the dcc is blacklisted. */
    731 static ConfigItem_deny_dcc *dcc_isforbidden(Client *client, const char *filename)
    732 {
    733 	ConfigItem_deny_dcc *d;
    734 	ConfigItem_allow_dcc *a;
    735 
    736 	if (!conf_deny_dcc || !filename)
    737 		return NULL;
    738 
    739 	for (d = conf_deny_dcc; d; d = d->next)
    740 	{
    741 		if ((d->flag.type == DCCDENY_HARD) && match_simple(d->filename, filename))
    742 		{
    743 			for (a = conf_allow_dcc; a; a = a->next)
    744 			{
    745 				if ((a->flag.type == DCCDENY_HARD) && match_simple(a->filename, filename))
    746 					return NULL;
    747 			}
    748 			return d;
    749 		}
    750 	}
    751 
    752 	return NULL;
    753 }
    754 
    755 /** checks if the dcc is discouraged ('soft bans'). */
    756 static ConfigItem_deny_dcc *dcc_isdiscouraged(Client *client, const char *filename)
    757 {
    758 	ConfigItem_deny_dcc *d;
    759 	ConfigItem_allow_dcc *a;
    760 
    761 	if (!conf_deny_dcc || !filename)
    762 		return NULL;
    763 
    764 	for (d = conf_deny_dcc; d; d = d->next)
    765 	{
    766 		if ((d->flag.type == DCCDENY_SOFT) && match_simple(d->filename, filename))
    767 		{
    768 			for (a = conf_allow_dcc; a; a = a->next)
    769 			{
    770 				if ((a->flag.type == DCCDENY_SOFT) && match_simple(a->filename, filename))
    771 					return NULL;
    772 			}
    773 			return d;
    774 		}
    775 	}
    776 
    777 	return NULL;
    778 }
    779 
    780 static void DCCdeny_add(const char *filename, const char *reason, int type, int type2)
    781 {
    782 	ConfigItem_deny_dcc *deny = NULL;
    783 
    784 	deny = safe_alloc(sizeof(ConfigItem_deny_dcc));
    785 	safe_strdup(deny->filename, filename);
    786 	safe_strdup(deny->reason, reason);
    787 	deny->flag.type = type;
    788 	deny->flag.type2 = type2;
    789 	AddListItem(deny, conf_deny_dcc);
    790 }
    791 
    792 static void DCCdeny_del(ConfigItem_deny_dcc *deny)
    793 {
    794 	DelListItem(deny, conf_deny_dcc);
    795 	safe_free(deny->filename);
    796 	safe_free(deny->reason);
    797 	safe_free(deny);
    798 }
    799 
    800 ConfigItem_deny_dcc *find_deny_dcc(const char *name)
    801 {
    802 	ConfigItem_deny_dcc	*e;
    803 
    804 	if (!name)
    805 		return NULL;
    806 
    807 	for (e = conf_deny_dcc; e; e = e->next)
    808 	{
    809 		if (match_simple(name, e->filename))
    810 			return e;
    811 	}
    812 	return NULL;
    813 }
    814 
    815 static void dcc_wipe_services(void)
    816 {
    817 	ConfigItem_deny_dcc *dconf, *next;
    818 
    819 	for (dconf = conf_deny_dcc; dconf; dconf = next)
    820 	{
    821 		next = dconf->next;
    822 		if (dconf->flag.type2 == CONF_BAN_TYPE_AKILL)
    823 		{
    824 			DelListItem(dconf, conf_deny_dcc);
    825 			safe_free(dconf->filename);
    826 			safe_free(dconf->reason);
    827 			safe_free(dconf);
    828 		}
    829 	}
    830 
    831 }
    832 
    833 int dccdeny_stats(Client *client, const char *para)
    834 {
    835 	ConfigItem_deny_dcc *denytmp;
    836 	ConfigItem_allow_dcc *allowtmp;
    837 	char *filemask, *reason;
    838 	char a = 0;
    839 
    840 	/* '/STATS F' or '/STATS denydcc' is for us... */
    841 	if (strcmp(para, "F") && strcasecmp(para, "denydcc"))
    842 		return 0;
    843 
    844 	for (denytmp = conf_deny_dcc; denytmp; denytmp = denytmp->next)
    845 	{
    846 		filemask = BadPtr(denytmp->filename) ? "<NULL>" : denytmp->filename;
    847 		reason = BadPtr(denytmp->reason) ? "<NULL>" : denytmp->reason;
    848 		if (denytmp->flag.type2 == CONF_BAN_TYPE_CONF)
    849 			a = 'c';
    850 		if (denytmp->flag.type2 == CONF_BAN_TYPE_AKILL)
    851 			a = 's';
    852 		if (denytmp->flag.type2 == CONF_BAN_TYPE_TEMPORARY)
    853 			a = 'o';
    854 		/* <d> <s|h> <howadded> <filemask> <reason> */
    855 		sendtxtnumeric(client, "d %c %c %s %s", (denytmp->flag.type == DCCDENY_SOFT) ? 's' : 'h',
    856 			a, filemask, reason);
    857 	}
    858 	for (allowtmp = conf_allow_dcc; allowtmp; allowtmp = allowtmp->next)
    859 	{
    860 		filemask = BadPtr(allowtmp->filename) ? "<NULL>" : allowtmp->filename;
    861 		if (allowtmp->flag.type2 == CONF_BAN_TYPE_CONF)
    862 			a = 'c';
    863 		if (allowtmp->flag.type2 == CONF_BAN_TYPE_AKILL)
    864 			a = 's';
    865 		if (allowtmp->flag.type2 == CONF_BAN_TYPE_TEMPORARY)
    866 			a = 'o';
    867 		/* <a> <s|h> <howadded> <filemask> */
    868 		sendtxtnumeric(client, "a %c %c %s", (allowtmp->flag.type == DCCDENY_SOFT) ? 's' : 'h',
    869 			a, filemask);
    870 	}
    871 	return 1;
    872 }
    873 
    874 int dccdeny_dcc_denied(Client *client, const char *target, const char *realfile, const char *displayfile, ConfigItem_deny_dcc *dccdeny)
    875 {
    876 	unreal_log(ULOG_INFO, "dcc", "DCC_REJECTED", client,
    877 	           "$client.details tried to send forbidden file $filename ($ban_reason) to $target (is blocked now)",
    878 	           log_data_string("filename", displayfile),
    879 	           log_data_string("ban_reason", dccdeny->reason),
    880 	           log_data_string("target", target));
    881 	return 0;
    882 }