pidgin/pidgin
Clone
Summary
Browse
Changes
Graph
Merge to default to fix conflicts
2016-10-24, Mike Ruprecht
6dc8cae57962
Merge to default to fix conflicts
/*
* MXit Protocol libPurple Plugin
*
* -- MXit Forms & Commands --
*
* Andrew Victor <libpurple@mxit.com>
*
* (C) Copyright 2009 MXit Lifestyle (Pty) Ltd.
* <http://www.mxitlifestyle.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
*/
#include
"internal.h"
#include
"debug.h"
#include
"http.h"
#include
"image-store.h"
#include
"client.h"
#include
"mxit.h"
#include
"markup.h"
#include
"formcmds.h"
#undef MXIT_DEBUG_COMMANDS
/*
* the MXit Command identifiers
*/
typedef
enum
{
MXIT_CMD_UNKNOWN
=
0
,
/* Unknown command */
MXIT_CMD_CLEAR
,
/* Clear (clear) */
MXIT_CMD_SENDSMS
,
/* Send SMS (sendsms) */
MXIT_CMD_REPLY
,
/* Reply (reply) */
MXIT_CMD_PLATREQ
,
/* Platform Request (platreq) */
MXIT_CMD_SELECTCONTACT
,
/* Select Contact (selc) */
MXIT_CMD_IMAGE
,
/* Inline image (img) */
MXIT_CMD_SCREENCONFIG
,
/* Chat-screen config (csc) */
MXIT_CMD_SCREENINFO
,
/* Chat-screen info (csi) */
MXIT_CMD_IMAGESTRIP
,
/* Image Strip (is) */
MXIT_CMD_TABLE
/* Table (tbl) */
}
MXitCommandType
;
/* Chat-screen behaviours (bhvr) */
#define SCREEN_NO_HEADINGS 0x01
#define SCREEN_FULLSCREEN 0x02
#define SCREEN_AUTOCLEAR 0x04
#define SCREEN_NO_AUDIO 0x08
#define SCREEN_NO_MSGPREFIX 0x10
#define SCREEN_NOTIFY 0x20
#define SCREEN_PROGRESSBAR 0x40
/*
* object for an inline image request with an URL
*/
struct
ii_url_request
{
struct
RXMsgData
*
mx
;
char
*
url
;
};
/*------------------------------------------------------------------------
* Callback function invoked when an inline image request to a web site completes.
*/
static
void
mxit_cb_ii_returned
(
PurpleHttpConnection
*
http_conn
,
PurpleHttpResponse
*
response
,
gpointer
_iireq
)
{
struct
ii_url_request
*
iireq
=
_iireq
;
PurpleImage
*
img
;
const
gchar
*
data
;
size_t
len
;
#ifdef MXIT_DEBUG_COMMANDS
purple_debug_info
(
MXIT_PLUGIN_ID
,
"Inline Image returned from %s
\n
"
,
iireq
->
url
);
#endif
if
(
!
purple_http_response_is_successful
(
response
))
{
/* no reply from the WAP site */
purple_debug_error
(
MXIT_PLUGIN_ID
,
"Error downloading Inline Image from %s.
\n
"
,
iireq
->
url
);
goto
done
;
}
/* lets first see if we don't have the inline image already in cache */
if
(
g_hash_table_lookup
(
iireq
->
mx
->
session
->
inline_images
,
iireq
->
url
))
{
/* inline image found in the cache, so we just ignore this reply */
goto
done
;
}
/* we now have the inline image, store a copy in the imagestore */
data
=
purple_http_response_get_data
(
response
,
&
len
);
img
=
purple_image_new_from_data
(
g_memdup
(
data
,
len
),
len
);
g_hash_table_insert
(
iireq
->
mx
->
session
->
inline_images
,
iireq
->
url
,
img
);
iireq
->
mx
->
flags
|=
PURPLE_MESSAGE_IMAGES
;
done
:
iireq
->
mx
->
img_count
--
;
if
((
iireq
->
mx
->
img_count
==
0
)
&&
(
iireq
->
mx
->
converted
))
{
/*
* this was the last outstanding emoticon for this message,
* so we can now display it to the user.
*/
mxit_show_message
(
iireq
->
mx
);
}
g_free
(
iireq
);
}
/*------------------------------------------------------------------------
* Return the command identifier of this MXit Command.
*
* @param cmd The MXit command <key,value> map
* @return The MXit command identifier
*/
static
MXitCommandType
command_type
(
GHashTable
*
hash
)
{
char
*
op
;
char
*
type
;
op
=
g_hash_table_lookup
(
hash
,
"op"
);
if
(
op
)
{
if
(
strcmp
(
op
,
"cmd"
)
==
0
)
{
type
=
g_hash_table_lookup
(
hash
,
"type"
);
if
(
type
==
NULL
)
/* no command provided */
return
MXIT_CMD_UNKNOWN
;
else
if
(
strcmp
(
type
,
"clear"
)
==
0
)
/* clear */
return
MXIT_CMD_CLEAR
;
else
if
(
strcmp
(
type
,
"sendsms"
)
==
0
)
/* send an SMS */
return
MXIT_CMD_SENDSMS
;
else
if
(
strcmp
(
type
,
"reply"
)
==
0
)
/* list of options */
return
MXIT_CMD_REPLY
;
else
if
(
strcmp
(
type
,
"platreq"
)
==
0
)
/* platform request */
return
MXIT_CMD_PLATREQ
;
else
if
(
strcmp
(
type
,
"selc"
)
==
0
)
/* select contact */
return
MXIT_CMD_SELECTCONTACT
;
}
else
if
(
strcmp
(
op
,
"img"
)
==
0
)
/* inline image */
return
MXIT_CMD_IMAGE
;
else
if
(
strcmp
(
op
,
"csc"
)
==
0
)
/* chat-screen config */
return
MXIT_CMD_SCREENCONFIG
;
else
if
(
strcmp
(
op
,
"csi"
)
==
0
)
/* chat-screen info */
return
MXIT_CMD_SCREENINFO
;
else
if
(
strcmp
(
op
,
"is"
)
==
0
)
/* image-strip */
return
MXIT_CMD_IMAGESTRIP
;
else
if
(
strcmp
(
op
,
"tbl"
)
==
0
)
/* table */
return
MXIT_CMD_TABLE
;
}
return
MXIT_CMD_UNKNOWN
;
}
/*------------------------------------------------------------------------
* Tokenize a MXit Command string into a <key,value> map.
*
* @param cmd The MXit command string
* @return The <key,value> hash-map, or NULL on error.
*/
static
GHashTable
*
command_tokenize
(
char
*
cmd
)
{
GHashTable
*
hash
=
NULL
;
gchar
**
parts
;
int
i
=
0
;
#ifdef MXIT_DEBUG_COMMANDS
purple_debug_info
(
MXIT_PLUGIN_ID
,
"command: '%s'
\n
"
,
cmd
);
#endif
/* explode the command into parts */
parts
=
g_strsplit
(
cmd
,
"|"
,
0
);
hash
=
g_hash_table_new_full
(
g_str_hash
,
g_str_equal
,
g_free
,
g_free
);
/* now break part into a key & value */
while
(
parts
[
i
]
!=
NULL
)
{
char
*
value
;
value
=
strchr
(
parts
[
i
],
'='
);
/* find start of value */
if
(
value
!=
NULL
)
{
*
value
=
'\0'
;
value
++
;
}
#ifdef MXIT_DEBUG_COMMANDS
purple_debug_info
(
MXIT_PLUGIN_ID
,
" key='%s' value='%s'
\n
"
,
parts
[
i
],
value
);
#endif
g_hash_table_insert
(
hash
,
g_strdup
(
parts
[
i
]),
g_strdup
(
value
));
i
++
;
}
g_strfreev
(
parts
);
return
hash
;
}
/*------------------------------------------------------------------------
* Process a Clear MXit command.
* [::op=cmd|type=clear|clearmsgscreen=true|auto=true|id=12345:]
*
* @param session The MXit session object
* @param from The sender of the message.
* @param hash The MXit command <key,value> map
*/
static
void
command_clear
(
struct
MXitSession
*
session
,
const
char
*
from
,
GHashTable
*
hash
)
{
PurpleIMConversation
*
im
;
char
*
clearmsgscreen
;
im
=
purple_conversations_find_im_with_account
(
from
,
session
->
acc
);
if
(
im
==
NULL
)
{
purple_debug_error
(
MXIT_PLUGIN_ID
,
_
(
"Conversation with '%s' not found
\n
"
),
from
);
return
;
}
clearmsgscreen
=
g_hash_table_lookup
(
hash
,
"clearmsgscreen"
);
if
(
(
clearmsgscreen
)
&&
(
strcmp
(
clearmsgscreen
,
"true"
)
==
0
)
)
{
/* this is a command to clear the chat screen */
purple_conversation_clear_message_history
(
PURPLE_CONVERSATION
(
im
));
}
}
/*------------------------------------------------------------------------
* Process a Reply MXit command.
* [::op=cmd|type=reply|replymsg=back|selmsg=b) Back|displaymsg=Processing|id=12345:]
* [::op=cmd|nm=rep|type=reply|replymsg=back|selmsg=b) Back|displaymsg=Processing|id=12345:]
*
* @param mx The received message data object
* @param hash The MXit command <key,value> map
*/
static
void
command_reply
(
struct
RXMsgData
*
mx
,
GHashTable
*
hash
)
{
char
*
replymsg
;
char
*
selmsg
;
char
*
nm
;
selmsg
=
g_hash_table_lookup
(
hash
,
"selmsg"
);
/* selection message */
replymsg
=
g_hash_table_lookup
(
hash
,
"replymsg"
);
/* reply message */
nm
=
g_hash_table_lookup
(
hash
,
"nm"
);
/* name parameter */
if
((
selmsg
==
NULL
)
||
(
replymsg
==
NULL
))
return
;
/* these parameters are required */
if
(
nm
)
{
/* indicates response must be a structured response */
gchar
*
seltext
=
g_markup_escape_text
(
purple_url_decode
(
selmsg
),
-1
);
gchar
*
replycmd
=
g_strdup_printf
(
"type=reply|nm=%s|res=%s|err=0"
,
nm
,
purple_url_decode
(
replymsg
));
mxit_add_html_link
(
mx
,
replycmd
,
TRUE
,
seltext
);
g_free
(
seltext
);
g_free
(
replycmd
);
}
else
{
gchar
*
seltext
=
g_markup_escape_text
(
purple_url_decode
(
selmsg
),
-1
);
mxit_add_html_link
(
mx
,
purple_url_decode
(
replymsg
),
FALSE
,
seltext
);
g_free
(
seltext
);
}
}
/*------------------------------------------------------------------------
* Process a PlatformRequest MXit command.
* [::op=cmd|type=platreq|selmsg=Upgrade MXit|dest=http%3a//m.mxit.com|id=12345:]
*
* @param hash The MXit command <key,value> map
* @param msg The message to display (as generated so far)
*/
static
void
command_platformreq
(
GHashTable
*
hash
,
GString
*
msg
)
{
gchar
*
text
=
NULL
;
char
*
selmsg
;
char
*
dest
;
selmsg
=
g_hash_table_lookup
(
hash
,
"selmsg"
);
/* find the selection message */
if
(
selmsg
&&
(
strlen
(
selmsg
)
>
0
))
{
text
=
g_markup_escape_text
(
purple_url_decode
(
selmsg
),
-1
);
}
dest
=
g_hash_table_lookup
(
hash
,
"dest"
);
/* find the destination */
if
(
dest
)
{
g_string_append_printf
(
msg
,
"<a href=
\"
%s
\"
>%s</a>"
,
purple_url_decode
(
dest
),
(
text
)
?
text
:
_
(
"Download"
));
/* add link to display message */
}
g_free
(
text
);
}
/*------------------------------------------------------------------------
* Process an inline image MXit command.
* [::op=img|dat=ASDF23408asdflkj2309flkjsadf%3d%3d|algn=1|w=120|h=12|t=100|replymsg=text:]
*
* @param mx The received message data object
* @param hash The MXit command <key,value> map
* @param msg The message to display (as generated so far)
*/
static
void
command_image
(
struct
RXMsgData
*
mx
,
GHashTable
*
hash
,
GString
*
msg
)
{
const
char
*
img
;
const
char
*
reply
;
guchar
*
rawimg
;
gsize
rawimglen
;
img
=
g_hash_table_lookup
(
hash
,
"dat"
);
if
(
img
)
{
PurpleImage
*
pimg
;
guint
pimg_id
;
rawimg
=
purple_base64_decode
(
img
,
&
rawimglen
);
//purple_util_write_data_to_file_absolute("/tmp/mxitinline.png", (char*) rawimg, rawimglen);
pimg
=
purple_image_new_from_data
(
rawimg
,
rawimglen
);
pimg_id
=
purple_image_store_add
(
pimg
);
g_string_append_printf
(
msg
,
"<img src=
\"
"
PURPLE_IMAGE_STORE_PROTOCOL
"%u
\"
>"
,
pimg_id
);
mx
->
flags
|=
PURPLE_MESSAGE_IMAGES
;
}
else
{
img
=
g_hash_table_lookup
(
hash
,
"src"
);
if
(
img
)
{
struct
ii_url_request
*
iireq
;
iireq
=
g_new0
(
struct
ii_url_request
,
1
);
iireq
->
url
=
g_strdup
(
purple_url_decode
(
img
));
iireq
->
mx
=
mx
;
g_string_append_printf
(
msg
,
"%s%s>"
,
MXIT_II_TAG
,
iireq
->
url
);
mx
->
got_img
=
TRUE
;
/* lets first see if we don't have the inline image already in cache */
if
(
g_hash_table_lookup
(
mx
->
session
->
inline_images
,
iireq
->
url
))
{
/* inline image found in the cache, so we do not have to request it from the web */
g_free
(
iireq
);
}
else
{
/* send the request for the inline image */
purple_debug_info
(
MXIT_PLUGIN_ID
,
"sending request for inline image '%s'
\n
"
,
iireq
->
url
);
purple_http_get
(
mx
->
session
->
con
,
mxit_cb_ii_returned
,
iireq
,
iireq
->
url
);
mx
->
img_count
++
;
}
}
}
/* if this is a clickable image, show a click link */
reply
=
g_hash_table_lookup
(
hash
,
"replymsg"
);
if
(
reply
)
{
g_string_append_printf
(
msg
,
"
\n
"
);
mxit_add_html_link
(
mx
,
purple_url_decode
(
reply
),
FALSE
,
_
(
"click here"
));
}
}
/*------------------------------------------------------------------------
* Process an Imagestrip MXit command.
* [::op=is|nm=status|dat=iVBORw0KGgoAAAA%3d%3d|v=63398792426788|fw=8|fh=8|layer=0:]
*
* @param from The sender of the message.
* @param hash The MXit command <key,value> map
*/
static
void
command_imagestrip
(
struct
MXitSession
*
session
,
const
char
*
from
,
GHashTable
*
hash
)
{
const
char
*
name
;
const
char
*
validator
;
const
char
*
tmp
;
int
width
,
height
,
layer
;
purple_debug_info
(
MXIT_PLUGIN_ID
,
"ImageStrip received from %s
\n
"
,
from
);
/* image strip name */
name
=
g_hash_table_lookup
(
hash
,
"nm"
);
/* validator */
validator
=
g_hash_table_lookup
(
hash
,
"v"
);
if
(
!
name
||
!
validator
)
return
;
/* image data */
tmp
=
g_hash_table_lookup
(
hash
,
"dat"
);
if
(
tmp
)
{
guchar
*
rawimg
;
gsize
rawimglen
;
char
*
dir
;
char
*
escfrom
;
char
*
escname
;
char
*
escvalidator
;
char
*
filename
;
/* base64 decode the image data */
rawimg
=
purple_base64_decode
(
tmp
,
&
rawimglen
);
if
(
!
rawimg
)
return
;
/* save it to a file */
dir
=
g_build_filename
(
purple_user_dir
(),
"mxit"
,
"imagestrips"
,
NULL
);
purple_build_dir
(
dir
,
S_IRUSR
|
S_IWUSR
|
S_IXUSR
);
/* ensure directory exists */
escfrom
=
g_strdup
(
purple_escape_filename
(
from
));
escname
=
g_strdup
(
purple_escape_filename
(
name
));
escvalidator
=
g_strdup
(
purple_escape_filename
(
validator
));
filename
=
g_strdup_printf
(
"%s"
G_DIR_SEPARATOR_S
"%s-%s-%s.png"
,
dir
,
escfrom
,
escname
,
escvalidator
);
purple_util_write_data_to_file_absolute
(
filename
,
(
char
*
)
rawimg
,
rawimglen
);
g_free
(
dir
);
g_free
(
escfrom
);
g_free
(
escname
);
g_free
(
escvalidator
);
g_free
(
filename
);
}
tmp
=
g_hash_table_lookup
(
hash
,
"fw"
);
width
=
(
tmp
?
atoi
(
tmp
)
:
0
);
tmp
=
g_hash_table_lookup
(
hash
,
"fh"
);
height
=
(
tmp
?
atoi
(
tmp
)
:
0
);
tmp
=
g_hash_table_lookup
(
hash
,
"layer"
);
layer
=
(
tmp
?
atoi
(
tmp
)
:
0
);
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
);
}
/*------------------------------------------------------------------------
* Process a Chat-Screen-Info MXit command.
* [::op=csi:]
*
* @param session The MXit session object
* @param from The sender of the message.
*/
static
void
command_screeninfo
(
struct
MXitSession
*
session
,
const
char
*
from
)
{
char
*
response
;
purple_debug_info
(
MXIT_PLUGIN_ID
,
"Chat Screen Info received from %s
\n
"
,
from
);
// TODO: Determine width, height, colors of chat-screen.
response
=
g_strdup_printf
(
"::type=csi|res=bhvr,0;w,%i;h,%i;col,0.ffffffff,29.ff000000:"
,
300
,
400
);
/* send response back to MXit */
mxit_send_message
(
session
,
from
,
response
,
FALSE
,
TRUE
);
g_free
(
response
);
}
/*------------------------------------------------------------------------
* Process a Chat-Screen-Configure MXit command.
* [::op=csc|bhvr=|menu=<menu>|col=<colors>:]
* where:
* menu ::= <menuitem> { ";" <menuitem> }
* menuitem ::= { type "," <text> "," <name> "," <meta> }
* colors ::= <color> { ";" <color> }
* color ::= <colorid> "," <ARGB hex color>
*
* @param session The MXit session object
* @param from The sender of the message.
* @param hash The MXit command <key,value> map
*/
static
void
command_screenconfig
(
struct
MXitSession
*
session
,
const
char
*
from
,
GHashTable
*
hash
)
{
const
char
*
tmp
;
purple_debug_info
(
MXIT_PLUGIN_ID
,
"Chat Screen Configure received from %s
\n
"
,
from
);
/* Behaviour */
tmp
=
g_hash_table_lookup
(
hash
,
"bhvr"
);
if
(
tmp
)
{
purple_debug_info
(
MXIT_PLUGIN_ID
,
" behaviour = %s
\n
"
,
tmp
);
// TODO: Re-configure conversation screen.
}
/* Menu */
tmp
=
g_hash_table_lookup
(
hash
,
"menu"
);
if
(
tmp
)
{
purple_debug_info
(
MXIT_PLUGIN_ID
,
" menu = %s
\n
"
,
tmp
);
// TODO: Implement conversation-specific sub-menu.
}
/* Colours */
tmp
=
g_hash_table_lookup
(
hash
,
"col"
);
if
(
tmp
)
{
purple_debug_info
(
MXIT_PLUGIN_ID
,
" colours = %s
\n
"
,
tmp
);
// TODO: Re-configuration conversation colors.
}
}
/*------------------------------------------------------------------------
* Process a Table Markup MXit command.
*
* @param mx The received message data object
* @param hash The MXit command <key,value> map
*/
static
void
command_table
(
struct
RXMsgData
*
mx
,
GHashTable
*
hash
)
{
const
char
*
tmp
;
const
char
*
name
;
int
mode
;
unsigned
int
nr_columns
=
0
,
nr_rows
=
0
;
gchar
**
coldata
;
unsigned
int
i
,
j
;
/* table name */
name
=
g_hash_table_lookup
(
hash
,
"nm"
);
if
(
!
name
)
return
;
/* number of columns */
tmp
=
g_hash_table_lookup
(
hash
,
"col"
);
nr_columns
=
(
tmp
?
atoi
(
tmp
)
:
0
);
/* number of rows */
tmp
=
g_hash_table_lookup
(
hash
,
"row"
);
nr_rows
=
(
tmp
?
atoi
(
tmp
)
:
0
);
/* mode */
tmp
=
g_hash_table_lookup
(
hash
,
"mode"
);
mode
=
(
tmp
?
atoi
(
tmp
)
:
0
);
/* table data */
tmp
=
g_hash_table_lookup
(
hash
,
"d"
);
if
(
!
tmp
)
tmp
=
""
;
coldata
=
g_strsplit
(
tmp
,
"~"
,
0
);
/* split into entries for each row & column */
if
(
g_strv_length
(
coldata
)
!=
(
nr_rows
*
nr_columns
))
{
purple_debug_info
(
MXIT_PLUGIN_ID
,
"Invalid table data: cols=%i rows=%i
\n
"
,
nr_columns
,
nr_rows
);
g_strfreev
(
coldata
);
return
;
}
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
);
for
(
i
=
0
;
i
<
nr_rows
;
i
++
)
{
for
(
j
=
0
;
j
<
nr_columns
;
j
++
)
{
purple_debug_info
(
MXIT_PLUGIN_ID
,
" Row %i Column %i = %s
\n
"
,
i
,
j
,
coldata
[
i
*
nr_columns
+
j
]);
}
}
g_strfreev
(
coldata
);
}
/*------------------------------------------------------------------------
* Process a received MXit Command message.
*
* @param mx The received message data object
* @param message The message text
* @return The length of the command
*/
int
mxit_parse_command
(
struct
RXMsgData
*
mx
,
char
*
message
)
{
GHashTable
*
hash
=
NULL
;
char
*
start
;
char
*
end
;
/* ensure that this is really a command */
if
(
(
message
[
0
]
!=
':'
)
||
(
message
[
1
]
!=
':'
)
)
{
/* this is not a command */
return
0
;
}
start
=
message
+
2
;
end
=
strstr
(
start
,
":"
);
if
(
end
)
{
/* end of a command found */
*
end
=
'\0'
;
/* terminate command string */
hash
=
command_tokenize
(
start
);
/* break into <key,value> pairs */
if
(
hash
)
{
MXitCommandType
type
=
command_type
(
hash
);
switch
(
type
)
{
case
MXIT_CMD_CLEAR
:
command_clear
(
mx
->
session
,
mx
->
from
,
hash
);
break
;
case
MXIT_CMD_REPLY
:
command_reply
(
mx
,
hash
);
break
;
case
MXIT_CMD_PLATREQ
:
command_platformreq
(
hash
,
mx
->
msg
);
break
;
case
MXIT_CMD_IMAGE
:
command_image
(
mx
,
hash
,
mx
->
msg
);
break
;
case
MXIT_CMD_SCREENCONFIG
:
command_screenconfig
(
mx
->
session
,
mx
->
from
,
hash
);
break
;
case
MXIT_CMD_SCREENINFO
:
command_screeninfo
(
mx
->
session
,
mx
->
from
);
break
;
case
MXIT_CMD_IMAGESTRIP
:
command_imagestrip
(
mx
->
session
,
mx
->
from
,
hash
);
break
;
case
MXIT_CMD_TABLE
:
command_table
(
mx
,
hash
);
break
;
default
:
/* command unknown, or not currently supported */
break
;
}
g_hash_table_destroy
(
hash
);
}
*
end
=
':'
;
return
end
-
message
;
}
else
{
return
0
;
}
}