unrealircd

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

serv.c (33816B)

      1 /*
      2  *   Unreal Internet Relay Chat Daemon, src/serv.c
      3  *   Copyright (C) 1990 Jarkko Oikarinen and
      4  *                      University of Oulu, Computing Center
      5  *
      6  *   See file AUTHORS in IRC package for additional names of
      7  *   the programmers.
      8  *
      9  *   This program is free software; you can redistribute it and/or modify
     10  *   it under the terms of the GNU General Public License as published by
     11  *   the Free Software Foundation; either version 1, or (at your option)
     12  *   any later version.
     13  *
     14  *   This program is distributed in the hope that it will be useful,
     15  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
     16  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     17  *   GNU General Public License for more details.
     18  *
     19  *   You should have received a copy of the GNU General Public License
     20  *   along with this program; if not, write to the Free Software
     21  *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
     22  */
     23 
     24 /** @file
     25  * @brief Server-related functions
     26  */
     27 
     28 /* s_serv.c 2.55 2/7/94 (C) 1988 University of Oulu, Computing Center and Jarkko Oikarinen */
     29 
     30 #include "unrealircd.h"
     31 #include <ares.h>
     32 #ifndef _WIN32
     33 /* for uname(), is POSIX so should be OK... */
     34 #include <sys/utsname.h>
     35 #endif
     36 
     37 MODVAR int  max_connection_count = 1, max_client_count = 1;
     38 extern int do_garbage_collect;
     39 /* We need all these for cached MOTDs -- codemastr */
     40 extern char *buildid;
     41 MOTDFile opermotd;
     42 MOTDFile rules;
     43 MOTDFile motd;
     44 MOTDFile svsmotd;
     45 MOTDFile botmotd;
     46 MOTDFile smotd;
     47 
     48 /** Hash list of TKL entries */
     49 MODVAR TKL *tklines[TKLISTLEN];
     50 /** 2D hash list of TKL entries + IP address */
     51 MODVAR TKL *tklines_ip_hash[TKLIPHASHLEN1][TKLIPHASHLEN2];
     52 int MODVAR spamf_ugly_vchanoverride = 0;
     53 
     54 void read_motd(const char *filename, MOTDFile *motd);
     55 void do_read_motd(const char *filename, MOTDFile *themotd);
     56 
     57 extern MOTDLine *find_file(char *, short);
     58 
     59 void reread_motdsandrules();
     60 
     61 #if defined(__GNUC__)
     62 /* Temporarily ignore for this function. FIXME later!!! */
     63 #pragma GCC diagnostic push
     64 #pragma GCC diagnostic ignored "-Wformat-nonliteral"
     65 #endif
     66 
     67 /** Send a message upstream if necessary and check if it's for us.
     68  * @param client	The sender
     69  * @param mtags		Message tags associated with this message
     70  * @param command	The command (eg: "NOTICE")
     71  * @param server	This indicates parv[server] contains the destination
     72  * @param parc		Parameter count (MAX 8!!)
     73  * @param parv		Parameter values (MAX 8!!)
     74  * @note While sending parv[server] is replaced with the name of the matched client
     75  *       (virtually, as parv[] is not actually written to)
     76  */
     77 int hunt_server(Client *client, MessageTag *mtags, const char *command, int server, int parc, const char *parv[])
     78 {
     79 	Client *acptr;
     80 	const char *saved;
     81 	int i;
     82 	char buf[1024];
     83 
     84 	if (strchr(command, '%') || strchr(command, ' '))
     85 	{
     86 		unreal_log(ULOG_ERROR, "main", "BUG_HUNT_SERVER", client,
     87 		           "[BUG] hunt_server called with command '$command' but it may not contain "
     88 		           "spaces or percentage signs nowadays, it must be ONLY the command.",
     89 		           log_data_string("command", command));
     90 		abort();
     91 	}
     92 
     93 	/* This would be strange and bad. Previous version assumed "it's for me". Hmm.. okay. */
     94 	if (parc <= server || BadPtr(parv[server]))
     95 		return HUNTED_ISME;
     96 
     97 	acptr = find_client(parv[server], NULL);
     98 
     99 	/* find_client() may find a variety of clients. Only servers/persons please, no 'unknowns'. */
    100 	if (acptr && MyConnect(acptr) && !IsMe(acptr) && !IsUser(acptr) && !IsServer(acptr))
    101 		acptr = NULL;
    102 
    103 	if (!acptr)
    104 	{
    105 		sendnumeric(client, ERR_NOSUCHSERVER, parv[server]);
    106 		return HUNTED_NOSUCH;
    107 	}
    108 
    109 	if (IsMe(acptr) || MyUser(acptr))
    110 		return HUNTED_ISME;
    111 
    112 	/* Never send the message back from where it came from */
    113 	if (acptr->direction == client->direction)
    114 	{
    115 		sendnumeric(client, ERR_NOSUCHSERVER, parv[server]);
    116 		return HUNTED_NOSUCH;
    117 	}
    118 
    119 	/* This puts all parv[] arguments in 'buf'
    120 	 * Taken from concat_params() but this one is
    121 	 * with parv[server] magic replacement.
    122 	 */
    123 	*buf = '\0';
    124 	for (i = 1; i < parc; i++)
    125 	{
    126 		const char *param = parv[i];
    127 
    128 		if (!param)
    129 			break;
    130 
    131 		/* The magic parv[server] replacement:
    132 		 * this replaces eg 'User' with '001' in S2S traffic.
    133 		 */
    134 		if (i == server)
    135 			param = acptr->id;
    136 
    137 		if (*buf)
    138 			strlcat(buf, " ", sizeof(buf));
    139 
    140 		if (strchr(param, ' ') || (*param == ':'))
    141 		{
    142 			/* Last parameter, with : */
    143 			strlcat(buf, ":", sizeof(buf));
    144 			strlcat(buf, parv[i], sizeof(buf));
    145 			break;
    146 		}
    147 		strlcat(buf, parv[i], sizeof(buf));
    148 	}
    149 
    150 	sendto_one(acptr, mtags, ":%s %s %s", client->id, command, buf);
    151 
    152 	return HUNTED_PASS;
    153 }
    154 
    155 #if defined(__GNUC__)
    156 #pragma GCC diagnostic pop
    157 #endif
    158 
    159 #ifndef _WIN32
    160 /** Grab operating system name on Windows (outdated) */
    161 char *getosname(void)
    162 {
    163 	static char buf[1024];
    164 	struct utsname osinf;
    165 	char *p;
    166 
    167 	memset(&osinf, 0, sizeof(osinf));
    168 	if (uname(&osinf) != 0)
    169 		return "<unknown>";
    170 	snprintf(buf, sizeof(buf), "%s %s %s %s %s",
    171 		osinf.sysname,
    172 		osinf.nodename,
    173 		osinf.release,
    174 		osinf.version,
    175 		osinf.machine);
    176 	/* get rid of cr/lf */
    177 	for (p=buf; *p; p++)
    178 		if ((*p == '\n') || (*p == '\r'))
    179 		{
    180 			*p = '\0';
    181 			break;
    182 		}
    183 	return buf;
    184 }
    185 #endif
    186 
    187 /** Helper function to send version strings */
    188 void send_version(Client *client, int remote)
    189 {
    190 	int i;
    191 
    192 	for (i = 0; ISupportStrings[i]; i++)
    193 	{
    194 		if (remote)
    195 			sendnumeric(client, RPL_REMOTEISUPPORT, ISupportStrings[i]);
    196 		else
    197 			sendnumeric(client, RPL_ISUPPORT, ISupportStrings[i]);
    198 	}
    199 }
    200 
    201 /** VERSION command:
    202  * Syntax: VERSION [server]
    203  */
    204 CMD_FUNC(cmd_version)
    205 {
    206 	/* Only allow remote VERSIONs if registered -- Syzop */
    207 	if (!IsUser(client) && !IsServer(client))
    208 	{
    209 		send_version(client, 0);
    210 		return;
    211 	}
    212 
    213 	if (hunt_server(client, recv_mtags, "VERSION", 1, parc, parv) == HUNTED_ISME)
    214 	{
    215 		sendnumeric(client, RPL_VERSION, version, debugmode, me.name,
    216 			    (ValidatePermissionsForPath("server:info",client,NULL,NULL,NULL) ? serveropts : "0"),
    217 			    extraflags ? extraflags : "",
    218 			    tainted ? "3" : "",
    219 			    (ValidatePermissionsForPath("server:info",client,NULL,NULL,NULL) ? MYOSNAME : "*"),
    220 			    UnrealProtocol);
    221 		if (ValidatePermissionsForPath("server:info",client,NULL,NULL,NULL))
    222 		{
    223 			sendnotice(client, "%s", SSLeay_version(SSLEAY_VERSION));
    224 			sendnotice(client, "libsodium %s", sodium_version_string());
    225 #ifdef USE_LIBCURL
    226 			sendnotice(client, "%s", curl_version());
    227 #endif
    228 			sendnotice(client, "c-ares %s", ares_version(NULL));
    229 			sendnotice(client, "%s", pcre2_version());
    230 #if JANSSON_VERSION_HEX >= 0x020D00
    231 			sendnotice(client, "jansson %s\n", jansson_version_str());
    232 #endif
    233 		}
    234 		if (MyUser(client))
    235 			send_version(client,0);
    236 		else
    237 			send_version(client,1);
    238 	}
    239 }
    240 
    241 char *num = NULL;
    242 
    243 /** Send all our PROTOCTL messages to remote server.
    244  * We send multiple PROTOCTL's since 4.x. If this breaks your services
    245  * because you fail to maintain PROTOCTL state, then fix them!
    246  */
    247 void send_proto(Client *client, ConfigItem_link *aconf)
    248 {
    249 	ISupport *prefix = ISupportFind("PREFIX");
    250 
    251 	/* CAUTION: If adding a token to an existing PROTOCTL line below,
    252 	 *          then ensure that MAXPARA is not reached!
    253 	 */
    254 
    255 	/* First line */
    256 	sendto_one(client, NULL, "PROTOCTL NOQUIT NICKv2 SJOIN SJOIN2 UMODE2 VL SJ3 TKLEXT TKLEXT2 NICKIP ESVID NEXTBANS %s %s",
    257 	           iConf.ban_setter_sync ? "SJSBY" : "",
    258 	           ClientCapabilityFindReal("message-tags") ? "MTAGS" : "");
    259 
    260 	/* Second line */
    261 	sendto_one(client, NULL, "PROTOCTL CHANMODES=%s%s,%s,%s,%s USERMODES=%s BOOTED=%lld PREFIX=%s SID=%s MLOCK TS=%lld EXTSWHOIS",
    262 		CHPAR1, EXPAR1, EXPAR2, EXPAR3, EXPAR4,
    263 		umodestring, (long long)me.local->fake_lag, prefix->value,
    264 		me.id, (long long)TStime());
    265 
    266 	/* Third line */
    267 	sendto_one(client, NULL, "PROTOCTL NICKCHARS=%s CHANNELCHARS=%s",
    268 		charsys_get_current_languages(),
    269 		allowed_channelchars_valtostr(iConf.allowed_channelchars));
    270 }
    271 
    272 #ifndef IRCDTOTALVERSION
    273 #define IRCDTOTALVERSION BASE_VERSION "-" PATCH1 PATCH2 PATCH3 PATCH4 PATCH5 PATCH6 PATCH7 PATCH8 PATCH9
    274 #endif
    275 
    276 /** Special filter for remote commands */
    277 int remotecmdfilter(Client *client, int parc, const char *parv[])
    278 {
    279 	/* no remote requests permitted from non-ircops */
    280 	if (MyUser(client) && !ValidatePermissionsForPath("server:remote",client,NULL,NULL,NULL) && !BadPtr(parv[1]))
    281 	{
    282 		sendnumeric(client, ERR_NOPRIVILEGES);
    283 		return 1; /* STOP */
    284 	}
    285 
    286 	/* same as above, but in case an old server forwards a request to us: we ignore it */
    287 	if (!MyUser(client) && !ValidatePermissionsForPath("server:remote",client,NULL,NULL,NULL))
    288 		return 1; /* STOP (return) */
    289 	
    290 	return 0; /* Continue */
    291 }
    292 
    293 /** Output for /INFO */
    294 char *unrealinfo[] =
    295 {
    296 	"This release was brought to you by the following people:",
    297 	"",
    298 	"Head coder:",
    299 	"* Bram Matthys (Syzop) <syzop@unrealircd.org>",
    300 	"",
    301 	"Coders:",
    302 	"* Krzysztof Beresztant (k4be) <k4be@unrealircd.org>",
    303 	"* Gottem <gottem@unrealircd.org>",
    304 	"* i <i@unrealircd.org>",
    305 	"",
    306 	"Past UnrealIRCd 4.x coders/contributors:",
    307 	"* Heero, binki, nenolod, ..",
    308 	"",
    309 	"Past UnrealIRCd 3.2.x coders/contributors:",
    310 	"* Stskeeps (ret. head coder / project leader)",
    311 	"* codemastr (ret. u3.2 head coder)",
    312 	"* aquanight, WolfSage, ..",
    313 	"* McSkaf, Zogg, NiQuiL, chasm, llthangel, nighthawk, ..",
    314 	NULL
    315 };
    316 
    317 /** Send /INFO output */
    318 void cmd_info_send(Client *client)
    319 {
    320 	char **text = unrealinfo;
    321 
    322 	sendnumericfmt(client, RPL_INFO, ":========== %s ==========", IRCDTOTALVERSION);
    323 
    324 	while (*text)
    325 		sendnumericfmt(client, RPL_INFO, ":| %s", *text++);
    326 
    327 	sendnumericfmt(client, RPL_INFO, ":|");
    328 	sendnumericfmt(client, RPL_INFO, ":|");
    329 	sendnumericfmt(client, RPL_INFO, ":| Credits - Type /CREDITS");
    330 	sendnumericfmt(client, RPL_INFO, ":|");
    331 	sendnumericfmt(client, RPL_INFO, ":| This is an UnrealIRCd-style server");
    332 	sendnumericfmt(client, RPL_INFO, ":| If you find any bugs, please report them at:");
    333 	sendnumericfmt(client, RPL_INFO, ":|  https://bugs.unrealircd.org/");
    334 	sendnumericfmt(client, RPL_INFO, ":| UnrealIRCd Homepage: https://www.unrealircd.org");
    335 	sendnumericfmt(client, RPL_INFO, ":============================================");
    336 	sendnumericfmt(client, RPL_INFO, ":Birth Date: %s, compile # %s", creation, generation);
    337 	sendnumericfmt(client, RPL_INFO, ":On-line since %s", myctime(me.local->creationtime));
    338 	sendnumericfmt(client, RPL_INFO, ":ReleaseID (%s)", buildid);
    339 	sendnumeric(client, RPL_ENDOFINFO);
    340 }
    341 
    342 /** The INFO command.
    343  * Syntax: INFO [server]
    344  */
    345 CMD_FUNC(cmd_info)
    346 {
    347 	if (remotecmdfilter(client, parc, parv))
    348 		return;
    349 
    350 	if (hunt_server(client, recv_mtags, "INFO", 1, parc, parv) == HUNTED_ISME)
    351 		cmd_info_send(client);
    352 }
    353 
    354 /** LICENSE command
    355  * Syntax: LICENSE [server]
    356  */
    357 CMD_FUNC(cmd_license)
    358 {
    359 	char **text = gnulicense;
    360 
    361 	if (remotecmdfilter(client, parc, parv))
    362 		return;
    363 
    364 	if (hunt_server(client, recv_mtags, "LICENSE", 1, parc, parv) == HUNTED_ISME)
    365 	{
    366 		while (*text)
    367 			sendnumeric(client, RPL_INFO, *text++);
    368 
    369 		sendnumeric(client, RPL_INFO, "");
    370 		sendnumeric(client, RPL_ENDOFINFO);
    371 	}
    372 }
    373 
    374 /** CREDITS command
    375  * Syntax: CREDITS [servername]
    376  */
    377 CMD_FUNC(cmd_credits)
    378 {
    379 	char **text = unrealcredits;
    380 
    381 	if (remotecmdfilter(client, parc, parv))
    382 		return;
    383 
    384 	if (hunt_server(client, recv_mtags, "CREDITS", 1, parc, parv) == HUNTED_ISME)
    385 	{
    386 		while (*text)
    387 			sendnumeric(client, RPL_INFO, *text++);
    388 
    389 		sendnumeric(client, RPL_INFO, "");
    390 		sendnumericfmt(client, RPL_INFO, ":Birth Date: %s, compile # %s", creation, generation);
    391 		sendnumericfmt(client, RPL_INFO, ":On-line since %s", myctime(me.local->creationtime));
    392 		sendnumeric(client, RPL_ENDOFINFO);
    393 	}
    394 }
    395 
    396 /** Return flags for a client (connection), eg 's' for TLS - used in STATS L/l */
    397 const char *get_client_status(Client *client)
    398 {
    399 	static char buf[10];
    400 	char *p = buf;
    401 
    402 	*p = '\0';
    403 	*p++ = '[';
    404 	if (IsListening(client))
    405 	{
    406 		if (client->umodes & LISTENER_NORMAL)
    407 			*p++ = '*';
    408 		if (client->umodes & LISTENER_SERVERSONLY)
    409 			*p++ = 'S';
    410 		if (client->umodes & LISTENER_CLIENTSONLY)
    411 			*p++ = 'C';
    412 		if (client->umodes & LISTENER_TLS)
    413 			*p++ = 's';
    414 	}
    415 	else
    416 	{
    417 		if (IsTLS(client))
    418 			*p++ = 's';
    419 	}
    420 	*p++ = ']';
    421 	*p++ = '\0';
    422 	return buf;
    423 }
    424 
    425 /** ERROR command - used by servers to indicate errors.
    426  * Syntax: ERROR :<reason>
    427  */
    428 CMD_FUNC(cmd_error)
    429 {
    430 	const char *para;
    431 
    432 	if (!MyConnect(client))
    433 		return;
    434 
    435 	para = (parc > 1 && *parv[1] != '\0') ? parv[1] : "<>";
    436 
    437 	/* Errors from untrusted sources are ignored as any
    438 	 * malicious user can send these, confusing IRCOps etc.
    439 	 * One can always see the errors from the other side anyway.
    440 	 */
    441 	if (!IsServer(client) && !client->server)
    442 		return;
    443 
    444 	unreal_log(ULOG_ERROR, "link", "LINK_ERROR_MESSAGE", client,
    445 	           "Error from $client: $error_message",
    446 	           log_data_string("error_message", para),
    447 	           client->server->conf ? log_data_link_block(client->server->conf) : NULL);
    448 }
    449 
    450 /** Save the tunefile (such as: highest seen connection count) */
    451 EVENT(save_tunefile)
    452 {
    453 	FILE *tunefile;
    454 
    455 	tunefile = fopen(conf_files->tune_file, "w");
    456 	if (!tunefile)
    457 	{
    458 		char *errstr = strerror(errno);
    459 		unreal_log(ULOG_WARNING, "config", "WRITE_TUNE_FILE_FAILED", NULL,
    460 		           "Unable to write tunefile '$filename': $system_error",
    461 		           log_data_string("filename", conf_files->tune_file),
    462 		           log_data_string("system_error", errstr));
    463 		return;
    464 	}
    465 	fprintf(tunefile, "0\n");
    466 	fprintf(tunefile, "%d\n", irccounts.me_max);
    467 	fclose(tunefile);
    468 }
    469 
    470 /** Load the tunefile (such as: highest seen connection count) */
    471 void load_tunefile(void)
    472 {
    473 	FILE *tunefile;
    474 	char buf[1024];
    475 
    476 	tunefile = fopen(conf_files->tune_file, "r");
    477 	if (!tunefile)
    478 		return;
    479 	/* We ignore the first line, hence the weird looking double fgets here... */
    480 	if (!fgets(buf, sizeof(buf), tunefile) || !fgets(buf, sizeof(buf), tunefile))
    481 	{
    482 		char *errstr = strerror(errno);
    483 		unreal_log(ULOG_WARNING, "config", "READ_TUNE_FILE_FAILED", NULL,
    484 		           "Unable to read tunefile '$filename': $system_error",
    485 		           log_data_string("filename", conf_files->tune_file),
    486 		           log_data_string("system_error", errstr));
    487 	}
    488 	irccounts.me_max = atol(buf);
    489 	fclose(tunefile);
    490 }
    491 
    492 /** Rehash motd and rule files (motd_file/rules_file and all tld entries). */
    493 void rehash_motdrules()
    494 {
    495 ConfigItem_tld *tlds;
    496 
    497 	reread_motdsandrules();
    498 	for (tlds = conf_tld; tlds; tlds = tlds->next)
    499 	{
    500 		/* read_motd() accepts NULL in first arg and acts sanely */
    501 		read_motd(tlds->motd_file, &tlds->motd);
    502 		read_motd(tlds->rules_file, &tlds->rules);
    503 		read_motd(tlds->smotd_file, &tlds->smotd);
    504 		read_motd(tlds->opermotd_file, &tlds->opermotd);
    505 		read_motd(tlds->botmotd_file, &tlds->botmotd);
    506 	}
    507 }
    508 
    509 /** Rehash motd and rules (only the default files) */
    510 void reread_motdsandrules()
    511 {
    512 	read_motd(conf_files->motd_file, &motd);
    513 	read_motd(conf_files->rules_file, &rules);
    514 	read_motd(conf_files->smotd_file, &smotd);
    515 	read_motd(conf_files->botmotd_file, &botmotd);
    516 	read_motd(conf_files->opermotd_file, &opermotd);
    517 	read_motd(conf_files->svsmotd_file, &svsmotd);
    518 }
    519 
    520 extern void reinit_resolver(Client *client);
    521 
    522 /** REHASH command - reload configuration file on server(s).
    523  * Syntax: see HELPOP REHASH
    524  */
    525 CMD_FUNC(cmd_rehash)
    526 {
    527 	int x;
    528 
    529 	/* This is one of the (few) commands that cannot be handled
    530 	 * by labeled-response accurately in all circumstances.
    531 	 */
    532 	labeled_response_inhibit = 1;
    533 
    534 	if (!ValidatePermissionsForPath("server:rehash",client,NULL,NULL,NULL))
    535 	{
    536 		sendnumeric(client, ERR_NOPRIVILEGES);
    537 		return;
    538 	}
    539 
    540 	if ((parc < 3) || BadPtr(parv[2])) {
    541 		/* If the argument starts with a '-' (like -motd, -opermotd, etc) then it's
    542 		 * assumed not to be a server. -- Syzop
    543 		 */
    544 		if (parv[1] && (parv[1][0] == '-'))
    545 			x = HUNTED_ISME;
    546 		else
    547 			x = hunt_server(client, recv_mtags, "REHASH", 1, parc, parv);
    548 	} else {
    549 		if (match_simple("-glob*", parv[1])) /* This is really ugly... hack to make /rehash -global -something work */
    550 		{
    551 			x = HUNTED_ISME;
    552 		} else {
    553 			x = hunt_server(client, NULL, "REHASH", 1, parc, parv);
    554 		}
    555 	}
    556 	if (x != HUNTED_ISME)
    557 		return; /* Now forwarded or server didnt exist */
    558 
    559 	if (!MyConnect(client))
    560 	{
    561 #ifndef REMOTE_REHASH
    562 		sendnumeric(client, ERR_NOPRIVILEGES);
    563 		return;
    564 #endif
    565 		if (parv[2] == NULL)
    566 		{
    567 			if (loop.rehashing)
    568 			{
    569 				sendnotice(client, "A rehash is already in progress");
    570 				return;
    571 			}
    572 			remote_rehash_client = client;
    573 			/* fallthrough... so we deal with this the same way as local rehashes */
    574 		}
    575 		parv[1] = parv[2];
    576 	} else {
    577 		/* Ok this is in an 'else' because it should be only executed for local clients,
    578 		 * but it's totally unrelated to the above ;).
    579 		 */
    580 		if (parv[1] && match_simple("-glob*", parv[1]))
    581 		{
    582 			/* /REHASH -global [options] */
    583 			Client *acptr;
    584 			
    585 			/* Shift parv's to the left */
    586 			parv[1] = parv[2];
    587 			parv[2] = NULL;
    588 			parc--;
    589 			if (parv[1] && *parv[1] != '-')
    590 			{
    591 				sendnotice(client, "You cannot specify a server name after /REHASH -global, for obvious reasons");
    592 				return;
    593 			}
    594 			/* Broadcast it in an inefficient, but backwards compatible way. */
    595 			list_for_each_entry(acptr, &global_server_list, client_node)
    596 			{
    597 				if (acptr == &me)
    598 					continue;
    599 				sendto_one(acptr, NULL, ":%s REHASH %s %s",
    600 					client->name,
    601 					acptr->name,
    602 					parv[1] ? parv[1] : "-all");
    603 			}
    604 			/* Don't return, continue, because we need to REHASH ourselves as well. */
    605 		}
    606 	}
    607 
    608 	if (!BadPtr(parv[1]) && strcasecmp(parv[1], "-all"))
    609 	{
    610 		if (*parv[1] == '-')
    611 		{
    612 			if (!strncasecmp("-gar", parv[1], 4))
    613 			{
    614 				loop.do_garbage_collect = 1;
    615 				RunHook(HOOKTYPE_REHASHFLAG, client, parv[1]);
    616 				return;
    617 			}
    618 			if (!strncasecmp("-dns", parv[1], 4))
    619 			{
    620 				reinit_resolver(client);
    621 				return;
    622 			}
    623 			if (match_simple("-ssl*", parv[1]) || match_simple("-tls*", parv[1]))
    624 			{
    625 				unreal_log(ULOG_INFO, "config", "CONFIG_RELOAD_TLS", client, "Reloading all TLS related data. [by: $client.details]");
    626 				reinit_tls();
    627 				return;
    628 			}
    629 			RunHook(HOOKTYPE_REHASHFLAG, client, parv[1]);
    630 			return;
    631 		}
    632 	}
    633 	else
    634 	{
    635 		if (loop.rehashing)
    636 		{
    637 			sendnotice(client, "ERROR: A rehash is already in progress");
    638 			return;
    639 		}
    640 		unreal_log(ULOG_INFO, "config", "CONFIG_RELOAD", client, "Rehashing server configuration file [by: $client.details]");
    641 	}
    642 
    643 	/* Normal rehash, rehash motds&rules too, just like the on in the tld block will :p */
    644 	sendnumeric(client, RPL_REHASHING, configfile);
    645 	request_rehash(client);
    646 }
    647 
    648 /** RESTART command - restart the server (discouraged command)
    649  * parv[1] - password *OR* reason if no drpass { } block exists
    650  * parv[2] - reason for restart (optional & only if drpass block exists)
    651  */
    652 CMD_FUNC(cmd_restart)
    653 {
    654 	const char *reason = parv[1];
    655 	Client *acptr;
    656 
    657 	if (!MyUser(client))
    658 		return;
    659 
    660 	/* Check permissions */
    661 	if (!ValidatePermissionsForPath("server:restart",client,NULL,NULL,NULL))
    662 	{
    663 		sendnumeric(client, ERR_NOPRIVILEGES);
    664 		return;
    665 	}
    666 
    667 	/* Syntax: /restart */
    668 	if (parc == 1)
    669 	{
    670 		if (conf_drpass)
    671 		{
    672 			sendnumeric(client, ERR_NEEDMOREPARAMS, "RESTART");
    673 			return;
    674 		}
    675 	} else
    676 	if (parc >= 2)
    677 	{
    678 		/* Syntax: /restart <pass> [reason] */
    679 		if (conf_drpass)
    680 		{
    681 			if (!Auth_Check(client, conf_drpass->restartauth, parv[1]))
    682 			{
    683 				sendnumeric(client, ERR_PASSWDMISMATCH);
    684 				return;
    685 			}
    686 			reason = parv[2];
    687 		}
    688 	}
    689 
    690 	list_for_each_entry(acptr, &lclient_list, lclient_node)
    691 	{
    692 		if (IsUser(acptr))
    693 			sendnotice(acptr, "Server Restarted by %s", client->name);
    694 		else if (IsServer(acptr))
    695 			sendto_one(acptr, NULL, ":%s ERROR :Restarted by %s: %s",
    696 			    me.name, get_client_name(client, TRUE), reason ? reason : "No reason");
    697 	}
    698 
    699 	server_reboot(reason ? reason : "No reason");
    700 }
    701 
    702 /** Send short message of the day to the client */
    703 void short_motd(Client *client)
    704 {
    705 	ConfigItem_tld *tld;
    706 	MOTDFile *themotd;
    707 	MOTDLine *motdline;
    708 	struct tm *tm;
    709 	char is_short;
    710 
    711 	tm = NULL;
    712 	is_short = 1;
    713 
    714 	tld = find_tld(client);
    715 
    716 	/*
    717 	* Try different sources of short MOTDs, falling back to the
    718 	* long MOTD.
    719 	*/
    720 	themotd = &smotd;
    721 	if (tld && tld->smotd.lines)
    722 		themotd = &tld->smotd;
    723 
    724 	/* try long MOTDs */
    725 	if (!themotd->lines)
    726 	{
    727 		is_short = 0;
    728 		if (tld && tld->motd.lines)
    729 			themotd = &tld->motd;
    730 		else
    731 			themotd = &motd;
    732 	}
    733 
    734 	if (!themotd->lines)
    735 	{
    736 		sendnumeric(client, ERR_NOMOTD);
    737 		return;
    738 	}
    739 	if (themotd->last_modified.tm_year)
    740 	{
    741 		tm = &themotd->last_modified; /* for readability */
    742 		sendnumeric(client, RPL_MOTDSTART, me.name);
    743 		sendnumericfmt(client, RPL_MOTD, ":- %d/%d/%d %d:%02d", tm->tm_mday, tm->tm_mon + 1,
    744 		               1900 + tm->tm_year, tm->tm_hour, tm->tm_min);
    745 	}
    746 	if (is_short)
    747 	{
    748 		sendnumeric(client, RPL_MOTD, "This is the short MOTD. To view the complete MOTD type /motd");
    749 		sendnumeric(client, RPL_MOTD, "");
    750 	}
    751 
    752 	motdline = NULL;
    753 	if (themotd)
    754 		motdline = themotd->lines;
    755 	while (motdline)
    756 	{
    757 		sendnumeric(client, RPL_MOTD, motdline->line);
    758 		motdline = motdline->next;
    759 	}
    760 
    761 	if (!is_short)
    762 	{
    763 		/* If the admin does not use a short MOTD then we append the SVSMOTD here...
    764 		 * If we did show a short motd then we don't append SVSMOTD,
    765 		 * since they want to keep it short.
    766 		 */
    767 		motdline = svsmotd.lines;
    768 		while (motdline)
    769 		{
    770 			sendnumeric(client, RPL_MOTD, motdline->line);
    771 			motdline = motdline->next;
    772 		}
    773 	}
    774 
    775 	sendnumeric(client, RPL_ENDOFMOTD);
    776 }
    777 
    778 /** Read motd-like file, used for rules/motd/botmotd/opermotd/etc.
    779  * @param filename Filename of file to read or URL. NULL is accepted and causes the *motd to be free()d.
    780  * @param motd Reference to motd pointer (used for freeing if needed and for asynchronous remote MOTD support)
    781  */
    782 void read_motd(const char *filename, MOTDFile *themotd)
    783 {
    784 	FILE *fd;
    785 	struct tm *tm_tmp;
    786 	time_t modtime;
    787 
    788 	char line[512];
    789 	char *tmp;
    790 
    791 	MOTDLine *last, *temp;
    792 
    793 	free_motd(themotd);
    794 
    795 	if (!filename)
    796 		return;
    797 
    798 	fd = fopen(filename, "r");
    799 	if (!fd)
    800 		return;
    801 
    802 	/* record file modification time */
    803 	modtime = unreal_getfilemodtime(filename);
    804 	tm_tmp = localtime(&modtime);
    805 	memcpy(&themotd->last_modified, tm_tmp, sizeof(struct tm));
    806 
    807 	last = NULL;
    808 	while (fgets(line, sizeof(line), fd))
    809 	{
    810 		if ((tmp = strchr(line, '\n')))
    811 			*tmp = '\0';
    812 		if ((tmp = strchr(line, '\r')))
    813 			*tmp = '\0';
    814 		
    815 		if (strlen(line) > 510)
    816 			line[510] = '\0';
    817 
    818 		temp = safe_alloc(sizeof(MOTDLine));
    819 		safe_strdup(temp->line, line);
    820 
    821 		if (last)
    822 			last->next = temp;
    823 		else
    824 			/* handle the special case of the first line */
    825 			themotd->lines = temp;
    826 
    827 		last = temp;
    828 	}
    829 	/* the file could be zero bytes long? */
    830 	if (last)
    831 		last->next = NULL;
    832 
    833 	fclose(fd);
    834 	
    835 	return;
    836 }
    837 
    838 /** Free the contents of a MOTDFile structure.
    839  * The MOTDFile structure itself should be statically
    840  * allocated and deallocated. If the caller wants, it must
    841  * manually free the MOTDFile structure itself.
    842  */
    843 void free_motd(MOTDFile *themotd)
    844 {
    845 	MOTDLine *next, *motdline;
    846 
    847 	if (!themotd)
    848 		return;
    849 
    850 	for (motdline = themotd->lines; motdline; motdline = next)
    851 	{
    852 		next = motdline->next;
    853 		safe_free(motdline->line);
    854 		safe_free(motdline);
    855 	}
    856 
    857 	themotd->lines = NULL;
    858 	memset(&themotd->last_modified, '\0', sizeof(struct tm));
    859 }
    860 
    861 /** DIE command - terminate the server
    862  * DIE [password]
    863  */
    864 CMD_FUNC(cmd_die)
    865 {
    866 	Client *acptr;
    867 
    868 	if (!MyUser(client))
    869 		return;
    870 
    871 	if (!ValidatePermissionsForPath("server:die",client,NULL,NULL,NULL))
    872 	{
    873 		sendnumeric(client, ERR_NOPRIVILEGES);
    874 		return;
    875 	}
    876 
    877 	if (conf_drpass)	/* See if we have and DIE/RESTART password */
    878 	{
    879 		if (parc < 2)	/* And if so, require a password :) */
    880 		{
    881 			sendnumeric(client, ERR_NEEDMOREPARAMS, "DIE");
    882 			return;
    883 		}
    884 		if (!Auth_Check(client, conf_drpass->dieauth, parv[1]))
    885 		{
    886 			sendnumeric(client, ERR_PASSWDMISMATCH);
    887 			return;
    888 		}
    889 	}
    890 
    891 	/* Let the +s know what is going on */
    892 	unreal_log(ULOG_INFO, "main", "UNREALIRCD_STOP", client,
    893 	           "Terminating server by request of $client.details");
    894 
    895 	list_for_each_entry(acptr, &lclient_list, lclient_node)
    896 	{
    897 		if (IsUser(acptr))
    898 			sendnotice(acptr, "Server Terminated by %s", 
    899 				client->name);
    900 		else if (IsServer(acptr))
    901 			sendto_one(acptr, NULL, ":%s ERROR :Terminated by %s",
    902 			    me.name, get_client_name(client, TRUE));
    903 	}
    904 
    905 	s_die();
    906 }
    907 
    908 /** Server list (network) of pending connections */
    909 PendingNet *pendingnet = NULL;
    910 
    911 /** Add server list (network) from 'client' connection */
    912 void add_pending_net(Client *client, const char *str)
    913 {
    914 	PendingNet *net;
    915 	PendingServer *srv;
    916 	char *p, *name;
    917 	char buf[512];
    918 
    919 	if (BadPtr(str) || !client)
    920 		return;
    921 
    922 	/* Skip any * at the beginning (indicating a reply),
    923 	 * and work on a copy.
    924 	 */
    925 	if (*str == '*')
    926 		strlcpy(buf, str+1, sizeof(buf));
    927 	else
    928 		strlcpy(buf, str, sizeof(buf));
    929 
    930 	/* Allocate */
    931 	net = safe_alloc(sizeof(PendingNet));
    932 	net->client = client;
    933 
    934 	for (name = strtoken(&p, buf, ","); name; name = strtoken(&p, NULL, ","))
    935 	{
    936 		if (!*name)
    937 			continue;
    938 		
    939 		srv = safe_alloc(sizeof(PendingServer));
    940 		strlcpy(srv->sid, name, sizeof(srv->sid));
    941 		AddListItem(srv, net->servers);
    942 	}
    943 	
    944 	AddListItem(net, pendingnet);
    945 }
    946 
    947 /** Free server list (network) previously added by 'client' */
    948 void free_pending_net(Client *client)
    949 {
    950 	PendingNet *net, *net_next;
    951 	PendingServer *srv, *srv_next;
    952 	
    953 	for (net = pendingnet; net; net = net_next)
    954 	{
    955 		net_next = net->next;
    956 		if (net->client == client)
    957 		{
    958 			for (srv = net->servers; srv; srv = srv_next)
    959 			{
    960 				srv_next = srv->next;
    961 				safe_free(srv);
    962 			}
    963 			DelListItem(net, pendingnet);
    964 			safe_free(net);
    965 			/* Don't break, there can be multiple objects */
    966 		}
    967 	}
    968 }
    969 
    970 /** Find SID in any server list (network) that is pending, except 'exempt' */
    971 PendingNet *find_pending_net_by_sid_butone(const char *sid, Client *exempt)
    972 {
    973 	PendingNet *net;
    974 	PendingServer *srv;
    975 
    976 	if (BadPtr(sid))
    977 		return NULL;
    978 
    979 	for (net = pendingnet; net; net = net->next)
    980 	{
    981 		if (net->client == exempt)
    982 			continue;
    983 		for (srv = net->servers; srv; srv = srv->next)
    984 			if (!strcmp(srv->sid, sid))
    985 				return net;
    986 	}
    987 	return NULL;
    988 }
    989 
    990 /** Search the pending connections list for any identical sids */
    991 Client *find_pending_net_duplicates(Client *cptr, Client **srv, char **sid)
    992 {
    993 	PendingNet *net, *other;
    994 	PendingServer *s;
    995 
    996 	*srv = NULL;
    997 	*sid = NULL;
    998 	
    999 	for (net = pendingnet; net; net = net->next)
   1000 	{
   1001 		if (net->client != cptr)
   1002 			continue;
   1003 		/* Ok, found myself */
   1004 		for (s = net->servers; s; s = s->next)
   1005 		{
   1006 			char *curr_sid = s->sid;
   1007 			other = find_pending_net_by_sid_butone(curr_sid, cptr);
   1008 			if (other)
   1009 			{
   1010 				*srv = net->client;
   1011 				*sid = s->sid;
   1012 				return other->client; /* Found another (pending) server with identical numeric */
   1013 			}
   1014 		}
   1015 	}
   1016 	
   1017 	return NULL;
   1018 }
   1019 
   1020 /** Like find_pending_net_duplicates() but the other way around? Eh.. */
   1021 Client *find_non_pending_net_duplicates(Client *client)
   1022 {
   1023 	PendingNet *net;
   1024 	PendingServer *s;
   1025 	Client *acptr;
   1026 
   1027 	for (net = pendingnet; net; net = net->next)
   1028 	{
   1029 		if (net->client != client)
   1030 			continue;
   1031 		/* Ok, found myself */
   1032 		for (s = net->servers; s; s = s->next)
   1033 		{
   1034 			acptr = find_server(s->sid, NULL);
   1035 			if (acptr)
   1036 				return acptr; /* Found another (fully CONNECTED) server with identical numeric */
   1037 		}
   1038 	}
   1039 	
   1040 	return NULL;
   1041 }
   1042 
   1043 /** Parse CHANMODES= in PROTOCTL */
   1044 void parse_chanmodes_protoctl(Client *client, const char *str)
   1045 {
   1046 	char *modes, *p;
   1047 	char copy[256];
   1048 
   1049 	strlcpy(copy, str, sizeof(copy));
   1050 
   1051 	modes = strtoken(&p, copy, ",");
   1052 	if (modes)
   1053 	{
   1054 		safe_strdup(client->server->features.chanmodes[0], modes);
   1055 		modes = strtoken(&p, NULL, ",");
   1056 		if (modes)
   1057 		{
   1058 			safe_strdup(client->server->features.chanmodes[1], modes);
   1059 			modes = strtoken(&p, NULL, ",");
   1060 			if (modes)
   1061 			{
   1062 				safe_strdup(client->server->features.chanmodes[2], modes);
   1063 				modes = strtoken(&p, NULL, ",");
   1064 				if (modes)
   1065 				{
   1066 					safe_strdup(client->server->features.chanmodes[3], modes);
   1067 				}
   1068 			}
   1069 		}
   1070 	}
   1071 }
   1072 
   1073 static char previous_langsinuse[512];
   1074 static int previous_langsinuse_ready = 0;
   1075 
   1076 /** Check the nick character system (set::allowed-nickchars) for changes.
   1077  * If there are changes, then we broadcast the new PROTOCTL NICKCHARS= to all servers.
   1078  */
   1079 void charsys_check_for_changes(void)
   1080 {
   1081 	const char *langsinuse = charsys_get_current_languages();
   1082 	/* already called by charsys_finish() */
   1083 	safe_strdup(me.server->features.nickchars, langsinuse);
   1084 
   1085 	if (!previous_langsinuse_ready)
   1086 	{
   1087 		previous_langsinuse_ready = 1;
   1088 		strlcpy(previous_langsinuse, langsinuse, sizeof(previous_langsinuse));
   1089 		return; /* not booted yet. then we are done here. */
   1090 	}
   1091 
   1092 	if (strcmp(langsinuse, previous_langsinuse))
   1093 	{
   1094 		unreal_log(ULOG_INFO, "charsys", "NICKCHARS_CHANGED", NULL,
   1095 		           "Permitted nick characters changed at runtime: $old_nickchars -> $new_nickchars",
   1096 		           log_data_string("old_nickchars", previous_langsinuse),
   1097 		           log_data_string("new_nickchars", langsinuse));
   1098 		/* Broadcast change to all (locally connected) servers */
   1099 		sendto_server(NULL, 0, 0, NULL, "PROTOCTL NICKCHARS=%s", langsinuse);
   1100 	}
   1101 
   1102 	strlcpy(previous_langsinuse, langsinuse, sizeof(previous_langsinuse));
   1103 }
   1104 
   1105 /** Check if supplied server name is valid, that is: does not contain forbidden characters etc */
   1106 int valid_server_name(const char *name)
   1107 {
   1108 	const char *p;
   1109 
   1110 	if (!valid_host(name, 0))
   1111 		return 0; /* invalid hostname */
   1112 
   1113 	if (!strchr(name, '.'))
   1114 		return 0; /* no dot */
   1115 
   1116 	return 1;
   1117 }
   1118 
   1119 /** Check if the supplied name is a valid SID, as in: syntax. */
   1120 int valid_sid(const char *name)
   1121 {
   1122 	if (strlen(name) != 3)
   1123 		return 0;
   1124 	if (!isdigit(*name))
   1125 		return 0;
   1126 	if (!isdigit(name[1]) && !isupper(name[1]))
   1127 		return 0;
   1128 	if (!isdigit(name[2]) && !isupper(name[2]))
   1129 		return 0;
   1130 	return 1;
   1131 }
   1132 
   1133 /** Check if the supplied name is a valid UID, as in: syntax. */
   1134 int valid_uid(const char *name)
   1135 {
   1136 	const char *p;
   1137 
   1138 	/* Enforce at least some minimum length */
   1139 	if (strlen(name) < 6)
   1140 		return 0;
   1141 
   1142 	/* UID cannot be larger than IDLEN or it would be cut off later */
   1143 	if (strlen(name) > IDLEN)
   1144 		return 0;
   1145 
   1146 	/* Must start with a digit */
   1147 	if (!isdigit(*name))
   1148 		return 0;
   1149 
   1150 	/* For all the remaining characters: digit or uppercase character */
   1151 	for (p = name+1; *p; p++)
   1152 		if (!isdigit(*p) && !isupper(*p))
   1153 			return 0;
   1154 
   1155 	return 1;
   1156 }
   1157 
   1158 /** Initialize the TKL subsystem */
   1159 void tkl_init(void)
   1160 {
   1161 	memset(tklines, 0, sizeof(tklines));
   1162 	memset(tklines_ip_hash, 0, sizeof(tklines_ip_hash));
   1163 }
   1164 
   1165 /** Called when a server link is lost.
   1166  * Used for logging only, API users can use the HOOKTYPE_SERVER_QUIT hook.
   1167  */
   1168 void lost_server_link(Client *client, const char *tls_error_string)
   1169 {
   1170 	if (IsServer(client))
   1171 	{
   1172 		/* An already established link is now lost. */
   1173 		// FIXME: we used to broadcast this GLOBALLY.. not anymore since the U6 rewrite.. is that what we want?
   1174 		if (tls_error_string)
   1175 		{
   1176 			/* TLS */
   1177 			unreal_log(ULOG_ERROR, "link", "LINK_DISCONNECTED", client,
   1178 				   "Lost server link to $client [$client.ip]: $tls_error_string",
   1179 				   log_data_string("tls_error_string", tls_error_string),
   1180 				   client->server->conf ? log_data_link_block(client->server->conf) : NULL);
   1181 		} else {
   1182 			/* NON-TLS */
   1183 			unreal_log(ULOG_ERROR, "link", "LINK_DISCONNECTED", client,
   1184 				   "Lost server link to $client [$client.ip]: $socket_error",
   1185 				   log_data_socket_error(client->local->fd),
   1186 				   client->server->conf ? log_data_link_block(client->server->conf) : NULL);
   1187 		}
   1188 	} else {
   1189 		/* A link attempt failed (it was never a fully connected server) */
   1190 		/* We send these to local ops only */
   1191 		if (tls_error_string)
   1192 		{
   1193 			/* TLS */
   1194 			if (client->server->conf)
   1195 			{
   1196 				unreal_log(ULOG_ERROR, "link", "LINK_ERROR_CONNECT", client,
   1197 					   client->server->conf->outgoing.file
   1198 					   ? "Unable to link with server $client [$link_block.file]: $tls_error_string"
   1199 					   : "Unable to link with server $client [$link_block.ip:$link_block.port]: $tls_error_string",
   1200 					   log_data_string("tls_error_string", tls_error_string),
   1201 					   log_data_link_block(client->server->conf));
   1202 			} else {
   1203 				unreal_log(ULOG_ERROR, "link", "LINK_ERROR_CONNECT", client,
   1204 					   "Unable to link with server $client: $tls_error_string",
   1205 					   log_data_string("tls_error_string", tls_error_string));
   1206 			}
   1207 		} else {
   1208 			/* non-TLS */
   1209 			if (client->server->conf)
   1210 			{
   1211 				unreal_log(ULOG_ERROR, "link", "LINK_ERROR_CONNECT", client,
   1212 					   client->server->conf->outgoing.file
   1213 					   ? "Unable to link with server $client [$link_block.file]: $socket_error"
   1214 					   : "Unable to link with server $client [$link_block.ip:$link_block.port]: $socket_error",
   1215 					   log_data_socket_error(client->local->fd),
   1216 					   log_data_link_block(client->server->conf));
   1217 			} else {
   1218 				unreal_log(ULOG_ERROR, "link", "LINK_ERROR_CONNECT", client,
   1219 					   "Unable to link with server $client: $socket_error",
   1220 					   log_data_socket_error(client->local->fd));
   1221 			}
   1222 		}
   1223 	}
   1224 	SetServerDisconnectLogged(client);
   1225 }
   1226 
   1227 /** Reject an insecure (outgoing) server link that isn't TLS.
   1228  * This function is void and not int because it can be called from other void functions
   1229  */
   1230 void reject_insecure_server(Client *client)
   1231 {
   1232 	unreal_log(ULOG_ERROR, "link", "SERVER_STARTTLS_FAILED", client,
   1233 	           "Could not link with server $client with TLS enabled. "
   1234 	           "Please check logs on the other side of the link. "
   1235 	           "If you insist with insecure linking then you can set link::options::outgoing::insecure "
   1236 	           "(NOT recommended!).");
   1237 	dead_socket(client, "Rejected server link without TLS");
   1238 }
   1239 
   1240 /** Start server handshake - called after the outgoing connection has been established.
   1241  * @param client	The remote server
   1242  */
   1243 void start_server_handshake(Client *client)
   1244 {
   1245 	ConfigItem_link *aconf = client->server ? client->server->conf : NULL;
   1246 
   1247 	if (!aconf)
   1248 	{
   1249 		/* Should be impossible. */
   1250 		unreal_log(ULOG_ERROR, "link", "BUG_LOST_CONFIGURATION_ON_HANDSHAKE", client,
   1251 		           "Lost configuration while connecting to $client.details");
   1252 		return;
   1253 	}
   1254 
   1255 	RunHook(HOOKTYPE_SERVER_HANDSHAKE_OUT, client);
   1256 
   1257 	sendto_one(client, NULL, "PASS :%s", (aconf->auth->type == AUTHTYPE_PLAINTEXT) ? aconf->auth->data : "*");
   1258 
   1259 	send_protoctl_servers(client, 0);
   1260 	send_proto(client, aconf);
   1261 	/* Sending SERVER message moved to cmd_protoctl, so it's send after the first PROTOCTL
   1262 	 * that we receive from the remote server. -- Syzop
   1263 	 */
   1264 }