comparison libpurple/protocols/mxit/http.c @ 28526:69aa4660401a

Initial addition of the MXit protocol plugin, provided by the MXit folks themselves.
author John Bailey <rekkanoryo@rekkanoryo.org>
date Sun, 08 Nov 2009 23:55:56 +0000
parents
children 95f8e7fb1f67
comparison
equal deleted inserted replaced
28525:13e668ef158d 28526:69aa4660401a
1 /*
2 * MXit Protocol libPurple Plugin
3 *
4 * -- MXit client protocol implementation --
5 *
6 * Pieter Loubser <libpurple@mxit.com>
7 *
8 * (C) Copyright 2009 MXit Lifestyle (Pty) Ltd.
9 * <http://www.mxitlifestyle.com>
10 *
11 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; either version 2 of the License, or
14 * (at your option) any later version.
15 *
16 * This program is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU General Public License for more details.
20 *
21 * You should have received a copy of the GNU General Public License
22 * along with this program; if not, write to the Free Software
23 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
24 */
25
26 #include <stdio.h>
27 #include <unistd.h>
28 #include <string.h>
29 #include <errno.h>
30
31 #include "purple.h"
32
33 #include "mxit.h"
34 #include "protocol.h"
35 #include "http.h"
36
37
38 /* HTTP constants */
39 #define HTTP_11_200_OK "HTTP/1.1 200 OK\r\n"
40 #define HTTP_11_100_CONT "HTTP/1.1 100 Continue\r\n"
41 #define HTTP_11_SEPERATOR "\r\n\r\n"
42 #define HTTP_CONTENT_LEN "Content-Length: "
43
44
45 /* define to enable HTTP debugging */
46 #define DEBUG_HTTP
47
48
49 /*------------------------------------------------------------------------
50 * This will freeup the memory used by a HTTP request structure
51 *
52 * @param req The HTTP structure's resources should be freed up
53 */
54 static void free_http_request( struct http_request* req )
55 {
56 g_free( req->host );
57 g_free( req->data );
58 g_free( req );
59 }
60
61
62 /*------------------------------------------------------------------------
63 * Write the request to the HTTP server.
64 *
65 * @param fd The file descriptor
66 * @param pktdata The packet data
67 * @param pktlen The length of the packet data
68 * @return Return -1 on error, otherwise 0
69 */
70 static int mxit_http_raw_write( int fd, const char* pktdata, int pktlen )
71 {
72 int written;
73 int res;
74
75 written = 0;
76 while ( written < pktlen ) {
77 res = write( fd, &pktdata[written], pktlen - written );
78 if ( res <= 0 ) {
79 /* error on socket */
80 if ( errno == EAGAIN )
81 continue;
82
83 purple_debug_error( MXIT_PLUGIN_ID, "Error while writing packet to HTTP server (%i)\n", res );
84 return -1;
85 }
86 written += res;
87 }
88
89 return 0;
90 }
91
92
93 /*------------------------------------------------------------------------
94 * Callback when data is received from the HTTP server.
95 *
96 * @param user_data The MXit session object
97 * @param source The file-descriptor on which data was received
98 * @param cond Condition which caused the callback (PURPLE_INPUT_READ)
99 */
100 static void mxit_cb_http_read( gpointer user_data, gint source, PurpleInputCondition cond )
101 {
102 struct MXitSession* session = (struct MXitSession*) user_data;
103 char buf[256];
104 int buflen;
105 char* body;
106 int bodylen;
107 char* ch;
108 int len;
109 char* tmp;
110 int res;
111 char* next;
112
113 purple_debug_info( MXIT_PLUGIN_ID, "mxit_cb_http_read\n" );
114
115 if ( session->rx_state == RX_STATE_RLEN ) {
116 /* we are reading in the HTTP headers */
117
118 /* copy partial headers if we have any part saved */
119 memcpy( buf, session->rx_dbuf, session->rx_i );
120 buflen = session->rx_i;
121
122 /* read bytes from the socket */
123 len = read( session->fd, buf + buflen, sizeof( buf ) - buflen );
124 if ( len <= 0 ) {
125 /* connection has been terminated, or error occured */
126 goto done;
127 }
128
129 //nextpacket:
130
131 #ifdef DEBUG_HTTP
132 purple_debug_info( MXIT_PLUGIN_ID, "HTTP POST READ 1: (%i)\n", len );
133 dump_bytes( session, buf + buflen, len );
134 #endif
135
136 /* see if we have all the HTTP headers yet */
137 ch = strstr( buf, HTTP_11_SEPERATOR );
138 if ( !ch ) {
139 /* we need to wait for more input, so save what we have */
140 session->rx_i = buflen + len;
141 memcpy( session->rx_dbuf, buf, session->rx_i );
142 return;
143 }
144 buflen += len;
145
146 /* we have the header's end now skip over the http seperator to get the body offset */
147 ch += strlen( HTTP_11_SEPERATOR );
148 *(ch - 1) = '\0';
149 body = ch;
150
151 res = buflen - ( ch - buf );
152 if ( res > 0 ) {
153 /* we read more bytes than just the header so copy it over */
154 memcpy( session->rx_dbuf, ch, res );
155 session->rx_i = res;
156 }
157 else {
158 session->rx_i = 0;
159 }
160
161 /* test for a good response */
162 if ( ( strncmp( buf, HTTP_11_200_OK, strlen( HTTP_11_200_OK ) ) != 0 ) && ( strncmp( buf, HTTP_11_100_CONT, strlen( HTTP_11_100_CONT ) ) != 0 ) ) {
163 /* bad result */
164 purple_debug_error( MXIT_PLUGIN_ID, "HTTP error: %s\n", ch );
165 goto done;
166 }
167
168 /* find the content-length */
169 ch = (char*) purple_strcasestr( buf, HTTP_CONTENT_LEN );
170 if ( !ch ) {
171 /* bad request. it does not contain a content-length header */
172 purple_debug_error( MXIT_PLUGIN_ID, "HTTP reply received without content-length header (ignoring packet)\n" );
173 goto done;
174 }
175
176 /* parse the content-length */
177 ch += strlen( HTTP_CONTENT_LEN );
178 tmp = strchr( ch, '\r' );
179 if ( !tmp ) {
180 purple_debug_error( MXIT_PLUGIN_ID, "Received bad HTTP reply packet (ignoring packet)\n" );
181 goto done;
182 }
183 tmp = g_strndup( ch, tmp - ch );
184 bodylen = atoi( tmp );
185 g_free( tmp );
186 tmp = NULL;
187
188 if ( buflen > ( ( body - buf ) + bodylen ) ) {
189 /* we have a second packet here */
190 next = body + bodylen;
191 session->rx_res = 0;
192 }
193 else {
194 session->rx_res = bodylen - session->rx_i;
195 }
196
197 if ( session->rx_res == 0 ) {
198 /* we have read all the data */
199 session->rx_i = bodylen;
200 session->rx_state = RX_STATE_PROC;
201 }
202 else {
203 /* there is still some data outstanding */
204 session->rx_state = RX_STATE_DATA;
205 }
206 }
207 else if ( session->rx_state == RX_STATE_DATA ) {
208 /* we are reading the HTTP content (body) */
209
210 /* read bytes from the socket */
211 len = read( session->fd, &session->rx_dbuf[session->rx_i], session->rx_res );
212 if ( len <= 0 ) {
213 /* connection has been terminated, or error occured */
214 goto done;
215 }
216
217 #ifdef DEBUG_HTTP
218 purple_debug_info( MXIT_PLUGIN_ID, "HTTP POST READ 2: (%i)\n", len );
219 dump_bytes( session, &session->rx_dbuf[session->rx_i], len );
220 #endif
221 session->rx_i += len;
222 session->rx_res -= len;
223
224 if ( session->rx_res == 0 ) {
225 /* ok, so now we have read in the whole packet */
226 session->rx_state = RX_STATE_PROC;
227 }
228 }
229
230 if ( session->rx_state == RX_STATE_PROC ) {
231 mxit_parse_packet( session );
232
233 #if 0
234 if ( next ) {
235 /* there is another packet of which we read some data */
236
237 /* reset input */
238 session->rx_state = RX_STATE_RLEN;
239 session->rx_lbuf[0] = '\0';
240 session->rx_i = 0;
241 session->rx_res = 0;
242
243 /* move read data */
244 len = next - buf;
245 buflen = len;
246 memcpy( buf, next, len );
247 goto nextpacket;
248 }
249 #endif
250
251 /* we are done */
252 goto done;
253 }
254
255 return;
256 done:
257 close( session->fd );
258 purple_input_remove( session->http_handler );
259 session->http_handler = 0;
260 }
261
262
263 /*------------------------------------------------------------------------
264 * Callback invoked once the connection has been established to the HTTP server,
265 * or on connection failure.
266 *
267 * @param user_data The MXit session object
268 * @param source The file-descriptor associated with the connection
269 * @param error_message Message explaining why the connection failed
270 */
271 static void mxit_cb_http_connect( gpointer user_data, gint source, const gchar* error_message )
272 {
273 struct http_request* req = (struct http_request*) user_data;
274
275 purple_debug_info( MXIT_PLUGIN_ID, "mxit_cb_http_connect\n" );
276
277 /* source is the file descriptor of the new connection */
278 if ( source < 0 ) {
279 purple_debug_info( MXIT_PLUGIN_ID, "mxit_cb_http_connect failed: %s\n", error_message );
280 purple_connection_error( req->session->con, _( "Unable to connect to the mxit HTTP server. Please check your server server settings." ) );
281 return;
282 }
283
284 /* we now have an open and active TCP connection to the mxit server */
285 req->session->fd = source;
286
287 /* reset the receive buffer */
288 req->session->rx_state = RX_STATE_RLEN;
289 req->session->rx_lbuf[0] = '\0';
290 req->session->rx_i = 0;
291 req->session->rx_res = 0;
292
293 /* start listening on the open connection for messages from the server (reference: "libpurple/eventloop.h") */
294 req->session->http_handler = purple_input_add( req->session->fd, PURPLE_INPUT_READ, mxit_cb_http_read, req->session );
295
296 /* actually send the request to the HTTP server */
297 mxit_http_raw_write( req->session->fd, req->data, req->datalen );
298
299 /* free up resources */
300 free_http_request( req );
301 req = NULL;
302 }
303
304
305 /*------------------------------------------------------------------------
306 * Create HTTP connection for sending a HTTP request
307 *
308 * @param session The MXit session object
309 * @param host The server name to connect to
310 * @param port The port number to connect to
311 * @param data The HTTP request data (including HTTP headers etc.)
312 * @param datalen The HTTP request data length
313 */
314 void mxit_http_send_request( struct MXitSession* session, char* host, int port, const char* data, int datalen )
315 {
316 PurpleProxyConnectData* con = NULL;
317 struct http_request* req;
318
319 /* build the http request */
320 req = g_new0( struct http_request, 1 );
321 req->session = session;
322 req->host = host;
323 req->port = port;
324 req->data = g_malloc0( datalen );
325 memcpy( req->data, data, datalen );
326 req->datalen = datalen;
327
328 /* open connection to the HTTP server */
329 con = purple_proxy_connect( NULL, session->acc, host, port, mxit_cb_http_connect, req );
330 }
331