diff --git a/include/send.h b/include/send.h index 4985b460..84a4de12 100644 --- a/include/send.h +++ b/include/send.h @@ -93,6 +93,18 @@ extern void kill_client(struct Client *client_p, struct Client *diedie, extern void kill_client_serv_butone(struct Client *one, struct Client *source_p, const char *pattern, ...) AFP(3, 4); +enum multiline_item_result { + MULTILINE_FAILURE, + MULTILINE_SUCCESS, + MULTILINE_WRAPPED, +}; + +extern bool send_multiline_init(struct Client *target_p, const char *separator, const char *format, ...) AFP(3, 4); +extern bool send_multiline_remote_pad(struct Client *target_p, struct Client *client_p); +extern enum multiline_item_result send_multiline_item(struct Client *target_p, const char *format, ...) AFP(2, 3); +extern bool send_multiline_fini(struct Client *target_p, const char *format, ...) AFP(2, 3); +extern void send_multiline_reset(void); + #define L_ALL 0 #define L_OPER 1 #define L_ADMIN 2 diff --git a/ircd/send.c b/ircd/send.c index 43a64c10..da2a9c53 100644 --- a/ircd/send.c +++ b/ircd/send.c @@ -1503,3 +1503,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; +}