Changes in / [ab19567:41a94dd]


Ignore:
Files:
7 edited

Legend:

Unmodified
Added
Removed
  • lib/http_client.c

    rab19567 r41a94dd  
    22  * BitlBee -- An IRC to other IM-networks gateway                     *
    33  *                                                                    *
    4   * Copyright 2002-2013 Wilmer van der Gaast and others                *
     4  * Copyright 2002-2012 Wilmer van der Gaast and others                *
    55  \********************************************************************/
    66
     
    6969        req->request_length = strlen( request );
    7070        req->redir_ttl = 3;
    71         req->content_length = -1;
    7271       
    7372        if( getenv( "BITLBEE_DEBUG" ) )
     
    9796        request = g_strdup_printf( "GET %s HTTP/1.0\r\n"
    9897                                   "Host: %s\r\n"
     98                                   "Connection: close\r\n"
    9999                                   "User-Agent: BitlBee " BITLBEE_VERSION " " ARCH "/" CPU "\r\n"
    100100                                   "\r\n", url->file, url->host );
     
    193193}
    194194
    195 typedef enum {
    196         CR_OK,
    197         CR_EOF,
    198         CR_ERROR,
    199         CR_ABORT,
    200 } http_ret_t;
    201 
    202195static gboolean http_handle_headers( struct http_request *req );
    203 static http_ret_t http_process_chunked_data( struct http_request *req, const char *buffer, int len );
    204 static http_ret_t http_process_data( struct http_request *req, const char *buffer, int len );
    205196
    206197static gboolean http_incoming_data( gpointer data, int source, b_input_condition cond )
     
    208199        struct http_request *req = data;
    209200        char buffer[4096];
     201        char *s;
     202        size_t content_length;
    210203        int st;
    211204       
     
    251244        }
    252245       
    253         if( st > 0 )
    254         {
    255                 http_ret_t c;
    256                
    257                 if( req->flags & HTTPC_CHUNKED )
    258                         c = http_process_chunked_data( req, buffer, st );
    259                 else
    260                         c = http_process_data( req, buffer, st );
    261                
    262                 if( c == CR_EOF )
    263                         goto eof;
    264                 else if( c == CR_ERROR || c == CR_ABORT )
    265                         return FALSE;
    266         }
    267        
    268         if( req->content_length != -1 &&
    269             req->body_size >= req->content_length )
    270                 goto eof;
     246        if( st > 0 && !req->sbuf )
     247        {
     248                req->reply_headers = g_realloc( req->reply_headers, req->bytes_read + st + 1 );
     249                memcpy( req->reply_headers + req->bytes_read, buffer, st );
     250                req->bytes_read += st;
     251               
     252                st = 0;
     253        }
     254       
     255        if( st >= 0 && ( req->flags & HTTPC_STREAMING ) )
     256        {
     257                if( !req->reply_body &&
     258                    ( strstr( req->reply_headers, "\r\n\r\n" ) ||
     259                      strstr( req->reply_headers, "\n\n" ) ) )
     260                {
     261                        size_t hlen;
     262                       
     263                        /* We've now received all headers, so process them once
     264                           before we start feeding back data. */
     265                        if( !http_handle_headers( req ) )
     266                                return FALSE;
     267                       
     268                        hlen = req->reply_body - req->reply_headers;
     269                       
     270                        req->sblen = req->bytes_read - hlen;
     271                        req->sbuf = g_memdup( req->reply_body, req->sblen + 1 );
     272                        req->reply_headers = g_realloc( req->reply_headers, hlen + 1 );
     273                       
     274                        req->reply_body = req->sbuf;
     275                }
     276               
     277                if( st > 0 )
     278                {
     279                        int pos = req->reply_body - req->sbuf;
     280                        req->sbuf = g_realloc( req->sbuf, req->sblen + st + 1 );
     281                        memcpy( req->sbuf + req->sblen, buffer, st );
     282                        req->bytes_read += st;
     283                        req->sblen += st;
     284                        req->sbuf[req->sblen] = '\0';
     285                        req->reply_body = req->sbuf + pos;
     286                        req->body_size = req->sblen - pos;
     287                }
     288               
     289                if( req->reply_body )
     290                        req->func( req );
     291        }
    271292       
    272293        if( ssl_pending( req->ssl ) )
     
    290311                goto cleanup;
    291312        }
     313       
     314        if( !( req->flags & HTTPC_STREAMING ) )
     315        {
     316                /* Returns FALSE if we were redirected, in which case we should abort
     317                   and not run any callback yet. */
     318                if( !http_handle_headers( req ) )
     319                        return FALSE;
     320        }
    292321
    293322cleanup:
     
    297326                closesocket( req->fd );
    298327       
    299         if( req->body_size < req->content_length )
    300         {
    301                 req->status_code = -1;
    302                 g_free( req->status_string );
    303                 req->status_string = g_strdup( "Response truncated" );
    304         }
     328        if( ( s = get_rfc822_header( req->reply_headers, "Content-Length", 0 ) ) &&
     329            sscanf( s, "%zd", &content_length ) == 1 )
     330        {
     331                if( content_length < req->body_size )
     332                {
     333                        req->status_code = -1;
     334                        g_free( req->status_string );
     335                        req->status_string = g_strdup( "Response truncated" );
     336                }
     337        }
     338        g_free( s );
    305339       
    306340        if( getenv( "BITLBEE_DEBUG" ) && req )
     
    313347}
    314348
    315 static http_ret_t http_process_chunked_data( struct http_request *req, const char *buffer, int len )
    316 {
    317         char *chunk, *eos, *s;
    318        
    319         if( len < 0 )
    320                 return TRUE;
    321        
    322         if( len > 0 )
    323         {
    324                 req->cbuf = g_realloc( req->cbuf, req->cblen + len + 1 );
    325                 memcpy( req->cbuf + req->cblen, buffer, len );
    326                 req->cblen += len;
    327                 req->cbuf[req->cblen] = '\0';
    328         }
    329        
    330         /* Turns out writing a proper chunked-encoding state machine is not
    331            that simple. :-( I've tested this one feeding it byte by byte so
    332            I hope it's solid now. */
    333         chunk = req->cbuf;
    334         eos = req->cbuf + req->cblen;
    335         while( TRUE )
    336         {
    337                 int clen = 0;
    338                
    339                 /* Might be a \r\n from the last chunk. */
    340                 s = chunk;
    341                 while( isspace( *s ) )
    342                         s ++;
    343                 /* Chunk length. Might be incomplete. */
    344                 if( s < eos && sscanf( s, "%x", &clen ) != 1 )
    345                         return CR_ERROR;
    346                 while( isxdigit( *s ) )
    347                         s ++;
    348                
    349                 /* If we read anything here, it *must* be \r\n. */
    350                 if( strncmp( s, "\r\n", MIN( 2, eos - s ) ) != 0 )
    351                         return CR_ERROR;
    352                 s += 2;
    353                
    354                 if( s >= eos )
    355                         break;
    356                
    357                 /* 0-length chunk means end of response. */     
    358                 if( clen == 0 )
    359                         return CR_EOF;
    360                
    361                 /* Wait for the whole chunk to arrive. */
    362                 if( s + clen > eos )
    363                         break;
    364                 if( http_process_data( req, s, clen ) != CR_OK )
    365                         return CR_ABORT;
    366                
    367                 chunk = s + clen;
    368         }
    369        
    370         if( chunk != req->cbuf )
    371         {
    372                 req->cblen = eos - chunk;
    373                 s = g_memdup( chunk, req->cblen + 1 );
    374                 g_free( req->cbuf );
    375                 req->cbuf = s;
    376         }
    377        
    378         return CR_OK;
    379 }
    380 
    381 static http_ret_t http_process_data( struct http_request *req, const char *buffer, int len )
    382 {
    383         if( len <= 0 )
    384                 return CR_OK;
    385        
    386         if( !req->reply_body )
    387         {
    388                 req->reply_headers = g_realloc( req->reply_headers, req->bytes_read + len + 1 );
    389                 memcpy( req->reply_headers + req->bytes_read, buffer, len );
    390                 req->bytes_read += len;
    391                 req->reply_headers[req->bytes_read] = '\0';
    392                
    393                 if( strstr( req->reply_headers, "\r\n\r\n" ) ||
    394                     strstr( req->reply_headers, "\n\n" ) )
    395                 {
    396                         /* We've now received all headers. Look for something
    397                            interesting. */
    398                         if( !http_handle_headers( req ) )
    399                                 return CR_ABORT;
    400                        
    401                         /* Start parsing the body as chunked if required. */
    402                         if( req->flags & HTTPC_CHUNKED )
    403                                 return http_process_chunked_data( req, NULL, 0 );
    404                 }
    405         }
    406         else
    407         {
    408                 int pos = req->reply_body - req->sbuf;
    409                 req->sbuf = g_realloc( req->sbuf, req->sblen + len + 1 );
    410                 memcpy( req->sbuf + req->sblen, buffer, len );
    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         }
    417        
    418         if( ( req->flags & HTTPC_STREAMING ) && req->reply_body )
    419                 req->func( req );
    420        
    421         return CR_OK;
    422 }
    423 
    424349/* Splits headers and body. Checks result code, in case of 300s it'll handle
    425350   redirects. If this returns FALSE, don't call any callbacks! */
    426351static gboolean http_handle_headers( struct http_request *req )
    427352{
    428         char *end1, *end2, *s;
     353        char *end1, *end2;
    429354        int evil_server = 0;
    430355       
     
    452377        }
    453378       
    454         *end1 = '\0';
     379        *end1 = 0;
    455380       
    456381        if( getenv( "BITLBEE_DEBUG" ) )
     
    462387                req->reply_body = end1 + 2;
    463388       
    464         /* Separately allocated space for headers and body. */
    465         req->sblen = req->body_size = req->reply_headers + req->bytes_read - req->reply_body;
    466         req->sbuf = req->reply_body = g_memdup( req->reply_body, req->body_size + 1 );
    467         req->reply_headers = g_realloc( req->reply_headers, end1 - req->reply_headers + 1 );
     389        req->body_size = req->reply_headers + req->bytes_read - req->reply_body;
    468390       
    469391        if( ( end1 = strchr( req->reply_headers, ' ' ) ) != NULL )
     
    530452                           don't need this yet anyway, I won't implement it. */
    531453                       
    532                         req->status_string = g_strdup( "Can't handle relative redirects" );
     454                        req->status_string = g_strdup( "Can't handle recursive redirects" );
    533455                       
    534456                        return TRUE;
     
    538460                        /* A whole URL */
    539461                        url_t *url;
    540                         char *s, *version, *headers;
     462                        char *s;
    541463                        const char *new_method;
    542464                       
     
    566488                                return TRUE;
    567489                        }
    568                         headers = s;
    569490                       
    570491                        /* More or less HTTP/1.0 compliant, from my reading of RFC 2616.
     
    586507                                new_method = "POST";
    587508                       
    588                         if( ( version = strstr( req->request, " HTTP/" ) ) &&
    589                             ( s = strstr( version, "\r\n" ) ) )
    590                         {
    591                                 version ++;
    592                                 version = g_strndup( version, s - version );
    593                         }
    594                         else
    595                                 version = g_strdup( "HTTP/1.0" );
    596                        
    597509                        /* Okay, this isn't fun! We have to rebuild the request... :-( */
    598                         new_request = g_strdup_printf( "%s %s %s\r\nHost: %s%s",
    599                                                        new_method, url->file, version,
    600                                                        url->host, headers );
     510                        new_request = g_strdup_printf( "%s %s HTTP/1.0\r\nHost: %s%s",
     511                                                       new_method, url->file, url->host, s );
    601512                       
    602513                        new_host = g_strdup( url->host );
     
    610521                       
    611522                        g_free( url );
    612                         g_free( version );
    613523                }
    614524               
     
    647557                g_free( req->request );
    648558                g_free( req->reply_headers );
    649                 g_free( req->sbuf );
    650559                req->request = new_request;
    651560                req->request_length = strlen( new_request );
    652561                req->bytes_read = req->bytes_written = req->inpa = 0;
    653562                req->reply_headers = req->reply_body = NULL;
    654                 req->sbuf = req->cbuf = NULL;
    655                 req->sblen = req->cblen = 0;
    656563               
    657564                return FALSE;
    658         }
    659 
    660         if( ( s = get_rfc822_header( req->reply_headers, "Content-Length", 0 ) ) &&
    661             sscanf( s, "%d", &req->content_length ) != 1 )
    662                 req->content_length = -1;
    663         g_free( s );
    664        
    665         if( ( s = get_rfc822_header( req->reply_headers, "Transfer-Encoding", 0 ) ) )
    666         {
    667                 if( strcasestr( s, "chunked" ) )
    668                 {
    669                         req->flags |= HTTPC_CHUNKED;
    670                         req->cbuf = req->sbuf;
    671                         req->cblen = req->sblen;
    672                        
    673                         req->reply_body = req->sbuf = g_strdup( "" );
    674                         req->body_size = req->sblen = 0;
    675                 }
    676                 g_free( s );
    677565        }
    678566       
     
    719607        g_free( req->status_string );
    720608        g_free( req->sbuf );
    721         g_free( req->cbuf );
    722609        g_free( req );
    723610}
  • lib/http_client.h

    rab19567 r41a94dd  
    4242        HTTPC_STREAMING = 1,
    4343        HTTPC_EOF = 2,
    44         HTTPC_CHUNKED = 4,
    4544       
    4645        /* Let's reserve 0x1000000+ for lib users. */
     
    7877        int bytes_written;
    7978        int bytes_read;
    80         int content_length;     /* "Content-Length:" header or -1 */
    8179       
    8280        /* Used in streaming mode. Caller should read from reply_body. */
    8381        char *sbuf;
    8482        size_t sblen;
    85        
    86         /* Chunked encoding only. Raw chunked stream is decoded from here. */
    87         char *cbuf;
    88         size_t cblen;
    8983};
    9084
  • lib/oauth.c

    rab19567 r41a94dd  
    262262                             "Content-Type: application/x-www-form-urlencoded\r\n"
    263263                             "Content-Length: %zd\r\n"
     264                             "Connection: close\r\n"
    264265                             "\r\n"
    265266                             "%s", url_p.file, url_p.host, strlen( post ), post );
  • lib/oauth2.c

    rab19567 r41a94dd  
    9696                             "Content-Type: application/x-www-form-urlencoded\r\n"
    9797                             "Content-Length: %zd\r\n"
     98                             "Connection: close\r\n"
    9899                             "\r\n"
    99100                             "%s", url_p.file, url_p.host, strlen( args_s ), args_s );
  • lib/proxy.c

    rab19567 r41a94dd  
    158158                }
    159159
    160                 event_debug("proxy_connect_none( \"%s\", %d ) = %d\n", host, port_, fd);
     160                event_debug("proxy_connect_none( \"%s\", %d ) = %d\n", host, port, fd);
    161161       
    162162                if (connect(fd, phb->gai_cur->ai_addr, phb->gai_cur->ai_addrlen) < 0 && !sockerr_again()) {
  • protocols/twitter/twitter.c

    rab19567 r41a94dd  
    289289        char *def_url;
    290290        char *def_tul;
    291         char *def_mentions;
    292291
    293292        if (strcmp(acc->prpl->name, "twitter") == 0) {
    294293                def_url = TWITTER_API_URL;
    295294                def_tul = "20";
    296                 def_mentions = "true";
    297295        } else {                /* if( strcmp( acc->prpl->name, "identica" ) == 0 ) */
    298296                def_url = IDENTICA_API_URL;
    299297                def_tul = "0";
    300                 def_mentions = "false";
    301298        }
    302299
     
    311308        s->flags |= ACC_SET_OFFLINE_ONLY;
    312309
    313         s = set_add(&acc->set, "fetch_mentions", def_mentions, set_eval_bool, acc);
     310        s = set_add(&acc->set, "fetch_mentions", "true", set_eval_bool, acc);
    314311
    315312        s = set_add(&acc->set, "message_length", "140", set_eval_int, acc);
  • protocols/twitter/twitter_http.c

    rab19567 r41a94dd  
    7878       
    7979        // Make the request.
    80         g_string_printf(request, "%s %s%s%s%s HTTP/1.1\r\n"
     80        g_string_printf(request, "%s %s%s%s%s HTTP/1.0\r\n"
    8181                        "Host: %s\r\n"
    8282                        "User-Agent: BitlBee " BITLBEE_VERSION " " ARCH "/" CPU "\r\n",
Note: See TracChangeset for help on using the changeset viewer.