comparison libpurple/protocols/mxit/formcmds.c @ 28526:69aa4660401a

Initial addition of the MXit protocol plugin, provided by the MXit folks themselves.
author John Bailey <rekkanoryo@rekkanoryo.org>
date Sun, 08 Nov 2009 23:55:56 +0000
parents
children 5f80ab7ac183
comparison
equal deleted inserted replaced
28525:13e668ef158d 28526:69aa4660401a
1 /*
2 * MXit Protocol libPurple Plugin
3 *
4 * -- MXit Forms & Commands --
5 *
6 * Andrew Victor <libpurple@mxit.com>
7 *
8 * (C) Copyright 2009 MXit Lifestyle (Pty) Ltd.
9 * <http://www.mxitlifestyle.com>
10 *
11 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; either version 2 of the License, or
14 * (at your option) any later version.
15 *
16 * This program is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU General Public License for more details.
20 *
21 * You should have received a copy of the GNU General Public License
22 * along with this program; if not, write to the Free Software
23 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
24 */
25
26 #include <string.h>
27 #include <glib.h>
28 #include <glib/gprintf.h>
29
30 #include "purple.h"
31
32 #include "protocol.h"
33 #include "mxit.h"
34 #include "markup.h"
35 #include "formcmds.h"
36
37 #undef MXIT_DEBUG_COMMANDS
38
39 /*
40 * the MXit Command identifiers
41 */
42 typedef enum
43 {
44 MXIT_CMD_UNKNOWN = 0, /* Unknown command */
45 MXIT_CMD_CLRSCR, /* Clear screen (clrmsgscreen) */
46 MXIT_CMD_SENDSMS, /* Send SMS (sendsms) */
47 MXIT_CMD_REPLY, /* Reply (reply) */
48 MXIT_CMD_PLATREQ, /* Platform Request (platreq) */
49 MXIT_CMD_SELECTCONTACT, /* Select Contact (selc) */
50 MXIT_CMD_IMAGE /* Inline image (img) */
51 } MXitCommandType;
52
53
54 /*
55 * object for an inline image request with an URL
56 */
57 struct ii_url_request
58 {
59 struct RXMsgData* mx;
60 char* url;
61 };
62
63
64 /*------------------------------------------------------------------------
65 * Callback function invoked when an inline image request to a web site completes.
66 *
67 * @param url_data
68 * @param user_data The Markup message object
69 * @param url_text The data returned from the WAP site
70 * @param len The length of the data returned
71 * @param error_message Descriptive error message
72 */
73 static void mxit_cb_ii_returned(PurpleUtilFetchUrlData* url_data, gpointer user_data, const gchar* url_text, gsize len, const gchar* error_message)
74 {
75 struct ii_url_request* iireq = (struct ii_url_request*) user_data;
76 char* ii_data;
77 int* intptr = NULL;
78 int id;
79
80 #ifdef MXIT_DEBUG_COMMANDS
81 purple_debug_info(MXIT_PLUGIN_ID, "Inline Image returned from %s\n", iireq->url);
82 #endif
83
84 if (!url_text) {
85 /* no reply from the WAP site */
86 purple_debug_error(MXIT_PLUGIN_ID, "Error downloading Inline Image from %s.\n", iireq->url);
87 goto done;
88 }
89
90 /* lets first see if we dont have the inline image already in cache */
91 if (g_hash_table_lookup(iireq->mx->session->iimages, iireq->url)) {
92 /* inline image found in the cache, so we just ignore this reply */
93 goto done;
94 }
95
96 /* make a copy of the data */
97 ii_data = g_malloc(len);
98 memcpy(ii_data, (const char*) url_text, len);
99
100 /* we now have the inline image, store it in the imagestore */
101 id = purple_imgstore_add_with_id(ii_data, len, NULL);
102
103 /* map the inline image id to purple image id */
104 intptr = g_malloc(sizeof(int));
105 *intptr = id;
106 g_hash_table_insert(iireq->mx->session->iimages, iireq->url, intptr);
107
108 iireq->mx->flags |= PURPLE_MESSAGE_IMAGES;
109
110 done:
111 iireq->mx->img_count--;
112 if ((iireq->mx->img_count == 0) && (iireq->mx->converted)) {
113 /*
114 * this was the last outstanding emoticon for this message,
115 * so we can now display it to the user.
116 */
117 mxit_show_message(iireq->mx);
118 }
119
120 g_free(iireq);
121 }
122
123
124 /*------------------------------------------------------------------------
125 * Return the command identifier of this MXit Command.
126 *
127 * @param cmd The MXit command <key,value> map
128 * @return The MXit command identifier
129 */
130 static MXitCommandType command_type(GHashTable* hash)
131 {
132 char* op;
133 char* type;
134
135 op = g_hash_table_lookup(hash, "op");
136 if (op) {
137 if ( strcmp(op, "cmd") == 0 ) {
138 type = g_hash_table_lookup(hash, "type");
139 if (type == NULL) /* no command provided */
140 return MXIT_CMD_UNKNOWN;
141 else if (strcmp(type, "clrmsgscreen") == 0) /* clear the screen */
142 return MXIT_CMD_CLRSCR;
143 else if (strcmp(type, "sendsms") == 0) /* send an SMS */
144 return MXIT_CMD_SENDSMS;
145 else if (strcmp(type, "reply") == 0) /* list of options */
146 return MXIT_CMD_REPLY;
147 else if (strcmp(type, "platreq") == 0) /* platform request */
148 return MXIT_CMD_PLATREQ;
149 else if (strcmp(type, "selc") == 0) /* select contact */
150 return MXIT_CMD_SELECTCONTACT;
151 }
152 else if (strcmp(op, "img") == 0)
153 return MXIT_CMD_IMAGE;
154 }
155
156 return MXIT_CMD_UNKNOWN;
157 }
158
159
160 /*------------------------------------------------------------------------
161 * Tokenize a MXit Command string into a <key,value> map.
162 *
163 * @param cmd The MXit command string
164 * @return The <key,value> hash-map, or NULL on error.
165 */
166 static GHashTable* command_tokenize(char* cmd)
167 {
168 GHashTable* hash = NULL;
169 gchar** parts;
170 gchar* part;
171 int i = 0;
172
173 #ifdef MXIT_DEBUG_COMMANDS
174 purple_debug_info(MXIT_PLUGIN_ID, "command: '%s'\n", cmd);
175 #endif
176
177 /* explode the command into parts */
178 parts = g_strsplit(cmd, "|", 0);
179
180 hash = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free);
181
182 /* now break part into a key & value */
183 while ((part = parts[i]) != NULL) {
184 char* value;
185
186 value = strchr(parts[i], '='); /* find start of value */
187 if (value != NULL) {
188 *value = '\0';
189 value++;
190 }
191
192 #ifdef MXIT_DEBUG_COMMANDS
193 purple_debug_info(MXIT_PLUGIN_ID, " key='%s' value='%s'\n", parts[i], value);
194 #endif
195
196 g_hash_table_insert(hash, g_strdup(parts[i]), g_strdup(value));
197
198 i++;
199 }
200
201 g_strfreev(parts);
202
203 return hash;
204 }
205
206
207 /*------------------------------------------------------------------------
208 * Process a ClearScreen MXit command.
209 *
210 * @param session The MXit session object
211 * @param from The sender of the message.
212 */
213 static void command_clearscreen(struct MXitSession* session, const char* from)
214 {
215 PurpleConversation *conv;
216
217 conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM, from, session->acc);
218 if (conv == NULL) {
219 purple_debug_error(MXIT_PLUGIN_ID, "Conversation with '%s' not found\n", from);
220 return;
221 }
222
223 purple_conversation_clear_message_history(conv); // TODO: This doesn't actually clear the screen.
224 }
225
226
227 /*------------------------------------------------------------------------
228 * Process a Reply MXit command.
229 *
230 * @param mx The received message data object
231 * @param hash The MXit command <key,value> map
232 */
233 static void command_reply(struct RXMsgData* mx, GHashTable* hash)
234 {
235 char* replymsg;
236 char* selmsg;
237
238 selmsg = g_hash_table_lookup(hash, "selmsg"); /* find the selection message */
239 replymsg = g_hash_table_lookup(hash, "replymsg"); /* find the reply message */
240 if ((selmsg) && (replymsg)) {
241 gchar* seltext = g_markup_escape_text(purple_url_decode(selmsg), -1);
242 gchar* replytext = g_markup_escape_text(purple_url_decode(replymsg), -1);
243
244 mxit_add_html_link( mx, replytext, seltext );
245
246 g_free(seltext);
247 g_free(replytext);
248 }
249 }
250
251
252 /*------------------------------------------------------------------------
253 * Process a PlatformRequest MXit command.
254 *
255 * @param hash The MXit command <key,value> map
256 * @param msg The message to display (as generated so far)
257 */
258 static void command_platformreq(GHashTable* hash, GString* msg)
259 {
260 gchar* text = NULL;
261 char* selmsg;
262 char* dest;
263
264 selmsg = g_hash_table_lookup(hash, "selmsg"); /* find the selection message */
265 if (selmsg) {
266 text = g_markup_escape_text(purple_url_decode(selmsg), -1);
267 }
268
269 dest = g_hash_table_lookup(hash, "dest"); /* find the destination */
270 if (dest) {
271 g_string_append_printf(msg, "<a href=\"%s\">%s</a>", purple_url_decode(dest), (text) ? text : "Download"); /* add link to display message */
272 }
273
274 if (text)
275 g_free(text);
276 }
277
278
279 /*------------------------------------------------------------------------
280 * Process an inline image MXit command.
281 *
282 * @param mx The received message data object
283 * @param hash The MXit command <key,value> map
284 * @param msg The message to display (as generated so far)
285 */
286 static void command_image(struct RXMsgData* mx, GHashTable* hash, GString* msg)
287 {
288 const char* img;
289 const char* reply;
290 guchar* rawimg;
291 char link[256];
292 gsize rawimglen;
293 int imgid;
294
295 img = g_hash_table_lookup(hash, "dat");
296 if (img) {
297 rawimg = purple_base64_decode(img, &rawimglen);
298 //purple_util_write_data_to_file_absolute("/tmp/mxitinline.png", (char*) rawimg, rawimglen);
299 imgid = purple_imgstore_add_with_id(rawimg, rawimglen, NULL);
300 g_snprintf(link, sizeof(link), "<img id=\"%i\">", imgid);
301 g_string_append_printf(msg, "%s", link);
302 mx->flags |= PURPLE_MESSAGE_IMAGES;
303 }
304 else {
305 img = g_hash_table_lookup(hash, "src");
306 if (img) {
307 struct ii_url_request* iireq;
308
309 iireq = g_new0(struct ii_url_request,1);
310 iireq->url = g_strdup(purple_url_decode(img));
311 iireq->mx = mx;
312
313 g_string_append_printf(msg, "%s%s>", MXIT_II_TAG, iireq->url);
314 mx->got_img = TRUE;
315
316 /* lets first see if we dont have the inline image already in cache */
317 if (g_hash_table_lookup(mx->session->iimages, iireq->url)) {
318 /* inline image found in the cache, so we do not have to request it from the web */
319 g_free(iireq);
320 }
321 else {
322 /* send the request for the inline image */
323 purple_debug_info(MXIT_PLUGIN_ID, "sending request for inline image '%s'\n", iireq->url);
324
325 /* request the image (reference: "libpurple/util.h") */
326 purple_util_fetch_url_request(iireq->url, TRUE, NULL, TRUE, NULL, FALSE, mxit_cb_ii_returned, iireq);
327 mx->img_count++;
328 }
329 }
330 }
331
332 /* if this is a clickable image, show a click link */
333 reply = g_hash_table_lookup(hash, "replymsg");
334 if (reply) {
335 g_string_append_printf(msg, "\n");
336 mxit_add_html_link(mx, reply, "click here");
337 }
338 }
339
340
341 /*------------------------------------------------------------------------
342 * Process a received MXit Command message.
343 *
344 * @param mx The received message data object
345 * @param message The message text
346 * @return The length of the command
347 */
348 //void mxit_command_received(struct MXitSession* session, const char* from, char* message, time_t timestamp)
349 int mxit_parse_command(struct RXMsgData* mx, char* message)
350 {
351 GHashTable* hash = NULL;
352 char* start;
353 char* end;
354
355 /* ensure that this is really a command */
356 if ( ( message[0] != ':' ) || ( message[1] != ':' ) ) {
357 /* this is not a command */
358 return 0;
359 }
360
361 start = message + 2;
362 end = strstr(start, ":");
363 if (end) {
364 /* end of a command found */
365 *end = '\0'; /* terminate command string */
366
367 hash = command_tokenize(start); /* break into <key,value> pairs */
368 if (hash) {
369 MXitCommandType type = command_type(hash);
370
371 switch (type) {
372 case MXIT_CMD_CLRSCR :
373 command_clearscreen(mx->session, mx->from);
374 break;
375 case MXIT_CMD_REPLY :
376 command_reply(mx, hash);
377 break;
378 case MXIT_CMD_PLATREQ :
379 command_platformreq(hash, mx->msg);
380 break;
381 case MXIT_CMD_IMAGE :
382 command_image(mx, hash, mx->msg);
383 break;
384 default :
385 /* command unknown, or not currently supported */
386 break;
387 }
388 g_hash_table_destroy(hash);
389 }
390 *end = ':';
391
392 return end - message;
393 }
394 else {
395 return 0;
396 }
397 }