comparison libpurple/protocols/myspace/message.c @ 17321:ddcf9ef2ccec

Add MsimMessage implementation (sending only).
author Jeffrey Connelly <jaconnel@calpoly.edu>
date Thu, 31 May 2007 02:29:50 +0000
parents
children 793301c04e3a
comparison
equal deleted inserted replaced
17320:0409947da92f 17321:ddcf9ef2ccec
1 /** MySpaceIM protocol messages
2 *
3 * \author Jeff Connelly
4 *
5 * Copyright (C) 2007, Jeff Connelly <jeff2@homing.pidgin.im>
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 */
21
22 #include "myspace.h"
23 #include "message.h"
24
25 static void msim_msg_free_element(gpointer data, gpointer user_data);
26 static void msim_msg_debug_string_element(gpointer data, gpointer user_data);
27 static gchar *msim_msg_pack_using(MsimMessage *msg, GFunc gf, gchar *sep, gchar *begin, gchar *end);
28
29 MsimMessage *msim_msg_new(void)
30 {
31 /* Just an empty list. */
32 return NULL;
33 }
34
35 /** Free an individual message element. */
36 static void msim_msg_free_element(gpointer data, gpointer user_data)
37 {
38 MsimMessageElement *elem;
39
40 elem = (MsimMessageElement *)data;
41
42 switch (elem->type)
43 {
44 case MSIM_TYPE_BOOLEAN: /* Fall through */
45 case MSIM_TYPE_INTEGER:
46 /* Integer value stored in gpointer - no need to free(). */
47 break;
48
49 case MSIM_TYPE_STRING:
50 /* Always free strings - caller should have g_strdup()'d if
51 * string was static or temporary and not to be freed. */
52 g_free(elem->data);
53 break;
54
55 case MSIM_TYPE_BINARY:
56 /* Free the GString itself and the binary data. */
57 g_string_free((GString *)elem->data, TRUE);
58 break;
59
60 case MSIM_TYPE_DICTIONARY:
61 /* TODO: free dictionary */
62 break;
63
64 case MSIM_TYPE_LIST:
65 /* TODO: free list */
66 break;
67
68 default:
69 purple_debug_info("msim", "msim_msg_free_element: not freeing unknown type %d\n",
70 elem->type);
71 break;
72 }
73
74 g_free(elem);
75 }
76
77 /** Free a complete message. */
78 void msim_msg_free(MsimMessage *msg)
79 {
80 if (!msg)
81 {
82 /* already free as can be */
83 return;
84 }
85
86 g_list_foreach(msg, msim_msg_free_element, NULL);
87 g_list_free(msg);
88 }
89
90 /** Send an existing MsimMessage. */
91 gboolean msim_msg_send(MsimSession *session, MsimMessage *msg)
92 {
93 gchar *raw;
94 gboolean success;
95
96 raw = msim_msg_pack(msg);
97 success = msim_send_raw(session, raw);
98 g_free(raw);
99
100 return success;
101 }
102
103 /**
104 *
105 * Send a message to the server, whose contents is specified using
106 * variable arguments.
107 *
108 * @param session
109 * @param ... A sequence of gchar* key/type/value triplets, terminated with NULL.
110 *
111 * This function exists for coding convenience: it allows a message to be created
112 * and sent in one line of code. Internally it calls msim_msg_send().
113 *
114 * IMPORTANT: See msim_msg_append() documentation for details on element types.
115 *
116 */
117 gboolean msim_send(MsimSession *session, ...)
118 {
119 va_list argp;
120 gchar *key, *value;
121 MsimMessageType type;
122 gboolean success;
123 MsimMessage *msg;
124 GString *gs;
125
126 msg = msim_msg_new();
127
128 /* Read key, type, value triplets until NULL. */
129 va_start(argp, session);
130 do
131 {
132 key = va_arg(argp, gchar *);
133 if (!key)
134 {
135 break;
136 }
137
138 type = va_arg(argp, int);
139
140 switch (type)
141 {
142 case MSIM_TYPE_INTEGER:
143 msg = msim_msg_append(msg, key, type, GUINT_TO_POINTER(va_arg(argp, int)));
144 break;
145
146 case MSIM_TYPE_STRING:
147 value = va_arg(argp, char *);
148
149 g_return_val_if_fail(value != NULL, FALSE);
150
151 msg = msim_msg_append(msg, key, type, value);
152 break;
153
154 case MSIM_TYPE_BINARY:
155 gs = va_arg(argp, GString *);
156
157 g_return_val_if_fail(gs != NULL, FALSE);
158
159 /* msim_msg_free() will free this GString the caller created. */
160 msg = msim_msg_append(msg, key, type, gs);
161 break;
162
163 default:
164 purple_debug_info("msim", "msim_send: unknown type %d\n", type);
165 break;
166 }
167 } while(key);
168
169 /* Actually send the message. */
170 success = msim_msg_send(session, msg);
171
172 /* Cleanup. */
173 va_end(argp);
174 msim_msg_free(msg);
175
176 return success;
177 }
178
179
180
181
182 /** Append a new element to a message.
183 *
184 * @param name Textual name of element (static string, neither copied nor freed).
185 * @param type An MSIM_TYPE_* code.
186 * @param data Pointer to data, see below.
187 *
188 * @return The new message - must be assigned to as with GList*. For example:
189 *
190 * msg = msim_msg_append(msg, ...)
191 *
192 * The data parameter depends on the type given:
193 *
194 * * MSIM_TYPE_INTEGER: Use GUINT_TO_POINTER(x).
195 *
196 * * MSIM_TYPE_BINARY: Same as integer, non-zero is TRUE and zero is FALSE.
197 *
198 * * MSIM_TYPE_STRING: gchar *. The data WILL BE FREED - use g_strdup() if needed.
199 *
200 * * MSIM_TYPE_BINARY: g_string_new_len(data, length). The data and GString will be freed.
201 *
202 * * MSIM_TYPE_DICTIONARY: TODO
203 *
204 * * MSIM_TYPE_LIST: TODO
205 *
206 * */
207 MsimMessage *msim_msg_append(MsimMessage *msg, gchar *name, MsimMessageType type, gpointer data)
208 {
209 MsimMessageElement *elem;
210
211 elem = g_new0(MsimMessageElement, 1);
212
213 elem->name = name;
214 elem->type = type;
215 elem->data = data;
216
217 return g_list_append(msg, elem);
218 }
219
220 /** Pack a string using the given GFunc and seperator.
221 * Used by msim_msg_debug_string() and msim_msg_pack().
222 */
223 static gchar *msim_msg_pack_using(MsimMessage *msg, GFunc gf, gchar *sep, gchar *begin, gchar *end)
224 {
225 gchar **strings;
226 gchar **strings_tmp;
227 gchar *joined;
228 gchar *final;
229 int i;
230
231 g_return_val_if_fail(msg != NULL, NULL);
232
233 /* Add one for NULL terminator for g_strjoinv(). */
234 strings = (gchar **)g_new0(gchar *, g_list_length(msg) + 1);
235
236 strings_tmp = strings;
237 g_list_foreach(msg, gf, &strings_tmp);
238
239 joined = g_strjoinv(sep, strings);
240 final = g_strconcat(begin, joined, end, NULL);
241 g_free(joined);
242
243 /* Clean up. */
244 for (i = 0; i < g_list_length(msg); ++i)
245 {
246 g_free(strings[i]);
247 }
248
249 g_free(strings);
250
251 return final;
252 }
253 /** Store a human-readable string describing the element.
254 *
255 * @param data Pointer to an MsimMessageElement.
256 * @param user_data
257 */
258 static void msim_msg_debug_string_element(gpointer data, gpointer user_data)
259 {
260 MsimMessageElement *elem;
261 gchar *string;
262 GString *gs;
263 gchar *binary;
264 gchar ***items; /* wow, a pointer to a pointer to a pointer */
265
266 elem = (MsimMessageElement *)data;
267 items = user_data;
268
269 switch (elem->type)
270 {
271 case MSIM_TYPE_INTEGER:
272 string = g_strdup_printf("%s(integer): %d", elem->name, GPOINTER_TO_UINT(elem->data));
273 break;
274
275 case MSIM_TYPE_STRING:
276 string = g_strdup_printf("%s(string): %s", elem->name, (gchar *)elem->data);
277 break;
278
279 case MSIM_TYPE_BINARY:
280 gs = (GString *)elem->data;
281 binary = purple_base64_encode((guchar*)gs->str, gs->len);
282 string = g_strdup_printf("%s(binary, %d bytes): %s", elem->name, (int)gs->len, binary);
283 g_free(binary);
284 break;
285
286 case MSIM_TYPE_BOOLEAN:
287 string = g_strdup_printf("%s(boolean): %s", elem->name,
288 GPOINTER_TO_UINT(elem->data) ? "TRUE" : "FALSE");
289 break;
290
291 case MSIM_TYPE_DICTIONARY:
292 /* TODO: provide human-readable output of dictionary. */
293 string = g_strdup_printf("%s(dict): TODO", elem->name);
294 break;
295
296 case MSIM_TYPE_LIST:
297 /* TODO: provide human-readable output of list. */
298 string = g_strdup_printf("%s(list): TODO", elem->name);
299 break;
300
301 default:
302 string = g_strdup_printf("%s(unknown type %d)", elem->name, elem->type);
303 break;
304 }
305
306 **items = string;
307 ++(*items);
308 }
309
310 /** Return a human-readable string of the message.
311 *
312 * @return A string. Caller must g_free().
313 */
314 gchar *msim_msg_debug_string(MsimMessage *msg)
315 {
316 if (!msg)
317 {
318 return g_strdup("<MsimMessage: empty>");
319 }
320
321 return msim_msg_pack_using(msg, msim_msg_debug_string_element, "\n", "<MsimMessage: \n", ">");
322 }
323
324 /** Pack an element into its protocol representation.
325 *
326 * @param data Pointer to an MsimMessageElement.
327 * @param user_data
328 */
329 static void msim_msg_pack_element(gpointer data, gpointer user_data)
330 {
331 MsimMessageElement *elem;
332 gchar *string;
333 gchar ***items;
334 gchar *binary;
335 gchar *escaped;
336 GString *gs;
337
338 elem = (MsimMessageElement *)data;
339 items = user_data;
340
341 switch (elem->type)
342 {
343 case MSIM_TYPE_INTEGER:
344 string = g_strdup_printf("%s\\%d", elem->name, GPOINTER_TO_UINT(elem->data));
345 break;
346
347 case MSIM_TYPE_STRING:
348 /* Strings get escaped. */
349 escaped = msim_escape((gchar *)elem->data);
350 string = g_strdup_printf("%s\\%s", elem->name, escaped);
351 g_free(escaped);
352 break;
353
354 case MSIM_TYPE_BINARY:
355 gs = (GString *)elem->data;
356 binary = purple_base64_encode((guchar*)gs->str, gs->len);
357 /* Do not escape! */
358 string = g_strdup_printf("%s\\%s", elem->name, binary);
359 g_free(binary);
360 break;
361
362 case MSIM_TYPE_BOOLEAN:
363 if (GPOINTER_TO_UINT(elem->data))
364 {
365 string = g_strdup_printf("%s\\\\", elem->name);
366 } else {
367 /* False - leave out. */
368 string = g_strdup("");
369 }
370 break;
371
372 case MSIM_TYPE_DICTIONARY:
373 /* TODO: pack using k=v\034k2=v2\034... */
374 string = g_strdup_printf("%s\\TODO", elem->name);
375 break;
376
377 case MSIM_TYPE_LIST:
378 /* TODO: pack using a|b|c|d|... */
379 string = g_strdup_printf("%s\\TODO", elem->name);
380 break;
381
382 default:
383 g_return_if_fail(FALSE);
384 break;
385 }
386
387 **items = string;
388 ++(*items);
389 }
390
391
392 /** Return a packed string suitable for sending over the wire.
393 *
394 * @return A string. Caller must g_free().
395 */
396 gchar *msim_msg_pack(MsimMessage *msg)
397 {
398 g_return_val_if_fail(msg != NULL, NULL);
399
400 return msim_msg_pack_using(msg, msim_msg_pack_element, "\\", "\\", "\\final\\");
401 }
402