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