Changeset cc6fdf8 for lib


Ignore:
Timestamp:
2012-12-22T00:14:26Z (11 years ago)
Author:
Wilmer van der Gaast <wilmer@…>
Branches:
master
Children:
7d5afa6
Parents:
92d3044 (diff), 573e274 (diff)
Note: this is a merge changeset, the changes displayed below correspond to the merge itself.
Use the (diff) links above to see all the changes relative to each parent.
Message:

Merging JSON branch. It's very stable by now, and I want more testers.

Location:
lib
Files:
4 added
5 edited

Legend:

Unmodified
Added
Removed
  • lib/Makefile

    r92d3044 rcc6fdf8  
    1313
    1414# [SH] Program variables
    15 objects = arc.o base64.o $(EVENT_HANDLER) ftutil.o http_client.o ini.o md5.o misc.o oauth.o oauth2.o proxy.o sha1.o $(SSL_CLIENT) url.o xmltree.o
     15objects = arc.o base64.o $(EVENT_HANDLER) ftutil.o http_client.o ini.o json.o json_util.o md5.o misc.o oauth.o oauth2.o proxy.o sha1.o $(SSL_CLIENT) url.o xmltree.o
    1616
    1717LFLAGS += -r
  • lib/http_client.c

    r92d3044 rcc6fdf8  
    193193}
    194194
     195static gboolean http_handle_headers( struct http_request *req );
     196
    195197static gboolean http_incoming_data( gpointer data, int source, b_input_condition cond )
    196198{
    197199        struct http_request *req = data;
    198         int evil_server = 0;
    199         char buffer[2048];
    200         char *end1, *end2, *s;
     200        char buffer[4096];
     201        char *s;
    201202        size_t content_length;
    202203        int st;
     
    218219                                   packets that abort connections! \o/ */
    219220                               
    220                                 goto got_reply;
     221                                goto eof;
    221222                        }
    222223                }
    223224                else if( st == 0 )
    224225                {
    225                         goto got_reply;
     226                        goto eof;
    226227                }
    227228        }
     
    239240                else if( st == 0 )
    240241                {
    241                         goto got_reply;
    242                 }
    243         }
    244        
    245         if( st > 0 )
     242                        goto eof;
     243                }
     244        }
     245       
     246        if( st > 0 && !req->sbuf )
    246247        {
    247248                req->reply_headers = g_realloc( req->reply_headers, req->bytes_read + st + 1 );
    248249                memcpy( req->reply_headers + req->bytes_read, buffer, st );
    249250                req->bytes_read += st;
    250         }
     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        }
     292       
     293        if( ssl_pending( req->ssl ) )
     294                return http_incoming_data( data, source, cond );
    251295       
    252296        /* There will be more! */
     
    255299                                 http_incoming_data, req );
    256300       
    257         if( ssl_pending( req->ssl ) )
    258                 return http_incoming_data( data, source, cond );
    259         else
    260                 return FALSE;
    261 
    262 got_reply:
     301        return FALSE;
     302
     303eof:
     304        req->flags |= HTTPC_EOF;
     305       
    263306        /* Maybe if the webserver is overloaded, or when there's bad SSL
    264307           support... */
     
    269312        }
    270313       
     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        }
     321
     322cleanup:
     323        if( req->ssl )
     324                ssl_disconnect( req->ssl );
     325        else
     326                closesocket( req->fd );
     327       
     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 );
     339       
     340        if( getenv( "BITLBEE_DEBUG" ) && req )
     341                printf( "Finishing HTTP request with status: %s\n",
     342                        req->status_string ? req->status_string : "NULL" );
     343       
     344        req->func( req );
     345        http_free( req );
     346        return FALSE;
     347}
     348
     349/* Splits headers and body. Checks result code, in case of 300s it'll handle
     350   redirects. If this returns FALSE, don't call any callbacks! */
     351static gboolean http_handle_headers( struct http_request *req )
     352{
     353        char *end1, *end2;
     354        int evil_server = 0;
     355       
    271356        /* Zero termination is very convenient. */
    272         req->reply_headers[req->bytes_read] = 0;
     357        req->reply_headers[req->bytes_read] = '\0';
    273358       
    274359        /* Find the separation between headers and body, and keep stupid
     
    289374        {
    290375                req->status_string = g_strdup( "Malformed HTTP reply" );
    291                 goto cleanup;
     376                return TRUE;
    292377        }
    293378       
     
    306391        if( ( end1 = strchr( req->reply_headers, ' ' ) ) != NULL )
    307392        {
    308                 if( sscanf( end1 + 1, "%d", &req->status_code ) != 1 )
     393                if( sscanf( end1 + 1, "%hd", &req->status_code ) != 1 )
    309394                {
    310395                        req->status_string = g_strdup( "Can't parse status code" );
     
    349434                {
    350435                        req->status_string = g_strdup( "Can't locate Location: header" );
    351                         goto cleanup;
     436                        return TRUE;
    352437                }
    353438               
     
    369454                        req->status_string = g_strdup( "Can't handle recursive redirects" );
    370455                       
    371                         goto cleanup;
     456                        return TRUE;
    372457                }
    373458                else
     
    380465                        s = strstr( loc, "\r\n" );
    381466                        if( s == NULL )
    382                                 goto cleanup;
     467                                return TRUE;
    383468                       
    384469                        url = g_new0( url_t, 1 );
     
    389474                                req->status_string = g_strdup( "Malformed redirect URL" );
    390475                                g_free( url );
    391                                 goto cleanup;
     476                                return TRUE;
    392477                        }
    393478                       
     
    401486                                req->status_string = g_strdup( "Error while rebuilding request string" );
    402487                                g_free( url );
    403                                 goto cleanup;
     488                                return TRUE;
    404489                        }
    405490                       
     
    467552                        req->status_string = g_strdup( "Connection problem during redirect" );
    468553                        g_free( new_request );
    469                         goto cleanup;
     554                        return TRUE;
    470555                }
    471556               
     
    480565        }
    481566       
    482         /* Assume that a closed connection means we're finished, this indeed
    483            breaks with keep-alive connections and faulty connections. */
    484         /* req->finished = 1; */
    485 
    486 cleanup:
     567        return TRUE;
     568}
     569
     570void http_flush_bytes( struct http_request *req, size_t len )
     571{
     572        if( len <= 0 || len > req->body_size || !( req->flags & HTTPC_STREAMING ) )
     573                return;
     574       
     575        req->reply_body += len;
     576        req->body_size -= len;
     577       
     578        if( req->reply_body - req->sbuf >= 512 )
     579        {
     580                printf( "Wasting %ld bytes, cleaning up stream buffer\n", req->reply_body - req->sbuf );
     581                char *new = g_memdup( req->reply_body, req->body_size + 1 );
     582                g_free( req->sbuf );
     583                req->reply_body = req->sbuf = new;
     584                req->sblen = req->body_size;
     585        }
     586}
     587
     588void http_close( struct http_request *req )
     589{
     590        if( !req )
     591                return;
     592       
     593        if( req->inpa > 0 )
     594                b_event_remove( req->inpa );
     595       
    487596        if( req->ssl )
    488597                ssl_disconnect( req->ssl );
     
    490599                closesocket( req->fd );
    491600       
    492         if( ( s = get_rfc822_header( req->reply_headers, "Content-Length", 0 ) ) &&
    493             sscanf( s, "%zd", &content_length ) == 1 )
    494         {
    495                 if( content_length < req->body_size )
    496                 {
    497                         req->status_code = -1;
    498                         g_free( req->status_string );
    499                         req->status_string = g_strdup( "Response truncated" );
    500                 }
    501         }
    502         g_free( s );
    503        
    504         if( getenv( "BITLBEE_DEBUG" ) && req )
    505                 printf( "Finishing HTTP request with status: %s\n",
    506                         req->status_string ? req->status_string : "NULL" );
    507        
    508         req->func( req );
    509601        http_free( req );
    510         return FALSE;
    511602}
    512603
     
    516607        g_free( req->reply_headers );
    517608        g_free( req->status_string );
     609        g_free( req->sbuf );
    518610        g_free( req );
    519611}
    520 
  • lib/http_client.h

    r92d3044 rcc6fdf8  
    2626/* http_client allows you to talk (asynchronously, again) to HTTP servers.
    2727   In the "background" it will send the whole query and wait for a complete
    28    response to come back. Right now it's only used by the MSN Passport
    29    authentication code, but it might be useful for other things too (for
    30    example the AIM usericon patch uses this so icons can be stored on
    31    webservers instead of the local filesystem).
     28   response to come back. Initially written for MS Passport authentication,
     29   but used for many other things now like OAuth and Twitter.
    3230   
    33    Didn't test this too much, but it seems to work well. Just don't look
    34    at the code that handles HTTP 30x redirects. ;-) The function is
    35    probably not very useful for downloading lots of data since it keeps
    36    everything in a memory buffer until the download is completed (and
    37    can't pass any data or whatever before then). It's very useful for
    38    doing quick requests without blocking the whole program, though. */
     31   It's very useful for doing quick requests without blocking the whole
     32   program. Unfortunately it doesn't support fancy stuff like HTTP keep-
     33   alives. */
    3934
    4035#include <glib.h>
     
    4237
    4338struct http_request;
     39
     40typedef enum http_client_flags
     41{
     42        HTTPC_STREAMING = 1,
     43        HTTPC_EOF = 2,
     44       
     45        /* Let's reserve 0x1000000+ for lib users. */
     46} http_client_flags_t;
    4447
    4548/* Your callback function should look like this: */
     
    5356        char *request;          /* The request to send to the server. */
    5457        int request_length;     /* Its size. */
    55         int status_code;        /* The numeric HTTP status code. (Or -1
     58        short status_code;      /* The numeric HTTP status code. (Or -1
    5659                                   if something really went wrong) */
    5760        char *status_string;    /* The error text. */
     
    5962        char *reply_body;
    6063        int body_size;          /* The number of bytes in reply_body. */
    61         /* int finished;           Set to non-0 if the request was completed
    62                                    successfully. */
    63         int redir_ttl;          /* You can set it to 0 if you don't want
     64        short redir_ttl;        /* You can set it to 0 if you don't want
    6465                                   http_client to follow them. */
     66       
     67        http_client_flags_t flags;
    6568       
    6669        http_input_function func;
     
    6871       
    6972        /* Please don't touch the things down here, you shouldn't need them. */
    70        
    7173        void *ssl;
    7274        int fd;
     
    7577        int bytes_written;
    7678        int bytes_read;
     79       
     80        /* Used in streaming mode. Caller should read from reply_body. */
     81        char *sbuf;
     82        size_t sblen;
    7783};
    7884
     
    8389struct http_request *http_dorequest( char *host, int port, int ssl, char *request, http_input_function func, gpointer data );
    8490struct http_request *http_dorequest_url( char *url_string, http_input_function func, gpointer data );
     91
     92/* For streaming connections only; flushes len bytes at the start of the buffer. */
     93void http_flush_bytes( struct http_request *req, size_t len );
     94void http_close( struct http_request *req );
  • lib/oauth2.c

    r92d3044 rcc6fdf8  
    44*  Simple OAuth client (consumer) implementation.                           *
    55*                                                                           *
    6 *  Copyright 2010-2011 Wilmer van der Gaast <wilmer@gaast.net>              *
     6*  Copyright 2010-2012 Wilmer van der Gaast <wilmer@gaast.net>              *
    77*                                                                           *
    88*  This program is free software; you can redistribute it and/or modify     *
     
    2626#include "oauth2.h"
    2727#include "oauth.h"
     28#include "json.h"
    2829#include "url.h"
    2930
     
    4445};
    4546
    46 static char *oauth2_json_dumb_get( const char *json, const char *key );
    4747static void oauth2_access_token_done( struct http_request *req );
    4848
     
    115115        else if( content_type && strstr( content_type, "application/json" ) )
    116116        {
    117                 atoken = oauth2_json_dumb_get( req->reply_body, "access_token" );
    118                 rtoken = oauth2_json_dumb_get( req->reply_body, "refresh_token" );
     117                json_value *js = json_parse( req->reply_body );
     118                if( js && js->type == json_object )
     119                {
     120                        int i;
     121                       
     122                        for( i = 0; i < js->u.object.length; i ++ )
     123                        {
     124                                if( js->u.object.values[i].value->type != json_string )
     125                                        continue;
     126                                if( strcmp( js->u.object.values[i].name, "access_token" ) == 0 )
     127                                        atoken = g_strdup( js->u.object.values[i].value->u.string.ptr );
     128                                if( strcmp( js->u.object.values[i].name, "refresh_token" ) == 0 )
     129                                        rtoken = g_strdup( js->u.object.values[i].value->u.string.ptr );
     130                        }
     131                }
     132                json_value_free( js );
    119133        }
    120134        else
     
    137151        g_free( cb_data );
    138152}
    139 
    140 /* Super dumb. I absolutely refuse to use/add a complete json parser library
    141    (adding a new dependency to BitlBee for the first time in.. 6 years?) just
    142    to parse 100 bytes of data. So I have to do my own parsing because OAuth2
    143    dropped support for XML. (GRRR!) This is very dumb and for example won't
    144    work for integer values, nor will it strip/handle backslashes. */
    145 static char *oauth2_json_dumb_get( const char *json, const char *key )
    146 {
    147         int is_key = 0; /* 1 == reading key, 0 == reading value */
    148         int found_key = 0;
    149                
    150         while( json && *json )
    151         {
    152                 /* Grab strings and see if they're what we're looking for. */
    153                 if( *json == '"' || *json == '\'' )
    154                 {
    155                         char q = *json;
    156                         const char *str_start;
    157                         json ++;
    158                         str_start = json;
    159                        
    160                         while( *json )
    161                         {
    162                                 /* \' and \" are not string terminators. */
    163                                 if( *json == '\\' && json[1] == q )
    164                                         json ++;
    165                                 /* But without a \ it is. */
    166                                 else if( *json == q )
    167                                         break;
    168                                 json ++;
    169                         }
    170                         if( *json == '\0' )
    171                                 return NULL;
    172                        
    173                         if( is_key && strncmp( str_start, key, strlen( key ) ) == 0 )
    174                         {
    175                                 found_key = 1;
    176                         }
    177                         else if( !is_key && found_key )
    178                         {
    179                                 char *ret = g_memdup( str_start, json - str_start + 1 );
    180                                 ret[json-str_start] = '\0';
    181                                 return ret;
    182                         }
    183                        
    184                 }
    185                 else if( *json == '{' || *json == ',' )
    186                 {
    187                         found_key = 0;
    188                         is_key = 1;
    189                 }
    190                 else if( *json == ':' )
    191                         is_key = 0;
    192                
    193                 json ++;
    194         }
    195        
    196         return NULL;
    197 }
  • lib/ssl_gnutls.c

    r92d3044 rcc6fdf8  
    3838
    3939static gboolean initialized = FALSE;
    40 gnutls_certificate_credentials xcred;
     40gnutls_certificate_credentials_t xcred;
    4141
    4242#include <limits.h>
     
    6060        gboolean verify;
    6161       
    62         gnutls_session session;
     62        gnutls_session_t session;
    6363};
    6464
     
    134134        conn->data = data;
    135135        conn->inpa = -1;
    136         conn->hostname = hostname;
     136        conn->hostname = g_strdup( hostname );
    137137       
    138138        /* For now, SSL verification is globally enabled by setting the cafile
     
    171171        int verifyret = 0;
    172172        gnutls_x509_crt_t cert;
    173         const char *hostname;
    174        
    175         hostname = gnutls_session_get_ptr( session );
     173        struct scd *conn;
     174       
     175        conn = gnutls_session_get_ptr( session );
    176176
    177177        gnutlsret = gnutls_certificate_verify_peers2( session, &status );
     
    211211                return VERIFY_CERT_ERROR;
    212212
    213         if( !gnutls_x509_crt_check_hostname( cert, hostname ) )
     213        if( !gnutls_x509_crt_check_hostname( cert, conn->hostname ) )
    214214        {
    215215                verifyret |= VERIFY_CERT_INVALID;
     
    267267       
    268268        gnutls_init( &conn->session, GNUTLS_CLIENT );
    269         if( conn->verify )
    270                 gnutls_session_set_ptr( conn->session, (void *) conn->hostname );
     269        gnutls_session_set_ptr( conn->session, (void *) conn );
    271270#if GNUTLS_VERSION_NUMBER < 0x020c00
    272271        gnutls_transport_set_lowat( conn->session, 0 );
     
    276275       
    277276        sock_make_nonblocking( conn->fd );
    278         gnutls_transport_set_ptr( conn->session, (gnutls_transport_ptr) GNUTLS_STUPID_CAST conn->fd );
     277        gnutls_transport_set_ptr( conn->session, (gnutls_transport_ptr_t) GNUTLS_STUPID_CAST conn->fd );
    279278       
    280279        return ssl_handshake( data, source, cond );
     
    402401        if( conn->session )
    403402                gnutls_deinit( conn->session );
     403        g_free( conn->hostname );
    404404        g_free( conn );
    405405}
Note: See TracChangeset for help on using the changeset viewer.