ViewVC Help
View File | Revision Log | Show Annotations | View Changeset | Root Listing
root/svn/ircd-hybrid/trunk/src/send.c
Revision: 9812
Committed: Thu Dec 10 22:31:35 2020 UTC (4 years, 8 months ago) by michael
Content type: text/x-csrc
File size: 23004 byte(s)
Log Message:
- send.c:sendto_anywhere(): use a separate buffer for sending the echo message
  as we always need to have the sender in the n!u@h format here

File Contents

# Content
1 /*
2 * ircd-hybrid: an advanced, lightweight Internet Relay Chat Daemon (ircd)
3 *
4 * Copyright (c) 1997-2020 ircd-hybrid development team
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
19 * USA
20 */
21
22 /*! \file send.c
23 * \brief Functions for sending messages.
24 * \version $Id$
25 */
26
27 #include "stdinc.h"
28 #include "list.h"
29 #include "send.h"
30 #include "channel.h"
31 #include "client.h"
32 #include "dbuf.h"
33 #include "irc_string.h"
34 #include "ircd.h"
35 #include "s_bsd.h"
36 #include "server_capab.h"
37 #include "conf_class.h"
38 #include "log.h"
39
40
41 static uintmax_t current_serial;
42
43
44 /* send_format()
45 *
46 * inputs
47 * - buffer
48 * - format pattern to use
49 * - var args
50 * output - number of bytes formatted output
51 * side effects - modifies sendbuf
52 */
53 static void
54 send_format(struct dbuf_block *buffer, const char *pattern, va_list args)
55 {
56 /*
57 * from rfc1459
58 *
59 * IRC messages are always lines of characters terminated with a CR-LF
60 * (Carriage Return - Line Feed) pair, and these messages shall not
61 * exceed 512 characters in length, counting all characters
62 * including the trailing CR-LF.
63 * Thus, there are 510 characters maximum allowed
64 * for the command and its parameters. There is no provision for
65 * continuation message lines. See section 7 for more details about
66 * current implementations.
67 */
68 dbuf_put_args(buffer, pattern, args);
69
70 if (buffer->size > IRCD_BUFSIZE - 2)
71 buffer->size = IRCD_BUFSIZE - 2;
72
73 buffer->data[buffer->size++] = '\r';
74 buffer->data[buffer->size++] = '\n';
75 }
76
77 /*
78 ** send_message
79 ** Internal utility which appends given buffer to the sockets
80 ** sendq.
81 */
82 static void
83 send_message(struct Client *to, struct dbuf_block *buffer)
84 {
85 assert(!IsMe(to));
86 assert(to != &me);
87 assert(MyConnect(to));
88
89 if (dbuf_length(&to->connection->buf_sendq) + buffer->size > get_sendq(&to->connection->confs))
90 {
91 if (IsServer(to))
92 sendto_realops_flags(UMODE_SERVNOTICE, L_ALL, SEND_NOTICE,
93 "Max SendQ limit exceeded for %s: %zu > %u",
94 client_get_name(to, HIDE_IP),
95 (dbuf_length(&to->connection->buf_sendq) + buffer->size),
96 get_sendq(&to->connection->confs));
97
98 if (IsClient(to))
99 AddFlag(to, FLAGS_SENDQEX);
100
101 dead_link_on_write(to, 0);
102 return;
103 }
104
105 dbuf_add(&to->connection->buf_sendq, buffer);
106
107 /*
108 * Update statistics. The following is slightly incorrect because
109 * it counts messages even if queued, but bytes only really sent.
110 * Queued bytes get updated in send_queued_write().
111 */
112 ++to->connection->send.messages;
113 ++me.connection->send.messages;
114
115 send_queued_write(to);
116 }
117
118 /* send_message_remote()
119 *
120 * inputs - pointer to client from message is being sent
121 * - pointer to client to send to
122 * - pointer to buffer
123 * output - none
124 * side effects - Despite the function name, this only sends to directly
125 * connected clients.
126 *
127 */
128 static void
129 send_message_remote(struct Client *to, const struct Client *from, struct dbuf_block *buffer)
130 {
131 assert(MyConnect(to));
132 assert(IsServer(to));
133 assert(!IsMe(to));
134 assert(to->from == to);
135
136 if (to == from->from)
137 sendto_realops_flags(UMODE_SERVNOTICE, L_ALL, SEND_NOTICE, "Send message to %s dropped from %s (Fake Dir)",
138 to->name, from->name);
139 else
140 send_message(to, buffer);
141 }
142
143 /*
144 ** sendq_unblocked
145 ** Called when a socket is ready for writing.
146 */
147 void
148 sendq_unblocked(fde_t *F, void *data)
149 {
150 struct Client *const client = data;
151
152 assert(client);
153 assert(client->connection);
154 assert(client->connection->fd);
155 assert(client->connection->fd == F);
156
157 DelFlag(client, FLAGS_BLOCKED);
158 send_queued_write(client);
159 }
160
161 /*
162 ** send_queued_write
163 ** This is called when there is a chance that some output would
164 ** be possible. This attempts to empty the send queue as far as
165 ** possible, and then if any data is left, a write is rescheduled.
166 */
167 void
168 send_queued_write(struct Client *to)
169 {
170 ssize_t retlen;
171
172 /*
173 * Once socket is marked dead, we cannot start writing to it,
174 * even if the error is removed...
175 */
176 if (IsDead(to) || HasFlag(to, FLAGS_BLOCKED))
177 return; /* no use calling send() now */
178
179 /* Next, lets try to write some data */
180 while (dbuf_length(&to->connection->buf_sendq))
181 {
182 bool want_read = false;
183 const struct dbuf_block *first = to->connection->buf_sendq.blocks.head->data;
184
185 if (tls_isusing(&to->connection->fd->tls))
186 {
187 retlen = tls_write(&to->connection->fd->tls, first->data + to->connection->buf_sendq.pos,
188 first->size - to->connection->buf_sendq.pos, &want_read);
189
190 if (want_read == true)
191 return; /* Retry later, don't register for write events */
192 }
193 else
194 retlen = send(to->connection->fd->fd, first->data + to->connection->buf_sendq.pos,
195 first->size - to->connection->buf_sendq.pos, 0);
196
197 if (retlen <= 0)
198 {
199 if (retlen < 0 && comm_ignore_errno(errno) == true)
200 {
201 AddFlag(to, FLAGS_BLOCKED);
202 /* We have a non-fatal error, reschedule a write */
203 comm_setselect(to->connection->fd, COMM_SELECT_WRITE, sendq_unblocked, to, 0);
204 }
205 else
206 dead_link_on_write(to, errno);
207 return;
208 }
209
210 dbuf_delete(&to->connection->buf_sendq, retlen);
211
212 /* We have some data written .. update counters */
213 to->connection->send.bytes += retlen;
214 me.connection->send.bytes += retlen;
215 }
216 }
217
218 /* sendto_one()
219 *
220 * inputs - pointer to destination client
221 * - var args message
222 * output - NONE
223 * side effects - send message to single client
224 */
225 void
226 sendto_one(struct Client *to, const char *pattern, ...)
227 {
228 va_list args;
229
230 if (IsDead(to->from))
231 return; /* This socket has already been marked as dead */
232
233 va_start(args, pattern);
234
235 struct dbuf_block *buffer = dbuf_alloc();
236 send_format(buffer, pattern, args);
237
238 va_end(args);
239
240 send_message(to->from, buffer);
241
242 dbuf_ref_free(buffer);
243 }
244
245 void
246 sendto_one_numeric(struct Client *to, const struct Client *from, enum irc_numerics numeric, ...)
247 {
248 va_list args;
249
250 if (IsDead(to->from))
251 return; /* This socket has already been marked as dead */
252
253 const char *dest = ID_or_name(to, to);
254 if (EmptyString(dest))
255 dest = "*";
256
257 struct dbuf_block *buffer = dbuf_alloc();
258 dbuf_put_fmt(buffer, ":%s %03d %s ", ID_or_name(from, to), numeric & ~SND_EXPLICIT, dest);
259
260 va_start(args, numeric);
261
262 const char *numstr;
263 if (numeric & SND_EXPLICIT)
264 numstr = va_arg(args, const char *);
265 else
266 numstr = numeric_form(numeric);
267
268 send_format(buffer, numstr, args);
269 va_end(args);
270
271 send_message(to->from, buffer);
272
273 dbuf_ref_free(buffer);
274 }
275
276 void
277 sendto_one_notice(struct Client *to, const struct Client *from, const char *pattern, ...)
278 {
279 va_list args;
280
281 if (IsDead(to->from))
282 return; /* This socket has already been marked as dead */
283
284 const char *dest = ID_or_name(to, to);
285 if (EmptyString(dest))
286 dest = "*";
287
288 struct dbuf_block *buffer = dbuf_alloc();
289 dbuf_put_fmt(buffer, ":%s NOTICE %s ", ID_or_name(from, to), dest);
290
291 va_start(args, pattern);
292 send_format(buffer, pattern, args);
293 va_end(args);
294
295 send_message(to->from, buffer);
296
297 dbuf_ref_free(buffer);
298 }
299
300 /* sendto_channel_butone()
301 *
302 * inputs - pointer to client(server) to NOT send message to
303 * - pointer to client that is sending this message
304 * - pointer to channel being sent to
305 * - vargs message
306 * output - NONE
307 * side effects - message as given is sent to given channel members.
308 *
309 * WARNING - +D clients are ignored
310 */
311 void
312 sendto_channel_butone(struct Client *one, struct Client *from,
313 struct Channel *channel, unsigned int type,
314 const char *pattern, ...)
315 {
316 va_list args_l, args_r;
317 dlink_node *node;
318 struct dbuf_block *buffer_l = dbuf_alloc();
319 struct dbuf_block *buffer_r = dbuf_alloc();
320
321 if (IsClient(from))
322 dbuf_put_fmt(buffer_l, ":%s!%s@%s ", from->name, from->username, from->host);
323 else
324 dbuf_put_fmt(buffer_l, ":%s ", from->name);
325
326 dbuf_put_fmt(buffer_r, ":%s ", from->id);
327
328 va_start(args_l, pattern);
329 va_start(args_r, pattern);
330 send_format(buffer_l, pattern, args_l);
331 send_format(buffer_r, pattern, args_r);
332 va_end(args_l);
333 va_end(args_r);
334
335 ++current_serial;
336
337 DLINK_FOREACH(node, channel->members.head)
338 {
339 struct ChannelMember *member = node->data;
340 struct Client *target = member->client;
341
342 assert(IsClient(target));
343
344 if (IsDead(target->from))
345 continue;
346
347 if (one && (target->from == one->from))
348 continue;
349
350 if (type && (member->flags & type) == 0)
351 continue;
352
353 if (HasUMode(target, UMODE_DEAF))
354 continue;
355
356 if (MyConnect(target))
357 send_message(target, buffer_l);
358 else if (target->from->connection->serial != current_serial)
359 send_message_remote(target->from, from, buffer_r);
360
361 target->from->connection->serial = current_serial;
362 }
363
364 if (MyClient(from) && HasCap(from, CAP_ECHO_MESSAGE))
365 send_message(from, buffer_l);
366
367 dbuf_ref_free(buffer_l);
368 dbuf_ref_free(buffer_r);
369 }
370
371 /* sendto_server()
372 *
373 * inputs - pointer to client to NOT send to
374 * - pointer to channel
375 * - capabs or'd together which must ALL be present
376 * - capabs or'd together which must ALL NOT be present
377 * - printf style format string
378 * - args to format string
379 * output - NONE
380 * side effects - Send a message to all connected servers, except the
381 * client 'one' (if non-NULL), as long as the servers
382 * support ALL capabs in 'capab', and NO capabs in 'nocapab'.
383 *
384 * This function was written in an attempt to merge together the other
385 * billion sendto_*serv*() functions, which sprung up with capabs,
386 * lazylinks, uids, etc.
387 * -davidt
388 */
389 void
390 sendto_server(const struct Client *one,
391 const unsigned int capab,
392 const unsigned int nocapab,
393 const char *format, ...)
394 {
395 va_list args;
396 dlink_node *node;
397 struct dbuf_block *buffer = dbuf_alloc();
398
399 va_start(args, format);
400 send_format(buffer, format, args);
401 va_end(args);
402
403 DLINK_FOREACH(node, local_server_list.head)
404 {
405 struct Client *client = node->data;
406
407 /* If dead already skip */
408 if (IsDead(client))
409 continue;
410
411 /* check against 'one' */
412 if (one && (client == one->from))
413 continue;
414
415 /* check we have required capabs */
416 if (IsCapable(client, capab) != capab)
417 continue;
418
419 /* check we don't have any forbidden capabs */
420 if (IsCapable(client, nocapab))
421 continue;
422
423 send_message(client, buffer);
424 }
425
426 dbuf_ref_free(buffer);
427 }
428
429 /* sendto_common_channels_local()
430 *
431 * inputs - pointer to client
432 * - pattern to send
433 * output - NONE
434 * side effects - Sends a message to all people on local server who are
435 * in same channel with user.
436 * used by m_nick.c and exit_one_client.
437 */
438 void
439 sendto_common_channels_local(struct Client *user, bool touser, unsigned int poscap,
440 unsigned int negcap, const char *pattern, ...)
441 {
442 va_list args;
443 dlink_node *node, *node2;
444 struct dbuf_block *buffer = dbuf_alloc();
445
446 va_start(args, pattern);
447 send_format(buffer, pattern, args);
448 va_end(args);
449
450 ++current_serial;
451
452 DLINK_FOREACH(node, user->channel.head)
453 {
454 struct ChannelMember *member = node->data;
455 struct Channel *channel = member->channel;
456
457 DLINK_FOREACH(node2, channel->members_local.head)
458 {
459 struct ChannelMember *member2 = node2->data;
460 struct Client *target = member2->client;
461
462 if (IsDead(target))
463 continue;
464
465 if (target == user)
466 continue;
467
468 if (target->connection->serial == current_serial)
469 continue;
470
471 if (poscap && HasCap(target, poscap) != poscap)
472 continue;
473
474 if (negcap && HasCap(target, negcap))
475 continue;
476
477 target->connection->serial = current_serial;
478 send_message(target, buffer);
479 }
480 }
481
482 if (touser == true && MyConnect(user) && !IsDead(user))
483 if (HasCap(user, poscap) == poscap)
484 send_message(user, buffer);
485
486 dbuf_ref_free(buffer);
487 }
488
489 /*! \brief Send a message to members of a channel that are locally connected to this server.
490 * \param one Client to skip; can be NULL
491 * \param channel Destination channel
492 * \param status Channel member status flags clients must have
493 * \param poscap Positive client capabilities flags (CAP)
494 * \param negcap Negative client capabilities flags (CAP)
495 * \param pattern Format string for command arguments
496 */
497 void
498 sendto_channel_local(const struct Client *one, struct Channel *channel, unsigned int status,
499 unsigned int poscap, unsigned int negcap, const char *pattern, ...)
500 {
501 va_list args;
502 dlink_node *node;
503 struct dbuf_block *buffer = dbuf_alloc();
504
505 va_start(args, pattern);
506 send_format(buffer, pattern, args);
507 va_end(args);
508
509 DLINK_FOREACH(node, channel->members_local.head)
510 {
511 struct ChannelMember *member = node->data;
512 struct Client *target = member->client;
513
514 if (IsDead(target))
515 continue;
516
517 if (one && (target == one->from))
518 continue;
519
520 if (status && (member->flags & status) == 0)
521 continue;
522
523 if (poscap && HasCap(target, poscap) != poscap)
524 continue;
525
526 if (negcap && HasCap(target, negcap))
527 continue;
528
529 send_message(target, buffer);
530 }
531
532 dbuf_ref_free(buffer);
533 }
534
535 /*
536 ** match_it() and sendto_match_butone() ARE only used
537 ** to send a msg to all ppl on servers/hosts that match a specified mask
538 ** (used for enhanced PRIVMSGs) for opers
539 **
540 ** addition -- Armin, 8jun90 (gruner@informatik.tu-muenchen.de)
541 **
542 */
543
544 /* match_it()
545 *
546 * inputs - client pointer to match on
547 * - actual mask to match
548 * - what to match on, HOST or SERVER
549 * output - 1 or 0 if match or not
550 * side effects - NONE
551 */
552 static bool
553 match_it(const struct Client *one, const char *mask, unsigned int what)
554 {
555 if (what == MATCH_HOST)
556 return match(mask, one->host) == 0;
557
558 return match(mask, one->servptr->name) == 0;
559 }
560
561 /* sendto_match_butone()
562 *
563 * Send to all clients which match the mask in a way defined on 'what';
564 * either by user hostname or user servername.
565 *
566 * ugh. ONLY used by m_message.c to send an "oper magic" message. ugh.
567 */
568 void
569 sendto_match_butone(const struct Client *one, const struct Client *from,
570 const char *mask, int what, const char *pattern, ...)
571 {
572 va_list args_l, args_r;
573 dlink_node *node;
574 struct dbuf_block *buffer_l = dbuf_alloc();
575 struct dbuf_block *buffer_r = dbuf_alloc();
576
577 dbuf_put_fmt(buffer_l, ":%s!%s@%s ", from->name, from->username, from->host);
578 dbuf_put_fmt(buffer_r, ":%s ", from->id);
579
580 va_start(args_l, pattern);
581 va_start(args_r, pattern);
582 send_format(buffer_l, pattern, args_l);
583 send_format(buffer_r, pattern, args_r);
584 va_end(args_l);
585 va_end(args_r);
586
587 /* Scan the local clients */
588 DLINK_FOREACH(node, local_client_list.head)
589 {
590 struct Client *client = node->data;
591
592 if (IsDead(client))
593 continue;
594
595 if (one && (client == one->from))
596 continue;
597
598 if (match_it(client, mask, what) == false)
599 continue;
600
601 send_message(client, buffer_l);
602 }
603
604 /* Now scan servers */
605 DLINK_FOREACH(node, local_server_list.head)
606 {
607 struct Client *client = node->data;
608
609 /*
610 * The old code looped through every client on the network for each
611 * server to check if the server (client) has at least 1 client
612 * matching the mask, using something like:
613 *
614 * for (target = GlobalClientList; target; target = target->next)
615 * if (IsRegisteredUser(target) &&
616 * match_it(target, mask, what) &&
617 * (target->from == client))
618 * vsendto_prefix_one(client, from, pattern, args);
619 *
620 * That way, we wouldn't send the message to a server who didn't have
621 * a matching client. However, on a network such as EFNet, that code
622 * would have looped through about 50 servers, and in each loop, loop
623 * through about 50k clients as well, calling match() in each nested
624 * loop. That is a very bad thing cpu wise - just send the message to
625 * every connected server and let that server deal with it.
626 * -wnder
627 */
628 if (IsDead(client))
629 continue;
630
631 if (one && (client == one->from))
632 continue;
633
634 send_message_remote(client, from, buffer_r);
635 }
636
637 dbuf_ref_free(buffer_l);
638 dbuf_ref_free(buffer_r);
639 }
640
641 /* sendto_match_servs()
642 *
643 * inputs - source client
644 * - mask to send to
645 * - capab needed
646 * - data
647 * outputs - none
648 * side effects - data sent to servers matching with capab
649 */
650 void
651 sendto_match_servs(const struct Client *source_p, const char *mask, unsigned int cap,
652 const char *pattern, ...)
653 {
654 va_list args;
655 dlink_node *node;
656 struct dbuf_block *buffer = dbuf_alloc();
657
658 dbuf_put_fmt(buffer, ":%s ", source_p->id);
659
660 va_start(args, pattern);
661 send_format(buffer, pattern, args);
662 va_end(args);
663
664 ++current_serial;
665
666 DLINK_FOREACH(node, global_server_list.head)
667 {
668 struct Client *target = node->data;
669
670 if (IsDead(target->from))
671 continue;
672
673 /* Do not attempt to send to ourselves ... */
674 if (IsMe(target))
675 continue;
676
677 /* ... or the source */
678 if (target->from == source_p->from)
679 continue;
680
681 if (target->from->connection->serial == current_serial)
682 continue;
683
684 if (cap && IsCapable(target->from, cap) != cap)
685 continue;
686
687 if (match(mask, target->name))
688 continue;
689
690 target->from->connection->serial = current_serial;
691 send_message_remote(target->from, source_p, buffer);
692 }
693
694 dbuf_ref_free(buffer);
695 }
696
697 /* sendto_anywhere()
698 *
699 * inputs - pointer to dest client
700 * - pointer to from client
701 * - varags
702 * output - NONE
703 * side effects - less efficient than sendto_remote and sendto_one
704 * but useful when one does not know where target "lives"
705 */
706 void
707 sendto_anywhere(struct Client *to, struct Client *from,
708 const char *command,
709 const char *pattern, ...)
710 {
711 va_list args;
712
713 if (IsDead(to->from))
714 return;
715
716 struct dbuf_block *buffer = dbuf_alloc();
717 if (MyClient(to) && IsClient(from))
718 dbuf_put_fmt(buffer, ":%s!%s@%s %s %s ", from->name, from->username,
719 from->host, command, to->name);
720 else
721 dbuf_put_fmt(buffer, ":%s %s %s ", ID_or_name(from, to),
722 command, ID_or_name(to, to));
723
724 va_start(args, pattern);
725 send_format(buffer, pattern, args);
726 va_end(args);
727
728 if (MyConnect(to))
729 send_message(to, buffer);
730 else
731 send_message_remote(to->from, from, buffer);
732
733 dbuf_ref_free(buffer);
734
735 if (MyClient(from) && HasCap(from, CAP_ECHO_MESSAGE) && from != to)
736 {
737 va_list args_l;
738 struct dbuf_block *buffer_l = dbuf_alloc();
739 dbuf_put_fmt(buffer_l, ":%s!%s@%s %s %s ", from->name, from->username,
740 from->host, command, to->name);
741
742 va_start(args_l, pattern);
743 send_format(buffer, pattern, args_l);
744 va_end(args_l);
745
746 send_message(from, buffer_l);
747 dbuf_ref_free(buffer_l);
748 }
749 }
750
751 /* sendto_realops_flags()
752 *
753 * inputs - flag types of messages to show to real opers
754 * - flag indicating opers/admins
755 * - var args input message
756 * output - NONE
757 * side effects - Send to *local* ops only but NOT +s nonopers.
758 */
759 void
760 sendto_realops_flags(unsigned int flags, int level, int type, const char *pattern, ...)
761 {
762 va_list args;
763 dlink_node *node;
764 const char *ntype = "???";
765
766 switch (type)
767 {
768 case SEND_NOTICE:
769 ntype = "Notice";
770 break;
771 case SEND_GLOBAL:
772 ntype = "Global";
773 break;
774 case SEND_LOCOPS:
775 ntype = "LocOps";
776 break;
777 default:
778 assert(0);
779 }
780
781 struct dbuf_block *buffer = dbuf_alloc();
782 dbuf_put_fmt(buffer, ":%s NOTICE * :*** %s -- ", me.name, ntype);
783
784 va_start(args, pattern);
785 send_format(buffer, pattern, args);
786 va_end(args);
787
788 DLINK_FOREACH(node, oper_list.head)
789 {
790 struct Client *client = node->data;
791 assert(HasUMode(client, UMODE_OPER));
792
793 if (IsDead(client))
794 continue;
795
796 /*
797 * If we're sending it to opers and they're an admin, skip.
798 * If we're sending it to admins, and they're not, skip.
799 */
800 if (((level == L_ADMIN) && !HasUMode(client, UMODE_ADMIN)) ||
801 ((level == L_OPER) && HasUMode(client, UMODE_ADMIN)))
802 continue;
803
804 if (!HasUMode(client, flags))
805 continue;
806
807 send_message(client, buffer);
808 }
809
810 dbuf_ref_free(buffer);
811 }
812
813 /* ts_warn()
814 *
815 * inputs - var args message
816 * output - NONE
817 * side effects - Call sendto_realops_flags, with some flood checking
818 * (at most 5 warnings every 5 seconds)
819 */
820 void
821 sendto_realops_flags_ratelimited(uintmax_t *rate, const char *pattern, ...)
822 {
823 va_list args;
824 char buffer[IRCD_BUFSIZE];
825
826 if ((event_base->time.sec_monotonic - *rate) < 20)
827 return;
828
829 *rate = event_base->time.sec_monotonic;
830
831 va_start(args, pattern);
832 vsnprintf(buffer, sizeof(buffer), pattern, args);
833 va_end(args);
834
835 sendto_realops_flags(UMODE_SERVNOTICE, L_ALL, SEND_NOTICE, "%s", buffer);
836 ilog(LOG_TYPE_IRCD, "%s", buffer);
837 }
838
839 /* sendto_wallops_flags()
840 *
841 * inputs - flag types of messages to show to real opers
842 * - client sending request
843 * - var args input message
844 * output - NONE
845 * side effects - Send a wallops to local opers
846 */
847 void
848 sendto_wallops_flags(unsigned int flags, const struct Client *source_p,
849 const char *pattern, ...)
850 {
851 va_list args;
852 dlink_node *node;
853 struct dbuf_block *buffer = dbuf_alloc();
854
855 if (IsClient(source_p))
856 dbuf_put_fmt(buffer, ":%s!%s@%s WALLOPS :", source_p->name, source_p->username, source_p->host);
857 else
858 dbuf_put_fmt(buffer, ":%s WALLOPS :", source_p->name);
859
860 va_start(args, pattern);
861 send_format(buffer, pattern, args);
862 va_end(args);
863
864 DLINK_FOREACH(node, oper_list.head)
865 {
866 struct Client *client = node->data;
867 assert(client->umodes & UMODE_OPER);
868
869 if (IsDead(client))
870 continue;
871
872 if (!HasUMode(client, flags))
873 continue;
874
875 send_message(client, buffer);
876 }
877
878 dbuf_ref_free(buffer);
879 }

Properties

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