Skip to content
GitLab
Menu
Projects
Groups
Snippets
Loading...
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
Menu
Open sidebar
BC
public
liblinphone
Commits
d468050c
Commit
d468050c
authored
Jan 02, 2014
by
Ghislain MARY
Browse files
Implement RFC3994: Indication of Message Composition for Instant Messaging.
parent
fe1ca6f0
Changes
20
Hide whitespace changes
Inline
Side-by-side
Showing
20 changed files
with
552 additions
and
145 deletions
+552
-145
console/Makefile.am
console/Makefile.am
+4
-2
coreapi/Makefile.am
coreapi/Makefile.am
+1
-0
coreapi/bellesip_sal/sal_impl.c
coreapi/bellesip_sal/sal_impl.c
+10
-1
coreapi/bellesip_sal/sal_op_message.c
coreapi/bellesip_sal/sal_op_message.c
+17
-3
coreapi/callbacks.c
coreapi/callbacks.c
+14
-2
coreapi/chat.c
coreapi/chat.c
+245
-1
coreapi/linphonecore.h
coreapi/linphonecore.h
+23
-0
coreapi/presence.c
coreapi/presence.c
+43
-130
coreapi/private.h
coreapi/private.h
+40
-0
coreapi/xml.c
coreapi/xml.c
+98
-0
gtk/chat.c
gtk/chat.c
+20
-1
gtk/friendlist.c
gtk/friendlist.c
+22
-2
gtk/linphone.h
gtk/linphone.h
+1
-0
gtk/main.c
gtk/main.c
+1
-0
include/sal/sal.h
include/sal/sal.h
+10
-0
pixmaps/Makefile.am
pixmaps/Makefile.am
+1
-1
pixmaps/active_chat.png
pixmaps/active_chat.png
+0
-0
pixmaps/composing_active_chat.png
pixmaps/composing_active_chat.png
+0
-0
pixmaps/composing_chat.png
pixmaps/composing_chat.png
+0
-0
tester/Makefile.am
tester/Makefile.am
+2
-2
No files found.
console/Makefile.am
View file @
d468050c
...
...
@@ -13,7 +13,8 @@ COMMON_CFLAGS=\
$(MEDIASTREAMER_CFLAGS)
\
$(VIDEO_CFLAGS)
\
$(READLINE_CFLAGS)
\
$(SQLITE3_CFLAGS)
$(SQLITE3_CFLAGS)
\
$(LIBXML2_CFLAGS)
if
BUILD_CONSOLE
...
...
@@ -29,7 +30,8 @@ linphonec_LDADD=$(top_builddir)/coreapi/liblinphone.la \
$(READLINE_LIBS)
\
$(SQLITE3_LIBS)
\
$(X11_LIBS)
\
$(BELLESIP_LIBS)
$(BELLESIP_LIBS)
\
$(LIBXML2_LIBS)
if
BUILD_WIN32
#special build of linphonec to detach from the windows console
...
...
coreapi/Makefile.am
View file @
d468050c
...
...
@@ -51,6 +51,7 @@ liblinphone_la_SOURCES=\
contactprovider.c contactprovider.h contact_providers_priv.h
\
ldap/ldapprovider.c ldap/ldapprovider.h
\
dict.c
\
xml.c
\
$(GITVERSION_FILE)
if
BUILD_UPNP
...
...
coreapi/bellesip_sal/sal_impl.c
View file @
d468050c
...
...
@@ -464,6 +464,8 @@ void sal_set_callbacks(Sal *ctx, const SalCallbacks *cbs){
ctx
->
callbacks
.
subscribe_presence_received
=
(
SalOnSubscribePresenceReceived
)
unimplemented_stub
;
if
(
ctx
->
callbacks
.
text_received
==
NULL
)
ctx
->
callbacks
.
text_received
=
(
SalOnTextReceived
)
unimplemented_stub
;
if
(
ctx
->
callbacks
.
is_composing_received
==
NULL
)
ctx
->
callbacks
.
is_composing_received
=
(
SalOnIsComposingReceived
)
unimplemented_stub
;
if
(
ctx
->
callbacks
.
ping_reply
==
NULL
)
ctx
->
callbacks
.
ping_reply
=
(
SalOnPingReply
)
unimplemented_stub
;
if
(
ctx
->
callbacks
.
auth_requested
==
NULL
)
...
...
@@ -915,5 +917,12 @@ unsigned char * sal_get_random_bytes(unsigned char *ret, size_t size){
return
belle_sip_random_bytes
(
ret
,
size
);
}
belle_sip_source_t
*
sal_create_timer
(
Sal
*
sal
,
belle_sip_source_func_t
func
,
void
*
data
,
unsigned
int
timeout_value_ms
,
const
char
*
timer_name
)
{
belle_sip_main_loop_t
*
ml
=
belle_sip_stack_get_main_loop
(
sal
->
stack
);
return
belle_sip_main_loop_create_timeout
(
ml
,
func
,
data
,
timeout_value_ms
,
timer_name
);
}
void
sal_cancel_timer
(
Sal
*
sal
,
belle_sip_source_t
*
timer
)
{
belle_sip_main_loop_t
*
ml
=
belle_sip_stack_get_main_loop
(
sal
->
stack
);
belle_sip_main_loop_remove_source
(
ml
,
timer
);
}
coreapi/bellesip_sal/sal_op_message.c
View file @
d468050c
...
...
@@ -76,6 +76,10 @@ static bool_t is_external_body(belle_sip_header_content_type_t* content_type) {
return
strcmp
(
"message"
,
belle_sip_header_content_type_get_type
(
content_type
))
==
0
&&
strcmp
(
"external-body"
,
belle_sip_header_content_type_get_subtype
(
content_type
))
==
0
;
}
static
bool_t
is_im_iscomposing
(
belle_sip_header_content_type_t
*
content_type
)
{
return
strcmp
(
"application"
,
belle_sip_header_content_type_get_type
(
content_type
))
==
0
&&
strcmp
(
"im-iscomposing+xml"
,
belle_sip_header_content_type_get_subtype
(
content_type
))
==
0
;
}
static
void
process_request_event
(
void
*
op_base
,
const
belle_sip_request_event_t
*
event
)
{
SalOp
*
op
=
(
SalOp
*
)
op_base
;
...
...
@@ -88,8 +92,6 @@ static void process_request_event(void *op_base, const belle_sip_request_event_t
belle_sip_header_call_id_t
*
call_id
=
belle_sip_message_get_header_by_type
(
req
,
belle_sip_header_call_id_t
);
belle_sip_header_cseq_t
*
cseq
=
belle_sip_message_get_header_by_type
(
req
,
belle_sip_header_cseq_t
);
belle_sip_header_date_t
*
date
=
belle_sip_message_get_header_by_type
(
req
,
belle_sip_header_date_t
);
SalMessage
salmsg
;
char
message_id
[
256
]
=
{
0
};
int
response_code
=
501
;
char
*
from
;
bool_t
plain_text
=
FALSE
;
...
...
@@ -99,7 +101,8 @@ static void process_request_event(void *op_base, const belle_sip_request_event_t
content_type
=
belle_sip_message_get_header_by_type
(
BELLE_SIP_MESSAGE
(
req
),
belle_sip_header_content_type_t
);
if
(
content_type
&&
((
plain_text
=
is_plain_text
(
content_type
))
||
(
external_body
=
is_external_body
(
content_type
))))
{
SalMessage
salmsg
;
char
message_id
[
256
]
=
{
0
};
address
=
belle_sip_header_address_create
(
belle_sip_header_address_get_displayname
(
BELLE_SIP_HEADER_ADDRESS
(
from_header
))
,
belle_sip_header_address_get_uri
(
BELLE_SIP_HEADER_ADDRESS
(
from_header
)));
from
=
belle_sip_object_to_string
(
BELLE_SIP_OBJECT
(
address
));
...
...
@@ -121,6 +124,17 @@ static void process_request_event(void *op_base, const belle_sip_request_event_t
belle_sip_free
(
from
);
if
(
salmsg
.
url
)
ms_free
((
char
*
)
salmsg
.
url
);
response_code
=
200
;
}
else
if
(
content_type
&&
is_im_iscomposing
(
content_type
))
{
SalIsComposing
saliscomposing
;
address
=
belle_sip_header_address_create
(
belle_sip_header_address_get_displayname
(
BELLE_SIP_HEADER_ADDRESS
(
from_header
))
,
belle_sip_header_address_get_uri
(
BELLE_SIP_HEADER_ADDRESS
(
from_header
)));
from
=
belle_sip_object_to_string
(
BELLE_SIP_OBJECT
(
address
));
saliscomposing
.
from
=
from
;
saliscomposing
.
text
=
belle_sip_message_get_body
(
BELLE_SIP_MESSAGE
(
req
));
op
->
base
.
root
->
callbacks
.
is_composing_received
(
op
,
&
saliscomposing
);
belle_sip_object_unref
(
address
);
belle_sip_free
(
from
);
response_code
=
200
;
}
else
{
ms_error
(
"Unsupported MESSAGE with content type [%s/%s]"
,
belle_sip_header_content_type_get_type
(
content_type
)
,
belle_sip_header_content_type_get_subtype
(
content_type
));
...
...
coreapi/callbacks.c
View file @
d468050c
...
...
@@ -843,6 +843,11 @@ static void text_received(SalOp *op, const SalMessage *msg){
}
}
static
void
is_composing_received
(
SalOp
*
op
,
const
SalIsComposing
*
is_composing
)
{
LinphoneCore
*
lc
=
(
LinphoneCore
*
)
sal_get_user_pointer
(
sal_op_get_sal
(
op
));
linphone_core_is_composing_received
(
lc
,
op
,
is_composing
);
}
static
void
parse_presence_requested
(
SalOp
*
op
,
const
char
*
content_type
,
const
char
*
content_subtype
,
const
char
*
body
,
SalPresenceModel
**
result
)
{
linphone_notify_parse_presence
(
op
,
content_type
,
content_subtype
,
body
,
result
);
}
...
...
@@ -1001,8 +1006,14 @@ static int op_equals(LinphoneCall *a, SalOp *b) {
static
void
text_delivery_update
(
SalOp
*
op
,
SalTextDeliveryStatus
status
){
LinphoneChatMessage
*
chat_msg
=
(
LinphoneChatMessage
*
)
sal_op_get_user_pointer
(
op
);
const
MSList
*
calls
=
linphone_core_get_calls
(
chat_msg
->
chat_room
->
lc
);
const
MSList
*
calls
;
if
(
chat_msg
==
NULL
)
{
// Do not handle delivery status for isComposing messages.
return
;
}
calls
=
linphone_core_get_calls
(
chat_msg
->
chat_room
->
lc
);
chat_msg
->
state
=
chatStatusSal2Linphone
(
status
);
linphone_chat_message_store_state
(
chat_msg
);
if
(
chat_msg
&&
chat_msg
->
cb
)
{
...
...
@@ -1123,6 +1134,7 @@ SalCallbacks linphone_sal_callbacks={
refer_received
,
text_received
,
text_delivery_update
,
is_composing_received
,
notify_refer
,
subscribe_received
,
subscribe_closed
,
...
...
coreapi/chat.c
View file @
d468050c
...
...
@@ -26,6 +26,12 @@
#include "private.h"
#include "lpconfig.h"
#include <libxml/xmlwriter.h>
#define COMPOSING_DEFAULT_IDLE_TIMEOUT 15
#define COMPOSING_DEFAULT_REFRESH_TIMEOUT 60
#define COMPOSING_DEFAULT_REMOTE_REFRESH_TIMEOUT 120
/**
* @addtogroup chatroom
* @{
...
...
@@ -85,13 +91,40 @@ LinphoneChatRoom* linphone_core_get_or_create_chat_room(LinphoneCore* lc, const
}
return
ret
;
}
static
void
linphone_chat_room_delete_composing_idle_timer
(
LinphoneChatRoom
*
cr
)
{
if
(
cr
->
composing_idle_timer
)
{
sal_cancel_timer
(
cr
->
lc
->
sal
,
cr
->
composing_idle_timer
);
belle_sip_object_unref
(
cr
->
composing_idle_timer
);
cr
->
composing_idle_timer
=
NULL
;
}
}
static
void
linphone_chat_room_delete_composing_refresh_timer
(
LinphoneChatRoom
*
cr
)
{
if
(
cr
->
composing_refresh_timer
)
{
sal_cancel_timer
(
cr
->
lc
->
sal
,
cr
->
composing_refresh_timer
);
belle_sip_object_unref
(
cr
->
composing_refresh_timer
);
cr
->
composing_refresh_timer
=
NULL
;
}
}
static
void
linphone_chat_room_delete_remote_composing_refresh_timer
(
LinphoneChatRoom
*
cr
)
{
if
(
cr
->
remote_composing_refresh_timer
)
{
sal_cancel_timer
(
cr
->
lc
->
sal
,
cr
->
remote_composing_refresh_timer
);
belle_sip_object_unref
(
cr
->
remote_composing_refresh_timer
);
cr
->
remote_composing_refresh_timer
=
NULL
;
}
}
/**
* Destroy a LinphoneChatRoom.
* @param cr #LinphoneChatRoom object
*/
void
linphone_chat_room_destroy
(
LinphoneChatRoom
*
cr
){
LinphoneCore
*
lc
=
cr
->
lc
;
linphone_chat_room_delete_composing_idle_timer
(
cr
);
linphone_chat_room_delete_composing_refresh_timer
(
cr
);
linphone_chat_room_delete_remote_composing_refresh_timer
(
cr
);
lc
->
chatrooms
=
ms_list_remove
(
lc
->
chatrooms
,(
void
*
)
cr
);
linphone_address_destroy
(
cr
->
peer_url
);
ms_free
(
cr
->
peer
);
...
...
@@ -142,6 +175,12 @@ static void _linphone_chat_room_send_message(LinphoneChatRoom *cr, LinphoneChatM
msg
->
dir
=
LinphoneChatMessageOutgoing
;
msg
->
from
=
linphone_address_new
(
identity
);
msg
->
storage_id
=
linphone_chat_message_store
(
msg
);
if
(
cr
->
is_composing
==
LinphoneIsComposingActive
)
{
cr
->
is_composing
=
LinphoneIsComposingIdle
;
}
linphone_chat_room_delete_composing_idle_timer
(
cr
);
linphone_chat_room_delete_composing_refresh_timer
(
cr
);
}
/**
...
...
@@ -225,6 +264,89 @@ void linphone_core_message_received(LinphoneCore *lc, SalOp *op, const SalMessag
ms_free
(
from
);
}
static
int
linphone_chat_room_remote_refresh_composing_expired
(
void
*
data
,
unsigned
int
revents
)
{
LinphoneChatRoom
*
cr
=
(
LinphoneChatRoom
*
)
data
;
belle_sip_object_unref
(
cr
->
remote_composing_refresh_timer
);
cr
->
remote_composing_refresh_timer
=
NULL
;
cr
->
remote_is_composing
=
LinphoneIsComposingIdle
;
if
(
cr
->
lc
->
vtable
.
is_composing_received
!=
NULL
)
cr
->
lc
->
vtable
.
is_composing_received
(
cr
->
lc
,
cr
);
return
BELLE_SIP_STOP
;
}
static
const
char
*
iscomposing_prefix
=
"/xsi:isComposing"
;
static
void
process_im_is_composing_notification
(
LinphoneChatRoom
*
cr
,
xmlparsing_context_t
*
xml_ctx
)
{
char
xpath_str
[
MAX_XPATH_LENGTH
];
xmlXPathObjectPtr
iscomposing_object
;
const
char
*
state_str
=
NULL
;
const
char
*
refresh_str
=
NULL
;
int
refresh_duration
=
COMPOSING_DEFAULT_REMOTE_REFRESH_TIMEOUT
;
int
i
;
LinphoneIsComposingState
state
=
LinphoneIsComposingIdle
;
if
(
linphone_create_xml_xpath_context
(
xml_ctx
)
<
0
)
return
;
xmlXPathRegisterNs
(
xml_ctx
->
xpath_ctx
,
(
const
xmlChar
*
)
"xsi"
,
(
const
xmlChar
*
)
"urn:ietf:params:xml:ns:im-iscomposing"
);
iscomposing_object
=
linphone_get_xml_xpath_object_for_node_list
(
xml_ctx
,
iscomposing_prefix
);
if
((
iscomposing_object
!=
NULL
)
&&
(
iscomposing_object
->
nodesetval
!=
NULL
))
{
for
(
i
=
1
;
i
<=
iscomposing_object
->
nodesetval
->
nodeNr
;
i
++
)
{
snprintf
(
xpath_str
,
sizeof
(
xpath_str
),
"%s[%i]/xsi:state"
,
iscomposing_prefix
,
i
);
state_str
=
linphone_get_xml_text_content
(
xml_ctx
,
xpath_str
);
if
(
state_str
==
NULL
)
continue
;
snprintf
(
xpath_str
,
sizeof
(
xpath_str
),
"%s[%i]/xsi:refresh"
,
iscomposing_prefix
,
i
);
refresh_str
=
linphone_get_xml_text_content
(
xml_ctx
,
xpath_str
);
}
}
if
(
state_str
!=
NULL
)
{
if
(
strcmp
(
state_str
,
"active"
)
==
0
)
{
state
=
LinphoneIsComposingActive
;
if
(
refresh_str
!=
NULL
)
{
refresh_duration
=
atoi
(
refresh_str
);
}
if
(
!
cr
->
remote_composing_refresh_timer
)
{
cr
->
remote_composing_refresh_timer
=
sal_create_timer
(
cr
->
lc
->
sal
,
linphone_chat_room_remote_refresh_composing_expired
,
cr
,
refresh_duration
*
1000
,
"composing remote refresh timeout"
);
}
else
{
belle_sip_source_set_timeout
(
cr
->
remote_composing_refresh_timer
,
refresh_duration
*
1000
);
}
}
else
{
linphone_chat_room_delete_remote_composing_refresh_timer
(
cr
);
}
cr
->
remote_is_composing
=
state
;
if
(
cr
->
lc
->
vtable
.
is_composing_received
!=
NULL
)
cr
->
lc
->
vtable
.
is_composing_received
(
cr
->
lc
,
cr
);
}
}
static
void
linphone_chat_room_notify_is_composing
(
LinphoneChatRoom
*
cr
,
const
char
*
text
)
{
xmlparsing_context_t
*
xml_ctx
=
linphone_xmlparsing_context_new
();
xmlSetGenericErrorFunc
(
xml_ctx
,
linphone_xmlparsing_genericxml_error
);
xml_ctx
->
doc
=
xmlReadDoc
((
const
unsigned
char
*
)
text
,
0
,
NULL
,
0
);
if
(
xml_ctx
->
doc
!=
NULL
)
{
process_im_is_composing_notification
(
cr
,
xml_ctx
);
}
else
{
ms_warning
(
"Wrongly formatted presence XML: %s"
,
xml_ctx
->
errorBuffer
);
}
linphone_xmlparsing_context_destroy
(
xml_ctx
);
}
void
linphone_core_is_composing_received
(
LinphoneCore
*
lc
,
SalOp
*
op
,
const
SalIsComposing
*
is_composing
)
{
LinphoneChatRoom
*
cr
=
NULL
;
LinphoneAddress
*
addr
=
linphone_address_new
(
is_composing
->
from
);
linphone_address_clean
(
addr
);
cr
=
linphone_core_get_chat_room
(
lc
,
addr
);
if
(
cr
!=
NULL
)
{
linphone_chat_room_notify_is_composing
(
cr
,
is_composing
->
text
);
}
linphone_address_destroy
(
addr
);
}
bool_t
linphone_chat_room_is_remote_composing
(
const
LinphoneChatRoom
*
cr
)
{
return
(
cr
->
remote_is_composing
==
LinphoneIsComposingActive
)
?
TRUE
:
FALSE
;
}
/**
* Returns back pointer to LinphoneCore object.
**/
...
...
@@ -319,6 +441,128 @@ void linphone_chat_room_send_message2(LinphoneChatRoom *cr, LinphoneChatMessage*
_linphone_chat_room_send_message
(
cr
,
msg
);
}
static
char
*
linphone_chat_room_create_is_composing_xml
(
LinphoneChatRoom
*
cr
)
{
xmlBufferPtr
buf
;
xmlTextWriterPtr
writer
;
int
err
;
char
*
content
=
NULL
;
buf
=
xmlBufferCreate
();
if
(
buf
==
NULL
)
{
ms_error
(
"Error creating the XML buffer"
);
return
content
;
}
writer
=
xmlNewTextWriterMemory
(
buf
,
0
);
if
(
writer
==
NULL
)
{
ms_error
(
"Error creating the XML writer"
);
return
content
;
}
err
=
xmlTextWriterStartDocument
(
writer
,
"1.0"
,
"UTF-8"
,
NULL
);
if
(
err
>=
0
)
{
err
=
xmlTextWriterStartElementNS
(
writer
,
NULL
,
(
const
xmlChar
*
)
"isComposing"
,
(
const
xmlChar
*
)
"urn:ietf:params:xml:ns:im-iscomposing"
);
}
if
(
err
>=
0
)
{
err
=
xmlTextWriterWriteAttributeNS
(
writer
,
(
const
xmlChar
*
)
"xmlns"
,
(
const
xmlChar
*
)
"xsi"
,
NULL
,
(
const
xmlChar
*
)
"http://www.w3.org/2001/XMLSchema-instance"
);
}
if
(
err
>=
0
)
{
err
=
xmlTextWriterWriteAttributeNS
(
writer
,
(
const
xmlChar
*
)
"xsi"
,
(
const
xmlChar
*
)
"schemaLocation"
,
NULL
,
(
const
xmlChar
*
)
"urn:ietf:params:xml:ns:im-composing iscomposing.xsd"
);
}
if
(
err
>=
0
)
{
err
=
xmlTextWriterWriteElement
(
writer
,
(
const
xmlChar
*
)
"state"
,
(
cr
->
is_composing
==
LinphoneIsComposingActive
)
?
(
const
xmlChar
*
)
"active"
:
(
const
xmlChar
*
)
"idle"
);
}
if
((
err
>=
0
)
&&
(
cr
->
is_composing
==
LinphoneIsComposingActive
))
{
char
refresh_str
[
4
]
=
{
0
};
snprintf
(
refresh_str
,
sizeof
(
refresh_str
),
"%u"
,
COMPOSING_DEFAULT_REFRESH_TIMEOUT
);
err
=
xmlTextWriterWriteElement
(
writer
,
(
const
xmlChar
*
)
"refresh"
,
(
const
xmlChar
*
)
refresh_str
);
}
if
(
err
>=
0
)
{
/* Close the "isComposing" element. */
err
=
xmlTextWriterEndElement
(
writer
);
}
if
(
err
>=
0
)
{
err
=
xmlTextWriterEndDocument
(
writer
);
}
if
(
err
>
0
)
{
/* xmlTextWriterEndDocument returns the size of the content. */
content
=
ms_strdup
((
char
*
)
buf
->
content
);
}
xmlFreeTextWriter
(
writer
);
xmlBufferFree
(
buf
);
return
content
;
}
static
void
linphone_chat_room_send_is_composing_notification
(
LinphoneChatRoom
*
cr
)
{
SalOp
*
op
=
NULL
;
LinphoneCall
*
call
;
const
char
*
identity
=
NULL
;
char
*
content
=
NULL
;
if
(
lp_config_get_int
(
cr
->
lc
->
config
,
"sip"
,
"chat_use_call_dialogs"
,
0
))
{
if
((
call
=
linphone_core_get_call_by_remote_address
(
cr
->
lc
,
cr
->
peer
))
!=
NULL
)
{
if
(
call
->
state
==
LinphoneCallConnected
||
call
->
state
==
LinphoneCallStreamsRunning
||
call
->
state
==
LinphoneCallPaused
||
call
->
state
==
LinphoneCallPausing
||
call
->
state
==
LinphoneCallPausedByRemote
)
{
ms_message
(
"send SIP message through the existing call."
);
op
=
call
->
op
;
identity
=
linphone_core_find_best_identity
(
cr
->
lc
,
linphone_call_get_remote_address
(
call
));
}
}
}
if
(
op
==
NULL
)
{
LinphoneProxyConfig
*
proxy
=
linphone_core_lookup_known_proxy
(
cr
->
lc
,
cr
->
peer_url
);
if
(
proxy
)
identity
=
linphone_proxy_config_get_identity
(
proxy
);
else
identity
=
linphone_core_get_primary_contact
(
cr
->
lc
);
/*sending out of calls*/
op
=
sal_op_new
(
cr
->
lc
->
sal
);
linphone_configure_op
(
cr
->
lc
,
op
,
cr
->
peer_url
,
NULL
,
lp_config_get_int
(
cr
->
lc
->
config
,
"sip"
,
"chat_msg_with_contact"
,
0
));
}
content
=
linphone_chat_room_create_is_composing_xml
(
cr
);
if
(
content
!=
NULL
)
{
sal_message_send
(
op
,
identity
,
cr
->
peer
,
"application/im-iscomposing+xml"
,
content
);
ms_free
(
content
);
}
}
static
int
linphone_chat_room_stop_composing
(
void
*
data
,
unsigned
int
revents
)
{
LinphoneChatRoom
*
cr
=
(
LinphoneChatRoom
*
)
data
;
cr
->
is_composing
=
LinphoneIsComposingIdle
;
linphone_chat_room_send_is_composing_notification
(
cr
);
linphone_chat_room_delete_composing_refresh_timer
(
cr
);
belle_sip_object_unref
(
cr
->
composing_idle_timer
);
cr
->
composing_idle_timer
=
NULL
;
return
BELLE_SIP_STOP
;
}
static
int
linphone_chat_room_refresh_composing
(
void
*
data
,
unsigned
int
revents
)
{
LinphoneChatRoom
*
cr
=
(
LinphoneChatRoom
*
)
data
;
linphone_chat_room_send_is_composing_notification
(
cr
);
return
BELLE_SIP_CONTINUE
;
}
void
linphone_chat_room_compose
(
LinphoneChatRoom
*
cr
)
{
if
(
cr
->
is_composing
==
LinphoneIsComposingIdle
)
{
cr
->
is_composing
=
LinphoneIsComposingActive
;
linphone_chat_room_send_is_composing_notification
(
cr
);
if
(
!
cr
->
composing_refresh_timer
)
{
cr
->
composing_refresh_timer
=
sal_create_timer
(
cr
->
lc
->
sal
,
linphone_chat_room_refresh_composing
,
cr
,
COMPOSING_DEFAULT_REFRESH_TIMEOUT
*
1000
,
"composing refresh timeout"
);
}
else
{
belle_sip_source_set_timeout
(
cr
->
composing_refresh_timer
,
COMPOSING_DEFAULT_REFRESH_TIMEOUT
*
1000
);
}
if
(
!
cr
->
composing_idle_timer
)
{
cr
->
composing_idle_timer
=
sal_create_timer
(
cr
->
lc
->
sal
,
linphone_chat_room_stop_composing
,
cr
,
COMPOSING_DEFAULT_IDLE_TIMEOUT
*
1000
,
"composing idle timeout"
);
}
}
belle_sip_source_set_timeout
(
cr
->
composing_idle_timer
,
COMPOSING_DEFAULT_IDLE_TIMEOUT
*
1000
);
}
/**
* Returns a #LinphoneChatMessageState as a string.
*/
...
...
coreapi/linphonecore.h
View file @
d468050c
...
...
@@ -977,6 +977,20 @@ LINPHONE_PUBLIC MSList *linphone_chat_room_get_history(LinphoneChatRoom *cr,int
LINPHONE_PUBLIC
void
linphone_chat_room_mark_as_read
(
LinphoneChatRoom
*
cr
);
LINPHONE_PUBLIC
void
linphone_chat_room_delete_message
(
LinphoneChatRoom
*
cr
,
LinphoneChatMessage
*
msg
);
LINPHONE_PUBLIC
void
linphone_chat_room_delete_history
(
LinphoneChatRoom
*
cr
);
/**
* Notify the destination of the chat message being composed that the user is typing a new message.
* @param[in] cr The #LinphoneChatRoom object corresponding to the conversation for which a new message is being typed.
*/
LINPHONE_PUBLIC
void
linphone_chat_room_compose
(
LinphoneChatRoom
*
cr
);
/**
* Tells whether the remote is currently composing a message.
* @param[in] cr The "LinphoneChatRoom object corresponding to the conversation.
* @return TRUE if the remote is currently composing a message, FALSE otherwise.
*/
LINPHONE_PUBLIC
bool_t
linphone_chat_room_is_remote_composing
(
const
LinphoneChatRoom
*
cr
);
LINPHONE_PUBLIC
int
linphone_chat_room_get_unread_messages_count
(
LinphoneChatRoom
*
cr
);
LINPHONE_PUBLIC
LinphoneCore
*
linphone_chat_room_get_lc
(
LinphoneChatRoom
*
cr
);
LINPHONE_PUBLIC
void
linphone_chat_room_set_user_data
(
LinphoneChatRoom
*
cr
,
void
*
ud
);
...
...
@@ -1130,6 +1144,14 @@ typedef void (*LinphoneCoreTextMessageReceivedCb)(LinphoneCore *lc, LinphoneChat
*/
typedef
void
(
*
LinphoneCoreMessageReceivedCb
)(
LinphoneCore
*
lc
,
LinphoneChatRoom
*
room
,
LinphoneChatMessage
*
message
);
/**
* Is composing notification callback prototype.
*
* @param[in] lc #LinphoneCore object
* @param[in] room #LinphoneChatRoom involved in the conversation.
*/
typedef
void
(
*
LinphoneCoreIsComposingReceivedCb
)(
LinphoneCore
*
lc
,
LinphoneChatRoom
*
room
);
/**
* Callback for being notified of DTMFs received.
* @param lc the linphone core
...
...
@@ -1179,6 +1201,7 @@ typedef struct _LinphoneCoreVTable{
LinphoneCoreAuthInfoRequestedCb
auth_info_requested
;
/**< Ask the application some authentication information */
LinphoneCoreCallLogUpdatedCb
call_log_updated
;
/**< Notifies that call log list has been updated */
LinphoneCoreMessageReceivedCb
message_received
;
/** a message is received, can be text or external body*/
LinphoneCoreIsComposingReceivedCb
is_composing_received
;
/**< An is-composing notification has been received */
LinphoneCoreDtmfReceivedCb
dtmf_received
;
/**< A dtmf has been received received */
LinphoneCoreReferReceivedCb
refer_received
;
/**< An out of call refer was received */
LinphoneCoreCallEncryptionChangedCb
call_encryption_changed
;
/**<Notifies on change in the encryption of call streams */
...
...
coreapi/presence.c
View file @
d468050c
...
...
@@ -22,15 +22,6 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#include "lpconfig.h"
#include "linphonepresence.h"
#include <libxml/xmlreader.h>
#include <libxml/xmlwriter.h>
#include <libxml/xpath.h>
#include <libxml/xpathInternals.h>
#define XMLPARSING_BUFFER_LEN 2048
#define MAX_XPATH_LENGTH 256
extern
const
char
*
__policy_enum_to_str
(
LinphoneSubscribePolicy
pol
);
...
...
@@ -83,13 +74,6 @@ struct _LinphonePresenceModel {
MSList
*
notes
;
/**< A list of _LinphonePresenceNote structures. */
};
typedef
struct
_xmlparsing_context
{
xmlDoc
*
doc
;
xmlXPathContextPtr
xpath_ctx
;
char
errorBuffer
[
XMLPARSING_BUFFER_LEN
];
char
warningBuffer
[
XMLPARSING_BUFFER_LEN
];
}
xmlparsing_context_t
;
static
const
char
*
person_prefix
=
"/pidf:presence/dm:person"
;
...
...
@@ -98,38 +82,6 @@ static const char *person_prefix = "/pidf:presence/dm:person";
* PRIVATE FUNCTIONS *
****************************************************************************/
static
xmlparsing_context_t
*
xmlparsing_context_new
()
{
xmlparsing_context_t
*
xmlCtx
=
(
xmlparsing_context_t
*
)
malloc
(
sizeof
(
xmlparsing_context_t
));
if
(
xmlCtx
!=
NULL
)
{
xmlCtx
->
doc
=
NULL
;
xmlCtx
->
xpath_ctx
=
NULL
;
xmlCtx
->
errorBuffer
[
0
]
=
'\0'
;
xmlCtx
->
warningBuffer
[
0
]
=
'\0'
;
}
return
xmlCtx
;
}
static
void
xmlparsing_context_destroy
(
xmlparsing_context_t
*
ctx
)
{
if
(
ctx
->
doc
!=
NULL
)
{
xmlFreeDoc
(
ctx
->
doc
);
ctx
->
doc
=
NULL
;
}
if
(
ctx
->
xpath_ctx
!=
NULL
)
{
xmlXPathFreeContext
(
ctx
->
xpath_ctx
);
ctx
->
xpath_ctx
=
NULL
;
}
free
(
ctx
);
}
static
void
xmlparsing_genericxml_error
(
void
*
ctx
,
const
char
*
fmt
,
...)
{
xmlparsing_context_t
*
xmlCtx
=
(
xmlparsing_context_t
*
)
ctx
;
int
sl
=
strlen
(
xmlCtx
->
errorBuffer
);
va_list
args
;
va_start
(
args
,
fmt
);
vsnprintf
(
xmlCtx
->
errorBuffer
+
sl
,
XMLPARSING_BUFFER_LEN
-
sl
,
fmt
,
args
);
va_end
(
args
);
}
static
char
presence_id_valid_characters
[]
=
"0123456789abcdefghijklmnopqrstuvwxyz"
;
static
char
*
generate_presence_id
(
void
)
{
...
...
@@ -1183,45 +1135,6 @@ void * linphone_presence_note_get_user_data(LinphonePresenceNote *note) {
* XML PRESENCE INTERNAL HANDLING *
****************************************************************************/
static
int
create_xml_xpath_context
(
xmlparsing_context_t
*
xml_ctx
)
{
if
(
xml_ctx
->
xpath_ctx
!=
NULL
)
{
xmlXPathFreeContext
(
xml_ctx
->
xpath_ctx
);
}
xml_ctx
->
xpath_ctx
=
xmlXPathNewContext
(
xml_ctx
->
doc
);
if
(
xml_ctx
->
xpath_ctx
==
NULL
)
return
-
1
;
return
0
;
}
static
char
*
get_xml_text_content
(
xmlparsing_context_t
*
xml_ctx
,
const
char
*
xpath_expression
)
{
xmlXPathObjectPtr
xpath_obj
;
xmlChar
*
text
=
NULL
;
int
i
;
xpath_obj
=
xmlXPathEvalExpression
((
const
xmlChar
*
)
xpath_expression
,
xml_ctx
->
xpath_ctx
);
if
(
xpath_obj
!=
NULL
)
{
if
(
xpath_obj
->
nodesetval
!=
NULL
)
{
xmlNodeSetPtr
nodes
=
xpath_obj
->
nodesetval
;
for
(
i
=
0
;
i
<
nodes
->
nodeNr
;
i
++
)
{
xmlNodePtr
node
=
nodes
->
nodeTab
[
i
];
if
(
node
->
children
!=
NULL
)
{
text
=
xmlNodeListGetString
(
xml_ctx
->
doc
,
node
->
children
,
1
);
}
}
}
xmlXPathFreeObject
(
xpath_obj
);
}
return
(
char
*
)
text
;
}
static
void
free_xml_text_content
(
const
char
*
text
)
{
xmlFree
((
xmlChar
*
)
text
);
}
static
xmlXPathObjectPtr
get_xml_xpath_object_for_node_list
(
xmlparsing_context_t
*
xml_ctx
,
const
char
*
xpath_expression
)
{
return
xmlXPathEvalExpression
((
const
xmlChar
*
)
xpath_expression
,
xml_ctx
->
xpath_ctx
);
}
static
const
char
*
service_prefix
=
"/pidf:presence/pidf:tuple"
;
static
int
process_pidf_xml_presence_service_notes
(
xmlparsing_context_t
*
xml_ctx
,
LinphonePresenceService
*
service
,
unsigned
int
service_idx
)
{
...
...
@@ -1233,19 +1146,19 @@ static int process_pidf_xml_presence_service_notes(xmlparsing_context_t *xml_ctx
int
i
;
snprintf
(
xpath_str
,
sizeof
(
xpath_str
),
"%s[%i]/pidf:note"
,
service_prefix
,
service_idx
);
note_object
=
get_xml_xpath_object_for_node_list
(
xml_ctx
,
xpath_str
);
note_object
=
linphone_
get_xml_xpath_object_for_node_list
(
xml_ctx
,
xpath_str
);
if
((
note_object
!=
NULL
)
&&
(
note_object
->
nodesetval
!=
NULL
))
{
for
(
i
=
1
;
i
<=
note_object
->
nodesetval
->
nodeNr
;
i
++
)
{
snprintf
(
xpath_str
,
sizeof
(
xpath_str
),
"%s[%i]/pidf:note[%i]"
,
service_prefix
,
service_idx
,
i
);
note_str
=
get_xml_text_content
(
xml_ctx
,
xpath_str
);
note_str
=
linphone_
get_xml_text_content
(
xml_ctx
,
xpath_str
);
if
(
note_str
==
NULL
)
continue
;
snprintf
(
xpath_str
,
sizeof
(
xpath_str
),
"%s[%i]/pidf:note[%i]/@xml:lang"
,
service_prefix
,
service_idx
,
i
);
lang
=
get_xml_text_content
(
xml_ctx
,
xpath_str
);
lang
=
linphone_
get_xml_text_content
(
xml_ctx
,
xpath_str
);
note
=
linphone_presence_note_new
(
note_str
,
lang
);
presence_service_add_note
(
service
,
note
);
if
(
lang
!=
NULL
)
free_xml_text_content
(
lang
);