irc.c 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557
  1. /*
  2. * ngIRCd -- The Next Generation IRC Daemon
  3. * Copyright (c)2001-2004 Alexander Barton <alex@barton.de>
  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. * IRC commands
  12. */
  13. #include "portab.h"
  14. static char UNUSED id[] = "$Id: irc.c,v 1.132 2008/01/15 22:28:14 fw Exp $";
  15. #include "imp.h"
  16. #include <assert.h>
  17. #include <stdio.h>
  18. #include <string.h>
  19. #include "ngircd.h"
  20. #include "resolve.h"
  21. #include "conn-func.h"
  22. #include "conf.h"
  23. #include "client.h"
  24. #include "channel.h"
  25. #include "defines.h"
  26. #include "irc-write.h"
  27. #include "log.h"
  28. #include "match.h"
  29. #include "messages.h"
  30. #include "parse.h"
  31. #include "tool.h"
  32. #include "exp.h"
  33. #include "irc.h"
  34. static char *Option_String PARAMS((CONN_ID Idx));
  35. static bool Send_Message PARAMS((CLIENT *Client, REQUEST *Req, int ForceType,
  36. bool SendErrors));
  37. static bool Send_Message_Mask PARAMS((CLIENT *from, char *command,
  38. char *targetMask, char *message,
  39. bool SendErrors));
  40. GLOBAL bool
  41. IRC_ERROR( CLIENT *Client, REQUEST *Req )
  42. {
  43. assert( Client != NULL );
  44. assert( Req != NULL );
  45. if( Req->argc < 1 ) Log( LOG_NOTICE, "Got ERROR from \"%s\"!", Client_Mask( Client ));
  46. else Log( LOG_NOTICE, "Got ERROR from \"%s\": %s!", Client_Mask( Client ), Req->argv[0] );
  47. return CONNECTED;
  48. } /* IRC_ERROR */
  49. /**
  50. * Kill client on request.
  51. * This function implements the IRC command "KILL" wich is used to selectively
  52. * disconnect clients. It can be used by IRC operators and servers, for example
  53. * to "solve" nick collisions after netsplits.
  54. * Please note that this function is also called internally, without a real
  55. * KILL command beeing received over the network! Client is Client_ThisServer()
  56. * in this case. */
  57. GLOBAL bool
  58. IRC_KILL( CLIENT *Client, REQUEST *Req )
  59. {
  60. CLIENT *prefix, *c;
  61. char reason[COMMAND_LEN], *msg;
  62. CONN_ID my_conn, conn;
  63. assert( Client != NULL );
  64. assert( Req != NULL );
  65. if(( Client_Type( Client ) != CLIENT_SERVER ) &&
  66. ( ! Client_OperByMe( Client )))
  67. {
  68. /* The originator of the KILL is neither an IRC operator of
  69. * this server nor a server. */
  70. return IRC_WriteStrClient( Client, ERR_NOPRIVILEGES_MSG,
  71. Client_ID( Client ));
  72. }
  73. if( Req->argc != 2 )
  74. {
  75. /* This command requires exactly 2 parameters! */
  76. return IRC_WriteStrClient( Client, ERR_NEEDMOREPARAMS_MSG,
  77. Client_ID( Client ), Req->command );
  78. }
  79. if( Req->prefix ) prefix = Client_Search( Req->prefix );
  80. else prefix = Client;
  81. if( ! prefix )
  82. {
  83. Log( LOG_WARNING, "Got KILL with invalid prefix: \"%s\"!",
  84. Req->prefix );
  85. prefix = Client_ThisServer( );
  86. }
  87. if( Client != Client_ThisServer( ))
  88. {
  89. /* This is a "real" KILL received from the network. */
  90. Log( LOG_NOTICE|LOG_snotice, "Got KILL command from \"%s\" for \"%s\": %s",
  91. Client_Mask( prefix ), Req->argv[0], Req->argv[1] );
  92. }
  93. /* Build reason string */
  94. if( Client_Type( Client ) == CLIENT_USER )
  95. {
  96. /* Prefix the "reason" if the originator is a regular user,
  97. * so users can't spoof KILLs of servers. */
  98. snprintf( reason, sizeof( reason ), "KILLed by %s: %s",
  99. Client_ID( Client ), Req->argv[1] );
  100. }
  101. else
  102. strlcpy( reason, Req->argv[1], sizeof( reason ));
  103. /* Inform other servers */
  104. IRC_WriteStrServersPrefix( Client, prefix, "KILL %s :%s",
  105. Req->argv[0], reason );
  106. /* Save ID of this connection */
  107. my_conn = Client_Conn( Client );
  108. /* Do we host such a client? */
  109. c = Client_Search( Req->argv[0] );
  110. if( c )
  111. {
  112. if(( Client_Type( c ) != CLIENT_USER ) &&
  113. ( Client_Type( c ) != CLIENT_GOTNICK ))
  114. {
  115. /* Target of this KILL is not a regular user, this is
  116. * invalid! So we ignore this case if we received a
  117. * regular KILL from the network and try to kill the
  118. * client/connection anyway (but log an error!) if the
  119. * origin is the local server. */
  120. if( Client != Client_ThisServer( ))
  121. {
  122. /* Invalid KILL received from remote */
  123. if( Client_Type( c ) == CLIENT_SERVER )
  124. msg = ERR_CANTKILLSERVER_MSG;
  125. else
  126. msg = ERR_NOPRIVILEGES_MSG;
  127. return IRC_WriteStrClient( Client, msg,
  128. Client_ID( Client ));
  129. }
  130. Log( LOG_ERR, "Got KILL for invalid client type: %d, \"%s\"!",
  131. Client_Type( c ), Req->argv[0] );
  132. }
  133. /* Kill client NOW! */
  134. conn = Client_Conn( c );
  135. Client_Destroy( c, NULL, reason, false );
  136. if( conn > NONE )
  137. Conn_Close( conn, NULL, reason, true );
  138. }
  139. else
  140. Log( LOG_NOTICE, "Client with nick \"%s\" is unknown here.", Req->argv[0] );
  141. /* Are we still connected or were we killed, too? */
  142. if(( my_conn > NONE ) && ( Conn_GetClient( my_conn )))
  143. return CONNECTED;
  144. else
  145. return DISCONNECTED;
  146. } /* IRC_KILL */
  147. /**
  148. * Handler for the IRC command NOTICE.
  149. */
  150. GLOBAL bool
  151. IRC_NOTICE(CLIENT *Client, REQUEST *Req)
  152. {
  153. return Send_Message(Client, Req, CLIENT_USER, false);
  154. } /* IRC_NOTICE */
  155. /**
  156. * Handler for the IRC command PRIVMSG.
  157. */
  158. GLOBAL bool
  159. IRC_PRIVMSG(CLIENT *Client, REQUEST *Req)
  160. {
  161. return Send_Message(Client, Req, CLIENT_USER, true);
  162. } /* IRC_PRIVMSG */
  163. /**
  164. * Handler for the IRC command SQUERY.
  165. */
  166. GLOBAL bool
  167. IRC_SQUERY(CLIENT *Client, REQUEST *Req)
  168. {
  169. return Send_Message(Client, Req, CLIENT_SERVICE, true);
  170. } /* IRC_SQUERY */
  171. GLOBAL bool
  172. IRC_TRACE( CLIENT *Client, REQUEST *Req )
  173. {
  174. CLIENT *from, *target, *c;
  175. CONN_ID idx, idx2;
  176. char user[CLIENT_USER_LEN];
  177. assert( Client != NULL );
  178. assert( Req != NULL );
  179. /* Bad number of arguments? */
  180. if( Req->argc > 1 ) return IRC_WriteStrClient( Client, ERR_NORECIPIENT_MSG, Client_ID( Client ), Req->command );
  181. /* Search sender */
  182. if( Client_Type( Client ) == CLIENT_SERVER ) from = Client_Search( Req->prefix );
  183. else from = Client;
  184. if( ! from ) return IRC_WriteStrClient( Client, ERR_NOSUCHNICK_MSG, Client_ID( Client ), Req->prefix );
  185. /* Search target */
  186. if( Req->argc == 1 ) target = Client_Search( Req->argv[0] );
  187. else target = Client_ThisServer( );
  188. /* Forward command to other server? */
  189. if( target != Client_ThisServer( ))
  190. {
  191. if(( ! target ) || ( Client_Type( target ) != CLIENT_SERVER )) return IRC_WriteStrClient( from, ERR_NOSUCHSERVER_MSG, Client_ID( from ), Req->argv[0] );
  192. /* Send RPL_TRACELINK back to initiator */
  193. idx = Client_Conn( Client ); assert( idx > NONE );
  194. idx2 = Client_Conn( Client_NextHop( target )); assert( idx2 > NONE );
  195. if( ! IRC_WriteStrClient( from, RPL_TRACELINK_MSG, Client_ID( from ), PACKAGE_NAME, PACKAGE_VERSION, Client_ID( target ), Client_ID( Client_NextHop( target )), Option_String( idx2 ), time( NULL ) - Conn_StartTime( idx2 ), Conn_SendQ( idx ), Conn_SendQ( idx2 ))) return DISCONNECTED;
  196. /* Forward command */
  197. IRC_WriteStrClientPrefix( target, from, "TRACE %s", Req->argv[0] );
  198. return CONNECTED;
  199. }
  200. /* Infos about all connected servers */
  201. c = Client_First( );
  202. while( c )
  203. {
  204. if( Client_Conn( c ) > NONE )
  205. {
  206. /* Local client */
  207. if( Client_Type( c ) == CLIENT_SERVER )
  208. {
  209. /* Server link */
  210. strlcpy( user, Client_User( c ), sizeof( user ));
  211. if( user[0] == '~' ) strlcpy( user, "unknown", sizeof( user ));
  212. if( ! IRC_WriteStrClient( from, RPL_TRACESERVER_MSG, Client_ID( from ), Client_ID( c ), user, Client_Hostname( c ), Client_Mask( Client_ThisServer( )), Option_String( Client_Conn( c )))) return DISCONNECTED;
  213. }
  214. if(( Client_Type( c ) == CLIENT_USER ) && ( strchr( Client_Modes( c ), 'o' )))
  215. {
  216. /* IRC Operator */
  217. if( ! IRC_WriteStrClient( from, RPL_TRACEOPERATOR_MSG, Client_ID( from ), Client_ID( c ))) return DISCONNECTED;
  218. }
  219. }
  220. c = Client_Next( c );
  221. }
  222. IRC_SetPenalty( Client, 3 );
  223. return IRC_WriteStrClient( from, RPL_TRACEEND_MSG, Client_ID( from ), Conf_ServerName, PACKAGE_NAME, PACKAGE_VERSION, NGIRCd_DebugLevel );
  224. } /* IRC_TRACE */
  225. GLOBAL bool
  226. IRC_HELP( CLIENT *Client, REQUEST *Req )
  227. {
  228. COMMAND *cmd;
  229. assert( Client != NULL );
  230. assert( Req != NULL );
  231. /* Bad number of arguments? */
  232. if( Req->argc > 0 ) return IRC_WriteStrClient( Client, ERR_NORECIPIENT_MSG, Client_ID( Client ), Req->command );
  233. cmd = Parse_GetCommandStruct( );
  234. while( cmd->name )
  235. {
  236. if( ! IRC_WriteStrClient( Client, "NOTICE %s :%s", Client_ID( Client ), cmd->name )) return DISCONNECTED;
  237. cmd++;
  238. }
  239. IRC_SetPenalty( Client, 2 );
  240. return CONNECTED;
  241. } /* IRC_HELP */
  242. static char *
  243. Option_String( CONN_ID Idx )
  244. {
  245. static char option_txt[8];
  246. UINT16 options;
  247. options = Conn_Options(Idx);
  248. strcpy(option_txt, "F"); /* No idea what this means, but the
  249. * original ircd sends it ... */
  250. #ifdef ZLIB
  251. if(options & CONN_ZIP) /* zlib compression supported. */
  252. strcat(option_txt, "z");
  253. #endif
  254. return option_txt;
  255. } /* Option_String */
  256. static bool
  257. Send_Message(CLIENT * Client, REQUEST * Req, int ForceType, bool SendErrors)
  258. {
  259. CLIENT *cl, *from;
  260. CHANNEL *chan;
  261. char *currentTarget = Req->argv[0];
  262. char *lastCurrentTarget = NULL;
  263. assert(Client != NULL);
  264. assert(Req != NULL);
  265. if (Req->argc == 0) {
  266. if (!SendErrors)
  267. return CONNECTED;
  268. return IRC_WriteStrClient(Client, ERR_NORECIPIENT_MSG,
  269. Client_ID(Client), Req->command);
  270. }
  271. if (Req->argc == 1) {
  272. if (!SendErrors)
  273. return CONNECTED;
  274. return IRC_WriteStrClient(Client, ERR_NOTEXTTOSEND_MSG,
  275. Client_ID(Client));
  276. }
  277. if (Req->argc > 2) {
  278. if (!SendErrors)
  279. return CONNECTED;
  280. return IRC_WriteStrClient(Client, ERR_NEEDMOREPARAMS_MSG,
  281. Client_ID(Client), Req->command);
  282. }
  283. if (Client_Type(Client) == CLIENT_SERVER)
  284. from = Client_Search(Req->prefix);
  285. else
  286. from = Client;
  287. if (!from)
  288. return IRC_WriteStrClient(Client, ERR_NOSUCHNICK_MSG,
  289. Client_ID(Client), Req->prefix);
  290. /* handle msgtarget = msgto *("," msgto) */
  291. currentTarget = strtok_r(currentTarget, ",", &lastCurrentTarget);
  292. ngt_UpperStr(Req->command);
  293. while (currentTarget) {
  294. /* Check for and handle valid <msgto> of form:
  295. * RFC 2812 2.3.1:
  296. * msgto = channel / ( user [ "%" host ] "@" servername )
  297. * msgto =/ ( user "%" host ) / targetmask
  298. * msgto =/ nickname / ( nickname "!" user "@" host )
  299. */
  300. if (strchr(currentTarget, '!') == NULL)
  301. /* nickname */
  302. cl = Client_Search(currentTarget);
  303. else
  304. cl = NULL;
  305. if (cl == NULL) {
  306. /* If currentTarget isn't a nickname check for:
  307. * user ["%" host] "@" servername
  308. * user "%" host
  309. * nickname "!" user "@" host
  310. */
  311. char target[COMMAND_LEN];
  312. char * nick = NULL;
  313. char * user = NULL;
  314. char * host = NULL;
  315. char * server = NULL;
  316. strlcpy(target, currentTarget, COMMAND_LEN);
  317. server = strchr(target, '@');
  318. if (server) {
  319. *server = '\0';
  320. server++;
  321. }
  322. host = strchr(target, '%');
  323. if (host) {
  324. *host = '\0';
  325. host++;
  326. }
  327. user = strchr(target, '!');
  328. if (user) {
  329. /* msgto form: nick!user@host */
  330. *user = '\0';
  331. user++;
  332. nick = target;
  333. host = server; /* not "@server" but "@host" */
  334. } else {
  335. user = target;
  336. }
  337. for (cl = Client_First(); cl != NULL; cl = Client_Next(cl)) {
  338. if (Client_Type(cl) != CLIENT_USER &&
  339. Client_Type(cl) != CLIENT_SERVICE)
  340. continue;
  341. if (nick != NULL && host != NULL) {
  342. if (strcmp(nick, Client_ID(cl)) == 0 &&
  343. strcmp(user, Client_User(cl)) == 0 &&
  344. strcasecmp(host, Client_Hostname(cl)) == 0)
  345. break;
  346. else
  347. continue;
  348. }
  349. if (strcasecmp(user, Client_User(cl)) != 0)
  350. continue;
  351. if (host != NULL && strcasecmp(host,
  352. Client_Hostname(cl)) != 0)
  353. continue;
  354. if (server != NULL && strcasecmp(server,
  355. Client_ID(Client_Introducer(cl))) != 0)
  356. continue;
  357. break;
  358. }
  359. }
  360. if (cl) {
  361. /* Target is a user, enforce type */
  362. #ifndef STRICT_RFC
  363. if (Client_Type(cl) != ForceType &&
  364. !(ForceType == CLIENT_USER &&
  365. (Client_Type(cl) == CLIENT_USER ||
  366. Client_Type(cl) == CLIENT_SERVICE))) {
  367. #else
  368. if (Client_Type(cl) != ForceType) {
  369. #endif
  370. if (!SendErrors)
  371. return CONNECTED;
  372. return IRC_WriteStrClient(from, ERR_NOSUCHNICK_MSG,
  373. Client_ID(from),
  374. currentTarget);
  375. }
  376. #ifndef STRICT_RFC
  377. if (ForceType == CLIENT_SERVICE &&
  378. (Conn_Options(Client_Conn(Client_NextHop(cl)))
  379. & CONN_RFC1459)) {
  380. /* SQUERY command but RFC 1459 link: convert
  381. * request to PRIVMSG command */
  382. Req->command = "PRIVMSG";
  383. }
  384. #endif
  385. if (SendErrors && (Client_Type(Client) != CLIENT_SERVER)
  386. && strchr(Client_Modes(cl), 'a')) {
  387. /* Target is away */
  388. if (!IRC_WriteStrClient(from, RPL_AWAY_MSG,
  389. Client_ID(from),
  390. Client_ID(cl),
  391. Client_Away(cl)))
  392. return DISCONNECTED;
  393. }
  394. if (Client_Conn(from) > NONE) {
  395. Conn_UpdateIdle(Client_Conn(from));
  396. }
  397. if (!IRC_WriteStrClientPrefix(cl, from, "%s %s :%s",
  398. Req->command, Client_ID(cl),
  399. Req->argv[1]))
  400. return DISCONNECTED;
  401. } else if (ForceType != CLIENT_SERVICE
  402. && (chan = Channel_Search(currentTarget))) {
  403. if (!Channel_Write(chan, from, Client, Req->command,
  404. SendErrors, Req->argv[1]))
  405. return DISCONNECTED;
  406. } else if (ForceType != CLIENT_SERVICE
  407. /* $#: server/target mask, RFC 2812, sec. 3.3.1 */
  408. && strchr("$#", currentTarget[0])
  409. && strchr(currentTarget, '.')) {
  410. /* targetmask */
  411. if (!Send_Message_Mask(from, Req->command, currentTarget,
  412. Req->argv[1], SendErrors))
  413. return DISCONNECTED;
  414. } else {
  415. if (!SendErrors)
  416. return CONNECTED;
  417. if (!IRC_WriteStrClient(from, ERR_NOSUCHNICK_MSG,
  418. Client_ID(from), currentTarget))
  419. return DISCONNECTED;
  420. }
  421. currentTarget = strtok_r(NULL, ",", &lastCurrentTarget);
  422. }
  423. return CONNECTED;
  424. } /* Send_Message */
  425. static bool
  426. Send_Message_Mask(CLIENT * from, char * command, char * targetMask,
  427. char * message, bool SendErrors)
  428. {
  429. CLIENT *cl;
  430. bool client_match;
  431. char *mask = targetMask + 1;
  432. const char *check_wildcards;
  433. cl = NULL;
  434. if (strchr(Client_Modes(from), 'o') == NULL) {
  435. if (!SendErrors)
  436. return true;
  437. return IRC_WriteStrClient(from, ERR_NOPRIVILEGES_MSG,
  438. Client_ID(from));
  439. }
  440. /*
  441. * RFC 2812, sec. 3.3.1 requires that targetMask have at least one
  442. * dot (".") and no wildcards ("*", "?") following the last one.
  443. */
  444. check_wildcards = strrchr(targetMask, '.');
  445. assert(check_wildcards != NULL);
  446. if (check_wildcards &&
  447. check_wildcards[strcspn(check_wildcards, "*?")])
  448. {
  449. if (!SendErrors)
  450. return true;
  451. return IRC_WriteStrClient(from, ERR_WILDTOPLEVEL, targetMask);
  452. }
  453. /* #: hostmask, see RFC 2812, sec. 3.3.1 */
  454. if (targetMask[0] == '#') {
  455. for (cl = Client_First(); cl != NULL; cl = Client_Next(cl)) {
  456. if (Client_Type(cl) != CLIENT_USER)
  457. continue;
  458. client_match = MatchCaseInsensitive(mask, Client_Hostname(cl));
  459. if (client_match)
  460. if (!IRC_WriteStrClientPrefix(cl, from, "%s %s :%s",
  461. command, Client_ID(cl), message))
  462. return false;
  463. }
  464. } else {
  465. assert(targetMask[0] == '$'); /* $: server mask, see RFC 2812, sec. 3.3.1 */
  466. for (cl = Client_First(); cl != NULL; cl = Client_Next(cl)) {
  467. if (Client_Type(cl) != CLIENT_USER)
  468. continue;
  469. client_match = MatchCaseInsensitive(mask,
  470. Client_ID(Client_Introducer(cl)));
  471. if (client_match)
  472. if (!IRC_WriteStrClientPrefix(cl, from, "%s %s :%s",
  473. command, Client_ID(cl), message))
  474. return false;
  475. }
  476. }
  477. return CONNECTED;
  478. } /* Send_Message_Mask */
  479. /* -eof- */