duckstation

duckstation, but archived from the revision just before upstream changed it to a proprietary software project, this version is the libre one
git clone https://git.neptards.moe/u3shit/duckstation.git
Log | Files | Refs | README | LICENSE

url.c (11308B)


      1 #include "rc_url.h"
      2 
      3 #include "../rc_compat.h"
      4 #include "../rhash/md5.h"
      5 
      6 #include <stdio.h>
      7 #include <string.h>
      8 
      9 #if RCHEEVOS_URL_SSL
     10 #define RCHEEVOS_URL_PROTOCOL "https"
     11 #else
     12 #define RCHEEVOS_URL_PROTOCOL "http"
     13 #endif
     14 
     15 static int rc_url_encode(char* encoded, size_t len, const char* str) {
     16   for (;;) {
     17     switch (*str) {
     18       case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': case 'g': case 'h': case 'i': case 'j':
     19       case 'k': case 'l': case 'm': case 'n': case 'o': case 'p': case 'q': case 'r': case 's': case 't':
     20       case 'u': case 'v': case 'w': case 'x': case 'y': case 'z':
     21       case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': case 'G': case 'H': case 'I': case 'J':
     22       case 'K': case 'L': case 'M': case 'N': case 'O': case 'P': case 'Q': case 'R': case 'S': case 'T':
     23       case 'U': case 'V': case 'W': case 'X': case 'Y': case 'Z':
     24       case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9':
     25       case '-': case '_': case '.': case '~':
     26         if (len < 2)
     27           return -1;
     28 
     29         *encoded++ = *str++;
     30         --len;
     31         break;
     32 
     33       case ' ':
     34         if (len < 2)
     35           return -1;
     36 
     37         *encoded++ = '+';
     38         ++str;
     39         --len;
     40         break;
     41 
     42       default:
     43         if (len < 4)
     44           return -1;
     45 
     46         snprintf(encoded, len, "%%%02x", (unsigned char)*str);
     47         encoded += 3;
     48         ++str;
     49         len -= 3;
     50         break;
     51 
     52       case '\0':
     53         *encoded = 0;
     54         return 0;
     55     }
     56   }
     57 }
     58 
     59 int rc_url_award_cheevo(char* buffer, size_t size, const char* user_name, const char* login_token,
     60                         unsigned cheevo_id, int hardcore, const char* game_hash) {
     61   char urle_user_name[64];
     62   char urle_login_token[64];
     63   int written;
     64 
     65   if (rc_url_encode(urle_user_name, sizeof(urle_user_name), user_name) != 0) {
     66     return -1;
     67   }
     68 
     69   if (rc_url_encode(urle_login_token, sizeof(urle_login_token), login_token) != 0) {
     70     return -1;
     71   }
     72 
     73   written = snprintf(
     74     buffer,
     75     size,
     76     RCHEEVOS_URL_PROTOCOL"://retroachievements.org/dorequest.php?r=awardachievement&u=%s&t=%s&a=%u&h=%d",
     77     urle_user_name,
     78     urle_login_token,
     79     cheevo_id,
     80     hardcore ? 1 : 0
     81   );
     82 
     83   if (game_hash && strlen(game_hash) == 32 && (size - (size_t)written) >= 35) {
     84      written += snprintf(buffer + written, size - (size_t)written, "&m=%s", game_hash);
     85   }
     86 
     87   return (size_t)written >= size ? -1 : 0;
     88 }
     89 
     90 int rc_url_submit_lboard(char* buffer, size_t size, const char* user_name, const char* login_token, unsigned lboard_id, int value) {
     91   char urle_user_name[64];
     92   char urle_login_token[64];
     93   char signature[64];
     94   unsigned char hash[16];
     95   md5_state_t state;
     96   int written;
     97 
     98   if (rc_url_encode(urle_user_name, sizeof(urle_user_name), user_name) != 0) {
     99     return -1;
    100   }
    101 
    102   if (rc_url_encode(urle_login_token, sizeof(urle_login_token), login_token) != 0) {
    103     return -1;
    104   }
    105 
    106   /* Evaluate the signature. */
    107   snprintf(signature, sizeof(signature), "%u%s%d", lboard_id, user_name, value);
    108   md5_init(&state);
    109   md5_append(&state, (unsigned char*)signature, (int)strlen(signature));
    110   md5_finish(&state, hash);
    111 
    112   written = snprintf(
    113     buffer,
    114     size,
    115     RCHEEVOS_URL_PROTOCOL"://retroachievements.org/dorequest.php?r=submitlbentry&u=%s&t=%s&i=%u&s=%d&v=%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x",
    116     urle_user_name,
    117     urle_login_token,
    118     lboard_id,
    119     value,
    120     hash[ 0], hash[ 1], hash[ 2], hash[ 3], hash[ 4], hash[ 5], hash[ 6], hash[ 7],
    121     hash[ 8], hash[ 9], hash[10], hash[11],hash[12], hash[13], hash[14], hash[15]
    122   );
    123 
    124   return (size_t)written >= size ? -1 : 0;
    125 }
    126 
    127 int rc_url_get_gameid(char* buffer, size_t size, const char* hash) {
    128   int written = snprintf(
    129     buffer,
    130     size,
    131     RCHEEVOS_URL_PROTOCOL"://retroachievements.org/dorequest.php?r=gameid&m=%s",
    132     hash
    133   );
    134 
    135   return (size_t)written >= size ? -1 : 0;
    136 }
    137 
    138 int rc_url_get_patch(char* buffer, size_t size, const char* user_name, const char* login_token, unsigned gameid) {
    139   char urle_user_name[64];
    140   char urle_login_token[64];
    141   int written;
    142 
    143   if (rc_url_encode(urle_user_name, sizeof(urle_user_name), user_name) != 0) {
    144     return -1;
    145   }
    146 
    147   if (rc_url_encode(urle_login_token, sizeof(urle_login_token), login_token) != 0) {
    148     return -1;
    149   }
    150 
    151   written = snprintf(
    152     buffer,
    153     size,
    154     RCHEEVOS_URL_PROTOCOL"://retroachievements.org/dorequest.php?r=patch&u=%s&t=%s&g=%u",
    155     urle_user_name,
    156     urle_login_token,
    157     gameid
    158   );
    159 
    160   return (size_t)written >= size ? -1 : 0;
    161 }
    162 
    163 int rc_url_get_badge_image(char* buffer, size_t size, const char* badge_name) {
    164   int written = snprintf(
    165     buffer,
    166     size,
    167     "http://i.retroachievements.org/Badge/%s",
    168     badge_name
    169   );
    170 
    171   return (size_t)written >= size ? -1 : 0;
    172 }
    173 
    174 int rc_url_login_with_password(char* buffer, size_t size, const char* user_name, const char* password) {
    175   char urle_user_name[64];
    176   char urle_password[256];
    177   int written;
    178 
    179   if (rc_url_encode(urle_user_name, sizeof(urle_user_name), user_name) != 0) {
    180     return -1;
    181   }
    182 
    183   if (rc_url_encode(urle_password, sizeof(urle_password), password) != 0) {
    184     return -1;
    185   }
    186 
    187   written = snprintf(
    188     buffer,
    189     size,
    190     RCHEEVOS_URL_PROTOCOL"://retroachievements.org/dorequest.php?r=login&u=%s&p=%s",
    191     urle_user_name,
    192     urle_password
    193   );
    194 
    195   return (size_t)written >= size ? -1 : 0;
    196 }
    197 
    198 int rc_url_login_with_token(char* buffer, size_t size, const char* user_name, const char* login_token) {
    199   char urle_user_name[64];
    200   char urle_login_token[64];
    201   int written;
    202 
    203   if (rc_url_encode(urle_user_name, sizeof(urle_user_name), user_name) != 0) {
    204     return -1;
    205   }
    206 
    207   if (rc_url_encode(urle_login_token, sizeof(urle_login_token), login_token) != 0) {
    208     return -1;
    209   }
    210 
    211   written = snprintf(
    212     buffer,
    213     size,
    214     RCHEEVOS_URL_PROTOCOL"://retroachievements.org/dorequest.php?r=login&u=%s&t=%s",
    215     urle_user_name,
    216     urle_login_token
    217   );
    218 
    219   return (size_t)written >= size ? -1 : 0;
    220 }
    221 
    222 int rc_url_get_unlock_list(char* buffer, size_t size, const char* user_name, const char* login_token, unsigned gameid, int hardcore) {
    223   char urle_user_name[64];
    224   char urle_login_token[64];
    225   int written;
    226 
    227   if (rc_url_encode(urle_user_name, sizeof(urle_user_name), user_name) != 0) {
    228     return -1;
    229   }
    230 
    231   if (rc_url_encode(urle_login_token, sizeof(urle_login_token), login_token) != 0) {
    232     return -1;
    233   }
    234 
    235   written = snprintf(
    236     buffer,
    237     size,
    238     RCHEEVOS_URL_PROTOCOL"://retroachievements.org/dorequest.php?r=unlocks&u=%s&t=%s&g=%u&h=%d",
    239     urle_user_name,
    240     urle_login_token,
    241     gameid,
    242     hardcore ? 1 : 0
    243   );
    244 
    245   return (size_t)written >= size ? -1 : 0;
    246 }
    247 
    248 int rc_url_post_playing(char* buffer, size_t size, const char* user_name, const char* login_token, unsigned gameid) {
    249   char urle_user_name[64];
    250   char urle_login_token[64];
    251   int written;
    252 
    253   if (rc_url_encode(urle_user_name, sizeof(urle_user_name), user_name) != 0) {
    254     return -1;
    255   }
    256 
    257   if (rc_url_encode(urle_login_token, sizeof(urle_login_token), login_token) != 0) {
    258     return -1;
    259   }
    260 
    261   written = snprintf(
    262     buffer,
    263     size,
    264     RCHEEVOS_URL_PROTOCOL"://retroachievements.org/dorequest.php?r=postactivity&u=%s&t=%s&a=3&m=%u",
    265     urle_user_name,
    266     urle_login_token,
    267     gameid
    268   );
    269 
    270   return (size_t)written >= size ? -1 : 0;
    271 }
    272 
    273 static int rc_url_append_param_equals(char* buffer, size_t buffer_size, size_t buffer_offset, const char* param)
    274 {
    275   int written = 0;
    276   size_t param_len;
    277 
    278   if (buffer_offset >= buffer_size)
    279     return -1;
    280 
    281   if (buffer_offset) {
    282     buffer += buffer_offset;
    283     buffer_size -= buffer_offset;
    284 
    285     if (buffer[-1] != '?') {
    286       *buffer++ = '&';
    287       buffer_size--;
    288       written = 1;
    289     }
    290   }
    291 
    292   param_len = strlen(param);
    293   if (param_len + 1 >= buffer_size)
    294     return -1;
    295   memcpy(buffer, param, param_len);
    296   buffer[param_len] = '=';
    297 
    298   written += (int)param_len + 1;
    299   return written + (int)buffer_offset;
    300 }
    301 
    302 static int rc_url_append_unum(char* buffer, size_t buffer_size, size_t* buffer_offset, const char* param, unsigned value)
    303 {
    304   int written = rc_url_append_param_equals(buffer, buffer_size, *buffer_offset, param);
    305   if (written > 0) {
    306     char num[16];
    307     int chars = snprintf(num, sizeof(num), "%u", value);
    308 
    309     if (chars + written < (int)buffer_size) {
    310       memcpy(&buffer[written], num, chars + 1);
    311       *buffer_offset = written + chars;
    312       return 0;
    313     }
    314   }
    315 
    316   return -1;
    317 }
    318 
    319 static int rc_url_append_str(char* buffer, size_t buffer_size, size_t* buffer_offset, const char* param, const char* value)
    320 {
    321   int written = rc_url_append_param_equals(buffer, buffer_size, *buffer_offset, param);
    322   if (written > 0) {
    323     buffer += written;
    324     buffer_size -= written;
    325 
    326     if (rc_url_encode(buffer, buffer_size, value) == 0) {
    327       written += (int)strlen(buffer);
    328       *buffer_offset = written;
    329       return 0;
    330     }
    331   }
    332 
    333   return -1;
    334 }
    335 
    336 static int rc_url_build_dorequest(char* url_buffer, size_t url_buffer_size, size_t* buffer_offset,
    337    const char* api, const char* user_name)
    338 {
    339   const char* base_url = RCHEEVOS_URL_PROTOCOL"://retroachievements.org/dorequest.php";
    340   size_t written = strlen(base_url);
    341   int failure = 0;
    342 
    343   if (url_buffer_size < written + 1)
    344     return -1;
    345   memcpy(url_buffer, base_url, written);
    346   url_buffer[written++] = '?';
    347 
    348   failure |= rc_url_append_str(url_buffer, url_buffer_size, &written, "r", api);
    349   if (user_name)
    350     failure |= rc_url_append_str(url_buffer, url_buffer_size, &written, "u", user_name);
    351 
    352   *buffer_offset += written;
    353   return failure;
    354 }
    355 
    356 int rc_url_ping(char* url_buffer, size_t url_buffer_size, char* post_buffer, size_t post_buffer_size,
    357                 const char* user_name, const char* login_token, unsigned gameid, const char* rich_presence) 
    358 {
    359   size_t written = 0;
    360   int failure = rc_url_build_dorequest(url_buffer, url_buffer_size, &written, "ping", user_name);
    361   failure |= rc_url_append_unum(url_buffer, url_buffer_size, &written, "g", gameid);
    362 
    363   written = 0;
    364   failure |= rc_url_append_str(post_buffer, post_buffer_size, &written, "t", login_token);
    365 
    366   if (rich_presence && *rich_presence)
    367     failure |= rc_url_append_str(post_buffer, post_buffer_size, &written, "m", rich_presence);
    368 
    369   if (failure) {
    370     if (url_buffer_size)
    371       url_buffer[0] = '\0';
    372     if (post_buffer_size)
    373       post_buffer[0] = '\0';
    374   }
    375 
    376   return failure;
    377 }
    378 
    379 int rc_url_get_lboard_entries(char* buffer, size_t size, unsigned lboard_id, unsigned first_index, unsigned count)
    380 {
    381   size_t written = 0;
    382   int failure = rc_url_build_dorequest(buffer, size, &written, "lbinfo", NULL);
    383   failure |= rc_url_append_unum(buffer, size, &written, "i", lboard_id);
    384   if (first_index > 1)
    385     failure |= rc_url_append_unum(buffer, size, &written, "o", first_index - 1);
    386   failure |= rc_url_append_unum(buffer, size, &written, "c", count);
    387 
    388   return failure;
    389 }
    390 
    391 int rc_url_get_lboard_entries_near_user(char* buffer, size_t size, unsigned lboard_id, const char* user_name, unsigned count)
    392 {
    393   size_t written = 0;
    394   int failure = rc_url_build_dorequest(buffer, size, &written, "lbinfo", NULL);
    395   failure |= rc_url_append_unum(buffer, size, &written, "i", lboard_id);
    396   failure |= rc_url_append_str(buffer, size, &written, "u", user_name);
    397   failure |= rc_url_append_unum(buffer, size, &written, "c", count);
    398 
    399   return failure;
    400 }
    401 
    402 #undef RCHEEVOS_URL_PROTOCOL