Annotation of ircnowd/src/ngircd/irc.c, Revision 1.1.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