Mercurial > pidgin
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 |