source: lib/http_client.c @ d63f37c

Last change on this file since d63f37c was 0db6618, checked in by dequis <dx@…>, at 2015-10-26T08:28:10Z

Use proxy_disconnect() in http, ssl, jabber, oscar

Twitter and MSN are all HTTP/SSL, so they don't need it either.

The out of tree facebook and steam plugins are also covered by the
HTTP/SSL changes.

Yahoo is written in a weird way and doesn't seem to need it (it seems it
doesn't immediately stop connections when you tell it to logout)

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