]> jfr.im git - solanum.git/blobdiff - ircd/send.c
global masktrace doesn't need to be an operspy action
[solanum.git] / ircd / send.c
index 8aaba7d730dd680ba8042910a9c27b34166f3f8f..9d678a3d0a6337b508dc5994c1cb3a01ee0dcdde 100644 (file)
@@ -20,8 +20,6 @@
  *  along with this program; if not, write to the Free Software
  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307
  *  USA
- *
- *  $Id: send.c 3520 2007-06-30 22:15:35Z jilles $
  */
 
 #include "stdinc.h"
 #include "channel.h"
 #include "class.h"
 #include "client.h"
-#include "common.h"
 #include "match.h"
 #include "ircd.h"
 #include "numeric.h"
+#include "s_assert.h"
 #include "s_serv.h"
 #include "s_conf.h"
 #include "s_newconf.h"
@@ -44,6 +42,8 @@
 /* send the message to the link the target is attached to */
 #define send_linebuf(a,b) _send_linebuf((a->from ? a->from : a) ,b)
 
+#define CLIENT_CAPS_ONLY(x)    ((IsClient((x)) && (x)->localClient) ? (x)->localClient->caps : 0)
+
 static void send_queued_write(rb_fde_t *F, void *data);
 
 unsigned long current_serial = 0L;
@@ -70,9 +70,11 @@ _send_linebuf(struct Client *to, buf_head_t *linebuf)
 
        if(rb_linebuf_len(&to->localClient->buf_sendq) > get_sendq(to))
        {
+               dead_link(to, 1);
+
                if(IsServer(to))
                {
-                       sendto_realops_snomask(SNO_GENERAL, L_ALL,
+                       sendto_realops_snomask(SNO_GENERAL, L_NETWIDE,
                                             "Max SendQ limit exceeded for %s: %u > %lu",
                                             to->name,
                                             rb_linebuf_len(&to->localClient->buf_sendq),
@@ -84,7 +86,6 @@ _send_linebuf(struct Client *to, buf_head_t *linebuf)
                             get_sendq(to));
                }
 
-               dead_link(to, 1);
                return -1;
        }
        else
@@ -210,28 +211,53 @@ send_queued_write(rb_fde_t *F, void *data)
        send_queued(to);
 }
 
-/* linebuf_put_msgbuf
+/*
+ * linebuf_put_*
  *
  * inputs       - msgbuf header, linebuf object, capability mask, pattern, arguments
  * outputs      - none
- * side effects - the linebuf object is cleared, then populated using rb_linebuf_putmsg().
+ * side effects - the linebuf object is cleared, then populated
  */
 static void
-linebuf_put_msgbuf(struct MsgBuf *msgbuf, buf_head_t *linebuf, unsigned int capmask, const char *pattern, ...)
+linebuf_put_tags(buf_head_t *linebuf, const struct MsgBuf *msgbuf, const struct Client *target_p, rb_strf_t *message)
+{
+       struct MsgBuf_str_data msgbuf_str_data = { .msgbuf = msgbuf, .caps = CLIENT_CAPS_ONLY(target_p) };
+       rb_strf_t strings = { .func = msgbuf_unparse_linebuf_tags, .func_args = &msgbuf_str_data, .length = TAGSLEN + 1, .next = message };
+
+       message->length = DATALEN + 1;
+       rb_linebuf_put(linebuf, &strings);
+}
+
+static void
+linebuf_put_tagsf(buf_head_t *linebuf, const struct MsgBuf *msgbuf, const struct Client *target_p, const rb_strf_t *message, const char *format, ...)
 {
-       char buf[IRCD_BUFSIZE];
        va_list va;
+       rb_strf_t strings = { .format = format, .format_args = &va, .next = message };
 
-       rb_linebuf_newbuf(linebuf);
+       va_start(va, format);
+       linebuf_put_tags(linebuf, msgbuf, target_p, &strings);
+       va_end(va);
+}
 
-       msgbuf_unparse_prefix(buf, sizeof buf, msgbuf, capmask);
+static void
+linebuf_put_msg(buf_head_t *linebuf, rb_strf_t *message)
+{
+       message->length = DATALEN + 1;
+       rb_linebuf_put(linebuf, message);
+}
 
-       va_start(va, pattern);
-       rb_linebuf_putprefix(linebuf, pattern, &va, buf);
+static void
+linebuf_put_msgf(buf_head_t *linebuf, const rb_strf_t *message, const char *format, ...)
+{
+       va_list va;
+       rb_strf_t strings = { .format = format, .format_args = &va, .next = message };
+
+       va_start(va, format);
+       linebuf_put_msg(linebuf, &strings);
        va_end(va);
 }
 
-/* build_msgbuf_from
+/* build_msgbuf_tags
  *
  * inputs       - msgbuf object, client the message is from
  * outputs      - none
@@ -239,20 +265,12 @@ linebuf_put_msgbuf(struct MsgBuf *msgbuf, buf_head_t *linebuf, unsigned int capm
  * notes        - to make this reentrant, find a solution for `buf` below
  */
 static void
-build_msgbuf_from(struct MsgBuf *msgbuf, struct Client *from)
+build_msgbuf_tags(struct MsgBuf *msgbuf, struct Client *from)
 {
-       static char buf[BUFSIZE];
        hook_data hdata;
 
        msgbuf_init(msgbuf);
 
-       msgbuf->origin = buf;
-
-       if (IsPerson(from))
-               snprintf(buf, sizeof buf, "%s!%s@%s", from->name, from->username, from->host);
-       else
-               rb_strlcpy(buf, from->name, sizeof buf);
-
        hdata.client = from;
        hdata.arg1 = msgbuf;
 
@@ -269,7 +287,9 @@ void
 sendto_one(struct Client *target_p, const char *pattern, ...)
 {
        va_list args;
+       struct MsgBuf msgbuf;
        buf_head_t linebuf;
+       rb_strf_t strings = { .format = pattern, .format_args = &args, .next = NULL };
 
        /* send remote if to->from non NULL */
        if(target_p->from != NULL)
@@ -280,8 +300,9 @@ sendto_one(struct Client *target_p, const char *pattern, ...)
 
        rb_linebuf_newbuf(&linebuf);
 
+       build_msgbuf_tags(&msgbuf, &me);
        va_start(args, pattern);
-       rb_linebuf_putmsg(&linebuf, pattern, &args, NULL);
+       linebuf_put_tags(&linebuf, &msgbuf, target_p, &strings);
        va_end(args);
 
        _send_linebuf(target_p, &linebuf);
@@ -299,15 +320,11 @@ void
 sendto_one_prefix(struct Client *target_p, struct Client *source_p,
                  const char *command, const char *pattern, ...)
 {
-       struct Client *dest_p;
+       struct Client *dest_p = target_p->from;
        va_list args;
+       struct MsgBuf msgbuf;
        buf_head_t linebuf;
-
-       /* send remote if to->from non NULL */
-       if(target_p->from != NULL)
-               dest_p = target_p->from;
-       else
-               dest_p = target_p;
+       rb_strf_t strings = { .format = pattern, .format_args = &args, .next = NULL };
 
        if(IsIOError(dest_p))
                return;
@@ -318,12 +335,13 @@ sendto_one_prefix(struct Client *target_p, struct Client *source_p,
                return;
        }
 
+       build_msgbuf_tags(&msgbuf, source_p);
+
        rb_linebuf_newbuf(&linebuf);
        va_start(args, pattern);
-       rb_linebuf_putmsg(&linebuf, pattern, &args,
-                      ":%s %s %s ",
-                      get_id(source_p, target_p),
-                      command, get_id(target_p, target_p));
+       linebuf_put_tagsf(&linebuf, &msgbuf, target_p, &strings,
+               ":%s %s %s ", get_id(source_p, target_p),
+               command, get_id(target_p, target_p));
        va_end(args);
 
        _send_linebuf(dest_p, &linebuf);
@@ -339,17 +357,13 @@ sendto_one_prefix(struct Client *target_p, struct Client *source_p,
 void
 sendto_one_notice(struct Client *target_p, const char *pattern, ...)
 {
-       struct Client *dest_p;
+       struct Client *dest_p = target_p->from;
        va_list args;
+       struct MsgBuf msgbuf;
        buf_head_t linebuf;
+       rb_strf_t strings = { .format = pattern, .format_args = &args, .next = NULL };
        char *to;
 
-       /* send remote if to->from non NULL */
-       if(target_p->from != NULL)
-               dest_p = target_p->from;
-       else
-               dest_p = target_p;
-
        if(IsIOError(dest_p))
                return;
 
@@ -359,11 +373,13 @@ sendto_one_notice(struct Client *target_p, const char *pattern, ...)
                return;
        }
 
+       build_msgbuf_tags(&msgbuf, &me);
+
        rb_linebuf_newbuf(&linebuf);
        va_start(args, pattern);
-       rb_linebuf_putmsg(&linebuf, pattern, &args,
-                      ":%s NOTICE %s ",
-                      get_id(&me, target_p), *(to = get_id(target_p, target_p)) != '\0' ? to : "*");
+       linebuf_put_tagsf(&linebuf, &msgbuf, target_p, &strings,
+               ":%s NOTICE %s ", get_id(&me, target_p),
+               *(to = get_id(target_p, target_p)) != '\0' ? to : "*");
        va_end(args);
 
        _send_linebuf(dest_p, &linebuf);
@@ -380,17 +396,13 @@ sendto_one_notice(struct Client *target_p, const char *pattern, ...)
 void
 sendto_one_numeric(struct Client *target_p, int numeric, const char *pattern, ...)
 {
-       struct Client *dest_p;
+       struct Client *dest_p = target_p->from;
        va_list args;
+       struct MsgBuf msgbuf;
        buf_head_t linebuf;
+       rb_strf_t strings = { .format = pattern, .format_args = &args, .next = NULL };
        char *to;
 
-       /* send remote if to->from non NULL */
-       if(target_p->from != NULL)
-               dest_p = target_p->from;
-       else
-               dest_p = target_p;
-
        if(IsIOError(dest_p))
                return;
 
@@ -400,12 +412,13 @@ sendto_one_numeric(struct Client *target_p, int numeric, const char *pattern, ..
                return;
        }
 
+       build_msgbuf_tags(&msgbuf, &me);
+
        rb_linebuf_newbuf(&linebuf);
        va_start(args, pattern);
-       rb_linebuf_putmsg(&linebuf, pattern, &args,
-                      ":%s %03d %s ",
-                      get_id(&me, target_p),
-                      numeric, *(to = get_id(target_p, target_p)) != '\0' ? to : "*");
+       linebuf_put_tagsf(&linebuf, &msgbuf, target_p, &strings,
+               ":%s %03d %s ", get_id(&me, target_p), numeric,
+               *(to = get_id(target_p, target_p)) != '\0' ? to : "*");
        va_end(args);
 
        _send_linebuf(dest_p, &linebuf);
@@ -438,6 +451,7 @@ sendto_server(struct Client *one, struct Channel *chptr, unsigned long caps,
        rb_dlink_node *ptr;
        rb_dlink_node *next_ptr;
        buf_head_t linebuf;
+       rb_strf_t strings = { .format = format, .format_args = &args, .next = NULL };
 
        /* noone to send to.. */
        if(rb_dlink_list_length(&serv_list) == 0)
@@ -448,7 +462,7 @@ sendto_server(struct Client *one, struct Channel *chptr, unsigned long caps,
 
        rb_linebuf_newbuf(&linebuf);
        va_start(args, format);
-       rb_linebuf_putmsg(&linebuf, format, &args, NULL);
+       linebuf_put_msg(&linebuf, &strings);
        va_end(args);
 
        RB_DLINK_FOREACH_SAFE(ptr, next_ptr, serv_list.head)
@@ -471,7 +485,6 @@ sendto_server(struct Client *one, struct Channel *chptr, unsigned long caps,
        }
 
        rb_linebuf_donebuf(&linebuf);
-
 }
 
 /* sendto_channel_flags()
@@ -486,39 +499,39 @@ sendto_channel_flags(struct Client *one, int type, struct Client *source_p,
 {
        static char buf[BUFSIZE];
        va_list args;
-       buf_head_t rb_linebuf_local;
-       buf_head_t rb_linebuf_id;
+       buf_head_t rb_linebuf_remote;
        struct Client *target_p;
        struct membership *msptr;
        rb_dlink_node *ptr;
        rb_dlink_node *next_ptr;
+       struct MsgBuf msgbuf;
+       struct MsgBuf_cache msgbuf_cache;
+       rb_strf_t strings = { .format = buf, .format_args = NULL, .next = NULL };
 
-       rb_linebuf_newbuf(&rb_linebuf_local);
-       rb_linebuf_newbuf(&rb_linebuf_id);
+       rb_linebuf_newbuf(&rb_linebuf_remote);
 
        current_serial++;
 
+       build_msgbuf_tags(&msgbuf, source_p);
+
        va_start(args, pattern);
-       vsnprintf(buf, sizeof(buf), pattern, args);
+       vsnprintf(buf, sizeof buf, pattern, args);
        va_end(args);
 
-       if(IsServer(source_p))
-               rb_linebuf_putmsg(&rb_linebuf_local, NULL, NULL,
-                              ":%s %s", source_p->name, buf);
-       else
-               rb_linebuf_putmsg(&rb_linebuf_local, NULL, NULL,
-                              ":%s!%s@%s %s",
-                              source_p->name, source_p->username,
-                              source_p->host, buf);
-
-       rb_linebuf_putmsg(&rb_linebuf_id, NULL, NULL, ":%s %s", use_id(source_p), buf);
+       linebuf_put_msgf(&rb_linebuf_remote, NULL, ":%s %s", use_id(source_p), buf);
+       msgbuf_cache_initf(&msgbuf_cache, &msgbuf, &strings,
+               IsPerson(source_p) ? ":%1$s!%2$s@%3$s " : ":%1$s ",
+               source_p->name, source_p->username, source_p->host);
 
        RB_DLINK_FOREACH_SAFE(ptr, next_ptr, chptr->members.head)
        {
                msptr = ptr->data;
                target_p = msptr->client_p;
 
-               if(IsIOError(target_p->from) || target_p->from == one)
+               if(!MyClient(source_p) && (IsIOError(target_p->from) || target_p->from == one))
+                       continue;
+
+               if(MyClient(source_p) && target_p == one)
                        continue;
 
                if(type && ((msptr->flags & type) == 0))
@@ -537,16 +550,26 @@ sendto_channel_flags(struct Client *one, int type, struct Client *source_p,
 
                        if(target_p->from->serial != current_serial)
                        {
-                               send_linebuf_remote(target_p, source_p, &rb_linebuf_id);
+                               send_linebuf_remote(target_p, source_p, &rb_linebuf_remote);
                                target_p->from->serial = current_serial;
                        }
                }
                else
-                       _send_linebuf(target_p, &rb_linebuf_local);
+               {
+                       _send_linebuf(target_p, msgbuf_cache_get(&msgbuf_cache, CLIENT_CAPS_ONLY(target_p)));
+               }
        }
 
-       rb_linebuf_donebuf(&rb_linebuf_local);
-       rb_linebuf_donebuf(&rb_linebuf_id);
+       /* source client may not be on the channel, send echo separately */
+       if(MyClient(source_p) && IsCapable(source_p, CLICAP_ECHO_MESSAGE))
+       {
+               target_p = one;
+
+               _send_linebuf(target_p, msgbuf_cache_get(&msgbuf_cache, CLIENT_CAPS_ONLY(target_p)));
+       }
+
+       rb_linebuf_donebuf(&rb_linebuf_remote);
+       msgbuf_cache_free(&msgbuf_cache);
 }
 
 /* sendto_channel_flags()
@@ -560,49 +583,57 @@ sendto_channel_opmod(struct Client *one, struct Client *source_p,
                     struct Channel *chptr, const char *command,
                     const char *text)
 {
-       buf_head_t rb_linebuf_local;
        buf_head_t rb_linebuf_old;
        buf_head_t rb_linebuf_new;
        struct Client *target_p;
        struct membership *msptr;
        rb_dlink_node *ptr;
        rb_dlink_node *next_ptr;
+       struct MsgBuf msgbuf;
+       struct MsgBuf_cache msgbuf_cache;
+       rb_strf_t strings = { .format = text, .format_args = NULL, .next = NULL };
 
-       rb_linebuf_newbuf(&rb_linebuf_local);
        rb_linebuf_newbuf(&rb_linebuf_old);
        rb_linebuf_newbuf(&rb_linebuf_new);
 
-       current_serial++;
+       build_msgbuf_tags(&msgbuf, source_p);
 
-       if(IsServer(source_p))
-               rb_linebuf_putmsg(&rb_linebuf_local, NULL, NULL,
-                              ":%s %s %s :%s",
-                              source_p->name, command, chptr->chname, text);
-       else
-               rb_linebuf_putmsg(&rb_linebuf_local, NULL, NULL,
-                              ":%s!%s@%s %s %s :%s",
+       current_serial++;
+       const char *statusmsg_prefix = (ConfigChannel.opmod_send_statusmsg ? "@" : "");
+
+       if(IsServer(source_p)) {
+               msgbuf_cache_initf(&msgbuf_cache, &msgbuf, &strings,
+                              ":%s %s %s%s :",
+                              source_p->name, command, statusmsg_prefix, chptr->chname);
+       } else {
+               msgbuf_cache_initf(&msgbuf_cache, &msgbuf, &strings,
+                              ":%s!%s@%s %s %s%s :",
                               source_p->name, source_p->username,
-                              source_p->host, command, chptr->chname, text);
+                              source_p->host, command, statusmsg_prefix, chptr->chname);
+       }
 
-       if (chptr->mode.mode & MODE_MODERATED)
-               rb_linebuf_putmsg(&rb_linebuf_old, NULL, NULL,
-                              ":%s %s %s :%s",
-                              use_id(source_p), command, chptr->chname, text);
-       else
-               rb_linebuf_putmsg(&rb_linebuf_old, NULL, NULL,
-                              ":%s NOTICE @%s :<%s:%s> %s",
+       if (chptr->mode.mode & MODE_MODERATED) {
+               linebuf_put_msgf(&rb_linebuf_old, &strings,
+                              ":%s %s %s%s :",
+                              use_id(source_p), command, statusmsg_prefix, chptr->chname, text);
+       } else {
+               linebuf_put_msgf(&rb_linebuf_old, &strings,
+                              ":%s NOTICE @%s :<%s:%s> ",
                               use_id(source_p->servptr), chptr->chname,
-                              source_p->name, chptr->chname, text);
-       rb_linebuf_putmsg(&rb_linebuf_new, NULL, NULL,
-                      ":%s %s =%s :%s",
-                      use_id(source_p), command, chptr->chname, text);
-
+                              source_p->name, chptr->chname);
+       }
+       linebuf_put_msgf(&rb_linebuf_new, &strings,
+                      ":%s %s =%s :",
+                      use_id(source_p), command, chptr->chname);
        RB_DLINK_FOREACH_SAFE(ptr, next_ptr, chptr->members.head)
        {
                msptr = ptr->data;
                target_p = msptr->client_p;
 
-               if(IsIOError(target_p->from) || target_p->from == one)
+               if(!MyClient(source_p) && (IsIOError(target_p->from) || target_p->from == one))
+                       continue;
+
+               if(MyClient(source_p) && target_p == one)
                        continue;
 
                if((msptr->flags & CHFL_CHANOP) == 0)
@@ -627,58 +658,93 @@ sendto_channel_opmod(struct Client *one, struct Client *source_p,
                                        send_linebuf_remote(target_p, source_p, &rb_linebuf_old);
                                target_p->from->serial = current_serial;
                        }
+               } else {
+                       _send_linebuf(target_p, msgbuf_cache_get(&msgbuf_cache, CLIENT_CAPS_ONLY(target_p)));
                }
-               else
-                       _send_linebuf(target_p, &rb_linebuf_local);
        }
 
-       rb_linebuf_donebuf(&rb_linebuf_local);
+       /* source client may not be on the channel, send echo separately */
+       if(MyClient(source_p) && IsCapable(source_p, CLICAP_ECHO_MESSAGE))
+       {
+               target_p = one;
+
+               _send_linebuf(target_p, msgbuf_cache_get(&msgbuf_cache, CLIENT_CAPS_ONLY(target_p)));
+       }
+
        rb_linebuf_donebuf(&rb_linebuf_old);
        rb_linebuf_donebuf(&rb_linebuf_new);
+       msgbuf_cache_free(&msgbuf_cache);
 }
 
-/* sendto_channel_local()
+/* _sendto_channel_local
  *
- * inputs      - flags to send to, channel to send to, va_args
+ * inputs      - source, flags to send to, privs to send to, channel to send to, va_args
  * outputs     - message to local channel members
  * side effects -
  */
 void
-sendto_channel_local(int type, struct Channel *chptr, const char *pattern, ...)
+_sendto_channel_local(struct Client *source_p, int type, const char *priv, struct Channel *chptr, const char *pattern, va_list *args)
 {
-       va_list args;
-       buf_head_t linebuf;
        struct membership *msptr;
        struct Client *target_p;
        rb_dlink_node *ptr;
        rb_dlink_node *next_ptr;
+       struct MsgBuf msgbuf;
+       struct MsgBuf_cache msgbuf_cache;
+       rb_strf_t strings = { .format = pattern, .format_args = args, .next = NULL };
 
-       rb_linebuf_newbuf(&linebuf);
+       build_msgbuf_tags(&msgbuf, source_p);
 
-       va_start(args, pattern);
-       rb_linebuf_putmsg(&linebuf, pattern, &args, NULL);
-       va_end(args);
+       msgbuf_cache_init(&msgbuf_cache, &msgbuf, &strings);
 
        RB_DLINK_FOREACH_SAFE(ptr, next_ptr, chptr->locmembers.head)
        {
                msptr = ptr->data;
                target_p = msptr->client_p;
 
-               if(IsIOError(target_p))
+               if (IsIOError(target_p))
                        continue;
 
-               if(type == ONLY_OPERS)
-               {
-                       if (!IsOper(target_p))
-                               continue;
-               }
-               else if(type && ((msptr->flags & type) == 0))
+               if (type && ((msptr->flags & type) == 0))
                        continue;
 
-               _send_linebuf(target_p, &linebuf);
+               if (priv != NULL && !HasPrivilege(target_p, priv))
+                       continue;
+
+               _send_linebuf(target_p, msgbuf_cache_get(&msgbuf_cache, CLIENT_CAPS_ONLY(target_p)));
        }
 
-       rb_linebuf_donebuf(&linebuf);
+       msgbuf_cache_free(&msgbuf_cache);
+}
+
+/* sendto_channel_local_priv()
+ *
+ * inputs      - source, flags to send to, privs to send to, channel to send to, va_args
+ * outputs     - message to local channel members
+ * side effects -
+ */
+void
+sendto_channel_local_priv(struct Client *source_p, int type, const char *priv, struct Channel *chptr, const char *pattern, ...)
+{
+       va_list args;
+       va_start(args, pattern);
+       _sendto_channel_local(source_p, type, priv, chptr, pattern, &args);
+       va_end(args);
+}
+
+/* sendto_channel_local()
+ *
+ * inputs      - source, flags to send to, channel to send to, va_args
+ * outputs     - message to local channel members
+ * side effects -
+ */
+void
+sendto_channel_local(struct Client *source_p, int type, struct Channel *chptr, const char *pattern, ...)
+{
+       va_list args;
+       va_start(args, pattern);
+       _sendto_channel_local(source_p, type, NULL, chptr, pattern, &args);
+       va_end(args);
 }
 
 /*
@@ -687,17 +753,19 @@ sendto_channel_local(int type, struct Channel *chptr, const char *pattern, ...)
  * Shared implementation of sendto_channel_local_with_capability and sendto_channel_local_with_capability_butone
  */
 static void
-_sendto_channel_local_with_capability_butone(struct Client *one, int type, int caps, int negcaps, struct Channel *chptr,
-       const char *pattern, va_list * args)
+_sendto_channel_local_with_capability_butone(struct Client *source_p, struct Client *one, int type,
+       int caps, int negcaps, struct Channel *chptr, const char *pattern, va_list * args)
 {
-       buf_head_t linebuf;
        struct membership *msptr;
        struct Client *target_p;
        rb_dlink_node *ptr;
        rb_dlink_node *next_ptr;
+       struct MsgBuf msgbuf;
+       struct MsgBuf_cache msgbuf_cache;
+       rb_strf_t strings = { .format = pattern, .format_args = args, .next = NULL };
 
-       rb_linebuf_newbuf(&linebuf);
-       rb_linebuf_putmsg(&linebuf, pattern, args, NULL);
+       build_msgbuf_tags(&msgbuf, source_p);
+       msgbuf_cache_init(&msgbuf_cache, &msgbuf, &strings);
 
        RB_DLINK_FOREACH_SAFE(ptr, next_ptr, chptr->locmembers.head)
        {
@@ -715,43 +783,43 @@ _sendto_channel_local_with_capability_butone(struct Client *one, int type, int c
                if(type && ((msptr->flags & type) == 0))
                        continue;
 
-               _send_linebuf(target_p, &linebuf);
+               _send_linebuf(target_p, msgbuf_cache_get(&msgbuf_cache, CLIENT_CAPS_ONLY(target_p)));
        }
 
-       rb_linebuf_donebuf(&linebuf);
+       msgbuf_cache_free(&msgbuf_cache);
 }
 
 /* sendto_channel_local_with_capability()
  *
- * inputs      - flags to send to, caps, negate caps, channel to send to, va_args
+ * inputs      - source, flags to send to, caps, negate caps, channel to send to, va_args
  * outputs     - message to local channel members
  * side effects -
  */
 void
-sendto_channel_local_with_capability(int type, int caps, int negcaps, struct Channel *chptr, const char *pattern, ...)
+sendto_channel_local_with_capability(struct Client *source_p, int type, int caps, int negcaps, struct Channel *chptr, const char *pattern, ...)
 {
        va_list args;
 
        va_start(args, pattern);
-       _sendto_channel_local_with_capability_butone(NULL, type, caps, negcaps, chptr, pattern, &args);
+       _sendto_channel_local_with_capability_butone(source_p, NULL, type, caps, negcaps, chptr, pattern, &args);
        va_end(args);
 }
 
 
 /* sendto_channel_local_with_capability()
  *
- * inputs      - flags to send to, caps, negate caps, channel to send to, va_args
+ * inputs      - source, flags to send to, caps, negate caps, channel to send to, va_args
  * outputs     - message to local channel members
  * side effects -
  */
 void
-sendto_channel_local_with_capability_butone(struct Client *one, int type, int caps, int negcaps, struct Channel *chptr,
-               const char *pattern, ...)
+sendto_channel_local_with_capability_butone(struct Client *one, int type,
+       int caps, int negcaps, struct Channel *chptr, const char *pattern, ...)
 {
        va_list args;
 
        va_start(args, pattern);
-       _sendto_channel_local_with_capability_butone(one, type, caps, negcaps, chptr, pattern, &args);
+       _sendto_channel_local_with_capability_butone(one, one, type, caps, negcaps, chptr, pattern, &args);
        va_end(args);
 }
 
@@ -767,16 +835,18 @@ void
 sendto_channel_local_butone(struct Client *one, int type, struct Channel *chptr, const char *pattern, ...)
 {
        va_list args;
-       buf_head_t linebuf;
        struct membership *msptr;
        struct Client *target_p;
+       struct MsgBuf msgbuf;
        rb_dlink_node *ptr;
        rb_dlink_node *next_ptr;
+       struct MsgBuf_cache msgbuf_cache;
+       rb_strf_t strings = { .format = pattern, .format_args = &args, .next = NULL };
 
-       rb_linebuf_newbuf(&linebuf);
+       build_msgbuf_tags(&msgbuf, one);
 
        va_start(args, pattern);
-       rb_linebuf_putmsg(&linebuf, pattern, &args, NULL);
+       msgbuf_cache_init(&msgbuf_cache, &msgbuf, &strings);
        va_end(args);
 
        RB_DLINK_FOREACH_SAFE(ptr, next_ptr, chptr->locmembers.head)
@@ -793,18 +863,19 @@ sendto_channel_local_butone(struct Client *one, int type, struct Channel *chptr,
                if(type && ((msptr->flags & type) == 0))
                        continue;
 
-               _send_linebuf(target_p, &linebuf);
+               /* attach the present linebuf to the target */
+               _send_linebuf(target_p, msgbuf_cache_get(&msgbuf_cache, CLIENT_CAPS_ONLY(target_p)));
        }
 
-       rb_linebuf_donebuf(&linebuf);
+       msgbuf_cache_free(&msgbuf_cache);
 }
 
 /*
  * sendto_common_channels_local()
  *
  * inputs      - pointer to client
- *              - capability mask
- *             - negated capability mask
+ *             - capability mask
+ *             - negated capability mask
  *             - pattern to send
  * output      - NONE
  * side effects        - Sends a message to all people on local server who are
@@ -823,11 +894,14 @@ sendto_common_channels_local(struct Client *user, int cap, int negcap, const cha
        struct Client *target_p;
        struct membership *msptr;
        struct membership *mscptr;
-       buf_head_t linebuf;
+       struct MsgBuf msgbuf;
+       struct MsgBuf_cache msgbuf_cache;
+       rb_strf_t strings = { .format = pattern, .format_args = &args, .next = NULL };
+
+       build_msgbuf_tags(&msgbuf, user);
 
-       rb_linebuf_newbuf(&linebuf);
        va_start(args, pattern);
-       rb_linebuf_putmsg(&linebuf, pattern, &args, NULL);
+       msgbuf_cache_init(&msgbuf_cache, &msgbuf, &strings);
        va_end(args);
 
        ++current_serial;
@@ -849,25 +923,27 @@ sendto_common_channels_local(struct Client *user, int cap, int negcap, const cha
                                continue;
 
                        target_p->serial = current_serial;
-                       send_linebuf(target_p, &linebuf);
+                       send_linebuf(target_p, msgbuf_cache_get(&msgbuf_cache, CLIENT_CAPS_ONLY(target_p)));
                }
        }
 
        /* this can happen when the user isnt in any channels, but we still
         * need to send them the data, ie a nick change
         */
-       if(MyConnect(user) && (user->serial != current_serial))
-               send_linebuf(user, &linebuf);
+       if(MyConnect(user) && (user->serial != current_serial)
+                       && IsCapable(user, cap) && NotCapable(user, negcap)) {
+               send_linebuf(user, msgbuf_cache_get(&msgbuf_cache, CLIENT_CAPS_ONLY(user)));
+       }
 
-       rb_linebuf_donebuf(&linebuf);
+       msgbuf_cache_free(&msgbuf_cache);
 }
 
 /*
  * sendto_common_channels_local_butone()
  *
  * inputs      - pointer to client
- *              - capability mask
- *             - negated capability mask
+ *             - capability mask
+ *             - negated capability mask
  *             - pattern to send
  * output      - NONE
  * side effects        - Sends a message to all people on local server who are
@@ -885,11 +961,14 @@ sendto_common_channels_local_butone(struct Client *user, int cap, int negcap, co
        struct Client *target_p;
        struct membership *msptr;
        struct membership *mscptr;
-       buf_head_t linebuf;
+       struct MsgBuf msgbuf;
+       struct MsgBuf_cache msgbuf_cache;
+       rb_strf_t strings = { .format = pattern, .format_args = &args, .next = NULL };
+
+       build_msgbuf_tags(&msgbuf, user);
 
-       rb_linebuf_newbuf(&linebuf);
        va_start(args, pattern);
-       rb_linebuf_putmsg(&linebuf, pattern, &args, NULL);
+       msgbuf_cache_init(&msgbuf_cache, &msgbuf, &strings);
        va_end(args);
 
        ++current_serial;
@@ -913,11 +992,11 @@ sendto_common_channels_local_butone(struct Client *user, int cap, int negcap, co
                                continue;
 
                        target_p->serial = current_serial;
-                       send_linebuf(target_p, &linebuf);
+                       send_linebuf(target_p, msgbuf_cache_get(&msgbuf_cache, CLIENT_CAPS_ONLY(target_p)));
                }
        }
 
-       rb_linebuf_donebuf(&linebuf);
+       msgbuf_cache_free(&msgbuf_cache);
 }
 
 /* sendto_match_butone()
@@ -935,26 +1014,24 @@ sendto_match_butone(struct Client *one, struct Client *source_p,
        struct Client *target_p;
        rb_dlink_node *ptr;
        rb_dlink_node *next_ptr;
-       buf_head_t rb_linebuf_local;
-       buf_head_t rb_linebuf_id;
+       buf_head_t rb_linebuf_remote;
+       struct MsgBuf msgbuf;
+       struct MsgBuf_cache msgbuf_cache;
+       rb_strf_t strings = { .format = buf, .format_args = NULL, .next = NULL };
 
-       rb_linebuf_newbuf(&rb_linebuf_local);
-       rb_linebuf_newbuf(&rb_linebuf_id);
+       rb_linebuf_newbuf(&rb_linebuf_remote);
+
+       build_msgbuf_tags(&msgbuf, source_p);
 
        va_start(args, pattern);
        vsnprintf(buf, sizeof(buf), pattern, args);
        va_end(args);
 
-       if(IsServer(source_p))
-               rb_linebuf_putmsg(&rb_linebuf_local, NULL, NULL,
-                              ":%s %s", source_p->name, buf);
-       else
-               rb_linebuf_putmsg(&rb_linebuf_local, NULL, NULL,
-                              ":%s!%s@%s %s",
-                              source_p->name, source_p->username,
-                              source_p->host, buf);
+       msgbuf_cache_initf(&msgbuf_cache, &msgbuf, &strings,
+               IsServer(source_p) ? ":%s " : ":%s!%s@%s ",
+               source_p->name, source_p->username, source_p->host);
 
-       rb_linebuf_putmsg(&rb_linebuf_id, NULL, NULL, ":%s %s", use_id(source_p), buf);
+       linebuf_put_msgf(&rb_linebuf_remote, &strings, ":%s ", use_id(source_p));
 
        if(what == MATCH_HOST)
        {
@@ -963,7 +1040,7 @@ sendto_match_butone(struct Client *one, struct Client *source_p,
                        target_p = ptr->data;
 
                        if(match(mask, target_p->host))
-                               _send_linebuf(target_p, &rb_linebuf_local);
+                               _send_linebuf(target_p, msgbuf_cache_get(&msgbuf_cache, CLIENT_CAPS_ONLY(target_p)));
                }
        }
        /* what = MATCH_SERVER, if it doesnt match us, just send remote */
@@ -972,7 +1049,7 @@ sendto_match_butone(struct Client *one, struct Client *source_p,
                RB_DLINK_FOREACH_SAFE(ptr, next_ptr, lclient_list.head)
                {
                        target_p = ptr->data;
-                       _send_linebuf(target_p, &rb_linebuf_local);
+                       _send_linebuf(target_p, msgbuf_cache_get(&msgbuf_cache, CLIENT_CAPS_ONLY(target_p)));
                }
        }
 
@@ -983,11 +1060,11 @@ sendto_match_butone(struct Client *one, struct Client *source_p,
                if(target_p == one)
                        continue;
 
-               send_linebuf_remote(target_p, source_p, &rb_linebuf_id);
+               send_linebuf_remote(target_p, source_p, &rb_linebuf_remote);
        }
 
-       rb_linebuf_donebuf(&rb_linebuf_local);
-       rb_linebuf_donebuf(&rb_linebuf_id);
+       msgbuf_cache_free(&msgbuf_cache);
+       rb_linebuf_donebuf(&rb_linebuf_remote);
 }
 
 /* sendto_match_servs()
@@ -1005,6 +1082,7 @@ sendto_match_servs(struct Client *source_p, const char *mask, int cap,
        rb_dlink_node *ptr;
        struct Client *target_p;
        buf_head_t rb_linebuf_id;
+       rb_strf_t strings = { .format = buf, .format_args = NULL, .next = NULL };
 
        if(EmptyString(mask))
                return;
@@ -1015,8 +1093,7 @@ sendto_match_servs(struct Client *source_p, const char *mask, int cap,
        vsnprintf(buf, sizeof(buf), pattern, args);
        va_end(args);
 
-       rb_linebuf_putmsg(&rb_linebuf_id, NULL, NULL,
-                       ":%s %s", use_id(source_p), buf);
+       linebuf_put_msgf(&rb_linebuf_id, &strings, ":%s ", use_id(source_p));
 
        current_serial++;
 
@@ -1063,12 +1140,14 @@ sendto_local_clients_with_capability(int cap, const char *pattern, ...)
        va_list args;
        rb_dlink_node *ptr;
        struct Client *target_p;
-       buf_head_t linebuf;
+       struct MsgBuf msgbuf;
+       struct MsgBuf_cache msgbuf_cache;
+       rb_strf_t strings = { .format = pattern, .format_args = &args, .next = NULL };
 
-       rb_linebuf_newbuf(&linebuf);
+       build_msgbuf_tags(&msgbuf, &me);
 
        va_start(args, pattern);
-       rb_linebuf_putmsg(&linebuf, pattern, &args, NULL);
+       msgbuf_cache_init(&msgbuf_cache, &msgbuf, &strings);
        va_end(args);
 
        RB_DLINK_FOREACH(ptr, lclient_list.head)
@@ -1078,10 +1157,10 @@ sendto_local_clients_with_capability(int cap, const char *pattern, ...)
                if(IsIOError(target_p) || !IsCapable(target_p, cap))
                        continue;
 
-               send_linebuf(target_p, &linebuf);
+               send_linebuf(target_p, msgbuf_cache_get(&msgbuf_cache, CLIENT_CAPS_ONLY(target_p)));
        }
 
-       rb_linebuf_donebuf(&linebuf);
+       msgbuf_cache_free(&msgbuf_cache);
 }
 
 /* sendto_monitor()
@@ -1091,18 +1170,20 @@ sendto_local_clients_with_capability(int cap, const char *pattern, ...)
  * side effects -
  */
 void
-sendto_monitor(struct monitor *monptr, const char *pattern, ...)
+sendto_monitor(struct Client *source_p, struct monitor *monptr, const char *pattern, ...)
 {
        va_list args;
-       buf_head_t linebuf;
        struct Client *target_p;
        rb_dlink_node *ptr;
        rb_dlink_node *next_ptr;
+       struct MsgBuf msgbuf;
+       struct MsgBuf_cache msgbuf_cache;
+       rb_strf_t strings = { .format = pattern, .format_args = &args, .next = NULL };
 
-       rb_linebuf_newbuf(&linebuf);
+       build_msgbuf_tags(&msgbuf, source_p);
 
        va_start(args, pattern);
-       rb_linebuf_putmsg(&linebuf, pattern, &args, NULL);
+       msgbuf_cache_init(&msgbuf_cache, &msgbuf, &strings);
        va_end(args);
 
        RB_DLINK_FOREACH_SAFE(ptr, next_ptr, monptr->users.head)
@@ -1112,7 +1193,51 @@ sendto_monitor(struct monitor *monptr, const char *pattern, ...)
                if(IsIOError(target_p))
                        continue;
 
-               _send_linebuf(target_p, &linebuf);
+               _send_linebuf(target_p, msgbuf_cache_get(&msgbuf_cache, CLIENT_CAPS_ONLY(target_p)));
+       }
+
+       msgbuf_cache_free(&msgbuf_cache);
+}
+
+/* _sendto_anywhere()
+ *
+ * inputs      - real_target, target, source, va_args
+ * outputs     -
+ * side effects - client is sent message/own message with correct prefix.
+ */
+static void
+_sendto_anywhere(struct Client *dest_p, struct Client *target_p,
+               struct Client *source_p, const char *command,
+               const char *pattern, va_list *args)
+{
+       buf_head_t linebuf;
+       rb_strf_t strings = { .format = pattern, .format_args = args, .next = NULL };
+
+       rb_linebuf_newbuf(&linebuf);
+
+       if (MyClient(dest_p)) {
+               if (IsServer(source_p)) {
+                       linebuf_put_msgf(&linebuf, &strings, ":%s %s %s ",
+                                      source_p->name, command,
+                                      target_p->name);
+               } else {
+                       struct MsgBuf msgbuf;
+
+                       build_msgbuf_tags(&msgbuf, source_p);
+
+                       linebuf_put_tagsf(&linebuf, &msgbuf, dest_p, &strings,
+                               IsPerson(source_p) ? ":%1$s!%4$s@%5$s %2$s %3$s " : ":%1$s %2$s %3$s ",
+                               source_p->name, command, target_p->name,
+                               source_p->username, source_p->host);
+               }
+
+               _send_linebuf(dest_p, &linebuf);
+       } else {
+               linebuf_put_msgf(&linebuf, &strings, ":%s %s %s ",
+                              get_id(source_p, target_p), command,
+                              get_id(target_p, target_p));
+
+               send_linebuf_remote(dest_p, source_p, &linebuf);
        }
 
        rb_linebuf_donebuf(&linebuf);
@@ -1129,37 +1254,30 @@ sendto_anywhere(struct Client *target_p, struct Client *source_p,
                const char *command, const char *pattern, ...)
 {
        va_list args;
-       buf_head_t linebuf;
-
-       rb_linebuf_newbuf(&linebuf);
 
        va_start(args, pattern);
-
-       if(MyClient(target_p))
-       {
-               if(IsServer(source_p))
-                       rb_linebuf_putmsg(&linebuf, pattern, &args, ":%s %s %s ",
-                                      source_p->name, command,
-                                      target_p->name);
-               else
-                       rb_linebuf_putmsg(&linebuf, pattern, &args,
-                                      ":%s!%s@%s %s %s ",
-                                      source_p->name, source_p->username,
-                                      source_p->host, command,
-                                      target_p->name);
-       }
-       else
-               rb_linebuf_putmsg(&linebuf, pattern, &args, ":%s %s %s ",
-                              get_id(source_p, target_p), command,
-                              get_id(target_p, target_p));
+       _sendto_anywhere(target_p, target_p, source_p, command, pattern, &args);
        va_end(args);
+}
 
-       if(MyClient(target_p))
-               _send_linebuf(target_p, &linebuf);
-       else
-               send_linebuf_remote(target_p, source_p, &linebuf);
+/* sendto_anywhere_echo()
+ *
+ * inputs      - target, source, va_args
+ * outputs     -
+ * side effects - client is sent own message with correct prefix.
+ */
+void
+sendto_anywhere_echo(struct Client *target_p, struct Client *source_p,
+               const char *command, const char *pattern, ...)
+{
+       va_list args;
 
-       rb_linebuf_donebuf(&linebuf);
+       s_assert(MyClient(source_p));
+       s_assert(!IsServer(source_p));
+
+       va_start(args, pattern);
+       _sendto_anywhere(source_p, target_p, source_p, command, pattern, &args);
+       va_end(args);
 }
 
 /* sendto_realops_snomask()
@@ -1177,20 +1295,23 @@ sendto_realops_snomask(int flags, int level, const char *pattern, ...)
        rb_dlink_node *ptr;
        rb_dlink_node *next_ptr;
        va_list args;
-       buf_head_t linebuf;
+       struct MsgBuf msgbuf;
+       struct MsgBuf_cache msgbuf_cache;
 
-       rb_linebuf_newbuf(&linebuf);
+       build_msgbuf_tags(&msgbuf, &me);
+
+       /* rather a lot of copying around, oh well -- jilles */
+       va_start(args, pattern);
+       vsnprintf(buf, sizeof(buf), pattern, args);
+       va_end(args);
+
+       msgbuf_cache_initf(&msgbuf_cache, &msgbuf, NULL,
+               ":%s NOTICE * :*** Notice -- %s", me.name, buf);
 
        /* Be very sure not to do things like "Trying to send to myself"
         * L_NETWIDE, otherwise infinite recursion may result! -- jilles */
        if (level & L_NETWIDE && ConfigFileEntry.global_snotices)
        {
-               /* rather a lot of copying around, oh well -- jilles */
-               va_start(args, pattern);
-               vsnprintf(buf, sizeof(buf), pattern, args);
-               va_end(args);
-               rb_linebuf_putmsg(&linebuf, pattern, NULL,
-                               ":%s NOTICE * :*** Notice -- %s", me.name, buf);
                snobuf = construct_snobuf(flags);
                if (snobuf[1] != '\0')
                        sendto_server(NULL, NULL, CAP_ENCAP|CAP_TS6, NOCAPS,
@@ -1199,21 +1320,8 @@ sendto_realops_snomask(int flags, int level, const char *pattern, ...)
        }
        else if (remote_rehash_oper_p != NULL)
        {
-               /* rather a lot of copying around, oh well -- jilles */
-               va_start(args, pattern);
-               vsnprintf(buf, sizeof(buf), pattern, args);
-               va_end(args);
-               rb_linebuf_putmsg(&linebuf, pattern, NULL,
-                               ":%s NOTICE * :*** Notice -- %s", me.name, buf);
                sendto_one_notice(remote_rehash_oper_p, ":*** Notice -- %s", buf);
        }
-       else
-       {
-               va_start(args, pattern);
-               rb_linebuf_putmsg(&linebuf, pattern, &args,
-                               ":%s NOTICE * :*** Notice -- ", me.name);
-               va_end(args);
-       }
        level &= ~L_NETWIDE;
 
        RB_DLINK_FOREACH_SAFE(ptr, next_ptr, local_oper_list.head)
@@ -1227,11 +1335,12 @@ sendto_realops_snomask(int flags, int level, const char *pattern, ...)
                   ((level == L_OPER) && IsOperAdmin(client_p)))
                        continue;
 
-               if(client_p->snomask & flags)
-                       _send_linebuf(client_p, &linebuf);
+               if (client_p->snomask & flags) {
+                       _send_linebuf(client_p, msgbuf_cache_get(&msgbuf_cache, CLIENT_CAPS_ONLY(client_p)));
+               }
        }
 
-       rb_linebuf_donebuf(&linebuf);
+       msgbuf_cache_free(&msgbuf_cache);
 }
 /* sendto_realops_snomask_from()
  *
@@ -1247,12 +1356,14 @@ sendto_realops_snomask_from(int flags, int level, struct Client *source_p,
        rb_dlink_node *ptr;
        rb_dlink_node *next_ptr;
        va_list args;
-       buf_head_t linebuf;
+       struct MsgBuf msgbuf;
+       struct MsgBuf_cache msgbuf_cache;
+       rb_strf_t strings = { .format = pattern, .format_args = &args, .next = NULL };
 
-       rb_linebuf_newbuf(&linebuf);
+       build_msgbuf_tags(&msgbuf, &me);
 
        va_start(args, pattern);
-       rb_linebuf_putmsg(&linebuf, pattern, &args,
+       msgbuf_cache_initf(&msgbuf_cache, &msgbuf, &strings,
                       ":%s NOTICE * :*** Notice -- ", source_p->name);
        va_end(args);
 
@@ -1267,11 +1378,12 @@ sendto_realops_snomask_from(int flags, int level, struct Client *source_p,
                   ((level == L_OPER) && IsOperAdmin(client_p)))
                        continue;
 
-               if(client_p->snomask & flags)
-                       _send_linebuf(client_p, &linebuf);
+               if (client_p->snomask & flags) {
+                       _send_linebuf(client_p, msgbuf_cache_get(&msgbuf_cache, CLIENT_CAPS_ONLY(client_p)));
+               }
        }
 
-       rb_linebuf_donebuf(&linebuf);
+       msgbuf_cache_free(&msgbuf_cache);
 }
 
 /*
@@ -1290,30 +1402,33 @@ sendto_wallops_flags(int flags, struct Client *source_p, const char *pattern, ..
        rb_dlink_node *ptr;
        rb_dlink_node *next_ptr;
        va_list args;
-       buf_head_t linebuf;
+       struct MsgBuf msgbuf;
+       struct MsgBuf_cache msgbuf_cache;
+       rb_strf_t strings = { .format = pattern, .format_args = &args, .next = NULL };
 
-       rb_linebuf_newbuf(&linebuf);
+       build_msgbuf_tags(&msgbuf, source_p);
 
        va_start(args, pattern);
-
-       if(IsPerson(source_p))
-               rb_linebuf_putmsg(&linebuf, pattern, &args,
+       if (IsPerson(source_p)) {
+               msgbuf_cache_initf(&msgbuf_cache, &msgbuf, &strings,
                               ":%s!%s@%s WALLOPS :", source_p->name,
                               source_p->username, source_p->host);
-       else
-               rb_linebuf_putmsg(&linebuf, pattern, &args, ":%s WALLOPS :", source_p->name);
-
+       } else {
+               msgbuf_cache_initf(&msgbuf_cache, &msgbuf, &strings,
+                       ":%s WALLOPS :", source_p->name);
+       }
        va_end(args);
 
        RB_DLINK_FOREACH_SAFE(ptr, next_ptr, IsPerson(source_p) && flags == UMODE_WALLOP ? lclient_list.head : local_oper_list.head)
        {
                client_p = ptr->data;
 
-               if(client_p->umodes & flags)
-                       _send_linebuf(client_p, &linebuf);
+               if (client_p->umodes & flags) {
+                       _send_linebuf(client_p, msgbuf_cache_get(&msgbuf_cache, CLIENT_CAPS_ONLY(client_p)));
+               }
        }
 
-       rb_linebuf_donebuf(&linebuf);
+       msgbuf_cache_free(&msgbuf_cache);
 }
 
 /* kill_client()
@@ -1327,12 +1442,16 @@ kill_client(struct Client *target_p, struct Client *diedie, const char *pattern,
 {
        va_list args;
        buf_head_t linebuf;
+       struct MsgBuf msgbuf;
+       rb_strf_t strings = { .format = pattern, .format_args = &args, .next = NULL };
+
+       build_msgbuf_tags(&msgbuf, &me);
 
        rb_linebuf_newbuf(&linebuf);
 
        va_start(args, pattern);
-       rb_linebuf_putmsg(&linebuf, pattern, &args, ":%s KILL %s :",
-                     get_id(&me, target_p), get_id(diedie, target_p));
+       linebuf_put_tagsf(&linebuf, &msgbuf, target_p, &strings,
+               ":%s KILL %s :", get_id(&me, target_p), get_id(diedie, target_p));
        va_end(args);
 
        send_linebuf(target_p, &linebuf);
@@ -1360,15 +1479,14 @@ kill_client_serv_butone(struct Client *one, struct Client *target_p, const char
        rb_dlink_node *ptr;
        rb_dlink_node *next_ptr;
        buf_head_t rb_linebuf_id;
+       rb_strf_t strings = { .format = pattern, .format_args = &args, .next = NULL };
 
        rb_linebuf_newbuf(&rb_linebuf_id);
 
        va_start(args, pattern);
-       vsnprintf(buf, sizeof(buf), pattern, args);
-       va_end(args);
-
-       rb_linebuf_putmsg(&rb_linebuf_id, NULL, NULL, ":%s KILL %s :%s",
+       linebuf_put_msgf(&rb_linebuf_id, &strings, ":%s KILL %s :%s",
                       use_id(&me), use_id(target_p), buf);
+       va_end(args);
 
        RB_DLINK_FOREACH_SAFE(ptr, next_ptr, serv_list.head)
        {
@@ -1386,3 +1504,171 @@ kill_client_serv_butone(struct Client *one, struct Client *target_p, const char
 
        rb_linebuf_donebuf(&rb_linebuf_id);
 }
+
+static struct Client *multiline_stashed_target_p;
+static char multiline_prefix[DATALEN+1]; /* allow for null termination */
+static int multiline_prefix_len;
+static char multiline_separator[2];
+static int multiline_separator_len;
+static char *multiline_item_start;
+static char *multiline_cur;
+static int multiline_cur_len;
+static int multiline_remote_pad;
+
+bool
+send_multiline_init(struct Client *target_p, const char *separator, const char *format, ...)
+{
+       va_list args;
+
+       s_assert(multiline_stashed_target_p == NULL && "Multiline: didn't cleanup after last usage!");
+
+       va_start(args, format);
+       multiline_prefix_len = vsnprintf(multiline_prefix, sizeof multiline_prefix, format, args);
+       va_end(args);
+
+       if (multiline_prefix_len <= 0 || multiline_prefix_len >= DATALEN)
+       {
+               s_assert(false && "Multiline: failure preparing prefix!");
+               return false;
+       }
+
+       multiline_separator_len = rb_strlcpy(multiline_separator, separator, sizeof multiline_separator);
+       if (multiline_separator_len >= sizeof multiline_separator)
+       {
+               s_assert(false && "Multiline: separator too long");
+               return false;
+       }
+
+       multiline_stashed_target_p = target_p;
+       multiline_item_start = multiline_prefix + multiline_prefix_len;
+       multiline_cur = multiline_item_start;
+       multiline_cur_len = multiline_prefix_len;
+       multiline_remote_pad = 0;
+       return true;
+}
+
+bool
+send_multiline_remote_pad(struct Client *target_p, struct Client *client_p)
+{
+       ssize_t remote_pad;
+
+       if (target_p != multiline_stashed_target_p)
+       {
+               s_assert(false && "Multiline: missed init call!");
+               multiline_stashed_target_p = NULL;
+               return false;
+       }
+
+       if (MyConnect(target_p))
+               return true;
+
+       remote_pad = strlen(client_p->name) - strlen(client_p->id);
+
+       if (remote_pad > 0)
+       {
+               multiline_remote_pad += remote_pad;
+       }
+
+       return true;
+}
+
+enum multiline_item_result
+send_multiline_item(struct Client *target_p, const char *format, ...)
+{
+       va_list args;
+       char item[DATALEN];
+       int item_len, res;
+       enum multiline_item_result ret = MULTILINE_SUCCESS;
+
+       if (target_p != multiline_stashed_target_p)
+       {
+               s_assert(false && "Multiline: missed init call!");
+               multiline_stashed_target_p = NULL;
+               return MULTILINE_FAILURE;
+       }
+
+       va_start(args, format);
+       item_len = vsnprintf(item, sizeof item, format, args);
+       va_end(args);
+
+       if (item_len < 0 || multiline_prefix_len + multiline_remote_pad + item_len > DATALEN)
+       {
+               s_assert(false && "Multiline: failure preparing item!");
+               multiline_stashed_target_p = NULL;
+               return MULTILINE_FAILURE;
+       }
+
+       if (multiline_cur_len + ((*multiline_item_start != '\0') ? multiline_separator_len : 0) + item_len > DATALEN - multiline_remote_pad)
+       {
+               sendto_one(target_p, "%s", multiline_prefix);
+               *multiline_item_start = '\0';
+               multiline_cur_len = multiline_prefix_len;
+               multiline_cur = multiline_item_start;
+               ret = MULTILINE_WRAPPED;
+       }
+
+       res = snprintf(multiline_cur, sizeof multiline_prefix - multiline_cur_len, "%s%s",
+                       (*multiline_item_start != '\0') ? multiline_separator : "",
+                       item);
+
+       if (res < 0)
+       {
+               s_assert(false && "Multiline: failure appending item!");
+               multiline_stashed_target_p = NULL;
+               return MULTILINE_FAILURE;
+       }
+
+       multiline_cur_len += res;
+       multiline_cur += res;
+       return ret;
+}
+
+bool
+send_multiline_fini(struct Client *target_p, const char *format, ...)
+{
+       va_list args;
+       char final[DATALEN];
+       int final_len;
+
+       if (target_p != multiline_stashed_target_p)
+       {
+               s_assert(false && "Multiline: missed init call!");
+               multiline_stashed_target_p = NULL;
+               return false;
+       }
+
+       if (multiline_cur_len == multiline_prefix_len)
+       {
+               multiline_stashed_target_p = NULL;
+               return true;
+       }
+
+       if (format)
+       {
+               va_start(args, format);
+               final_len = vsnprintf(final, sizeof final, format, args);
+               va_end(args);
+
+               if (final_len <= 0 || final_len > multiline_prefix_len)
+               {
+                       s_assert(false && "Multiline: failure preparing final prefix!");
+                       multiline_stashed_target_p = NULL;
+                       return false;
+               }
+       }
+       else
+       {
+               rb_strlcpy(final, multiline_prefix, multiline_prefix_len + 1);
+       }
+
+       sendto_one(target_p, "%s%s", final, multiline_item_start);
+
+       multiline_stashed_target_p = NULL;
+       return true;
+}
+
+void
+send_multiline_reset(void)
+{
+       multiline_stashed_target_p = NULL;
+}