Mercurial > pidgin
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 } |