Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
stenzek
GitHub Repository: stenzek/duckstation
Path: blob/master/dep/rcheevos/src/rurl/url.c
4253 views
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
403
404