Пример #1
0
static void irc_input_cb_ssl(gpointer data, PurpleSslConnection *gsc,
		PurpleInputCondition cond)
{

	PurpleConnection *gc = data;
	struct irc_conn *irc = gc->proto_data;
	int len;

	if(!g_list_find(purple_connections_get_all(), gc)) {
		purple_ssl_close(gsc);
		return;
	}

	if (irc->inbuflen < irc->inbufused + IRC_INITIAL_BUFSIZE) {
		irc->inbuflen += IRC_INITIAL_BUFSIZE;
		irc->inbuf = g_realloc(irc->inbuf, irc->inbuflen);
	}

	len = purple_ssl_read(gsc, irc->inbuf + irc->inbufused, IRC_INITIAL_BUFSIZE - 1);

	if (len < 0 && errno == EAGAIN) {
		/* Try again later */
		return;
	} else if (len < 0) {
		purple_connection_error(gc, _("Read error"));
		return;
	} else if (len == 0) {
		purple_connection_error(gc, _("Server has disconnected"));
		return;
	}

	read_input(irc, len);
}
Пример #2
0
/* Try to read some data and push it to the WhatsApp API */
void waprpl_ssl_input_cb(gpointer data, gint source, PurpleInputCondition cond)
{
	PurpleConnection *gc = data;
	whatsapp_connection *wconn = purple_connection_get_protocol_data(gc);

	/* End point closed the connection */
	if (!g_list_find(purple_connections_get_all(), gc)) {
		waprpl_ssl_cerr_cb(0, 0, gc);
		return;
	}

	char tempbuff[1024];
	int ret;
	do {
		ret = purple_ssl_read(wconn->gsc, tempbuff, sizeof(tempbuff));
		if (ret > 0) {
			waAPI_sslinput(wconn->waAPI, tempbuff, ret);
		} else if (ret < 0 && errno == EAGAIN)
			break;
		else if (ret < 0) {
			waprpl_ssl_cerr_cb(0, 0, gc);
			break;
		} else {
			waprpl_ssl_cerr_cb(0, 0, gc);
		}
	} while (ret > 0);

	/*waprpl_process_ssl_events(gc); */
	waprpl_check_ssl_output(gc);	/* The input data may generate responses! */
}
Пример #3
0
static void
msn_soap_read_cb(gpointer data, gint fd, PurpleInputCondition cond)
{
	MsnSoapConnection *conn = data;
	int count = 0, cnt, perrno;
	/* This buffer needs to be larger than any packets received from
		login.live.com or Adium will fail to receive the packet
		(something weird with the login.live.com server). With NSS it works
		fine, so I believe it's some bug with OS X */
	char buf[16 * 1024];
	gsize cursor;

	if (conn->message == NULL) {
		conn->message = msn_soap_message_new(NULL, NULL);
	}

	if (conn->buf == NULL) {
		conn->buf = g_string_new_len(buf, 0);
	}

	cursor = conn->buf->len;
	while ((cnt = purple_ssl_read(conn->ssl, buf, sizeof(buf))) > 0) {
		purple_debug_info("soap", "read %d bytes\n", cnt);
		count += cnt;
		g_string_append_len(conn->buf, buf, cnt);
	}

	perrno = errno;
	if (cnt < 0 && perrno != EAGAIN)
		purple_debug_info("soap", "read: %s\n", g_strerror(perrno));

	if (conn->current_request && conn->current_request->secure &&
		!purple_debug_is_unsafe())
		purple_debug_misc("soap", "Received secure request.\n");
	else if (count != 0)
		purple_debug_misc("soap", "current %s\n", conn->buf->str + cursor);

	/* && count is necessary for Adium, on OS X the last read always
	   return an error, so we want to proceed anyway. See #5212 for
	   discussion on this and the above buffer size issues */
	if(cnt < 0 && errno == EAGAIN && count == 0)
		return;

	/* msn_soap_process could alter errno */
	msn_soap_process(conn);

	if ((cnt < 0 && perrno != EAGAIN) || cnt == 0) {
		/* It's possible msn_soap_process closed the ssl connection */
		if (conn->ssl) {
			purple_ssl_close(conn->ssl);
			conn->ssl = NULL;
			msn_soap_connection_handle_next(conn);
		}
	}
}
Пример #4
0
gssize
purple_socket_read(PurpleSocket *ps, guchar *buf, size_t len)
{
	g_return_val_if_fail(ps != NULL, -1);
	g_return_val_if_fail(buf != NULL, -1);

	if (!purple_socket_check_state(ps, PURPLE_SOCKET_STATE_CONNECTED))
		return -1;

	if (ps->is_tls)
		return purple_ssl_read(ps->tls_connection, buf, len);
	else
		return read(ps->fd, buf, len);
}
Пример #5
0
Файл: irc.c Проект: dylex/pidgin
static void irc_input_cb_ssl(gpointer data, PurpleSslConnection *gsc,
		PurpleInputCondition cond)
{

	PurpleConnection *gc = data;
	struct irc_conn *irc = gc->proto_data;
	int len;

	if(!g_list_find(purple_connections_get_all(), gc)) {
		purple_ssl_close(gsc);
		return;
	}

	do {
		// resize buffer upwards so we have at least IRC_BUFSIZE_INCREMENT
		// bytes free in inbuf
		if (irc->inbuflen < irc->inbufused + IRC_BUFSIZE_INCREMENT) {
			if (irc->inbuflen + IRC_BUFSIZE_INCREMENT <= IRC_MAX_BUFSIZE) {
				irc->inbuflen += IRC_BUFSIZE_INCREMENT;
				irc->inbuf = g_realloc(irc->inbuf, irc->inbuflen);
			} else {
				// discard unparseable data from the buffer
				irc->inbufused = 0;
			}
		}

		len = purple_ssl_read(gsc, irc->inbuf + irc->inbufused, irc->inbuflen - irc->inbufused - 1);
		if (len > 0) {
			read_input(irc, len);
		}
	} while (len > 0);

	if (len < 0 && errno != EAGAIN) {
		gchar *tmp = g_strdup_printf(_("Lost connection with server: %s"),
				g_strerror(errno));
		purple_connection_error_reason (gc,
			PURPLE_CONNECTION_ERROR_NETWORK_ERROR, tmp);
		g_free(tmp);
	} else if (len == 0) {
		purple_connection_error_reason (gc,
			PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
			_("Server closed the connection"));
	}

	/* else: len < 0 && errno == EAGAIN; this is fine, try again later */
}
Пример #6
0
static gssize
msn_ssl_read(MsnNexus *nexus)
{
	gssize len;
	char temp_buf[4096];

	if ((len = purple_ssl_read(nexus->gsc, temp_buf,
			sizeof(temp_buf))) > 0)
	{
		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';
	}

	return len;
}
Пример #7
0
/*
 * Common code for reading, called from http_connection_read_cb_ssl and
 * http_connection_read_cb.
 */
static void
http_connection_read(PurpleHTTPConnection *conn)
{
	char buffer[1025];
	int cnt, count = 0;

	if (!conn->read_buf)
		conn->read_buf = g_string_new(NULL);

	do {
		if (conn->psc)
			cnt = purple_ssl_read(conn->psc, buffer, sizeof(buffer));
		else
			cnt = read(conn->fd, buffer, sizeof(buffer));

		if (cnt > 0) {
			count += cnt;
			g_string_append_len(conn->read_buf, buffer, cnt);
		}
	} while (cnt > 0);

	if (cnt == 0 || (cnt < 0 && errno != EAGAIN)) {
		if (cnt < 0)
			purple_debug_info("jabber", "BOSH (%p) read=%d, errno=%d, error=%s\n",
			                  conn, cnt, errno, g_strerror(errno));
		else
			purple_debug_info("jabber", "BOSH server closed the connection (%p)\n",
			                  conn);

		/*
		 * If the socket is closed, the processing really needs to know about
		 * it. Handle that now.
		 */
		http_connection_disconnected(conn);

		/* Process what we do have */
	}

	if (conn->read_buf->len > 0)
		jabber_bosh_http_connection_process(conn);
}
Пример #8
0
static void om_post_or_get_readdata_cb(gpointer data, gint source,
		PurpleInputCondition cond)
{
	OmegleConnection *omconn;
	gchar buf[4096];
	ssize_t len;

	omconn = data;

	if (omconn->method & OM_METHOD_SSL) {
		len = purple_ssl_read(omconn->ssl_conn,
				buf, sizeof(buf) - 1);
	} else {
		len = recv(omconn->fd, buf, sizeof(buf) - 1, 0);
	}

	if (len < 0)
	{
		if (errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR) {
			/* Try again later */
			return;
		}

		if (omconn->method & OM_METHOD_SSL && omconn->rx_len > 0) {
			/*
			 * This is a slightly hacky workaround for a bug in either
			 * GNU TLS or in the SSL implementation on Omegle's web
			 * servers.  The sequence of events is:
			 * 1. We attempt to read the first time and successfully read
			 *    the server's response.
			 * 2. We attempt to read a second time and libpurple's call
			 *    to gnutls_record_recv() returns the error
			 *    GNUTLS_E_UNEXPECTED_PACKET_LENGTH, or
			 *    "A TLS packet with unexpected length was received."
			 *
			 * Normally the server would have closed the connection
			 * cleanly and this second read() request would have returned
			 * 0.  Or maybe it's normal for SSL connections to be severed
			 * in this manner?  In any case, this differs from the behavior
			 * of the standard recv() system call.
			 */
			purple_debug_warning("omegle",
				"ssl error, but data received.  attempting to continue\n");
		} else {
			/* TODO: Is this a regular occurrence?  If so then maybe resend the request? */
			om_fatal_connection_cb(omconn);
			return;
		}
	}

	if (len > 0)
	{
		buf[len] = '\0';

		omconn->rx_buf = g_realloc(omconn->rx_buf,
				omconn->rx_len + len + 1);
		memcpy(omconn->rx_buf + omconn->rx_len, buf, len + 1);
		omconn->rx_len += len;

		/* Wait for more data before processing */
		return;
	}

	/* The server closed the connection, let's parse the data */
	om_connection_process_data(omconn);

	om_connection_destroy(omconn);
}
Пример #9
0
gboolean read_cookie(gpointer sodata, PurpleSslConnection * source, gint con)
{
    gchar buf[10240];
    gchar *cur = NULL;
    gchar *end = NULL;
    const gchar *uri = NULL;
    xmlnode *isc, *item;
    gint len, rcv_len;
    PurpleSslConnection *gsc;
    struct fetion_account_data *sip;
    sip = sodata;
    purple_debug_info("fetion:", "read cookie\n");
    gsc = (PurpleSslConnection *) source;
    rcv_len = purple_ssl_read(gsc, buf, 10240);
    if (rcv_len > 0) {
        buf[rcv_len] = '\0';
        purple_debug_info("fetion:", "read_cookie:%s\n", buf);
        cur = strstr(buf, "Cookie: ssic=");
        if (cur != NULL) {
            cur += 13;
            end = strstr(cur, ";");
            sip->ssic = g_strndup(cur, end - cur);
            purple_debug_info("fetion:", "read_cookie:[%s]\n",
                              sip->ssic);
            //      end=purple_url_encode(sip->ssic);
            //      purple_debug_info("fetion:","read_cookie:[%s]\n",end);
        }

        if ((cur = strstr(buf, "\r\n\r\n"))) {
            if (((strncmp(buf, "HTTP/1.1 200 OK\r\n", 17) != 0) &&
                    (strncmp(buf, "HTTP/1.1 100 Continue\r\n", 23) !=
                     0))) {
                purple_connection_error_reason(sip->gc,
                                               PURPLE_CONNECTION_ERROR_AUTHENTICATION_FAILED,
                                               _
                                               ("Invalid Password or Mobileno"));

                return FALSE;
            }

            cur += 4;
            len = strlen(cur);
            isc = xmlnode_from_str(cur, len);
            g_return_val_if_fail(isc != NULL, FALSE);
            item = xmlnode_get_child(isc, "user");
            g_return_val_if_fail(item != NULL, FALSE);
            uri = xmlnode_get_attrib(item, "uri");
            g_return_val_if_fail(uri != NULL, FALSE);
            sip->uri = g_strdup(uri);
            cur = strstr(uri, "@");
            g_return_val_if_fail(cur != NULL, FALSE);
            *cur = '\0';
            sip->username = g_strdup_printf("%s", uri + 4);
            purple_debug_info("fetion:", "cookie[%s]\n",
                              sip->username);
            purple_timeout_remove(sip->registertimeout);
            srvresolved(sip);

            xmlnode_free(isc);
            purple_ssl_close(gsc);

            return TRUE;

        }

    }
    purple_ssl_close(gsc);
    return FALSE;
}
Пример #10
0
/**
 * Read in all available data on the socket for a given connection.
 * All complete FLAPs handled immedate after they're received.
 * Incomplete FLAP data is stored locally and appended to the next
 * time this callback is triggered.
 *
 * This is called by flap_connection_recv_cb and
 * flap_connection_recv_cb_ssl for unencrypted/encrypted connections.
 */
static void
flap_connection_recv(FlapConnection *conn)
{
	gpointer buf;
	gsize buflen;
	gssize read;

	/* Read data until we run out of data and break out of the loop */
	while (TRUE)
	{
		/* Start reading a new FLAP */
		if (conn->buffer_incoming.data.data == NULL)
		{
			buf = conn->header + conn->header_received;
			buflen = 6 - conn->header_received;

			/* Read the first 6 bytes (the FLAP header) */
			if (conn->gsc)
				read = purple_ssl_read(conn->gsc, buf, buflen);
			else
				read = recv(conn->fd, buf, buflen, 0);

			/* Check if the FLAP server closed the connection */
			if (read == 0)
			{
				flap_connection_schedule_destroy(conn,
						OSCAR_DISCONNECT_REMOTE_CLOSED, NULL);
				break;
			}

			/* If there was an error then close the connection */
			if (read < 0)
			{
				if ((errno == EAGAIN) || (errno == EWOULDBLOCK))
					/* No worries */
					break;

				/* Error! */
				flap_connection_schedule_destroy(conn,
						OSCAR_DISCONNECT_LOST_CONNECTION, g_strerror(errno));
				break;
			}
			conn->od->gc->last_received = time(NULL);

			/* If we don't even have a complete FLAP header then do nothing */
			conn->header_received += read;
			if (conn->header_received < 6)
				break;

			/* All FLAP frames must start with the byte 0x2a */
			if (aimutil_get8(&conn->header[0]) != 0x2a)
			{
				flap_connection_schedule_destroy(conn,
						OSCAR_DISCONNECT_INVALID_DATA, NULL);
				break;
			}

			/* Initialize a new temporary FlapFrame for incoming data */
			conn->buffer_incoming.channel = aimutil_get8(&conn->header[1]);
			conn->buffer_incoming.seqnum = aimutil_get16(&conn->header[2]);
			conn->buffer_incoming.data.len = aimutil_get16(&conn->header[4]);
			conn->buffer_incoming.data.data = g_new(guint8, conn->buffer_incoming.data.len);
			conn->buffer_incoming.data.offset = 0;
		}

		buflen = conn->buffer_incoming.data.len - conn->buffer_incoming.data.offset;
		if (buflen)
		{
			buf = &conn->buffer_incoming.data.data[conn->buffer_incoming.data.offset];
			/* Read data into the temporary FlapFrame until it is complete */
			if (conn->gsc)
				read = purple_ssl_read(conn->gsc, buf, buflen);
			else
				read = recv(conn->fd, buf, buflen, 0);

			/* Check if the FLAP server closed the connection */
			if (read == 0)
			{
				flap_connection_schedule_destroy(conn,
						OSCAR_DISCONNECT_REMOTE_CLOSED, NULL);
				break;
			}

			if (read < 0)
			{
				if ((errno == EAGAIN) || (errno == EWOULDBLOCK))
					/* No worries */
					break;

				/* Error! */
				flap_connection_schedule_destroy(conn,
						OSCAR_DISCONNECT_LOST_CONNECTION, g_strerror(errno));
				break;
			}

			conn->buffer_incoming.data.offset += read;
			if (conn->buffer_incoming.data.offset < conn->buffer_incoming.data.len)
				/* Waiting for more data to arrive */
				break;
		}

		/* We have a complete FLAP!  Handle it and continue reading */
		byte_stream_rewind(&conn->buffer_incoming.data);
		parse_flap(conn->od, conn, &conn->buffer_incoming);
		conn->lastactivity = time(NULL);

		g_free(conn->buffer_incoming.data.data);
		conn->buffer_incoming.data.data = NULL;

		conn->header_received = 0;
	}
}
Пример #11
0
static void sevencup_post_or_get_readdata_cb(gpointer data, gint source,
		PurpleInputCondition cond)
{
	SevenCupConnection *scon;
	SevenCupAccount *sa;
	gchar buf[4096];
	gssize len;

	scon = data;
	sa = scon->sa;

	if (scon->method & STEAM_METHOD_SSL) {
		len = purple_ssl_read(scon->ssl_conn,
				buf, sizeof(buf) - 1);
	} else {
		len = recv(scon->fd, buf, sizeof(buf) - 1, 0);
	}

	if (len < 0)
	{
		if (errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR) {
			/* Try again later */
			return;
		}

		if (scon->method & STEAM_METHOD_SSL && scon->rx_len > 0) {
			/*
			 * This is a slightly hacky workaround for a bug in either
			 * GNU TLS or in the SSL implementation on steam's web
			 * servers.  The sequence of events is:
			 * 1. We attempt to read the first time and successfully read
			 *    the server's response.
			 * 2. We attempt to read a second time and libpurple's call
			 *    to gnutls_record_recv() returns the error
			 *    GNUTLS_E_UNEXPECTED_PACKET_LENGTH, or
			 *    "A TLS packet with unexpected length was received."
			 *
			 * Normally the server would have closed the connection
			 * cleanly and this second read() request would have returned
			 * 0.  Or maybe it's normal for SSL connections to be severed
			 * in this manner?  In any case, this differs from the behavior
			 * of the standard recv() system call.
			 */
			purple_debug_warning("7cups",
				"ssl error, but data received.  attempting to continue\n");
		} else {
			/* Try resend the request */
			scon->retry_count++;
			if (scon->retry_count < 3) {
				sevencup_connection_close(scon);
				scon->request_time = time(NULL);
				
				g_queue_push_head(sa->waiting_conns, scon);
				sevencup_next_connection(sa);
			} else {
				sevencup_fatal_connection_cb(scon);
			}
			return;
		}
	}

	if (len > 0)
	{
		buf[len] = '\0';

		scon->rx_buf = g_realloc(scon->rx_buf,
				scon->rx_len + len + 1);
		memcpy(scon->rx_buf + scon->rx_len, buf, len + 1);
		scon->rx_len += len;

		/* Wait for more data before processing */
		return;
	}

	/* The server closed the connection, let's parse the data */
	sevencup_connection_process_data(scon);

	sevencup_connection_destroy(scon);
	
	sevencup_next_connection(sa);
}
Пример #12
0
static void
pb_socket_got_data(gpointer userdata, PurpleSslConnection *conn, PurpleInputCondition cond)
{
	PushBulletAccount *pba = userdata;
	gchar *frame;
	guchar packet_code, length_code;
	guint64 frame_len;
	int read_len = 0;
	gboolean done_some_reads = FALSE;
	
	
	if (G_UNLIKELY(!pba->websocket_header_received)) {
		// HTTP/1.1 101 Switching Protocols
		// Server: nginx
		// Date: Sun, 19 Jul 2015 23:44:27 GMT
		// Connection: upgrade
		// Upgrade: websocket
		// Sec-WebSocket-Accept: pUDN5Js0uDN5KhEWoPJGLyTqwME=
		// Expires: 0
		// Cache-Control: no-cache
		gint nlbr_count = 0;
		gchar nextchar;
		
		while(nlbr_count < 4 && purple_ssl_read(conn, &nextchar, 1)) {
			if (nextchar == '\r' || nextchar == '\n') {
				nlbr_count++;
			} else {
				nlbr_count = 0;
			}
		}
		
		pba->websocket_header_received = TRUE;
		done_some_reads = TRUE;
	}
	
	packet_code = 0;
	while((read_len = purple_ssl_read(conn, &packet_code, 1)) == 1) {
		done_some_reads = TRUE;
		if (packet_code != 129) {
			if (packet_code == 136) {
				purple_debug_error("pushbullet", "websocket closed\n");
				
				purple_ssl_close(conn);
				pba->websocket = NULL;
				pba->websocket_header_received = FALSE;
				
				// revert to polling
				pb_start_polling(pba);
				
				return;
			}
			purple_debug_error("pushbullet", "unknown websocket error %d\n", packet_code);
			return;
		}
		
		length_code = 0;
		purple_ssl_read(conn, &length_code, 1);
		if (length_code <= 125) {
			frame_len = length_code;
		} else if (length_code == 126) {
			guchar len_buf[2];
			purple_ssl_read(conn, len_buf, 2);
			frame_len = (len_buf[1] << 8) + len_buf[0];
		} else if (length_code == 127) {
			purple_ssl_read(conn, &frame_len, 8);
			frame_len = GUINT64_FROM_BE(frame_len);
		}
		purple_debug_info("pushbullet", "frame_len: %d\n", frame_len);
		
		frame = g_new0(gchar, frame_len + 1);
		purple_ssl_read(conn, frame, frame_len);
		
		pb_process_frame(pba, frame);
		
		g_free(frame);
		packet_code = 0;
	}
	
	if (done_some_reads == FALSE && read_len == 0) {
		purple_connection_error_reason(pba->pc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, "Lost connection to server");
	}
}
Пример #13
0
static void
fb_mqtt_cb_read(gpointer data, gint fd, PurpleInputCondition cond)
{
	FbMqtt *mqtt = data;
	FbMqttMessage *msg;
	FbMqttPrivate *priv = mqtt->priv;
	gint res;
	guint mult;
	guint8 buf[1024];
	guint8 byte;
	gsize size;
	gssize rize;

	if (priv->remz < 1) {
		/* Reset the read buffer */
		g_byte_array_set_size(priv->rbuf, 0);

		res = purple_ssl_read(priv->gsc, &byte, sizeof byte);
		g_byte_array_append(priv->rbuf, &byte, sizeof byte);

		if (res != sizeof byte) {
			fb_mqtt_error(mqtt, FB_MQTT_ERROR_GENERAL,
			              _("Failed to read fixed header"));
			return;
		}

		mult = 1;

		do {
			res = purple_ssl_read(priv->gsc, &byte, sizeof byte);
			g_byte_array_append(priv->rbuf, &byte, sizeof byte);

			if (res != sizeof byte) {
				fb_mqtt_error(mqtt, FB_MQTT_ERROR_GENERAL,
				              _("Failed to read packet size"));
				return;
			}

			priv->remz += (byte & 127) * mult;
			mult *= 128;
		} while ((byte & 128) != 0);
	}

	if (priv->remz > 0) {
		size = MIN(priv->remz, sizeof buf);
		rize = purple_ssl_read(priv->gsc, buf, size);

		if (rize < 1) {
			fb_mqtt_error(mqtt, FB_MQTT_ERROR_GENERAL,
			              _("Failed to read packet data"));
			return;
		}

		g_byte_array_append(priv->rbuf, buf, rize);
		priv->remz -= rize;
	}

	if (priv->remz < 1) {
		msg = fb_mqtt_message_new_bytes(priv->rbuf);
		priv->remz = 0;

		if (G_UNLIKELY(msg == NULL)) {
			fb_mqtt_error(mqtt, FB_MQTT_ERROR_GENERAL,
			              _("Failed to parse message"));
			return;
		}

		fb_mqtt_read(mqtt, msg);
		g_object_unref(msg);
	}
}
Пример #14
0
gint
campfire_http_response(PurpleSslConnection * gsc,
		       CampfireSslTransaction * xaction,
		       G_GNUC_UNUSED PurpleInputCondition cond,
		       G_GNUC_UNUSED xmlnode ** node)
{
	gchar buf[1024];
	GString *ssl_input;
	gint len, errsv = 0;
	gint status;
	CampfireHttpResponse *response = &xaction->http_response;

	if (response->rx_state == CAMPFIRE_HTTP_RX_DONE) {
		purple_debug_info("campfire", "somefin aint right.\n");
		return -1;
	}

	/**********************************************************************
	 * read input from file descriptor
	 *********************************************************************/
	ssl_input = g_string_new("");
	errno = 0;
	while ((len = purple_ssl_read(gsc, buf, sizeof(buf))) > 0) {
		purple_debug_info("campfire",
				  "read %d bytes from HTTP Response, errno: %i\n",
				  len, errno);
		ssl_input = g_string_append_len(ssl_input, buf, len);
	}
	errsv = errno;

	/**********************************************************************
	 * handle return value of ssl input read
	 *********************************************************************/
	if (len < 0 && errsv == EAGAIN) {
		if (ssl_input->len == 0) {
			purple_debug_info("campfire",
					  "TRY AGAIN (returning)\n");
			g_string_free(ssl_input, TRUE);
			return 0;
		} else {
			purple_debug_info("campfire", "EAGAIN (continuing)\n");
		}
	} else if (len == 0) {
		purple_debug_info("campfire", "SERVER CLOSED CONNECTION\n");
		if (ssl_input->len == 0) {
			g_string_free(ssl_input, TRUE);
			return -1;
		}
	} else {
		purple_debug_info("campfire", "LOST CONNECTION\n");
		purple_debug_info("campfire", "errno: %d\n", errsv);
		g_string_free(ssl_input, TRUE);
		return -1;
	}

	if (!response->response) {
		response->response = g_string_new("");
	}

	purple_debug_info("campfire", "ssl_input:\n%s", ssl_input->str);

	/**********************************************************************
	 * process input with a simple state machine
	 *********************************************************************/
	while (!ssl_input_consumed(ssl_input)) {
		switch (response->rx_state) {
		case CAMPFIRE_HTTP_RX_HEADER:
			purple_debug_info("campfire",
					  "CAMPFIRE_HTTP_RX_HEADER\n");
			campfire_consume_http_header(response, ssl_input);
			if (campfire_http_header_received(response)) {
				campfire_process_http_header(response);
				response->rx_state = CAMPFIRE_HTTP_RX_CONTENT;
			}
			break;
		case CAMPFIRE_HTTP_RX_CONTENT:
			purple_debug_info("campfire",
					  "CAMPFIRE_HTTP_RX_CONTENT\n");
			campfire_consume_http_content(response, ssl_input);
			if (campfire_http_content_received(response)) {
				campfire_process_http_content(response);
				response->rx_state = CAMPFIRE_HTTP_RX_DONE;
			}
			break;
		case CAMPFIRE_HTTP_RX_DONE:
			purple_debug_info("campfire",
					  "CAMPFIRE_HTTP_RX_DONE\n");
			g_string_erase(ssl_input, 0, -1);	/* consume input */
			break;
		default:
			g_string_erase(ssl_input, 0, -1);	/* consume input */
			break;
		}
	}

	/**********************************************************************
	 * return http status code: -1=error, 0=input_not_received
	 *********************************************************************/
	if (response->rx_state == CAMPFIRE_HTTP_RX_DONE) {
		status = response->status;
	} else {
		status = 0;
	}
	g_string_free(ssl_input, TRUE);
	return status;
}