pidgin/pidgin
Clone
Summary
Browse
Changes
Graph
Bump the version for release
release-2.x.y
v2.10.12
2015-12-31, Gary Kramlich
2ac1dcb9eb2e
Bump the version for release
/**
* @file msg.c Message functions
*
* purple
*
* Purple is the legal property of its developers, whose names are too numerous
* to list here. Please refer to the COPYRIGHT file distributed with this
* source distribution.
*
* 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
"msn.h"
#include
"msg.h"
#include
"msnutils.h"
#include
"slpmsg.h"
#include
"slpmsg_part.h"
MsnMessage
*
msn_message_new
(
MsnMsgType
type
)
{
MsnMessage
*
msg
;
msg
=
g_new0
(
MsnMessage
,
1
);
msg
->
type
=
type
;
if
(
purple_debug_is_verbose
())
purple_debug_info
(
"msn"
,
"message new (%p)(%d)
\n
"
,
msg
,
type
);
msg
->
header_table
=
g_hash_table_new_full
(
g_str_hash
,
g_str_equal
,
g_free
,
g_free
);
msn_message_ref
(
msg
);
return
msg
;
}
/**
* Destroys a message.
*
* @param msg The message to destroy.
*/
static
void
msn_message_destroy
(
MsnMessage
*
msg
)
{
g_return_if_fail
(
msg
!=
NULL
);
if
(
purple_debug_is_verbose
())
purple_debug_info
(
"msn"
,
"message destroy (%p)
\n
"
,
msg
);
g_free
(
msg
->
remote_user
);
g_free
(
msg
->
body
);
g_free
(
msg
->
content_type
);
g_free
(
msg
->
charset
);
g_hash_table_destroy
(
msg
->
header_table
);
g_list_free
(
msg
->
header_list
);
if
(
msg
->
part
)
msn_slpmsgpart_unref
(
msg
->
part
);
g_free
(
msg
);
}
MsnMessage
*
msn_message_ref
(
MsnMessage
*
msg
)
{
g_return_val_if_fail
(
msg
!=
NULL
,
NULL
);
msg
->
ref_count
++
;
if
(
purple_debug_is_verbose
())
purple_debug_info
(
"msn"
,
"message ref (%p)[%u]
\n
"
,
msg
,
msg
->
ref_count
);
return
msg
;
}
void
msn_message_unref
(
MsnMessage
*
msg
)
{
g_return_if_fail
(
msg
!=
NULL
);
g_return_if_fail
(
msg
->
ref_count
>
0
);
msg
->
ref_count
--
;
if
(
purple_debug_is_verbose
())
purple_debug_info
(
"msn"
,
"message unref (%p)[%u]
\n
"
,
msg
,
msg
->
ref_count
);
if
(
msg
->
ref_count
==
0
)
msn_message_destroy
(
msg
);
}
MsnMessage
*
msn_message_new_plain
(
const
char
*
message
)
{
MsnMessage
*
msg
;
char
*
message_cr
;
msg
=
msn_message_new
(
MSN_MSG_TEXT
);
msg
->
retries
=
1
;
msn_message_set_header
(
msg
,
"User-Agent"
,
PACKAGE_NAME
"/"
VERSION
);
msn_message_set_content_type
(
msg
,
"text/plain"
);
msn_message_set_charset
(
msg
,
"UTF-8"
);
msn_message_set_flag
(
msg
,
'A'
);
msn_message_set_header
(
msg
,
"X-MMS-IM-Format"
,
"FN=Segoe%20UI; EF=; CO=0; CS=1;PF=0"
);
message_cr
=
purple_str_add_cr
(
message
);
msn_message_set_bin_data
(
msg
,
message_cr
,
strlen
(
message_cr
));
g_free
(
message_cr
);
return
msg
;
}
MsnMessage
*
msn_message_new_msnslp
(
void
)
{
MsnMessage
*
msg
;
msg
=
msn_message_new
(
MSN_MSG_SLP
);
msn_message_set_header
(
msg
,
"User-Agent"
,
NULL
);
msn_message_set_flag
(
msg
,
'D'
);
msn_message_set_content_type
(
msg
,
"application/x-msnmsgrp2p"
);
return
msg
;
}
MsnMessage
*
msn_message_new_nudge
(
void
)
{
MsnMessage
*
msg
;
msg
=
msn_message_new
(
MSN_MSG_NUDGE
);
msn_message_set_content_type
(
msg
,
"text/x-msnmsgr-datacast"
);
msn_message_set_flag
(
msg
,
'N'
);
msn_message_set_bin_data
(
msg
,
"ID: 1
\r\n
"
,
7
);
return
msg
;
}
void
msn_message_parse_payload
(
MsnMessage
*
msg
,
const
char
*
payload
,
size_t
payload_len
,
const
char
*
line_dem
,
const
char
*
body_dem
)
{
char
*
tmp_base
,
*
tmp
;
const
char
*
content_type
;
char
*
end
;
char
**
elems
,
**
cur
,
**
tokens
;
g_return_if_fail
(
payload
!=
NULL
);
tmp_base
=
tmp
=
g_malloc
(
payload_len
+
1
);
memcpy
(
tmp_base
,
payload
,
payload_len
);
tmp_base
[
payload_len
]
=
'\0'
;
/* Find the end of the headers */
end
=
strstr
(
tmp
,
body_dem
);
/* TODO? some clients use \r delimiters instead of \r\n, the official client
* doesn't send such messages, but does handle receiving them. We'll just
* avoid crashing for now */
if
(
end
==
NULL
)
{
g_free
(
tmp_base
);
g_return_if_reached
();
}
/* NUL-terminate the end of the headers - it'll get skipped over below */
*
end
=
'\0'
;
/* Split the headers and parse each one */
elems
=
g_strsplit
(
tmp
,
line_dem
,
0
);
for
(
cur
=
elems
;
*
cur
!=
NULL
;
cur
++
)
{
const
char
*
key
,
*
value
;
/* If this line starts with whitespace, it's been folded from the
previous line and won't have ':'. */
if
((
**
cur
==
' '
)
||
(
**
cur
==
'\t'
))
{
tokens
=
g_strsplit
(
g_strchug
(
*
cur
),
"=
\"
"
,
2
);
key
=
tokens
[
0
];
value
=
tokens
[
1
];
/* The only one I care about is 'boundary' (which is folded from
the key 'Content-Type'), so only process that. */
if
(
!
strcmp
(
key
,
"boundary"
)
&&
value
)
{
char
*
end
=
strchr
(
value
,
'\"'
);
if
(
end
)
{
*
end
=
'\0'
;
msn_message_set_header
(
msg
,
key
,
value
);
}
}
g_strfreev
(
tokens
);
continue
;
}
tokens
=
g_strsplit
(
*
cur
,
": "
,
2
);
key
=
tokens
[
0
];
value
=
tokens
[
1
];
if
(
!
strcmp
(
key
,
"MIME-Version"
))
{
/* Ignore MIME-Version header */
}
else
if
(
!
strcmp
(
key
,
"Content-Type"
))
{
char
*
charset
,
*
c
;
if
(
value
&&
(
c
=
strchr
(
value
,
';'
))
!=
NULL
)
{
if
((
charset
=
strchr
(
c
,
'='
))
!=
NULL
)
{
charset
++
;
msn_message_set_charset
(
msg
,
charset
);
}
*
c
=
'\0'
;
}
msn_message_set_content_type
(
msg
,
value
);
}
else
{
msn_message_set_header
(
msg
,
key
,
value
);
}
g_strfreev
(
tokens
);
}
g_strfreev
(
elems
);
/* Proceed to the end of the "\r\n\r\n" */
tmp
=
end
+
strlen
(
body_dem
);
/* Now we *should* be at the body. */
content_type
=
msn_message_get_content_type
(
msg
);
if
(
payload_len
-
(
tmp
-
tmp_base
)
>
0
)
{
msg
->
body_len
=
payload_len
-
(
tmp
-
tmp_base
);
g_free
(
msg
->
body
);
msg
->
body
=
g_malloc
(
msg
->
body_len
+
1
);
memcpy
(
msg
->
body
,
tmp
,
msg
->
body_len
);
msg
->
body
[
msg
->
body_len
]
=
'\0'
;
}
if
(
msg
->
body
&&
content_type
&&
purple_str_has_prefix
(
content_type
,
"text/"
))
{
char
*
body
=
NULL
;
if
(
msg
->
charset
==
NULL
||
g_str_equal
(
msg
->
charset
,
"UTF-8"
))
{
/* Charset is UTF-8 */
if
(
!
g_utf8_validate
(
msg
->
body
,
msg
->
body_len
,
NULL
))
{
purple_debug_warning
(
"msn"
,
"Message contains invalid "
"UTF-8. Attempting to salvage.
\n
"
);
body
=
purple_utf8_salvage
(
msg
->
body
);
payload_len
=
strlen
(
body
);
}
}
else
{
/* Charset is something other than UTF-8 */
GError
*
err
=
NULL
;
body
=
g_convert
(
msg
->
body
,
msg
->
body_len
,
"UTF-8"
,
msg
->
charset
,
NULL
,
&
payload_len
,
&
err
);
if
(
!
body
||
err
)
{
purple_debug_warning
(
"msn"
,
"Unable to convert message from "
"%s to UTF-8: %s
\n
"
,
msg
->
charset
,
err
?
err
->
message
:
"Unknown error"
);
if
(
err
)
g_error_free
(
err
);
/* Fallback to ISO-8859-1 */
g_free
(
body
);
body
=
g_convert
(
msg
->
body
,
msg
->
body_len
,
"UTF-8"
,
"ISO-8859-1"
,
NULL
,
&
payload_len
,
NULL
);
if
(
!
body
)
{
g_free
(
msg
->
body
);
msg
->
body
=
NULL
;
msg
->
body_len
=
0
;
}
}
}
if
(
body
)
{
g_free
(
msg
->
body
);
msg
->
body
=
body
;
msg
->
body_len
=
payload_len
;
msn_message_set_charset
(
msg
,
"UTF-8"
);
}
}
g_free
(
tmp_base
);
}
MsnMessage
*
msn_message_new_from_cmd
(
MsnSession
*
session
,
MsnCommand
*
cmd
)
{
MsnMessage
*
msg
;
g_return_val_if_fail
(
cmd
!=
NULL
,
NULL
);
msg
=
msn_message_new
(
MSN_MSG_UNKNOWN
);
msg
->
remote_user
=
g_strdup
(
cmd
->
params
[
0
]);
/* msg->size = atoi(cmd->params[2]); */
msg
->
cmd
=
cmd
;
return
msg
;
}
char
*
msn_message_gen_payload
(
MsnMessage
*
msg
,
size_t
*
ret_size
)
{
GList
*
l
;
char
*
n
,
*
base
,
*
end
;
int
len
;
size_t
body_len
=
0
;
const
void
*
body
;
g_return_val_if_fail
(
msg
!=
NULL
,
NULL
);
len
=
MSN_BUF_LEN
;
base
=
n
=
end
=
g_malloc
(
len
+
1
);
end
+=
len
;
/* Standard header. */
if
(
msg
->
charset
==
NULL
)
{
g_snprintf
(
n
,
len
,
"MIME-Version: 1.0
\r\n
"
"Content-Type: %s
\r\n
"
,
msg
->
content_type
);
}
else
{
g_snprintf
(
n
,
len
,
"MIME-Version: 1.0
\r\n
"
"Content-Type: %s; charset=%s
\r\n
"
,
msg
->
content_type
,
msg
->
charset
);
}
n
+=
strlen
(
n
);
for
(
l
=
msg
->
header_list
;
l
!=
NULL
;
l
=
l
->
next
)
{
const
char
*
key
;
const
char
*
value
;
key
=
l
->
data
;
value
=
msn_message_get_header_value
(
msg
,
key
);
g_snprintf
(
n
,
end
-
n
,
"%s: %s
\r\n
"
,
key
,
value
);
n
+=
strlen
(
n
);
}
if
((
end
-
n
)
>
2
)
n
+=
g_strlcpy
(
n
,
"
\r\n
"
,
end
-
n
);
body
=
msn_message_get_bin_data
(
msg
,
&
body_len
);
if
(
body
!=
NULL
&&
(
end
-
n
)
>
(
gssize
)
body_len
)
{
memcpy
(
n
,
body
,
body_len
);
n
+=
body_len
;
*
n
=
'\0'
;
}
if
(
ret_size
!=
NULL
)
{
*
ret_size
=
n
-
base
;
if
(
*
ret_size
>
1664
)
*
ret_size
=
1664
;
}
return
base
;
}
void
msn_message_set_flag
(
MsnMessage
*
msg
,
char
flag
)
{
g_return_if_fail
(
msg
!=
NULL
);
g_return_if_fail
(
flag
!=
0
);
msg
->
flag
=
flag
;
}
char
msn_message_get_flag
(
const
MsnMessage
*
msg
)
{
g_return_val_if_fail
(
msg
!=
NULL
,
0
);
return
msg
->
flag
;
}
void
msn_message_set_bin_data
(
MsnMessage
*
msg
,
const
void
*
data
,
size_t
len
)
{
g_return_if_fail
(
msg
!=
NULL
);
/* There is no need to waste memory on data we cannot send anyway */
if
(
len
>
1664
)
len
=
1664
;
if
(
msg
->
body
!=
NULL
)
g_free
(
msg
->
body
);
if
(
data
!=
NULL
&&
len
>
0
)
{
msg
->
body
=
g_malloc
(
len
+
1
);
memcpy
(
msg
->
body
,
data
,
len
);
msg
->
body
[
len
]
=
'\0'
;
msg
->
body_len
=
len
;
}
else
{
msg
->
body
=
NULL
;
msg
->
body_len
=
0
;
}
}
const
void
*
msn_message_get_bin_data
(
const
MsnMessage
*
msg
,
size_t
*
len
)
{
g_return_val_if_fail
(
msg
!=
NULL
,
NULL
);
if
(
len
)
*
len
=
msg
->
body_len
;
return
msg
->
body
;
}
void
msn_message_set_content_type
(
MsnMessage
*
msg
,
const
char
*
type
)
{
g_return_if_fail
(
msg
!=
NULL
);
g_free
(
msg
->
content_type
);
msg
->
content_type
=
g_strdup
(
type
);
}
const
char
*
msn_message_get_content_type
(
const
MsnMessage
*
msg
)
{
g_return_val_if_fail
(
msg
!=
NULL
,
NULL
);
return
msg
->
content_type
;
}
void
msn_message_set_charset
(
MsnMessage
*
msg
,
const
char
*
charset
)
{
g_return_if_fail
(
msg
!=
NULL
);
g_free
(
msg
->
charset
);
msg
->
charset
=
g_strdup
(
charset
);
}
const
char
*
msn_message_get_charset
(
const
MsnMessage
*
msg
)
{
g_return_val_if_fail
(
msg
!=
NULL
,
NULL
);
return
msg
->
charset
;
}
void
msn_message_set_header
(
MsnMessage
*
msg
,
const
char
*
name
,
const
char
*
value
)
{
const
char
*
temp
;
char
*
new_name
;
g_return_if_fail
(
msg
!=
NULL
);
g_return_if_fail
(
name
!=
NULL
);
temp
=
msn_message_get_header_value
(
msg
,
name
);
if
(
value
==
NULL
)
{
if
(
temp
!=
NULL
)
{
GList
*
l
;
for
(
l
=
msg
->
header_list
;
l
!=
NULL
;
l
=
l
->
next
)
{
if
(
!
g_ascii_strcasecmp
(
l
->
data
,
name
))
{
msg
->
header_list
=
g_list_remove
(
msg
->
header_list
,
l
->
data
);
break
;
}
}
g_hash_table_remove
(
msg
->
header_table
,
name
);
}
return
;
}
new_name
=
g_strdup
(
name
);
g_hash_table_insert
(
msg
->
header_table
,
new_name
,
g_strdup
(
value
));
if
(
temp
==
NULL
)
msg
->
header_list
=
g_list_append
(
msg
->
header_list
,
new_name
);
}
const
char
*
msn_message_get_header_value
(
const
MsnMessage
*
msg
,
const
char
*
name
)
{
g_return_val_if_fail
(
msg
!=
NULL
,
NULL
);
g_return_val_if_fail
(
name
!=
NULL
,
NULL
);
return
g_hash_table_lookup
(
msg
->
header_table
,
name
);
}
GHashTable
*
msn_message_get_hashtable_from_body
(
const
MsnMessage
*
msg
)
{
GHashTable
*
table
;
size_t
body_len
;
const
char
*
body
;
char
**
elems
,
**
cur
,
**
tokens
,
*
body_str
;
g_return_val_if_fail
(
msg
!=
NULL
,
NULL
);
table
=
g_hash_table_new_full
(
g_str_hash
,
g_str_equal
,
g_free
,
g_free
);
body
=
msn_message_get_bin_data
(
msg
,
&
body_len
);
g_return_val_if_fail
(
body
!=
NULL
,
NULL
);
body_str
=
g_strndup
(
body
,
body_len
);
elems
=
g_strsplit
(
body_str
,
"
\r\n
"
,
0
);
g_free
(
body_str
);
for
(
cur
=
elems
;
*
cur
!=
NULL
;
cur
++
)
{
if
(
**
cur
==
'\0'
)
break
;
tokens
=
g_strsplit
(
*
cur
,
": "
,
2
);
if
(
tokens
[
0
]
!=
NULL
&&
tokens
[
1
]
!=
NULL
)
{
g_hash_table_insert
(
table
,
tokens
[
0
],
tokens
[
1
]);
g_free
(
tokens
);
}
else
g_strfreev
(
tokens
);
}
g_strfreev
(
elems
);
return
table
;
}
char
*
msn_message_to_string
(
MsnMessage
*
msg
)
{
size_t
body_len
;
const
char
*
body
;
g_return_val_if_fail
(
msg
!=
NULL
,
NULL
);
g_return_val_if_fail
(
msg
->
type
==
MSN_MSG_TEXT
,
NULL
);
body
=
msn_message_get_bin_data
(
msg
,
&
body_len
);
return
g_strndup
(
body
,
body_len
);
}
void
msn_message_show_readable
(
MsnMessage
*
msg
,
const
char
*
info
,
gboolean
text_body
)
{
GString
*
str
;
size_t
body_len
;
const
char
*
body
;
GList
*
l
;
g_return_if_fail
(
msg
!=
NULL
);
str
=
g_string_new
(
NULL
);
/* Standard header. */
if
(
msg
->
charset
==
NULL
)
{
g_string_append_printf
(
str
,
"MIME-Version: 1.0
\r\n
"
"Content-Type: %s
\r\n
"
,
msg
->
content_type
);
}
else
{
g_string_append_printf
(
str
,
"MIME-Version: 1.0
\r\n
"
"Content-Type: %s; charset=%s
\r\n
"
,
msg
->
content_type
,
msg
->
charset
);
}
for
(
l
=
msg
->
header_list
;
l
;
l
=
l
->
next
)
{
char
*
key
;
const
char
*
value
;
key
=
l
->
data
;
value
=
msn_message_get_header_value
(
msg
,
key
);
g_string_append_printf
(
str
,
"%s: %s
\r\n
"
,
key
,
value
);
}
g_string_append
(
str
,
"
\r\n
"
);
body
=
msn_message_get_bin_data
(
msg
,
&
body_len
);
if
(
body
!=
NULL
)
{
if
(
msg
->
type
==
MSN_MSG_TEXT
)
{
g_string_append_len
(
str
,
body
,
body_len
);
g_string_append
(
str
,
"
\r\n
"
);
}
else
{
size_t
i
;
for
(
i
=
0
;
i
<
body_len
;
i
++
,
body
++
)
{
g_string_append_printf
(
str
,
"%02x "
,
(
unsigned
char
)
*
body
);
if
(
i
%
16
==
0
&&
i
!=
0
)
g_string_append_c
(
str
,
'\n'
);
}
g_string_append_c
(
str
,
'\n'
);
}
}
purple_debug_info
(
"msn"
,
"Message %s:
\n
{%s}
\n
"
,
info
,
str
->
str
);
g_string_free
(
str
,
TRUE
);
}
/**************************************************************************
* Message Handlers
**************************************************************************/
void
msn_plain_msg
(
MsnCmdProc
*
cmdproc
,
MsnMessage
*
msg
)
{
PurpleConnection
*
gc
;
const
char
*
body
;
char
*
body_enc
;
char
*
body_final
;
size_t
body_len
;
const
char
*
passport
;
const
char
*
value
;
gc
=
cmdproc
->
session
->
account
->
gc
;
body
=
msn_message_get_bin_data
(
msg
,
&
body_len
);
body_enc
=
g_markup_escape_text
(
body
,
body_len
);
passport
=
msg
->
remote_user
;
if
(
!
strcmp
(
passport
,
"messenger@microsoft.com"
)
&&
strstr
(
body
,
"immediate security update"
))
{
return
;
}
#if 0
if ((value = msn_message_get_header_value(msg, "User-Agent")) != NULL)
{
purple_debug_misc("msn", "User-Agent = '%s'\n", value);
}
#endif
if
((
value
=
msn_message_get_header_value
(
msg
,
"X-MMS-IM-Format"
))
!=
NULL
)
{
char
*
pre
,
*
post
;
msn_parse_format
(
value
,
&
pre
,
&
post
);
body_final
=
g_strdup_printf
(
"%s%s%s"
,
pre
?
pre
:
""
,
body_enc
?
body_enc
:
""
,
post
?
post
:
""
);
g_free
(
pre
);
g_free
(
post
);
g_free
(
body_enc
);
}
else
{
body_final
=
body_enc
;
}
if
(
cmdproc
->
servconn
->
type
==
MSN_SERVCONN_SB
)
{
MsnSwitchBoard
*
swboard
=
cmdproc
->
data
;
swboard
->
flag
|=
MSN_SB_FLAG_IM
;
if
(
swboard
->
current_users
>
1
||
((
swboard
->
conv
!=
NULL
)
&&
purple_conversation_get_type
(
swboard
->
conv
)
==
PURPLE_CONV_TYPE_CHAT
))
{
/* If current_users is always ok as it should then there is no need to
* check if this is a chat. */
if
(
swboard
->
current_users
<=
1
)
purple_debug_misc
(
"msn"
,
"plain_msg: current_users(%d)
\n
"
,
swboard
->
current_users
);
serv_got_chat_in
(
gc
,
swboard
->
chat_id
,
passport
,
0
,
body_final
,
time
(
NULL
));
if
(
swboard
->
conv
==
NULL
)
{
swboard
->
conv
=
purple_find_chat
(
gc
,
swboard
->
chat_id
);
swboard
->
flag
|=
MSN_SB_FLAG_IM
;
}
}
else
if
(
!
g_str_equal
(
passport
,
purple_account_get_username
(
gc
->
account
)))
{
/* Don't im ourselves ... */
serv_got_im
(
gc
,
passport
,
body_final
,
0
,
time
(
NULL
));
if
(
swboard
->
conv
==
NULL
)
{
swboard
->
conv
=
purple_find_conversation_with_account
(
PURPLE_CONV_TYPE_IM
,
passport
,
purple_connection_get_account
(
gc
));
swboard
->
flag
|=
MSN_SB_FLAG_IM
;
}
}
}
else
{
serv_got_im
(
gc
,
passport
,
body_final
,
0
,
time
(
NULL
));
}
g_free
(
body_final
);
}
void
msn_control_msg
(
MsnCmdProc
*
cmdproc
,
MsnMessage
*
msg
)
{
PurpleConnection
*
gc
;
char
*
passport
;
gc
=
cmdproc
->
session
->
account
->
gc
;
passport
=
msg
->
remote_user
;
if
(
msn_message_get_header_value
(
msg
,
"TypingUser"
)
==
NULL
)
return
;
if
(
cmdproc
->
servconn
->
type
==
MSN_SERVCONN_SB
)
{
MsnSwitchBoard
*
swboard
=
cmdproc
->
data
;
if
(
swboard
->
current_users
==
1
)
{
serv_got_typing
(
gc
,
passport
,
MSN_TYPING_RECV_TIMEOUT
,
PURPLE_TYPING
);
}
}
else
{
serv_got_typing
(
gc
,
passport
,
MSN_TYPING_RECV_TIMEOUT
,
PURPLE_TYPING
);
}
}
static
void
datacast_inform_user
(
MsnSwitchBoard
*
swboard
,
const
char
*
who
,
const
char
*
msg
,
const
char
*
filename
)
{
char
*
username
,
*
str
;
PurpleAccount
*
account
;
PurpleBuddy
*
b
;
PurpleConnection
*
pc
;
gboolean
chat
;
account
=
swboard
->
session
->
account
;
pc
=
purple_account_get_connection
(
account
);
if
((
b
=
purple_find_buddy
(
account
,
who
))
!=
NULL
)
username
=
g_markup_escape_text
(
purple_buddy_get_alias
(
b
),
-1
);
else
username
=
g_markup_escape_text
(
who
,
-1
);
str
=
g_strdup_printf
(
msg
,
username
,
filename
);
g_free
(
username
);
swboard
->
flag
|=
MSN_SB_FLAG_IM
;
if
(
swboard
->
current_users
>
1
)
chat
=
TRUE
;
else
chat
=
FALSE
;
if
(
swboard
->
conv
==
NULL
)
{
if
(
chat
)
swboard
->
conv
=
purple_find_chat
(
account
->
gc
,
swboard
->
chat_id
);
else
{
swboard
->
conv
=
purple_find_conversation_with_account
(
PURPLE_CONV_TYPE_IM
,
who
,
account
);
if
(
swboard
->
conv
==
NULL
)
swboard
->
conv
=
purple_conversation_new
(
PURPLE_CONV_TYPE_IM
,
account
,
who
);
}
}
if
(
chat
)
serv_got_chat_in
(
pc
,
purple_conv_chat_get_id
(
PURPLE_CONV_CHAT
(
swboard
->
conv
)),
who
,
PURPLE_MESSAGE_RECV
|
PURPLE_MESSAGE_SYSTEM
,
str
,
time
(
NULL
));
else
serv_got_im
(
pc
,
who
,
str
,
PURPLE_MESSAGE_RECV
|
PURPLE_MESSAGE_SYSTEM
,
time
(
NULL
));
g_free
(
str
);
}
/* TODO: Make these not be such duplicates of each other */
static
void
got_wink_cb
(
MsnSlpCall
*
slpcall
,
const
guchar
*
data
,
gsize
size
)
{
FILE
*
f
=
NULL
;
char
*
path
=
NULL
;
const
char
*
who
=
slpcall
->
slplink
->
remote_user
;
purple_debug_info
(
"msn"
,
"Received wink from %s
\n
"
,
who
);
if
((
f
=
purple_mkstemp
(
&
path
,
TRUE
))
&&
(
fwrite
(
data
,
1
,
size
,
f
)
==
size
))
{
datacast_inform_user
(
slpcall
->
slplink
->
swboard
,
who
,
_
(
"%s sent a wink. <a href='msn-wink://%s'>Click here to play it</a>"
),
path
);
}
else
{
purple_debug_error
(
"msn"
,
"Couldn
\'
t create temp file to store wink
\n
"
);
datacast_inform_user
(
slpcall
->
slplink
->
swboard
,
who
,
_
(
"%s sent a wink, but it could not be saved"
),
NULL
);
}
if
(
f
)
fclose
(
f
);
g_free
(
path
);
}
static
void
got_voiceclip_cb
(
MsnSlpCall
*
slpcall
,
const
guchar
*
data
,
gsize
size
)
{
FILE
*
f
=
NULL
;
char
*
path
=
NULL
;
const
char
*
who
=
slpcall
->
slplink
->
remote_user
;
purple_debug_info
(
"msn"
,
"Received voice clip from %s
\n
"
,
who
);
if
((
f
=
purple_mkstemp
(
&
path
,
TRUE
))
&&
(
fwrite
(
data
,
1
,
size
,
f
)
==
size
))
{
datacast_inform_user
(
slpcall
->
slplink
->
swboard
,
who
,
_
(
"%s sent a voice clip. <a href='audio://%s'>Click here to play it</a>"
),
path
);
}
else
{
purple_debug_error
(
"msn"
,
"Couldn
\'
t create temp file to store sound
\n
"
);
datacast_inform_user
(
slpcall
->
slplink
->
swboard
,
who
,
_
(
"%s sent a voice clip, but it could not be saved"
),
NULL
);
}
if
(
f
)
fclose
(
f
);
g_free
(
path
);
}
void
msn_p2p_msg
(
MsnCmdProc
*
cmdproc
,
MsnMessage
*
msg
)
{
MsnSession
*
session
;
MsnSlpLink
*
slplink
;
MsnP2PVersion
p2p
;
session
=
cmdproc
->
servconn
->
session
;
slplink
=
msn_session_get_slplink
(
session
,
msg
->
remote_user
);
if
(
slplink
->
swboard
==
NULL
)
{
/*
* We will need swboard in order to change its flags. If its
* NULL, something has probably gone wrong earlier on. I
* didn't want to do this, but MSN 7 is somehow causing us
* to crash here, I couldn't reproduce it to debug more,
* and people are reporting bugs. Hopefully this doesn't
* cause more crashes. Stu.
*/
if
(
cmdproc
->
data
==
NULL
)
g_warning
(
"msn_p2p_msg cmdproc->data was NULL
\n
"
);
else
{
slplink
->
swboard
=
(
MsnSwitchBoard
*
)
cmdproc
->
data
;
slplink
->
swboard
->
slplinks
=
g_list_prepend
(
slplink
->
swboard
->
slplinks
,
slplink
);
}
}
p2p
=
msn_slplink_get_p2p_version
(
slplink
);
msg
->
part
=
msn_slpmsgpart_new_from_data
(
p2p
,
msg
->
body
,
msg
->
body_len
);
if
(
msg
->
part
)
msn_slplink_process_msg
(
slplink
,
msg
->
part
);
else
purple_debug_warning
(
"msn"
,
"P2P message failed to parse.
\n
"
);
}
static
void
got_emoticon
(
MsnSlpCall
*
slpcall
,
const
guchar
*
data
,
gsize
size
)
{
PurpleConversation
*
conv
;
MsnSwitchBoard
*
swboard
;
swboard
=
slpcall
->
slplink
->
swboard
;
conv
=
swboard
->
conv
;
if
(
conv
)
{
/* FIXME: it would be better if we wrote the data as we received it
instead of all at once, calling write multiple times and
close once at the very end
*/
purple_conv_custom_smiley_write
(
conv
,
slpcall
->
data_info
,
data
,
size
);
purple_conv_custom_smiley_close
(
conv
,
slpcall
->
data_info
);
}
if
(
purple_debug_is_verbose
())
purple_debug_info
(
"msn"
,
"Got smiley: %s
\n
"
,
slpcall
->
data_info
);
}
void
msn_emoticon_msg
(
MsnCmdProc
*
cmdproc
,
MsnMessage
*
msg
)
{
MsnSession
*
session
;
MsnSlpLink
*
slplink
;
MsnSwitchBoard
*
swboard
;
MsnObject
*
obj
;
char
**
tokens
;
char
*
smile
,
*
body_str
;
const
char
*
body
,
*
who
,
*
sha1
;
guint
tok
;
size_t
body_len
;
PurpleConversation
*
conv
;
session
=
cmdproc
->
servconn
->
session
;
if
(
!
purple_account_get_bool
(
session
->
account
,
"custom_smileys"
,
TRUE
))
return
;
swboard
=
cmdproc
->
data
;
conv
=
swboard
->
conv
;
body
=
msn_message_get_bin_data
(
msg
,
&
body_len
);
if
(
!
body
||
!
body_len
)
return
;
body_str
=
g_strndup
(
body
,
body_len
);
/* MSN Messenger 7 may send more than one MSNObject in a single message...
* Maybe 10 tokens is a reasonable max value. */
tokens
=
g_strsplit
(
body_str
,
"
\t
"
,
10
);
g_free
(
body_str
);
for
(
tok
=
0
;
tok
<
9
;
tok
+=
2
)
{
if
(
tokens
[
tok
]
==
NULL
||
tokens
[
tok
+
1
]
==
NULL
)
{
break
;
}
smile
=
tokens
[
tok
];
obj
=
msn_object_new_from_string
(
purple_url_decode
(
tokens
[
tok
+
1
]));
if
(
obj
==
NULL
)
break
;
who
=
msn_object_get_creator
(
obj
);
sha1
=
msn_object_get_sha1
(
obj
);
slplink
=
msn_session_get_slplink
(
session
,
who
);
if
(
slplink
->
swboard
!=
swboard
)
{
if
(
slplink
->
swboard
!=
NULL
)
/*
* Apparently we're using a different switchboard now or
* something? I don't know if this is normal, but it
* definitely happens. So make sure the old switchboard
* doesn't still have a reference to us.
*/
slplink
->
swboard
->
slplinks
=
g_list_remove
(
slplink
->
swboard
->
slplinks
,
slplink
);
slplink
->
swboard
=
swboard
;
slplink
->
swboard
->
slplinks
=
g_list_prepend
(
slplink
->
swboard
->
slplinks
,
slplink
);
}
/* If the conversation doesn't exist then this is a custom smiley
* used in the first message in a MSN conversation: we need to create
* the conversation now, otherwise the custom smiley won't be shown.
* This happens because every GtkIMHtml has its own smiley tree: if
* the conversation doesn't exist then we cannot associate the new
* smiley with its GtkIMHtml widget. */
if
(
!
conv
)
{
conv
=
purple_conversation_new
(
PURPLE_CONV_TYPE_IM
,
session
->
account
,
who
);
}
if
(
purple_conv_custom_smiley_add
(
conv
,
smile
,
"sha1"
,
sha1
,
TRUE
))
{
msn_slplink_request_object
(
slplink
,
smile
,
got_emoticon
,
NULL
,
obj
);
}
msn_object_destroy
(
obj
);
obj
=
NULL
;
who
=
NULL
;
sha1
=
NULL
;
}
g_strfreev
(
tokens
);
}
void
msn_datacast_msg
(
MsnCmdProc
*
cmdproc
,
MsnMessage
*
msg
)
{
GHashTable
*
body
;
const
char
*
id
;
body
=
msn_message_get_hashtable_from_body
(
msg
);
id
=
g_hash_table_lookup
(
body
,
"ID"
);
if
(
!
strcmp
(
id
,
"1"
))
{
/* Nudge */
PurpleAccount
*
account
;
const
char
*
user
;
account
=
cmdproc
->
session
->
account
;
user
=
msg
->
remote_user
;
if
(
cmdproc
->
servconn
->
type
==
MSN_SERVCONN_SB
)
{
MsnSwitchBoard
*
swboard
=
cmdproc
->
data
;
if
(
swboard
->
current_users
>
1
||
((
swboard
->
conv
!=
NULL
)
&&
purple_conversation_get_type
(
swboard
->
conv
)
==
PURPLE_CONV_TYPE_CHAT
))
purple_prpl_got_attention_in_chat
(
account
->
gc
,
swboard
->
chat_id
,
user
,
MSN_NUDGE
);
else
purple_prpl_got_attention
(
account
->
gc
,
user
,
MSN_NUDGE
);
}
else
{
purple_prpl_got_attention
(
account
->
gc
,
user
,
MSN_NUDGE
);
}
}
else
if
(
!
strcmp
(
id
,
"2"
))
{
/* Wink */
MsnSession
*
session
;
MsnSlpLink
*
slplink
;
MsnObject
*
obj
;
const
char
*
who
;
const
char
*
data
;
session
=
cmdproc
->
session
;
data
=
g_hash_table_lookup
(
body
,
"Data"
);
obj
=
msn_object_new_from_string
(
data
);
who
=
msn_object_get_creator
(
obj
);
slplink
=
msn_session_get_slplink
(
session
,
who
);
msn_slplink_request_object
(
slplink
,
data
,
got_wink_cb
,
NULL
,
obj
);
msn_object_destroy
(
obj
);
}
else
if
(
!
strcmp
(
id
,
"3"
))
{
/* Voiceclip */
MsnSession
*
session
;
MsnSlpLink
*
slplink
;
MsnObject
*
obj
;
const
char
*
who
;
const
char
*
data
;
session
=
cmdproc
->
session
;
data
=
g_hash_table_lookup
(
body
,
"Data"
);
obj
=
msn_object_new_from_string
(
data
);
who
=
msn_object_get_creator
(
obj
);
slplink
=
msn_session_get_slplink
(
session
,
who
);
msn_slplink_request_object
(
slplink
,
data
,
got_voiceclip_cb
,
NULL
,
obj
);
msn_object_destroy
(
obj
);
}
else
if
(
!
strcmp
(
id
,
"4"
))
{
/* Action */
}
else
{
purple_debug_warning
(
"msn"
,
"Got unknown datacast with ID %s.
\n
"
,
id
);
}
g_hash_table_destroy
(
body
);
}
void
msn_invite_msg
(
MsnCmdProc
*
cmdproc
,
MsnMessage
*
msg
)
{
GHashTable
*
body
;
const
gchar
*
command
;
const
gchar
*
cookie
;
gboolean
accepted
=
FALSE
;
g_return_if_fail
(
cmdproc
!=
NULL
);
g_return_if_fail
(
msg
!=
NULL
);
body
=
msn_message_get_hashtable_from_body
(
msg
);
if
(
body
==
NULL
)
{
purple_debug_warning
(
"msn"
,
"Unable to parse invite msg body.
\n
"
);
return
;
}
/*
* GUID is NOT always present but Invitation-Command and Invitation-Cookie
* are mandatory.
*/
command
=
g_hash_table_lookup
(
body
,
"Invitation-Command"
);
cookie
=
g_hash_table_lookup
(
body
,
"Invitation-Cookie"
);
if
(
command
==
NULL
||
cookie
==
NULL
)
{
purple_debug_warning
(
"msn"
,
"Invalid invitation message: either Invitation-Command "
"or Invitation-Cookie is missing or invalid.
\n
"
);
return
;
}
else
if
(
!
strcmp
(
command
,
"INVITE"
))
{
const
gchar
*
guid
=
g_hash_table_lookup
(
body
,
"Application-GUID"
);
if
(
guid
==
NULL
)
{
purple_debug_warning
(
"msn"
,
"Invite msg missing Application-GUID.
\n
"
);
accepted
=
TRUE
;
}
else
if
(
!
strcmp
(
guid
,
MSN_FT_GUID
))
{
}
else
if
(
!
strcmp
(
guid
,
"{02D3C01F-BF30-4825-A83A-DE7AF41648AA}"
))
{
purple_debug_info
(
"msn"
,
"Computer call
\n
"
);
if
(
cmdproc
->
session
)
{
PurpleConversation
*
conv
=
NULL
;
gchar
*
from
=
msg
->
remote_user
;
gchar
*
buf
=
NULL
;
if
(
from
)
conv
=
purple_find_conversation_with_account
(
PURPLE_CONV_TYPE_IM
,
from
,
cmdproc
->
session
->
account
);
if
(
conv
)
buf
=
g_strdup_printf
(
_
(
"%s sent you a voice chat "
"invite, which is not yet "
"supported."
),
from
);
if
(
buf
)
{
purple_conversation_write
(
conv
,
NULL
,
buf
,
PURPLE_MESSAGE_SYSTEM
|
PURPLE_MESSAGE_NOTIFY
,
time
(
NULL
));
g_free
(
buf
);
}
}
}
else
{
const
gchar
*
application
=
g_hash_table_lookup
(
body
,
"Application-Name"
);
purple_debug_warning
(
"msn"
,
"Unhandled invite msg with GUID %s: %s.
\n
"
,
guid
,
application
?
application
:
"(null)"
);
}
if
(
!
accepted
)
{
MsnSwitchBoard
*
swboard
=
cmdproc
->
data
;
char
*
text
;
MsnMessage
*
cancel
;
cancel
=
msn_message_new
(
MSN_MSG_TEXT
);
msn_message_set_content_type
(
cancel
,
"text/x-msmsgsinvite"
);
msn_message_set_charset
(
cancel
,
"UTF-8"
);
msn_message_set_flag
(
cancel
,
'U'
);
text
=
g_strdup_printf
(
"Invitation-Command: CANCEL
\r\n
"
"Invitation-Cookie: %s
\r\n
"
"Cancel-Code: REJECT_NOT_INSTALLED
\r\n
"
,
cookie
);
msn_message_set_bin_data
(
cancel
,
text
,
strlen
(
text
));
g_free
(
text
);
msn_switchboard_send_msg
(
swboard
,
cancel
,
TRUE
);
msn_message_unref
(
cancel
);
}
}
else
if
(
!
strcmp
(
command
,
"CANCEL"
))
{
const
gchar
*
code
=
g_hash_table_lookup
(
body
,
"Cancel-Code"
);
purple_debug_info
(
"msn"
,
"MSMSGS invitation cancelled: %s.
\n
"
,
code
?
code
:
"no reason given"
);
}
else
{
/*
* Some other already established invitation session.
* Can be retrieved by Invitation-Cookie.
*/
}
g_hash_table_destroy
(
body
);
}
/* Only called from chats. Handwritten messages for IMs come as a SLP message */
void
msn_handwritten_msg
(
MsnCmdProc
*
cmdproc
,
MsnMessage
*
msg
)
{
const
char
*
body
;
size_t
body_len
;
body
=
msn_message_get_bin_data
(
msg
,
&
body_len
);
msn_switchboard_show_ink
(
cmdproc
->
data
,
msg
->
remote_user
,
body
);
}