# HG changeset patch # User Daniel Atallah # Date 1139458676 0 # Node ID 33bef17125c211beaaed22325faa16b1582b37a5 # Parent d8f238864c88940fbf68322ed8d9421e15f19a8a [gaim-migrate @ 15563] This is the soon-to-be-infamous nonblocking network activity patch that I've been working on. Feel free to yell at me if this makes you unhappy. committer: Tailor Script diff -r d8f238864c88 -r 33bef17125c2 plugins/ChangeLog.API --- a/plugins/ChangeLog.API Thu Feb 09 04:14:54 2006 +0000 +++ b/plugins/ChangeLog.API Thu Feb 09 04:17:56 2006 +0000 @@ -109,6 +109,9 @@ * gaim_plugin_pref_add_choice(): label is now const char * * struct proto_chat_entry: label is now const char * * struct proto_chat_entry: identifier is now const char * + * All network activity has been updated to use non-blocking sockets. + This means that plugins must be updated to expect such a socket from + gaim_proxy_connect() and gaim_network_listen*(). Removed: * gaim_gtk_sound_{get,set}_mute() (replaced by the /gaim/gtk/sound/mute diff -r d8f238864c88 -r 33bef17125c2 plugins/ssl/ssl-gnutls.c --- a/plugins/ssl/ssl-gnutls.c Thu Feb 09 04:14:54 2006 +0000 +++ b/plugins/ssl/ssl-gnutls.c Thu Feb 09 04:17:56 2006 +0000 @@ -34,7 +34,7 @@ typedef struct { gnutls_session session; - + guint handshake_handler; } GaimSslGnutlsData; #define GAIM_SSL_GNUTLS_DATA(gsc) ((GaimSslGnutlsData *)gsc->private_data) @@ -48,7 +48,7 @@ gnutls_certificate_allocate_credentials(&xcred); gnutls_certificate_set_x509_trust_file(xcred, "ca.pem", - GNUTLS_X509_FMT_PEM); + GNUTLS_X509_FMT_PEM); } static gboolean @@ -65,15 +65,48 @@ gnutls_certificate_free_credentials(xcred); } + +static void ssl_gnutls_handshake_cb(gpointer data, gint source, + GaimInputCondition cond) +{ + GaimSslConnection *gsc = data; + GaimSslGnutlsData *gnutls_data = GAIM_SSL_GNUTLS_DATA(gsc); + ssize_t ret; + + gaim_debug_info("gnutls", "Handshaking\n"); + ret = gnutls_handshake(gnutls_data->session); + + if(ret == GNUTLS_E_AGAIN || ret == GNUTLS_E_INTERRUPTED) + return; + + gaim_input_remove(gnutls_data->handshake_handler); + gnutls_data->handshake_handler = 0; + + if(ret != 0) { + gaim_debug_error("gnutls", "Handshake failed. Error %d\n", ret); + + if(gsc->error_cb != NULL) + gsc->error_cb(gsc, GAIM_SSL_HANDSHAKE_FAILED, + gsc->connect_cb_data); + + gaim_ssl_close(gsc); + } else { + gaim_debug_info("gnutls", "Handshake complete\n"); + + gsc->connect_cb(gsc->connect_cb_data, gsc, cond); + } + +} + + static void ssl_gnutls_connect_cb(gpointer data, gint source, GaimInputCondition cond) { GaimSslConnection *gsc = (GaimSslConnection *)data; GaimSslGnutlsData *gnutls_data; static const int cert_type_priority[2] = { GNUTLS_CRT_X509, 0 }; - int ret; - if (source < 0) { + if(source < 0) { if(gsc->error_cb != NULL) gsc->error_cb(gsc, GAIM_SSL_CONNECT_FAILED, gsc->connect_cb_data); @@ -90,37 +123,17 @@ gnutls_set_default_priority(gnutls_data->session); gnutls_certificate_type_set_priority(gnutls_data->session, - cert_type_priority); + cert_type_priority); gnutls_credentials_set(gnutls_data->session, GNUTLS_CRD_CERTIFICATE, - xcred); + xcred); gnutls_transport_set_ptr(gnutls_data->session, GINT_TO_POINTER(source)); - - do - { - gaim_debug_info("gnutls", "Handshaking\n"); - ret = gnutls_handshake(gnutls_data->session); - } - while ((ret == GNUTLS_E_AGAIN) || (ret == GNUTLS_E_INTERRUPTED)); - - if (ret < 0) - { - gaim_debug_error("gnutls", "Handshake failed. Error %d\n", ret); + gnutls_data->handshake_handler = gaim_input_add(gsc->fd, + GAIM_INPUT_READ, ssl_gnutls_handshake_cb, gsc); - if (gsc->error_cb != NULL) - gsc->error_cb(gsc, GAIM_SSL_HANDSHAKE_FAILED, - gsc->connect_cb_data); - - gaim_ssl_close(gsc); - } - else - { - gaim_debug_info("gnutls", "Handshake complete\n"); - - gsc->connect_cb(gsc->connect_cb_data, gsc, cond); - } + ssl_gnutls_handshake_cb(gsc, gsc->fd, GAIM_INPUT_READ); } static void @@ -131,27 +144,29 @@ if(!gnutls_data) return; + if(gnutls_data->handshake_handler) + gaim_input_remove(gnutls_data->handshake_handler); + gnutls_bye(gnutls_data->session, GNUTLS_SHUT_RDWR); gnutls_deinit(gnutls_data->session); g_free(gnutls_data); + gsc->private_data = NULL; } static size_t ssl_gnutls_read(GaimSslConnection *gsc, void *data, size_t len) { GaimSslGnutlsData *gnutls_data = GAIM_SSL_GNUTLS_DATA(gsc); - int s; + ssize_t s; + + s = gnutls_record_recv(gnutls_data->session, data, len); - do - { - s = gnutls_record_recv(gnutls_data->session, data, len); - } - while ((s == GNUTLS_E_AGAIN) || (s == GNUTLS_E_INTERRUPTED)); - - if (s < 0) - { + if(s == GNUTLS_E_AGAIN || s == GNUTLS_E_INTERRUPTED) { + s = -1; + errno = EAGAIN; + } else if(s < 0) { gaim_debug_error("gnutls", "receive failed: %d\n", s); s = 0; } @@ -163,11 +178,20 @@ ssl_gnutls_write(GaimSslConnection *gsc, const void *data, size_t len) { GaimSslGnutlsData *gnutls_data = GAIM_SSL_GNUTLS_DATA(gsc); - size_t s = 0; + ssize_t s = 0; + /* XXX: when will gnutls_data be NULL? */ if(gnutls_data) s = gnutls_record_send(gnutls_data->session, data, len); + if(s == GNUTLS_E_AGAIN || s == GNUTLS_E_INTERRUPTED) { + s = -1; + errno = EAGAIN; + } else if(s < 0) { + gaim_debug_error("gnutls", "send failed: %d\n", s); + s = 0; + } + return s; } @@ -187,7 +211,7 @@ plugin_load(GaimPlugin *plugin) { #ifdef HAVE_GNUTLS - if (!gaim_ssl_get_ops()) { + if(!gaim_ssl_get_ops()) { gaim_ssl_set_ops(&ssl_ops); } @@ -204,7 +228,7 @@ plugin_unload(GaimPlugin *plugin) { #ifdef HAVE_GNUTLS - if (gaim_ssl_get_ops() == &ssl_ops) { + if(gaim_ssl_get_ops() == &ssl_ops) { gaim_ssl_set_ops(NULL); } #endif diff -r d8f238864c88 -r 33bef17125c2 plugins/ssl/ssl-nss.c --- a/plugins/ssl/ssl-nss.c Thu Feb 09 04:14:54 2006 +0000 +++ b/plugins/ssl/ssl-nss.c Thu Feb 09 04:17:56 2006 +0000 @@ -46,6 +46,7 @@ { PRFileDesc *fd; PRFileDesc *in; + guint handshake_handler; } GaimSslNssData; @@ -54,6 +55,54 @@ static const PRIOMethods *_nss_methods = NULL; static PRDescIdentity _identity; +/* Thank you, Evolution */ +static void +set_errno(int code) +{ + /* FIXME: this should handle more. */ + switch (code) { + case PR_INVALID_ARGUMENT_ERROR: + errno = EINVAL; + break; + case PR_PENDING_INTERRUPT_ERROR: + errno = EINTR; + break; + case PR_IO_PENDING_ERROR: + errno = EAGAIN; + break; + case PR_WOULD_BLOCK_ERROR: + errno = EAGAIN; + /*errno = EWOULDBLOCK; */ + break; + case PR_IN_PROGRESS_ERROR: + errno = EINPROGRESS; + break; + case PR_ALREADY_INITIATED_ERROR: + errno = EALREADY; + break; + case PR_NETWORK_UNREACHABLE_ERROR: + errno = EHOSTUNREACH; + break; + case PR_CONNECT_REFUSED_ERROR: + errno = ECONNREFUSED; + break; + case PR_CONNECT_TIMEOUT_ERROR: + case PR_IO_TIMEOUT_ERROR: + errno = ETIMEDOUT; + break; + case PR_NOT_CONNECTED_ERROR: + errno = ENOTCONN; + break; + case PR_CONNECT_RESET_ERROR: + errno = ECONNRESET; + break; + case PR_IO_ERROR: + default: + errno = EIO; + break; + } +} + static void ssl_nss_init_nss(void) { @@ -158,6 +207,36 @@ } static void +ssl_nss_handshake_cb(gpointer data, int fd, GaimInputCondition cond) +{ + GaimSslConnection *gsc = (GaimSslConnection *)data; + GaimSslNssData *nss_data = gsc->private_data; + + /* I don't think this the best way to do this... + * It seems to work because it'll eventually use the cached value + */ + if(SSL_ForceHandshake(nss_data->in) != SECSuccess) { + set_errno(PR_GetError()); + if (errno == EAGAIN || errno == EWOULDBLOCK) + return; + + gaim_debug_error("nss", "Handshake failed %u\n", PR_GetError()); + + if (gsc->error_cb != NULL) + gsc->error_cb(gsc, GAIM_SSL_HANDSHAKE_FAILED, gsc->connect_cb_data); + + gaim_ssl_close(gsc); + + return; + } + + gaim_input_remove(nss_data->handshake_handler); + nss_data->handshake_handler = 0; + + gsc->connect_cb(gsc->connect_cb_data, gsc, cond); +} + +static void ssl_nss_connect_cb(gpointer data, gint source, GaimInputCondition cond) { GaimSslConnection *gsc = (GaimSslConnection *)data; @@ -183,9 +262,10 @@ } socket_opt.option = PR_SockOpt_Nonblocking; - socket_opt.value.non_blocking = PR_FALSE; + socket_opt.value.non_blocking = PR_TRUE; - PR_SetSocketOption(nss_data->fd, &socket_opt); + if (PR_SetSocketOption(nss_data->fd, &socket_opt) != PR_SUCCESS) + gaim_debug_warning("nss", "unable to set socket into non-blocking mode: %u\n", PR_GetError()); nss_data->in = SSL_ImportFD(NULL, nss_data->fd); @@ -212,21 +292,17 @@ if(gsc->host) SSL_SetURL(nss_data->in, gsc->host); +#if 0 /* This seems like it'd the be the correct way to implement the nonblocking stuff, + but it doesn't seem to work */ + SSL_HandshakeCallback(nss_data->in, + (SSLHandshakeCallback) ssl_nss_handshake_cb, gsc); +#endif SSL_ResetHandshake(nss_data->in, PR_FALSE); - if (SSL_ForceHandshake(nss_data->in)) - { - gaim_debug_error("nss", "Handshake failed\n"); - - if (gsc->error_cb != NULL) - gsc->error_cb(gsc, GAIM_SSL_HANDSHAKE_FAILED, gsc->connect_cb_data); + nss_data->handshake_handler = gaim_input_add(gsc->fd, + GAIM_INPUT_READ, ssl_nss_handshake_cb, gsc); - gaim_ssl_close(gsc); - - return; - } - - gsc->connect_cb(gsc->connect_cb_data, gsc, cond); + ssl_nss_handshake_cb(gsc, gsc->fd, GAIM_INPUT_READ); } static void @@ -240,26 +316,42 @@ if (nss_data->in) PR_Close(nss_data->in); /* if (nss_data->fd) PR_Close(nss_data->fd); */ + if (nss_data->handshake_handler) + gaim_input_remove(nss_data->handshake_handler); + g_free(nss_data); + gsc->private_data = NULL; } static size_t ssl_nss_read(GaimSslConnection *gsc, void *data, size_t len) { + ssize_t ret; GaimSslNssData *nss_data = GAIM_SSL_NSS_DATA(gsc); - return PR_Read(nss_data->in, data, len); + ret = PR_Read(nss_data->in, data, len); + + if (ret == -1) + set_errno(PR_GetError()); + + return ret; } static size_t ssl_nss_write(GaimSslConnection *gsc, const void *data, size_t len) { + ssize_t ret; GaimSslNssData *nss_data = GAIM_SSL_NSS_DATA(gsc); if(!nss_data) return 0; - return PR_Write(nss_data->in, data, len); + ret = PR_Write(nss_data->in, data, len); + + if (ret == -1) + set_errno(PR_GetError()); + + return ret; } static GaimSslOps ssl_ops = diff -r d8f238864c88 -r 33bef17125c2 src/Makefile.am --- a/src/Makefile.am Thu Feb 09 04:14:54 2006 +0000 +++ b/src/Makefile.am Thu Feb 09 04:17:56 2006 +0000 @@ -80,6 +80,7 @@ desktopitem.c \ eventloop.c \ ft.c \ + gaim_buffer.c \ idle.c \ imgstore.c \ log.c \ @@ -128,6 +129,7 @@ desktopitem.h \ eventloop.h \ ft.h \ + gaim_buffer.h \ idle.h \ imgstore.h \ log.h \ diff -r d8f238864c88 -r 33bef17125c2 src/Makefile.mingw --- a/src/Makefile.mingw Thu Feb 09 04:14:54 2006 +0000 +++ b/src/Makefile.mingw Thu Feb 09 04:17:56 2006 +0000 @@ -94,6 +94,7 @@ dnssrv.c \ eventloop.c \ ft.c \ + gaim_buffer.c \ gtkaccount.c \ gtkblist.c \ gtkconn.c \ diff -r d8f238864c88 -r 33bef17125c2 src/gaim_buffer.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/gaim_buffer.c Thu Feb 09 04:17:56 2006 +0000 @@ -0,0 +1,138 @@ +/* + * @file gaim_buffer.h Buffer Utility Functions + * @ingroup core + * + * Gaim is the legal property of its developers, whose names are too numerous + * to list here. Please refer to the COPYRIGHT file distributed with this + * source distribution. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#include "internal.h" + +#include "gaim_buffer.h" + +#define DEFAULT_BUF_SIZE 256 + +GaimCircBuffer * +gaim_circ_buffer_new(gsize growsize) { + GaimCircBuffer *buf = g_new0(GaimCircBuffer, 1); + buf->growsize = growsize ? growsize : DEFAULT_BUF_SIZE; + return buf; +} + +void gaim_circ_buffer_destroy(GaimCircBuffer *buf) { + g_free(buf->buffer); + g_free(buf); +} + +static void grow_circ_buffer(GaimCircBuffer *buf, gsize len) { + int in_offset = 0, out_offset = 0; + int start_buflen = buf->buflen; + + while ((buf->buflen - buf->bufused) < len) + buf->buflen += buf->growsize; + + if (buf->inptr != NULL) { + in_offset = buf->inptr - buf->buffer; + out_offset = buf->outptr - buf->buffer; + } + buf->buffer = g_realloc(buf->buffer, buf->buflen); + + /* adjust the fill and remove pointer locations */ + if (buf->inptr == NULL) { + buf->inptr = buf->outptr = buf->buffer; + } else { + buf->inptr = buf->buffer + in_offset; + buf->outptr = buf->buffer + out_offset; + } + + /* If the fill pointer is wrapped to before the remove + * pointer, we need to shift the data */ + if (in_offset < out_offset) { + int shift_n = MIN(buf->buflen - start_buflen, + in_offset); + memcpy(buf->buffer + start_buflen, buf->buffer, + shift_n); + + /* If we couldn't fit the wrapped read buffer + * at the end */ + if (shift_n < in_offset) { + memcpy(buf->buffer, + buf->buffer + shift_n, + in_offset - shift_n); + buf->inptr = buf->buffer + + (in_offset - shift_n); + } else { + buf->inptr = buf->buffer + + start_buflen + in_offset; + } + } +} + +void gaim_circ_buffer_append(GaimCircBuffer *buf, gconstpointer src, gsize len) { + + int len_stored; + + /* Grow the buffer, if necessary */ + if ((buf->buflen - buf->bufused) < len) + grow_circ_buffer(buf, len); + + /* If we may need to wrap */ + if ((buf->inptr - buf->outptr) >= 0) + len_stored = MIN(len, buf->buflen + - (buf->inptr - buf->buffer)); + else + len_stored = len; + + memcpy(buf->inptr, src, len_stored); + + if (len_stored < len) { + memcpy(buf->buffer, src + len_stored, len - len_stored); + buf->inptr = buf->buffer + (len - len_stored); + } else if ((buf->buffer - buf->inptr) == len_stored) { + buf->inptr = buf->buffer; + } else { + buf->inptr += len_stored; + } + + buf->bufused += len; +} + +gsize gaim_circ_buffer_get_max_read(GaimCircBuffer *buf) { + int max_read; + + if (buf->bufused == 0) + max_read = 0; + else if ((buf->outptr - buf->inptr) >= 0) + max_read = buf->buflen - (buf->outptr - buf->buffer); + else + max_read = buf->inptr - buf->outptr; + + return max_read; +} + +gboolean gaim_circ_buffer_mark_read(GaimCircBuffer *buf, gsize len) { + g_return_val_if_fail(gaim_circ_buffer_get_max_read(buf) >= len, FALSE); + + buf->outptr += len; + buf->bufused -= len; + /* wrap to the start if we're at the end */ + if ((buf->outptr - buf->buffer) == buf->buflen) + buf->outptr = buf->buffer; + + return TRUE; +} + diff -r d8f238864c88 -r 33bef17125c2 src/gaim_buffer.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/gaim_buffer.h Thu Feb 09 04:17:56 2006 +0000 @@ -0,0 +1,98 @@ +/* + * @file gaim_buffer.h Buffer Utility Functions + * @ingroup core + * + * Gaim is the legal property of its developers, whose names are too numerous + * to list here. Please refer to the COPYRIGHT file distributed with this + * source distribution. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#ifndef _GAIM_BUFFER_H +#define _GAIM_BUFFER_H + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct _GaimCircBuffer { + gchar *buffer; + gsize growsize; + gsize buflen; + gsize bufused; + gchar *inptr; + gchar *outptr; +} GaimCircBuffer; + +/** + * Creates a new circular buffer. This will not allocate any memory for the + * actual buffer until data is appended to it. + * + * @param growsize The size that the buffer should grow by the first time data + * is appended and every time more space is needed. + * + * @return The new GaimCircBuffer. This should be freed with + * gaim_circ_buffer_destroy when you are done with it + */ +GaimCircBuffer *gaim_circ_buffer_new(gsize growsize); + +/** + * Dispose of the GaimCircBuffer and free any memory used by it (including any + * memory used by the internal buffer). + * + * @param buf The GaimCircBuffer to free + */ +void gaim_circ_buffer_destroy(GaimCircBuffer *buf); + +/** + * Append data to the GaimCircBuffer. This will automatically grow the internal + * buffer to fit the added data. + * + * @param buf The GaimCircBuffer to which to append the data + * @param src pointer to the data to copy into the buffer + * @param len number of bytes to copy into the buffer + */ +void gaim_circ_buffer_append(GaimCircBuffer *buf, gconstpointer src, gsize len); + +/** + * Determine the maximum number of contiguous bytes that can be read from the + * GaimCircBuffer. + * Note: This may not be the total number of bytes that are buffered - a + * subsequent call after calling gaim_circ_buffer_mark_read() may indicate more + * data is available to read. + * + * @param buf the GaimCircBuffer for which to determine the maximum contiguous + * bytes that can be read. + * + * @return the number of bytes that can be read from the GaimCircBuffer + */ +gsize gaim_circ_buffer_get_max_read(GaimCircBuffer *buf); + +/** + * Mark the number of bytes that have been read from the buffer. + * + * @param buf The GaimCircBuffer to mark bytes read from + * @param len The number of bytes to mark as read + * + * @return TRUE if we successfully marked the bytes as having been read, FALSE + * otherwise. + */ +gboolean gaim_circ_buffer_mark_read(GaimCircBuffer *buf, gsize len); + +#ifdef __cplusplus +} +#endif + +#endif /* _GAIM_BUFFER_H */ diff -r d8f238864c88 -r 33bef17125c2 src/protocols/irc/dcc_send.c --- a/src/protocols/irc/dcc_send.c Thu Feb 09 04:14:54 2006 +0000 +++ b/src/protocols/irc/dcc_send.c Thu Feb 09 04:17:56 2006 +0000 @@ -174,7 +174,12 @@ char *buffer[16]; int len; - if ((len = read(source, buffer, sizeof(buffer))) <= 0) { + len = read(source, buffer, sizeof(buffer)); + + if (len < 0 && errno == EAGAIN) + return; + else if (len < 0) { + /* XXX: Shouldn't this be canceling the transfer? */ gaim_input_remove(xd->inpa); xd->inpa = 0; return; @@ -209,20 +214,24 @@ gaim_xfer_end(xfer); return; } - - } } static gssize irc_dccsend_send_write(const guchar *buffer, size_t size, GaimXfer *xfer) { gssize s; + int ret; s = MIN(gaim_xfer_get_bytes_remaining(xfer), size); if (!s) return 0; - return write(xfer->fd, buffer, s); + ret = write(xfer->fd, buffer, s); + + if (ret < 0 && errno == EAGAIN) + ret = 0; + + return ret; } static void irc_dccsend_send_connected(gpointer data, int source, GaimInputCondition cond) { diff -r d8f238864c88 -r 33bef17125c2 src/protocols/irc/irc.c --- a/src/protocols/irc/irc.c Thu Feb 09 04:14:54 2006 +0000 +++ b/src/protocols/irc/irc.c Thu Feb 09 04:17:56 2006 +0000 @@ -83,23 +83,79 @@ gaim_notify_formatted(gc, title, title, NULL, irc->motd->str, NULL, NULL); } -int irc_send(struct irc_conn *irc, const char *buf) +static int do_send(struct irc_conn *irc, const char *buf, gsize len) { int ret; if (irc->gsc) { - ret = gaim_ssl_write(irc->gsc, buf, strlen(buf)); + ret = gaim_ssl_write(irc->gsc, buf, len); } else { - if (irc->fd < 0) - return -1; - ret = write(irc->fd, buf, strlen(buf)); + ret = write(irc->fd, buf, len); + } + + return ret; +} + +static void +irc_send_cb(gpointer data, gint source, GaimInputCondition cond) +{ + struct irc_conn *irc = data; + int ret, writelen; + + writelen = gaim_circ_buffer_get_max_read(irc->outbuf); + + if (writelen == 0) { + gaim_input_remove(irc->writeh); + irc->writeh = 0; + return; + } + + ret = do_send(irc, irc->outbuf->outptr, writelen); + + if (ret < 0 && errno == EAGAIN) + return; + else if (ret <= 0) { + gaim_connection_error(gaim_account_get_connection(irc->account), + _("Server has disconnected")); + return; + } + + gaim_circ_buffer_mark_read(irc->outbuf, ret); + +#if 0 + /* We *could* try to write more if we wrote it all */ + if (ret == write_len) { + irc_send_cb(data, source, cond); + } +#endif +} + +int irc_send(struct irc_conn *irc, const char *buf) +{ + int ret, buflen = strlen(buf); + + /* If we're not buffering writes, try to send immediately */ + if (!irc->writeh) + ret = do_send(irc, buf, buflen); + else { + ret = -1; + errno = EAGAIN; } /* gaim_debug(GAIM_DEBUG_MISC, "irc", "sent%s: %s", irc->gsc ? " (ssl)" : "", buf); */ - if (ret < 0) { + if (ret <= 0 && errno != EAGAIN) { gaim_connection_error(gaim_account_get_connection(irc->account), _("Server has disconnected")); + } else if (ret < buflen) { + if (ret < 0) + ret = 0; + if (!irc->writeh) + irc->writeh = gaim_input_add( + irc->gsc ? irc->gsc->fd : irc->fd, + GAIM_INPUT_WRITE, irc_send_cb, irc); + gaim_circ_buffer_append(irc->outbuf, buf + ret, + buflen - ret); } return ret; @@ -240,6 +296,7 @@ gc->proto_data = irc = g_new0(struct irc_conn, 1); irc->fd = -1; irc->account = account; + irc->outbuf = gaim_circ_buffer_new(512); userparts = g_strsplit(username, "@", 2); gaim_connection_set_display_name(gc, userparts[0]); @@ -262,6 +319,7 @@ irc_login_cb_ssl, irc_ssl_connect_failure, gc); } else { gaim_connection_error(gc, _("SSL support unavailable")); + return; } } @@ -288,7 +346,7 @@ if (pass && *pass) { buf = irc_format(irc, "vv", "PASS", pass); if (irc_send(irc, buf) < 0) { - gaim_connection_error(gc, "Error sending password"); +/* gaim_connection_error(gc, "Error sending password"); */ g_free(buf); return FALSE; } @@ -302,14 +360,14 @@ buf = irc_format(irc, "vvvv:", "USER", strlen(username) ? username : g_get_user_name(), hostname, irc->server, strlen(realname) ? realname : IRC_DEFAULT_ALIAS); if (irc_send(irc, buf) < 0) { - gaim_connection_error(gc, "Error registering with server"); +/* gaim_connection_error(gc, "Error registering with server");*/ g_free(buf); return FALSE; } g_free(buf); buf = irc_format(irc, "vn", "NICK", gaim_connection_get_display_name(gc)); if (irc_send(irc, buf) < 0) { - gaim_connection_error(gc, "Error sending nickname"); +/* gaim_connection_error(gc, "Error sending nickname");*/ g_free(buf); return FALSE; } @@ -404,6 +462,12 @@ if (irc->motd) g_string_free(irc->motd, TRUE); g_free(irc->server); + + if (irc->writeh) + gaim_input_remove(irc->writeh); + + gaim_circ_buffer_destroy(irc->outbuf); + g_free(irc); } @@ -443,7 +507,7 @@ const char *status_id = gaim_status_get_id(status); if (gc) - irc = gc->proto_data; + irc = gc->proto_data; if (!gaim_status_is_active(status)) return; @@ -527,8 +591,13 @@ irc->inbuflen += IRC_INITIAL_BUFSIZE; irc->inbuf = g_realloc(irc->inbuf, irc->inbuflen); } - - if ((len = gaim_ssl_read(gsc, irc->inbuf + irc->inbufused, IRC_INITIAL_BUFSIZE - 1)) < 0) { + + len = gaim_ssl_read(gsc, irc->inbuf + irc->inbufused, IRC_INITIAL_BUFSIZE - 1); + + if (len < 0 && errno == EAGAIN) { + /* Try again later */ + return; + } else if (len < 0) { gaim_connection_error(gc, _("Read error")); return; } else if (len == 0) { @@ -550,7 +619,10 @@ irc->inbuf = g_realloc(irc->inbuf, irc->inbuflen); } - if ((len = read(irc->fd, irc->inbuf + irc->inbufused, IRC_INITIAL_BUFSIZE - 1)) < 0) { + len = read(irc->fd, irc->inbuf + irc->inbufused, IRC_INITIAL_BUFSIZE - 1); + if (len < 0 && errno == EAGAIN) { + return; + } else if (len < 0) { gaim_connection_error(gc, _("Read error")); return; } else if (len == 0) { diff -r d8f238864c88 -r 33bef17125c2 src/protocols/irc/irc.h --- a/src/protocols/irc/irc.h Thu Feb 09 04:14:54 2006 +0000 +++ b/src/protocols/irc/irc.h Thu Feb 09 04:17:56 2006 +0000 @@ -1,10 +1,10 @@ /** * @file irc.h - * + * * gaim * * Copyright (C) 2003, Ethan Blanton - * + * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or @@ -26,6 +26,7 @@ #include #include "ft.h" +#include "gaim_buffer.h" #include "roomlist.h" #include "sslconn.h" @@ -77,6 +78,9 @@ GaimSslConnection *gsc; gboolean quitting; + + GaimCircBuffer *outbuf; + guint writeh; }; struct irc_buddy { diff -r d8f238864c88 -r 33bef17125c2 src/protocols/jabber/jabber.c --- a/src/protocols/jabber/jabber.c Thu Feb 09 04:14:54 2006 +0000 +++ b/src/protocols/jabber/jabber.c Thu Feb 09 04:17:56 2006 +0000 @@ -194,6 +194,42 @@ } } +static int jabber_do_send(JabberStream *js, const char *data, int len) +{ + int ret; + + if (js->gsc) + ret = gaim_ssl_write(js->gsc, data, len); + else + ret = write(js->fd, data, len); + + return ret; +} + +static void jabber_send_cb(gpointer data, gint source, GaimInputCondition cond) +{ + JabberStream *js = data; + int ret, writelen; + writelen = gaim_circ_buffer_get_max_read(js->write_buffer); + + if (writelen == 0) { + gaim_input_remove(js->writeh); + js->writeh = -1; + return; + } + + ret = jabber_do_send(js, js->write_buffer->outptr, writelen); + + if (ret < 0 && errno == EAGAIN) + return; + else if (ret <= 0) { + gaim_connection_error(js->gc, _("Write error")); + return; + } + + gaim_circ_buffer_mark_read(js->write_buffer, ret); +} + void jabber_send_raw(JabberStream *js, const char *data, int len) { int ret; @@ -228,27 +264,53 @@ sasl_encode(js->sasl, &data[pos], towrite, &out, &olen); pos += towrite; - if (js->gsc) - ret = gaim_ssl_write(js->gsc, out, olen); - else - ret = write(js->fd, out, olen); - if (ret < 0) + if (js->writeh != -1) + ret = jabber_do_send(js, out, olen); + else { + ret = -1; + errno = EAGAIN; + } + + if (ret < 0 && errno != EAGAIN) gaim_connection_error(js->gc, _("Write error")); + else if (ret < olen) { + if (ret < 0) + ret = 0; + if (js->writeh == -1) + js->writeh = gaim_input_add( + js->gsc ? js->gsc->fd : js->fd, + GAIM_INPUT_WRITE, + jabber_send_cb, js); + gaim_circ_buffer_append(js->write_buffer, + out + ret, olen - ret); + } } return; } #endif - - if(js->gsc) { - ret = gaim_ssl_write(js->gsc, data, len == -1 ? strlen(data) : len); - } else { - if(js->fd < 0) - return; - ret = write(js->fd, data, len == -1 ? strlen(data) : len); + + if (len == -1) + len = strlen(data); + + if (js->writeh == -1) + ret = jabber_do_send(js, data, len); + else { + ret = -1; + errno = EAGAIN; } - if(ret < 0) + if (ret < 0 && errno != EAGAIN) gaim_connection_error(js->gc, _("Write error")); + else if (ret < len) { + if (ret < 0) + ret = 0; + if (js->writeh == -1) + js->writeh = gaim_input_add( + js->gsc ? js->gsc->fd : js->fd, + GAIM_INPUT_WRITE, jabber_send_cb, js); + gaim_circ_buffer_append(js->write_buffer, + data + ret, len - ret); + } } @@ -285,7 +347,9 @@ buf[len] = '\0'; gaim_debug(GAIM_DEBUG_INFO, "jabber", "Recv (ssl)(%d): %s\n", len, buf); jabber_parser_process(js, buf, len); - } else { + } else if(errno == EAGAIN) + return; + else { gaim_connection_error(gc, _("Read Error")); } } @@ -317,6 +381,8 @@ buf[len] = '\0'; gaim_debug(GAIM_DEBUG_INFO, "jabber", "Recv (%d): %s\n", len, buf); jabber_parser_process(js, buf, len); + } else if(errno == EAGAIN) { + return; } else { gaim_connection_error(gc, _("Read Error")); } @@ -446,6 +512,8 @@ js->chat_servers = g_list_append(NULL, g_strdup("conference.jabber.org")); js->user = jabber_id_new(gaim_account_get_username(account)); js->next_id = g_random_int(); + js->write_buffer = gaim_circ_buffer_new(512); + js->writeh = -1; if(!js->user) { gaim_connection_error(gc, _("Invalid Jabber ID")); @@ -789,6 +857,9 @@ return; } + js->write_buffer = gaim_circ_buffer_new(512); + js->writeh = -1; + if(!js->user->resource) { char *me; js->user->resource = g_strdup("Home"); @@ -871,6 +942,9 @@ jabber_id_free(js->user); if(js->avatar_hash) g_free(js->avatar_hash); + gaim_circ_buffer_destroy(js->write_buffer); + if(js->writeh) + gaim_input_remove(js->writeh); #ifdef HAVE_CYRUS_SASL if(js->sasl) sasl_dispose(&js->sasl); diff -r d8f238864c88 -r 33bef17125c2 src/protocols/jabber/jabber.h --- a/src/protocols/jabber/jabber.h Thu Feb 09 04:14:54 2006 +0000 +++ b/src/protocols/jabber/jabber.h Thu Feb 09 04:17:56 2006 +0000 @@ -26,6 +26,7 @@ #include "connection.h" #include "roomlist.h" #include "sslconn.h" +#include "gaim_buffer.h" #include "jutil.h" #include "xmlnode.h" @@ -108,6 +109,9 @@ char *avatar_hash; GSList *pending_avatar_requests; + GaimCircBuffer *write_buffer; + guint writeh; + /* OK, this stays at the end of the struct, so plugins can depend * on the rest of the stuff being in the right place */ diff -r d8f238864c88 -r 33bef17125c2 src/protocols/jabber/oob.c --- a/src/protocols/jabber/oob.c Thu Feb 09 04:14:54 2006 +0000 +++ b/src/protocols/jabber/oob.c Thu Feb 09 04:17:56 2006 +0000 @@ -33,12 +33,15 @@ char *page; GString *headers; - gboolean newline; char *iq_id; JabberStream *js; + gchar *write_buffer; + gsize written_len; + guint writeh; + } JabberOOBXfer; static void jabber_oob_xfer_init(GaimXfer *xfer) @@ -57,6 +60,9 @@ g_free(jox->address); g_free(jox->page); g_free(jox->iq_id); + g_free(jox->write_buffer); + if(jox->writeh) + gaim_input_remove(jox->writeh); g_free(jox); xfer->data = NULL; @@ -76,41 +82,72 @@ jabber_oob_xfer_free(xfer); } +static void jabber_oob_xfer_request_send(gpointer data, gint source, GaimInputCondition cond) { + GaimXfer *xfer = data; + JabberOOBXfer *jox = xfer->data; + int len, total_len = strlen(jox->write_buffer); + + len = write(xfer->fd, jox->write_buffer + jox->written_len, + total_len - jox->written_len); + + if(len < 0 && errno == EAGAIN) + return; + else if(len < 0) { + gaim_debug(GAIM_DEBUG_ERROR, "jabber", "Write error on oob xfer!\n"); + gaim_input_remove(jox->writeh); + gaim_xfer_cancel_local(xfer); + } + jox->written_len += len; + + if(jox->written_len == total_len) { + gaim_input_remove(jox->writeh); + g_free(jox->write_buffer); + jox->write_buffer = NULL; + } +} + static void jabber_oob_xfer_start(GaimXfer *xfer) { JabberOOBXfer *jox = xfer->data; - char *buf = g_strdup_printf("GET /%s HTTP/1.1\r\nHost: %s\r\n\r\n", + if(jox->write_buffer == NULL) { + jox->write_buffer = g_strdup_printf( + "GET /%s HTTP/1.1\r\nHost: %s\r\n\r\n", jox->page, jox->address); - write(xfer->fd, buf, strlen(buf)); - g_free(buf); + jox->written_len = 0; + } + + jox->writeh = gaim_input_add(xfer->fd, GAIM_INPUT_WRITE, + jabber_oob_xfer_request_send, xfer); + + jabber_oob_xfer_request_send(xfer, xfer->fd, GAIM_INPUT_WRITE); } static gssize jabber_oob_xfer_read(guchar **buffer, GaimXfer *xfer) { JabberOOBXfer *jox = xfer->data; - char test; - int size; + char test[2048]; + char *tmp, *lenstr; + int len; - if(read(xfer->fd, &test, sizeof(test)) > 0) { - jox->headers = g_string_append_c(jox->headers, test); - if(test == '\r') - return 0; - if(test == '\n') { - if(jox->newline) { - gchar *lenstr = strstr(jox->headers->str, "Content-Length: "); - if(lenstr) { - sscanf(lenstr, "Content-Length: %d", &size); - gaim_xfer_set_size(xfer, size); - } - gaim_xfer_set_read_fnc(xfer, NULL); - return 0; - } else - jox->newline = TRUE; - return 0; + if((len = read(xfer->fd, test, sizeof(test))) > 0) { + jox->headers = g_string_append_len(jox->headers, test, len); + if((tmp = strstr(jox->headers->str, "\r\n\r\n"))) { + *tmp = '\0'; + lenstr = strstr(jox->headers->str, "Content-Length: "); + if(lenstr) { + int size; + sscanf(lenstr, "Content-Length: %d", &size); + gaim_xfer_set_size(xfer, size); } - jox->newline = FALSE; + gaim_xfer_set_read_fnc(xfer, NULL); + + tmp += 4; + + *buffer = g_strdup(tmp); + return strlen(tmp); + } return 0; - } else { + } else if (errno != EAGAIN) { gaim_debug(GAIM_DEBUG_ERROR, "jabber", "Read error on oob xfer!\n"); gaim_xfer_cancel_local(xfer); } diff -r d8f238864c88 -r 33bef17125c2 src/protocols/jabber/si.c --- a/src/protocols/jabber/si.c Thu Feb 09 04:14:54 2006 +0000 +++ b/src/protocols/jabber/si.c Thu Feb 09 04:17:56 2006 +0000 @@ -63,6 +63,7 @@ char *rxqueue; size_t rxlen; + gsize rxmaxlen; } JabberSIXfer; static GaimXfer* @@ -218,6 +219,39 @@ jabber_si_bytestreams_attempt_connect(xfer); } + +static void +jabber_si_xfer_bytestreams_send_read_again_resp_cb(gpointer data, gint source, + GaimInputCondition cond) +{ + GaimXfer *xfer = data; + JabberSIXfer *jsx = xfer->data; + int len; + + len = write(source, jsx->rxqueue + jsx->rxlen, jsx->rxmaxlen - jsx->rxlen); + if (len < 0 && errno == EAGAIN) + return; + else if (len < 0) { + gaim_input_remove(xfer->watcher); + xfer->watcher = 0; + g_free(jsx->rxqueue); + jsx->rxqueue = NULL; + close(source); + gaim_xfer_cancel_remote(xfer); + } + jsx->rxlen += len; + + if (jsx->rxlen < jsx->rxmaxlen) + return; + + gaim_input_remove(xfer->watcher); + xfer->watcher = 0; + g_free(jsx->rxqueue); + jsx->rxqueue = NULL; + + gaim_xfer_start(xfer, source, NULL, -1); +} + static void jabber_si_xfer_bytestreams_send_read_again_cb(gpointer data, gint source, GaimInputCondition cond) @@ -235,7 +269,10 @@ if(jsx->rxlen < 5) { gaim_debug_info("jabber", "reading the first 5 bytes\n"); - if((len = read(source, buffer, 5 - jsx->rxlen)) <= 0) { + len = read(source, buffer, 5 - jsx->rxlen); + if(len < 0 && errno == EAGAIN) + return; + else if(len < 0) { gaim_input_remove(xfer->watcher); xfer->watcher = 0; close(source); @@ -256,7 +293,10 @@ return; } else if(jsx->rxlen - 5 < jsx->rxqueue[4] + 2) { gaim_debug_info("jabber", "reading umpteen more bytes\n"); - if((len = read(source, buffer, jsx->rxqueue[4] + 5 + 2 - jsx->rxlen)) <= 0) { + len = read(source, buffer, jsx->rxqueue[4] + 5 + 2 - jsx->rxlen); + if(len < 0 && errno == EAGAIN) + return; + else if(len < 0) { gaim_input_remove(xfer->watcher); xfer->watcher = 0; close(source); @@ -294,20 +334,64 @@ return; } + g_free(jsx->rxqueue); host = gaim_network_get_my_ip(jsx->js->fd); - buffer[0] = 0x05; - buffer[1] = 0x00; - buffer[2] = 0x00; - buffer[3] = 0x03; - buffer[4] = strlen(host); - memcpy(buffer + 5, host, strlen(host)); - buffer[5+strlen(host)] = 0x00; - buffer[6+strlen(host)] = 0x00; + jsx->rxmaxlen = 5 + strlen(host) + 2; + jsx->rxqueue = g_malloc(jsx->rxmaxlen); + jsx->rxlen = 0; + + jsx->rxqueue[0] = 0x05; + jsx->rxqueue[1] = 0x00; + jsx->rxqueue[2] = 0x00; + jsx->rxqueue[3] = 0x03; + jsx->rxqueue[4] = strlen(host); + memcpy(jsx->rxqueue + 5, host, strlen(host)); + jsx->rxqueue[5+strlen(host)] = 0x00; + jsx->rxqueue[6+strlen(host)] = 0x00; + + xfer->watcher = gaim_input_add(source, GAIM_INPUT_WRITE, + jabber_si_xfer_bytestreams_send_read_again_resp_cb, xfer); + jabber_si_xfer_bytestreams_send_read_again_resp_cb(xfer, source, + GAIM_INPUT_WRITE); +} + +static void +jabber_si_xfer_bytestreams_send_read_response_cb(gpointer data, gint source, + GaimInputCondition cond) +{ + GaimXfer *xfer = data; + JabberSIXfer *jsx = xfer->data; + int len; - write(source, buffer, strlen(host)+7); + len = write(source, jsx->rxqueue + jsx->rxlen, jsx->rxmaxlen - jsx->rxlen); + if (len < 0 && errno == EAGAIN) + return; + else if (len < 0) { + gaim_input_remove(xfer->watcher); + xfer->watcher = 0; + g_free(jsx->rxqueue); + jsx->rxqueue = NULL; + close(source); + gaim_xfer_cancel_remote(xfer); + } + jsx->rxlen += len; - gaim_xfer_start(xfer, source, NULL, -1); + if (jsx->rxlen < jsx->rxmaxlen) + return; + + gaim_input_remove(xfer->watcher); + xfer->watcher = 0; + + if (jsx->rxqueue[1] == 0x00) { + xfer->watcher = gaim_input_add(source, GAIM_INPUT_READ, + jabber_si_xfer_bytestreams_send_read_again_cb, xfer); + g_free(jsx->rxqueue); + jsx->rxqueue = NULL; + } else { + close(source); + gaim_xfer_cancel_remote(xfer); + } } static void @@ -326,7 +410,10 @@ if(jsx->rxlen < 2) { gaim_debug_info("jabber", "reading those first two bytes\n"); - if((len = read(source, buffer, 2 - jsx->rxlen)) <= 0) { + len = read(source, buffer, 2 - jsx->rxlen); + if(len < 0 && errno == EAGAIN) + return; + else if(len < 0) { gaim_input_remove(xfer->watcher); xfer->watcher = 0; close(source); @@ -339,7 +426,10 @@ return; } else if(jsx->rxlen - 2 < jsx->rxqueue[1]) { gaim_debug_info("jabber", "reading the next umpteen bytes\n"); - if((len = read(source, buffer, jsx->rxqueue[1] + 2 - jsx->rxlen)) <= 0) { + len = read(source, buffer, jsx->rxqueue[1] + 2 - jsx->rxlen); + if(len < 0 && errno == EAGAIN) + return; + else if(len < 0) { gaim_input_remove(xfer->watcher); xfer->watcher = 0; close(source); @@ -357,7 +447,6 @@ gaim_input_remove(xfer->watcher); xfer->watcher = 0; - gaim_debug_info("jabber", "checking to make sure we're socks FIVE\n"); if(jsx->rxqueue[0] != 0x05) { @@ -372,26 +461,33 @@ gaim_debug_info("jabber", "testing %hhu\n", jsx->rxqueue[i+2]); if(jsx->rxqueue[i+2] == 0x00) { - buffer[0] = 0x05; - buffer[1] = 0x00; - write(source, buffer, 2); - xfer->watcher = gaim_input_add(source, GAIM_INPUT_READ, - jabber_si_xfer_bytestreams_send_read_again_cb, xfer); g_free(jsx->rxqueue); + jsx->rxlen = 0; + jsx->rxmaxlen = 2; + jsx->rxqueue = g_malloc(jsx->rxmaxlen); + jsx->rxqueue[0] = 0x05; + jsx->rxqueue[1] = 0x00; + xfer->watcher = gaim_input_add(source, GAIM_INPUT_WRITE, + jabber_si_xfer_bytestreams_send_read_response_cb, + xfer); + jabber_si_xfer_bytestreams_send_read_response_cb(xfer, + source, GAIM_INPUT_WRITE); jsx->rxqueue = NULL; jsx->rxlen = 0; return; } } - buffer[0] = 0x05; - buffer[1] = 0xFF; - write(source, buffer, 2); - close(source); g_free(jsx->rxqueue); - jsx->rxqueue = NULL; jsx->rxlen = 0; - gaim_xfer_cancel_remote(xfer); + jsx->rxmaxlen = 2; + jsx->rxqueue = g_malloc(jsx->rxmaxlen); + jsx->rxqueue[0] = 0x05; + jsx->rxqueue[1] = 0xFF; + xfer->watcher = gaim_input_add(source, GAIM_INPUT_WRITE, + jabber_si_xfer_bytestreams_send_read_response_cb, xfer); + jabber_si_xfer_bytestreams_send_read_response_cb(xfer, + source, GAIM_INPUT_WRITE); } static void @@ -403,7 +499,10 @@ gaim_debug_info("jabber", "in jabber_si_xfer_bytestreams_send_connected_cb\n"); - if((acceptfd = accept(source, NULL, 0)) == -1) { + acceptfd = accept(source, NULL, 0); + if(acceptfd == -1 && (errno == EAGAIN || errno == EWOULDBLOCK)) + return; + else if(acceptfd == -1) { gaim_debug_warning("jabber", "accept: %s\n", strerror(errno)); return; } @@ -584,6 +683,7 @@ g_free(jsx->stream_id); g_free(jsx->iq_id); /* XXX: free other stuff */ + g_free(jsx->rxqueue); g_free(jsx); xfer->data = NULL; } diff -r d8f238864c88 -r 33bef17125c2 src/protocols/msn/directconn.c --- a/src/protocols/msn/directconn.c Thu Feb 09 04:14:54 2006 +0000 +++ b/src/protocols/msn/directconn.c Thu Feb 09 04:17:56 2006 +0000 @@ -460,8 +460,8 @@ directconn->fd = fd; - directconn->inpa = gaim_input_add(fd, GAIM_INPUT_READ, - connect_cb, directconn); + directconn->inpa = gaim_input_add(fd, GAIM_INPUT_READ, connect_cb, + directconn); directconn->port = port; directconn->c = 0; diff -r d8f238864c88 -r 33bef17125c2 src/protocols/msn/httpconn.c --- a/src/protocols/msn/httpconn.c Thu Feb 09 04:14:54 2006 +0000 +++ b/src/protocols/msn/httpconn.c Thu Feb 09 04:17:56 2006 +0000 @@ -25,16 +25,7 @@ #include "debug.h" #include "httpconn.h" -typedef struct -{ - MsnHttpConn *httpconn; - char *data; - size_t size; - -} MsnHttpQueueData; - static void read_cb(gpointer data, gint source, GaimInputCondition cond); -void msn_httpconn_process_queue(MsnHttpConn *httpconn); gboolean msn_httpconn_parse_data(MsnHttpConn *httpconn, const char *buf, size_t size, char **ret_buf, size_t *ret_size, gboolean *error); @@ -55,6 +46,9 @@ httpconn->servconn = servconn; + httpconn->tx_buf = gaim_circ_buffer_new(MSN_BUF_LEN); + httpconn->tx_handler = -1; + return httpconn; } @@ -68,14 +62,15 @@ if (httpconn->connected) msn_httpconn_disconnect(httpconn); - if (httpconn->full_session_id != NULL) - g_free(httpconn->full_session_id); + g_free(httpconn->full_session_id); + + g_free(httpconn->session_id); - if (httpconn->session_id != NULL) - g_free(httpconn->session_id); + g_free(httpconn->host); - if (httpconn->host != NULL) - g_free(httpconn->host); + gaim_circ_buffer_destroy(httpconn->tx_buf); + if (httpconn->tx_handler > 0) + gaim_input_remove(httpconn->tx_handler); g_free(httpconn); } @@ -114,49 +109,70 @@ return auth; } -static ssize_t -write_raw(MsnHttpConn *httpconn, const char *header, - const char *body, size_t body_len) +static void +httpconn_write_cb(gpointer data, gint source, GaimInputCondition cond) { - char *buf; - size_t buf_len; + MsnHttpConn *httpconn = data; + int ret, writelen; + + if (httpconn->waiting_response) + return; + + writelen = gaim_circ_buffer_get_max_read(httpconn->tx_buf); + + if (writelen == 0) { + gaim_input_remove(httpconn->tx_handler); + httpconn->tx_handler = -1; + return; + } - ssize_t s; + ret = write(httpconn->fd, httpconn->tx_buf->outptr, writelen); + + if (ret < 0 && errno == EAGAIN) + return; + else if (ret <= 0) { + msn_servconn_got_error(httpconn->servconn, + MSN_SERVCONN_ERROR_WRITE); + return; + } + + gaim_circ_buffer_mark_read(httpconn->tx_buf, ret); +} + +static ssize_t +write_raw(MsnHttpConn *httpconn, const char *data, size_t data_len) +{ ssize_t res; /* result of the write operation */ #ifdef MSN_DEBUG_HTTP gaim_debug_misc("msn", "Writing HTTP (header): {%s}\n", header); #endif - buf = g_strdup_printf("%s\r\n", header); - buf_len = strlen(buf); - if (body != NULL) + if (httpconn->tx_handler == -1 && !httpconn->waiting_response) + res = write(httpconn->fd, data, data_len); + else { - buf = g_realloc(buf, buf_len + body_len); - memcpy(buf + buf_len, body, body_len); - buf_len += body_len; + res = -1; + errno = EAGAIN; } - s = 0; - - do + if (res <= 0 && errno != EAGAIN) { - res = write(httpconn->fd, buf + s, buf_len - s); - if (res >= 0) - { - s += res; - } - else if (errno != EAGAIN) - { - msn_servconn_got_error(httpconn->servconn, MSN_SERVCONN_ERROR_WRITE); - return -1; - } - } while (s < buf_len); + msn_servconn_got_error(httpconn->servconn, + MSN_SERVCONN_ERROR_WRITE); + return -1; + } else if (res < data_len) { + if (res < 0) + res = 0; + if (httpconn->tx_handler == -1) + httpconn->tx_handler = gaim_input_add(httpconn->fd, + GAIM_INPUT_WRITE, httpconn_write_cb, httpconn); + gaim_circ_buffer_append(httpconn->tx_buf, data + res, + data_len - res); + } - g_free(buf); - - return s; + return res; } static void @@ -169,11 +185,14 @@ g_return_if_fail(httpconn != NULL); if (httpconn->waiting_response || - httpconn->queue != NULL) + httpconn->tx_handler > 0) { return; } + /* It is OK if this is buffered because it will only be buffered if + nothing else is in the buffer */ + auth = msn_httpconn_proxy_auth(httpconn); header = g_strdup_printf( @@ -187,20 +206,19 @@ "Connection: Keep-Alive\r\n" "Pragma: no-cache\r\n" "Content-Type: application/x-msn-messenger\r\n" - "Content-Length: 0\r\n", + "Content-Length: 0\r\n\r\n", httpconn->host, httpconn->full_session_id, httpconn->host, auth ? auth : ""); - if (auth != NULL) - g_free(auth); + g_free(auth); - r = write_raw(httpconn, header, NULL, -1); + r = write_raw(httpconn, header, strlen(header)); g_free(header); - if (r > 0) + if (r >= 0) { httpconn->waiting_response = TRUE; httpconn->dirty = FALSE; @@ -242,12 +260,13 @@ if (source > 0) { httpconn->inpa = gaim_input_add(httpconn->fd, GAIM_INPUT_READ, - read_cb, data); + read_cb, data); httpconn->timer = gaim_timeout_add(2000, do_poll, httpconn); httpconn->waiting_response = FALSE; - msn_httpconn_process_queue(httpconn); + if (httpconn->tx_handler > 0) + httpconn_write_cb(httpconn, source, GAIM_INPUT_WRITE); } else { @@ -269,8 +288,7 @@ msn_httpconn_disconnect(httpconn); r = gaim_proxy_connect(httpconn->session->account, - "gateway.messenger.hotmail.com", 80, connect_cb, - httpconn); + "gateway.messenger.hotmail.com", 80, connect_cb, httpconn); if (r == 0) { @@ -329,7 +347,9 @@ len = read(httpconn->fd, buf, sizeof(buf) - 1); - if (len <= 0) + if (len < 0 && errno == EAGAIN) + return; + else if (len <= 0) { gaim_debug_error("msn", "HTTP: Read error\n"); msn_servconn_got_error(httpconn->servconn, MSN_SERVCONN_ERROR_READ); @@ -444,37 +464,12 @@ g_free(old_rx_buf); } -void -msn_httpconn_process_queue(MsnHttpConn *httpconn) -{ - if (httpconn->queue != NULL) - { - MsnHttpQueueData *queue_data; - - queue_data = (MsnHttpQueueData *)httpconn->queue->data; - - httpconn->queue = g_list_remove(httpconn->queue, queue_data); - - msn_httpconn_write(queue_data->httpconn, - queue_data->data, - queue_data->size); - - g_free(queue_data->data); - g_free(queue_data); - } - else - { - httpconn->dirty = TRUE; - } -} - size_t -msn_httpconn_write(MsnHttpConn *httpconn, const char *data, size_t size) +msn_httpconn_write(MsnHttpConn *httpconn, const char *body, size_t size) { char *params; - char *header; + char *data; char *auth; - gboolean first; const char *server_types[] = { "NS", "SB" }; const char *server_type; size_t r; /* result of the write operation */ @@ -484,31 +479,14 @@ /* TODO: remove http data from servconn */ g_return_val_if_fail(httpconn != NULL, 0); - g_return_val_if_fail(data != NULL, 0); - g_return_val_if_fail(size > 0, 0); + g_return_val_if_fail(body != NULL, 0); + g_return_val_if_fail(size > 0, 0); servconn = httpconn->servconn; - if (httpconn->waiting_response) - { - MsnHttpQueueData *queue_data = g_new0(MsnHttpQueueData, 1); - - queue_data->httpconn = httpconn; - queue_data->data = g_memdup(data, size); - queue_data->size = size; - - httpconn->queue = g_list_append(httpconn->queue, queue_data); - /* httpconn->dirty = TRUE; */ - - /* servconn->processing = TRUE; */ - - return size; - } - - first = httpconn->virgin; server_type = server_types[servconn->type]; - if (first) + if (httpconn->virgin) { host = "gateway.messenger.hotmail.com"; @@ -516,6 +494,7 @@ params = g_strdup_printf("Action=open&Server=%s&IP=%s", server_type, servconn->host); + httpconn->virgin = FALSE; } else { @@ -529,12 +508,12 @@ } params = g_strdup_printf("SessionID=%s", - httpconn->full_session_id); + httpconn->full_session_id); } auth = msn_httpconn_proxy_auth(httpconn); - header = g_strdup_printf( + data = g_strdup_printf( "POST http://%s/gateway/gateway.dll?%s HTTP/1.1\r\n" "Accept: */*\r\n" "Accept-Language: en-us\r\n" @@ -545,25 +524,26 @@ "Connection: Keep-Alive\r\n" "Pragma: no-cache\r\n" "Content-Type: application/x-msn-messenger\r\n" - "Content-Length: %d\r\n", + "Content-Length: %d\r\n\r\n" + "%s", host, params, host, auth ? auth : "", - (int)size); + (int) size, + body ? body : ""); + g_free(params); - if (auth != NULL) - g_free(auth); + g_free(auth); - r = write_raw(httpconn, header, data, size); + r = write_raw(httpconn, data, strlen(data)); - g_free(header); + g_free(data); - if (r > 0) + if (r >= 0) { - httpconn->virgin = FALSE; httpconn->waiting_response = TRUE; httpconn->dirty = FALSE; } @@ -629,7 +609,9 @@ *ret_buf = g_strdup(""); *ret_size = 0; - msn_httpconn_process_queue(httpconn); + if (httpconn->tx_handler > 0) + httpconn_write_cb(httpconn, httpconn->fd, + GAIM_INPUT_WRITE); return TRUE; } @@ -781,7 +763,8 @@ *ret_buf = body; *ret_size = body_len; - msn_httpconn_process_queue(httpconn); + if (httpconn->tx_handler > 0) + httpconn_write_cb(httpconn, httpconn->fd, GAIM_INPUT_WRITE); return TRUE; } diff -r d8f238864c88 -r 33bef17125c2 src/protocols/msn/httpconn.h --- a/src/protocols/msn/httpconn.h Thu Feb 09 04:14:54 2006 +0000 +++ b/src/protocols/msn/httpconn.h Thu Feb 09 04:17:56 2006 +0000 @@ -27,6 +27,7 @@ typedef struct _MsnHttpConn MsnHttpConn; #include "servconn.h" +#include "gaim_buffer.h" /** * An HTTP Connection. @@ -50,13 +51,15 @@ connect to. */ char *host; /**< The HTTP gateway host. */ - GList *queue; /**< The queue of data chunks to write. */ int fd; /**< The connection's file descriptor. */ - int inpa; /**< The connection's input handler. */ + guint inpa; /**< The connection's input handler. */ char *rx_buf; /**< The receive buffer. */ - int rx_len; /**< The receive buffer lenght. */ + int rx_len; /**< The receive buffer length. */ + + GaimCircBuffer *tx_buf; + guint tx_handler; }; /** diff -r d8f238864c88 -r 33bef17125c2 src/protocols/msn/nexus.c --- a/src/protocols/msn/nexus.c Thu Feb 09 04:14:54 2006 +0000 +++ b/src/protocols/msn/nexus.c Thu Feb 09 04:17:56 2006 +0000 @@ -36,8 +36,8 @@ nexus = g_new0(MsnNexus, 1); nexus->session = session; - nexus->challenge_data = g_hash_table_new_full(g_str_hash, g_str_equal, - g_free, g_free); + nexus->challenge_data = g_hash_table_new_full(g_str_hash, + g_str_equal, g_free, g_free); return nexus; } @@ -45,15 +45,18 @@ void msn_nexus_destroy(MsnNexus *nexus) { - if (nexus->login_host != NULL) - g_free(nexus->login_host); + g_free(nexus->login_host); - if (nexus->login_path != NULL) - g_free(nexus->login_path); + g_free(nexus->login_path); if (nexus->challenge_data != NULL) g_hash_table_destroy(nexus->challenge_data); + if (nexus->input_handler > 0) + gaim_input_remove(nexus->input_handler); + g_free(nexus->write_buf); + g_free(nexus->read_buf); + g_free(nexus); } @@ -61,27 +64,58 @@ * Util **************************************************************************/ -static size_t -msn_ssl_read(GaimSslConnection *gsc, char **dest_buffer) +static gssize +msn_ssl_read(MsnNexus *nexus) { - gssize size = 0, s; - char *buffer = NULL; + gssize len; char temp_buf[4096]; - while ((s = gaim_ssl_read(gsc, temp_buf, sizeof(temp_buf))) > 0) + if ((len = gaim_ssl_read(nexus->gsc, temp_buf, + sizeof(temp_buf))) > 0) { - buffer = g_realloc(buffer, size + s + 1); - - strncpy(buffer + size, temp_buf, s); - - buffer[size + s] = '\0'; - - size += s; + nexus->read_buf = g_realloc(nexus->read_buf, + nexus->read_len + len + 1); + strncpy(nexus->read_buf + nexus->read_len, temp_buf, len); + nexus->read_len += len; + nexus->read_buf[nexus->read_len] = '\0'; } - *dest_buffer = buffer; + return len; +} + +static void +nexus_write_cb(gpointer data, gint source, GaimInputCondition cond) +{ + MsnNexus *nexus = data; + int len, total_len; + + total_len = strlen(nexus->write_buf); + + len = gaim_ssl_write(nexus->gsc, + nexus->write_buf + nexus->written_len, + total_len - nexus->written_len); - return size; + if (len < 0 && errno == EAGAIN) + return; + else if (len <= 0) { + gaim_input_remove(nexus->input_handler); + nexus->input_handler = -1; + /* TODO: notify of the error */ + return; + } + nexus->written_len += len; + + if (nexus->written_len < total_len) + return; + + gaim_input_remove(nexus->input_handler); + nexus->input_handler = -1; + + g_free(nexus->write_buf); + nexus->write_buf = NULL; + nexus->written_len = 0; + + nexus->written_cb(nexus, source, 0); } /************************************************************************** @@ -89,6 +123,10 @@ **************************************************************************/ static void +login_connect_cb(gpointer data, GaimSslConnection *gsc, + GaimInputCondition cond); + +static void login_error_cb(GaimSslConnection *gsc, GaimSslErrorType error, void *data) { MsnNexus *nexus; @@ -106,6 +144,157 @@ } static void +nexus_login_written_cb(gpointer data, gint source, GaimInputCondition cond) +{ + MsnNexus *nexus = data; + MsnSession *session; + int len; + + session = nexus->session; + g_return_if_fail(session != NULL); + + if (nexus->input_handler == -1) + nexus->input_handler = gaim_input_add(nexus->gsc->fd, + GAIM_INPUT_READ, nexus_login_written_cb, nexus); + + + len = msn_ssl_read(nexus); + + if (len < 0 && errno == EAGAIN) + return; + else if (len < 0) { + gaim_input_remove(nexus->input_handler); + nexus->input_handler = -1; + g_free(nexus->read_buf); + nexus->read_buf = NULL; + nexus->read_len = 0; + /* TODO: error handling */ + return; + } + + if (g_strstr_len(nexus->read_buf, nexus->read_len, + "\r\n\r\n") == NULL) + return; + + gaim_input_remove(nexus->input_handler); + nexus->input_handler = -1; + + gaim_ssl_close(nexus->gsc); + nexus->gsc = NULL; + + gaim_debug_misc("msn", "ssl buffer: {%s}", nexus->read_buf); + + if (strstr(nexus->read_buf, "HTTP/1.1 302") != NULL) + { + /* Redirect. */ + char *location, *c; + + location = strstr(nexus->read_buf, "Location: "); + if (location == NULL) + { + g_free(nexus->read_buf); + nexus->read_buf = NULL; + nexus->read_len = 0; + + return; + } + location = strchr(location, ' ') + 1; + + if ((c = strchr(location, '\r')) != NULL) + *c = '\0'; + + /* Skip the http:// */ + if ((c = strchr(location, '/')) != NULL) + location = c + 2; + + if ((c = strchr(location, '/')) != NULL) + { + g_free(nexus->login_path); + nexus->login_path = g_strdup(c); + + *c = '\0'; + } + + g_free(nexus->login_host); + nexus->login_host = g_strdup(location); + + gaim_ssl_connect(session->account, nexus->login_host, + GAIM_SSL_DEFAULT_PORT, login_connect_cb, + login_error_cb, nexus); + } + else if (strstr(nexus->read_buf, "HTTP/1.1 401 Unauthorized") != NULL) + { + const char *error; + + if ((error = strstr(nexus->read_buf, "WWW-Authenticate")) != NULL) + { + if ((error = strstr(error, "cbtxt=")) != NULL) + { + const char *c; + char *temp; + + error += strlen("cbtxt="); + + if ((c = strchr(error, '\n')) == NULL) + c = error + strlen(error); + + temp = g_strndup(error, c - error); + error = gaim_url_decode(temp); + g_free(temp); + } + } + + msn_session_set_error(session, MSN_ERROR_AUTH, error); + } + else if (strstr(nexus->read_buf, "HTTP/1.1 200 OK")) + { + char *base, *c; + char *login_params; + +#if 0 + /* All your base are belong to us. */ + base = buffer; + + /* For great cookie! */ + while ((base = strstr(base, "Set-Cookie: ")) != NULL) + { + base += strlen("Set-Cookie: "); + + c = strchr(base, ';'); + + session->login_cookies = + g_list_append(session->login_cookies, + g_strndup(base, c - base)); + } +#endif + + base = strstr(nexus->read_buf, "Authentication-Info: "); + + g_return_if_fail(base != NULL); + + base = strstr(base, "from-PP='"); + base += strlen("from-PP='"); + c = strchr(base, '\''); + + login_params = g_strndup(base, c - base); + + msn_got_login_params(session, login_params); + + g_free(login_params); + + msn_nexus_destroy(nexus); + session->nexus = NULL; + return; + } + + g_free(nexus->read_buf); + nexus->read_buf = NULL; + nexus->read_len = 0; + +} + + +void login_connect_cb(gpointer data, GaimSslConnection *gsc, GaimInputCondition cond) { @@ -115,7 +304,6 @@ char *request_str, *head, *tail; char *buffer = NULL; guint32 ctint; - size_t s; nexus = data; g_return_if_fail(nexus != NULL); @@ -123,6 +311,8 @@ session = nexus->session; g_return_if_fail(session != NULL); + nexus->gsc = gsc; + msn_session_set_login_step(session, MSN_LOGIN_STEP_GET_COOKIE); username = @@ -169,168 +359,64 @@ g_free(username); g_free(password); - if ((s = gaim_ssl_write(gsc, request_str, strlen(request_str))) <= 0) - { - g_free(request_str); + nexus->write_buf = request_str; + nexus->written_len = 0; + + nexus->read_len = 0; + + nexus->written_cb = nexus_login_written_cb; + + nexus->input_handler = gaim_input_add(gsc->fd, GAIM_INPUT_WRITE, + nexus_write_cb, nexus); + + nexus_write_cb(nexus, gsc->fd, GAIM_INPUT_WRITE); + + return; + + +} +static void +nexus_connect_written_cb(gpointer data, gint source, GaimInputCondition cond) +{ + MsnNexus *nexus = data; + int len; + char *da_login; + char *base, *c; + + if (nexus->input_handler == -1) + nexus->input_handler = gaim_input_add(nexus->gsc->fd, + GAIM_INPUT_READ, nexus_connect_written_cb, nexus); + + /* Get the PassportURLs line. */ + len = msn_ssl_read(nexus); + + if (len < 0 && errno == EAGAIN) + return; + else if (len < 0) { + gaim_input_remove(nexus->input_handler); + nexus->input_handler = -1; + g_free(nexus->read_buf); + nexus->read_buf = NULL; + nexus->read_len = 0; + /* TODO: error handling */ return; } - g_free(request_str); - - if ((s = msn_ssl_read(gsc, &buffer)) <= 0) + if (g_strstr_len(nexus->read_buf, nexus->read_len, + "\r\n\r\n") == NULL) return; - gaim_ssl_close(gsc); - - gaim_debug_misc("msn", "ssl buffer: {%s}", buffer); - - if (strstr(buffer, "HTTP/1.1 302") != NULL) - { - /* Redirect. */ - char *location, *c; - - location = strstr(buffer, "Location: "); - if (location == NULL) - { - g_free(buffer); - - return; - } - location = strchr(location, ' ') + 1; - - if ((c = strchr(location, '\r')) != NULL) - *c = '\0'; - - /* Skip the http:// */ - if ((c = strchr(location, '/')) != NULL) - location = c + 2; - - if ((c = strchr(location, '/')) != NULL) - { - g_free(nexus->login_path); - nexus->login_path = g_strdup(c); - - *c = '\0'; - } - - g_free(nexus->login_host); - nexus->login_host = g_strdup(location); - - gaim_ssl_connect(session->account, nexus->login_host, - GAIM_SSL_DEFAULT_PORT, login_connect_cb, - login_error_cb, nexus); - } - else if (strstr(buffer, "HTTP/1.1 401 Unauthorized") != NULL) - { - const char *error; - - if ((error = strstr(buffer, "WWW-Authenticate")) != NULL) - { - if ((error = strstr(error, "cbtxt=")) != NULL) - { - const char *c; - char *temp; - - error += strlen("cbtxt="); - - if ((c = strchr(error, '\n')) == NULL) - c = error + strlen(error); - - temp = g_strndup(error, c - error); - error = gaim_url_decode(temp); - g_free(temp); - } - } - - msn_session_set_error(session, MSN_ERROR_AUTH, error); - } - else if (strstr(buffer, "HTTP/1.1 200 OK")) - { - char *base, *c; - char *login_params; - -#if 0 - /* All your base are belong to us. */ - base = buffer; + gaim_input_remove(nexus->input_handler); + nexus->input_handler = -1; - /* For great cookie! */ - while ((base = strstr(base, "Set-Cookie: ")) != NULL) - { - base += strlen("Set-Cookie: "); - - c = strchr(base, ';'); - - session->login_cookies = - g_list_append(session->login_cookies, - g_strndup(base, c - base)); - } -#endif - - base = strstr(buffer, "Authentication-Info: "); - - g_return_if_fail(base != NULL); - - base = strstr(base, "from-PP='"); - base += strlen("from-PP='"); - c = strchr(base, '\''); - - login_params = g_strndup(base, c - base); - - msn_got_login_params(session, login_params); - - g_free(login_params); - - msn_nexus_destroy(nexus); - session->nexus = NULL; - } - - g_free(buffer); -} - -/************************************************************************** - * Connect - **************************************************************************/ - -static void -nexus_connect_cb(gpointer data, GaimSslConnection *gsc, - GaimInputCondition cond) -{ - MsnNexus *nexus; - MsnSession *session; - char *request_str; - char *da_login; - char *base, *c; - char *buffer = NULL; - size_t s; - - nexus = data; - g_return_if_fail(nexus != NULL); - - session = nexus->session; - g_return_if_fail(session != NULL); - - msn_session_set_login_step(session, MSN_LOGIN_STEP_AUTH); - - request_str = g_strdup_printf("GET /rdr/pprdr.asp\r\n\r\n"); - - if ((s = gaim_ssl_write(gsc, request_str, strlen(request_str))) <= 0) - { - g_free(request_str); - return; - } - - g_free(request_str); - - /* Get the PassportURLs line. */ - if ((s = msn_ssl_read(gsc, &buffer)) <= 0) - return; - - base = strstr(buffer, "PassportURLs"); + base = strstr(nexus->read_buf, "PassportURLs"); if (base == NULL) { - g_free(buffer); + g_free(nexus->read_buf); + nexus->read_buf = NULL; + nexus->read_len = 0; return; } @@ -345,27 +431,64 @@ if ((c = strchr(da_login, '/')) != NULL) { nexus->login_path = g_strdup(c); - *c = '\0'; } nexus->login_host = g_strdup(da_login); } - g_free(buffer); + g_free(nexus->read_buf); + nexus->read_buf = NULL; + nexus->read_len = 0; - gaim_ssl_close(gsc); + gaim_ssl_close(nexus->gsc); + nexus->gsc = NULL; /* Now begin the connection to the login server. */ - gaim_ssl_connect(session->account, nexus->login_host, - GAIM_SSL_DEFAULT_PORT, login_connect_cb, - login_error_cb, nexus); + gaim_ssl_connect(nexus->session->account, nexus->login_host, + GAIM_SSL_DEFAULT_PORT, login_connect_cb, login_error_cb, + nexus); +} + + +/************************************************************************** + * Connect + **************************************************************************/ + +static void +nexus_connect_cb(gpointer data, GaimSslConnection *gsc, + GaimInputCondition cond) +{ + MsnNexus *nexus; + MsnSession *session; + + nexus = data; + g_return_if_fail(nexus != NULL); + + session = nexus->session; + g_return_if_fail(session != NULL); + + nexus->gsc = gsc; + + msn_session_set_login_step(session, MSN_LOGIN_STEP_AUTH); + + nexus->write_buf = g_strdup("GET /rdr/pprdr.asp\r\n\r\n"); + nexus->written_len = 0; + + nexus->read_len = 0; + + nexus->written_cb = nexus_connect_written_cb; + + nexus->input_handler = gaim_input_add(gsc->fd, GAIM_INPUT_WRITE, + nexus_write_cb, nexus); + + nexus_write_cb(nexus, gsc->fd, GAIM_INPUT_WRITE); } void msn_nexus_connect(MsnNexus *nexus) { gaim_ssl_connect(nexus->session->account, "nexus.passport.com", - GAIM_SSL_DEFAULT_PORT, nexus_connect_cb, - login_error_cb, nexus); + GAIM_SSL_DEFAULT_PORT, nexus_connect_cb, + login_error_cb, nexus); } diff -r d8f238864c88 -r 33bef17125c2 src/protocols/msn/nexus.h --- a/src/protocols/msn/nexus.h Thu Feb 09 04:14:54 2006 +0000 +++ b/src/protocols/msn/nexus.h Thu Feb 09 04:17:56 2006 +0000 @@ -26,8 +26,6 @@ typedef struct _MsnNexus MsnNexus; -#include "nexus.h" - struct _MsnNexus { MsnSession *session; @@ -35,6 +33,16 @@ char *login_host; char *login_path; GHashTable *challenge_data; + GaimSslConnection *gsc; + + guint input_handler; + + char *write_buf; + gsize written_len; + GaimInputFunction written_cb; + + char *read_buf; + gsize read_len; }; void msn_nexus_connect(MsnNexus *nexus); diff -r d8f238864c88 -r 33bef17125c2 src/protocols/msn/servconn.c --- a/src/protocols/msn/servconn.c Thu Feb 09 04:14:54 2006 +0000 +++ b/src/protocols/msn/servconn.c Thu Feb 09 04:17:56 2006 +0000 @@ -50,6 +50,9 @@ servconn->num = session->servconns_count++; + servconn->tx_buf = gaim_circ_buffer_new(MSN_BUF_LEN); + servconn->tx_handler = -1; + return servconn; } @@ -73,8 +76,11 @@ if (servconn->httpconn != NULL) msn_httpconn_destroy(servconn->httpconn); - if (servconn->host != NULL) - g_free(servconn->host); + g_free(servconn->host); + + gaim_circ_buffer_destroy(servconn->tx_buf); + if (servconn->tx_handler > 0) + gaim_input_remove(servconn->tx_handler); msn_cmdproc_destroy(servconn->cmdproc); g_free(servconn); @@ -181,7 +187,7 @@ /* Someone wants to know we connected. */ servconn->connect_cb(servconn); servconn->inpa = gaim_input_add(servconn->fd, GAIM_INPUT_READ, - read_cb, data); + read_cb, data); } else { @@ -227,7 +233,7 @@ } r = gaim_proxy_connect(session->account, host, port, connect_cb, - servconn); + servconn); if (r == 0) { @@ -279,30 +285,72 @@ servconn->disconnect_cb(servconn); } +static void +servconn_write_cb(gpointer data, gint source, GaimInputCondition cond) +{ + MsnServConn *servconn = data; + int ret, writelen; + + writelen = gaim_circ_buffer_get_max_read(servconn->tx_buf); + + if (writelen == 0) { + gaim_input_remove(servconn->tx_handler); + servconn->tx_handler = -1; + return; + } + + ret = write(servconn->fd, servconn->tx_buf->outptr, writelen); + + if (ret < 0 && errno == EAGAIN) + return; + else if (ret <= 0) { + msn_servconn_got_error(servconn, MSN_SERVCONN_ERROR_WRITE); + return; + } + + gaim_circ_buffer_mark_read(servconn->tx_buf, ret); +} + size_t msn_servconn_write(MsnServConn *servconn, const char *buf, size_t len) { - size_t ret = FALSE; + size_t ret = 0; g_return_val_if_fail(servconn != NULL, 0); if (!servconn->session->http_method) { - switch (servconn->type) - { - case MSN_SERVCONN_NS: - case MSN_SERVCONN_SB: - ret = write(servconn->fd, buf, len); - break; + if (servconn->tx_handler == -1) { + switch (servconn->type) + { + case MSN_SERVCONN_NS: + case MSN_SERVCONN_SB: + ret = write(servconn->fd, buf, len); + break; #if 0 - case MSN_SERVCONN_DC: - ret = write(servconn->fd, &buf, sizeof(len)); - ret = write(servconn->fd, buf, len); - break; + case MSN_SERVCONN_DC: + ret = write(servconn->fd, &buf, sizeof(len)); + ret = write(servconn->fd, buf, len); + break; #endif - default: - ret = write(servconn->fd, buf, len); - break; + default: + ret = write(servconn->fd, buf, len); + break; + } + } else { + ret = -1; + errno = EAGAIN; + } + + if (ret < 0 && errno == EAGAIN) + ret = 0; + if (ret < len) { + if (servconn->tx_handler == -1) + servconn->tx_handler = gaim_input_add( + servconn->fd, GAIM_INPUT_WRITE, + servconn_write_cb, servconn); + gaim_circ_buffer_append(servconn->tx_buf, buf + ret, + len - ret); } } else @@ -332,7 +380,9 @@ len = read(servconn->fd, buf, sizeof(buf) - 1); - if (len <= 0) + if (len < 0 && errno == EAGAIN) + return; + else if (len <= 0) { gaim_debug_error("msn", "servconn read error, len: %d error: %s\n", len, strerror(errno)); msn_servconn_got_error(servconn, MSN_SERVCONN_ERROR_READ); diff -r d8f238864c88 -r 33bef17125c2 src/protocols/msn/servconn.h --- a/src/protocols/msn/servconn.h Thu Feb 09 04:14:54 2006 +0000 +++ b/src/protocols/msn/servconn.h Thu Feb 09 04:17:56 2006 +0000 @@ -84,6 +84,9 @@ It's only set when we've received a command that has a payload. */ + GaimCircBuffer *tx_buf; + guint tx_handler; + void (*connect_cb)(MsnServConn *); /**< The callback to call when connecting. */ void (*disconnect_cb)(MsnServConn *); /**< The callback to call when disconnecting. */ void (*destroy_cb)(MsnServConn *); /**< The callback to call when destroying. */ diff -r d8f238864c88 -r 33bef17125c2 src/protocols/napster/napster.c --- a/src/protocols/napster/napster.c Thu Feb 09 04:14:54 2006 +0000 +++ b/src/protocols/napster/napster.c Thu Feb 09 04:17:56 2006 +0000 @@ -508,6 +508,11 @@ return; } + /* Clear the nonblocking flag + This protocol should be updated to support nonblocking I/O if + anyone is going to actually use it */ + fcntl(source, F_SETFL, 0); + ndata->fd = source; /* Update the login progress status display */ diff -r d8f238864c88 -r 33bef17125c2 src/protocols/oscar/oscar.c --- a/src/protocols/oscar/oscar.c Thu Feb 09 04:14:54 2006 +0000 +++ b/src/protocols/oscar/oscar.c Thu Feb 09 04:17:56 2006 +0000 @@ -51,8 +51,8 @@ #define OSCAR_STATUS_ID_OFFLINE "offline" #define OSCAR_STATUS_ID_AVAILABLE "available" #define OSCAR_STATUS_ID_AWAY "away" -#define OSCAR_STATUS_ID_DND "dnd" -#define OSCAR_STATUS_ID_NA "na" +#define OSCAR_STATUS_ID_DND "dnd" +#define OSCAR_STATUS_ID_NA "na" #define OSCAR_STATUS_ID_OCCUPIED "occupied" #define OSCAR_STATUS_ID_FREE4CHAT "free4chat" #define OSCAR_STATUS_ID_CUSTOM "custom" @@ -1018,6 +1018,9 @@ g_return_if_fail(gc != NULL); + /* XXX:NBIO remove when nonblocking I/O implemented for oscar */ + fcntl(source, F_SETFL, 0); + dim->gpc_pend = FALSE; if (dim->killme) { oscar_direct_im_destroy(od, dim); @@ -1780,6 +1783,9 @@ return; } + /* XXX:NBIO remove when nonblocking I/O implemented for oscar */ + fcntl(source, F_SETFL, 0); + od = gc->proto_data; sess = od->sess; conn = aim_getconn_type_all(sess, AIM_CONN_TYPE_AUTH); @@ -1949,6 +1955,9 @@ return; } + /* XXX:NBIO remove when nonblocking I/O implemented for oscar */ + fcntl(source, F_SETFL, 0); + aim_conn_completeconnect(sess, bosconn); gc->inpa = gaim_input_add(bosconn->fd, GAIM_INPUT_READ, oscar_callback, bosconn); @@ -2310,6 +2319,9 @@ struct aim_oft_info *oft_info; struct aim_rv_proxy_info *proxy_info; + /* XXX:NBIO remove when nonblocking I/O implemented for oscar */ + fcntl(fd, F_SETFL, 0); + gaim_debug_info("oscar","AAA - in oscar_xfer_proxylogin_ready\n"); if (!(oft_info = xfer->data)) { gaim_debug_warning("oscar","NULL oft_info; aborting\n"); @@ -3063,6 +3075,9 @@ return; } + /* XXX:NBIO remove when nonblocking I/O implemented for oscar */ + fcntl(source, F_SETFL, 0); + buf = g_strdup_printf("GET " AIMHASHDATA "?offset=%ld&len=%ld&modname=%s HTTP/1.0\n\n", pos->offset, pos->len, pos->modname ? pos->modname : ""); write(pos->fd, buf, strlen(buf)); @@ -3256,6 +3271,9 @@ return; } + /* XXX:NBIO remove when nonblocking I/O implemented for oscar */ + fcntl(source, F_SETFL, 0); + aim_conn_completeconnect(sess, tstconn); od->cnpa = gaim_input_add(tstconn->fd, GAIM_INPUT_READ, oscar_callback, tstconn); gaim_debug_info("oscar", "chatnav: connected\n"); @@ -3285,6 +3303,9 @@ return; } + /* XXX:NBIO remove when nonblocking I/O implemented for oscar */ + fcntl(source, F_SETFL, 0); + aim_conn_completeconnect(sess, tstconn); od->paspa = gaim_input_add(tstconn->fd, GAIM_INPUT_READ, oscar_callback, tstconn); gaim_debug_info("oscar", "admin: connected\n"); @@ -3319,6 +3340,9 @@ return; } + /* XXX:NBIO remove when nonblocking I/O implemented for oscar */ + fcntl(source, F_SETFL, 0); + aim_conn_completeconnect(sess, ccon->conn); ccon->inpa = gaim_input_add(tstconn->fd, GAIM_INPUT_READ, oscar_callback, tstconn); od->oscar_chats = g_slist_append(od->oscar_chats, ccon); @@ -3347,6 +3371,9 @@ return; } + /* XXX:NBIO remove when nonblocking I/O implemented for oscar */ + fcntl(source, F_SETFL, 0); + aim_conn_completeconnect(sess, tstconn); od->emlpa = gaim_input_add(tstconn->fd, GAIM_INPUT_READ, oscar_callback, tstconn); gaim_debug_info("oscar", @@ -3376,6 +3403,9 @@ return; } + /* XXX:NBIO remove when nonblocking I/O implemented for oscar */ + fcntl(source, F_SETFL, 0); + aim_conn_completeconnect(sess, tstconn); od->icopa = gaim_input_add(tstconn->fd, GAIM_INPUT_READ, oscar_callback, tstconn); gaim_debug_info("oscar", "icon: connected\n"); @@ -3817,14 +3847,18 @@ if(oft_info->success) { gaim_debug_info("oscar","connection already successful; ignoring 2nd conn\n"); return; - } + } + if (source < 0) { gaim_debug_info("oscar","received fd of %d; aborting transfer\n", source); gaim_xfer_cancel_remote(xfer); return; } oft_info->success = TRUE; /* Mark this connection as successful before it times out */ - + + /* XXX:NBIO remove when nonblocking I/O implemented for oscar */ + fcntl(source, F_SETFL, 0); + /* We might have already set these in oscar_sendfile_proxylogin, but it won't * hurt to do it again since it is rather necessary */ xfer->fd = source; diff -r d8f238864c88 -r 33bef17125c2 src/protocols/simple/simple.c --- a/src/protocols/simple/simple.c Thu Feb 09 04:14:54 2006 +0000 +++ b/src/protocols/simple/simple.c Thu Feb 09 04:17:56 2006 +0000 @@ -69,7 +69,7 @@ struct simple_account_data *sip = gc->proto_data; if(sip->udp) { /* in case of UDP send a packet only with a 0 byte to remain in the NAT table */ - gchar buf[2]={0,0}; + gchar buf[2] = {0, 0}; gaim_debug_info("simple", "sending keep alive\n"); sendto(sip->fd, buf, 1, 0, (struct sockaddr*)&sip->serveraddr, sizeof(struct sockaddr_in)); } @@ -163,7 +163,7 @@ } static struct sip_connection *connection_create(struct simple_account_data *sip, int fd) { - struct sip_connection *ret = g_new0(struct sip_connection,1); + struct sip_connection *ret = g_new0(struct sip_connection, 1); ret->fd = fd; sip->openconns = g_slist_append(sip->openconns, ret); return ret; @@ -191,25 +191,25 @@ { struct simple_account_data *sip = (struct simple_account_data *)gc->proto_data; struct simple_buddy *b; - if(strncmp("sip:", buddy->name,4)) { - gchar *buf = g_strdup_printf("sip:%s",buddy->name); + if(strncmp("sip:", buddy->name, 4)) { + gchar *buf = g_strdup_printf("sip:%s", buddy->name); gaim_blist_rename_buddy(buddy, buf); g_free(buf); } if(!g_hash_table_lookup(sip->buddies, buddy->name)) { b = g_new0(struct simple_buddy, 1); - gaim_debug_info("simple","simple_add_buddy %s\n",buddy->name); + gaim_debug_info("simple", "simple_add_buddy %s\n", buddy->name); b->name = g_strdup(buddy->name); g_hash_table_insert(sip->buddies, b->name, b); } else { - gaim_debug_info("simple","buddy %s already in internal list\n", buddy->name); + gaim_debug_info("simple", "buddy %s already in internal list\n", buddy->name); } } static void simple_get_buddies(GaimConnection *gc) { GaimBlistNode *gnode, *cnode, *bnode; - gaim_debug_info("simple","simple_get_buddies\n"); + gaim_debug_info("simple", "simple_get_buddies\n"); for(gnode = gaim_get_blist()->root; gnode; gnode = gnode->next) { if(!GAIM_BLIST_NODE_IS_GROUP(gnode)) continue; @@ -250,7 +250,8 @@ return types; } -static gchar *auth_header(struct simple_account_data *sip, struct sip_auth *auth, gchar *method, gchar *target) { +static gchar *auth_header(struct simple_account_data *sip, + struct sip_auth *auth, const gchar *method, const gchar *target) { gchar noncecount[9]; gchar *response; gchar *ret; @@ -261,7 +262,7 @@ authdomain = gaim_account_get_string(sip->account, "authdomain", ""); authuser = gaim_account_get_string(sip->account, "authuser", sip->username); - if(!authuser || strlen(authuser)<1) { + if(!authuser || strlen(authuser) < 1) { authuser = sip->username; } @@ -297,7 +298,7 @@ return ret; } -static char * parse_attribute(const char *attrname, char *source) { +static char *parse_attribute(const char *attrname, char *source) { char *tmp, *tmp2, *retval = NULL; int len = strlen(attrname); @@ -314,14 +315,14 @@ } static void fill_auth(struct simple_account_data *sip, gchar *hdr, struct sip_auth *auth) { - int i=0; + int i = 0; const char *authuser; char *tmp; gchar **parts; authuser = gaim_account_get_string(sip->account, "authuser", sip->username); - if(!authuser || strlen(authuser)<1) { + if(!authuser || strlen(authuser) < 1) { authuser = sip->username; } @@ -334,11 +335,11 @@ gaim_debug_info("simple", "found NTLM\n"); auth->type = 2; if(!strstr(hdr, "gssapi-data")) { - gaim_debug_info("simple","here"); + gaim_debug_info("simple", "here"); parts = g_strsplit(hdr+5, "\", ", 0); i = 0; while(parts[i]) { - gaim_debug_info("simple","parts[i] %s\n",parts[i]); + gaim_debug_info("simple", "parts[i] %s\n", parts[i]); if((tmp = parse_attribute("targetname=\"", parts[i]))) { auth->target = tmp; @@ -388,7 +389,34 @@ auth->digest_session_key = gaim_cipher_http_digest_calculate_session_key( "md5", authuser, auth->realm, sip->password, auth->nonce, NULL); - auth->nc=1; + auth->nc = 1; +} + +static void simple_canwrite_cb(gpointer data, gint source, GaimInputCondition cond) { + GaimConnection *gc = data; + struct simple_account_data *sip = gc->proto_data; + gsize max_write; + gssize written; + + max_write = gaim_circ_buffer_get_max_read(sip->txbuf); + + if(max_write == 0) { + gaim_input_remove(sip->tx_handler); + sip->tx_handler = 0; + return; + } + + written = write(sip->fd, sip->txbuf->outptr, max_write); + + if(written < 0 && errno == EAGAIN) + written = 0; + else if(written <= 0) { + /*TODO: do we really want to disconnect on a failure to write?*/ + gaim_connection_error(gc, _("Could not write")); + return; + } + + gaim_circ_buffer_mark_read(sip->txbuf, written); } static void simple_input_cb(gpointer data, gint source, GaimInputCondition cond); @@ -398,18 +426,23 @@ struct simple_account_data *sip = gc->proto_data; struct sip_connection *conn; - if( source < 0 ) { - gaim_connection_error(gc,"Could not connect"); + if(source < 0) { + gaim_connection_error(gc, _("Could not connect")); return; } sip->fd = source; sip->connecting = FALSE; - write(sip->fd, sip->sendlater, strlen(sip->sendlater)); + + simple_canwrite_cb(gc, sip->fd, GAIM_INPUT_WRITE); + + /* If there is more to write now, we need to register a handler */ + if(sip->txbuf->bufused > 0) + sip->tx_handler = gaim_input_add(sip->fd, GAIM_INPUT_WRITE, + simple_canwrite_cb, gc); + conn = connection_create(sip, source); conn->inputhandler = gaim_input_add(sip->fd, GAIM_INPUT_READ, simple_input_cb, gc); - g_free(sip->sendlater); - sip->sendlater = NULL; } @@ -417,44 +450,65 @@ struct simple_account_data *sip = gc->proto_data; int error = 0; if(!sip->connecting) { - gaim_debug_info("simple","connecting to %s port %d\n", sip->realhostname ? sip->realhostname : "{NULL}", sip->realport); + gaim_debug_info("simple", "connecting to %s port %d\n", sip->realhostname ? sip->realhostname : "{NULL}", sip->realport); error = gaim_proxy_connect(sip->account, sip->realhostname, sip->realport, send_later_cb, gc); if(error) { gaim_connection_error(gc, _("Couldn't create socket")); } sip->connecting = TRUE; } - if(sip->sendlater) { - gchar *old = sip->sendlater; - sip->sendlater = g_strdup_printf("%s\r\n%s",old, buf); - g_free(old); - } else { - sip->sendlater = g_strdup(buf); - } + + if(gaim_circ_buffer_get_max_read(sip->txbuf) > 0) + gaim_circ_buffer_append(sip->txbuf, "\r\n", 2); + + gaim_circ_buffer_append(sip->txbuf, buf, strlen(buf)); } -static int sendout_pkt(GaimConnection *gc, const char *buf) { +static void sendout_pkt(GaimConnection *gc, const char *buf) { struct simple_account_data *sip = gc->proto_data; time_t currtime = time(NULL); - int ret = 0; + int writelen = strlen(buf); gaim_debug(GAIM_DEBUG_MISC, "simple", "\n\nsending - %s\n######\n%s\n######\n\n", ctime(&currtime), buf); if(sip->udp) { - if(sendto(sip->fd, buf, strlen(buf), 0, (struct sockaddr*)&sip->serveraddr, sizeof(struct sockaddr_in)) < strlen(buf)) { + if(sendto(sip->fd, buf, writelen, 0, (struct sockaddr*)&sip->serveraddr, sizeof(struct sockaddr_in)) < writelen) { gaim_debug_info("simple", "could not send packet\n"); } } else { - if(sip->fd <0 ) { + int ret; + if(sip->fd < 0) { sendlater(gc, buf); - return 0; + return; } - ret = write(sip->fd, buf, strlen(buf)); - if(ret < 0) { - sendlater(gc,buf); - return 0; + + if(sip->tx_handler) { + ret = -1; + errno = EAGAIN; + } else + ret = write(sip->fd, buf, writelen); + + if (ret < 0 && errno == EAGAIN) + ret = 0; + else if(ret <= 0) { /* XXX: When does this happen legitimately? */ + sendlater(gc, buf); + return; + } + + if (ret < writelen) { + if(!sip->tx_handler) + sip->tx_handler = gaim_input_add(sip->fd, + GAIM_INPUT_WRITE, simple_canwrite_cb, + gc); + + /* XXX: is it OK to do this? You might get part of a request sent + with part of another. */ + if(sip->txbuf->bufused > 0) + gaim_circ_buffer_append(sip->txbuf, "\r\n", 2); + + gaim_circ_buffer_append(sip->txbuf, buf + ret, + writelen - ret); } } - return ret; } static void sendout_sipmsg(struct simple_account_data *sip, struct sipmsg *msg) { @@ -487,7 +541,7 @@ if(body) { gchar len[12]; sprintf(len, "%" G_GSIZE_FORMAT , strlen(body)); - sipmsg_add_header(msg, "Content-Length",len); + sipmsg_add_header(msg, "Content-Length", len); } else sipmsg_add_header(msg, "Content-Length", "0"); @@ -532,18 +586,20 @@ transactions = transactions->next; } - return (struct transaction *)NULL; + return NULL; } -static void send_sip_request(GaimConnection *gc, gchar *method, gchar *url, gchar *to, gchar *addheaders, gchar *body, struct sip_dialog *dialog, TransCallback tc) { +static void send_sip_request(GaimConnection *gc, const gchar *method, + const gchar *url, const gchar *to, const gchar *addheaders, + const gchar *body, struct sip_dialog *dialog, TransCallback tc) { struct simple_account_data *sip = gc->proto_data; char *callid = dialog ? g_strdup(dialog->callid) : gencallid(); char *auth = ""; - char *addh = ""; + const char *addh = ""; gchar *branch = genbranch(); char *buf; - if(!strcmp(method,"REGISTER")) { + if(!strcmp(method, "REGISTER")) { if(sip->regcallid) { g_free(callid); callid = g_strdup(sip->regcallid); @@ -552,14 +608,14 @@ } if(addheaders) addh = addheaders; - if(sip->registrar.type && !strcmp(method,"REGISTER")) { + if(sip->registrar.type && !strcmp(method, "REGISTER")) { buf = auth_header(sip, &sip->registrar, method, url); auth = g_strdup_printf("Authorization: %s", buf); g_free(buf); gaim_debug(GAIM_DEBUG_MISC, "simple", "header %s", auth); } - if(sip->proxy.type && strcmp(method,"REGISTER")) { + if(sip->proxy.type && strcmp(method, "REGISTER")) { buf = auth_header(sip, &sip->proxy, method, url); auth = g_strdup_printf("Proxy-Authorization: %s", buf); g_free(buf); @@ -603,7 +659,7 @@ transactions_add_buf(sip, buf, tc); - sendout_pkt(gc,buf); + sendout_pkt(gc, buf); g_free(buf); } @@ -613,8 +669,8 @@ } static void do_register_exp(struct simple_account_data *sip, int expire) { - char *uri = g_strdup_printf("sip:%s",sip->servername); - char *to = g_strdup_printf("sip:%s@%s",sip->username,sip->servername); + char *uri = g_strdup_printf("sip:%s", sip->servername); + char *to = g_strdup_printf("sip:%s@%s", sip->username, sip->servername); char *contact = get_contact(sip); char *hdr = g_strdup_printf("Contact: %s\r\nExpires: %d\r\n", contact, expire); g_free(contact); @@ -626,7 +682,10 @@ } else { sip->reregister = time(NULL) + 600; } - send_sip_request(sip->gc,"REGISTER",uri,to, hdr, "", NULL, process_register_response); + + send_sip_request(sip->gc, "REGISTER", uri, to, hdr, "", NULL, + process_register_response); + g_free(hdr); g_free(uri); g_free(to); @@ -641,15 +700,15 @@ gchar *tmp; if(!from) return NULL; - gaim_debug_info("simple", "parsing address out of %s\n",from); + gaim_debug_info("simple", "parsing address out of %s\n", from); tmp = strchr(from, '<'); /* i hate the different SIP UA behaviours... */ if(tmp) { /* sip address in <...> */ from = tmp+1; - tmp = strchr(from,'>'); + tmp = strchr(from, '>'); if(tmp) { - from = g_strndup(from,tmp-from); + from = g_strndup(from, tmp-from); } else { gaim_debug_info("simple", "found < without > in From\n"); return NULL; @@ -657,19 +716,19 @@ } else { tmp = strchr(from, ';'); if(tmp) { - from = g_strndup(from,tmp-from); + from = g_strndup(from, tmp-from); } else { from = g_strdup(from); } } - gaim_debug_info("simple", "got %s\n",from); + gaim_debug_info("simple", "got %s\n", from); return from; } static gboolean process_subscribe_response(struct simple_account_data *sip, struct sipmsg *msg, struct transaction *tc) { - gchar *to = parse_from(sipmsg_find_header(tc->msg,"To")); /* cant be NULL since it is our own msg */ + gchar *to = parse_from(sipmsg_find_header(tc->msg, "To")); /* cant be NULL since it is our own msg */ - if(msg->response==200 || msg->response==202) { + if(msg->response == 200 || msg->response == 202) { return TRUE; } @@ -684,15 +743,21 @@ gchar *contact = "Expires: 300\r\nAccept: application/pidf+xml, application/xpidf+xml\r\nEvent: presence\r\n"; gchar *to; gchar *tmp; - if(strstr(buddy->name,"sip:")) to = g_strdup(buddy->name); - else to = g_strdup_printf("sip:%s",buddy->name); + + if(strstr(buddy->name,"sip:")) + to = g_strdup(buddy->name); + else + to = g_strdup_printf("sip:%s", buddy->name); + tmp = get_contact(sip); contact = g_strdup_printf("%sContact: %s\r\n", contact, tmp); g_free(tmp); + /* subscribe to buddy presence * we dont need to know the status so we do not need a callback */ - send_sip_request(sip->gc, "SUBSCRIBE",to, to, contact, "", NULL, process_subscribe_response); + send_sip_request(sip->gc, "SUBSCRIBE", to, to, contact, "", NULL, + process_subscribe_response); g_free(to); g_free(contact); @@ -704,9 +769,9 @@ static void simple_buddy_resub(char *name, struct simple_buddy *buddy, struct simple_account_data *sip) { time_t curtime = time(NULL); - gaim_debug_info("simple","buddy resub\n"); + gaim_debug_info("simple", "buddy resub\n"); if(buddy->resubscribe < curtime) { - gaim_debug(GAIM_DEBUG_MISC, "simple", "simple_buddy_resub %s\n",name); + gaim_debug(GAIM_DEBUG_MISC, "simple", "simple_buddy_resub %s\n", name); simple_subscribe(sip, buddy); } } @@ -755,16 +820,16 @@ return TRUE; } -static void simple_send_message(struct simple_account_data *sip, char *to, char *msg, char *type) { +static void simple_send_message(struct simple_account_data *sip, const char *to, const char *msg, const char *type) { gchar *hdr; gchar *fullto; - if(strncmp("sip:",to,4)) { - fullto = g_strdup_printf("sip:%s",to); + if(strncmp("sip:", to, 4)) { + fullto = g_strdup_printf("sip:%s", to); } else { fullto = g_strdup(to); } if(type) { - hdr = g_strdup_printf("Content-Type: %s\r\n",type); + hdr = g_strdup_printf("Content-Type: %s\r\n", type); } else { hdr = g_strdup("Content-Type: text/plain\r\n"); } @@ -800,26 +865,26 @@ send_sip_response(sip->gc, msg, 200, "OK", NULL); found = TRUE; } - if(!strncmp(contenttype, "application/im-iscomposing+xml",30)) { + if(!strncmp(contenttype, "application/im-iscomposing+xml", 30)) { xmlnode *isc = xmlnode_from_str(msg->body, msg->bodylen); xmlnode *state; gchar *statedata; if(!isc) { - gaim_debug_info("simple","process_incoming_message: can not parse iscomposing\n"); + gaim_debug_info("simple", "process_incoming_message: can not parse iscomposing\n"); return; } state = xmlnode_get_child(isc, "state"); if(!state) { - gaim_debug_info("simple","process_incoming_message: no state found\n"); + gaim_debug_info("simple", "process_incoming_message: no state found\n"); return; } statedata = xmlnode_get_data(state); if(statedata) { - if(strstr(statedata,"active")) serv_got_typing(sip->gc, from, 0, GAIM_TYPING); + if(strstr(statedata, "active")) serv_got_typing(sip->gc, from, 0, GAIM_TYPING); else serv_got_typing_stopped(sip->gc, from); } xmlnode_free(isc); @@ -839,12 +904,12 @@ gaim_debug(GAIM_DEBUG_MISC, "simple", "in process register response response: %d\n", msg->response); switch (msg->response) { case 200: - if(sip->registerstatus<3) { /* registered */ + if(sip->registerstatus < 3) { /* registered */ if(gaim_account_get_bool(sip->account, "dopublish", TRUE)) { send_publish(sip); } } - sip->registerstatus=3; + sip->registerstatus = 3; gaim_connection_set_state(sip->gc, GAIM_CONNECTED); /* get buddies from blist */ @@ -853,15 +918,15 @@ subscribe_timeout(sip); break; case 401: - if(sip->registerstatus!=2) { - gaim_debug_info("simple","REGISTER retries %d\n",sip->registrar.retries); - if(sip->registrar.retries>3) { - gaim_connection_error(sip->gc,"Wrong Password"); + if(sip->registerstatus != 2) { + gaim_debug_info("simple", "REGISTER retries %d\n", sip->registrar.retries); + if(sip->registrar.retries > 3) { + gaim_connection_error(sip->gc, _("Wrong Password")); return TRUE; } tmp = sipmsg_find_header(msg, "WWW-Authenticate"); fill_auth(sip, tmp, &sip->registrar); - sip->registerstatus=2; + sip->registerstatus = 2; do_register(sip); } break; @@ -877,28 +942,28 @@ xmlnode *basicstatus; gboolean isonline = FALSE; - fromhdr = sipmsg_find_header(msg,"From"); + fromhdr = sipmsg_find_header(msg, "From"); from = parse_from(fromhdr); if(!from) return; pidf = xmlnode_from_str(msg->body, msg->bodylen); if(!pidf) { - gaim_debug_info("simple","process_incoming_notify: no parseable pidf\n"); + gaim_debug_info("simple", "process_incoming_notify: no parseable pidf\n"); return; } - basicstatus = xmlnode_get_child(xmlnode_get_child(xmlnode_get_child(pidf,"tuple"),"status"), "basic"); + basicstatus = xmlnode_get_child(xmlnode_get_child(xmlnode_get_child(pidf, "tuple"), "status"), "basic"); if(!basicstatus) { - gaim_debug_info("simple","process_incoming_notify: no basic found\n"); + gaim_debug_info("simple", "process_incoming_notify: no basic found\n"); return; } tmp2 = xmlnode_get_data(basicstatus); if(!tmp2) { - gaim_debug_info("simple","process_incoming_notify: no basic data found\n"); + gaim_debug_info("simple", "process_incoming_notify: no basic data found\n"); return; } @@ -1013,7 +1078,10 @@ static void send_publish(struct simple_account_data *sip) { gchar *uri = g_strdup_printf("sip:%s@%s", sip->username, sip->servername); gchar *doc = gen_pidf(sip); - send_sip_request(sip->gc, "PUBLISH", uri, uri, "Expires: 600\r\nEvent: presence\r\nContent-Type: application/pidf+xml\r\n", doc, NULL, process_publish_response); + send_sip_request(sip->gc, "PUBLISH", uri, uri, + "Expires: 600\r\nEvent: presence\r\n" + "Content-Type: application/pidf+xml\r\n", + doc, NULL, process_publish_response); sip->republish = time(NULL) + 500; g_free(doc); } @@ -1106,7 +1174,7 @@ if(msg->response == 407) { gchar *resend, *auth, *ptmp; - if(sip->proxy.retries>3) return; + if(sip->proxy.retries > 3) return; sip->proxy.retries++; /* do proxy authentication */ @@ -1124,7 +1192,7 @@ } else { if(msg->response == 100) { /* ignore provisional response */ - gaim_debug_info("simple","got trying response\n"); + gaim_debug_info("simple", "got trying response\n"); } else { sip->proxy.retries = 0; if(msg->response == 401) sip->registrar.retries++; @@ -1142,7 +1210,7 @@ } } if(!found) { - gaim_debug(GAIM_DEBUG_MISC, "simple", "received a unknown sip message with method %s and response %d\n",msg->method, msg->response); + gaim_debug(GAIM_DEBUG_MISC, "simple", "received a unknown sip message with method %s and response %d\n", msg->method, msg->response); } } @@ -1160,33 +1228,33 @@ } if(cur != conn->inbuf) { memmove(conn->inbuf, cur, conn->inbufused-(cur-conn->inbuf)); - conn->inbufused=strlen(conn->inbuf); + conn->inbufused = strlen(conn->inbuf); } /* Received a full Header? */ - if((cur = strstr(conn->inbuf, "\r\n\r\n"))!=NULL) { + if((cur = strstr(conn->inbuf, "\r\n\r\n")) != NULL) { time_t currtime = time(NULL); cur += 2; cur[0] = '\0'; - gaim_debug_info("simple","\n\nreceived - %s\n######\n%s\n#######\n\n",ctime(&currtime), conn->inbuf); + gaim_debug_info("simple", "\n\nreceived - %s\n######\n%s\n#######\n\n", ctime(&currtime), conn->inbuf); msg = sipmsg_parse_header(conn->inbuf); cur[0] = '\r'; cur += 2; restlen = conn->inbufused - (cur-conn->inbuf); - if(restlen>=msg->bodylen) { - dummy = g_malloc(msg->bodylen+1); + if(restlen >= msg->bodylen) { + dummy = g_malloc(msg->bodylen + 1); memcpy(dummy, cur, msg->bodylen); - dummy[msg->bodylen]='\0'; + dummy[msg->bodylen] = '\0'; msg->body = dummy; - cur+=msg->bodylen; - memmove(conn->inbuf, cur, conn->inbuflen-(cur-conn->inbuf)); - conn->inbufused=strlen(conn->inbuf); + cur += msg->bodylen; + memmove(conn->inbuf, cur, conn->inbuflen - (cur - conn->inbuf)); + conn->inbufused = strlen(conn->inbuf); } else { sipmsg_free(msg); return; } gaim_debug(GAIM_DEBUG_MISC, "simple", "in process response response: %d\n", msg->response); - process_input_message(sip,msg); + process_input_message(sip, msg); } else { gaim_debug(GAIM_DEBUG_MISC, "simple", "received a incomplete sip msg: %s\n", conn->inbuf); } @@ -1202,7 +1270,7 @@ static char buffer[65536]; if((len = recv(source, buffer, sizeof(buffer) - 1, 0)) > 0) { buffer[len] = '\0'; - gaim_debug_info("simple","\n\nreceived - %s\n######\n%s\n#######\n\n",ctime(&currtime), buffer); + gaim_debug_info("simple", "\n\nreceived - %s\n######\n%s\n#######\n\n", ctime(&currtime), buffer); msg = sipmsg_parse_msg(buffer); if(msg) process_input_message(sip, msg); } @@ -1219,25 +1287,24 @@ return; } - if (conn->inbuflen < conn->inbufused + SIMPLE_BUF_INC) { + if(conn->inbuflen < conn->inbufused + SIMPLE_BUF_INC) { conn->inbuflen += SIMPLE_BUF_INC; conn->inbuf = g_realloc(conn->inbuf, conn->inbuflen); } - if ((len = read(source, conn->inbuf + conn->inbufused, SIMPLE_BUF_INC - 1)) <= 0) { - gaim_debug_info("simple","simple_input_cb: read error\n"); + len = read(source, conn->inbuf + conn->inbufused, SIMPLE_BUF_INC - 1); + + if(len < 0 && errno == EAGAIN) + return; + else if(len <= 0) { + gaim_debug_info("simple", "simple_input_cb: read error\n"); connection_remove(sip, source); if(sip->fd == source) sip->fd = -1; return; } - if(len == 0) { - /* connection was closed */ - connection_remove(sip, source); - if(sip->fd == source) sip->fd = -1; - } conn->inbufused += len; - conn->inbuf[conn->inbufused]='\0'; + conn->inbuf[conn->inbufused] = '\0'; process_input(sip, conn); } @@ -1260,8 +1327,8 @@ struct simple_account_data *sip = gc->proto_data; struct sip_connection *conn; - if( source < 0 ) { - gaim_connection_error(gc,"Could not connect"); + if(source < 0) { + gaim_connection_error(gc, _("Could not connect")); return; } @@ -1351,7 +1418,7 @@ sip->listenport = gaim_network_get_port_from_fd(sip->listenfd); sip->listenpa = gaim_input_add(sip->listenfd, GAIM_INPUT_READ, simple_newconn_cb, sip->gc); - gaim_debug_info("simple","connecting to %s port %d\n", + gaim_debug_info("simple", "connecting to %s port %d\n", sip->realhostname, sip->realport); /* open tcp connection to the server */ error = gaim_proxy_connect(sip->account, sip->realhostname, @@ -1367,7 +1434,6 @@ gchar *hostname; int port = gaim_account_get_int(sip->account, "port", 0); - /* find the host to connect to */ if(results) { hostname = g_strdup(resp->hostname); @@ -1385,8 +1451,9 @@ sip->realhostname = hostname; sip->realport = port; if(!sip->realport) sip->realport = 5060; + /* TCP case */ - if(! sip->udp) { + if(!sip->udp) { /* create socket for incoming connections */ if(!gaim_network_listen_range(5060, 5160, SOCK_STREAM, simple_tcp_connect_listen_cb, sip)) { @@ -1408,20 +1475,24 @@ gchar *hosttoconnect; const char *username = gaim_account_get_username(account); + gc = gaim_account_get_connection(account); - gc = gaim_account_get_connection(account); - gc->proto_data = sip = g_new0(struct simple_account_data,1); - sip->gc=gc; - sip->account = account; - sip->registerexpire = 900; - sip->udp = gaim_account_get_bool(account, "udp", FALSE); if (strpbrk(username, " \t\v\r\n") != NULL) { gaim_connection_error(gc, _("SIP usernames may not contain whitespaces or @ symbols")); return; } + gc->proto_data = sip = g_new0(struct simple_account_data, 1); + sip->gc = gc; + sip->account = account; + sip->registerexpire = 900; + sip->udp = gaim_account_get_bool(account, "udp", FALSE); + /* TODO: is there a good default grow size? */ + if(!sip->udp) + sip->txbuf = gaim_circ_buffer_new(0); + userserver = g_strsplit(username, "@", 2); - gaim_connection_set_display_name(gc,userserver[0]); + gaim_connection_set_display_name(gc, userserver[0]); sip->username = g_strdup(userserver[0]); sip->servername = g_strdup(userserver[1]); sip->password = g_strdup(gaim_connection_get_password(gc)); @@ -1441,10 +1512,10 @@ } /* TCP case */ - if(! sip->udp) { - gaim_srv_resolve("sip","tcp",hosttoconnect,srvresolved, sip); + if(!sip->udp) { + gaim_srv_resolve("sip", "tcp", hosttoconnect, srvresolved, sip); } else { /* UDP */ - gaim_srv_resolve("sip","udp",hosttoconnect,srvresolved, sip); + gaim_srv_resolve("sip", "udp", hosttoconnect, srvresolved, sip); } g_free(hosttoconnect); } @@ -1470,12 +1541,13 @@ g_free(sip->proxy.target); g_free(sip->proxy.realm); g_free(sip->proxy.digest_session_key); - g_free(sip->sendlater); + if(sip->txbuf) + gaim_circ_buffer_destroy(sip->txbuf); g_free(sip->realhostname); if(sip->listenpa) gaim_input_remove(sip->listenpa); + if(sip->tx_handler) gaim_input_remove(sip->tx_handler); if(sip->resendtimeout) gaim_timeout_remove(sip->resendtimeout); if(sip->registertimeout) gaim_timeout_remove(sip->registertimeout); - sip->servername = sip->username = sip->password = sip->registrar.nonce = sip->registrar.realm = sip->proxy.nonce = sip->proxy.realm = sip->sendlater = sip->realhostname = NULL; } g_free(gc->proto_data); gc->proto_data = NULL; @@ -1597,7 +1669,6 @@ option = gaim_account_option_int_new(_("Connect port"), "port", 0); prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option); - option = gaim_account_option_bool_new(_("Use UDP"), "udp", FALSE); prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option); option = gaim_account_option_bool_new(_("Use proxy"), "useproxy", FALSE); diff -r d8f238864c88 -r 33bef17125c2 src/protocols/simple/simple.h --- a/src/protocols/simple/simple.h Thu Feb 09 04:14:54 2006 +0000 +++ b/src/protocols/simple/simple.h Thu Feb 09 04:17:56 2006 +0000 @@ -27,6 +27,7 @@ #include #include +#include #include #include "sipmsg.h" @@ -84,7 +85,8 @@ guint resendtimeout; gboolean connecting; GaimAccount *account; - gchar *sendlater; + GaimCircBuffer *txbuf; + guint tx_handler; gchar *regcallid; GSList *transactions; GSList *watcher; diff -r d8f238864c88 -r 33bef17125c2 src/protocols/yahoo/yahoo.c --- a/src/protocols/yahoo/yahoo.c Thu Feb 09 04:14:54 2006 +0000 +++ b/src/protocols/yahoo/yahoo.c Thu Feb 09 04:17:56 2006 +0000 @@ -2286,19 +2286,34 @@ GaimConnection *gc = data; GaimAccount *account = gaim_connection_get_account(gc); struct yahoo_data *yd = gc->proto_data; - char buf[2048], *i = buf; + char bufread[2048], *i = bufread, *buf = bufread; int len; GString *s; - len = read(source, buf, sizeof(buf)-1); - if (len <= 0 || (strncmp(buf, "HTTP/1.0 302", strlen("HTTP/1.0 302")) && + len = read(source, bufread, sizeof(bufread) - 1); + if (len < 0 && errno == EAGAIN) + return; + else if (len <= 0) { + gaim_connection_error(gc, _("Unable to read")); + return; + } + + if (yd->rxlen > 0 || !g_strstr_len(buf, len, "\r\n\r\n")) { + yd->rxqueue = g_realloc(yd->rxqueue, yd->rxlen + len + 1); + memcpy(yd->rxqueue + yd->rxlen, buf, len); + yd->rxlen += len; + i = buf = yd->rxqueue; + len = yd->rxlen; + } + buf[len] = '\0'; + + if ((strncmp(buf, "HTTP/1.0 302", strlen("HTTP/1.0 302")) && strncmp(buf, "HTTP/1.1 302", strlen("HTTP/1.1 302")))) { gaim_connection_error(gc, _("Unable to read")); return; } s = g_string_sized_new(len); - buf[sizeof(buf)-1] = '\0'; while ((i = strstr(i, "Set-Cookie: "))) { i += strlen("Set-Cookie: "); @@ -2311,6 +2326,9 @@ yd->auth = g_string_free(s, FALSE); gaim_input_remove(gc->inpa); close(source); + g_free(yd->rxqueue); + yd->rxqueue = NULL; + yd->rxlen = 0; /* Now we have our cookies to login with. I'll go get the milk. */ if (gaim_proxy_connect(account, "wcs2.msg.dcn.yahoo.com", gaim_account_get_int(account, "port", YAHOO_PAGER_PORT), @@ -2324,12 +2342,41 @@ { GaimConnection *gc = data; struct yahoo_data *yd = gc->proto_data; + int written, total_len; + if (source < 0) { gaim_connection_error(gc, _("Unable to connect.")); return; } - write(source, yd->auth, strlen(yd->auth)); + + total_len = strlen(yd->auth) - yd->auth_written; + written = write(source, yd->auth + yd->auth_written, total_len); + + if (written < 0 && errno == EAGAIN) + written = 0; + else if (written <= 0) { + g_free(yd->auth); + yd->auth = NULL; + if (gc->inpa) + gaim_input_remove(gc->inpa); + gc->inpa = 0; + gaim_connection_error(gc, _("Unable to connect.")); + return; + } + + if (written < total_len) { + yd->auth_written += written; + if (!gc->inpa) + gc->inpa = gaim_input_add(source, GAIM_INPUT_WRITE, + yahoo_got_cookies, gc); + return; + } + g_free(yd->auth); + yd->auth = NULL; + yd->auth_written = 0; + if (gc->inpa) + gaim_input_remove(gc->inpa); gc->inpa = gaim_input_add(source, GAIM_INPUT_READ, yahoo_web_pending, gc); } @@ -2526,6 +2573,9 @@ gaim_connection_set_display_name(gc, gaim_account_get_username(account)); yd->fd = -1; + yd->txhandler = -1; + /* TODO: Is there a good grow size for the buffer? */ + yd->txbuf = gaim_circ_buffer_new(0); yd->friends = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, yahoo_friend_free); yd->confs = NULL; yd->conf_id = 2; @@ -2579,22 +2629,23 @@ yahoo_c_leave(gc, 1); /* 1 = YAHOO_CHAT_ID */ g_hash_table_destroy(yd->friends); - if (yd->chat_name) - g_free(yd->chat_name); - - if (yd->cookie_y) - g_free(yd->cookie_y); - if (yd->cookie_t) - g_free(yd->cookie_t); + g_free(yd->chat_name); + + g_free(yd->cookie_y); + g_free(yd->cookie_t); + + if (yd->txhandler) + gaim_input_remove(yd->txhandler); + + gaim_circ_buffer_destroy(yd->txbuf); if (yd->fd >= 0) close(yd->fd); - if (yd->rxqueue) - g_free(yd->rxqueue); + g_free(yd->rxqueue); yd->rxlen = 0; - if (yd->picture_url) - g_free(yd->picture_url); + g_free(yd->picture_url); + if (yd->picture_upload_todo) yahoo_buddy_icon_upload_data_free(yd->picture_upload_todo); if (yd->ycht) diff -r d8f238864c88 -r 33bef17125c2 src/protocols/yahoo/yahoo.h --- a/src/protocols/yahoo/yahoo.h Thu Feb 09 04:14:54 2006 +0000 +++ b/src/protocols/yahoo/yahoo.h Thu Feb 09 04:17:56 2006 +0000 @@ -26,6 +26,7 @@ #define _YAHOO_H_ #include "prpl.h" +#include "gaim_buffer.h" #define YAHOO_PAGER_HOST "scs.msg.yahoo.com" #define YAHOO_PAGER_PORT 5050 @@ -97,9 +98,12 @@ struct _YchtConn; struct yahoo_data { + GaimConnection *gc; int fd; guchar *rxqueue; int rxlen; + GaimCircBuffer *txbuf; + guint txhandler; GHashTable *friends; int current_status; gboolean logged_in; @@ -110,6 +114,7 @@ gboolean in_chat; char *chat_name; char *auth; + gsize auth_written; char *cookie_y; char *cookie_t; int session_id; diff -r d8f238864c88 -r 33bef17125c2 src/protocols/yahoo/yahoo_filexfer.c --- a/src/protocols/yahoo/yahoo_filexfer.c Thu Feb 09 04:14:54 2006 +0000 +++ b/src/protocols/yahoo/yahoo_filexfer.c Thu Feb 09 04:17:56 2006 +0000 @@ -39,16 +39,21 @@ GaimConnection *gc; long expires; gboolean started; + guchar *txbuf; + gsize txbuflen; + gsize txbuf_written; + guint tx_handler; gchar *rxqueue; guint rxlen; }; static void yahoo_xfer_data_free(struct yahoo_xfer_data *xd) { - if (xd->host) - g_free(xd->host); - if (xd->path) - g_free(xd->path); + g_free(xd->host); + g_free(xd->path); + g_free(xd->txbuf); + if (xd->tx_handler) + gaim_input_remove(xd->tx_handler); g_free(xd); } @@ -56,7 +61,7 @@ { GaimXfer *xfer; struct yahoo_xfer_data *xd; - gchar *buf; + int total_len, written; gaim_debug(GAIM_DEBUG_INFO, "yahoo", "AAA - in yahoo_receivefile_connected\n"); @@ -71,29 +76,53 @@ return; } + /* The first time we get here, assemble the tx buffer */ + if (xd->txbuflen == 0) { + xd->txbuf = g_strdup_printf("GET /%s HTTP/1.0\r\nHost: %s\r\n\r\n", + xd->path, xd->host); + xd->txbuflen = strlen(xd->txbuf); + xd->txbuf_written = 0; + } + + total_len = xd->txbuflen - xd->txbuf_written; + xfer->fd = source; + + written = write(xfer->fd, xd->txbuf + xd->txbuf_written, total_len); + + if (written < 0 && errno == EAGAIN) + written = 0; + else if (written <= 0) { + gaim_debug_error("yahoo", "Unable to write in order to start ft errno = %d\n", errno); + gaim_xfer_cancel_remote(xfer); + return; + } + + if (written < total_len) { + if (!xd->tx_handler) + xd->tx_handler = gaim_input_add(source, GAIM_INPUT_WRITE, + yahoo_receivefile_connected, xfer); + xd->txbuf_written += written; + return; + } + + if (xd->tx_handler) + gaim_input_remove(xd->tx_handler); + xd->tx_handler = 0; + g_free(xd->txbuf); + xd->txbuf = NULL; + xd->txbuflen = 0; + gaim_xfer_start(xfer, source, NULL, 0); - buf = g_strdup_printf("GET /%s HTTP/1.0\r\nHost: %s\r\n\r\n", - xd->path, xd->host); - write(xfer->fd, buf, strlen(buf)); - g_free(buf); +} - return; -} static void yahoo_sendfile_connected(gpointer data, gint source, GaimInputCondition condition) { GaimXfer *xfer; struct yahoo_xfer_data *xd; - struct yahoo_packet *pkt; - gchar *size, *post, *buf; - const char *host; - int content_length, port; - GaimConnection *gc; - GaimAccount *account; - struct yahoo_data *yd; - char *filename, *encoded_filename; + int written, total_len; gaim_debug(GAIM_DEBUG_INFO, "yahoo", "AAA - in yahoo_sendfile_connected\n"); @@ -102,9 +131,6 @@ if (!(xd = xfer->data)) return; - gc = xd->gc; - account = gaim_connection_get_account(gc); - yd = gc->proto_data; if (source < 0) { gaim_xfer_error(GAIM_XFER_RECEIVE, gaim_xfer_get_account(xfer), @@ -113,43 +139,96 @@ return; } - xfer->fd = source; - gaim_xfer_start(xfer, source, NULL, 0); + /* The first time we get here, assemble the tx buffer */ + if (xd->txbuflen == 0) { + struct yahoo_packet *pkt; + gchar *size, *filename, *encoded_filename, *header; + guchar *pkt_buf; + const char *host; + int port; + gsize content_length, header_len, pkt_buf_len; + GaimConnection *gc; + GaimAccount *account; + struct yahoo_data *yd; - pkt = yahoo_packet_new(YAHOO_SERVICE_FILETRANSFER, YAHOO_STATUS_AVAILABLE, yd->session_id); + gc = xd->gc; + account = gaim_connection_get_account(gc); + yd = gc->proto_data; + + pkt = yahoo_packet_new(YAHOO_SERVICE_FILETRANSFER, + YAHOO_STATUS_AVAILABLE, yd->session_id); - size = g_strdup_printf("%" G_GSIZE_FORMAT, gaim_xfer_get_size(xfer)); - filename = g_path_get_basename(gaim_xfer_get_local_filename(xfer)); - encoded_filename = yahoo_string_encode(gc, filename, NULL); + size = g_strdup_printf("%" G_GSIZE_FORMAT, gaim_xfer_get_size(xfer)); + filename = g_path_get_basename(gaim_xfer_get_local_filename(xfer)); + encoded_filename = yahoo_string_encode(gc, filename, NULL); + + yahoo_packet_hash(pkt, "sssss", 0, gaim_connection_get_display_name(gc), + 5, xfer->who, 14, "", 27, encoded_filename, 28, size); + g_free(size); + g_free(encoded_filename); + g_free(filename); + + content_length = YAHOO_PACKET_HDRLEN + yahoo_packet_length(pkt); - yahoo_packet_hash(pkt, "sssss", 0, gaim_connection_get_display_name(gc), - 5, xfer->who, 14, "", 27, encoded_filename, 28, size); + pkt_buf_len = yahoo_packet_build(pkt, 8, FALSE, &pkt_buf); + yahoo_packet_free(pkt); - content_length = YAHOO_PACKET_HDRLEN + yahoo_packet_length(pkt); - - buf = g_strdup_printf("Y=%s; T=%s", yd->cookie_y, yd->cookie_t); + host = gaim_account_get_string(account, "xfer_host", YAHOO_XFER_HOST); + port = gaim_account_get_int(account, "xfer_port", YAHOO_XFER_PORT); + header = g_strdup_printf( + "POST http://%s:%d/notifyft HTTP/1.0\r\n" + "Content-length: %" G_GSIZE_FORMAT "\r\n" + "Host: %s:%d\r\n" + "Cookie: Y=%s; T=%s\r\n" + "\r\n", + host, port, content_length + 4 + gaim_xfer_get_size(xfer), + host, port, yd->cookie_y, yd->cookie_t); - host = gaim_account_get_string(account, "xfer_host", YAHOO_XFER_HOST); - port = gaim_account_get_int(account, "xfer_port", YAHOO_XFER_PORT); - post = g_strdup_printf("POST http://%s:%d/notifyft HTTP/1.0\r\n" - "Content-length: %" G_GSIZE_FORMAT "\r\n" - "Host: %s:%d\r\n" - "Cookie: %s\r\n" - "\r\n", - host, port, content_length + 4 + gaim_xfer_get_size(xfer), - host, port, buf); - write(xfer->fd, post, strlen(post)); + + header_len = strlen(header); + + xd->txbuflen = header_len + pkt_buf_len + 4; + xd->txbuf = g_malloc(xd->txbuflen); + + memcpy(xd->txbuf, header, header_len); + g_free(header); + memcpy(xd->txbuf + header_len, pkt_buf, pkt_buf_len); + g_free(pkt_buf); + memcpy(xd->txbuf + header_len + pkt_buf_len, "29\xc0\x80", 4); + + xd->txbuf_written = 0; + } + + total_len = xd->txbuflen - xd->txbuf_written; + + xfer->fd = source; + + written = write(xfer->fd, xd->txbuf + xd->txbuf_written, total_len); - yahoo_packet_send_special(pkt, xfer->fd, 8); - yahoo_packet_free(pkt); - - write(xfer->fd, "29\xc0\x80", 4); + if (written < 0 && errno == EAGAIN) + written = 0; + else if (written <= 0) { + gaim_debug_error("yahoo", "Unable to write in order to start ft errno = %d\n", errno); + gaim_xfer_cancel_remote(xfer); + return; + } - g_free(size); - g_free(post); - g_free(buf); - g_free(encoded_filename); - g_free(filename); + if (written < total_len) { + if (!xd->tx_handler) + xd->tx_handler = gaim_input_add(source, GAIM_INPUT_WRITE, + yahoo_sendfile_connected, xfer); + xd->txbuf_written += written; + return; + } + + if (xd->tx_handler) + gaim_input_remove(xd->tx_handler); + xd->tx_handler = 0; + g_free(xd->txbuf); + xd->txbuf = NULL; + xd->txbuflen = 0; + + gaim_xfer_start(xfer, source, NULL, 0); } static void yahoo_xfer_init(GaimXfer *xfer) diff -r d8f238864c88 -r 33bef17125c2 src/protocols/yahoo/yahoo_packet.c --- a/src/protocols/yahoo/yahoo_packet.c Thu Feb 09 04:14:54 2006 +0000 +++ b/src/protocols/yahoo/yahoo_packet.c Thu Feb 09 04:17:56 2006 +0000 @@ -242,39 +242,97 @@ #endif } -int yahoo_packet_send(struct yahoo_packet *pkt, struct yahoo_data *yd) +static void +yahoo_packet_send_can_write(gpointer data, gint source, GaimInputCondition cond) +{ + struct yahoo_data *yd = data; + int ret, writelen; + + writelen = gaim_circ_buffer_get_max_read(yd->txbuf); + + if (writelen == 0) { + gaim_input_remove(yd->txhandler); + yd->txhandler = -1; + return; + } + + ret = write(yd->fd, yd->txbuf->outptr, writelen); + + if (ret < 0 && errno == EAGAIN) + return; + else if (ret < 0) { + /* TODO: what to do here - do we really have to disconnect? */ + gaim_connection_error(yd->gc, _("Write Error")); + return; + } + + gaim_circ_buffer_mark_read(yd->txbuf, ret); +} + + +gsize yahoo_packet_build(struct yahoo_packet *pkt, int pad, gboolean wm, + guchar **buf) { int pktlen = yahoo_packet_length(pkt); int len = YAHOO_PACKET_HDRLEN + pktlen; - int ret; - guchar *data; int pos = 0; - if (yd->fd < 0) - return -1; - data = g_malloc0(len + 1); memcpy(data + pos, "YMSG", 4); pos += 4; - if (yd->wm) + if (wm) pos += yahoo_put16(data + pos, YAHOO_WEBMESSENGER_PROTO_VER); else pos += yahoo_put16(data + pos, YAHOO_PROTO_VER); - pos += yahoo_put16(data + pos, 0x0000); - pos += yahoo_put16(data + pos, pktlen); + pos += yahoo_put16(data + pos, pktlen + pad); pos += yahoo_put16(data + pos, pkt->service); pos += yahoo_put32(data + pos, pkt->status); pos += yahoo_put32(data + pos, pkt->id); yahoo_packet_write(pkt, data + pos); + *buf = data; + + return len; +} + +int yahoo_packet_send(struct yahoo_packet *pkt, struct yahoo_data *yd) +{ + gsize len; + int ret; + guchar *data; + + if (yd->fd < 0) + return -1; + + len = yahoo_packet_build(pkt, 0, yd->wm, &data); + yahoo_packet_dump(data, len); - ret = write(yd->fd, data, len); - if (ret != len) + if (yd->txhandler == -1) + ret = write(yd->fd, data, len); + else { + ret = -1; + errno = EAGAIN; + } + + if (ret < 0 && errno == EAGAIN) + ret = 0; + else if (ret <= 0) { gaim_debug_warning("yahoo", "Only wrote %d of %d bytes!", ret, len); + g_free(data); + return ret; + } + + if (ret < len) { + if (yd->txhandler == -1) + yd->txhandler = gaim_input_add(yd->fd, GAIM_INPUT_WRITE, + yahoo_packet_send_can_write, yd); + gaim_circ_buffer_append(yd->txbuf, data + ret, len - ret); + } + g_free(data); return ret; @@ -289,37 +347,6 @@ return ret; } -int yahoo_packet_send_special(struct yahoo_packet *pkt, int fd, int pad) -{ - int pktlen = yahoo_packet_length(pkt); - int len = YAHOO_PACKET_HDRLEN + pktlen; - int ret; - - guchar *data; - int pos = 0; - - if (fd < 0) - return -1; - - data = g_malloc0(len + 1); - - memcpy(data + pos, "YMSG", 4); pos += 4; - - pos += yahoo_put16(data + pos, YAHOO_PROTO_VER); - pos += yahoo_put16(data + pos, 0x0000); - pos += yahoo_put16(data + pos, pktlen + pad); - pos += yahoo_put16(data + pos, pkt->service); - pos += yahoo_put32(data + pos, pkt->status); - pos += yahoo_put32(data + pos, pkt->id); - - yahoo_packet_write(pkt, data + pos); - - ret = write(fd, data, len); - g_free(data); - - return ret; -} - void yahoo_packet_free(struct yahoo_packet *pkt) { while (pkt->hash) { diff -r d8f238864c88 -r 33bef17125c2 src/protocols/yahoo/yahoo_packet.h --- a/src/protocols/yahoo/yahoo_packet.h Thu Feb 09 04:14:54 2006 +0000 +++ b/src/protocols/yahoo/yahoo_packet.h Thu Feb 09 04:17:56 2006 +0000 @@ -125,7 +125,8 @@ void yahoo_packet_hash_int(struct yahoo_packet *pkt, int key, int value); int yahoo_packet_send(struct yahoo_packet *pkt, struct yahoo_data *yd); int yahoo_packet_send_and_free(struct yahoo_packet *pkt, struct yahoo_data *yd); -int yahoo_packet_send_special(struct yahoo_packet *pkt, int fd, int pad); +gsize yahoo_packet_build(struct yahoo_packet *pkt, int pad, gboolean wm, +guchar **buf); void yahoo_packet_read(struct yahoo_packet *pkt, guchar *data, int len); void yahoo_packet_write(struct yahoo_packet *pkt, guchar *data); void yahoo_packet_dump(guchar *data, int len); diff -r d8f238864c88 -r 33bef17125c2 src/protocols/yahoo/yahoo_picture.c --- a/src/protocols/yahoo/yahoo_picture.c Thu Feb 09 04:14:54 2006 +0000 +++ b/src/protocols/yahoo/yahoo_picture.c Thu Feb 09 04:17:56 2006 +0000 @@ -307,8 +307,7 @@ if (d->str) g_string_free(d->str, TRUE); - if (d->filename) - g_free(d->filename); + g_free(d->filename); if (d->watcher) gaim_input_remove(d->watcher); if (d->fd != -1) @@ -316,19 +315,24 @@ g_free(d); } -/* we could care less about the server's responce, but yahoo gets grumpy if we close before it sends it */ +/* we couldn't care less about the server's response, but yahoo gets grumpy if we close before it sends it */ static void yahoo_buddy_icon_upload_reading(gpointer data, gint source, GaimInputCondition condition) { struct yahoo_buddy_icon_upload_data *d = data; GaimConnection *gc = d->gc; char buf[1024]; + int ret; if (!GAIM_CONNECTION_IS_VALID(gc)) { yahoo_buddy_icon_upload_data_free(d); return; } - if (read(d->fd, buf, sizeof(buf)) <= 0) + ret = read(d->fd, buf, sizeof(buf)); + + if (ret < 0 && errno == EAGAIN) + return; + else if (ret <= 0) yahoo_buddy_icon_upload_data_free(d); } @@ -344,6 +348,8 @@ } wrote = write(d->fd, d->str->str + d->pos, d->str->len - d->pos); + if (wrote < 0 && errno == EAGAIN) + return; if (wrote <= 0) { yahoo_buddy_icon_upload_data_free(d); return; @@ -360,9 +366,10 @@ { struct yahoo_buddy_icon_upload_data *d = data; struct yahoo_packet *pkt; - gchar *size, *post, *buf; + gchar *size, *header; + guchar *pkt_buf; const char *host; - int content_length, port; + gsize content_length, port, pkt_buf_len; GaimConnection *gc; GaimAccount *account; struct yahoo_data *yd; @@ -380,8 +387,6 @@ return; } - d->fd = source; - d->watcher = gaim_input_add(d->fd, GAIM_INPUT_WRITE, yahoo_buddy_icon_upload_pending, d); pkt = yahoo_packet_new(0xc2, YAHOO_STATUS_AVAILABLE, yd->session_id); @@ -392,30 +397,38 @@ gaim_account_set_int(account, YAHOO_PICEXPIRE_SETTING, time(NULL) + 604800); yahoo_packet_hash_str(pkt, 0, gaim_connection_get_display_name(gc)); yahoo_packet_hash_str(pkt, 28, size); + g_free(size); yahoo_packet_hash_str(pkt, 27, d->filename); yahoo_packet_hash_str(pkt, 14, ""); content_length = YAHOO_PACKET_HDRLEN + yahoo_packet_length(pkt); - buf = g_strdup_printf("Y=%s; T=%s", yd->cookie_y, yd->cookie_t); - host = gaim_account_get_string(account, "xfer_host", YAHOO_XFER_HOST); + host = gaim_account_get_string(account, "xfer_host", YAHOO_XFER_HOST); port = gaim_account_get_int(account, "xfer_port", YAHOO_XFER_PORT); - post = g_strdup_printf("POST http://%s:%d/notifyft HTTP/1.0\r\n" - "Content-length: %" G_GSIZE_FORMAT "\r\n" - "Host: %s:%d\r\n" - "Cookie: %s\r\n" - "\r\n", - host, port, content_length + 4 + d->str->len, host, port, buf); - write(d->fd, post, strlen(post)); + header = g_strdup_printf( + "POST http://%s:%d/notifyft HTTP/1.0\r\n" + "Content-length: %" G_GSIZE_FORMAT "\r\n" + "Host: %s:%d\r\n" + "Cookie: Y=%s; T=%s\r\n" + "\r\n", + host, port, content_length + 4 + d->str->len, + host, port, yd->cookie_y, yd->cookie_t); + + /* There's no magic here, we just need to prepend in reverse order */ + g_string_prepend(d->str, "29\xc0\x80"); - yahoo_packet_send_special(pkt, d->fd, 8); + pkt_buf_len = yahoo_packet_build(pkt, 8, FALSE, &pkt_buf); yahoo_packet_free(pkt); + g_string_prepend_len(d->str, pkt_buf, pkt_buf_len); + g_free(pkt_buf); - write(d->fd, "29\xc0\x80", 4); + g_string_prepend(d->str, header); + g_free(header); - g_free(size); - g_free(post); - g_free(buf); + d->fd = source; + d->watcher = gaim_input_add(d->fd, GAIM_INPUT_WRITE, yahoo_buddy_icon_upload_pending, d); + + yahoo_buddy_icon_upload_pending(d, d->fd, GAIM_INPUT_WRITE); } void yahoo_buddy_icon_upload(GaimConnection *gc, struct yahoo_buddy_icon_upload_data *d) diff -r d8f238864c88 -r 33bef17125c2 src/protocols/yahoo/yahoochat.c --- a/src/protocols/yahoo/yahoochat.c Thu Feb 09 04:14:54 2006 +0000 +++ b/src/protocols/yahoo/yahoochat.c Thu Feb 09 04:17:56 2006 +0000 @@ -1080,6 +1080,8 @@ struct yahoo_roomlist { int fd; int inpa; + guchar *txbuf; + gsize tx_written; guchar *rxqueue; int rxlen; gboolean started; @@ -1095,12 +1097,10 @@ { if (yrl->inpa) gaim_input_remove(yrl->inpa); - if (yrl->rxqueue) - g_free(yrl->rxqueue); - if (yrl->path) - g_free(yrl->path); - if (yrl->host) - g_free(yrl->host); + g_free(yrl->txbuf); + g_free(yrl->rxqueue); + g_free(yrl->path); + g_free(yrl->host); if (yrl->parse) g_markup_parse_context_free(yrl->parse); g_free(yrl); @@ -1143,12 +1143,9 @@ static void yahoo_chatxml_state_destroy(struct yahoo_chatxml_state *s) { g_queue_free(s->q); - if (s->room.name) - g_free(s->room.name); - if (s->room.topic) - g_free(s->room.topic); - if (s->room.id) - g_free(s->room.id); + g_free(s->room.name); + g_free(s->room.topic); + g_free(s->room.id); g_free(s); } @@ -1298,6 +1295,9 @@ len = read(yrl->fd, buf, sizeof(buf)); + if (len < 0 && errno == EAGAIN) + return; + if (len <= 0) { if (yrl->parse) g_markup_parse_context_end_parse(yrl->parse, NULL); @@ -1338,8 +1338,8 @@ { struct yahoo_roomlist *yrl = data; GaimRoomlist *list = yrl->list; - char *buf, *cookie; struct yahoo_data *yd = gaim_account_get_connection(list->account)->proto_data; + int written, total_len; if (source < 0) { gaim_notify_error(gaim_account_get_connection(list->account), NULL, _("Unable to connect"), _("Fetching the room list failed.")); @@ -1347,15 +1347,48 @@ return; } - yrl->fd = source; + if (yrl->txbuf == NULL) { + yrl->fd = source; + + yrl->txbuf = g_strdup_printf( + "GET http://%s/%s HTTP/1.0\r\n" + "Host: %s\r\n" + "Cookie: Y=%s; T=%s\r\n\r\n", + yrl->host, yrl->path, yrl->host, yd->cookie_y, + yd->cookie_t); + } + + total_len = strlen(yrl->txbuf) - yrl->tx_written; + written = write(yrl->fd, yrl->txbuf + yrl->tx_written, total_len); - cookie = g_strdup_printf("Y=%s; T=%s", yd->cookie_y, yd->cookie_t); - buf = g_strdup_printf("GET http://%s/%s HTTP/1.0\r\nHost: %s\r\nCookie: %s\r\n\r\n", - yrl->host, yrl->path, yrl->host, cookie); - write(yrl->fd, buf, strlen(buf)); - g_free(cookie); - g_free(buf); - yrl->inpa = gaim_input_add(yrl->fd, GAIM_INPUT_READ, yahoo_roomlist_pending, yrl); + if (written < 0 && errno == EAGAIN) + written = 0; + else if (written <= 0) { + if (yrl->inpa) + gaim_input_remove(yrl->inpa); + yrl->inpa = 0; + g_free(yrl->txbuf); + yrl->txbuf = NULL; + gaim_notify_error(gaim_account_get_connection(list->account), NULL, _("Unable to connect"), _("Fetching the room list failed.")); + yahoo_roomlist_cleanup(list, yrl); + return; + } + + if (written < total_len) { + if (!yrl->inpa) + yrl->inpa = gaim_input_add(yrl->fd, + GAIM_INPUT_WRITE, yahoo_roomlist_got_connected, + yrl); + yrl->tx_written += written; + return; + } + + g_free(yrl->txbuf); + yrl->txbuf = NULL; + if (yrl->inpa) + gaim_input_remove(yrl->inpa); + yrl->inpa = gaim_input_add(yrl->fd, GAIM_INPUT_READ, + yahoo_roomlist_pending, yrl); } diff -r d8f238864c88 -r 33bef17125c2 src/protocols/yahoo/ycht.c --- a/src/protocols/yahoo/ycht.c Thu Feb 09 04:14:54 2006 +0000 +++ b/src/protocols/yahoo/ycht.c Thu Feb 09 04:17:56 2006 +0000 @@ -265,9 +265,39 @@ return ret; } +static void ycht_packet_send_write_cb(gpointer data, gint source, GaimInputCondition cond) +{ + YchtConn *ycht = data; + int ret, writelen; + + writelen = gaim_circ_buffer_get_max_read(ycht->txbuf); + + if (writelen == 0) { + gaim_input_remove(ycht->tx_handler); + ycht->tx_handler = 0; + return; + } + + ret = write(ycht->fd, ycht->txbuf->outptr, writelen); + + if (ret < 0 && errno == EAGAIN) + return; + else if (ret <= 0) { + /* TODO: error handling */ +/* + gaim_connection_error(gaim_account_get_connection(irc->account), + _("Server has disconnected")); +*/ + return; + } + + gaim_circ_buffer_mark_read(ycht->txbuf, ret); + +} + static void ycht_packet_send(YchtConn *ycht, YchtPkt *pkt) { - int len, pos; + int len, pos, written; char *buf; GList *l; @@ -295,7 +325,29 @@ } } - write(ycht->fd, buf, len); + if (!ycht->tx_handler) + written = write(ycht->fd, buf, len); + else { + written = -1; + errno = EAGAIN; + } + + if (written < 0 && errno == EAGAIN) + written = 0; + else if (written <= 0) { + /* TODO: Error handling (was none before NBIO changes) */ + written = 0; + } + + if (written < len) { + if (!ycht->tx_handler) + ycht->tx_handler = gaim_input_add(ycht->fd, + GAIM_INPUT_WRITE, ycht_packet_send_write_cb, + ycht); + gaim_circ_buffer_append(ycht->txbuf, buf + written, + len - written); + } + g_free(buf); } @@ -388,8 +440,12 @@ if (ycht->inpa) gaim_input_remove(ycht->inpa); - if (ycht->rxqueue) - g_free(ycht->rxqueue); + if (ycht->tx_handler) + gaim_input_remove(ycht->tx_handler); + + gaim_circ_buffer_destroy(ycht->txbuf); + + g_free(ycht->rxqueue); g_free(ycht); } @@ -409,6 +465,9 @@ len = read(ycht->fd, buf, sizeof(buf)); + if (len < 0 && errno == EAGAIN) + return; + if (len <= 0) { ycht_connection_error(ycht, _("Unable to read")); return; @@ -529,8 +588,7 @@ char *tmp; tmp = g_strdup(room); - if (ycht->room) - g_free(ycht->room); + g_free(ycht->room); ycht->room = tmp; if (!ycht->logged_in) diff -r d8f238864c88 -r 33bef17125c2 src/protocols/yahoo/ycht.h --- a/src/protocols/yahoo/ycht.h Thu Feb 09 04:14:54 2006 +0000 +++ b/src/protocols/yahoo/ycht.h Thu Feb 09 04:17:56 2006 +0000 @@ -73,6 +73,8 @@ gboolean changing_rooms; guchar *rxqueue; guint rxlen; + GaimCircBuffer *txbuf; + guint tx_handler; } YchtConn; typedef struct { diff -r d8f238864c88 -r 33bef17125c2 src/proxy.c --- a/src/proxy.c Thu Feb 09 04:14:54 2006 +0000 +++ b/src/proxy.c Thu Feb 09 04:17:56 2006 +0000 @@ -49,6 +49,13 @@ GaimProxyInfo *gpi; GaimAccount *account; GSList *hosts; + guchar *write_buffer; + gsize write_buf_len; + gsize written_len; + GaimInputFunction read_cb; + guchar *read_buffer; + gsize read_buf_len; + gsize read_len; }; static void try_connect(struct PHB *); @@ -254,7 +261,7 @@ * Proxy API **************************************************************************/ -#ifdef __unix__ +#ifdef __unix__ /* * This structure represents both a pending DNS request and @@ -956,7 +963,6 @@ return; } - fcntl(source, F_SETFL, 0); gaim_input_remove(phb->inpa); if (phb->account == NULL || @@ -1027,7 +1033,6 @@ close(fd); return -1; } - fcntl(fd, F_SETFL, 0); phb->port = fd; /* bleh */ gaim_timeout_add(50, clean_connect, phb); /* we do this because we never want to call our callback @@ -1037,6 +1042,37 @@ return fd; } +static void +proxy_do_write(gpointer data, gint source, GaimInputCondition cond) +{ + struct PHB *phb = data; + const char * request = phb->write_buffer + phb->written_len; + gsize request_len = phb->write_buf_len - phb->written_len; + + int ret = write(source, request, request_len); + + if(ret < 0 && errno == EAGAIN) + return; + else if(ret < 0) { + gaim_input_remove(phb->inpa); + close(source); + g_free(phb->write_buffer); + phb->write_buffer = NULL; + try_connect(phb); + return; + } else if (ret < request_len) { + phb->written_len += ret; + return; + } + + gaim_input_remove(phb->inpa); + g_free(phb->write_buffer); + phb->write_buffer = NULL; + + /* register the response handler for the response */ + phb->inpa = gaim_input_add(source, GAIM_INPUT_READ, phb->read_cb, phb); +} + #define HTTP_GOODSTRING "HTTP/1.0 200" #define HTTP_GOODSTRING2 "HTTP/1.1 200" @@ -1044,13 +1080,11 @@ http_complete(struct PHB *phb, gint source) { gaim_debug_info("http proxy", "proxy connection established\n"); - if(source < 0) { - try_connect(phb); - } else if(!phb->account || phb->account->gc) { + if(!phb->account || phb->account->gc) { phb->func(phb->data, source, GAIM_INPUT_READ); - g_free(phb->host); - g_free(phb); } + g_free(phb->host); + g_free(phb); } @@ -1058,65 +1092,117 @@ static void http_canread(gpointer data, gint source, GaimInputCondition cond) { - int nlc = 0; - int pos = 0; - int minor, major, status = 0, error=0; + int len, headers_len, status = 0; + gboolean error; struct PHB *phb = data; - char inputline[8192], *p; + guchar *p; + gsize max_read; + if(phb->read_buffer == NULL) { + phb->read_buf_len = 8192; + phb->read_buffer = g_malloc(phb->read_buf_len); + phb->read_len = 0; + } + + p = phb->read_buffer + phb->read_len - 1; + max_read = phb->read_buf_len - phb->read_len; - while ((pos < sizeof(inputline)-1) && (nlc != 2) && (read(source, &inputline[pos++], 1) == 1)) { - if (inputline[pos - 1] == '\n') - nlc++; - else if (inputline[pos - 1] != '\r') - nlc = 0; + len = read(source, p, max_read); + if(len < 0 && errno == EAGAIN) + return; + else if(len < 0) { + close(source); + source = -1; + g_free(phb->read_buffer); + phb->read_buffer = NULL; + gaim_input_remove(phb->inpa); + phb->inpa = 0; + http_complete(phb, source); + return; + } else { + phb->read_len += len; } - inputline[pos] = '\0'; + p[len] = '\0'; - error = strncmp(inputline, "HTTP/", 5) != 0; + if((p = g_strstr_len(phb->read_buffer, phb->read_len, "\r\n\r\n"))) { + *p = '\0'; + headers_len = (p - phb->read_buffer) + 4; + } else if(len == max_read) + headers_len = len; + else + return; + + error = strncmp(phb->read_buffer, "HTTP/", 5) != 0; if(!error) { - p = inputline + 5; - major = strtol(p, &p, 10); - error = (major==0) || (*p != '.'); + char *c; + int major; + p = phb->read_buffer + 5; + c = p; + major = strtol(c, &c, 10); + p = c; + error = (major == 0) || (*p != '.'); if(!error) { + int minor; p++; - minor = strtol(p, &p, 10); - error = (*p!=' '); + c = p; + minor = strtol(c, &c, 10); + p = c; + error = (*p != ' '); if(!error) { p++; - status = strtol(p, &p, 10); - error = (*p!=' '); + c = p; + status = strtol(c, &c, 10); + p = c; + error = (*p != ' '); } } } /* Read the contents */ - p = g_strrstr(inputline, "Content-Length: "); + p = g_strrstr(phb->read_buffer, "Content-Length: "); if(p != NULL) { gchar *tmp; int len = 0; char tmpc; p += strlen("Content-Length: "); tmp = strchr(p, '\r'); - *tmp = 0; + *tmp = '\0'; len = atoi(p); *tmp = '\r'; - while(len--) read(source, &tmpc, 1); + + /* Compensate for what has already been read */ + len -= phb->read_len - headers_len; + /* I'm assuming that we're doing this to prevent the server from + complaining / breaking since we don't read the whole page */ + while(len--) { + /* TODO: deal with EAGAIN (and other errors) better */ + if (read(source, &tmpc, 1) < 0 && errno != EAGAIN) + break; + } } + if(error) { gaim_debug_error("proxy", - "Unable to parse proxy's response: %s\n", inputline); + "Unable to parse proxy's response: %s\n", + phb->read_buffer); close(source); - source=-1; - } - else if(status!=200) { + source = -1; + g_free(phb->read_buffer); + phb->read_buffer = NULL; + gaim_input_remove(phb->inpa); + phb->inpa = 0; + http_complete(phb, source); + return; + } else if(status != 200) { gaim_debug_error("proxy", - "Proxy server replied with:\n%s\n", inputline); + "Proxy server replied with:\n%s\n", + phb->read_buffer); + /* XXX: why in the hell are we calling gaim_connection_error() here? */ - if ( status == 407 /* Proxy Auth */ ) { + if(status == 407 /* Proxy Auth */) { gchar *ntlm; - if( (ntlm = g_strrstr(inputline, "Proxy-Authenticate: NTLM "))) { /* Check for Type-2 */ + if((ntlm = g_strrstr(phb->read_buffer, "Proxy-Authenticate: NTLM "))) { /* Check for Type-2 */ gchar *nonce = ntlm; gchar *domain = (gchar*)gaim_proxy_info_get_username(phb->gpi); gchar *username; @@ -1128,28 +1214,51 @@ source = -1; gaim_connection_error(phb->account->gc, msg); g_free(msg); - gaim_input_remove(phb->inpa); + gaim_input_remove(phb->inpa); + g_free(phb->read_buffer); + g_free(phb->host); + g_free(phb); return; } - *username = 0; - username ++; + *username = '\0'; + username++; ntlm += strlen("Proxy-Authenticate: NTLM "); while(*nonce != '\r' && *nonce != '\0') nonce ++; - *nonce = 0; + *nonce = '\0'; nonce = gaim_ntlm_parse_type2(ntlm, NULL); - response = gaim_ntlm_gen_type3(username, (gchar*)gaim_proxy_info_get_password(phb->gpi), (gchar*)gaim_proxy_info_get_host(phb->gpi), domain, nonce, NULL); + response = gaim_ntlm_gen_type3(username, + (gchar*) gaim_proxy_info_get_password(phb->gpi), + (gchar*) gaim_proxy_info_get_host(phb->gpi), + domain, nonce, NULL); username--; *username = '\\'; - request = g_strdup_printf("CONNECT %s:%d HTTP/1.1\r\nHost: %s:%d\r\nProxy-Authorization: NTLM %s\r\nProxy-Connection: Keep-Alive\r\n\r\n", - phb->host, phb->port, phb->host, phb->port, - response); - write(source, request, strlen(request)); - g_free(request); + request = g_strdup_printf( + "CONNECT %s:%d HTTP/1.1\r\n" + "Host: %s:%d\r\n" + "Proxy-Authorization: NTLM %s\r\n" + "Proxy-Connection: Keep-Alive\r\n\r\n", + phb->host, phb->port, phb->host, + phb->port, response); g_free(response); + + gaim_input_remove(phb->inpa); + g_free(phb->read_buffer); + phb->read_buffer = NULL; + + phb->write_buffer = request; + phb->write_buf_len = strlen(request); + phb->written_len = 0; + + phb->read_cb = http_canread; + + phb->inpa = gaim_input_add(source, + GAIM_INPUT_WRITE, proxy_do_write, phb); + + proxy_do_write(phb, source, cond); return; - } else if((ntlm = g_strrstr(inputline, "Proxy-Authenticate: NTLM"))) { /* Empty message */ + } else if((ntlm = g_strrstr(phb->read_buffer, "Proxy-Authenticate: NTLM"))) { /* Empty message */ gchar request[2048]; - gchar *domain = (gchar*)gaim_proxy_info_get_username(phb->gpi); + gchar *domain = (gchar*) gaim_proxy_info_get_username(phb->gpi); gchar *username; int request_len; if(!(username = strchr(domain, '\\'))) { @@ -1158,21 +1267,45 @@ source = -1; gaim_connection_error(phb->account->gc, msg); g_free(msg); - gaim_input_remove(phb->inpa); + gaim_input_remove(phb->inpa); + g_free(phb->read_buffer); + g_free(phb->host); + g_free(phb); return; } - *username = 0; + *username = '\0'; request_len = g_snprintf(request, sizeof(request), - "CONNECT %s:%d HTTP/1.1\r\nHost: %s:%d\r\n", - phb->host, phb->port, phb->host, phb->port); + "CONNECT %s:%d HTTP/1.1\r\n" + "Host: %s:%d\r\n", + phb->host, phb->port, + phb->host, phb->port); g_return_if_fail(request_len < sizeof(request)); request_len += g_snprintf(request + request_len, - sizeof(request) - request_len, - "Proxy-Authorization: NTLM %s\r\nProxy-Connection: Keep-Alive\r\n\r\n", gaim_ntlm_gen_type1((gchar*)gaim_proxy_info_get_host(phb->gpi),domain)); + sizeof(request) - request_len, + "Proxy-Authorization: NTLM %s\r\n" + "Proxy-Connection: Keep-Alive\r\n\r\n", + gaim_ntlm_gen_type1( + (gchar*) gaim_proxy_info_get_host(phb->gpi), + domain)); *username = '\\'; - write(source, request, request_len); + + gaim_input_remove(phb->inpa); + g_free(phb->read_buffer); + phb->read_buffer = NULL; + + phb->write_buffer = g_strndup(request, + request_len); + phb->write_buf_len = request_len; + phb->written_len = 0; + + phb->read_cb = http_canread; + + phb->inpa = gaim_input_add(source, + GAIM_INPUT_WRITE, proxy_do_write, phb); + + proxy_do_write(phb, source, cond); return; } else { char *msg = g_strdup_printf(_("Proxy connection error %d"), status); @@ -1180,24 +1313,39 @@ source = -1; gaim_connection_error(phb->account->gc, msg); g_free(msg); + gaim_input_remove(phb->inpa); + g_free(phb->read_buffer); + g_free(phb->host); + g_free(phb); } } - if ( status == 403 /* Forbidden */ ) { + if(status == 403 /* Forbidden */ ) { gchar *msg = g_strdup_printf(_("Access denied: proxy server forbids port %d tunnelling."), phb->port); gaim_connection_error(phb->account->gc, msg); g_free(msg); + gaim_input_remove(phb->inpa); + g_free(phb->read_buffer); + g_free(phb->host); + g_free(phb); } else { char *msg = g_strdup_printf(_("Proxy connection error %d"), status); gaim_connection_error(phb->account->gc, msg); g_free(msg); + gaim_input_remove(phb->inpa); + g_free(phb->read_buffer); + g_free(phb->host); + g_free(phb); } } else { + gaim_input_remove(phb->inpa); + g_free(phb->read_buffer); + phb->read_buffer = NULL; http_complete(phb, source); + return; } +} - gaim_input_remove(phb->inpa); - return; -} + static void http_canwrite(gpointer data, gint source, GaimInputCondition cond) @@ -1222,7 +1370,8 @@ return; } - gaim_debug_info("proxy", "using CONNECT tunnelling for %s:%d\n", phb->host, phb->port); + gaim_debug_info("proxy", "using CONNECT tunnelling for %s:%d\n", + phb->host, phb->port); request_len = g_snprintf(request, sizeof(request), "CONNECT %s:%d HTTP/1.1\r\nHost: %s:%d\r\n", phb->host, phb->port, phb->host, phb->port); @@ -1230,32 +1379,36 @@ if (gaim_proxy_info_get_username(phb->gpi) != NULL) { char *t1, *t2; t1 = g_strdup_printf("%s:%s", - gaim_proxy_info_get_username(phb->gpi), - gaim_proxy_info_get_password(phb->gpi) ? - gaim_proxy_info_get_password(phb->gpi) : ""); + gaim_proxy_info_get_username(phb->gpi), + gaim_proxy_info_get_password(phb->gpi) ? + gaim_proxy_info_get_password(phb->gpi) : ""); t2 = gaim_base64_encode((const guchar *)t1, strlen(t1)); g_free(t1); g_return_if_fail(request_len < sizeof(request)); request_len += g_snprintf(request + request_len, - sizeof(request) - request_len, - "Proxy-Authorization: Basic %s\r\nProxy-Authorization: NTLM %s\r\nProxy-Connection: Keep-Alive\r\n", t2, gaim_ntlm_gen_type1((gchar*)gaim_proxy_info_get_host(phb->gpi),"")); + sizeof(request) - request_len, + "Proxy-Authorization: Basic %s\r\n" + "Proxy-Authorization: NTLM %s\r\n" + "Proxy-Connection: Keep-Alive\r\n", t2, + gaim_ntlm_gen_type1( + (gchar*)gaim_proxy_info_get_host(phb->gpi),"")); g_free(t2); } g_return_if_fail(request_len < sizeof(request)); strcpy(request + request_len, "\r\n"); request_len += 2; - - if (write(source, request, request_len) < 0) { - close(source); + phb->write_buffer = g_strndup(request, request_len); + phb->write_buf_len = request_len; + phb->written_len = 0; - try_connect(phb); - return; - } + phb->read_cb = http_canread; - /* register the response handler for the CONNECT request */ - phb->inpa = gaim_input_add(source, GAIM_INPUT_READ, http_canread, phb); + phb->inpa = gaim_input_add(source, GAIM_INPUT_WRITE, proxy_do_write, + phb); + + proxy_do_write(phb, source, cond); } static int @@ -1307,34 +1460,55 @@ close(fd); return -1; } - fcntl(fd, F_SETFL, 0); http_canwrite(phb, fd, GAIM_INPUT_WRITE); } return fd; } + static void s4_canread(gpointer data, gint source, GaimInputCondition cond) { - unsigned char packet[12]; struct PHB *phb = data; + char *buf; + int len, max_read; + + /* This is really not going to block under normal circumstances, but to + * be correct, we deal with the unlikely scenario */ + + if (phb->read_buffer == NULL) { + phb->read_buf_len = 12; + phb->read_buffer = g_malloc(phb->read_buf_len); + phb->read_len = 0; + } + + buf = phb->read_buffer + phb->read_len - 1; + max_read = phb->read_buf_len - phb->read_len; + + len = read(source, buf, max_read); + + if ((len < 0 && errno == EAGAIN) || len + phb->read_len < 4) + return; + else if (len + phb->read_len >= 4) { + if (phb->read_buffer[1] == 90) { + if (phb->account == NULL || + gaim_account_get_connection(phb->account) != NULL) { + + phb->func(phb->data, source, GAIM_INPUT_READ); + } + + gaim_input_remove(phb->inpa); + g_free(phb->read_buffer); + g_free(phb->host); + g_free(phb); + return; + } + } gaim_input_remove(phb->inpa); - - memset(packet, 0, sizeof(packet)); - - if (read(source, packet, 9) >= 4 && packet[1] == 90) { - if (phb->account == NULL || - gaim_account_get_connection(phb->account) != NULL) { - - phb->func(phb->data, source, GAIM_INPUT_READ); - } - - g_free(phb->host); - g_free(phb); - return; - } + g_free(phb->read_buffer); + phb->read_buffer = NULL; close(source); @@ -1344,7 +1518,7 @@ static void s4_canwrite(gpointer data, gint source, GaimInputCondition cond) { - unsigned char packet[12]; + unsigned char packet[9]; struct hostent *hp; struct PHB *phb = data; socklen_t len; @@ -1363,7 +1537,6 @@ try_connect(phb); return; } - fcntl(source, F_SETFL, 0); /* * The socks4 spec doesn't include support for doing host name @@ -1390,14 +1563,14 @@ packet[7] = (unsigned char)(hp->h_addr_list[0])[3]; packet[8] = 0; - if (write(source, packet, 9) != 9) { - close(source); + phb->write_buffer = g_strndup(packet, sizeof(packet)); + phb->write_buf_len = sizeof(packet); + phb->written_len = 0; + phb->read_cb = s4_canread; - try_connect(phb); - return; - } + phb->inpa = gaim_input_add(source, GAIM_INPUT_WRITE, proxy_do_write, phb); - phb->inpa = gaim_input_add(source, GAIM_INPUT_READ, s4_canread, phb); + proxy_do_write(phb, source, cond); } static int @@ -1443,7 +1616,6 @@ return -1; } - fcntl(fd, F_SETFL, 0); s4_canwrite(phb, fd, GAIM_INPUT_WRITE); } @@ -1453,26 +1625,47 @@ static void s5_canread_again(gpointer data, gint source, GaimInputCondition cond) { - unsigned char buf[512]; + guchar *dest, *buf; struct PHB *phb = data; + int len; - gaim_input_remove(phb->inpa); + if (phb->read_buffer == NULL) { + phb->read_buf_len = 512; + phb->read_buffer = g_malloc(phb->read_buf_len); + phb->read_len = 0; + } + + dest = phb->read_buffer + phb->read_len; + buf = phb->read_buffer; + gaim_debug_info("socks5 proxy", "Able to read again.\n"); - if (read(source, buf, 4) < 4) { + len = read(source, dest, (phb->read_buf_len - phb->read_len)); + if(len < 0 && errno == EAGAIN) + return; + else if(len < 0) { gaim_debug_warning("socks5 proxy", "or not...\n"); close(source); - + gaim_input_remove(phb->inpa); + g_free(phb->read_buffer); + phb->read_buffer = NULL; try_connect(phb); return; } + phb->read_len += len; + + if(phb->read_len < 4) + return; + if ((buf[0] != 0x05) || (buf[1] != 0x00)) { if ((buf[0] == 0x05) && (buf[1] < 0x09)) gaim_debug_error("socks5 proxy", socks5errors[buf[1]]); else gaim_debug_error("socks5 proxy", "Bad data.\n"); close(source); - + gaim_input_remove(phb->inpa); + g_free(phb->read_buffer); + phb->read_buffer = NULL; try_connect(phb); return; } @@ -1480,21 +1673,32 @@ /* Skip past BND.ADDR */ switch(buf[3]) { case 0x01: /* the address is a version-4 IP address, with a length of 4 octets */ - read(source, buf, 4); + if(phb->read_len < 4 + 4) + return; + buf += 4 + 4; break; case 0x03: /* the address field contains a fully-qualified domain name. The first octet of the address field contains the number of octets of name that follow, there is no terminating NUL octet. */ - read(source, buf, 1); - read(source, buf, buf[0]); + if(phb->read_len < 4 + 1) + return; + buf += 4 + 1; + if(phb->read_len < 4 + 1 + buf[0]) + return; + buf += buf[0]; break; case 0x04: /* the address is a version-6 IP address, with a length of 16 octets */ - read(source, buf, 16); + if(phb->read_len < 4 + 16) + return; + buf += 4 + 16; break; } + if(phb->read_len < (buf - phb->read_buffer) + 2) + return; + /* Skip past BND.PORT */ - read(source, buf, 2); + buf += 2; if (phb->account == NULL || gaim_account_get_connection(phb->account) != NULL) { @@ -1502,6 +1706,8 @@ phb->func(phb->data, source, GAIM_INPUT_READ); } + gaim_input_remove(phb->inpa); + g_free(phb->read_buffer); g_free(phb->host); g_free(phb); } @@ -1509,51 +1715,71 @@ static void s5_sendconnect(gpointer data, gint source) { - unsigned char buf[512]; struct PHB *phb = data; int hlen = strlen(phb->host); + phb->write_buf_len = 5 + hlen + 1; + phb->write_buffer = g_malloc(phb->write_buf_len); + phb->written_len = 0; - buf[0] = 0x05; - buf[1] = 0x01; /* CONNECT */ - buf[2] = 0x00; /* reserved */ - buf[3] = 0x03; /* address type -- host name */ - buf[4] = hlen; - memcpy(buf + 5, phb->host, hlen); - buf[5 + hlen] = phb->port >> 8; - buf[5 + hlen + 1] = phb->port & 0xff; + phb->write_buffer[0] = 0x05; + phb->write_buffer[1] = 0x01; /* CONNECT */ + phb->write_buffer[2] = 0x00; /* reserved */ + phb->write_buffer[3] = 0x03; /* address type -- host name */ + phb->write_buffer[4] = hlen; + memcpy(phb->write_buffer + 5, phb->host, hlen); + phb->write_buffer[5 + hlen] = phb->port >> 8; + phb->write_buffer[5 + hlen + 1] = phb->port & 0xff; - if (write(source, buf, (5 + hlen + 2)) < (5 + hlen + 2)) { - close(source); + phb->read_cb = s5_canread_again; - try_connect(phb); - return; - } + phb->inpa = gaim_input_add(source, GAIM_INPUT_WRITE, proxy_do_write, phb); + proxy_do_write(phb, source, GAIM_INPUT_WRITE); - phb->inpa = gaim_input_add(source, GAIM_INPUT_READ, s5_canread_again, phb); } static void s5_readauth(gpointer data, gint source, GaimInputCondition cond) { - unsigned char buf[512]; struct PHB *phb = data; + int len; + + if (phb->read_buffer == NULL) { + phb->read_buf_len = 2; + phb->read_buffer = g_malloc(phb->read_buf_len); + phb->read_len = 0; + } + + gaim_debug_info("socks5 proxy", "Got auth response.\n"); + + len = read(source, phb->read_buffer + phb->read_len, + phb->read_buf_len - phb->read_len); + if(len < 0 && errno == EAGAIN) + return; + else if(len < 0) { + close(source); + gaim_input_remove(phb->inpa); + g_free(phb->read_buffer); + phb->read_buffer = NULL; + try_connect(phb); + return; + } + phb->read_len += len; + + if (phb->read_len < 2) + return; gaim_input_remove(phb->inpa); - gaim_debug_info("socks5 proxy", "Got auth response.\n"); - if (read(source, buf, 2) < 2) { + if ((phb->read_buffer[0] != 0x01) || (phb->read_buffer[1] != 0x00)) { close(source); - + g_free(phb->read_buffer); + phb->read_buffer = NULL; try_connect(phb); return; } - if ((buf[0] != 0x01) || (buf[1] != 0x00)) { - close(source); - - try_connect(phb); - return; - } + g_free(phb->read_buffer); + phb->read_buffer = NULL; s5_sendconnect(phb, source); } @@ -1604,125 +1830,175 @@ static void s5_readchap(gpointer data, gint source, GaimInputCondition cond) { - unsigned char buf[260]; - unsigned char cmdbuf[20]; + guchar *cmdbuf, *buf; struct PHB *phb = data; + int len, navas, currentav; - int navas, currentav; - - gaim_input_remove(phb->inpa); gaim_debug(GAIM_DEBUG_INFO, "socks5 proxy", "Got CHAP response.\n"); - if (read(source, cmdbuf, 2) < 2) { + if (phb->read_buffer == NULL) { + phb->read_buf_len = 20; + phb->read_buffer = g_malloc(phb->read_buf_len); + phb->read_len = 0; + } + + len = read(source, phb->read_buffer + phb->read_len, + phb->read_buf_len - phb->read_len); + + if(len < 0 && errno == EAGAIN) + return; + else if(len < 0) { close(source); - + gaim_input_remove(phb->inpa); + g_free(phb->read_buffer); + phb->read_buffer = NULL; try_connect(phb); return; } + phb->read_len += len; - if (cmdbuf[0] != 0x01) { + if (phb->read_len < 2) + return; + + cmdbuf = phb->read_buffer; + + if (*cmdbuf != 0x01) { close(source); - + gaim_input_remove(phb->inpa); + g_free(phb->read_buffer); + phb->read_buffer = NULL; try_connect(phb); return; } + cmdbuf++; - navas = cmdbuf[1]; + navas = *cmdbuf; + cmdbuf++; for (currentav = 0; currentav < navas; currentav++) { - if (read(source, cmdbuf, 2) < 2) { - close(source); - - try_connect(phb); + if (phb->read_len - (cmdbuf - phb->read_buffer) < 2) return; - } - if (read(source, buf, cmdbuf[1]) < cmdbuf[1]) { - close(source); - - try_connect(phb); + if (phb->read_len - (cmdbuf - phb->read_buffer) < cmdbuf[1]) return; - } + buf = cmdbuf + 2; switch (cmdbuf[0]) { case 0x00: /* Did auth work? */ if (buf[0] == 0x00) { + gaim_input_remove(phb->inpa); + g_free(phb->read_buffer); + phb->read_buffer = NULL; /* Success */ s5_sendconnect(phb, source); return; } else { /* Failure */ - gaim_debug_warning("proxy", "socks5 CHAP authentication " - "failed. Disconnecting..."); + gaim_debug_warning("proxy", + "socks5 CHAP authentication " + "failed. Disconnecting..."); close(source); - + gaim_input_remove(phb->inpa); + g_free(phb->read_buffer); + phb->read_buffer = NULL; try_connect(phb); return; } break; case 0x03: /* Server wants our credentials */ + + phb->write_buf_len = 16 + 4; + phb->write_buffer = g_malloc(phb->write_buf_len); + phb->written_len = 0; + hmacmd5_chap(buf, cmdbuf[1], gaim_proxy_info_get_password(phb->gpi), - buf + 4); - buf[0] = 0x01; - buf[1] = 0x01; - buf[2] = 0x04; - buf[3] = 0x10; - if (write(source, buf, 20) < 20) { - close(source); + phb->write_buffer + 4); + phb->write_buffer[0] = 0x01; + phb->write_buffer[1] = 0x01; + phb->write_buffer[2] = 0x04; + phb->write_buffer[3] = 0x10; - try_connect(phb); - return; - } + gaim_input_remove(phb->inpa); + g_free(phb->read_buffer); + phb->read_buffer = NULL; + + phb->read_cb = s5_readchap; + + phb->inpa = gaim_input_add(source, + GAIM_INPUT_WRITE, proxy_do_write, phb); + + proxy_do_write(phb, source, GAIM_INPUT_WRITE); break; case 0x11: /* Server wants to select an algorithm */ if (buf[0] != 0x85) { /* Only currently support HMAC-MD5 */ - gaim_debug_warning("proxy", "Server tried to select an " - "algorithm that we did not advertise " - "as supporting. This is a violation " - "of the socks5 CHAP specification. " - "Disconnecting..."); + gaim_debug_warning("proxy", + "Server tried to select an " + "algorithm that we did not advertise " + "as supporting. This is a violation " + "of the socks5 CHAP specification. " + "Disconnecting..."); close(source); - + gaim_input_remove(phb->inpa); + g_free(phb->read_buffer); + phb->read_buffer = NULL; try_connect(phb); return; } break; } + cmdbuf = buf + cmdbuf[1]; } /* Fell through. We ran out of CHAP events to process, but haven't * succeeded or failed authentication - there may be more to come. * If this is the case, come straight back here. */ - phb->inpa = gaim_input_add(source, GAIM_INPUT_READ, s5_readchap, phb); } static void s5_canread(gpointer data, gint source, GaimInputCondition cond) { - unsigned char buf[512]; struct PHB *phb = data; + int len; + + if (phb->read_buffer == NULL) { + phb->read_buf_len = 2; + phb->read_buffer = g_malloc(phb->read_buf_len); + phb->read_len = 0; + } + + gaim_debug_info("socks5 proxy", "Able to read.\n"); + + len = read(source, phb->read_buffer + phb->read_len, + phb->read_buf_len - phb->read_len); + if(len < 0 && errno == EAGAIN) + return; + else if(len < 0) { + close(source); + gaim_input_remove(phb->inpa); + g_free(phb->read_buffer); + phb->read_buffer = NULL; + try_connect(phb); + return; + } + phb->read_len += len; + + if (phb->read_len < 2) + return; gaim_input_remove(phb->inpa); - gaim_debug_info("socks5 proxy", "Able to read.\n"); - if (read(source, buf, 2) < 2) { + if ((phb->read_buffer[0] != 0x05) || (phb->read_buffer[1] == 0xff)) { close(source); - + g_free(phb->read_buffer); + phb->read_buffer = NULL; try_connect(phb); return; } - if ((buf[0] != 0x05) || (buf[1] == 0xff)) { - close(source); - - try_connect(phb); - return; - } - - if (buf[1] == 0x02) { - unsigned int i, j; + if (phb->read_buffer[1] == 0x02) { + gsize i, j; const char *u, *p; u = gaim_proxy_info_get_username(phb->gpi); @@ -1731,43 +2007,62 @@ i = (u == NULL) ? 0 : strlen(u); j = (p == NULL) ? 0 : strlen(p); - buf[0] = 0x01; /* version 1 */ - buf[1] = i; + phb->write_buf_len = 1 + 1 + i + 1 + j; + phb->write_buffer = g_malloc(phb->write_buf_len); + phb->written_len = 0; + + phb->write_buffer[0] = 0x01; /* version 1 */ + phb->write_buffer[1] = i; if (u != NULL) - memcpy(buf + 2, u, i); - buf[2 + i] = j; + memcpy(phb->write_buffer + 2, u, i); + phb->write_buffer[2 + i] = j; if (p != NULL) - memcpy(buf + 2 + i + 1, p, j); + memcpy(phb->write_buffer + 2 + i + 1, p, j); - if (write(source, buf, 3 + i + j) < 3 + i + j) { - close(source); + g_free(phb->read_buffer); + phb->read_buffer = NULL; + + phb->read_cb = s5_readauth; - try_connect(phb); - return; - } + phb->inpa = gaim_input_add(source, GAIM_INPUT_WRITE, + proxy_do_write, phb); - phb->inpa = gaim_input_add(source, GAIM_INPUT_READ, s5_readauth, phb); - } else if (buf[1] == 0x03) { - unsigned int userlen; + proxy_do_write(phb, source, GAIM_INPUT_WRITE); + + return; + } else if (phb->read_buffer[1] == 0x03) { + gsize userlen; userlen = strlen(gaim_proxy_info_get_username(phb->gpi)); - buf[0] = 0x01; - buf[1] = 0x02; - buf[2] = 0x11; - buf[3] = 0x01; - buf[4] = 0x85; - buf[5] = 0x02; - buf[6] = userlen; - memcpy(buf + 7, gaim_proxy_info_get_username(phb->gpi), userlen); - if (write(source, buf, 7 + userlen) < 7 + userlen) { - close(source); + + phb->write_buf_len = 7 + userlen; + phb->write_buffer = g_malloc(phb->write_buf_len); + phb->written_len = 0; + + phb->write_buffer[0] = 0x01; + phb->write_buffer[1] = 0x02; + phb->write_buffer[2] = 0x11; + phb->write_buffer[3] = 0x01; + phb->write_buffer[4] = 0x85; + phb->write_buffer[5] = 0x02; + phb->write_buffer[6] = userlen; + memcpy(phb->write_buffer + 7, + gaim_proxy_info_get_username(phb->gpi), userlen); - try_connect(phb); - return; - } + g_free(phb->read_buffer); + phb->read_buffer = NULL; + + phb->read_cb = s5_readchap; + + phb->inpa = gaim_input_add(source, GAIM_INPUT_WRITE, + proxy_do_write, phb); - phb->inpa = gaim_input_add(source, GAIM_INPUT_READ, s5_readchap, phb); - } - else { + proxy_do_write(phb, source, GAIM_INPUT_WRITE); + + return; + } else { + g_free(phb->read_buffer); + phb->read_buffer = NULL; + s5_sendconnect(phb, source); } } @@ -1775,7 +2070,7 @@ static void s5_canwrite(gpointer data, gint source, GaimInputCondition cond) { - unsigned char buf[512]; + unsigned char buf[5]; int i; struct PHB *phb = data; socklen_t len; @@ -1793,7 +2088,6 @@ try_connect(phb); return; } - fcntl(source, F_SETFL, 0); i = 0; buf[0] = 0x05; /* SOCKS version 5 */ @@ -1811,15 +2105,14 @@ i = 3; } - if (write(source, buf, i) < i) { - gaim_debug_error("socks5 proxy", "Unable to write\n"); - close(source); + phb->write_buf_len = i; + phb->write_buffer = g_malloc(phb->write_buf_len); + memcpy(phb->write_buffer, buf, i); - try_connect(phb); - return; - } + phb->read_cb = s5_canread; - phb->inpa = gaim_input_add(source, GAIM_INPUT_READ, s5_canread, phb); + phb->inpa = gaim_input_add(source, GAIM_INPUT_WRITE, proxy_do_write, phb); + proxy_do_write(phb, source, GAIM_INPUT_WRITE); } static int @@ -1867,7 +2160,6 @@ return -1; } - fcntl(fd, F_SETFL, 0); s5_canwrite(phb, fd, GAIM_INPUT_WRITE); } @@ -2048,7 +2340,7 @@ } return gaim_gethostbyname_async(connecthost, connectport, - connection_host_resolved, phb); + connection_host_resolved, phb); } int @@ -2064,8 +2356,8 @@ phb->host = g_strdup(host); phb->port = port; - return gaim_gethostbyname_async(gaim_proxy_info_get_host(gpi), gaim_proxy_info_get_port(gpi), - connection_host_resolved, phb); + return gaim_gethostbyname_async(gaim_proxy_info_get_host(gpi), + gaim_proxy_info_get_port(gpi), connection_host_resolved, phb); } @@ -2121,16 +2413,16 @@ /* Setup callbacks for the preferences. */ handle = gaim_proxy_get_handle(); - gaim_prefs_connect_callback(handle, "/core/proxy/type", - proxy_pref_cb, NULL); - gaim_prefs_connect_callback(handle, "/core/proxy/host", - proxy_pref_cb, NULL); - gaim_prefs_connect_callback(handle, "/core/proxy/port", - proxy_pref_cb, NULL); + gaim_prefs_connect_callback(handle, "/core/proxy/type", proxy_pref_cb, + NULL); + gaim_prefs_connect_callback(handle, "/core/proxy/host", proxy_pref_cb, + NULL); + gaim_prefs_connect_callback(handle, "/core/proxy/port", proxy_pref_cb, + NULL); gaim_prefs_connect_callback(handle, "/core/proxy/username", - proxy_pref_cb, NULL); + proxy_pref_cb, NULL); gaim_prefs_connect_callback(handle, "/core/proxy/password", - proxy_pref_cb, NULL); + proxy_pref_cb, NULL); #ifdef _WIN32 if(!g_thread_supported()) g_thread_init(NULL); diff -r d8f238864c88 -r 33bef17125c2 src/upnp.c --- a/src/upnp.c Thu Feb 09 04:14:54 2006 +0000 +++ b/src/upnp.c Thu Feb 09 04:17:56 2006 +0000 @@ -408,6 +408,7 @@ dd->full_url = g_strdup_printf("http://%s:%d", descriptionAddress, port); + g_free(descriptionAddress); /* Remove the timeout because everything it is waiting for has * successfully completed */ @@ -417,7 +418,6 @@ gaim_url_fetch_request(descriptionURL, TRUE, NULL, TRUE, httpRequest, TRUE, upnp_parse_description_cb, dd); - g_free(descriptionAddress); g_free(httpRequest); } @@ -543,7 +543,7 @@ we should retry the send NUM_UDP_ATTEMPTS times. Also, try different requests for WANIPConnection and WANPPPConnection*/ for(; dd->retry_count < NUM_UDP_ATTEMPTS; dd->retry_count++) { - sentSuccess = TRUE; + sentSuccess = FALSE; if((dd->retry_count % 2) == 0) { strncpy(dd->service_type, WAN_IP_CONN_SERVICE, sizeof(dd->service_type)); @@ -563,7 +563,7 @@ sentSuccess = TRUE; break; } - } while (errno == EINTR); + } while (errno == EINTR || errno == EAGAIN); g_free(sendMessage); diff -r d8f238864c88 -r 33bef17125c2 src/util.c --- a/src/util.c Thu Feb 09 04:14:54 2006 +0000 +++ b/src/util.c Thu Feb 09 04:17:56 2006 +0000 @@ -49,12 +49,12 @@ char *user_agent; gboolean http11; char *request; + gsize request_written; gboolean include_headers; int inpa; - gboolean sentreq; - gboolean startsaving; + gboolean got_headers; gboolean has_explicit_data_len; char *webdata; unsigned long len; @@ -3218,6 +3218,7 @@ return content_len; } + static void url_fetched_cb(gpointer url_data, gint sock, GaimInputCondition cond) { @@ -3227,70 +3228,11 @@ char *data_cursor; gboolean got_eof = FALSE; - if (sock == -1) - { - gfud->callback(gfud->user_data, NULL, 0); - - destroy_fetch_url_data(gfud); - - return; - } - - if (!gfud->sentreq) - { - char *send; - char buf[1024]; - - if (gfud->request) { - send = gfud->request; - } else { - if (gfud->user_agent) { - /* Host header is not forbidden in HTTP/1.0 requests, and HTTP/1.1 - * clients must know how to handle the "chunked" transfer encoding. - * Gaim doesn't know how to handle "chunked", so should always send - * the Host header regardless, to get around some observed problems - */ - g_snprintf(buf, sizeof(buf), - "GET %s%s HTTP/%s\r\n" - "Connection: close\r\n" - "User-Agent: %s\r\n" - "Host: %s\r\n\r\n", - (gfud->full ? "" : "/"), - (gfud->full ? gfud->url : gfud->website.page), - (gfud->http11 ? "1.1" : "1.0"), - gfud->user_agent, gfud->website.address); - } else { - g_snprintf(buf, sizeof(buf), - "GET %s%s HTTP/%s\r\n" - "Connection: close\r\n" - "Host: %s\r\n\r\n", - (gfud->full ? "" : "/"), - (gfud->full ? gfud->url : gfud->website.page), - (gfud->http11 ? "1.1" : "1.0"), - gfud->website.address); - } - send = buf; - } - - gaim_debug_misc("gaim_url_fetch", "Request: %s\n", send); - - write(sock, send, strlen(send)); - fcntl(sock, F_SETFL, O_NONBLOCK); - gfud->sentreq = TRUE; - gfud->inpa = gaim_input_add(sock, GAIM_INPUT_READ, - url_fetched_cb, url_data); - gfud->data_len = 4096; - gfud->webdata = g_malloc(gfud->data_len); - - return; - } - - while ((len = read(sock, buf, sizeof(buf))) > 0) - { + while((len = read(sock, buf, sizeof(buf))) > 0) { /* If we've filled up our butfer, make it bigger */ - if ((gfud->len + len) >= gfud->data_len) - { - gfud->data_len += MAX(((gfud->data_len) / 2), sizeof(buf)); + if((gfud->len + len) >= gfud->data_len) { + while((gfud->len + len) >= gfud->data_len) + gfud->data_len += sizeof(buf); gfud->webdata = g_realloc(gfud->webdata, gfud->data_len); } @@ -3303,12 +3245,11 @@ gfud->webdata[gfud->len] = '\0'; - if (!gfud->startsaving) - { + if(!gfud->got_headers) { char *tmp; /** See if we've reached the end of the headers yet */ - if ((tmp = strstr(gfud->webdata, "\r\n\r\n"))) { + if((tmp = strstr(gfud->webdata, "\r\n\r\n"))) { char * new_data; guint header_len = (tmp + 4 - gfud->webdata); size_t content_len, body_len = 0; @@ -3320,7 +3261,7 @@ if (parse_redirect(gfud->webdata, header_len, sock, gfud)) return; - gfud->startsaving = TRUE; + gfud->got_headers = TRUE; /* No redirect. See if we can find a content length. */ content_len = parse_content_len(gfud->webdata, header_len); @@ -3383,9 +3324,8 @@ } } - if (len <= 0) { - if (errno == EWOULDBLOCK) { - errno = 0; + if(len <= 0) { + if(errno == EAGAIN) { return; } else if (errno != ETIMEDOUT) { got_eof = TRUE; @@ -3414,6 +3354,76 @@ } } +static void +url_fetch_connect_cb(gpointer url_data, gint sock, GaimInputCondition cond) { + GaimFetchUrlData *gfud = url_data; + int len, total_len; + + if(sock == -1) { + gfud->callback(gfud->user_data, NULL, 0); + destroy_fetch_url_data(gfud); + return; + } + + if (!gfud->request) { + if (gfud->user_agent) { + /* Host header is not forbidden in HTTP/1.0 requests, and HTTP/1.1 + * clients must know how to handle the "chunked" transfer encoding. + * Gaim doesn't know how to handle "chunked", so should always send + * the Host header regardless, to get around some observed problems + */ + gfud->request = g_strdup_printf( + "GET %s%s HTTP/%s\r\n" + "Connection: close\r\n" + "User-Agent: %s\r\n" + "Host: %s\r\n\r\n", + (gfud->full ? "" : "/"), + (gfud->full ? gfud->url : gfud->website.page), + (gfud->http11 ? "1.1" : "1.0"), + gfud->user_agent, gfud->website.address); + } else { + gfud->request = g_strdup_printf( + "GET %s%s HTTP/%s\r\n" + "Connection: close\r\n" + "Host: %s\r\n\r\n", + (gfud->full ? "" : "/"), + (gfud->full ? gfud->url : gfud->website.page), + (gfud->http11 ? "1.1" : "1.0"), + gfud->website.address); + } + } + + gaim_debug_misc("gaim_url_fetch", "Request: '%s'\n", gfud->request); + + if(!gfud->inpa) + gfud->inpa = gaim_input_add(sock, GAIM_INPUT_WRITE, + url_fetch_connect_cb, gfud); + + total_len = strlen(gfud->request); + + len = write(sock, gfud->request + gfud->request_written, + total_len - gfud->request_written); + + if(len < 0 && errno == EAGAIN) + return; + else if(len < 0) { + gaim_input_remove(gfud->inpa); + close(sock); + gfud->callback(gfud->user_data, NULL, 0); + destroy_fetch_url_data(gfud); + return; + } + gfud->request_written += len; + + if(gfud->request_written != total_len) + return; + + gaim_input_remove(gfud->inpa); + + gfud->inpa = gaim_input_add(sock, GAIM_INPUT_READ, url_fetched_cb, + gfud); +} + void gaim_url_fetch_request(const char *url, gboolean full, const char *user_agent, gboolean http11, @@ -3444,9 +3454,7 @@ &gfud->website.page, &gfud->website.user, &gfud->website.passwd); if (gaim_proxy_connect(NULL, gfud->website.address, - gfud->website.port, url_fetched_cb, - gfud) != 0) - { + gfud->website.port, url_fetch_connect_cb, gfud) != 0) { destroy_fetch_url_data(gfud); cb(user_data, g_strdup(_("g003: Error opening connection.\n")), 0); @@ -3731,7 +3739,7 @@ gunichar wc = g_utf8_get_char(str); /* super simple check. hopefully not too wrong. */ - if(wc >= 0x80) { + if(wc >= 0x80) { g_string_append_printf(out, "&#%u;", (guint32) wc); } else { g_string_append_unichar(out, wc); @@ -3757,12 +3765,12 @@ while( (b = strstr(buf, "&#")) ) { gunichar wc; int base = 0; - + /* append everything leading up to the &# */ g_string_append_len(out, buf, b-buf); b += 2; /* skip past the &# */ - + /* strtoul will handle 0x prefix as hex, but not x */ if(*b == 'x' || *b == 'X') base = 16; @@ -3815,7 +3823,7 @@ } /* previously conversation::find_nick() */ -gboolean +gboolean gaim_utf8_has_word(const char *haystack, const char *needle) { char *hay, *pin, *p; diff -r d8f238864c88 -r 33bef17125c2 src/win32/libc_interface.c --- a/src/win32/libc_interface.c Thu Feb 09 04:14:54 2006 +0000 +++ b/src/win32/libc_interface.c Thu Feb 09 04:17:56 2006 +0000 @@ -276,9 +276,11 @@ int wgaim_read(int fd, void *buf, unsigned int size) { int ret; - if( wgaim_is_socket(fd) ) { - if( (ret = recv(fd, buf, size, 0)) == SOCKET_ERROR ) { + if(wgaim_is_socket(fd)) { + if((ret = recv(fd, buf, size, 0)) == SOCKET_ERROR) { errno = WSAGetLastError(); + if(errno == WSAEWOULDBLOCK) + errno = EAGAIN; return -1; } #if 0 @@ -292,8 +294,7 @@ /* success reading socket */ return ret; } - } - else { + } else { /* fd is not a socket handle.. pass it off to read */ return read(fd, buf, size); } @@ -302,25 +303,27 @@ int wgaim_write(int fd, const void *buf, unsigned int size) { int ret; - if( wgaim_is_socket(fd) ) { - if( (ret = send(fd, buf, size, 0)) == SOCKET_ERROR ) { + if(wgaim_is_socket(fd)) { + if((ret = send(fd, buf, size, 0)) == SOCKET_ERROR) { errno = WSAGetLastError(); + if(errno == WSAEWOULDBLOCK) + errno = EAGAIN; return -1; - } - else { + } else { /* success */ return ret; } - } - else + } else return write(fd, buf, size); } int wgaim_recv(int fd, void *buf, size_t len, int flags) { int ret; - if ((ret = recv(fd, buf, len, flags)) == SOCKET_ERROR) { + if((ret = recv(fd, buf, len, flags)) == SOCKET_ERROR) { errno = WSAGetLastError(); + if(errno == WSAEWOULDBLOCK) + errno = EAGAIN; return -1; } else { return ret; diff -r d8f238864c88 -r 33bef17125c2 src/win32/wgaimerror.h --- a/src/win32/wgaimerror.h Thu Feb 09 04:14:54 2006 +0000 +++ b/src/win32/wgaimerror.h Thu Feb 09 04:17:56 2006 +0000 @@ -53,5 +53,6 @@ #define EMSGSIZE WSAEMSGSIZE #define ECONNABORTED WSAECONNABORTED #define ECONNRESET WSAECONNRESET +#define EHOSTUNREACH WSAEHOSTUNREACH #endif /* end _WGAIMERROR_H */