parse.c 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606
  1. /*
  2. * ngIRCd -- The Next Generation IRC Daemon
  3. * Copyright (c)2001-2015 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, 4, 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 true on success (valid command or "regular" error), false if a
  155. * 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 )) return ! closed;
  231. if( ! Validate_Command( Idx, &req, &closed )) return ! closed;
  232. if( ! Validate_Args( Idx, &req, &closed )) return ! closed;
  233. return Handle_Request( Idx, &req );
  234. } /* Parse_Request */
  235. /**
  236. * Initialize request structure.
  237. * @param Req Request structure to be initialized.
  238. */
  239. static void
  240. Init_Request( REQUEST *Req )
  241. {
  242. int i;
  243. assert( Req != NULL );
  244. Req->prefix = NULL;
  245. Req->command = NULL;
  246. for( i = 0; i < 15; Req->argv[i++] = NULL );
  247. Req->argc = 0;
  248. } /* Init_Request */
  249. static bool
  250. Validate_Prefix( CONN_ID Idx, REQUEST *Req, bool *Closed )
  251. {
  252. CLIENT *client, *c;
  253. assert( Idx >= 0 );
  254. assert( Req != NULL );
  255. *Closed = false;
  256. client = Conn_GetClient( Idx );
  257. assert( client != NULL );
  258. if (!Req->prefix && Client_Type(client) == CLIENT_SERVER
  259. && !(Conn_Options(Idx) & CONN_RFC1459)
  260. && strcasecmp(Req->command, "ERROR") != 0
  261. && strcasecmp(Req->command, "PING") != 0)
  262. {
  263. Log(LOG_ERR,
  264. "Received command without prefix (connection %d, command \"%s\")!?",
  265. Idx, Req->command);
  266. if (!Conn_WriteStr(Idx, "ERROR :Prefix missing"))
  267. *Closed = true;
  268. return false;
  269. }
  270. if (!Req->prefix)
  271. return true;
  272. /* only validate if this connection is already registered */
  273. if (Client_Type(client) != CLIENT_USER
  274. && Client_Type(client) != CLIENT_SERVER
  275. && Client_Type(client) != CLIENT_SERVICE) {
  276. /* not registered, ignore prefix */
  277. Req->prefix = NULL;
  278. return true;
  279. }
  280. /* check if client in prefix is known */
  281. c = Client_Search(Req->prefix);
  282. if (!c) {
  283. if (Client_Type(client) != CLIENT_SERVER) {
  284. Log(LOG_ERR,
  285. "Ignoring command with invalid prefix \"%s\" from \"%s\" (connection %d, command \"%s\")!",
  286. Req->prefix, Client_ID(client), Idx, Req->command);
  287. if (!Conn_WriteStr(Idx,
  288. "ERROR :Invalid prefix \"%s\"",
  289. Req->prefix))
  290. *Closed = true;
  291. IRC_SetPenalty(client, 2);
  292. } else
  293. LogDebug("Ignoring command with invalid prefix \"%s\" from \"%s\" (connection %d, command \"%s\")!",
  294. Req->prefix, Client_ID(client), Idx, Req->command);
  295. return false;
  296. }
  297. /* check if the client named in the prefix is expected
  298. * to come from that direction */
  299. if (Client_NextHop(c) != client) {
  300. if (Client_Type(client) != CLIENT_SERVER) {
  301. Log(LOG_ERR,
  302. "Spoofed prefix \"%s\" from \"%s\" (connection %d, command \"%s\"), closing connection!",
  303. Req->prefix, Client_ID(client), Idx, Req->command);
  304. Conn_Close(Idx, NULL, "Spoofed prefix", true);
  305. *Closed = true;
  306. } else {
  307. Log(LOG_WARNING,
  308. "Ignoring command with spoofed prefix \"%s\" from \"%s\" (connection %d, command \"%s\")!",
  309. Req->prefix, Client_ID(client), Idx, Req->command);
  310. }
  311. return false;
  312. }
  313. return true;
  314. } /* Validate_Prefix */
  315. static bool
  316. Validate_Command( UNUSED CONN_ID Idx, UNUSED REQUEST *Req, bool *Closed )
  317. {
  318. assert( Idx >= 0 );
  319. assert( Req != NULL );
  320. *Closed = false;
  321. return true;
  322. } /* Validate_Command */
  323. static bool
  324. #ifdef STRICT_RFC
  325. Validate_Args(CONN_ID Idx, REQUEST *Req, bool *Closed)
  326. #else
  327. Validate_Args(UNUSED CONN_ID Idx, UNUSED REQUEST *Req, bool *Closed)
  328. #endif
  329. {
  330. #ifdef STRICT_RFC
  331. int i;
  332. #endif
  333. *Closed = false;
  334. #ifdef STRICT_RFC
  335. assert( Idx >= 0 );
  336. assert( Req != NULL );
  337. /* CR and LF are never allowed in command parameters.
  338. * But since we do accept lines terminated only with CR or LF in
  339. * "non-RFC-compliant mode" (besides the correct CR+LF combination),
  340. * this check can only trigger in "strict RFC" mode; therefore we
  341. * optimize it away otherwise ... */
  342. for (i = 0; i < Req->argc; i++) {
  343. if (strchr(Req->argv[i], '\r') || strchr(Req->argv[i], '\n')) {
  344. Log(LOG_ERR,
  345. "Invalid character(s) in parameter (connection %d, command %s)!?",
  346. Idx, Req->command);
  347. if (!Conn_WriteStr(Idx,
  348. "ERROR :Invalid character(s) in parameter!"))
  349. *Closed = true;
  350. return false;
  351. }
  352. }
  353. #endif
  354. return true;
  355. } /* Validate_Args */
  356. /* Command is a status code ("numeric") from another server */
  357. static bool
  358. Handle_Numeric(CLIENT *client, REQUEST *Req)
  359. {
  360. static const struct _NUMERIC Numerics[] = {
  361. { 5, IRC_Num_ISUPPORT },
  362. { 20, NULL },
  363. { 376, IRC_Num_ENDOFMOTD }
  364. };
  365. int i, num;
  366. char str[COMMAND_LEN];
  367. CLIENT *prefix, *target = NULL;
  368. /* Determine target */
  369. if (Req->argc > 0) {
  370. if (strcmp(Req->argv[0], "*") != 0)
  371. target = Client_Search(Req->argv[0]);
  372. else
  373. target = Client_ThisServer();
  374. }
  375. if (!target) {
  376. /* Status code without target!? */
  377. if (Req->argc > 0)
  378. Log(LOG_WARNING,
  379. "Unknown target for status code %s: \"%s\"",
  380. Req->command, Req->argv[0]);
  381. else
  382. Log(LOG_WARNING,
  383. "Unknown target for status code %s!",
  384. Req->command);
  385. return true;
  386. }
  387. if (target == Client_ThisServer()) {
  388. /* This server is the target of the numeric */
  389. num = atoi(Req->command);
  390. for (i = 0; i < (int) C_ARRAY_SIZE(Numerics); i++) {
  391. if (num == Numerics[i].numeric) {
  392. if (!Numerics[i].function)
  393. return CONNECTED;
  394. return Numerics[i].function(client, Req);
  395. }
  396. }
  397. LogDebug("Ignored status code %s from \"%s\".",
  398. Req->command, Client_ID(client));
  399. return true;
  400. }
  401. /* Determine source */
  402. if (!Req->prefix) {
  403. Log(LOG_WARNING,
  404. "Got status code %s from \"%s\" without prefix!?",
  405. Req->command, Client_ID(client));
  406. return true;
  407. }
  408. prefix = Client_Search(Req->prefix);
  409. if (! prefix) { /* Oops, unknown prefix!? */
  410. Log(LOG_WARNING, "Got status code %s from unknown source: \"%s\"", Req->command, Req->prefix);
  411. return true;
  412. }
  413. /* Forward status code */
  414. strlcpy(str, Req->command, sizeof(str));
  415. for (i = 0; i < Req->argc; i++) {
  416. if (i < Req->argc - 1)
  417. strlcat(str, " ", sizeof(str));
  418. else
  419. strlcat(str, " :", sizeof(str));
  420. strlcat(str, Req->argv[i], sizeof(str));
  421. }
  422. return IRC_WriteStrClientPrefix(target, prefix, "%s", str);
  423. }
  424. static bool
  425. Handle_Request( CONN_ID Idx, REQUEST *Req )
  426. {
  427. CLIENT *client;
  428. bool result = true;
  429. int client_type;
  430. COMMAND *cmd;
  431. assert( Idx >= 0 );
  432. assert( Req != NULL );
  433. assert( Req->command != NULL );
  434. client = Conn_GetClient( Idx );
  435. assert( client != NULL );
  436. /* Numeric? */
  437. client_type = Client_Type(client);
  438. if ((client_type == CLIENT_SERVER ||
  439. client_type == CLIENT_UNKNOWNSERVER)
  440. && strlen(Req->command) == 3 && atoi(Req->command) > 1)
  441. return Handle_Numeric(client, Req);
  442. cmd = My_Commands;
  443. while (cmd->name) {
  444. if (strcasecmp(Req->command, cmd->name) != 0) {
  445. cmd++;
  446. continue;
  447. }
  448. if (!(client_type & cmd->type)) {
  449. if (client_type == CLIENT_USER
  450. && cmd->type & CLIENT_SERVER)
  451. return IRC_WriteErrClient(client,
  452. ERR_NOTREGISTEREDSERVER_MSG,
  453. Client_ID(client));
  454. else
  455. return IRC_WriteErrClient(client,
  456. ERR_NOTREGISTERED_MSG,
  457. Client_ID(client));
  458. }
  459. if (cmd->penalty)
  460. IRC_SetPenalty(client, cmd->penalty);
  461. if (Req->argc < cmd->min_argc ||
  462. (cmd->max_argc != -1 && Req->argc > cmd->max_argc))
  463. return IRC_WriteErrClient(client, ERR_NEEDMOREPARAMS_MSG,
  464. Client_ID(client), Req->command);
  465. /* Command is allowed for this client: call it and count
  466. * generated bytes in output */
  467. Conn_ResetWCounter();
  468. result = (cmd->function)(client, Req);
  469. cmd->bytes += Conn_WCounter();
  470. /* Adjust counters */
  471. if (client_type != CLIENT_SERVER)
  472. cmd->lcount++;
  473. else
  474. cmd->rcount++;
  475. return result;
  476. }
  477. if (client_type != CLIENT_USER &&
  478. client_type != CLIENT_SERVER &&
  479. client_type != CLIENT_SERVICE )
  480. return true;
  481. /* Unknown command and registered connection: generate error: */
  482. LogDebug("Connection %d: Unknown command \"%s\", %d %s,%s prefix.",
  483. Client_Conn( client ), Req->command, Req->argc,
  484. Req->argc == 1 ? "parameter" : "parameters",
  485. Req->prefix ? "" : " no" );
  486. if (Client_Type(client) != CLIENT_SERVER)
  487. result = IRC_WriteErrClient(client, ERR_UNKNOWNCOMMAND_MSG,
  488. Client_ID(client), Req->command);
  489. return result;
  490. } /* Handle_Request */
  491. /**
  492. * Check if incoming messages contains CTCP commands and should be dropped.
  493. *
  494. * @param Request NULL terminated incoming command.
  495. * @returns true, when the message should be dropped.
  496. */
  497. static bool
  498. ScrubCTCP(char *Request)
  499. {
  500. static const char me_cmd[] = "ACTION ";
  501. static const char ctcp_char = 0x1;
  502. bool dropCommand = false;
  503. char *ptr = Request;
  504. char *ptrEnd = strchr(Request, '\0');
  505. if (Request[0] == ':' && ptrEnd > ptr)
  506. ptr++;
  507. while (ptr != ptrEnd && *ptr != ':')
  508. ptr++;
  509. if ((ptrEnd - ptr) > 1) {
  510. ptr++;
  511. if (*ptr == ctcp_char) {
  512. dropCommand = true;
  513. ptr++;
  514. /* allow /me commands */
  515. if ((size_t)(ptrEnd - ptr) >= strlen(me_cmd)
  516. && !strncmp(ptr, me_cmd, strlen(me_cmd)))
  517. dropCommand = false;
  518. }
  519. }
  520. return dropCommand;
  521. }
  522. /* -eof- */