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