ViewVC Help
View File | Revision Log | Show Annotations | View Changeset | Root Listing
root/svn/ircd-hybrid/branches/8.2.x/modules/core/m_message.c
Revision: 1834
Committed: Fri Apr 19 19:50:27 2013 UTC (10 years, 11 months ago) by michael
Content type: text/x-csrc
Original Path: ircd-hybrid/trunk/modules/core/m_message.c
File size: 27255 byte(s)
Log Message:
- Revert to -r1831

File Contents

# Content
1 /*
2 * ircd-hybrid: an advanced Internet Relay Chat Daemon(ircd).
3 * m_message.c: Sends a (PRIVMSG|NOTICE) message to a user or channel.
4 *
5 * Copyright (C) 2002 by the past and present ircd coders, and others.
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
20 * USA
21 *
22 * $Id$
23 */
24
25 #include "stdinc.h"
26 #include "list.h"
27 #include "client.h"
28 #include "ircd.h"
29 #include "numeric.h"
30 #include "conf.h"
31 #include "s_serv.h"
32 #include "send.h"
33 #include "parse.h"
34 #include "modules.h"
35 #include "channel.h"
36 #include "channel_mode.h"
37 #include "irc_string.h"
38 #include "hash.h"
39 #include "packet.h"
40
41
42 struct entity
43 {
44 void *ptr;
45 int type;
46 int flags;
47 };
48
49 static int build_target_list(int p_or_n, const char *command,
50 struct Client *client_p,
51 struct Client *source_p,
52 char *nicks_channels, char *text);
53
54 static int flood_attack_client(int p_or_n, struct Client *source_p,
55 struct Client *target_p);
56 static int flood_attack_channel(int p_or_n, struct Client *source_p,
57 struct Channel *chptr);
58 static struct Client* find_userhost (char *, char *, int *);
59
60 #define ENTITY_NONE 0
61 #define ENTITY_CHANNEL 1
62 #define ENTITY_CHANOPS_ON_CHANNEL 2
63 #define ENTITY_CLIENT 3
64
65 static struct entity targets[512];
66 static int ntargets = 0;
67
68 static int duplicate_ptr(void *);
69
70 static void m_message(int, const char *, struct Client *,
71 struct Client *, int, char **);
72
73 static void msg_channel(int p_or_n, const char *command,
74 struct Client *client_p,
75 struct Client *source_p,
76 struct Channel *chptr, char *text);
77
78 static void msg_channel_flags(int p_or_n, const char *command,
79 struct Client *client_p,
80 struct Client *source_p,
81 struct Channel *chptr, int flags, char *text);
82
83 static void msg_client(int p_or_n, const char *command,
84 struct Client *source_p, struct Client *target_p,
85 char *text);
86
87 static void handle_special(int p_or_n, const char *command,
88 struct Client *client_p,
89 struct Client *source_p, char *nick, char *text);
90
91 /*
92 ** m_privmsg
93 **
94 ** massive cleanup
95 ** rev argv 6/91
96 **
97 ** Another massive cleanup Nov, 2000
98 ** (I don't think there is a single line left from 6/91. Maybe.)
99 ** m_privmsg and m_notice do basically the same thing.
100 ** in the original 2.8.2 code base, they were the same function
101 ** "m_message.c." When we did the great cleanup in conjuncton with bleep
102 ** of ircu fame, we split m_privmsg.c and m_notice.c.
103 ** I don't see the point of that now. Its harder to maintain, its
104 ** easier to introduce bugs into one version and not the other etc.
105 ** Really, the penalty of an extra function call isn't that big a deal folks.
106 ** -db Nov 13, 2000
107 **
108 */
109
110 #define PRIVMSG 0
111 #define NOTICE 1
112
113 static void
114 m_privmsg(struct Client *client_p, struct Client *source_p,
115 int parc, char *parv[])
116 {
117 /* servers have no reason to send privmsgs, yet sometimes there is cause
118 * for a notice.. (for example remote kline replies) --fl_
119 */
120 if (!IsClient(source_p))
121 return;
122
123 m_message(PRIVMSG, "PRIVMSG", client_p, source_p, parc, parv);
124 }
125
126 static void
127 m_notice(struct Client *client_p, struct Client *source_p,
128 int parc, char *parv[])
129 {
130 m_message(NOTICE, "NOTICE", client_p, source_p, parc, parv);
131 }
132
133 /*
134 * inputs - flag privmsg or notice
135 * - pointer to command "PRIVMSG" or "NOTICE"
136 * - pointer to client_p
137 * - pointer to source_p
138 * - pointer to channel
139 */
140 static void
141 m_message(int p_or_n, const char *command, struct Client *client_p,
142 struct Client *source_p, int parc, char *parv[])
143 {
144 int i;
145
146 if (parc < 2 || EmptyString(parv[1]))
147 {
148 if (p_or_n != NOTICE)
149 sendto_one(source_p, form_str(ERR_NORECIPIENT),
150 ID_or_name(&me, client_p),
151 ID_or_name(source_p, client_p), command);
152 return;
153 }
154
155 if (parc < 3 || EmptyString(parv[2]))
156 {
157 if (p_or_n != NOTICE)
158 sendto_one(source_p, form_str(ERR_NOTEXTTOSEND),
159 ID_or_name(&me, client_p),
160 ID_or_name(source_p, client_p));
161 return;
162 }
163
164 /* Finish the flood grace period... */
165 if (MyClient(source_p) && !IsFloodDone(source_p))
166 flood_endgrace(source_p);
167
168 if (build_target_list(p_or_n, command, client_p, source_p, parv[1],
169 parv[2]) < 0)
170 return;
171
172 for (i = 0; i < ntargets; i++)
173 {
174 switch (targets[i].type)
175 {
176 case ENTITY_CHANNEL:
177 msg_channel(p_or_n, command, client_p, source_p,
178 (struct Channel *)targets[i].ptr, parv[2]);
179 break;
180
181 case ENTITY_CHANOPS_ON_CHANNEL:
182 msg_channel_flags(p_or_n, command, client_p, source_p,
183 (struct Channel *)targets[i].ptr,
184 targets[i].flags, parv[2]);
185 break;
186
187 case ENTITY_CLIENT:
188 msg_client(p_or_n, command, source_p,
189 (struct Client *)targets[i].ptr, parv[2]);
190 break;
191 }
192 }
193 }
194
195 /* build_target_list()
196 *
197 * inputs - pointer to given client_p (server)
198 * - pointer to given source (oper/client etc.)
199 * - pointer to list of nicks/channels
200 * - pointer to table to place results
201 * - pointer to text (only used if source_p is an oper)
202 * output - number of valid entities
203 * side effects - target_table is modified to contain a list of
204 * pointers to channels or clients
205 * if source client is an oper
206 * all the classic old bizzare oper privmsg tricks
207 * are parsed and sent as is, if prefixed with $
208 * to disambiguate.
209 *
210 */
211 static int
212 build_target_list(int p_or_n, const char *command, struct Client *client_p,
213 struct Client *source_p, char *nicks_channels, char *text)
214 {
215 int type;
216 char *p = NULL, *nick, *target_list;
217 struct Channel *chptr = NULL;
218 struct Client *target_p = NULL;
219
220 target_list = nicks_channels;
221
222 ntargets = 0;
223
224 for (nick = strtoken(&p, target_list, ","); nick;
225 nick = strtoken(&p, NULL, ","))
226 {
227 char *with_prefix;
228 /*
229 * channels are privmsg'd a lot more than other clients, moved up
230 * here plain old channel msg?
231 */
232
233 if (IsChanPrefix(*nick))
234 {
235 if ((chptr = hash_find_channel(nick)) != NULL)
236 {
237 if (!duplicate_ptr(chptr))
238 {
239 if (ntargets >= ConfigFileEntry.max_targets)
240 {
241 sendto_one(source_p, form_str(ERR_TOOMANYTARGETS),
242 ID_or_name(&me, client_p),
243 ID_or_name(source_p, client_p), nick,
244 ConfigFileEntry.max_targets);
245 return (1);
246 }
247 targets[ntargets].ptr = (void *)chptr;
248 targets[ntargets++].type = ENTITY_CHANNEL;
249 }
250 }
251 else
252 {
253 if (p_or_n != NOTICE)
254 sendto_one(source_p, form_str(ERR_NOSUCHNICK),
255 ID_or_name(&me, client_p),
256 ID_or_name(source_p, client_p), nick);
257 }
258 continue;
259 }
260
261 /* look for a privmsg to another client */
262 if ((target_p = find_person(client_p, nick)) != NULL)
263 {
264 if (!duplicate_ptr(target_p))
265 {
266 if (ntargets >= ConfigFileEntry.max_targets)
267 {
268 sendto_one(source_p, form_str(ERR_TOOMANYTARGETS),
269 ID_or_name(&me, client_p),
270 ID_or_name(source_p, client_p), nick,
271 ConfigFileEntry.max_targets);
272 return (1);
273 }
274 targets[ntargets].ptr = (void *)target_p;
275 targets[ntargets].type = ENTITY_CLIENT;
276 targets[ntargets++].flags = 0;
277 }
278 continue;
279 }
280
281 /* @#channel or +#channel message ? */
282
283 type = 0;
284 with_prefix = nick;
285 /* allow %+@ if someone wants to do that */
286 for (; ;)
287 {
288 if (*nick == '@')
289 type |= CHFL_CHANOP;
290 #ifdef HALFOPS
291 else if (*nick == '%')
292 type |= CHFL_CHANOP | CHFL_HALFOP;
293 #endif
294 else if (*nick == '+')
295 type |= CHFL_CHANOP | CHFL_HALFOP | CHFL_VOICE;
296 else
297 break;
298 nick++;
299 }
300
301 if (type != 0)
302 {
303 /* suggested by Mortiis */
304 if (*nick == '\0') /* if its a '\0' dump it, there is no recipient */
305 {
306 sendto_one(source_p, form_str(ERR_NORECIPIENT),
307 ID_or_name(&me, client_p),
308 ID_or_name(source_p, client_p), command);
309 continue;
310 }
311
312 /* At this point, nick+1 should be a channel name i.e. #foo or &foo
313 * if the channel is found, fine, if not report an error
314 */
315
316 if ((chptr = hash_find_channel(nick)) != NULL)
317 {
318 if (IsClient(source_p) && !HasFlag(source_p, FLAGS_SERVICE))
319 {
320 if (!has_member_flags(find_channel_link(source_p, chptr),
321 CHFL_CHANOP|CHFL_HALFOP|CHFL_VOICE))
322 {
323 sendto_one(source_p, form_str(ERR_CHANOPRIVSNEEDED),
324 ID_or_name(&me, client_p),
325 ID_or_name(source_p, client_p), with_prefix);
326 return(-1);
327 }
328 }
329
330 if (!duplicate_ptr(chptr))
331 {
332 if (ntargets >= ConfigFileEntry.max_targets)
333 {
334 sendto_one(source_p, form_str(ERR_TOOMANYTARGETS),
335 ID_or_name(&me, client_p),
336 ID_or_name(source_p, client_p), nick,
337 ConfigFileEntry.max_targets);
338 return(1);
339 }
340 targets[ntargets].ptr = (void *)chptr;
341 targets[ntargets].type = ENTITY_CHANOPS_ON_CHANNEL;
342 targets[ntargets++].flags = type;
343 }
344 }
345 else
346 {
347 if (p_or_n != NOTICE)
348 sendto_one(source_p, form_str(ERR_NOSUCHNICK),
349 ID_or_name(&me, client_p),
350 ID_or_name(source_p, client_p), nick);
351 }
352 continue;
353 }
354
355 if ((*nick == '$') || strchr(nick, '@') != NULL)
356 {
357 handle_special(p_or_n, command, client_p, source_p, nick, text);
358 }
359 else
360 {
361 if (p_or_n != NOTICE)
362 {
363 if (!IsDigit(*nick) || MyClient(source_p))
364 sendto_one(source_p, form_str(ERR_NOSUCHNICK),
365 ID_or_name(&me, client_p),
366 ID_or_name(source_p, client_p), nick);
367 }
368 }
369 /* continue; */
370 }
371
372 return(1);
373 }
374
375 /* duplicate_ptr()
376 *
377 * inputs - pointer to check
378 * - pointer to table of entities
379 * - number of valid entities so far
380 * output - YES if duplicate pointer in table, NO if not.
381 * note, this does the canonize using pointers
382 * side effects - NONE
383 */
384 static int
385 duplicate_ptr(void *ptr)
386 {
387 int i;
388
389 for (i = 0; i < ntargets; i++)
390 {
391 if (targets[i].ptr == ptr)
392 return(1);
393 }
394
395 return(0);
396 }
397
398 /* msg_channel()
399 *
400 * inputs - flag privmsg or notice
401 * - pointer to command "PRIVMSG" or "NOTICE"
402 * - pointer to client_p
403 * - pointer to source_p
404 * - pointer to channel
405 * output - NONE
406 * side effects - message given channel
407 */
408 static void
409 msg_channel(int p_or_n, const char *command, struct Client *client_p,
410 struct Client *source_p, struct Channel *chptr, char *text)
411 {
412 int result;
413
414 if (MyClient(source_p))
415 {
416 /* idle time shouldnt be reset by notices --fl */
417 if (p_or_n != NOTICE)
418 source_p->localClient->last_privmsg = CurrentTime;
419 }
420
421 /* chanops and voiced can flood their own channel with impunity */
422 if ((result = can_send(chptr, source_p, NULL)) < 0)
423 {
424 if (result == CAN_SEND_OPV ||
425 !flood_attack_channel(p_or_n, source_p, chptr))
426 sendto_channel_butone(client_p, source_p, chptr, 0, "%s %s :%s",
427 command, chptr->chname, text);
428 }
429 else
430 {
431 if (p_or_n != NOTICE)
432 sendto_one(source_p, form_str(ERR_CANNOTSENDTOCHAN),
433 ID_or_name(&me, client_p),
434 ID_or_name(source_p, client_p), chptr->chname);
435 }
436 }
437
438 /* msg_channel_flags()
439 *
440 * inputs - flag 0 if PRIVMSG 1 if NOTICE. RFC
441 * say NOTICE must not auto reply
442 * - pointer to command, "PRIVMSG" or "NOTICE"
443 * - pointer to client_p
444 * - pointer to source_p
445 * - pointer to channel
446 * - flags
447 * - pointer to text to send
448 * output - NONE
449 * side effects - message given channel either chanop or voice
450 */
451 static void
452 msg_channel_flags(int p_or_n, const char *command, struct Client *client_p,
453 struct Client *source_p, struct Channel *chptr,
454 int flags, char *text)
455 {
456 unsigned int type;
457 char c;
458
459 if (flags & CHFL_VOICE)
460 {
461 type = CHFL_VOICE|CHFL_HALFOP|CHFL_CHANOP;
462 c = '+';
463 }
464 #ifdef HALFOPS
465 else if (flags & CHFL_HALFOP)
466 {
467 type = CHFL_HALFOP|CHFL_CHANOP;
468 c = '%';
469 }
470 #endif
471 else
472 {
473 type = CHFL_CHANOP;
474 c = '@';
475 }
476
477 if (MyClient(source_p) && p_or_n != NOTICE)
478 source_p->localClient->last_privmsg = CurrentTime;
479
480 sendto_channel_butone(client_p, source_p, chptr, type, "%s %c%s :%s",
481 command, c, chptr->chname, text);
482 }
483
484 /* msg_client()
485 *
486 * inputs - flag 0 if PRIVMSG 1 if NOTICE. RFC
487 * say NOTICE must not auto reply
488 * - pointer to command, "PRIVMSG" or "NOTICE"
489 * - pointer to source_p source (struct Client *)
490 * - pointer to target_p target (struct Client *)
491 * - pointer to text
492 * output - NONE
493 * side effects - message given channel either chanop or voice
494 */
495 static void
496 msg_client(int p_or_n, const char *command, struct Client *source_p,
497 struct Client *target_p, char *text)
498 {
499 if (MyConnect(source_p))
500 {
501 /*
502 * reset idle time for message only if it's not a notice
503 */
504 if ((p_or_n != NOTICE))
505 source_p->localClient->last_privmsg = CurrentTime;
506
507 if ((p_or_n != NOTICE) && target_p->away[0])
508 sendto_one(source_p, form_str(RPL_AWAY), me.name,
509 source_p->name, target_p->name, target_p->away);
510
511 if (HasUMode(target_p, UMODE_REGONLY) && target_p != source_p)
512 {
513 if (!HasUMode(source_p, UMODE_REGISTERED|UMODE_OPER))
514 {
515 if (p_or_n != NOTICE)
516 sendto_one(source_p, form_str(ERR_NONONREG), me.name, source_p->name,
517 target_p->name);
518 return;
519 }
520 }
521 }
522
523 if (MyClient(target_p))
524 {
525 if (!IsServer(source_p) && HasUMode(target_p, UMODE_CALLERID|UMODE_SOFTCALLERID))
526 {
527 /* Here is the anti-flood bot/spambot code -db */
528 if (accept_message(source_p, target_p) || HasFlag(source_p, FLAGS_SERVICE) ||
529 (HasUMode(source_p, UMODE_OPER) && (ConfigFileEntry.opers_bypass_callerid == 1)))
530 {
531 sendto_one(target_p, ":%s!%s@%s %s %s :%s",
532 source_p->name, source_p->username,
533 source_p->host, command, target_p->name, text);
534 }
535 else
536 {
537 /* check for accept, flag recipient incoming message */
538 if (p_or_n != NOTICE)
539 sendto_one(source_p, form_str(RPL_TARGUMODEG),
540 ID_or_name(&me, source_p->from),
541 ID_or_name(source_p, source_p->from), target_p->name);
542
543 if ((target_p->localClient->last_caller_id_time +
544 ConfigFileEntry.caller_id_wait) < CurrentTime)
545 {
546 if (p_or_n != NOTICE)
547 sendto_one(source_p, form_str(RPL_TARGNOTIFY),
548 ID_or_name(&me, source_p->from),
549 ID_or_name(source_p, source_p->from), target_p->name);
550
551 sendto_one(target_p, form_str(RPL_UMODEGMSG),
552 me.name, target_p->name,
553 get_client_name(source_p, HIDE_IP));
554
555 target_p->localClient->last_caller_id_time = CurrentTime;
556
557 }
558 /* Only so opers can watch for floods */
559 flood_attack_client(p_or_n, source_p, target_p);
560 }
561 }
562 else
563 {
564 /* If the client is remote, we dont perform a special check for
565 * flooding.. as we wouldnt block their message anyway.. this means
566 * we dont give warnings.. we then check if theyre opered
567 * (to avoid flood warnings), lastly if theyre our client
568 * and flooding -- fl */
569 if (!MyClient(source_p) || HasUMode(source_p, UMODE_OPER) ||
570 (MyClient(source_p) &&
571 !flood_attack_client(p_or_n, source_p, target_p)))
572 sendto_anywhere(target_p, source_p, "%s %s :%s",
573 command, target_p->name, text);
574 }
575 }
576 else
577 /* The target is a remote user.. same things apply -- fl */
578 if (!MyClient(source_p) || HasUMode(source_p, UMODE_OPER) ||
579 (MyClient(source_p)
580 && !flood_attack_client(p_or_n, source_p, target_p)))
581 sendto_anywhere(target_p, source_p, "%s %s :%s", command, target_p->name,
582 text);
583 }
584
585 /* flood_attack_client()
586 *
587 * inputs - flag 0 if PRIVMSG 1 if NOTICE. RFC
588 * say NOTICE must not auto reply
589 * - pointer to source Client
590 * - pointer to target Client
591 * output - 1 if target is under flood attack
592 * side effects - check for flood attack on target target_p
593 */
594 static int
595 flood_attack_client(int p_or_n, struct Client *source_p,
596 struct Client *target_p)
597 {
598 int delta;
599
600 if (GlobalSetOptions.floodcount && MyConnect(target_p)
601 && IsClient(source_p) && !IsCanFlood(source_p))
602 {
603 if ((target_p->localClient->first_received_message_time + 1)
604 < CurrentTime)
605 {
606 delta =
607 CurrentTime - target_p->localClient->first_received_message_time;
608 target_p->localClient->received_number_of_privmsgs -= delta;
609 target_p->localClient->first_received_message_time = CurrentTime;
610
611 if (target_p->localClient->received_number_of_privmsgs <= 0)
612 {
613 target_p->localClient->received_number_of_privmsgs = 0;
614 DelFlag(target_p, FLAGS_FLOOD_NOTICED);
615 }
616 }
617
618 if ((target_p->localClient->received_number_of_privmsgs >=
619 GlobalSetOptions.floodcount) || HasFlag(target_p, FLAGS_FLOOD_NOTICED))
620 {
621 if (!HasFlag(target_p, FLAGS_FLOOD_NOTICED))
622 {
623 sendto_realops_flags(UMODE_BOTS, L_ALL, SEND_NOTICE,
624 "Possible Flooder %s on %s target: %s",
625 get_client_name(source_p, HIDE_IP),
626 source_p->servptr->name, target_p->name);
627 AddFlag(target_p, FLAGS_FLOOD_NOTICED);
628 /* add a bit of penalty */
629 target_p->localClient->received_number_of_privmsgs += 2;
630 }
631
632 if (MyClient(source_p) && (p_or_n != NOTICE))
633 sendto_one(source_p,
634 ":%s NOTICE %s :*** Message to %s throttled due to flooding",
635 me.name, source_p->name, target_p->name);
636 return(1);
637 }
638 else
639 target_p->localClient->received_number_of_privmsgs++;
640 }
641
642 return(0);
643 }
644
645 /* flood_attack_channel()
646 *
647 * inputs - flag 0 if PRIVMSG 1 if NOTICE. RFC
648 * says NOTICE must not auto reply
649 * - pointer to source Client
650 * - pointer to target channel
651 * output - 1 if target is under flood attack
652 * side effects - check for flood attack on target chptr
653 */
654 static int
655 flood_attack_channel(int p_or_n, struct Client *source_p,
656 struct Channel *chptr)
657 {
658 int delta;
659
660 if (GlobalSetOptions.floodcount && !IsCanFlood(source_p))
661 {
662 if ((chptr->first_received_message_time + 1) < CurrentTime)
663 {
664 delta = CurrentTime - chptr->first_received_message_time;
665 chptr->received_number_of_privmsgs -= delta;
666 chptr->first_received_message_time = CurrentTime;
667 if (chptr->received_number_of_privmsgs <= 0)
668 {
669 chptr->received_number_of_privmsgs = 0;
670 ClearFloodNoticed(chptr);
671 }
672 }
673
674 if ((chptr->received_number_of_privmsgs >= GlobalSetOptions.floodcount)
675 || IsSetFloodNoticed(chptr))
676 {
677 if (!IsSetFloodNoticed(chptr))
678 {
679 sendto_realops_flags(UMODE_BOTS, L_ALL, SEND_NOTICE,
680 "Possible Flooder %s on %s target: %s",
681 get_client_name(source_p, HIDE_IP),
682 source_p->servptr->name, chptr->chname);
683 SetFloodNoticed(chptr);
684
685 /* Add a bit of penalty */
686 chptr->received_number_of_privmsgs += 2;
687 }
688 if (MyClient(source_p) && (p_or_n != NOTICE))
689 sendto_one(source_p,
690 ":%s NOTICE %s :*** Message to %s throttled due to flooding",
691 me.name, source_p->name, chptr->chname);
692 return(1);
693 }
694 else
695 chptr->received_number_of_privmsgs++;
696 }
697
698 return(0);
699 }
700
701 /* handle_special()
702 *
703 * inputs - server pointer
704 * - client pointer
705 * - nick stuff to grok for opers
706 * - text to send if grok
707 * output - none
708 * side effects - old style username@server is handled here for non opers
709 * opers are allowed username%hostname@server
710 * all the traditional oper type messages are also parsed here.
711 * i.e. "/msg #some.host."
712 * However, syntax has been changed.
713 * previous syntax "/msg #some.host.mask"
714 * now becomes "/msg $#some.host.mask"
715 * previous syntax of: "/msg $some.server.mask" remains
716 * This disambiguates the syntax.
717 *
718 * XXX N.B. dalnet changed it to nick@server as have other servers.
719 * we will stick with tradition for now.
720 * - Dianora
721 */
722 static void
723 handle_special(int p_or_n, const char *command, struct Client *client_p,
724 struct Client *source_p, char *nick, char *text)
725 {
726 struct Client *target_p;
727 char *host;
728 char *server;
729 char *s;
730 int count;
731
732 /*
733 * user[%host]@server addressed?
734 */
735 if ((server = strchr(nick, '@')) != NULL)
736 {
737 count = 0;
738
739 if ((host = strchr(nick, '%')) && !HasUMode(source_p, UMODE_OPER))
740 {
741 sendto_one(source_p, form_str(ERR_NOPRIVILEGES),
742 ID_or_name(&me, client_p),
743 ID_or_name(source_p, client_p));
744 return;
745 }
746
747 if ((target_p = hash_find_server(server + 1)) != NULL)
748 {
749 if (!IsMe(target_p))
750 {
751 /*
752 * Not destined for a user on me :-(
753 */
754 sendto_one(target_p, ":%s %s %s :%s",
755 ID_or_name(source_p, target_p->from),
756 command, nick, text);
757 if ((p_or_n != NOTICE) && MyClient(source_p))
758 source_p->localClient->last_privmsg = CurrentTime;
759 return;
760 }
761
762 *server = '\0';
763
764 if (host != NULL)
765 *host++ = '\0';
766
767 /*
768 * Look for users which match the destination host
769 * (no host == wildcard) and if one and one only is
770 * found connected to me, deliver message!
771 */
772 target_p = find_userhost(nick, host, &count);
773
774 if (target_p != NULL)
775 {
776 if (server != NULL)
777 *server = '@';
778 if (host != NULL)
779 *--host = '%';
780
781 if (count == 1)
782 {
783 sendto_one(target_p, ":%s!%s@%s %s %s :%s",
784 source_p->name, source_p->username, source_p->host,
785 command, nick, text);
786 if ((p_or_n != NOTICE) && MyClient(source_p))
787 source_p->localClient->last_privmsg = CurrentTime;
788 }
789 else
790 sendto_one(source_p, form_str(ERR_TOOMANYTARGETS),
791 ID_or_name(&me, client_p),
792 ID_or_name(source_p, client_p), nick,
793 ConfigFileEntry.max_targets);
794 }
795 }
796 else if (server && *(server+1) && (target_p == NULL))
797 sendto_one(source_p, form_str(ERR_NOSUCHSERVER),
798 ID_or_name(&me, client_p),
799 ID_or_name(source_p, client_p), server+1);
800 else if (server && (target_p == NULL))
801 sendto_one(source_p, form_str(ERR_NOSUCHNICK),
802 ID_or_name(&me, client_p),
803 ID_or_name(source_p, client_p), nick);
804 return;
805 }
806
807 if (!HasUMode(source_p, UMODE_OPER))
808 {
809 sendto_one(source_p, form_str(ERR_NOPRIVILEGES),
810 ID_or_name(&me, client_p),
811 ID_or_name(source_p, client_p));
812 return;
813 }
814
815 /*
816 * the following two cases allow masks in NOTICEs
817 * (for OPERs only)
818 *
819 * Armin, 8Jun90 (gruner@informatik.tu-muenchen.de)
820 */
821 if (*nick == '$')
822 {
823 if ((*(nick+1) == '$' || *(nick+1) == '#'))
824 nick++;
825 else if (MyClient(source_p) && HasUMode(source_p, UMODE_OPER))
826 {
827 sendto_one(source_p,
828 ":%s NOTICE %s :The command %s %s is no longer supported, please use $%s",
829 me.name, source_p->name, command, nick, nick);
830 return;
831 }
832
833 if ((s = strrchr(nick, '.')) == NULL)
834 {
835 sendto_one(source_p, form_str(ERR_NOTOPLEVEL),
836 me.name, source_p->name, nick);
837 return;
838 }
839
840 while (*++s)
841 if (*s == '.' || *s == '*' || *s == '?')
842 break;
843
844 if (*s == '*' || *s == '?')
845 {
846 sendto_one(source_p, form_str(ERR_WILDTOPLEVEL),
847 ID_or_name(&me, client_p),
848 ID_or_name(source_p, client_p), nick);
849 return;
850 }
851
852 sendto_match_butone(IsServer(client_p) ? client_p : NULL, source_p,
853 nick + 1, (*nick == '#') ? MATCH_HOST : MATCH_SERVER,
854 "%s $%s :%s", command, nick, text);
855
856 if ((p_or_n != NOTICE) && MyClient(source_p))
857 source_p->localClient->last_privmsg = CurrentTime;
858
859 return;
860 }
861 }
862
863 /*
864 * find_userhost - find a user@host (server or user).
865 * inputs - user name to look for
866 * - host name to look for
867 * - pointer to count of number of matches found
868 * outputs - pointer to client if found
869 * - count is updated
870 * side effects - none
871 *
872 */
873 static struct Client *
874 find_userhost(char *user, char *host, int *count)
875 {
876 struct Client *c2ptr;
877 struct Client *res = NULL;
878 dlink_node *lc2ptr;
879
880 *count = 0;
881
882 if (collapse(user) != NULL)
883 {
884 DLINK_FOREACH(lc2ptr, local_client_list.head)
885 {
886 c2ptr = lc2ptr->data;
887
888 if (!IsClient(c2ptr)) /* something other than a client */
889 continue;
890
891 if ((!host || !match(host, c2ptr->host)) &&
892 irccmp(user, c2ptr->username) == 0)
893 {
894 (*count)++;
895 res = c2ptr;
896 }
897 }
898 }
899
900 return(res);
901 }
902
903 static struct Message privmsg_msgtab = {
904 "PRIVMSG", 0, 0, 0, MAXPARA, MFLG_SLOW, 0,
905 {m_unregistered, m_privmsg, m_privmsg, m_ignore, m_privmsg, m_ignore}
906 };
907
908 static struct Message notice_msgtab = {
909 "NOTICE", 0, 0, 0, MAXPARA, MFLG_SLOW, 0,
910 {m_unregistered, m_notice, m_notice, m_ignore, m_notice, m_ignore}
911 };
912
913 static void
914 module_init(void)
915 {
916 mod_add_cmd(&privmsg_msgtab);
917 mod_add_cmd(&notice_msgtab);
918 }
919
920 static void
921 module_exit(void)
922 {
923 mod_del_cmd(&privmsg_msgtab);
924 mod_del_cmd(&notice_msgtab);
925 }
926
927 struct module module_entry = {
928 .node = { NULL, NULL, NULL },
929 .name = NULL,
930 .version = "$Revision$",
931 .handle = NULL,
932 .modinit = module_init,
933 .modexit = module_exit,
934 .flags = MODULE_FLAG_CORE
935 };

Properties

Name Value
svn:eol-style native
svn:keywords Id Revision