source: lib/http_client.c @ 77ed9b7

Last change on this file since 77ed9b7 was 5ebff60, checked in by dequis <dx@…>, at 2015-02-20T22:50:54Z

Reindent everything to K&R style with tabs

Used uncrustify, with the configuration file in ./doc/uncrustify.cfg

Commit author set to "Indent <please@…>" so that it's easier to
skip while doing git blame.

  • Property mode set to 100644
File size: 17.4 KB
RevLine 
[5ebff60]1/********************************************************************\
[8a9afe4]2  * BitlBee -- An IRC to other IM-networks gateway                     *
3  *                                                                    *
[ca8037e]4  * Copyright 2002-2013 Wilmer van der Gaast and others                *
[8a9afe4]5  \********************************************************************/
6
[52b3a99]7/* HTTP(S) module                                                       */
[8a9afe4]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;
[6f10697]22  if not, write to the Free Software Foundation, Inc., 51 Franklin St.,
23  Fifth Floor, Boston, MA  02110-1301  USA
[8a9afe4]24*/
25
26#include <string.h>
[52b3a99]27#include <stdio.h>
[8a9afe4]28
29#include "http_client.h"
[52b3a99]30#include "url.h"
[e4d6271]31#include "sock.h"
[8a9afe4]32
33
[5ebff60]34static gboolean http_connected(gpointer data, int source, b_input_condition cond);
35static gboolean http_ssl_connected(gpointer data, int returncode, void *source, b_input_condition cond);
36static gboolean http_incoming_data(gpointer data, int source, b_input_condition cond);
37static void http_free(struct http_request *req);
[8a9afe4]38
39
[5ebff60]40struct http_request *http_dorequest(char *host, int port, int ssl, char *request, http_input_function func,
41                                    gpointer data)
[8a9afe4]42{
43        struct http_request *req;
44        int error = 0;
[5ebff60]45
46        req = g_new0(struct http_request, 1);
47
48        if (ssl) {
49                req->ssl = ssl_connect(host, port, TRUE, http_ssl_connected, req);
50                if (req->ssl == NULL) {
[8a9afe4]51                        error = 1;
[5ebff60]52                }
53        } else {
54                req->fd = proxy_connect(host, port, http_connected, req);
55                if (req->fd < 0) {
[8a9afe4]56                        error = 1;
[5ebff60]57                }
[8a9afe4]58        }
[5ebff60]59
60        if (error) {
61                http_free(req);
[fb98634]62                return NULL;
[8a9afe4]63        }
[5ebff60]64
[52b3a99]65        req->func = func;
66        req->data = data;
[5ebff60]67        req->request = g_strdup(request);
68        req->request_length = strlen(request);
[7885d0f]69        req->redir_ttl = 3;
[ca8037e]70        req->content_length = -1;
[5ebff60]71
72        if (getenv("BITLBEE_DEBUG")) {
73                printf("About to send HTTP request:\n%s\n", req->request);
74        }
75
[11ec078]76        return req;
[8a9afe4]77}
78
[5ebff60]79struct http_request *http_dorequest_url(char *url_string, http_input_function func, gpointer data)
[0790644]80{
[5ebff60]81        url_t *url = g_new0(url_t, 1);
[0790644]82        char *request;
83        void *ret;
[5ebff60]84
85        if (!url_set(url, url_string)) {
86                g_free(url);
[0790644]87                return NULL;
88        }
[5ebff60]89
90        if (url->proto != PROTO_HTTP && url->proto != PROTO_HTTPS) {
91                g_free(url);
[0790644]92                return NULL;
93        }
[5ebff60]94
95        request = g_strdup_printf("GET %s HTTP/1.0\r\n"
96                                  "Host: %s\r\n"
97                                  "User-Agent: BitlBee " BITLBEE_VERSION " " ARCH "/" CPU "\r\n"
98                                  "\r\n", url->file, url->host);
99
100        ret = http_dorequest(url->host, url->port,
101                             url->proto == PROTO_HTTPS, request, func, data);
102
103        g_free(url);
104        g_free(request);
[266fe2f]105        return ret;
[0790644]106}
107
[5ebff60]108/* This one is actually pretty simple... Might get more calls if we can't write
[8a9afe4]109   the whole request at once. */
[5ebff60]110static gboolean http_connected(gpointer data, int source, b_input_condition cond)
[8a9afe4]111{
112        struct http_request *req = data;
113        int st;
[5ebff60]114
115        if (source < 0) {
[8a9afe4]116                goto error;
[5ebff60]117        }
118
119        if (req->inpa > 0) {
120                b_event_remove(req->inpa);
121        }
122
123        sock_make_nonblocking(req->fd);
124
125        if (req->ssl) {
126                st = ssl_write(req->ssl, req->request + req->bytes_written,
127                               req->request_length - req->bytes_written);
128                if (st < 0) {
129                        if (ssl_errno != SSL_AGAIN) {
130                                ssl_disconnect(req->ssl);
[8a9afe4]131                                goto error;
132                        }
133                }
[5ebff60]134        } else {
135                st = write(source, req->request + req->bytes_written,
136                           req->request_length - req->bytes_written);
137                if (st < 0) {
138                        if (!sockerr_again()) {
139                                closesocket(req->fd);
[8a9afe4]140                                goto error;
141                        }
142                }
143        }
[5ebff60]144
145        if (st > 0) {
[8a9afe4]146                req->bytes_written += st;
[5ebff60]147        }
148
149        if (req->bytes_written < req->request_length) {
150                req->inpa = b_input_add(source,
151                                        req->ssl ? ssl_getdirection(req->ssl) : B_EV_IO_WRITE,
152                                        http_connected, req);
153        } else {
154                req->inpa = b_input_add(source, B_EV_IO_READ, http_incoming_data, req);
155        }
156
[ba9edaa]157        return FALSE;
[5ebff60]158
[8a9afe4]159error:
[5ebff60]160        if (req->status_string == NULL) {
161                req->status_string = g_strdup("Error while writing HTTP request");
162        }
163
164        req->func(req);
165        http_free(req);
[ba9edaa]166        return FALSE;
[8a9afe4]167}
168
[5ebff60]169static gboolean http_ssl_connected(gpointer data, int returncode, void *source, b_input_condition cond)
[8a9afe4]170{
171        struct http_request *req = data;
[5ebff60]172
173        if (source == NULL) {
174                if (returncode != 0) {
175                        char *err = ssl_verify_strerror(returncode);
[a72dc2b]176                        req->status_string = g_strdup_printf(
[5ebff60]177                                "Certificate verification problem 0x%x: %s",
178                                returncode, err ? err : "Unknown");
179                        g_free(err);
[a72dc2b]180                }
[5ebff60]181                return http_connected(data, -1, cond);
[a72dc2b]182        }
[5ebff60]183
184        req->fd = ssl_getfd(source);
185
186        return http_connected(data, req->fd, cond);
[8a9afe4]187}
188
[ca8037e]189typedef enum {
190        CR_OK,
191        CR_EOF,
192        CR_ERROR,
193        CR_ABORT,
194} http_ret_t;
195
[5ebff60]196static gboolean http_handle_headers(struct http_request *req);
197static http_ret_t http_process_chunked_data(struct http_request *req, const char *buffer, int len);
198static http_ret_t http_process_data(struct http_request *req, const char *buffer, int len);
[8bd866f]199
[5ebff60]200static gboolean http_incoming_data(gpointer data, int source, b_input_condition cond)
[8a9afe4]201{
202        struct http_request *req = data;
[5f2f728]203        char buffer[4096];
[8a9afe4]204        int st;
[5ebff60]205
206        if (req->inpa > 0) {
207                b_event_remove(req->inpa);
[8256ad5]208                req->inpa = 0;
209        }
[5ebff60]210
211        if (req->ssl) {
212                st = ssl_read(req->ssl, buffer, sizeof(buffer));
213                if (st < 0) {
214                        if (ssl_errno != SSL_AGAIN) {
[ad8b8a3]215                                /* goto cleanup; */
[5ebff60]216
[ad8b8a3]217                                /* YAY! We have to deal with crappy Microsoft
218                                   servers that LOVE to send invalid TLS
219                                   packets that abort connections! \o/ */
[5ebff60]220
[8bd866f]221                                goto eof;
[8a9afe4]222                        }
[5ebff60]223                } else if (st == 0) {
[8bd866f]224                        goto eof;
[8a9afe4]225                }
[5ebff60]226        } else {
227                st = read(req->fd, buffer, sizeof(buffer));
228                if (st < 0) {
229                        if (!sockerr_again()) {
230                                req->status_string = g_strdup(strerror(errno));
[8a9afe4]231                                goto cleanup;
232                        }
[5ebff60]233                } else if (st == 0) {
[8bd866f]234                        goto eof;
[8a9afe4]235                }
236        }
[5ebff60]237
238        if (st > 0) {
[ca8037e]239                http_ret_t c;
[5ebff60]240
241                if (req->flags & HTTPC_CHUNKED) {
242                        c = http_process_chunked_data(req, buffer, st);
243                } else {
244                        c = http_process_data(req, buffer, st);
245                }
246
247                if (c == CR_EOF) {
[ca8037e]248                        goto eof;
[5ebff60]249                } else if (c == CR_ERROR || c == CR_ABORT) {
[ca8037e]250                        return FALSE;
[5ebff60]251                }
[8a9afe4]252        }
[5ebff60]253
254        if (req->content_length != -1 &&
255            req->body_size >= req->content_length) {
[ca8037e]256                goto eof;
[5ebff60]257        }
258
259        if (ssl_pending(req->ssl)) {
260                return http_incoming_data(data, source, cond);
261        }
262
[8a9afe4]263        /* There will be more! */
[5ebff60]264        req->inpa = b_input_add(req->fd,
265                                req->ssl ? ssl_getdirection(req->ssl) : B_EV_IO_READ,
266                                http_incoming_data, req);
267
[8bd866f]268        return FALSE;
[8a9afe4]269
[8bd866f]270eof:
[dd672e2]271        req->flags |= HTTPC_EOF;
[5ebff60]272
[0602496]273        /* Maybe if the webserver is overloaded, or when there's bad SSL
274           support... */
[5ebff60]275        if (req->bytes_read == 0) {
276                req->status_string = g_strdup("Empty HTTP reply");
[0602496]277                goto cleanup;
[7deb447]278        }
[8bd866f]279
280cleanup:
[286cd48]281        /* Avoid g_source_remove warnings */
282        req->inpa = 0;
283
[5ebff60]284        if (req->ssl) {
285                ssl_disconnect(req->ssl);
286        } else {
287                closesocket(req->fd);
288        }
289
290        if (req->body_size < req->content_length) {
[ca8037e]291                req->status_code = -1;
[5ebff60]292                g_free(req->status_string);
293                req->status_string = g_strdup("Response truncated");
294        }
295
296        if (getenv("BITLBEE_DEBUG") && req) {
297                printf("Finishing HTTP request with status: %s\n",
298                       req->status_string ? req->status_string : "NULL");
299        }
300
301        req->func(req);
302        http_free(req);
[8bd866f]303        return FALSE;
304}
305
[5ebff60]306static http_ret_t http_process_chunked_data(struct http_request *req, const char *buffer, int len)
[ca8037e]307{
308        char *chunk, *eos, *s;
[5ebff60]309
310        if (len < 0) {
[ca8037e]311                return TRUE;
[5ebff60]312        }
313
314        if (len > 0) {
315                req->cbuf = g_realloc(req->cbuf, req->cblen + len + 1);
316                memcpy(req->cbuf + req->cblen, buffer, len);
[ca8037e]317                req->cblen += len;
318                req->cbuf[req->cblen] = '\0';
319        }
[5ebff60]320
[ca8037e]321        /* Turns out writing a proper chunked-encoding state machine is not
[ab19567]322           that simple. :-( I've tested this one feeding it byte by byte so
323           I hope it's solid now. */
[ca8037e]324        chunk = req->cbuf;
325        eos = req->cbuf + req->cblen;
[5ebff60]326        while (TRUE) {
[ca8037e]327                int clen = 0;
[5ebff60]328
[ca8037e]329                /* Might be a \r\n from the last chunk. */
330                s = chunk;
[5ebff60]331                while (g_ascii_isspace(*s)) {
332                        s++;
333                }
[ca8037e]334                /* Chunk length. Might be incomplete. */
[5ebff60]335                if (s < eos && sscanf(s, "%x", &clen) != 1) {
[ca8037e]336                        return CR_ERROR;
[5ebff60]337                }
338                while (g_ascii_isxdigit(*s)) {
339                        s++;
340                }
341
[ca8037e]342                /* If we read anything here, it *must* be \r\n. */
[5ebff60]343                if (strncmp(s, "\r\n", MIN(2, eos - s)) != 0) {
[ca8037e]344                        return CR_ERROR;
[5ebff60]345                }
[ca8037e]346                s += 2;
[5ebff60]347
348                if (s >= eos) {
[ca8037e]349                        break;
[5ebff60]350                }
351
352                /* 0-length chunk means end of response. */
353                if (clen == 0) {
[ca8037e]354                        return CR_EOF;
[5ebff60]355                }
356
[ab19567]357                /* Wait for the whole chunk to arrive. */
[5ebff60]358                if (s + clen > eos) {
[ca8037e]359                        break;
[5ebff60]360                }
361                if (http_process_data(req, s, clen) != CR_OK) {
[ca8037e]362                        return CR_ABORT;
[5ebff60]363                }
364
[ca8037e]365                chunk = s + clen;
366        }
[5ebff60]367
368        if (chunk != req->cbuf) {
[ca8037e]369                req->cblen = eos - chunk;
[5ebff60]370                s = g_memdup(chunk, req->cblen + 1);
371                g_free(req->cbuf);
[ca8037e]372                req->cbuf = s;
373        }
[5ebff60]374
[ca8037e]375        return CR_OK;
376}
377
[5ebff60]378static http_ret_t http_process_data(struct http_request *req, const char *buffer, int len)
[ca8037e]379{
[5ebff60]380        if (len <= 0) {
[ca8037e]381                return CR_OK;
[5ebff60]382        }
383
384        if (!req->reply_body) {
385                req->reply_headers = g_realloc(req->reply_headers, req->bytes_read + len + 1);
386                memcpy(req->reply_headers + req->bytes_read, buffer, len);
[ca8037e]387                req->bytes_read += len;
388                req->reply_headers[req->bytes_read] = '\0';
[5ebff60]389
390                if (strstr(req->reply_headers, "\r\n\r\n") ||
391                    strstr(req->reply_headers, "\n\n")) {
[ca8037e]392                        /* We've now received all headers. Look for something
393                           interesting. */
[5ebff60]394                        if (!http_handle_headers(req)) {
[ca8037e]395                                return CR_ABORT;
[5ebff60]396                        }
397
[ca8037e]398                        /* Start parsing the body as chunked if required. */
[5ebff60]399                        if (req->flags & HTTPC_CHUNKED) {
400                                return http_process_chunked_data(req, NULL, 0);
401                        }
[ca8037e]402                }
[5ebff60]403        } else {
[ca8037e]404                int pos = req->reply_body - req->sbuf;
[5ebff60]405                req->sbuf = g_realloc(req->sbuf, req->sblen + len + 1);
406                memcpy(req->sbuf + req->sblen, buffer, len);
[ca8037e]407                req->bytes_read += len;
408                req->sblen += len;
409                req->sbuf[req->sblen] = '\0';
410                req->reply_body = req->sbuf + pos;
411                req->body_size = req->sblen - pos;
412        }
[5ebff60]413
414        if ((req->flags & HTTPC_STREAMING) && req->reply_body) {
415                req->func(req);
416        }
417
[ca8037e]418        return CR_OK;
419}
420
[8bd866f]421/* Splits headers and body. Checks result code, in case of 300s it'll handle
422   redirects. If this returns FALSE, don't call any callbacks! */
[5ebff60]423static gboolean http_handle_headers(struct http_request *req)
[8bd866f]424{
[ca8037e]425        char *end1, *end2, *s;
[8bd866f]426        int evil_server = 0;
[5ebff60]427
[8a9afe4]428        /* Zero termination is very convenient. */
[8bd866f]429        req->reply_headers[req->bytes_read] = '\0';
[5ebff60]430
[8a9afe4]431        /* Find the separation between headers and body, and keep stupid
432           webservers in mind. */
[5ebff60]433        end1 = strstr(req->reply_headers, "\r\n\r\n");
434        end2 = strstr(req->reply_headers, "\n\n");
435
436        if (end2 && end2 < end1) {
[52b3a99]437                end1 = end2 + 1;
[8a9afe4]438                evil_server = 1;
[5ebff60]439        } else if (end1) {
[52b3a99]440                end1 += 2;
[5ebff60]441        } else {
442                req->status_string = g_strdup("Malformed HTTP reply");
[8bd866f]443                return TRUE;
[52b3a99]444        }
[5ebff60]445
[ca8037e]446        *end1 = '\0';
[5ebff60]447
448        if (getenv("BITLBEE_DEBUG")) {
449                printf("HTTP response headers:\n%s\n", req->reply_headers);
450        }
451
452        if (evil_server) {
[0eec386]453                req->reply_body = end1 + 1;
[5ebff60]454        } else {
[0eec386]455                req->reply_body = end1 + 2;
[5ebff60]456        }
457
[ca8037e]458        /* Separately allocated space for headers and body. */
459        req->sblen = req->body_size = req->reply_headers + req->bytes_read - req->reply_body;
[5ebff60]460        req->sbuf = req->reply_body = g_memdup(req->reply_body, req->body_size + 1);
461        req->reply_headers = g_realloc(req->reply_headers, end1 - req->reply_headers + 1);
462
463        if ((end1 = strchr(req->reply_headers, ' ')) != NULL) {
464                if (sscanf(end1 + 1, "%hd", &req->status_code) != 1) {
465                        req->status_string = g_strdup("Can't parse status code");
[52b3a99]466                        req->status_code = -1;
[5ebff60]467                } else {
[7deb447]468                        char *eol;
[5ebff60]469
470                        if (evil_server) {
471                                eol = strchr(end1, '\n');
472                        } else {
473                                eol = strchr(end1, '\r');
474                        }
475
476                        req->status_string = g_strndup(end1 + 1, eol - end1 - 1);
477
[7deb447]478                        /* Just to be sure... */
[5ebff60]479                        if ((eol = strchr(req->status_string, '\r'))) {
[7deb447]480                                *eol = 0;
[5ebff60]481                        }
482                        if ((eol = strchr(req->status_string, '\n'))) {
[7deb447]483                                *eol = 0;
[5ebff60]484                        }
[7deb447]485                }
[5ebff60]486        } else {
487                req->status_string = g_strdup("Can't locate status code");
[52b3a99]488                req->status_code = -1;
489        }
[5ebff60]490
491        if (((req->status_code >= 301 && req->status_code <= 303) ||
492             req->status_code == 307) && req->redir_ttl-- > 0) {
[52b3a99]493                char *loc, *new_request, *new_host;
494                int error = 0, new_port, new_proto;
[5ebff60]495
[7deb447]496                /* We might fill it again, so let's not leak any memory. */
[5ebff60]497                g_free(req->status_string);
[7deb447]498                req->status_string = NULL;
[5ebff60]499
500                loc = strstr(req->reply_headers, "\nLocation: ");
501                if (loc == NULL) { /* We can't handle this redirect... */
502                        req->status_string = g_strdup("Can't locate Location: header");
[8bd866f]503                        return TRUE;
[7deb447]504                }
[5ebff60]505
[52b3a99]506                loc += 11;
[5ebff60]507                while (*loc == ' ') {
508                        loc++;
509                }
510
[52b3a99]511                /* TODO/FIXME: Possibly have to handle relative redirections,
512                   and rewrite Host: headers. Not necessary for now, it's
513                   enough for passport authentication like this. */
[5ebff60]514
515                if (*loc == '/') {
[52b3a99]516                        /* Just a different pathname... */
[5ebff60]517
[52b3a99]518                        /* Since we don't cache the servername, and since we
519                           don't need this yet anyway, I won't implement it. */
[5ebff60]520
521                        req->status_string = g_strdup("Can't handle relative redirects");
522
[8bd866f]523                        return TRUE;
[5ebff60]524                } else {
[52b3a99]525                        /* A whole URL */
526                        url_t *url;
[dd7b931]527                        char *s, *version, *headers;
[03a8f8e]528                        const char *new_method;
[5ebff60]529
530                        s = strstr(loc, "\r\n");
531                        if (s == NULL) {
[8bd866f]532                                return TRUE;
[5ebff60]533                        }
534
535                        url = g_new0(url_t, 1);
[52b3a99]536                        *s = 0;
[5ebff60]537
538                        if (!url_set(url, loc)) {
539                                req->status_string = g_strdup("Malformed redirect URL");
540                                g_free(url);
[8bd866f]541                                return TRUE;
[52b3a99]542                        }
[5ebff60]543
[03a8f8e]544                        /* Find all headers and, if necessary, the POST request contents.
545                           Skip the old Host: header though. This crappy code here means
546                           anything using this http_client MUST put the Host: header at
547                           the top. */
[5ebff60]548                        if (!((s = strstr(req->request, "\r\nHost: ")) &&
549                              (s = strstr(s + strlen("\r\nHost: "), "\r\n")))) {
550                                req->status_string = g_strdup("Error while rebuilding request string");
551                                g_free(url);
[8bd866f]552                                return TRUE;
[52b3a99]553                        }
[dd7b931]554                        headers = s;
[5ebff60]555
[03a8f8e]556                        /* More or less HTTP/1.0 compliant, from my reading of RFC 2616.
557                           Always perform a GET request unless we received a 301. 303 was
558                           meant for this but it's HTTP/1.1-only and we're specifically
[3f808ca]559                           speaking HTTP/1.0. ...
[5ebff60]560
[3f808ca]561                           Well except someone at identi.ca's didn't bother reading any
562                           RFCs and just return HTTP/1.1-specific status codes to HTTP/1.0
563                           requests. Fuckers. So here we are, handle 301..303,307. */
[5ebff60]564                        if (strncmp(req->request, "GET", 3) == 0) {
[3f808ca]565                                /* GETs never become POSTs. */
566                                new_method = "GET";
[5ebff60]567                        } else if (req->status_code == 302 || req->status_code == 303) {
[3f808ca]568                                /* 302 de-facto becomes GET, 303 as specified by RFC 2616#10.3.3 */
569                                new_method = "GET";
[5ebff60]570                        } else {
[3f808ca]571                                /* 301 de-facto should stay POST, 307 specifally RFC 2616#10.3.8 */
572                                new_method = "POST";
[dd7b931]573                        }
[5ebff60]574
575                        if ((version = strstr(req->request, " HTTP/")) &&
576                            (s = strstr(version, "\r\n"))) {
577                                version++;
578                                version = g_strndup(version, s - version);
579                        } else {
580                                version = g_strdup("HTTP/1.0");
581                        }
582
[03a8f8e]583                        /* Okay, this isn't fun! We have to rebuild the request... :-( */
[5ebff60]584                        new_request = g_strdup_printf("%s %s %s\r\nHost: %s%s",
585                                                      new_method, url->file, version,
586                                                      url->host, headers);
587
588                        new_host = g_strdup(url->host);
[52b3a99]589                        new_port = url->port;
590                        new_proto = url->proto;
[5ebff60]591
[03a8f8e]592                        /* If we went from POST to GET, truncate the request content. */
[5ebff60]593                        if (new_request[0] != req->request[0] && new_request[0] == 'G' &&
594                            (s = strstr(new_request, "\r\n\r\n"))) {
[03a8f8e]595                                s[4] = '\0';
[5ebff60]596                        }
597
598                        g_free(url);
599                        g_free(version);
600                }
601
602                if (req->ssl) {
603                        ssl_disconnect(req->ssl);
604                } else {
605                        closesocket(req->fd);
[52b3a99]606                }
[5ebff60]607
[52b3a99]608                req->fd = -1;
[7deb447]609                req->ssl = NULL;
[5ebff60]610
611                if (getenv("BITLBEE_DEBUG")) {
612                        printf("New headers for redirected HTTP request:\n%s\n", new_request);
[52b3a99]613                }
[5ebff60]614
615                if (new_proto == PROTO_HTTPS) {
616                        req->ssl = ssl_connect(new_host, new_port, TRUE, http_ssl_connected, req);
617                        if (req->ssl == NULL) {
[52b3a99]618                                error = 1;
[5ebff60]619                        }
620                } else {
621                        req->fd = proxy_connect(new_host, new_port, http_connected, req);
622                        if (req->fd < 0) {
623                                error = 1;
624                        }
[52b3a99]625                }
[5ebff60]626                g_free(new_host);
627
628                if (error) {
629                        req->status_string = g_strdup("Connection problem during redirect");
630                        g_free(new_request);
[8bd866f]631                        return TRUE;
[52b3a99]632                }
[5ebff60]633
634                g_free(req->request);
635                g_free(req->reply_headers);
636                g_free(req->sbuf);
[52b3a99]637                req->request = new_request;
[5ebff60]638                req->request_length = strlen(new_request);
[52b3a99]639                req->bytes_read = req->bytes_written = req->inpa = 0;
640                req->reply_headers = req->reply_body = NULL;
[ab19567]641                req->sbuf = req->cbuf = NULL;
642                req->sblen = req->cblen = 0;
[5ebff60]643
[ba9edaa]644                return FALSE;
[8a9afe4]645        }
[ca8037e]646
[5ebff60]647        if ((s = get_rfc822_header(req->reply_headers, "Content-Length", 0)) &&
648            sscanf(s, "%d", &req->content_length) != 1) {
[ca8037e]649                req->content_length = -1;
[5ebff60]650        }
651        g_free(s);
652
653        if ((s = get_rfc822_header(req->reply_headers, "Transfer-Encoding", 0))) {
654                if (strcasestr(s, "chunked")) {
[ca8037e]655                        req->flags |= HTTPC_CHUNKED;
656                        req->cbuf = req->sbuf;
657                        req->cblen = req->sblen;
[5ebff60]658
659                        req->reply_body = req->sbuf = g_strdup("");
[ca8037e]660                        req->body_size = req->sblen = 0;
661                }
[5ebff60]662                g_free(s);
[ca8037e]663        }
[5ebff60]664
[8bd866f]665        return TRUE;
666}
[8a9afe4]667
[5ebff60]668void http_flush_bytes(struct http_request *req, size_t len)
[8bd866f]669{
[5ebff60]670        if (len <= 0 || len > req->body_size || !(req->flags & HTTPC_STREAMING)) {
[1388d30]671                return;
[5ebff60]672        }
673
[1388d30]674        req->reply_body += len;
675        req->body_size -= len;
[5ebff60]676
677        if (req->reply_body - req->sbuf >= 512) {
678                char *new = g_memdup(req->reply_body, req->body_size + 1);
679                g_free(req->sbuf);
[1388d30]680                req->reply_body = req->sbuf = new;
681                req->sblen = req->body_size;
[55ccc9a0]682        }
[fb98634]683}
684
[5ebff60]685void http_close(struct http_request *req)
[1388d30]686{
[5ebff60]687        if (!req) {
[1388d30]688                return;
[5ebff60]689        }
690
691        if (req->inpa > 0) {
692                b_event_remove(req->inpa);
693        }
694
695        if (req->ssl) {
696                ssl_disconnect(req->ssl);
697        } else {
698                closesocket(req->fd);
699        }
700
701        http_free(req);
[1388d30]702}
703
[5ebff60]704static void http_free(struct http_request *req)
[fb98634]705{
[5ebff60]706        g_free(req->request);
707        g_free(req->reply_headers);
708        g_free(req->status_string);
709        g_free(req->sbuf);
710        g_free(req->cbuf);
711        g_free(req);
[8a9afe4]712}
Note: See TracBrowser for help on using the repository browser.