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