--- a/libpurple/protocols/irc/cmds.c Sat Apr 23 05:05:15 2022 -0500
+++ b/libpurple/protocols/irc/cmds.c Sat Apr 23 05:05:54 2022 -0500
@@ -390,7 +390,7 @@
buf = irc_format(irc, "vc:", "PART", args[0] ? args[0] : target, args[1]);
buf = irc_format(irc, "vc", "PART", args[0] ? args[0] : target);
+ irc_priority_send(irc, buf); @@ -479,7 +479,7 @@
* decide we want custom quit messages.
buf = irc_format(irc, "v:", "QUIT", (args && args[0]) ? args[0] : IRC_DEFAULT_QUIT);
+ irc_priority_send(irc, buf); --- a/libpurple/protocols/irc/irc.c Sat Apr 23 05:05:15 2022 -0500
+++ b/libpurple/protocols/irc/irc.c Sat Apr 23 05:05:54 2022 -0500
@@ -29,6 +29,7 @@
#include "conversation.h"
@@ -95,9 +96,21 @@
ret = write(irc->fd, buf, len);
+ irc->send_time = time(NULL); +void irc_priority_send(struct irc_conn *irc, const char *buf) + if(irc->sent_partial) { + g_queue_insert_after(irc->send_queue, irc->send_queue->head, + do_send(irc, buf, strlen(buf)); static int irc_send_raw(PurpleConnection *gc, const char *buf, int len)
struct irc_conn *irc = (struct irc_conn*)gc->proto_data;
@@ -108,99 +121,125 @@
-irc_send_cb(gpointer data, gint source, PurpleInputCondition cond)
- struct irc_conn *irc = data;
+irc_send_handler_cb(gpointer data) { + struct irc_conn *irc = (struct irc_conn *)data; + interval = purple_account_get_int(irc->account, "ratelimit-interval", + IRC_DEFAULT_COMMAND_INTERVAL); - writelen = purple_circ_buffer_get_max_read(irc->outbuf);
- purple_input_remove(irc->writeh);
+ /* Check if we're enabled. */ + gint burst = purple_account_get_int(irc->account, "ratelimit-burst", + IRC_DEFAULT_COMMAND_MAX_BURST); + available = (time(NULL) - irc->send_time) / interval; + if(available > burst) { - ret = do_send(irc, irc->outbuf->outptr, writelen);
+ gint length = 0, ret = 0; + /* No message in the queue should be NULL, so a NULL value means the + raw = g_queue_pop_head(irc->send_queue); + ret = do_send(irc, msg, length); + if(ret <= 0 && errno != EAGAIN) { + PurpleConnection *gc = purple_account_get_connection(irc->account); + gchar *tmp = g_strdup_printf(_("Lost connection with server: %s"), + purple_connection_error_reason(gc, + PURPLE_CONNECTION_ERROR_NETWORK_ERROR, + return G_SOURCE_REMOVE; + } else if(ret < length) { - if (ret < 0 && errno == EAGAIN)
- PurpleConnection *gc = purple_account_get_connection(irc->account);
- gchar *tmp = g_strdup_printf(_("Lost connection with server: %s"),
- purple_connection_error_reason (gc,
- PURPLE_CONNECTION_ERROR_NETWORK_ERROR, tmp);
+ /* The preceeding conditional allows EAGAIN to fall through to + * here so that we can retransmit it. There shouldn't even be a + * case where ret < 0 and != EAGAIN, which is why we have the + g_assert_not_reached(); + /* We need to move past the characters we already wrote and requeue + * the rest of the string. We know ret is less than length, so the + * starting address of msg plus ret can never get outside of the + * string, and likewise, length minus ret will always be < length + * because ret is less than length and if it was somehow negative, + * it has been reset to zero. + partial = g_strndup(msg + ret, length - ret); + /* requeue the item to the start of the queue */ + g_queue_push_head(irc->send_queue, partial); + irc->sent_partial = TRUE; + /* We successfully sent a message so decrement the counter. */ + irc->sent_partial = FALSE; + /* Message was processed successfully or a partial message was + * allocated and requeued so we can free the one we popped off. - purple_circ_buffer_mark_read(irc->outbuf, ret);
- /* We *could* try to write more if we wrote it all */
- if (ret == write_len) {
- irc_send_cb(data, source, cond);
+ return G_SOURCE_CONTINUE; -int irc_send(struct irc_conn *irc, const char *buf)
+void irc_send(struct irc_conn *irc, const char *buf) return irc_send_len(irc, buf, strlen(buf));
-int irc_send_len(struct irc_conn *irc, const char *buf, int buflen)
- char *tosend = g_strdup(buf);
+irc_send_len(struct irc_conn *irc, const char *buf, int buflen) { + char *tosend = g_strdup(buf); purple_signal_emit(_irc_plugin, "irc-sending-text", purple_account_get_connection(irc->account), &tosend);
- if (!purple_strequal(tosend, buf)) {
- buflen = strlen(tosend);
- if (purple_debug_is_verbose()) {
+ if(purple_debug_is_verbose()) { char *clean = purple_utf8_salvage(tosend);
clean = g_strstrip(clean);
purple_debug_misc("irc", "<< %s\n", clean);
- /* If we're not buffering writes, try to send immediately */
- ret = do_send(irc, tosend, buflen);
- /* purple_debug(PURPLE_DEBUG_MISC, "irc", "sent%s: %s",
- irc->gsc ? " (ssl)" : "", tosend); */
- if (ret <= 0 && errno != EAGAIN) {
- PurpleConnection *gc = purple_account_get_connection(irc->account);
- gchar *tmp = g_strdup_printf(_("Lost connection with server: %s"),
- purple_connection_error_reason (gc,
- PURPLE_CONNECTION_ERROR_NETWORK_ERROR, tmp);
- } else if (ret < buflen) {
- irc->writeh = purple_input_add(
- irc->gsc ? irc->gsc->fd : irc->fd,
- PURPLE_INPUT_WRITE, irc_send_cb, irc);
- purple_circ_buffer_append(irc->outbuf, tosend + ret,
+ g_queue_push_tail(irc->send_queue, tosend); /* XXX I don't like messing directly with these buddies */
@@ -357,7 +396,9 @@
gc->proto_data = irc = g_new0(struct irc_conn, 1);
- irc->outbuf = purple_circ_buffer_new(512);
+ irc->send_queue = g_queue_new(); + irc->sent_partial = FALSE; userparts = g_strsplit(username, "@", 2);
purple_connection_set_display_name(gc, userparts[0]);
@@ -406,6 +447,7 @@
const char *nickname, *identname, *realname;
struct irc_conn *irc = gc->proto_data;
const char *pass = purple_connection_get_password(gc);
const gboolean use_sasl = purple_account_get_bool(irc->account, "sasl", FALSE);
@@ -417,7 +459,7 @@
else /* intended to fall through */
buf = irc_format(irc, "v:", "PASS", pass);
- if (irc_send(irc, buf) < 0) {
+ if (do_send(irc, buf, strlen(buf)) < 0) { @@ -450,7 +492,7 @@
strlen(realname) ? realname : nickname);
- if (irc_send(irc, buf) < 0) {
+ if (do_send(irc, buf, strlen(buf)) < 0) { @@ -459,7 +501,7 @@
buf = irc_format(irc, "vn", "NICK", nickname);
irc->reqnick = g_strdup(nickname);
- if (irc_send(irc, buf) < 0) {
+ if (do_send(irc, buf, strlen(buf)) < 0) { @@ -467,6 +509,15 @@
irc->recv_time = time(NULL);
+ /* Give ourselves one full burst for startup commands. */ + interval = purple_account_get_int(irc->account, "ratelimit-interval", + IRC_DEFAULT_COMMAND_INTERVAL); + burst = purple_account_get_int(irc->account, "ratelimit-burst", + IRC_DEFAULT_COMMAND_MAX_BURST); + irc->send_time = time(NULL) - (interval * burst); + irc->send_handler = g_timeout_add_seconds(1, irc_send_handler_cb, irc); @@ -541,10 +592,10 @@
g_string_free(irc->motd, TRUE);
- purple_input_remove(irc->writeh);
- purple_circ_buffer_destroy(irc->outbuf);
+ g_queue_free_full(irc->send_queue, g_free); + if(irc->send_handler != 0) { + g_source_remove(irc->send_handler); @@ -1108,6 +1159,16 @@
prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);
+ option = purple_account_option_int_new(_("Seconds between sending messages"), + IRC_DEFAULT_COMMAND_INTERVAL); + prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option); + option = purple_account_option_int_new(_("Maximum messages to send at once"), + IRC_DEFAULT_COMMAND_MAX_BURST); + prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option); purple_prefs_remove("/plugins/prpl/irc/quitmsg");
--- a/libpurple/protocols/irc/irc.h Sat Apr 23 05:05:15 2022 -0500
+++ b/libpurple/protocols/irc/irc.h Sat Apr 23 05:05:54 2022 -0500
@@ -29,7 +29,6 @@
@@ -43,6 +42,13 @@
#define IRC_DEFAULT_QUIT "Leaving."
+/* By default set the command send interval to 2 seconds and allow bursting of + * 5 commands at once. This means, if we haven't sent a command in 10 seconds + * we can send 5 commands immediately with no penalty. +#define IRC_DEFAULT_COMMAND_INTERVAL 2 +#define IRC_DEFAULT_COMMAND_MAX_BURST 5 #define IRC_BUFSIZE_INCREMENT 1024
#define IRC_MAX_BUFSIZE 16384
@@ -92,10 +98,12 @@
- PurpleCircBuffer *outbuf;
@@ -119,8 +127,9 @@
typedef int (*IRCCmdCallback) (struct irc_conn *irc, const char *cmd, const char *target, const char **args);
-int irc_send(struct irc_conn *irc, const char *buf);
-int irc_send_len(struct irc_conn *irc, const char *buf, int len);
+void irc_send(struct irc_conn *irc, const char *buf); +void irc_send_len(struct irc_conn *irc, const char *buf, int len); +void irc_priority_send(struct irc_conn *irc, const char *buf); gboolean irc_blist_timeout(struct irc_conn *irc);
gboolean irc_who_channel_timeout(struct irc_conn *irc);
void irc_buddy_query(struct irc_conn *irc);
--- a/libpurple/protocols/irc/msgs.c Sat Apr 23 05:05:15 2022 -0500
+++ b/libpurple/protocols/irc/msgs.c Sat Apr 23 05:05:54 2022 -0500
@@ -1256,7 +1256,7 @@
buf = irc_format(irc, "v:", "PONG", args[0]);
+ irc_priority_send(irc, buf);