parse.c 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611
  1. /*
  2. * ngIRCd -- The Next Generation IRC Daemon
  3. * Copyright (c)2001-2018 Alexander Barton (alex@barton.de) and Contributors.
  4. *
  5. * This program is free software; you can redistribute it and/or modify
  6. * it under the terms of the GNU General Public License as published by
  7. * the Free Software Foundation; either version 2 of the License, or
  8. * (at your option) any later version.
  9. * Please read the file COPYING, README and AUTHORS for more information.
  10. */
  11. #include "portab.h"
  12. /**
  13. * @file
  14. * IRC command parser and validator.
  15. */
  16. #include <assert.h>
  17. #include <stdlib.h>
  18. #include <string.h>
  19. #include <strings.h>
  20. #include "ngircd.h"
  21. #include "conn-func.h"
  22. #include "conf.h"
  23. #include "channel.h"
  24. #include "log.h"
  25. #include "messages.h"
  26. #include "parse.h"
  27. #include "irc.h"
  28. #include "irc-cap.h"
  29. #include "irc-channel.h"
  30. #ifdef ICONV
  31. # include "irc-encoding.h"
  32. #endif
  33. #include "irc-info.h"
  34. #include "irc-login.h"
  35. #include "irc-metadata.h"
  36. #include "irc-mode.h"
  37. #include "irc-op.h"
  38. #include "irc-oper.h"
  39. #include "irc-server.h"
  40. #include "irc-write.h"
  41. #include "numeric.h"
  42. struct _NUMERIC {
  43. int numeric;
  44. bool (*function) PARAMS(( CLIENT *Client, REQUEST *Request ));
  45. };
  46. static COMMAND My_Commands[] =
  47. {
  48. #define _CMD(name, func, type, min, max, penalty) \
  49. { (name), (func), (type), (min), (max), (penalty), 0, 0, 0 }
  50. _CMD("ADMIN", IRC_ADMIN, CLIENT_USER|CLIENT_SERVER, 0, 1, 1),
  51. _CMD("AWAY", IRC_AWAY, CLIENT_USER, 0, 1, 0),
  52. _CMD("CAP", IRC_CAP, CLIENT_ANY, 1, 2, 0),
  53. _CMD("CONNECT", IRC_CONNECT, CLIENT_USER|CLIENT_SERVER, 0, -1, 0),
  54. #ifdef STRICT_RFC
  55. _CMD("DIE", IRC_DIE, CLIENT_USER, 0, 0, 0),
  56. #else
  57. _CMD("DIE", IRC_DIE, CLIENT_USER, 0, 1, 0),
  58. #endif
  59. _CMD("DISCONNECT", IRC_DISCONNECT, CLIENT_USER, 1, 1, 0),
  60. _CMD("ERROR", IRC_ERROR, CLIENT_ANY, 0, -1, 0),
  61. _CMD("GLINE", IRC_xLINE, CLIENT_USER|CLIENT_SERVER, 0, -1, 0),
  62. _CMD("HELP", IRC_HELP, CLIENT_USER, 0, 1, 2),
  63. _CMD("INFO", IRC_INFO, CLIENT_USER|CLIENT_SERVER, 0, 1, 2),
  64. _CMD("INVITE", IRC_INVITE, CLIENT_USER|CLIENT_SERVER, 2, 2, 1),
  65. _CMD("ISON", IRC_ISON, CLIENT_USER, 1, -1, 0),
  66. _CMD("JOIN", IRC_JOIN, CLIENT_USER|CLIENT_SERVER, 1, 2, 0),
  67. _CMD("KICK", IRC_KICK, CLIENT_USER|CLIENT_SERVER, 2, 3, 0),
  68. _CMD("KILL", IRC_KILL, CLIENT_USER|CLIENT_SERVER, 2, 2, 0),
  69. _CMD("KLINE", IRC_xLINE, CLIENT_USER|CLIENT_SERVER, 0, -1, 0),
  70. _CMD("LINKS", IRC_LINKS, CLIENT_USER|CLIENT_SERVER, 0, 2, 1),
  71. _CMD("LIST", IRC_LIST, CLIENT_USER|CLIENT_SERVER, 0, 2, 2),
  72. _CMD("LUSERS", IRC_LUSERS, CLIENT_USER|CLIENT_SERVER, 0, 2, 1),
  73. _CMD("METADATA", IRC_METADATA, CLIENT_SERVER, 3, 3, 0),
  74. _CMD("MODE", IRC_MODE, CLIENT_USER|CLIENT_SERVER, 1, -1, 1),
  75. _CMD("MOTD", IRC_MOTD, CLIENT_USER|CLIENT_SERVER, 0, 1, 3),
  76. _CMD("NAMES", IRC_NAMES, CLIENT_USER|CLIENT_SERVER, 0, 2, 1),
  77. _CMD("NICK", IRC_NICK, CLIENT_ANY, 0, -1, 0),
  78. _CMD("NJOIN", IRC_NJOIN, CLIENT_SERVER, 2, 2, 0),
  79. _CMD("NOTICE", IRC_NOTICE, CLIENT_ANY, 0, -1, 0),
  80. _CMD("OPER", IRC_OPER, CLIENT_USER, 2, 2, 0),
  81. _CMD("PART", IRC_PART, CLIENT_USER|CLIENT_SERVER, 1, 2, 0),
  82. _CMD("PASS", IRC_PASS, CLIENT_ANY, 0, -1, 0),
  83. _CMD("PING", IRC_PING, CLIENT_USER|CLIENT_SERVER, 0, -1, 0),
  84. _CMD("PONG", IRC_PONG, CLIENT_ANY, 0, -1, 0),
  85. _CMD("PRIVMSG", IRC_PRIVMSG, CLIENT_USER|CLIENT_SERVER, 0, 2, 0),
  86. _CMD("QUIT", IRC_QUIT, CLIENT_ANY, 0, 1, 0),
  87. _CMD("REHASH", IRC_REHASH, CLIENT_USER, 0, 0, 0),
  88. _CMD("RESTART", IRC_RESTART, CLIENT_USER, 0, 0, 0),
  89. _CMD("SERVER", IRC_SERVER, CLIENT_ANY, 0, -1, 0),
  90. _CMD("SERVICE", IRC_SERVICE, CLIENT_ANY, 6, 6, 0),
  91. _CMD("SERVLIST", IRC_SERVLIST, CLIENT_USER, 0, 2, 1),
  92. _CMD("SQUERY", IRC_SQUERY, CLIENT_USER|CLIENT_SERVER, 0, 2, 0),
  93. _CMD("SQUIT", IRC_SQUIT, CLIENT_USER|CLIENT_SERVER, 2, 2, 0),
  94. _CMD("STATS", IRC_STATS, CLIENT_USER|CLIENT_SERVER, 0, 2, 2),
  95. _CMD("SVSNICK", IRC_SVSNICK, CLIENT_SERVER, 2, 2, 0),
  96. _CMD("SUMMON", IRC_SUMMON, CLIENT_USER|CLIENT_SERVER, 0, -1, 0),
  97. _CMD("TIME", IRC_TIME, CLIENT_USER|CLIENT_SERVER, 0, 1, 1),
  98. _CMD("TOPIC", IRC_TOPIC, CLIENT_USER|CLIENT_SERVER, 1, 2, 1),
  99. _CMD("TRACE", IRC_TRACE, CLIENT_USER|CLIENT_SERVER, 0, 1, 3),
  100. _CMD("USER", IRC_USER, CLIENT_ANY, 0, -1, 0),
  101. _CMD("USERHOST", IRC_USERHOST, CLIENT_USER, 1, -1, 1),
  102. _CMD("USERS", IRC_USERS, CLIENT_USER|CLIENT_SERVER, 0, -1, 0),
  103. _CMD("VERSION", IRC_VERSION, CLIENT_USER|CLIENT_SERVER, 0, 1, 1),
  104. _CMD("WALLOPS", IRC_WALLOPS, CLIENT_USER|CLIENT_SERVER, 1, 1, 0),
  105. _CMD("WEBIRC", IRC_WEBIRC, CLIENT_UNKNOWN, 4, 5, 0),
  106. _CMD("WHO", IRC_WHO, CLIENT_USER, 0, 2, 1),
  107. _CMD("WHOIS", IRC_WHOIS, CLIENT_USER|CLIENT_SERVER, 0, -1, 1),
  108. _CMD("WHOWAS", IRC_WHOWAS, CLIENT_USER|CLIENT_SERVER, 0, -1, 0),
  109. #ifdef IRCPLUS
  110. _CMD("CHANINFO", IRC_CHANINFO, CLIENT_SERVER, 0, -1, 0),
  111. # ifdef ICONV
  112. _CMD("CHARCONV", IRC_CHARCONV, CLIENT_USER, 1, 1, 0),
  113. # endif
  114. #endif
  115. #ifndef STRICT_RFC
  116. _CMD("GET", IRC_QUIT_HTTP, CLIENT_UNKNOWN, 0, -1, 0),
  117. _CMD("POST", IRC_QUIT_HTTP, CLIENT_UNKNOWN, 0, -1, 0),
  118. #endif
  119. _CMD(NULL, NULL, 0, 0, 0, 0) /* End-Mark */
  120. #undef _CMD
  121. };
  122. static void Init_Request PARAMS(( REQUEST *Req ));
  123. static bool Validate_Prefix PARAMS(( CONN_ID Idx, REQUEST *Req, bool *Closed ));
  124. static bool Validate_Command PARAMS(( CONN_ID Idx, REQUEST *Req, bool *Closed ));
  125. static bool Validate_Args PARAMS(( CONN_ID Idx, REQUEST *Req, bool *Closed ));
  126. static bool Handle_Request PARAMS(( CONN_ID Idx, REQUEST *Req ));
  127. static bool ScrubCTCP PARAMS((char *Request));
  128. /**
  129. * Return the pointer to the global "IRC command structure".
  130. * This structure, an array of type "COMMAND" describes all the IRC commands
  131. * implemented by ngIRCd and how to handle them.
  132. * @return Pointer to the global command structure.
  133. */
  134. GLOBAL COMMAND *
  135. Parse_GetCommandStruct( void )
  136. {
  137. return My_Commands;
  138. } /* Parse_GetCommandStruct */
  139. /**
  140. * Parse a command ("request") received from a client.
  141. *
  142. * This function is called after the connection layer received a valid CR+LF
  143. * terminated line of text: we assume that this is a valid IRC command and
  144. * try to do something useful with it :-)
  145. *
  146. * All errors are reported to the client from which the command has been
  147. * received, and if the error is fatal this connection is closed down.
  148. *
  149. * This function is able to parse the syntax as described in RFC 2812,
  150. * section 2.3.
  151. *
  152. * @param Idx Index of the connection from which the command has been received.
  153. * @param Request NULL terminated line of text (the "command").
  154. * @return CONNECTED on success (valid command or "regular" error), DISCONNECTED
  155. * if a fatal error occurred and the connection has been shut down.
  156. */
  157. GLOBAL bool
  158. Parse_Request( CONN_ID Idx, char *Request )
  159. {
  160. REQUEST req;
  161. char *start, *ptr;
  162. bool closed;
  163. assert( Idx >= 0 );
  164. assert( Request != NULL );
  165. #ifdef SNIFFER
  166. if( NGIRCd_Sniffer ) Log( LOG_DEBUG, " <- connection %d: '%s'.", Idx, Request );
  167. #endif
  168. Init_Request( &req );
  169. /* remove leading & trailing whitespace */
  170. ngt_TrimStr( Request );
  171. if (Conf_ScrubCTCP && ScrubCTCP(Request))
  172. return true;
  173. if (Request[0] == ':') {
  174. /* Prefix */
  175. req.prefix = Request + 1;
  176. ptr = strchr( Request, ' ' );
  177. if( ! ptr )
  178. {
  179. LogDebug("Connection %d: Parse error: prefix without command!?", Idx);
  180. return Conn_WriteStr(Idx, "ERROR :Prefix without command");
  181. }
  182. *ptr = '\0';
  183. #ifndef STRICT_RFC
  184. /* ignore multiple spaces between prefix and command */
  185. while( *(ptr + 1) == ' ' ) ptr++;
  186. #endif
  187. start = ptr + 1;
  188. }
  189. else start = Request;
  190. ptr = strchr( start, ' ' );
  191. if( ptr )
  192. {
  193. *ptr = '\0';
  194. #ifndef STRICT_RFC
  195. /* ignore multiple spaces between parameters */
  196. while( *(ptr + 1) == ' ' ) ptr++;
  197. #endif
  198. }
  199. req.command = start;
  200. /* Arguments, Parameters */
  201. if( ptr )
  202. {
  203. start = ptr + 1;
  204. while( start )
  205. {
  206. if( start[0] == ':' )
  207. {
  208. req.argv[req.argc] = start + 1;
  209. ptr = NULL;
  210. }
  211. else
  212. {
  213. req.argv[req.argc] = start;
  214. ptr = strchr( start, ' ' );
  215. if( ptr )
  216. {
  217. *ptr = '\0';
  218. #ifndef STRICT_RFC
  219. while( *(ptr + 1) == ' ' ) ptr++;
  220. #endif
  221. }
  222. }
  223. req.argc++;
  224. if( start[0] == ':' ) break;
  225. if( req.argc > 14 ) break;
  226. if( ptr ) start = ptr + 1;
  227. else start = NULL;
  228. }
  229. }
  230. if(!Validate_Prefix(Idx, &req, &closed))
  231. return !closed;
  232. if(!Validate_Command(Idx, &req, &closed))
  233. return !closed;
  234. if(!Validate_Args(Idx, &req, &closed))
  235. return !closed;
  236. return Handle_Request(Idx, &req);
  237. } /* Parse_Request */
  238. /**
  239. * Initialize request structure.
  240. * @param Req Request structure to be initialized.
  241. */
  242. static void
  243. Init_Request( REQUEST *Req )
  244. {
  245. int i;
  246. assert( Req != NULL );
  247. Req->prefix = NULL;
  248. Req->command = NULL;
  249. for( i = 0; i < 15; Req->argv[i++] = NULL );
  250. Req->argc = 0;
  251. } /* Init_Request */
  252. static bool
  253. Validate_Prefix( CONN_ID Idx, REQUEST *Req, bool *Closed )
  254. {
  255. CLIENT *client, *c;
  256. assert( Idx >= 0 );
  257. assert( Req != NULL );
  258. *Closed = false;
  259. client = Conn_GetClient( Idx );
  260. assert( client != NULL );
  261. if (!Req->prefix && Client_Type(client) == CLIENT_SERVER
  262. && !(Conn_Options(Idx) & CONN_RFC1459)
  263. && strcasecmp(Req->command, "ERROR") != 0
  264. && strcasecmp(Req->command, "PING") != 0)
  265. {
  266. Log(LOG_ERR,
  267. "Received command without prefix (connection %d, command \"%s\")!?",
  268. Idx, Req->command);
  269. if (!Conn_WriteStr(Idx, "ERROR :Prefix missing"))
  270. *Closed = true;
  271. return false;
  272. }
  273. if (!Req->prefix)
  274. return true;
  275. /* only validate if this connection is already registered */
  276. if (Client_Type(client) != CLIENT_USER
  277. && Client_Type(client) != CLIENT_SERVER
  278. && Client_Type(client) != CLIENT_SERVICE) {
  279. /* not registered, ignore prefix */
  280. Req->prefix = NULL;
  281. return true;
  282. }
  283. /* check if client in prefix is known */
  284. c = Client_Search(Req->prefix);
  285. if (!c) {
  286. if (Client_Type(client) != CLIENT_SERVER) {
  287. Log(LOG_ERR,
  288. "Ignoring command with invalid prefix \"%s\" from \"%s\" (connection %d, command \"%s\")!",
  289. Req->prefix, Client_ID(client), Idx, Req->command);
  290. if (!Conn_WriteStr(Idx,
  291. "ERROR :Invalid prefix \"%s\"",
  292. Req->prefix))
  293. *Closed = true;
  294. IRC_SetPenalty(client, 2);
  295. } else
  296. LogDebug("Ignoring command with invalid prefix \"%s\" from \"%s\" (connection %d, command \"%s\")!",
  297. Req->prefix, Client_ID(client), Idx, Req->command);
  298. return false;
  299. }
  300. /* check if the client named in the prefix is expected
  301. * to come from that direction */
  302. if (Client_NextHop(c) != client) {
  303. if (Client_Type(client) != CLIENT_SERVER) {
  304. Log(LOG_ERR,
  305. "Spoofed prefix \"%s\" from \"%s\" (connection %d, command \"%s\"), closing connection!",
  306. Req->prefix, Client_ID(client), Idx, Req->command);
  307. Conn_Close(Idx, NULL, "Spoofed prefix", true);
  308. *Closed = true;
  309. } else {
  310. Log(LOG_WARNING,
  311. "Ignoring command with spoofed prefix \"%s\" from \"%s\" (connection %d, command \"%s\")!",
  312. Req->prefix, Client_ID(client), Idx, Req->command);
  313. }
  314. return false;
  315. }
  316. return true;
  317. } /* Validate_Prefix */
  318. static bool
  319. Validate_Command( UNUSED CONN_ID Idx, UNUSED REQUEST *Req, bool *Closed )
  320. {
  321. assert( Idx >= 0 );
  322. assert( Req != NULL );
  323. *Closed = false;
  324. return true;
  325. } /* Validate_Command */
  326. static bool
  327. #ifdef STRICT_RFC
  328. Validate_Args(CONN_ID Idx, REQUEST *Req, bool *Closed)
  329. #else
  330. Validate_Args(UNUSED CONN_ID Idx, UNUSED REQUEST *Req, bool *Closed)
  331. #endif
  332. {
  333. #ifdef STRICT_RFC
  334. int i;
  335. #endif
  336. *Closed = false;
  337. #ifdef STRICT_RFC
  338. assert( Idx >= 0 );
  339. assert( Req != NULL );
  340. /* CR and LF are never allowed in command parameters.
  341. * But since we do accept lines terminated only with CR or LF in
  342. * "non-RFC-compliant mode" (besides the correct CR+LF combination),
  343. * this check can only trigger in "strict RFC" mode; therefore we
  344. * optimize it away otherwise ... */
  345. for (i = 0; i < Req->argc; i++) {
  346. if (strchr(Req->argv[i], '\r') || strchr(Req->argv[i], '\n')) {
  347. Log(LOG_ERR,
  348. "Invalid character(s) in parameter (connection %d, command %s)!?",
  349. Idx, Req->command);
  350. if (!Conn_WriteStr(Idx,
  351. "ERROR :Invalid character(s) in parameter!"))
  352. *Closed = true;
  353. return false;
  354. }
  355. }
  356. #endif
  357. return true;
  358. } /* Validate_Args */
  359. /* Command is a status code ("numeric") from another server */
  360. static bool
  361. Handle_Numeric(CLIENT *client, REQUEST *Req)
  362. {
  363. static const struct _NUMERIC Numerics[] = {
  364. { 5, IRC_Num_ISUPPORT },
  365. { 20, NULL },
  366. { 376, IRC_Num_ENDOFMOTD }
  367. };
  368. int i, num;
  369. char str[COMMAND_LEN];
  370. CLIENT *prefix, *target = NULL;
  371. /* Determine target */
  372. if (Req->argc > 0) {
  373. if (strcmp(Req->argv[0], "*") != 0)
  374. target = Client_Search(Req->argv[0]);
  375. else
  376. target = Client_ThisServer();
  377. }
  378. if (!target) {
  379. /* Status code without target!? */
  380. if (Req->argc > 0)
  381. Log(LOG_WARNING,
  382. "Unknown target for status code %s: \"%s\"",
  383. Req->command, Req->argv[0]);
  384. else
  385. Log(LOG_WARNING,
  386. "Unknown target for status code %s!",
  387. Req->command);
  388. return true;
  389. }
  390. if (target == Client_ThisServer()) {
  391. /* This server is the target of the numeric */
  392. num = atoi(Req->command);
  393. for (i = 0; i < (int) C_ARRAY_SIZE(Numerics); i++) {
  394. if (num == Numerics[i].numeric) {
  395. if (!Numerics[i].function)
  396. return CONNECTED;
  397. return Numerics[i].function(client, Req);
  398. }
  399. }
  400. LogDebug("Ignored status code %s from \"%s\".",
  401. Req->command, Client_ID(client));
  402. return true;
  403. }
  404. /* Determine source */
  405. if (!Req->prefix) {
  406. Log(LOG_WARNING,
  407. "Got status code %s from \"%s\" without prefix!?",
  408. Req->command, Client_ID(client));
  409. return true;
  410. }
  411. prefix = Client_Search(Req->prefix);
  412. if (! prefix) { /* Oops, unknown prefix!? */
  413. Log(LOG_WARNING, "Got status code %s from unknown source: \"%s\"", Req->command, Req->prefix);
  414. return true;
  415. }
  416. /* Forward status code */
  417. strlcpy(str, Req->command, sizeof(str));
  418. for (i = 0; i < Req->argc; i++) {
  419. if (i < Req->argc - 1)
  420. strlcat(str, " ", sizeof(str));
  421. else
  422. strlcat(str, " :", sizeof(str));
  423. strlcat(str, Req->argv[i], sizeof(str));
  424. }
  425. return IRC_WriteStrClientPrefix(target, prefix, "%s", str);
  426. }
  427. static bool
  428. Handle_Request( CONN_ID Idx, REQUEST *Req )
  429. {
  430. CLIENT *client;
  431. bool result = CONNECTED;
  432. int client_type;
  433. COMMAND *cmd;
  434. assert( Idx >= 0 );
  435. assert( Req != NULL );
  436. assert( Req->command != NULL );
  437. client = Conn_GetClient( Idx );
  438. assert( client != NULL );
  439. /* Numeric? */
  440. client_type = Client_Type(client);
  441. if ((client_type == CLIENT_SERVER ||
  442. client_type == CLIENT_UNKNOWNSERVER)
  443. && strlen(Req->command) == 3 && atoi(Req->command) > 1)
  444. return Handle_Numeric(client, Req);
  445. cmd = My_Commands;
  446. while (cmd->name) {
  447. if (strcasecmp(Req->command, cmd->name) != 0) {
  448. cmd++;
  449. continue;
  450. }
  451. if (!(client_type & cmd->type)) {
  452. if (client_type == CLIENT_USER
  453. && cmd->type & CLIENT_SERVER)
  454. return IRC_WriteErrClient(client,
  455. ERR_NOTREGISTEREDSERVER_MSG,
  456. Client_ID(client));
  457. else
  458. return IRC_WriteErrClient(client,
  459. ERR_NOTREGISTERED_MSG,
  460. Client_ID(client));
  461. }
  462. if (cmd->penalty)
  463. IRC_SetPenalty(client, cmd->penalty);
  464. if (Req->argc < cmd->min_argc ||
  465. (cmd->max_argc != -1 && Req->argc > cmd->max_argc))
  466. return IRC_WriteErrClient(client, ERR_NEEDMOREPARAMS_MSG,
  467. Client_ID(client), Req->command);
  468. /* Command is allowed for this client: call it and count
  469. * generated bytes in output */
  470. Conn_ResetWCounter();
  471. result = (cmd->function)(client, Req);
  472. cmd->bytes += Conn_WCounter();
  473. /* Adjust counters */
  474. if (client_type != CLIENT_SERVER)
  475. cmd->lcount++;
  476. else
  477. cmd->rcount++;
  478. /* Return result of command (CONNECTED/DISCONNECTED). */
  479. return result;
  480. }
  481. if (client_type != CLIENT_USER &&
  482. client_type != CLIENT_SERVER &&
  483. client_type != CLIENT_SERVICE )
  484. return true;
  485. LogDebug("Connection %d: Unknown command \"%s\", %d %s,%s prefix.",
  486. Client_Conn( client ), Req->command, Req->argc,
  487. Req->argc == 1 ? "parameter" : "parameters",
  488. Req->prefix ? "" : " no" );
  489. /* Unknown command and registered connection: generate error: */
  490. if (client_type != CLIENT_SERVER)
  491. result = IRC_WriteErrClient(client, ERR_UNKNOWNCOMMAND_MSG,
  492. Client_ID(client), Req->command);
  493. return result;
  494. } /* Handle_Request */
  495. /**
  496. * Check if incoming messages contains CTCP commands and should be dropped.
  497. *
  498. * @param Request NULL terminated incoming command.
  499. * @returns true, when the message should be dropped.
  500. */
  501. static bool
  502. ScrubCTCP(char *Request)
  503. {
  504. static const char me_cmd[] = "ACTION ";
  505. static const char ctcp_char = 0x1;
  506. bool dropCommand = false;
  507. char *ptr = Request;
  508. char *ptrEnd = strchr(Request, '\0');
  509. if (Request[0] == ':' && ptrEnd > ptr)
  510. ptr++;
  511. while (ptr != ptrEnd && *ptr != ':')
  512. ptr++;
  513. if ((ptrEnd - ptr) > 1) {
  514. ptr++;
  515. if (*ptr == ctcp_char) {
  516. dropCommand = true;
  517. ptr++;
  518. /* allow /me commands */
  519. if ((size_t)(ptrEnd - ptr) >= strlen(me_cmd)
  520. && !strncmp(ptr, me_cmd, strlen(me_cmd)))
  521. dropCommand = false;
  522. }
  523. }
  524. return dropCommand;
  525. }
  526. /* -eof- */