source: protocols/http_client.c @ f3e1e92

Last change on this file since f3e1e92 was f3e1e92, checked in by Wilmer van der Gaast <wilmer@…>, at 2005-12-17T19:13:32Z

Fixed parameter ordering.

  • Property mode set to 100644
File size: 8.3 KB
Line 
1  /********************************************************************\
2  * BitlBee -- An IRC to other IM-networks gateway                     *
3  *                                                                    *
4  * Copyright 2002-2005 Wilmer van der Gaast and others                *
5  \********************************************************************/
6
7/* HTTP(S) module                                                       */
8
9/*
10  This program is free software; you can redistribute it and/or modify
11  it under the terms of the GNU General Public License as published by
12  the Free Software Foundation; either version 2 of the License, or
13  (at your option) any later version.
14
15  This program is distributed in the hope that it will be useful,
16  but WITHOUT ANY WARRANTY; without even the implied warranty of
17  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  GNU General Public License for more details.
19
20  You should have received a copy of the GNU General Public License with
21  the Debian GNU/Linux distribution in /usr/share/common-licenses/GPL;
22  if not, write to the Free Software Foundation, Inc., 59 Temple Place,
23  Suite 330, Boston, MA  02111-1307  USA
24*/
25
26#include <string.h>
27#include <stdio.h>
28
29#include "sock.h"
30#include "http_client.h"
31#include "url.h"
32
33
34static void http_connected( gpointer data, int source, GaimInputCondition cond );
35static void http_ssl_connected( gpointer data, void *source, GaimInputCondition cond );
36static void http_incoming_data( gpointer data, int source, GaimInputCondition cond );
37
38
39void *http_dorequest( char *host, int port, int ssl, char *request, http_input_function func, gpointer data )
40{
41        struct http_request *req;
42        int error = 0;
43       
44        req = g_new0( struct http_request, 1 );
45       
46        if( ssl )
47        {
48                req->ssl = ssl_connect( host, port, http_ssl_connected, req );
49                if( req->ssl == NULL )
50                        error = 1;
51        }
52        else
53        {
54                req->fd = proxy_connect( host, port, http_connected, req );
55                if( req->fd < 0 )
56                        error = 1;
57        }
58       
59        if( error )
60        {
61                g_free( req );
62                return( NULL );
63        }
64       
65        req->func = func;
66        req->data = data;
67        req->request = g_strdup( request );
68        req->request_length = strlen( request );
69       
70        return( req );
71}
72
73/* This one is actually pretty simple... Might get more calls if we can't write
74   the whole request at once. */
75static void http_connected( gpointer data, int source, GaimInputCondition cond )
76{
77        struct http_request *req = data;
78        int st;
79       
80        if( source < 0 )
81                goto error;
82       
83        if( req->inpa > 0 )
84                gaim_input_remove( req->inpa );
85       
86        sock_make_nonblocking( req->fd );
87       
88        if( req->ssl )
89        {
90                st = ssl_write( req->ssl, req->request + req->bytes_written,
91                                req->request_length - req->bytes_written );
92                if( st < 0 )
93                {
94                        if( ssl_errno != SSL_AGAIN )
95                        {
96                                ssl_disconnect( req->ssl );
97                                goto error;
98                        }
99                }
100        }
101        else
102        {
103                st = write( source, req->request + req->bytes_written,
104                                    req->request_length - req->bytes_written );
105                if( st < 0 )
106                {
107                        if( !sockerr_again() )
108                        {
109                                closesocket( req->fd );
110                                goto error;
111                        }
112                }
113        }
114       
115        if( st > 0 )
116                req->bytes_written += st;
117       
118        if( req->bytes_written < req->request_length )
119                req->inpa = gaim_input_add( source,
120                                            req->ssl ? ssl_getdirection( req->ssl ) : GAIM_INPUT_WRITE,
121                                            http_connected, req );
122        else
123                req->inpa = gaim_input_add( source, GAIM_INPUT_READ, http_incoming_data, req );
124       
125        return;
126       
127error:
128        req->func( req );
129       
130        g_free( req->request );
131        g_free( req );
132       
133        return;
134}
135
136static void http_ssl_connected( gpointer data, void *source, GaimInputCondition cond )
137{
138        struct http_request *req = data;
139       
140        if( source == NULL )
141                return http_connected( data, -1, cond );
142       
143        req->fd = ssl_getfd( source );
144       
145        return http_connected( data, req->fd, cond );
146}
147
148static void http_incoming_data( gpointer data, int source, GaimInputCondition cond )
149{
150        struct http_request *req = data;
151        int evil_server = 0;
152        char buffer[2048];
153        char *end1, *end2;
154        int st;
155       
156        if( req->inpa > 0 )
157                gaim_input_remove( req->inpa );
158       
159        if( req->ssl )
160        {
161                st = ssl_read( req->ssl, buffer, sizeof( buffer ) );
162                if( st < 0 )
163                {
164                        if( ssl_errno != SSL_AGAIN )
165                        {
166                                goto cleanup;
167                        }
168                }
169                else if( st == 0 )
170                {
171                        goto got_reply;
172                }
173        }
174        else
175        {
176                st = read( req->fd, buffer, sizeof( buffer ) );
177                if( st < 0 )
178                {
179                        if( !sockerr_again() )
180                        {
181                                goto cleanup;
182                        }
183                }
184                else if( st == 0 )
185                {
186                        goto got_reply;
187                }
188        }
189       
190        if( st > 0 )
191        {
192                req->reply_headers = g_realloc( req->reply_headers, req->bytes_read + st + 1 );
193                memcpy( req->reply_headers + req->bytes_read, buffer, st );
194                req->bytes_read += st;
195        }
196       
197        /* There will be more! */
198        req->inpa = gaim_input_add( req->fd,
199                                    req->ssl ? ssl_getdirection( req->ssl ) : GAIM_INPUT_READ,
200                                    http_incoming_data, req );
201       
202        return;
203
204got_reply:
205        /* Zero termination is very convenient. */
206        req->reply_headers[req->bytes_read] = 0;
207       
208        /* Find the separation between headers and body, and keep stupid
209           webservers in mind. */
210        end1 = strstr( req->reply_headers, "\r\n\r\n" );
211        end2 = strstr( req->reply_headers, "\n\n" );
212       
213        if( end2 && end2 < end1 )
214        {
215                end1 = end2 + 1;
216                evil_server = 1;
217        }
218        else
219        {
220                end1 += 2;
221        }
222       
223        if( end1 )
224        {
225                *end1 = 0;
226               
227                if( evil_server )
228                        req->reply_body = end1 + 1;
229                else
230                        req->reply_body = end1 + 2;
231        }
232       
233        if( ( end1 = strchr( req->reply_headers, ' ' ) ) != NULL )
234        {
235                if( sscanf( end1 + 1, "%d", &req->status_code ) != 1 )
236                        req->status_code = -1;
237        }
238        else
239        {
240                req->status_code = -1;
241        }
242       
243        if( req->status_code == 301 || req->status_code == 302 )
244        {
245                char *loc, *new_request, *new_host;
246                int error = 0, new_port, new_proto;
247               
248                loc = strstr( req->reply_headers, "\nLocation: " );
249                if( loc == NULL ) /* We can't handle this redirect... */
250                        goto cleanup;
251               
252                loc += 11;
253                while( *loc == ' ' )
254                        loc ++;
255               
256                /* TODO/FIXME: Possibly have to handle relative redirections,
257                   and rewrite Host: headers. Not necessary for now, it's
258                   enough for passport authentication like this. */
259               
260                if( *loc == '/' )
261                {
262                        /* Just a different pathname... */
263                       
264                        /* Since we don't cache the servername, and since we
265                           don't need this yet anyway, I won't implement it. */
266                       
267                        goto cleanup;
268                }
269                else
270                {
271                        /* A whole URL */
272                        url_t *url;
273                        char *s;
274                       
275                        s = strstr( loc, "\r\n" );
276                        if( s == NULL )
277                                goto cleanup;
278                       
279                        url = g_new0( url_t, 1 );
280                        *s = 0;
281                       
282                        if( !url_set( url, loc ) )
283                        {
284                                g_free( url );
285                                goto cleanup;
286                        }
287                       
288                        /* Okay, this isn't fun! We have to rebuild the request... :-( */
289                        new_request = g_malloc( req->request_length + strlen( url->file ) );
290                       
291                        /* So, now I just allocated enough memory, so I'm
292                           going to use strcat(), whether you like it or not. :-) */
293                       
294                        /* First, find the GET/POST/whatever from the original request. */
295                        s = strchr( req->request, ' ' );
296                        if( s == NULL )
297                        {
298                                g_free( new_request );
299                                g_free( url );
300                                goto cleanup;
301                        }
302                       
303                        *s = 0;
304                        sprintf( new_request, "%s %s HTTP/1.0\r\n", req->request, url->file );
305                        *s = ' ';
306                       
307                        s = strstr( req->request, "\r\n" );
308                        if( s == NULL )
309                        {
310                                g_free( new_request );
311                                g_free( url );
312                                goto cleanup;
313                        }
314                       
315                        strcat( new_request, s + 2 );
316                        new_host = g_strdup( url->host );
317                        new_port = url->port;
318                        new_proto = url->proto;
319                       
320                        g_free( url );
321                }
322               
323                if( req->ssl )
324                        ssl_disconnect( req->ssl );
325                else
326                        closesocket( req->fd );
327               
328                req->fd = -1;
329                req->ssl = 0;
330               
331                if( new_proto == PROTO_HTTPS )
332                {
333                        req->ssl = ssl_connect( new_host, new_port, http_ssl_connected, req );
334                        if( req->ssl == NULL )
335                                error = 1;
336                }
337                else
338                {
339                        req->fd = proxy_connect( new_host, new_port, http_connected, req );
340                        if( req->fd < 0 )
341                                error = 1;
342                }
343                g_free( new_host );
344               
345                if( error )
346                {
347                        g_free( new_request );
348                        goto cleanup;
349                }
350               
351                g_free( req->request );
352                g_free( req->reply_headers );
353                req->request = new_request;
354                req->request_length = strlen( new_request );
355                req->bytes_read = req->bytes_written = req->inpa = 0;
356                req->reply_headers = req->reply_body = NULL;
357               
358                return;
359        }
360       
361        /* Assume that a closed connection means we're finished, this indeed
362           breaks with keep-alive connections and faulty connections. */
363        req->finished = 1;
364
365cleanup:
366        if( req->ssl )
367                ssl_disconnect( req->ssl );
368        else
369                closesocket( req->fd );
370       
371        req->func( req );
372       
373        g_free( req->request );
374        g_free( req->reply_headers );
375        g_free( req );
376}
Note: See TracBrowser for help on using the repository browser.