Migrate certificates, icons, logs to XDG dirs
[pidgin-git.git] / libpurple / protocols / mxit / formcmds.c
blobd3b6a79d7140975ae7cdb61850ef82d1d2f91c15
1 /*
2 * MXit Protocol libPurple Plugin
4 * -- MXit Forms & Commands --
6 * Andrew Victor <libpurple@mxit.com>
8 * (C) Copyright 2009 MXit Lifestyle (Pty) Ltd.
9 * <http://www.mxitlifestyle.com>
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.
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.
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
27 #include "internal.h"
28 #include "debug.h"
29 #include "http.h"
30 #include "image-store.h"
32 #include "client.h"
33 #include "mxit.h"
34 #include "markup.h"
35 #include "formcmds.h"
37 #undef MXIT_DEBUG_COMMANDS
40 * the MXit Command identifiers
42 typedef enum
44 MXIT_CMD_UNKNOWN = 0, /* Unknown command */
45 MXIT_CMD_CLEAR, /* Clear (clear) */
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 MXIT_CMD_SCREENCONFIG, /* Chat-screen config (csc) */
52 MXIT_CMD_SCREENINFO, /* Chat-screen info (csi) */
53 MXIT_CMD_IMAGESTRIP, /* Image Strip (is) */
54 MXIT_CMD_TABLE /* Table (tbl) */
55 } MXitCommandType;
57 /* Chat-screen behaviours (bhvr) */
58 #define SCREEN_NO_HEADINGS 0x01
59 #define SCREEN_FULLSCREEN 0x02
60 #define SCREEN_AUTOCLEAR 0x04
61 #define SCREEN_NO_AUDIO 0x08
62 #define SCREEN_NO_MSGPREFIX 0x10
63 #define SCREEN_NOTIFY 0x20
64 #define SCREEN_PROGRESSBAR 0x40
68 * object for an inline image request with an URL
70 struct ii_url_request
72 struct RXMsgData* mx;
73 char* url;
77 /*------------------------------------------------------------------------
78 * Callback function invoked when an inline image request to a web site completes.
80 static void
81 mxit_cb_ii_returned(PurpleHttpConnection *http_conn, PurpleHttpResponse *response,
82 gpointer _iireq)
84 struct ii_url_request* iireq = _iireq;
85 PurpleImage *img;
86 const gchar* data;
87 size_t len;
89 #ifdef MXIT_DEBUG_COMMANDS
90 purple_debug_info(MXIT_PLUGIN_ID, "Inline Image returned from %s\n", iireq->url);
91 #endif
93 if (!purple_http_response_is_successful(response)) {
94 /* no reply from the WAP site */
95 purple_debug_error(MXIT_PLUGIN_ID, "Error downloading Inline Image from %s.\n", iireq->url);
96 goto done;
99 /* lets first see if we don't have the inline image already in cache */
100 if (g_hash_table_lookup(iireq->mx->session->inline_images, iireq->url)) {
101 /* inline image found in the cache, so we just ignore this reply */
102 goto done;
105 /* we now have the inline image, store a copy in the imagestore */
106 data = purple_http_response_get_data(response, &len);
107 img = purple_image_new_from_data(g_memdup(data, len), len);
108 g_hash_table_insert(iireq->mx->session->inline_images, iireq->url, img);
110 iireq->mx->flags |= PURPLE_MESSAGE_IMAGES;
112 done:
113 iireq->mx->img_count--;
114 if ((iireq->mx->img_count == 0) && (iireq->mx->converted)) {
116 * this was the last outstanding emoticon for this message,
117 * so we can now display it to the user.
119 mxit_show_message(iireq->mx);
122 g_free(iireq);
126 /*------------------------------------------------------------------------
127 * Return the command identifier of this MXit Command.
129 * @param cmd The MXit command <key,value> map
130 * @return The MXit command identifier
132 static MXitCommandType command_type(GHashTable* hash)
134 char* op;
135 char* type;
137 op = g_hash_table_lookup(hash, "op");
138 if (op) {
139 if ( strcmp(op, "cmd") == 0 ) {
140 type = g_hash_table_lookup(hash, "type");
141 if (type == NULL) /* no command provided */
142 return MXIT_CMD_UNKNOWN;
143 else if (strcmp(type, "clear") == 0) /* clear */
144 return MXIT_CMD_CLEAR;
145 else if (strcmp(type, "sendsms") == 0) /* send an SMS */
146 return MXIT_CMD_SENDSMS;
147 else if (strcmp(type, "reply") == 0) /* list of options */
148 return MXIT_CMD_REPLY;
149 else if (strcmp(type, "platreq") == 0) /* platform request */
150 return MXIT_CMD_PLATREQ;
151 else if (strcmp(type, "selc") == 0) /* select contact */
152 return MXIT_CMD_SELECTCONTACT;
154 else if (strcmp(op, "img") == 0) /* inline image */
155 return MXIT_CMD_IMAGE;
156 else if (strcmp(op, "csc") == 0) /* chat-screen config */
157 return MXIT_CMD_SCREENCONFIG;
158 else if (strcmp(op, "csi") == 0) /* chat-screen info */
159 return MXIT_CMD_SCREENINFO;
160 else if (strcmp(op, "is") == 0) /* image-strip */
161 return MXIT_CMD_IMAGESTRIP;
162 else if (strcmp(op, "tbl") == 0) /* table */
163 return MXIT_CMD_TABLE;
166 return MXIT_CMD_UNKNOWN;
170 /*------------------------------------------------------------------------
171 * Tokenize a MXit Command string into a <key,value> map.
173 * @param cmd The MXit command string
174 * @return The <key,value> hash-map, or NULL on error.
176 static GHashTable* command_tokenize(char* cmd)
178 GHashTable* hash = NULL;
179 gchar** parts;
180 int i = 0;
182 #ifdef MXIT_DEBUG_COMMANDS
183 purple_debug_info(MXIT_PLUGIN_ID, "command: '%s'\n", cmd);
184 #endif
186 /* explode the command into parts */
187 parts = g_strsplit(cmd, "|", 0);
189 hash = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free);
191 /* now break part into a key & value */
192 while (parts[i] != NULL) {
193 char* value;
195 value = strchr(parts[i], '='); /* find start of value */
196 if (value != NULL) {
197 *value = '\0';
198 value++;
201 #ifdef MXIT_DEBUG_COMMANDS
202 purple_debug_info(MXIT_PLUGIN_ID, " key='%s' value='%s'\n", parts[i], value);
203 #endif
205 g_hash_table_insert(hash, g_strdup(parts[i]), g_strdup(value));
207 i++;
210 g_strfreev(parts);
212 return hash;
216 /*------------------------------------------------------------------------
217 * Process a Clear MXit command.
218 * [::op=cmd|type=clear|clearmsgscreen=true|auto=true|id=12345:]
220 * @param session The MXit session object
221 * @param from The sender of the message.
222 * @param hash The MXit command <key,value> map
224 static void command_clear(struct MXitSession* session, const char* from, GHashTable* hash)
226 PurpleIMConversation *im;
227 char* clearmsgscreen;
229 im = purple_conversations_find_im_with_account(from, session->acc);
230 if (im == NULL) {
231 purple_debug_error(MXIT_PLUGIN_ID, _( "Conversation with '%s' not found\n" ), from);
232 return;
235 clearmsgscreen = g_hash_table_lookup(hash, "clearmsgscreen");
236 if ( (clearmsgscreen) && (strcmp(clearmsgscreen, "true") == 0) ) {
237 /* this is a command to clear the chat screen */
238 purple_conversation_clear_message_history(PURPLE_CONVERSATION(im));
243 /*------------------------------------------------------------------------
244 * Process a Reply MXit command.
245 * [::op=cmd|type=reply|replymsg=back|selmsg=b) Back|displaymsg=Processing|id=12345:]
246 * [::op=cmd|nm=rep|type=reply|replymsg=back|selmsg=b) Back|displaymsg=Processing|id=12345:]
248 * @param mx The received message data object
249 * @param hash The MXit command <key,value> map
251 static void command_reply(struct RXMsgData* mx, GHashTable* hash)
253 char* replymsg;
254 char* selmsg;
255 char* nm;
257 selmsg = g_hash_table_lookup(hash, "selmsg"); /* selection message */
258 replymsg = g_hash_table_lookup(hash, "replymsg"); /* reply message */
259 nm = g_hash_table_lookup(hash, "nm"); /* name parameter */
261 if ((selmsg == NULL) || (replymsg == NULL))
262 return; /* these parameters are required */
264 if (nm) { /* indicates response must be a structured response */
265 gchar* seltext = g_markup_escape_text(purple_url_decode(selmsg), -1);
266 gchar* replycmd = g_strdup_printf("type=reply|nm=%s|res=%s|err=0", nm, purple_url_decode(replymsg));
268 mxit_add_html_link( mx, replycmd, TRUE, seltext );
270 g_free(seltext);
271 g_free(replycmd);
273 else {
274 gchar* seltext = g_markup_escape_text(purple_url_decode(selmsg), -1);
276 mxit_add_html_link( mx, purple_url_decode(replymsg), FALSE, seltext );
278 g_free(seltext);
283 /*------------------------------------------------------------------------
284 * Process a PlatformRequest MXit command.
285 * [::op=cmd|type=platreq|selmsg=Upgrade MXit|dest=http%3a//m.mxit.com|id=12345:]
287 * @param hash The MXit command <key,value> map
288 * @param msg The message to display (as generated so far)
290 static void command_platformreq(GHashTable* hash, GString* msg)
292 gchar* text = NULL;
293 char* selmsg;
294 char* dest;
296 selmsg = g_hash_table_lookup(hash, "selmsg"); /* find the selection message */
297 if (selmsg && (strlen(selmsg) > 0)) {
298 text = g_markup_escape_text(purple_url_decode(selmsg), -1);
301 dest = g_hash_table_lookup(hash, "dest"); /* find the destination */
302 if (dest) {
303 g_string_append_printf(msg, "<a href=\"%s\">%s</a>", purple_url_decode(dest), (text) ? text : _( "Download" )); /* add link to display message */
306 g_free(text);
310 /*------------------------------------------------------------------------
311 * Process an inline image MXit command.
312 * [::op=img|dat=ASDF23408asdflkj2309flkjsadf%3d%3d|algn=1|w=120|h=12|t=100|replymsg=text:]
314 * @param mx The received message data object
315 * @param hash The MXit command <key,value> map
316 * @param msg The message to display (as generated so far)
318 static void command_image(struct RXMsgData* mx, GHashTable* hash, GString* msg)
320 const char* img;
321 const char* reply;
322 guchar* rawimg;
323 gsize rawimglen;
325 img = g_hash_table_lookup(hash, "dat");
326 if (img) {
327 PurpleImage *pimg;
328 guint pimg_id;
330 rawimg = purple_base64_decode(img, &rawimglen);
331 //purple_util_write_data_to_file_absolute("/tmp/mxitinline.png", (char*) rawimg, rawimglen);
332 pimg = purple_image_new_from_data(rawimg, rawimglen);
333 pimg_id = purple_image_store_add(pimg);
334 g_string_append_printf(msg, "<img src=\""
335 PURPLE_IMAGE_STORE_PROTOCOL "%u\">", pimg_id);
336 mx->flags |= PURPLE_MESSAGE_IMAGES;
338 else {
339 img = g_hash_table_lookup(hash, "src");
340 if (img) {
341 struct ii_url_request* iireq;
343 iireq = g_new0(struct ii_url_request,1);
344 iireq->url = g_strdup(purple_url_decode(img));
345 iireq->mx = mx;
347 g_string_append_printf(msg, "%s%s>", MXIT_II_TAG, iireq->url);
348 mx->got_img = TRUE;
350 /* lets first see if we don't have the inline image already in cache */
351 if (g_hash_table_lookup(mx->session->inline_images, iireq->url)) {
352 /* inline image found in the cache, so we do not have to request it from the web */
353 g_free(iireq);
355 else {
356 /* send the request for the inline image */
357 purple_debug_info(MXIT_PLUGIN_ID, "sending request for inline image '%s'\n", iireq->url);
359 purple_http_get(mx->session->con, mxit_cb_ii_returned, iireq, iireq->url);
360 mx->img_count++;
365 /* if this is a clickable image, show a click link */
366 reply = g_hash_table_lookup(hash, "replymsg");
367 if (reply) {
368 g_string_append_printf(msg, "\n");
369 mxit_add_html_link(mx, purple_url_decode(reply), FALSE, _( "click here" ));
374 /*------------------------------------------------------------------------
375 * Process an Imagestrip MXit command.
376 * [::op=is|nm=status|dat=iVBORw0KGgoAAAA%3d%3d|v=63398792426788|fw=8|fh=8|layer=0:]
378 * @param from The sender of the message.
379 * @param hash The MXit command <key,value> map
381 static void command_imagestrip(struct MXitSession* session, const char* from, GHashTable* hash)
383 const char* name;
384 const char* validator;
385 const char* tmp;
386 int width, height, layer;
388 purple_debug_info(MXIT_PLUGIN_ID, "ImageStrip received from %s\n", from);
390 /* image strip name */
391 name = g_hash_table_lookup(hash, "nm");
393 /* validator */
394 validator = g_hash_table_lookup(hash, "v");
396 if (!name || !validator)
397 return;
399 /* image data */
400 tmp = g_hash_table_lookup(hash, "dat");
401 if (tmp) {
402 guchar* rawimg;
403 gsize rawimglen;
404 char* dir;
405 char* escfrom;
406 char* escname;
407 char* escvalidator;
408 char* filename;
410 /* base64 decode the image data */
411 rawimg = purple_base64_decode(tmp, &rawimglen);
412 if (!rawimg)
413 return;
415 /* save it to a file */
416 dir = g_build_filename(purple_user_dir(), "mxit", "imagestrips", NULL);
417 purple_build_dir(dir, S_IRUSR | S_IWUSR | S_IXUSR); /* ensure directory exists */
419 escfrom = g_strdup(purple_escape_filename(from));
420 escname = g_strdup(purple_escape_filename(name));
421 escvalidator = g_strdup(purple_escape_filename(validator));
422 filename = g_strdup_printf("%s" G_DIR_SEPARATOR_S "%s-%s-%s.png", dir, escfrom, escname, escvalidator);
424 purple_util_write_data_to_file_absolute(filename, (char*) rawimg, rawimglen);
426 g_free(dir);
427 g_free(escfrom);
428 g_free(escname);
429 g_free(escvalidator);
430 g_free(filename);
433 tmp = g_hash_table_lookup(hash, "fw");
434 width = (tmp ? atoi(tmp) : 0);
436 tmp = g_hash_table_lookup(hash, "fh");
437 height = (tmp ? atoi(tmp) : 0);
439 tmp = g_hash_table_lookup(hash, "layer");
440 layer = (tmp ? atoi(tmp) : 0);
442 purple_debug_info(MXIT_PLUGIN_ID, "ImageStrip %s from %s: [w=%i h=%i l=%i validator=%s]\n", name, from, width, height, layer, validator);
446 /*------------------------------------------------------------------------
447 * Process a Chat-Screen-Info MXit command.
448 * [::op=csi:]
450 * @param session The MXit session object
451 * @param from The sender of the message.
453 static void command_screeninfo(struct MXitSession* session, const char* from)
455 char* response;
457 purple_debug_info(MXIT_PLUGIN_ID, "Chat Screen Info received from %s\n", from);
459 // TODO: Determine width, height, colors of chat-screen.
461 response = g_strdup_printf("::type=csi|res=bhvr,0;w,%i;h,%i;col,0.ffffffff,29.ff000000:", 300, 400);
463 /* send response back to MXit */
464 mxit_send_message( session, from, response, FALSE, TRUE );
466 g_free(response);
470 /*------------------------------------------------------------------------
471 * Process a Chat-Screen-Configure MXit command.
472 * [::op=csc|bhvr=|menu=<menu>|col=<colors>:]
473 * where:
474 * menu ::= <menuitem> { ";" <menuitem> }
475 * menuitem ::= { type "," <text> "," <name> "," <meta> }
476 * colors ::= <color> { ";" <color> }
477 * color ::= <colorid> "," <ARGB hex color>
479 * @param session The MXit session object
480 * @param from The sender of the message.
481 * @param hash The MXit command <key,value> map
483 static void command_screenconfig(struct MXitSession* session, const char* from, GHashTable* hash)
485 const char* tmp;
487 purple_debug_info(MXIT_PLUGIN_ID, "Chat Screen Configure received from %s\n", from);
489 /* Behaviour */
490 tmp = g_hash_table_lookup(hash, "bhvr");
491 if (tmp) {
492 purple_debug_info(MXIT_PLUGIN_ID, " behaviour = %s\n", tmp);
493 // TODO: Re-configure conversation screen.
496 /* Menu */
497 tmp = g_hash_table_lookup(hash, "menu");
498 if (tmp) {
499 purple_debug_info(MXIT_PLUGIN_ID, " menu = %s\n", tmp);
500 // TODO: Implement conversation-specific sub-menu.
503 /* Colours */
504 tmp = g_hash_table_lookup(hash, "col");
505 if (tmp) {
506 purple_debug_info(MXIT_PLUGIN_ID, " colours = %s\n", tmp);
507 // TODO: Re-configuration conversation colors.
512 /*------------------------------------------------------------------------
513 * Process a Table Markup MXit command.
515 * @param mx The received message data object
516 * @param hash The MXit command <key,value> map
518 static void command_table(struct RXMsgData* mx, GHashTable* hash)
520 const char* tmp;
521 const char* name;
522 int mode;
523 unsigned int nr_columns = 0, nr_rows = 0;
524 gchar** coldata;
525 unsigned int i, j;
527 /* table name */
528 name = g_hash_table_lookup(hash, "nm");
529 if (!name)
530 return;
532 /* number of columns */
533 tmp = g_hash_table_lookup(hash, "col");
534 nr_columns = (tmp ? atoi(tmp) : 0);
536 /* number of rows */
537 tmp = g_hash_table_lookup(hash, "row");
538 nr_rows = (tmp ? atoi(tmp) : 0);
540 /* mode */
541 tmp = g_hash_table_lookup(hash, "mode");
542 mode = (tmp ? atoi(tmp) : 0);
544 /* table data */
545 tmp = g_hash_table_lookup(hash, "d");
546 if (!tmp)
547 tmp = "";
549 coldata = g_strsplit(tmp, "~", 0); /* split into entries for each row & column */
551 if (g_strv_length(coldata) != (nr_rows * nr_columns)) {
552 purple_debug_info(MXIT_PLUGIN_ID, "Invalid table data: cols=%i rows=%i\n", nr_columns, nr_rows);
553 g_strfreev(coldata);
554 return;
557 purple_debug_info(MXIT_PLUGIN_ID, "Table %s from %s: [cols=%i rows=%i mode=%i]\n", name, mx->from, nr_columns, nr_rows, mode);
559 for (i = 0; i < nr_rows; i++) {
560 for (j = 0; j < nr_columns; j++) {
561 purple_debug_info(MXIT_PLUGIN_ID, " Row %i Column %i = %s\n", i, j, coldata[i*nr_columns + j]);
565 g_strfreev(coldata);
569 /*------------------------------------------------------------------------
570 * Process a received MXit Command message.
572 * @param mx The received message data object
573 * @param message The message text
574 * @return The length of the command
576 int mxit_parse_command(struct RXMsgData* mx, char* message)
578 GHashTable* hash = NULL;
579 char* start;
580 char* end;
582 /* ensure that this is really a command */
583 if ( ( message[0] != ':' ) || ( message[1] != ':' ) ) {
584 /* this is not a command */
585 return 0;
588 start = message + 2;
589 end = strstr(start, ":");
590 if (end) {
591 /* end of a command found */
592 *end = '\0'; /* terminate command string */
594 hash = command_tokenize(start); /* break into <key,value> pairs */
595 if (hash) {
596 MXitCommandType type = command_type(hash);
598 switch (type) {
599 case MXIT_CMD_CLEAR :
600 command_clear(mx->session, mx->from, hash);
601 break;
602 case MXIT_CMD_REPLY :
603 command_reply(mx, hash);
604 break;
605 case MXIT_CMD_PLATREQ :
606 command_platformreq(hash, mx->msg);
607 break;
608 case MXIT_CMD_IMAGE :
609 command_image(mx, hash, mx->msg);
610 break;
611 case MXIT_CMD_SCREENCONFIG :
612 command_screenconfig(mx->session, mx->from, hash);
613 break;
614 case MXIT_CMD_SCREENINFO :
615 command_screeninfo(mx->session, mx->from);
616 break;
617 case MXIT_CMD_IMAGESTRIP :
618 command_imagestrip(mx->session, mx->from, hash);
619 break;
620 case MXIT_CMD_TABLE :
621 command_table(mx, hash);
622 break;
623 default :
624 /* command unknown, or not currently supported */
625 break;
627 g_hash_table_destroy(hash);
629 *end = ':';
631 return end - message;
633 else {
634 return 0;