irc.c 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842
  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 commands
  15. */
  16. #include <assert.h>
  17. #include <stdio.h>
  18. #include <string.h>
  19. #include <strings.h>
  20. #include <time.h>
  21. #include "ngircd.h"
  22. #include "conn-func.h"
  23. #include "conf.h"
  24. #include "channel.h"
  25. #ifdef ICONV
  26. # include "conn-encoding.h"
  27. #endif
  28. #include "irc-macros.h"
  29. #include "irc-write.h"
  30. #include "log.h"
  31. #include "match.h"
  32. #include "messages.h"
  33. #include "parse.h"
  34. #include "op.h"
  35. #include "irc.h"
  36. static char *Option_String PARAMS((CONN_ID Idx));
  37. static bool Send_Message PARAMS((CLIENT *Client, REQUEST *Req, int ForceType,
  38. bool SendErrors));
  39. static bool Send_Message_Mask PARAMS((CLIENT *from, char *command,
  40. char *targetMask, char *message,
  41. bool SendErrors));
  42. static bool Help PARAMS((CLIENT *Client, const char *Topic));
  43. /**
  44. * Check if a list limit is reached and inform client accordingly.
  45. *
  46. * @param From The client.
  47. * @param Count Reply item count.
  48. * @param Limit Reply limit.
  49. * @param Name Name of the list.
  50. * @return true if list limit has been reached; false otherwise.
  51. */
  52. GLOBAL bool
  53. IRC_CheckListTooBig(CLIENT *From, const int Count, const int Limit,
  54. const char *Name)
  55. {
  56. assert(From != NULL);
  57. assert(Count >= 0);
  58. assert(Limit > 0);
  59. assert(Name != NULL);
  60. if (Count < Limit)
  61. return false;
  62. (void)IRC_WriteStrClient(From,
  63. "NOTICE %s :%s list limit (%d) reached!",
  64. Client_ID(From), Name, Limit);
  65. IRC_SetPenalty(From, 2);
  66. return true;
  67. }
  68. /**
  69. * Handler for the IRC "ERROR" command.
  70. *
  71. * @param Client The client from which this command has been received.
  72. * @param Req Request structure with prefix and all parameters.
  73. * @return CONNECTED or DISCONNECTED.
  74. */
  75. GLOBAL bool
  76. IRC_ERROR(CLIENT *Client, REQUEST *Req)
  77. {
  78. char *msg;
  79. assert( Client != NULL );
  80. assert( Req != NULL );
  81. if (Client_Type(Client) != CLIENT_GOTPASS
  82. && Client_Type(Client) != CLIENT_GOTPASS_2813
  83. && Client_Type(Client) != CLIENT_UNKNOWNSERVER
  84. && Client_Type(Client) != CLIENT_SERVER
  85. && Client_Type(Client) != CLIENT_SERVICE) {
  86. LogDebug("Ignored ERROR command from \"%s\" ...",
  87. Client_Mask(Client));
  88. IRC_SetPenalty(Client, 2);
  89. return CONNECTED;
  90. }
  91. if (Req->argc < 1) {
  92. msg = "Got ERROR command";
  93. Log(LOG_NOTICE, "Got ERROR from \"%s\"!",
  94. Client_Mask(Client));
  95. } else {
  96. msg = Req->argv[0];
  97. Log(LOG_NOTICE, "Got ERROR from \"%s\": \"%s\"!",
  98. Client_Mask(Client), msg);
  99. }
  100. if (Client_Conn(Client) != NONE) {
  101. Conn_Close(Client_Conn(Client), NULL, msg, false);
  102. return DISCONNECTED;
  103. }
  104. return CONNECTED;
  105. } /* IRC_ERROR */
  106. /**
  107. * Handler for the IRC "KILL" command.
  108. *
  109. * This function implements the IRC command "KILL" which is used to selectively
  110. * disconnect clients. It can be used by IRC operators and servers, for example
  111. * to "solve" nick collisions after netsplits. See RFC 2812 section 3.7.1.
  112. *
  113. * Please note that this function is also called internally, without a real
  114. * KILL command being received over the network! Client is Client_ThisServer()
  115. * in this case, and the prefix in Req is NULL.
  116. *
  117. * @param Client The client from which this command has been received or
  118. * Client_ThisServer() when generated interanlly.
  119. * @param Req Request structure with prefix and all parameters.
  120. * @return CONNECTED or DISCONNECTED.
  121. */
  122. GLOBAL bool
  123. IRC_KILL(CLIENT *Client, REQUEST *Req)
  124. {
  125. CLIENT *prefix;
  126. char reason[COMMAND_LEN];
  127. assert (Client != NULL);
  128. assert (Req != NULL);
  129. if (Client_Type(Client) != CLIENT_SERVER && !Op_Check(Client, Req))
  130. return Op_NoPrivileges(Client, Req);
  131. /* Get prefix (origin); use the client if no prefix is given. */
  132. if (Req->prefix)
  133. prefix = Client_Search(Req->prefix);
  134. else
  135. prefix = Client;
  136. /* Log a warning message and use this server as origin when the
  137. * prefix (origin) is invalid. And this is the reason why we don't
  138. * use the _IRC_GET_SENDER_OR_RETURN_ macro above! */
  139. if (!prefix) {
  140. Log(LOG_WARNING, "Got KILL with invalid prefix: \"%s\"!",
  141. Req->prefix );
  142. prefix = Client_ThisServer();
  143. }
  144. if (Client != Client_ThisServer())
  145. Log(LOG_NOTICE|LOG_snotice,
  146. "Got KILL command from \"%s\" for \"%s\": \"%s\".",
  147. Client_Mask(prefix), Req->argv[0], Req->argv[1]);
  148. /* Build reason string: Prefix the "reason" if the originator is a
  149. * regular user, so users can't spoof KILLs of servers. */
  150. if (Client_Type(Client) == CLIENT_USER)
  151. snprintf(reason, sizeof(reason), "KILLed by %s: %s",
  152. Client_ID(Client), Req->argv[1]);
  153. else
  154. strlcpy(reason, Req->argv[1], sizeof(reason));
  155. return IRC_KillClient(Client, prefix, Req->argv[0], reason);
  156. }
  157. /**
  158. * Handler for the IRC "NOTICE" command.
  159. *
  160. * @param Client The client from which this command has been received.
  161. * @param Req Request structure with prefix and all parameters.
  162. * @return CONNECTED or DISCONNECTED.
  163. */
  164. GLOBAL bool
  165. IRC_NOTICE(CLIENT *Client, REQUEST *Req)
  166. {
  167. return Send_Message(Client, Req, CLIENT_USER, false);
  168. } /* IRC_NOTICE */
  169. /**
  170. * Handler for the IRC "PRIVMSG" command.
  171. *
  172. * @param Client The client from which this command has been received.
  173. * @param Req Request structure with prefix and all parameters.
  174. * @return CONNECTED or DISCONNECTED.
  175. */
  176. GLOBAL bool
  177. IRC_PRIVMSG(CLIENT *Client, REQUEST *Req)
  178. {
  179. return Send_Message(Client, Req, CLIENT_USER, true);
  180. } /* IRC_PRIVMSG */
  181. /**
  182. * Handler for the IRC "SQUERY" command.
  183. *
  184. * @param Client The client from which this command has been received.
  185. * @param Req Request structure with prefix and all parameters.
  186. * @return CONNECTED or DISCONNECTED.
  187. */
  188. GLOBAL bool
  189. IRC_SQUERY(CLIENT *Client, REQUEST *Req)
  190. {
  191. return Send_Message(Client, Req, CLIENT_SERVICE, true);
  192. } /* IRC_SQUERY */
  193. /*
  194. * Handler for the IRC "TRACE" command.
  195. *
  196. * @param Client The client from which this command has been received.
  197. * @param Req Request structure with prefix and all parameters.
  198. * @return CONNECTED or DISCONNECTED.
  199. */
  200. GLOBAL bool
  201. IRC_TRACE(CLIENT *Client, REQUEST *Req)
  202. {
  203. CLIENT *from, *target, *c;
  204. CONN_ID idx, idx2;
  205. char user[CLIENT_USER_LEN];
  206. assert(Client != NULL);
  207. assert(Req != NULL);
  208. _IRC_GET_SENDER_OR_RETURN_(from, Req, Client)
  209. _IRC_GET_TARGET_SERVER_OR_RETURN_(target, Req, 0, from)
  210. /* Forward command to other server? */
  211. if (target != Client_ThisServer()) {
  212. /* Send RPL_TRACELINK back to initiator */
  213. idx = Client_Conn(Client);
  214. assert(idx > NONE);
  215. idx2 = Client_Conn(Client_NextHop(target));
  216. assert(idx2 > NONE);
  217. if (!IRC_WriteStrClient(from, RPL_TRACELINK_MSG,
  218. Client_ID(from), PACKAGE_NAME,
  219. PACKAGE_VERSION, Client_ID(target),
  220. Client_ID(Client_NextHop(target)),
  221. Option_String(idx2),
  222. (long)(time(NULL) - Conn_StartTime(idx2)),
  223. Conn_SendQ(idx), Conn_SendQ(idx2)))
  224. return DISCONNECTED;
  225. /* Forward command */
  226. IRC_WriteStrClientPrefix(target, from, "TRACE %s", Req->argv[0]);
  227. return CONNECTED;
  228. }
  229. /* Infos about all connected servers */
  230. c = Client_First();
  231. while (c) {
  232. if (Client_Conn(c) > NONE) {
  233. /* Local client */
  234. if (Client_Type(c) == CLIENT_SERVER) {
  235. /* Server link */
  236. strlcpy(user, Client_User(c), sizeof(user));
  237. if (user[0] == '~')
  238. strlcpy(user, "unknown", sizeof(user));
  239. if (!IRC_WriteStrClient(from,
  240. RPL_TRACESERVER_MSG,
  241. Client_ID(from), Client_ID(c),
  242. user, Client_Hostname(c),
  243. Client_Mask(Client_ThisServer()),
  244. Option_String(Client_Conn(c))))
  245. return DISCONNECTED;
  246. }
  247. if (Client_Type(c) == CLIENT_USER
  248. && Client_HasMode(c, 'o')) {
  249. /* IRC Operator */
  250. if (!IRC_WriteStrClient(from,
  251. RPL_TRACEOPERATOR_MSG,
  252. Client_ID(from), Client_ID(c)))
  253. return DISCONNECTED;
  254. }
  255. }
  256. c = Client_Next( c );
  257. }
  258. return IRC_WriteStrClient(from, RPL_TRACEEND_MSG, Client_ID(from),
  259. Conf_ServerName, PACKAGE_NAME,
  260. PACKAGE_VERSION, NGIRCd_DebugLevel);
  261. } /* IRC_TRACE */
  262. /**
  263. * Handler for the IRC "HELP" command.
  264. *
  265. * @param Client The client from which this command has been received.
  266. * @param Req Request structure with prefix and all parameters.
  267. * @return CONNECTED or DISCONNECTED.
  268. */
  269. GLOBAL bool
  270. IRC_HELP(CLIENT *Client, REQUEST *Req)
  271. {
  272. COMMAND *cmd;
  273. assert(Client != NULL);
  274. assert(Req != NULL);
  275. if ((Req->argc == 0 && array_bytes(&Conf_Helptext) > 0)
  276. || (Req->argc >= 1 && strcasecmp(Req->argv[0], "Commands") != 0)) {
  277. /* Help text available and requested */
  278. if (Req->argc >= 1)
  279. return Help(Client, Req->argv[0]);
  280. if (!Help(Client, "Intro"))
  281. return DISCONNECTED;
  282. return CONNECTED;
  283. }
  284. cmd = Parse_GetCommandStruct();
  285. while(cmd->name) {
  286. if (!IRC_WriteStrClient(Client, "NOTICE %s :%s",
  287. Client_ID(Client), cmd->name))
  288. return DISCONNECTED;
  289. cmd++;
  290. }
  291. return CONNECTED;
  292. } /* IRC_HELP */
  293. /**
  294. * Kill an client identified by its nick name.
  295. *
  296. * Please note that after killig a client, its CLIENT cond CONNECTION
  297. * structures are invalid. So the caller must make sure on its own not to
  298. * access data of probably killed clients after calling this function!
  299. *
  300. * @param Client The client from which the command leading to the KILL has
  301. * been received, or NULL. The KILL will no be forwarded in this
  302. * direction. Only relevant when From is set, too.
  303. * @param From The client from which the command originated, or NULL for
  304. the local server.
  305. * @param Nick The nick name to kill.
  306. * @param Reason Text to send as reason to the client and other servers.
  307. */
  308. GLOBAL bool
  309. IRC_KillClient(CLIENT *Client, CLIENT *From, const char *Nick, const char *Reason)
  310. {
  311. const char *msg;
  312. CONN_ID my_conn = NONE, conn;
  313. CLIENT *c;
  314. assert(Nick != NULL);
  315. assert(Reason != NULL);
  316. /* Do we know such a client in the network? */
  317. c = Client_Search(Nick);
  318. if (!c) {
  319. LogDebug("Client with nick \"%s\" is unknown, not forwaring.", Nick);
  320. return CONNECTED;
  321. }
  322. if (Client_Type(c) != CLIENT_USER && Client_Type(c) != CLIENT_GOTNICK
  323. && Client_Type(c) != CLIENT_SERVICE) {
  324. /* Target of this KILL is not a regular user, this is
  325. * invalid! So we ignore this case if we received a
  326. * regular KILL from the network and try to kill the
  327. * client/connection anyway (but log an error!) if the
  328. * origin is the local server. */
  329. if (Client != Client_ThisServer()) {
  330. /* Invalid KILL received from remote */
  331. if (Client_Type(c) == CLIENT_SERVER)
  332. msg = ERR_CANTKILLSERVER_MSG;
  333. else
  334. msg = ERR_NOPRIVILEGES_MSG;
  335. return IRC_WriteErrClient(Client, msg, Client_ID(Client));
  336. }
  337. Log(LOG_ERR,
  338. "Got KILL for invalid client type: %d, \"%s\"!",
  339. Client_Type(c), Nick);
  340. }
  341. /* Inform other servers */
  342. IRC_WriteStrServersPrefix(From ? Client : NULL,
  343. From ? From : Client_ThisServer(),
  344. "KILL %s :%s", Nick, Reason);
  345. /* Save ID of this connection */
  346. if (Client)
  347. my_conn = Client_Conn(Client);
  348. /* Kill the client NOW:
  349. * - Close the local connection (if there is one),
  350. * - Destroy the CLIENT structure for remote clients.
  351. * Note: Conn_Close() removes the CLIENT structure as well. */
  352. conn = Client_Conn(c);
  353. if(conn > NONE)
  354. Conn_Close(conn, NULL, Reason, true);
  355. else
  356. Client_Destroy(c, NULL, Reason, false);
  357. /* Are we still connected or were we killed, too? */
  358. if (my_conn > NONE && Conn_GetClient(my_conn))
  359. return CONNECTED;
  360. else
  361. return DISCONNECTED;
  362. }
  363. /**
  364. * Send help for a given topic to the client.
  365. *
  366. * @param Client The client requesting help.
  367. * @param Topic The help topic requested.
  368. * @return CONNECTED or DISCONNECTED.
  369. */
  370. static bool
  371. Help(CLIENT *Client, const char *Topic)
  372. {
  373. char *line;
  374. size_t helptext_len, len_str, idx_start, lines = 0;
  375. bool in_article = false;
  376. assert(Client != NULL);
  377. assert(Topic != NULL);
  378. helptext_len = array_bytes(&Conf_Helptext);
  379. line = array_start(&Conf_Helptext);
  380. while (helptext_len > 0) {
  381. len_str = strlen(line) + 1;
  382. assert(helptext_len >= len_str);
  383. helptext_len -= len_str;
  384. if (in_article) {
  385. /* The first character in each article text line must
  386. * be a TAB (ASCII 9) character which will be stripped
  387. * in the output. If it is not a TAB, the end of the
  388. * article has been reached. */
  389. if (line[0] != '\t') {
  390. if (lines > 0)
  391. return CONNECTED;
  392. else
  393. break;
  394. }
  395. /* A single '.' character indicates an empty line */
  396. if (line[1] == '.' && line[2] == '\0')
  397. idx_start = 2;
  398. else
  399. idx_start = 1;
  400. if (!IRC_WriteStrClient(Client, "NOTICE %s :%s",
  401. Client_ID(Client),
  402. &line[idx_start]))
  403. return DISCONNECTED;
  404. lines++;
  405. } else {
  406. if (line[0] == '-' && line[1] == ' '
  407. && strcasecmp(&line[2], Topic) == 0)
  408. in_article = true;
  409. }
  410. line += len_str;
  411. }
  412. /* Help topic not found (or empty)! */
  413. if (!IRC_WriteStrClient(Client, "NOTICE %s :No help for \"%s\" found!",
  414. Client_ID(Client), Topic))
  415. return DISCONNECTED;
  416. return CONNECTED;
  417. }
  418. /**
  419. * Get pointer to a static string representing the connection "options".
  420. *
  421. * @param Idx Connection index.
  422. * @return Pointer to static (global) string buffer.
  423. */
  424. static char *
  425. #if defined(SSL_SUPPORT) || defined(ZLIB)
  426. Option_String(CONN_ID Idx)
  427. {
  428. static char option_txt[8];
  429. UINT16 options;
  430. assert(Idx != NONE);
  431. options = Conn_Options(Idx);
  432. strcpy(option_txt, "F"); /* No idea what this means, but the
  433. * original ircd sends it ... */
  434. #ifdef SSL_SUPPORT
  435. if(options & CONN_SSL) /* SSL encrypted link */
  436. strlcat(option_txt, "s", sizeof(option_txt));
  437. #endif
  438. #ifdef ZLIB
  439. if(options & CONN_ZIP) /* zlib compression enabled */
  440. strlcat(option_txt, "z", sizeof(option_txt));
  441. #endif
  442. return option_txt;
  443. #else
  444. Option_String(UNUSED CONN_ID Idx)
  445. {
  446. return "";
  447. #endif
  448. } /* Option_String */
  449. /**
  450. * Send a message to target(s).
  451. *
  452. * This function is used by IRC_{PRIVMSG|NOTICE|SQUERY} to actualy
  453. * send the message(s).
  454. *
  455. * @param Client The client from which this command has been received.
  456. * @param Req Request structure with prefix and all parameters.
  457. * @param ForceType Required type of the destination of the message(s).
  458. * @param SendErrors Whether to report errors back to the client or not.
  459. * @return CONNECTED or DISCONNECTED.
  460. */
  461. static bool
  462. Send_Message(CLIENT * Client, REQUEST * Req, int ForceType, bool SendErrors)
  463. {
  464. CLIENT *cl, *from;
  465. CL2CHAN *cl2chan;
  466. CHANNEL *chan;
  467. char *currentTarget = Req->argv[0];
  468. char *strtok_last = NULL;
  469. char *message = NULL;
  470. char *targets[MAX_HNDL_TARGETS];
  471. int i, target_nr = 0;
  472. assert(Client != NULL);
  473. assert(Req != NULL);
  474. if (Req->argc == 0) {
  475. if (!SendErrors)
  476. return CONNECTED;
  477. return IRC_WriteErrClient(Client, ERR_NORECIPIENT_MSG,
  478. Client_ID(Client), Req->command);
  479. }
  480. if (Req->argc == 1) {
  481. if (!SendErrors)
  482. return CONNECTED;
  483. return IRC_WriteErrClient(Client, ERR_NOTEXTTOSEND_MSG,
  484. Client_ID(Client));
  485. }
  486. if (Req->argc > 2) {
  487. if (!SendErrors)
  488. return CONNECTED;
  489. return IRC_WriteErrClient(Client, ERR_NEEDMOREPARAMS_MSG,
  490. Client_ID(Client), Req->command);
  491. }
  492. if (Client_Type(Client) == CLIENT_SERVER && Req->prefix)
  493. from = Client_Search(Req->prefix);
  494. else
  495. from = Client;
  496. if (!from)
  497. return IRC_WriteErrClient(Client, ERR_NOSUCHNICK_MSG,
  498. Client_ID(Client), Req->prefix);
  499. #ifdef ICONV
  500. if (Client_Conn(Client) > NONE)
  501. message = Conn_EncodingFrom(Client_Conn(Client), Req->argv[1]);
  502. else
  503. #endif
  504. message = Req->argv[1];
  505. /* handle msgtarget = msgto *("," msgto) */
  506. currentTarget = strtok_r(currentTarget, ",", &strtok_last);
  507. ngt_UpperStr(Req->command);
  508. /* Please note that "currentTarget" is NULL when the target contains
  509. * the separator character only, e. g. "," or ",,,," etc.! */
  510. while (currentTarget) {
  511. /* Make sure that there hasn't been such a target already: */
  512. targets[target_nr++] = currentTarget;
  513. for(i = 0; i < target_nr - 1; i++) {
  514. if (strcasecmp(currentTarget, targets[i]) == 0)
  515. goto send_next_target;
  516. }
  517. /* Check for and handle valid <msgto> of form:
  518. * RFC 2812 2.3.1:
  519. * msgto = channel / ( user [ "%" host ] "@" servername )
  520. * msgto =/ ( user "%" host ) / targetmask
  521. * msgto =/ nickname / ( nickname "!" user "@" host )
  522. */
  523. if (strchr(currentTarget, '!') == NULL)
  524. /* nickname */
  525. cl = Client_Search(currentTarget);
  526. else
  527. cl = NULL;
  528. if (cl == NULL) {
  529. /* If currentTarget isn't a nickname check for:
  530. * user ["%" host] "@" servername
  531. * user "%" host
  532. * nickname "!" user "@" host
  533. */
  534. char target[COMMAND_LEN];
  535. char * nick = NULL;
  536. char * user = NULL;
  537. char * host = NULL;
  538. char * server = NULL;
  539. strlcpy(target, currentTarget, COMMAND_LEN);
  540. server = strchr(target, '@');
  541. if (server) {
  542. *server = '\0';
  543. server++;
  544. }
  545. host = strchr(target, '%');
  546. if (host) {
  547. *host = '\0';
  548. host++;
  549. }
  550. user = strchr(target, '!');
  551. if (user) {
  552. /* msgto form: nick!user@host */
  553. *user = '\0';
  554. user++;
  555. nick = target;
  556. host = server; /* not "@server" but "@host" */
  557. } else {
  558. user = target;
  559. }
  560. for (cl = Client_First(); cl != NULL; cl = Client_Next(cl)) {
  561. if (Client_Type(cl) != CLIENT_USER &&
  562. Client_Type(cl) != CLIENT_SERVICE)
  563. continue;
  564. if (nick != NULL && host != NULL) {
  565. if (strcasecmp(nick, Client_ID(cl)) == 0 &&
  566. strcasecmp(user, Client_User(cl)) == 0 &&
  567. strcasecmp(host, Client_HostnameDisplayed(cl)) == 0)
  568. break;
  569. else
  570. continue;
  571. }
  572. if (strcasecmp(user, Client_User(cl)) != 0)
  573. continue;
  574. if (host != NULL && strcasecmp(host,
  575. Client_HostnameDisplayed(cl)) != 0)
  576. continue;
  577. if (server != NULL && strcasecmp(server,
  578. Client_ID(Client_Introducer(cl))) != 0)
  579. continue;
  580. break;
  581. }
  582. }
  583. if (cl) {
  584. /* Target is a user, enforce type */
  585. #ifndef STRICT_RFC
  586. if (Client_Type(cl) != ForceType &&
  587. !(ForceType == CLIENT_USER &&
  588. (Client_Type(cl) == CLIENT_USER ||
  589. Client_Type(cl) == CLIENT_SERVICE))) {
  590. #else
  591. if (Client_Type(cl) != ForceType) {
  592. #endif
  593. if (SendErrors && !IRC_WriteErrClient(
  594. from, ERR_NOSUCHNICK_MSG,Client_ID(from),
  595. currentTarget))
  596. return DISCONNECTED;
  597. goto send_next_target;
  598. }
  599. #ifndef STRICT_RFC
  600. if (ForceType == CLIENT_SERVICE &&
  601. (Conn_Options(Client_Conn(Client_NextHop(cl)))
  602. & CONN_RFC1459)) {
  603. /* SQUERY command but RFC 1459 link: convert
  604. * request to PRIVMSG command */
  605. Req->command = "PRIVMSG";
  606. }
  607. #endif
  608. if (Client_HasMode(cl, 'b') &&
  609. !Client_HasMode(from, 'R') &&
  610. !Client_HasMode(from, 'o') &&
  611. !(Client_Type(from) == CLIENT_SERVER) &&
  612. !(Client_Type(from) == CLIENT_SERVICE)) {
  613. if (SendErrors && !IRC_WriteErrClient(from,
  614. ERR_NONONREG_MSG,
  615. Client_ID(from), Client_ID(cl)))
  616. return DISCONNECTED;
  617. goto send_next_target;
  618. }
  619. if (Client_HasMode(cl, 'C') &&
  620. !Client_HasMode(from, 'o') &&
  621. !(Client_Type(from) == CLIENT_SERVER) &&
  622. !(Client_Type(from) == CLIENT_SERVICE)) {
  623. cl2chan = Channel_FirstChannelOf(cl);
  624. while (cl2chan) {
  625. chan = Channel_GetChannel(cl2chan);
  626. if (Channel_IsMemberOf(chan, from))
  627. break;
  628. cl2chan = Channel_NextChannelOf(cl, cl2chan);
  629. }
  630. if (!cl2chan) {
  631. if (SendErrors && !IRC_WriteErrClient(
  632. from, ERR_NOTONSAMECHANNEL_MSG,
  633. Client_ID(from), Client_ID(cl)))
  634. return DISCONNECTED;
  635. goto send_next_target;
  636. }
  637. }
  638. if (SendErrors && (Client_Type(Client) != CLIENT_SERVER)
  639. && Client_HasMode(cl, 'a')) {
  640. /* Target is away */
  641. if (!IRC_WriteStrClient(from, RPL_AWAY_MSG,
  642. Client_ID(from),
  643. Client_ID(cl),
  644. Client_Away(cl)))
  645. return DISCONNECTED;
  646. }
  647. if (Client_Conn(from) > NONE) {
  648. Conn_UpdateIdle(Client_Conn(from));
  649. }
  650. if (!IRC_WriteStrClientPrefix(cl, from, "%s %s :%s",
  651. Req->command, Client_ID(cl),
  652. message))
  653. return DISCONNECTED;
  654. } else if (ForceType != CLIENT_SERVICE
  655. && (chan = Channel_Search(currentTarget))) {
  656. /* Target is a channel */
  657. if (!Channel_Write(chan, from, Client, Req->command,
  658. SendErrors, message))
  659. return DISCONNECTED;
  660. } else if (ForceType != CLIENT_SERVICE
  661. && strchr("$#", currentTarget[0])
  662. && strchr(currentTarget, '.')) {
  663. /* $#: server/host mask, RFC 2812, sec. 3.3.1 */
  664. if (!Send_Message_Mask(from, Req->command, currentTarget,
  665. message, SendErrors))
  666. return DISCONNECTED;
  667. } else {
  668. if (!SendErrors)
  669. return CONNECTED;
  670. if (!IRC_WriteErrClient(from, ERR_NOSUCHNICK_MSG,
  671. Client_ID(from), currentTarget))
  672. return DISCONNECTED;
  673. }
  674. send_next_target:
  675. currentTarget = strtok_r(NULL, ",", &strtok_last);
  676. if (!currentTarget)
  677. break;
  678. Conn_SetPenalty(Client_Conn(Client), 1);
  679. if (target_nr >= MAX_HNDL_TARGETS) {
  680. /* Too many targets given! */
  681. return IRC_WriteErrClient(Client,
  682. ERR_TOOMANYTARGETS_MSG,
  683. currentTarget);
  684. }
  685. }
  686. return CONNECTED;
  687. } /* Send_Message */
  688. /**
  689. * Send a message to "target mask" target(s).
  690. *
  691. * See RFC 2812, sec. 3.3.1 for details.
  692. *
  693. * @param from The client from which this command has been received.
  694. * @param command The command to use (PRIVMSG, NOTICE, ...).
  695. * @param targetMask The "target mask" (will be verified by this function).
  696. * @param message The message to send.
  697. * @param SendErrors Whether to report errors back to the client or not.
  698. * @return CONNECTED or DISCONNECTED.
  699. */
  700. static bool
  701. Send_Message_Mask(CLIENT * from, char * command, char * targetMask,
  702. char * message, bool SendErrors)
  703. {
  704. CLIENT *cl;
  705. bool client_match;
  706. char *mask = targetMask + 1;
  707. const char *check_wildcards;
  708. cl = NULL;
  709. if (!Client_HasMode(from, 'o')) {
  710. if (!SendErrors)
  711. return true;
  712. return IRC_WriteErrClient(from, ERR_NOPRIVILEGES_MSG,
  713. Client_ID(from));
  714. }
  715. /*
  716. * RFC 2812, sec. 3.3.1 requires that targetMask have at least one
  717. * dot (".") and no wildcards ("*", "?") following the last one.
  718. */
  719. check_wildcards = strrchr(targetMask, '.');
  720. if (!check_wildcards || check_wildcards[strcspn(check_wildcards, "*?")]) {
  721. if (!SendErrors)
  722. return true;
  723. return IRC_WriteErrClient(from, ERR_WILDTOPLEVEL_MSG,
  724. targetMask);
  725. }
  726. if (targetMask[0] == '#') {
  727. /* #: hostmask, see RFC 2812, sec. 3.3.1 */
  728. for (cl = Client_First(); cl != NULL; cl = Client_Next(cl)) {
  729. if (Client_Type(cl) != CLIENT_USER)
  730. continue;
  731. client_match = MatchCaseInsensitive(mask, Client_Hostname(cl));
  732. if (client_match)
  733. if (!IRC_WriteStrClientPrefix(cl, from, "%s %s :%s",
  734. command, Client_ID(cl), message))
  735. return false;
  736. }
  737. } else {
  738. /* $: server mask, see RFC 2812, sec. 3.3.1 */
  739. assert(targetMask[0] == '$');
  740. for (cl = Client_First(); cl != NULL; cl = Client_Next(cl)) {
  741. if (Client_Type(cl) != CLIENT_USER)
  742. continue;
  743. client_match = MatchCaseInsensitive(mask,
  744. Client_ID(Client_Introducer(cl)));
  745. if (client_match)
  746. if (!IRC_WriteStrClientPrefix(cl, from, "%s %s :%s",
  747. command, Client_ID(cl), message))
  748. return false;
  749. }
  750. }
  751. return CONNECTED;
  752. } /* Send_Message_Mask */
  753. /* -eof- */