we don't really need to build src/liblj/ as library; and we don't need to write each...
[k8lowj.git] / src / liblj / protocol.c
blob52b0bf9e6ceca7c350e33ad516624aa8abc3e300
1 /* logjam - a GTK client for LiveJournal.
2 * Copyright (C) 2000-2003 Evan Martin <evan@livejournal.com>
4 * vim: tabstop=4 shiftwidth=4 noexpandtab :
5 */
7 #include <glib.h>
9 #include <stdio.h>
10 #include <stdlib.h>
11 #include <errno.h>
12 #include <ctype.h>
13 #include <string.h>
15 #include "liblj/protocol.h"
16 #include "liblj/md5.h"
18 struct _LJRequest {
19 LJUser *user;
20 GHashTable *hash;
23 struct _LJResult {
24 /* in a result, only one string is allocated,
25 * and the rest of the hash table is pointers within that string.
26 * we need to keep a pointer to that string around for when we free the
27 * result. */
28 char *string;
29 GHashTable *hash;
32 static inline gboolean
33 needsescape(char c) {
34 return !isalnum(c) && c != '_';
37 char*
38 lj_urlencode(const char *string) {
39 int escapecount = 0;
40 const char *src;
41 char *dest;
42 char *newstr;
44 char hextable[] = { '0', '1', '2', '3', '4', '5', '6', '7',
45 '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
47 if (string == NULL) return NULL;
49 for (src = string; *src != 0; src++)
50 if (needsescape(*src)) escapecount++;
52 newstr = g_new(char, (src-string) - escapecount + (escapecount * 3) + 1);
54 src = string;
55 dest = newstr;
56 while (*src != 0) {
57 if (needsescape(*src)) {
58 unsigned char c = *src;
59 *dest++ = '%';
60 *dest++ = hextable[c >> 4];
61 *dest++ = hextable[c & 0x0F];
62 src++;
63 } else {
64 *dest++ = *src++;
67 *dest = 0;
69 return newstr;
72 char*
73 lj_urldecode(const char *string) {
74 int destlen = 0;
75 const char *src;
76 char *dest;
77 char *newstr;
79 if (string == NULL) return NULL;
81 for (src = string; *src != 0; src++) {
82 if (*src == '%') { src+=2; } /* FIXME: this isn't robust. should check
83 the next two chars for 0 */
84 destlen++;
87 newstr = g_new(char, destlen + 1);
88 src = string;
89 dest = newstr;
90 while (*src != 0) {
91 if (*src == '%') {
92 char h = (char)toupper(src[1]);
93 char l = (char)toupper(src[2]);
94 char vh, vl;
95 vh = isalpha(h) ? (10+(h-'A')) : (h-'0');
96 vl = isalpha(l) ? (10+(l-'A')) : (l-'0');
97 *dest++ = ((vh<<4)+vl);
98 src += 3;
99 } else if (*src == '+') {
100 *dest++ = ' ';
101 src++;
102 } else {
103 *dest++ = *src++;
106 *dest = 0;
108 return newstr;
111 /* retrieve a pointer to the current line, modifying src in place. */
112 static char *
113 get_line_inplace(char *src, char **dest) {
114 *dest = src;
115 while (*src != 0 && *src != '\n')
116 src++;
118 if (*src == '\n') {
119 *src = 0;
120 src++;
123 return src;
126 static LJResult*
127 lj_result_new(void) {
128 return g_new0(LJResult, 1);
131 LJResult*
132 lj_result_new_from_response(const char *res) {
133 char *origsrc, *src;
134 char *key;
135 char *val;
136 LJResult *result;
137 GHashTable *hash;
138 int read_keys = 0;
140 /* shortcut the common error case. */
141 if (res == NULL || *res == 0)
142 return NULL;
144 /* make our own copy so we can modify it. */
145 origsrc = src = g_strdup(res);
146 hash = g_hash_table_new_full(g_str_hash, g_str_equal, NULL, NULL);
148 while (*src != 0) {
149 src = get_line_inplace(src, &key);
150 if (*src) {
151 src = get_line_inplace(src, &val);
152 g_hash_table_insert(hash, key, val);
153 read_keys++;
156 if (read_keys == 0) { /* error. */
157 g_free(origsrc);
158 g_hash_table_destroy(hash);
159 return NULL;
162 result = lj_result_new();
163 result->string = origsrc;
164 result->hash = hash;
165 return result;
168 gboolean
169 lj_result_succeeded(LJResult *result) {
170 char *error;
172 if (result == NULL) return FALSE;
174 error = lj_result_get(result, "success");
175 if (error == NULL) return FALSE;
177 return (g_ascii_strcasecmp(error, "OK") == 0);
180 void
181 lj_request_add(LJRequest *request, const char *key, const char *val) {
182 g_hash_table_insert(request->hash, g_strdup(key), g_strdup(val));
184 void
185 lj_request_add_int(LJRequest *request, const char *key, int val) {
186 g_hash_table_insert(request->hash, g_strdup(key), g_strdup_printf("%d", val));
189 LJRequest*
190 lj_request_new_without_auth(LJUser *user, const char *mode) {
191 LJRequest *request = g_new0(LJRequest, 1);
193 request->user = user;
194 request->hash = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free);
196 lj_request_add(request, "mode", mode);
198 lj_request_add_int(request, "ver", user->server->protocolversion);
200 return request;
203 LJRequest*
204 lj_request_new(LJUser *user, const char *mode) {
205 LJRequest *request = lj_request_new_without_auth(user, mode);
206 char buf[33];
207 lj_request_add(request, "user", user->username);
208 lj_md5_hash(user->password, buf);
209 lj_request_add(request, "hpassword", buf);
210 return request;
213 void
214 lj_request_use_challenge(LJRequest *request, LJChallenge challenge) {
215 char passhash[33];
216 char *chalpass;
217 char fullhash[33];
219 /* make sure we're not passing a password. */
220 g_hash_table_remove(request->hash, "password");
221 g_hash_table_remove(request->hash, "hpassword");
223 lj_request_add(request, "auth_method", "challenge");
224 lj_request_add(request, "auth_challenge", challenge);
225 lj_md5_hash(request->user->password, passhash);
226 chalpass = g_strconcat(challenge, passhash, NULL);
227 lj_md5_hash(chalpass, fullhash);
228 g_free(chalpass);
229 lj_request_add(request, "auth_response", fullhash);
232 LJUser*
233 lj_request_get_user(LJRequest *request) {
234 return request->user;
237 static void
238 hash_append_str_encoded(gpointer key, gpointer value, gpointer data) {
239 GString *string = data;
240 gchar *en_key, *en_value;
242 if (key == NULL || value == NULL) return;
243 en_key = lj_urlencode(key);
244 en_value = lj_urlencode(value);
246 if (string->len > 0)
247 g_string_append_c(string, '&');
248 g_string_append_printf(string, "%s=%s", en_key, en_value);
250 g_free(en_key);
251 g_free(en_value);
254 GString*
255 lj_request_to_string(LJRequest* request) {
256 GString *str = g_string_sized_new(2048);
257 g_hash_table_foreach(request->hash, hash_append_str_encoded, str);
258 return str;
261 void
262 lj_request_free(LJRequest *request) {
263 g_hash_table_destroy(request->hash);
264 g_free(request);
267 #define SHOWCOUNT 6
268 char *showfirst[] = { "mode", "user", "password", "hpassword", "usejournal", "ver" };
269 static void
270 hash_dump(gpointer key, gpointer value, gpointer data) {
271 int i;
272 for (i = 0; i < SHOWCOUNT; i++)
273 if (g_ascii_strcasecmp((char*)key, showfirst[i]) == 0)
274 return;
275 g_print("\t%s: %s\n", (char*)key, (char*)value);
278 void
279 lj_request_dump(LJRequest *request) {
280 int i;
281 char *value;
282 for (i = 0; i < SHOWCOUNT; i++) {
283 value = g_hash_table_lookup(request->hash, showfirst[i]);
284 if (!value) continue;
285 g_print("\t%s: %s\n", showfirst[i], value);
287 g_hash_table_foreach(request->hash, hash_dump, NULL);
290 char*
291 lj_result_get(LJResult *result, const char *key) {
292 return g_hash_table_lookup(result->hash, key);
295 char*
296 lj_result_getf(LJResult* result, const char *key, ...) {
297 char buf[100];
298 va_list ap;
300 va_start(ap, key);
301 g_vsnprintf(buf, 100, key, ap);
302 va_end(ap);
303 return lj_result_get(result, buf);
307 lj_result_get_int(LJResult *result, const char *key) {
308 char *val;
309 val = lj_result_get(result, key);
310 if (val)
311 return atoi(val);
312 return 0;
316 lj_result_getf_int(LJResult* result, const char *key, ...) {
317 char buf[100];
318 va_list ap;
320 va_start(ap, key);
321 g_vsnprintf(buf, 100, key, ap);
322 va_end(ap);
323 return lj_result_get_int(result, buf);
326 void
327 lj_result_free(LJResult *result) {
328 if (!result) return; /* NULL result is the error result. */
329 g_free(result->string);
330 g_hash_table_destroy(result->hash);
331 g_free(result);