src/network/newgrf_download.cpp
changeset 11178 aa617a8b4f34
parent 11177 6d9a43c48924
child 11179 fa96e29d7187
equal deleted inserted replaced
11177:6d9a43c48924 11178:aa617a8b4f34
     9 #include "../window_gui.h"
     9 #include "../window_gui.h"
    10 #include "../textbuf_gui.h"
    10 #include "../textbuf_gui.h"
    11 #include "../variables.h"
    11 #include "../variables.h"
    12 #include "../newgrf.h"
    12 #include "../newgrf.h"
    13 #include "newgrf_download.h"
    13 #include "newgrf_download.h"
       
    14 #include "core/config.h"
    14 #include "../functions.h"
    15 #include "../functions.h"
       
    16 #include "../debug.h"
    15 #include "../window_func.h"
    17 #include "../window_func.h"
    16 #include "../core/alloc_func.hpp"
    18 #include "../core/alloc_func.hpp"
    17 #include "../string_func.h"
    19 #include "../string_func.h"
    18 #include "../gfx_func.h"
    20 #include "../gfx_func.h"
    19 #include "../querystring_gui.h"
    21 #include "../querystring_gui.h"
    20 #include "../sortlist_type.h"
    22 #include "../sortlist_type.h"
       
    23 #include "../rev.h"
       
    24 
       
    25 // XXX: ...
       
    26 #include <curl/curl.h>
    21 
    27 
    22 #include "table/strings.h"
    28 #include "table/strings.h"
    23 #include "../table/sprites.h"
    29 #include "../table/sprites.h"
       
    30 
       
    31 static int _has_curl_init = 0;
       
    32 
       
    33 static CURLcode do_init_curl () {
       
    34     CURLcode err;
       
    35 
       
    36     if (!_has_curl_init) {
       
    37         // XXX: be smarter about what to initialize
       
    38         if ((err = curl_global_init(CURL_GLOBAL_ALL)))
       
    39             return err;
       
    40 
       
    41         _has_curl_init = 1;
       
    42     }
       
    43 
       
    44     return CURLE_OK;
       
    45 }
       
    46 
       
    47 struct check_write_ctx {
       
    48     char *buf;
       
    49     size_t len;
       
    50     size_t offset;
       
    51 };
       
    52 
       
    53 #define QUERY_ITEM_SIZE (                   \
       
    54             4   /* the "grf=" prefix */     \
       
    55           + 8   /* the GRFID, in hex */     \
       
    56           + 1   /* the ":" */               \
       
    57           + 32  /* the md5sum, in hex */    \
       
    58           + 1   /* the "&" or '\0' */       \
       
    59         )
       
    60 
       
    61 static size_t check_available_write_cb (void *ptr, size_t size, size_t nmemb, void *arg) {
       
    62     struct check_write_ctx *ctx = (struct check_write_ctx *) arg;
       
    63     
       
    64     // total number of bytes
       
    65     size_t len = size * nmemb;
       
    66     
       
    67     // realloc if needed
       
    68     if (ctx->offset + len + 1 > ctx->len) {
       
    69         ctx->len *= 2;
       
    70         ctx->buf = ReallocT<char>(ctx->buf, ctx->len);
       
    71     }
       
    72     
       
    73     // thankfully we only have a limited amount of data..
       
    74     memcpy(ctx->buf + ctx->offset, ptr, len);
       
    75     ctx->offset += len;
       
    76     *(ctx->buf + ctx->offset) = '\0';
       
    77 
       
    78     return len;
       
    79 }
       
    80 
       
    81 /** Ask the master database about the availablity of the given NewGRFs, and update
       
    82  * the GRFConfigs that are avilable to GCS_AVAILABLE
       
    83  * @param config pointer to a linked-list of grfconfig's needed */
       
    84 static void CheckAvailableNewGRFs(GRFConfig *list)
       
    85 {
       
    86     GRFConfig *c;
       
    87     int i;
       
    88     char md5buf[64];
       
    89     struct check_write_ctx write_ctx;
       
    90     CURL *curl;
       
    91     char api_url[512], useragent[512];
       
    92     char curl_errbuf[CURL_ERROR_SIZE];
       
    93     long http_code;
       
    94     char *buf_ptr;
       
    95     CURLcode err;
       
    96 
       
    97     // initialize to a safe state for error handling
       
    98     curl = write_ctx.buf = NULL;
       
    99     
       
   100     // count how many NewGRFs in our list
       
   101     for (c = list, i = 0; c != NULL; c = c->next, i++) {}
       
   102     
       
   103     // how many bytes these take up...
       
   104     int query_size = i * QUERY_ITEM_SIZE;
       
   105     
       
   106     // allocate the buffer for the query data
       
   107     char *query_buf = MallocT<char>(query_size), *query_ptr = query_buf;
       
   108     
       
   109     // build the query
       
   110     for (c = list; c != NULL; c = c->next) {
       
   111         
       
   112         // the "&", except not for the first entry
       
   113         if (c != list)
       
   114             *(query_ptr++) = '&';
       
   115         
       
   116         // format the md5sum
       
   117         md5sumToString(md5buf, lastof(md5buf), c->md5sum);
       
   118 
       
   119         // snprintf the entry into the query buffer
       
   120         assert(snprintf(query_ptr, QUERY_ITEM_SIZE, "grf=%08X:%32s", BSWAP32(c->grfid), md5buf) == QUERY_ITEM_SIZE -1);
       
   121         
       
   122         // advance the query_ptr to point at the nul byte
       
   123         query_ptr += QUERY_ITEM_SIZE - 1;
       
   124     }
       
   125 
       
   126     // buil the target URL by formatting NETWORK_NEWGRF_MASTER_API with the method as the only parameter
       
   127     assert(snprintf(api_url, sizeof(api_url), NETWORK_NEWGRF_MASTER_API, "search") < 512);
       
   128     
       
   129     // our useragent
       
   130     assert(snprintf(useragent, sizeof(useragent), NETWORK_HTTP_USER_AGENT, _openttd_revision) < 512);
       
   131 
       
   132     // receiving is so damn hard...
       
   133     write_ctx.buf = MallocT<char>(512);
       
   134     write_ctx.len = 512;
       
   135     write_ctx.offset = 0;
       
   136 
       
   137     // set up curl globally if needed
       
   138     if ((err = do_init_curl())) {
       
   139         DEBUG(grfdl, 0, "curl_global_init failed: %s", curl_easy_strerror(err));
       
   140         goto error;
       
   141     }
       
   142     
       
   143     // allocate a new curl_easy
       
   144     if ((curl = curl_easy_init()) == NULL) {
       
   145         DEBUG(grfdl, 0, "curl_easy_init failed");
       
   146         goto error;
       
   147     }
       
   148 
       
   149     // let's set some options...
       
   150     if (
       
   151             (CURLE_OK != (err = curl_easy_setopt(curl, CURLOPT_VERBOSE, 1)))        // XXX: tie this in with OpenTTD's debug stuff
       
   152     /*            
       
   153             curl_easy_setopt(curl, CURLOPT_DEBUGFUNCTION, XXX)
       
   154     */            
       
   155         ||  (CURLE_OK != (err = curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 1)))             // no progress meter on stdout, thx
       
   156         ||  (CURLE_OK != (err = curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, curl_errbuf)))  // human-readable error messages
       
   157         ||  (CURLE_OK != (err = curl_easy_setopt(curl, CURLOPT_URL, api_url)))              // the URL to retrieve
       
   158         ||  (CURLE_OK != (err = curl_easy_setopt(curl, CURLOPT_USERAGENT, useragent)))      // the User-agent to use
       
   159         ||  (CURLE_OK != (err = curl_easy_setopt(curl, CURLOPT_POSTFIELDS, query_buf)))     // the POST data
       
   160         ||  (CURLE_OK != (err = curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, &check_available_write_cb)))
       
   161         ||  (CURLE_OK != (err = curl_easy_setopt(curl, CURLOPT_WRITEDATA, &write_ctx)))     // the write buffer
       
   162     ) {
       
   163         DEBUG(grfdl, 0, "curl_easy_setopt failed: %s", curl_easy_strerror(err));
       
   164         goto error;
       
   165     }
       
   166 
       
   167     // XXX: currently this freezes the UI..
       
   168     if ((err = curl_easy_perform(curl))) {
       
   169         DEBUG(grfdl, 0, "the curl request failed: %s", curl_easy_strerror(err));
       
   170         goto error;
       
   171     }
       
   172     
       
   173     // get the HTTP return code
       
   174     if ((err = curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &http_code))) {
       
   175         DEBUG(grfdl, 0, "curl_easy_getinfo failed: %s", curl_easy_strerror(err));
       
   176         goto error;
       
   177     }
       
   178 
       
   179     if (http_code != 200) {
       
   180         DEBUG(grfdl, 0, "Bad HTTP response code: %d", http_code);
       
   181         goto error;
       
   182     }
       
   183 
       
   184     // start parsing...
       
   185     buf_ptr = write_ctx.buf;
       
   186 
       
   187     while (buf_ptr < write_ctx.buf + write_ctx.offset) {
       
   188         // replace the \n with a \0
       
   189         char *nl = strchr(buf_ptr, '\n');
       
   190 
       
   191         if (!nl) {
       
   192             DEBUG(grfdl, 0, "Incomplete line in search response: %s", buf_ptr);
       
   193             goto error;
       
   194         }
       
   195 
       
   196         *nl = '\0';
       
   197         
       
   198         // check the format
       
   199         if (buf_ptr[8] != ':' || buf_ptr[8 + 1 + 32] != ':') {
       
   200             DEBUG(grfdl, 0, "Invalid line in search response: %s", buf_ptr);
       
   201             goto error;
       
   202         }
       
   203 
       
   204         // separate out the fields
       
   205         buf_ptr[8] = buf_ptr[8 + 1 + 32] = '\0';
       
   206         
       
   207         // parse the GRFID...
       
   208         char *invalid;
       
   209         uint32 grfid = strtol(buf_ptr + 0, &invalid, 16);
       
   210 
       
   211         if (*invalid) {
       
   212             DEBUG(grfdl, 0, "Invalid GRFID in search response: %s", buf_ptr);
       
   213             goto error;
       
   214         }
       
   215         
       
   216         // parse the md5sum
       
   217         uint8 md5sum[16]; 
       
   218 
       
   219         if (StringToMd5sum(md5sum, buf_ptr + 8 + 1)) {
       
   220             DEBUG(grfdl, 0, "Invalid md5sum in search response: %s", buf_ptr);
       
   221             goto error;
       
   222         }
       
   223 
       
   224         // copy the URL
       
   225         char *url = strdup(buf_ptr + 8 + 1 + 32 + 1);
       
   226 
       
   227         // look for the GRF
       
   228         for (c = list; c != NULL; c = c->next) {
       
   229             if (BSWAP32(c->grfid) == grfid && memcmp(c->md5sum, md5sum, 16) == 0) {
       
   230                 DEBUG(grfdl, 0, "Found %08X:%s at %s", buf_ptr + 0, buf_ptr + 8 + 1, url);
       
   231 
       
   232                 c->filename = url;
       
   233                 c->status = GCS_AVAILABLE;
       
   234                 break;
       
   235             } 
       
   236         }
       
   237         
       
   238         // free the allocated memory if it wasn't stored in a GRFConfig
       
   239         if (c == NULL) {
       
   240             free(url);
       
   241         }
       
   242 
       
   243         // move on to the next line
       
   244         buf_ptr = nl + 1;
       
   245     }
       
   246 
       
   247 error :
       
   248     // cleanup
       
   249     free(write_ctx.buf);
       
   250 
       
   251     if (curl)
       
   252         curl_easy_cleanup(curl);
       
   253 }
    24 
   254 
    25 // XXX: copy-pasted from newgrf_gui.cpp
   255 // XXX: copy-pasted from newgrf_gui.cpp
    26 static void ShowDownloadNewGRFInfo(const GRFConfig *c, uint x, uint y, uint w, uint bottom, bool show_params)
   256 static void ShowDownloadNewGRFInfo(const GRFConfig *c, uint x, uint y, uint w, uint bottom, bool show_params)
    27 {
   257 {
    28 	char buff[256];
   258 	char buff[256];
   243 
   473 
   244 	virtual void OnClick(Point pt, int widget)
   474 	virtual void OnClick(Point pt, int widget)
   245 	{
   475 	{
   246 		switch (widget) {
   476 		switch (widget) {
   247             case DNGRFS_CHECK_AVAILABLE:
   477             case DNGRFS_CHECK_AVAILABLE:
   248                 // XXX: implement
   478                 CheckAvailableNewGRFs(this->list);
       
   479 				
       
   480                 this->SetDirty();
   249 
   481 
   250                 break;
   482                 break;
   251 
   483 
   252             case DNGRFS_DOWNLOAD_AVAILABLE:
   484             case DNGRFS_DOWNLOAD_AVAILABLE:
   253                 // XXX: implement
   485                 // XXX: implement