libpurple/protocols/irc/irc.c

branch
release-2.x.y
changeset 41335
c49dcf00bee6
parent 41333
ed2b25ccdf2c
child 41339
3230f4408394
equal deleted inserted replaced
41334:b6896c389190 41335:c49dcf00bee6
27 27
28 #include "accountopt.h" 28 #include "accountopt.h"
29 #include "blist.h" 29 #include "blist.h"
30 #include "conversation.h" 30 #include "conversation.h"
31 #include "debug.h" 31 #include "debug.h"
32 #include "glibcompat.h"
32 #include "notify.h" 33 #include "notify.h"
33 #include "prpl.h" 34 #include "prpl.h"
34 #include "plugin.h" 35 #include "plugin.h"
35 #include "util.h" 36 #include "util.h"
36 #include "version.h" 37 #include "version.h"
93 ret = purple_ssl_write(irc->gsc, buf, len); 94 ret = purple_ssl_write(irc->gsc, buf, len);
94 } else { 95 } else {
95 ret = write(irc->fd, buf, len); 96 ret = write(irc->fd, buf, len);
96 } 97 }
97 98
99 irc->send_time = time(NULL);
100
98 return ret; 101 return ret;
102 }
103
104 void irc_priority_send(struct irc_conn *irc, const char *buf)
105 {
106 if(irc->sent_partial) {
107 g_queue_insert_after(irc->send_queue, irc->send_queue->head,
108 g_strdup(buf));
109 } else {
110 do_send(irc, buf, strlen(buf));
111 }
99 } 112 }
100 113
101 static int irc_send_raw(PurpleConnection *gc, const char *buf, int len) 114 static int irc_send_raw(PurpleConnection *gc, const char *buf, int len)
102 { 115 {
103 struct irc_conn *irc = (struct irc_conn*)gc->proto_data; 116 struct irc_conn *irc = (struct irc_conn*)gc->proto_data;
106 } 119 }
107 irc_send_len(irc, buf, len); 120 irc_send_len(irc, buf, len);
108 return len; 121 return len;
109 } 122 }
110 123
111 static void 124 static gboolean
112 irc_send_cb(gpointer data, gint source, PurpleInputCondition cond) 125 irc_send_handler_cb(gpointer data) {
113 { 126 struct irc_conn *irc = (struct irc_conn *)data;
114 struct irc_conn *irc = data; 127 gint available = 0;
115 int ret, writelen; 128 gint interval = 0;
116 129
117 writelen = purple_circ_buffer_get_max_read(irc->outbuf); 130 interval = purple_account_get_int(irc->account, "ratelimit-interval",
118 131 IRC_DEFAULT_COMMAND_INTERVAL);
119 if (writelen == 0) { 132
120 purple_input_remove(irc->writeh); 133 /* Check if we're enabled. */
121 irc->writeh = 0; 134 if(interval < 1) {
122 return; 135 available = G_MAXINT;
123 } 136 } else {
124 137 gint burst = purple_account_get_int(irc->account, "ratelimit-burst",
125 ret = do_send(irc, irc->outbuf->outptr, writelen); 138 IRC_DEFAULT_COMMAND_MAX_BURST);
126 139 available = (time(NULL) - irc->send_time) / interval;
127 if (ret < 0 && errno == EAGAIN) 140 if(available > burst) {
128 return; 141 available = burst;
129 else if (ret <= 0) { 142 }
130 PurpleConnection *gc = purple_account_get_connection(irc->account); 143 }
131 gchar *tmp = g_strdup_printf(_("Lost connection with server: %s"), 144
132 g_strerror(errno)); 145 while(available > 0) {
133 purple_connection_error_reason (gc, 146 gchar *msg = NULL;
134 PURPLE_CONNECTION_ERROR_NETWORK_ERROR, tmp); 147 gpointer raw = NULL;
135 g_free(tmp); 148 gint length = 0, ret = 0;
136 return; 149
137 } 150 /* No message in the queue should be NULL, so a NULL value means the
138 151 * queue is empty.
139 purple_circ_buffer_mark_read(irc->outbuf, ret); 152 */
140 153 raw = g_queue_pop_head(irc->send_queue);
141 #if 0 154 if(raw == NULL) {
142 /* We *could* try to write more if we wrote it all */ 155 break;
143 if (ret == write_len) { 156 }
144 irc_send_cb(data, source, cond); 157
145 } 158 msg = (gchar *)raw;
146 #endif 159 length = strlen(msg);
147 } 160
148 161 ret = do_send(irc, msg, length);
149 int irc_send(struct irc_conn *irc, const char *buf) 162 if(ret <= 0 && errno != EAGAIN) {
163 PurpleConnection *gc = purple_account_get_connection(irc->account);
164 gchar *tmp = g_strdup_printf(_("Lost connection with server: %s"),
165 g_strerror(errno));
166
167 purple_connection_error_reason(gc,
168 PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
169 tmp);
170 g_free(tmp);
171
172 g_free(msg);
173
174 irc->send_handler = 0;
175
176 return G_SOURCE_REMOVE;
177 } else if(ret < length) {
178 gchar *partial = NULL;
179
180 /* The preceeding conditional allows EAGAIN to fall through to
181 * here so that we can retransmit it. There shouldn't even be a
182 * case where ret < 0 and != EAGAIN, which is why we have the
183 * assert.
184 */
185 if(ret < 0) {
186 if(ret == EAGAIN) {
187 ret = 0;
188 } else {
189 g_assert_not_reached();
190 }
191 }
192
193 /* We need to move past the characters we already wrote and requeue
194 * the rest of the string. We know ret is less than length, so the
195 * starting address of msg plus ret can never get outside of the
196 * string, and likewise, length minus ret will always be < length
197 * because ret is less than length and if it was somehow negative,
198 * it has been reset to zero.
199 */
200 partial = g_strndup(msg + ret, length - ret);
201
202 /* requeue the item to the start of the queue */
203 g_queue_push_head(irc->send_queue, partial);
204 irc->sent_partial = TRUE;
205 } else {
206 /* We successfully sent a message so decrement the counter. */
207 available -= 1;
208 irc->sent_partial = FALSE;
209 }
210
211 /* Message was processed successfully or a partial message was
212 * allocated and requeued so we can free the one we popped off.
213 */
214 g_free(msg);
215 }
216
217 return G_SOURCE_CONTINUE;
218 }
219
220 void irc_send(struct irc_conn *irc, const char *buf)
150 { 221 {
151 return irc_send_len(irc, buf, strlen(buf)); 222 return irc_send_len(irc, buf, strlen(buf));
152 } 223 }
153 224
154 int irc_send_len(struct irc_conn *irc, const char *buf, int buflen) 225 void
155 { 226 irc_send_len(struct irc_conn *irc, const char *buf, int buflen) {
156 int ret; 227 char *tosend = g_strdup(buf);
157 char *tosend = g_strdup(buf);
158 228
159 purple_signal_emit(_irc_plugin, "irc-sending-text", purple_account_get_connection(irc->account), &tosend); 229 purple_signal_emit(_irc_plugin, "irc-sending-text", purple_account_get_connection(irc->account), &tosend);
160 230
161 if (tosend == NULL) 231 if(tosend == NULL) {
162 return 0; 232 return;
163 233 }
164 if (!purple_strequal(tosend, buf)) { 234
165 buflen = strlen(tosend); 235 if(purple_debug_is_verbose()) {
166 }
167
168 if (purple_debug_is_verbose()) {
169 char *clean = purple_utf8_salvage(tosend); 236 char *clean = purple_utf8_salvage(tosend);
170 clean = g_strstrip(clean); 237 clean = g_strstrip(clean);
171 purple_debug_misc("irc", "<< %s\n", clean); 238 purple_debug_misc("irc", "<< %s\n", clean);
172 g_free(clean); 239 g_free(clean);
173 } 240 }
174 241
175 /* If we're not buffering writes, try to send immediately */ 242 g_queue_push_tail(irc->send_queue, tosend);
176 if (!irc->writeh)
177 ret = do_send(irc, tosend, buflen);
178 else {
179 ret = -1;
180 errno = EAGAIN;
181 }
182
183 /* purple_debug(PURPLE_DEBUG_MISC, "irc", "sent%s: %s",
184 irc->gsc ? " (ssl)" : "", tosend); */
185 if (ret <= 0 && errno != EAGAIN) {
186 PurpleConnection *gc = purple_account_get_connection(irc->account);
187 gchar *tmp = g_strdup_printf(_("Lost connection with server: %s"),
188 g_strerror(errno));
189 purple_connection_error_reason (gc,
190 PURPLE_CONNECTION_ERROR_NETWORK_ERROR, tmp);
191 g_free(tmp);
192 } else if (ret < buflen) {
193 if (ret < 0)
194 ret = 0;
195 if (!irc->writeh)
196 irc->writeh = purple_input_add(
197 irc->gsc ? irc->gsc->fd : irc->fd,
198 PURPLE_INPUT_WRITE, irc_send_cb, irc);
199 purple_circ_buffer_append(irc->outbuf, tosend + ret,
200 buflen - ret);
201 }
202 g_free(tosend);
203 return ret;
204 } 243 }
205 244
206 /* XXX I don't like messing directly with these buddies */ 245 /* XXX I don't like messing directly with these buddies */
207 gboolean irc_blist_timeout(struct irc_conn *irc) 246 gboolean irc_blist_timeout(struct irc_conn *irc)
208 { 247 {
355 } 394 }
356 395
357 gc->proto_data = irc = g_new0(struct irc_conn, 1); 396 gc->proto_data = irc = g_new0(struct irc_conn, 1);
358 irc->fd = -1; 397 irc->fd = -1;
359 irc->account = account; 398 irc->account = account;
360 irc->outbuf = purple_circ_buffer_new(512); 399
400 irc->send_queue = g_queue_new();
401 irc->sent_partial = FALSE;
361 402
362 userparts = g_strsplit(username, "@", 2); 403 userparts = g_strsplit(username, "@", 2);
363 purple_connection_set_display_name(gc, userparts[0]); 404 purple_connection_set_display_name(gc, userparts[0]);
364 irc->server = g_strdup(userparts[1]); 405 irc->server = g_strdup(userparts[1]);
365 g_strfreev(userparts); 406 g_strfreev(userparts);
404 char *buf, *tmp = NULL; 445 char *buf, *tmp = NULL;
405 char *server; 446 char *server;
406 const char *nickname, *identname, *realname; 447 const char *nickname, *identname, *realname;
407 struct irc_conn *irc = gc->proto_data; 448 struct irc_conn *irc = gc->proto_data;
408 const char *pass = purple_connection_get_password(gc); 449 const char *pass = purple_connection_get_password(gc);
450 gint interval, burst;
409 #ifdef HAVE_CYRUS_SASL 451 #ifdef HAVE_CYRUS_SASL
410 const gboolean use_sasl = purple_account_get_bool(irc->account, "sasl", FALSE); 452 const gboolean use_sasl = purple_account_get_bool(irc->account, "sasl", FALSE);
411 #endif 453 #endif
412 454
413 if (pass && *pass) { 455 if (pass && *pass) {
415 if (use_sasl) 457 if (use_sasl)
416 buf = irc_format(irc, "vv:", "CAP", "REQ", "sasl"); 458 buf = irc_format(irc, "vv:", "CAP", "REQ", "sasl");
417 else /* intended to fall through */ 459 else /* intended to fall through */
418 #endif 460 #endif
419 buf = irc_format(irc, "v:", "PASS", pass); 461 buf = irc_format(irc, "v:", "PASS", pass);
420 if (irc_send(irc, buf) < 0) { 462 if (do_send(irc, buf, strlen(buf)) < 0) {
421 g_free(buf); 463 g_free(buf);
422 return FALSE; 464 return FALSE;
423 } 465 }
424 g_free(buf); 466 g_free(buf);
425 } 467 }
448 490
449 buf = irc_format(irc, "vvvv:", "USER", tmp ? tmp : identname, "*", server, 491 buf = irc_format(irc, "vvvv:", "USER", tmp ? tmp : identname, "*", server,
450 strlen(realname) ? realname : nickname); 492 strlen(realname) ? realname : nickname);
451 g_free(tmp); 493 g_free(tmp);
452 g_free(server); 494 g_free(server);
453 if (irc_send(irc, buf) < 0) { 495 if (do_send(irc, buf, strlen(buf)) < 0) {
454 g_free(buf); 496 g_free(buf);
455 return FALSE; 497 return FALSE;
456 } 498 }
457 g_free(buf); 499 g_free(buf);
458 500
459 buf = irc_format(irc, "vn", "NICK", nickname); 501 buf = irc_format(irc, "vn", "NICK", nickname);
460 irc->reqnick = g_strdup(nickname); 502 irc->reqnick = g_strdup(nickname);
461 irc->nickused = FALSE; 503 irc->nickused = FALSE;
462 if (irc_send(irc, buf) < 0) { 504 if (do_send(irc, buf, strlen(buf)) < 0) {
463 g_free(buf); 505 g_free(buf);
464 return FALSE; 506 return FALSE;
465 } 507 }
466 g_free(buf); 508 g_free(buf);
467 509
468 irc->recv_time = time(NULL); 510 irc->recv_time = time(NULL);
511
512 /* Give ourselves one full burst for startup commands. */
513 interval = purple_account_get_int(irc->account, "ratelimit-interval",
514 IRC_DEFAULT_COMMAND_INTERVAL);
515 burst = purple_account_get_int(irc->account, "ratelimit-burst",
516 IRC_DEFAULT_COMMAND_MAX_BURST);
517
518 irc->send_time = time(NULL) - (interval * burst);
519 irc->send_handler = g_timeout_add_seconds(1, irc_send_handler_cb, irc);
469 520
470 return TRUE; 521 return TRUE;
471 } 522 }
472 523
473 static void irc_login_cb_ssl(gpointer data, PurpleSslConnection *gsc, 524 static void irc_login_cb_ssl(gpointer data, PurpleSslConnection *gsc,
539 g_hash_table_destroy(irc->buddies); 590 g_hash_table_destroy(irc->buddies);
540 if (irc->motd) 591 if (irc->motd)
541 g_string_free(irc->motd, TRUE); 592 g_string_free(irc->motd, TRUE);
542 g_free(irc->server); 593 g_free(irc->server);
543 594
544 if (irc->writeh) 595 g_queue_free_full(irc->send_queue, g_free);
545 purple_input_remove(irc->writeh); 596 if(irc->send_handler != 0) {
546 597 g_source_remove(irc->send_handler);
547 purple_circ_buffer_destroy(irc->outbuf); 598 }
548 599
549 g_free(irc->mode_chars); 600 g_free(irc->mode_chars);
550 g_free(irc->reqnick); 601 g_free(irc->reqnick);
551 602
552 #ifdef HAVE_CYRUS_SASL 603 #ifdef HAVE_CYRUS_SASL
1106 _("Allow plaintext SASL auth over unencrypted connection"), 1157 _("Allow plaintext SASL auth over unencrypted connection"),
1107 "auth_plain_in_clear", FALSE); 1158 "auth_plain_in_clear", FALSE);
1108 prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option); 1159 prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);
1109 #endif 1160 #endif
1110 1161
1162 option = purple_account_option_int_new(_("Seconds between sending messages"),
1163 "ratelimit-interval",
1164 IRC_DEFAULT_COMMAND_INTERVAL);
1165 prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);
1166
1167 option = purple_account_option_int_new(_("Maximum messages to send at once"),
1168 "ratelimit-burst",
1169 IRC_DEFAULT_COMMAND_MAX_BURST);
1170 prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);
1171
1111 _irc_plugin = plugin; 1172 _irc_plugin = plugin;
1112 1173
1113 purple_prefs_remove("/plugins/prpl/irc/quitmsg"); 1174 purple_prefs_remove("/plugins/prpl/irc/quitmsg");
1114 purple_prefs_remove("/plugins/prpl/irc"); 1175 purple_prefs_remove("/plugins/prpl/irc");
1115 1176

mercurial