appshare: basic RDP viewer implementation
[siplcs.git] / src / core / sipe-incoming.c
blob149d0d49ad238fc051cb6e62fa0c9ae5695498f5
1 /**
2 * @file sipe-incoming.c
4 * pidgin-sipe
6 * Copyright (C) 2010-2016 SIPE Project <http://sipe.sourceforge.net/>
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
23 #ifdef HAVE_CONFIG_H
24 #include "config.h"
25 #endif
27 #include <stdlib.h>
28 #include <string.h>
30 #include <glib.h>
32 #ifdef HAVE_GIO
33 #include <gio/gio.h>
34 #endif
36 #include "sipe-appshare.h"
37 #include "sipmsg.h"
38 #include "sip-csta.h"
39 #include "sip-transport.h"
40 #include "sipe-backend.h"
41 #include "sipe-chat.h"
42 #include "sipe-conf.h"
43 #include "sipe-core.h"
44 #include "sipe-core-private.h"
45 #include "sipe-dialog.h"
46 #include "sipe-ft.h"
47 #include "sipe-ft-lync.h"
48 #include "sipe-groupchat.h"
49 #include "sipe-im.h"
50 #include "sipe-incoming.h"
51 #include "sipe-media.h"
52 #include "sipe-mime.h"
53 #include "sipe-nls.h"
54 #include "sipe-schedule.h"
55 #include "sipe-session.h"
56 #include "sipe-user.h"
57 #include "sipe-utils.h"
58 #include "sipe-xml.h"
60 void process_incoming_bye(struct sipe_core_private *sipe_private,
61 struct sipmsg *msg)
63 const gchar *callid = sipmsg_find_header(msg, "Call-ID");
64 gchar *from = parse_from(sipmsg_find_header(msg, "From"));
65 struct sip_session *session;
66 struct sip_dialog *dialog;
68 #ifdef HAVE_VV
69 struct sipe_media_call_private *call_private =
70 g_hash_table_lookup(sipe_private->media_calls, callid);
71 if (is_media_session_msg(call_private, msg)) {
72 // BYE ends a media call
73 sipe_media_hangup(call_private);
75 #endif
77 /* collect dialog identification
78 * we need callid, ourtag and theirtag to unambiguously identify dialog
80 /* take data before 'msg' will be modified by sip_transport_response */
81 dialog = g_new0(struct sip_dialog, 1);
82 dialog->callid = g_strdup(callid);
83 dialog->cseq = sipmsg_parse_cseq(msg);
84 dialog->with = g_strdup(from);
85 sipe_dialog_parse(dialog, msg, FALSE);
87 sip_transport_response(sipe_private, msg, 200, "OK", NULL);
89 session = sipe_session_find_chat_or_im(sipe_private, callid, from);
90 if (!session) {
91 SIPE_DEBUG_INFO_NOFORMAT("process_incoming_bye: couldn't find session. Ignoring");
92 sipe_dialog_free(dialog);
93 g_free(from);
94 return;
97 SIPE_DEBUG_INFO("process_incoming_bye: session found (chat ID %s)",
98 (session->chat_session && session->chat_session->id) ?
99 session->chat_session->id : "<NO CHAT>");
101 if (session->chat_session &&
102 (session->chat_session->type == SIPE_CHAT_TYPE_MULTIPARTY) &&
103 session->chat_session->id &&
104 !g_ascii_strcasecmp(from, session->chat_session->id))
105 sipe_chat_set_roster_manager(session, NULL);
107 sipe_im_cancel_unconfirmed(sipe_private, session, callid, from);
109 /* This what BYE is essentially for - terminating dialog */
110 sipe_dialog_remove_3(session, dialog);
111 sipe_dialog_free(dialog);
112 if (session->chat_session) {
113 if ((session->chat_session->type == SIPE_CHAT_TYPE_CONFERENCE) &&
114 !g_ascii_strcasecmp(from, session->im_mcu_uri)) {
115 SIPE_DEBUG_INFO("process_incoming_bye: disconnected from conference %s",
116 session->im_mcu_uri);
117 sipe_conf_immcu_closed(sipe_private, session);
118 } else if (session->chat_session->type == SIPE_CHAT_TYPE_MULTIPARTY) {
119 SIPE_DEBUG_INFO_NOFORMAT("process_incoming_bye: disconnected from multiparty chat");
120 sipe_backend_chat_remove(session->chat_session->backend,
121 from);
125 g_free(from);
128 void process_incoming_cancel(struct sipe_core_private *sipe_private,
129 struct sipmsg *msg)
131 const gchar *callid = sipmsg_find_header(msg, "Call-ID");
133 #ifdef HAVE_VV
134 struct sipe_media_call_private *call_private =
135 g_hash_table_lookup(sipe_private->media_calls, callid);
136 if (is_media_session_msg(call_private, msg)) {
137 process_incoming_cancel_call(call_private, msg);
138 return;
140 #endif
142 if (!sipe_session_find_chat_by_callid(sipe_private, callid))
143 sipe_conf_cancel_unaccepted(sipe_private, msg);
146 void process_incoming_info(struct sipe_core_private *sipe_private,
147 struct sipmsg *msg)
149 const gchar *contenttype = sipmsg_find_header(msg, "Content-Type");
150 const gchar *callid = sipmsg_find_header(msg, "Call-ID");
151 gchar *from;
152 struct sip_session *session;
154 SIPE_DEBUG_INFO_NOFORMAT("process_incoming_info");
156 /* Call Control protocol */
157 if (g_str_has_prefix(contenttype, "application/csta+xml"))
159 process_incoming_info_csta(sipe_private, msg);
160 return;
162 else if (g_str_has_prefix(contenttype, "application/xml+conversationinfo"))
164 process_incoming_info_conversation(sipe_private, msg);
165 return;
167 #ifdef HAVE_XDATA
168 else if (g_str_has_prefix(contenttype, "application/ms-filetransfer+xml"))
170 process_incoming_info_ft_lync(sipe_private, msg);
171 return;
173 #endif
175 from = parse_from(sipmsg_find_header(msg, "From"));
176 session = sipe_session_find_chat_or_im(sipe_private, callid, from);
177 if (!session) {
178 g_free(from);
179 return;
182 /* Group Chat uses text/plain */
183 if (session->is_groupchat) {
184 process_incoming_info_groupchat(sipe_private, msg, session);
185 g_free(from);
186 return;
189 if (g_str_has_prefix(contenttype, "application/x-ms-mim"))
191 sipe_xml *xn_action = sipe_xml_parse(msg->body, msg->bodylen);
192 const sipe_xml *xn_request_rm = sipe_xml_child(xn_action, "RequestRM");
193 const sipe_xml *xn_set_rm = sipe_xml_child(xn_action, "SetRM");
195 sipmsg_add_header(msg, "Content-Type", "application/x-ms-mim");
197 if (xn_request_rm) {
198 //const char *rm = sipe_xml_attribute(xn_request_rm, "uri");
199 int bid = sipe_xml_int_attribute(xn_request_rm, "bid", 0);
200 gchar *body = g_strdup_printf(
201 "<?xml version=\"1.0\"?>\r\n"
202 "<action xmlns=\"http://schemas.microsoft.com/sip/multiparty/\">"
203 "<RequestRMResponse uri=\"sip:%s\" allow=\"%s\"/></action>\r\n",
204 sipe_private->username,
205 session->bid < bid ? "true" : "false");
206 sip_transport_response(sipe_private, msg, 200, "OK", body);
207 g_free(body);
208 } else if (xn_set_rm) {
209 gchar *body;
211 sipe_chat_set_roster_manager(session,
212 sipe_xml_attribute(xn_set_rm, "uri"));
214 body = g_strdup_printf(
215 "<?xml version=\"1.0\"?>\r\n"
216 "<action xmlns=\"http://schemas.microsoft.com/sip/multiparty/\">"
217 "<SetRMResponse uri=\"sip:%s\"/></action>\r\n",
218 sipe_private->username);
219 sip_transport_response(sipe_private, msg, 200, "OK", body);
220 g_free(body);
222 sipe_xml_free(xn_action);
225 else
227 /* looks like purple lacks typing notification for chat */
228 if (!session->chat_session) {
229 sipe_xml *xn_keyboard_activity = sipe_xml_parse(msg->body, msg->bodylen);
230 const char *status = sipe_xml_attribute(sipe_xml_child(xn_keyboard_activity, "status"),
231 "status");
232 if (sipe_strequal(status, "type")) {
233 sipe_backend_user_feedback_typing(SIPE_CORE_PUBLIC,
234 from);
235 } else if (sipe_strequal(status, "idle")) {
236 sipe_backend_user_feedback_typing_stop(SIPE_CORE_PUBLIC,
237 from);
239 sipe_xml_free(xn_keyboard_activity);
242 sip_transport_response(sipe_private, msg, 200, "OK", NULL);
244 g_free(from);
247 static gboolean sipe_process_incoming_x_msmsgsinvite(struct sipe_core_private *sipe_private,
248 struct sip_dialog *dialog,
249 GSList *parsed_body)
251 gboolean found = FALSE;
253 if (parsed_body) {
254 const gchar *invitation_command = sipe_utils_nameval_find(parsed_body, "Invitation-Command");
256 if (sipe_strequal(invitation_command, "INVITE")) {
257 sipe_ft_incoming_transfer(sipe_private, dialog, parsed_body);
258 found = TRUE;
259 } else if (sipe_strequal(invitation_command, "CANCEL")) {
260 sipe_ft_incoming_cancel(dialog, parsed_body);
261 found = TRUE;
262 } else if (sipe_strequal(invitation_command, "ACCEPT")) {
263 sipe_ft_incoming_accept(dialog, parsed_body);
264 found = TRUE;
267 return found;
270 #ifdef HAVE_VV
271 static void sipe_invite_mime_cb(gpointer user_data, const GSList *fields,
272 const gchar *body, gsize length)
274 const gchar *type = sipe_utils_nameval_find(fields, "Content-Type");
275 const gchar *cd = sipe_utils_nameval_find(fields, "Content-Disposition");
277 if (!g_str_has_prefix(type, "application/sdp"))
278 return;
280 if (!cd || !strstr(cd, "ms-proxy-2007fallback")) {
281 struct sipmsg *msg = user_data;
282 const gchar* msg_ct = sipmsg_find_header(msg, "Content-Type");
284 if (g_str_has_prefix(msg_ct, "application/sdp")) {
285 /* We have already found suitable alternative and set message's body
286 * and Content-Type accordingly */
287 return;
290 sipmsg_remove_header_now(msg, "Content-Type");
291 sipmsg_add_header_now(msg, "Content-Type", type);
293 /* Replace message body with chosen alternative, so we can continue to
294 * process it as a normal single part message. */
295 g_free(msg->body);
296 msg->body = g_strndup(body, length);
297 msg->bodylen = length;
300 #endif
302 static void send_invite_response(struct sipe_core_private *sipe_private,
303 struct sipmsg *msg)
305 gchar *body = g_strdup_printf(
306 "v=0\r\n"
307 "o=- 0 0 IN IP4 %s\r\n"
308 "s=session\r\n"
309 "c=IN IP4 %s\r\n"
310 "t=0 0\r\n"
311 "m=%s %d sip sip:%s\r\n"
312 "a=accept-types:" SDP_ACCEPT_TYPES "\r\n",
313 sip_transport_ip_address(sipe_private),
314 sip_transport_ip_address(sipe_private),
315 SIPE_CORE_PRIVATE_FLAG_IS(OCS2007) ? "message" : "x-ms-message",
316 sip_transport_port(sipe_private),
317 sipe_private->username);
318 sipmsg_add_header(msg, "Content-Type", "application/sdp");
319 sip_transport_response(sipe_private, msg, 200, "OK", body);
320 g_free(body);
323 struct sipe_delayed_invite {
324 gchar *action;
325 struct sipmsg *msg;
328 static void delayed_invite_destroy(gpointer data)
330 struct sipe_delayed_invite *delayed_invite = data;
331 sipmsg_free(delayed_invite->msg);
332 g_free(delayed_invite->action);
333 g_free(delayed_invite);
336 static void delayed_invite_timeout(struct sipe_core_private *sipe_private,
337 gpointer data)
339 struct sipe_delayed_invite *delayed_invite = data;
340 send_invite_response(sipe_private, delayed_invite->msg);
343 static void delayed_invite_response(struct sipe_core_private *sipe_private,
344 struct sipmsg *msg,
345 const gchar *callid)
347 struct sipe_delayed_invite *delayed_invite = g_new0(struct sipe_delayed_invite, 1);
349 delayed_invite->action = g_strdup_printf("<delayed-invite-%s>", callid);
350 delayed_invite->msg = sipmsg_copy(msg);
351 sipe_schedule_seconds(sipe_private,
352 delayed_invite->action,
353 delayed_invite,
355 delayed_invite_timeout,
356 delayed_invite_destroy);
359 void sipe_incoming_cancel_delayed_invite(struct sipe_core_private *sipe_private,
360 struct sip_dialog *dialog)
362 struct sipe_delayed_invite *delayed_invite = dialog->delayed_invite;
363 dialog->delayed_invite = NULL;
364 send_invite_response(sipe_private, delayed_invite->msg);
365 sipe_schedule_cancel(sipe_private, delayed_invite->action);
368 void process_incoming_invite(struct sipe_core_private *sipe_private,
369 struct sipmsg *msg)
371 gchar *newTag;
372 const gchar *oldHeader;
373 gchar *newHeader;
374 gboolean is_multiparty = FALSE;
375 gboolean was_multiparty = TRUE;
376 gboolean just_joined = FALSE;
377 gchar *from;
378 const gchar *callid = sipmsg_find_header(msg, "Call-ID");
379 const gchar *roster_manager = sipmsg_find_header(msg, "Roster-Manager");
380 const gchar *end_points_hdr = sipmsg_find_header(msg, "EndPoints");
381 const gchar *trig_invite = sipmsg_find_header(msg, "TriggeredInvite");
382 const gchar *content_type = sipmsg_find_header(msg, "Content-Type");
383 const gchar *subject = sipmsg_find_header(msg, "Subject");
384 GSList *end_points = NULL;
385 struct sip_session *session;
386 struct sip_dialog *dialog;
387 const gchar *ms_text_format;
388 gboolean dont_delay = FALSE;
390 #ifdef HAVE_VV
391 if (g_str_has_prefix(content_type, "multipart/alternative")) {
392 sipe_mime_parts_foreach(content_type, msg->body, sipe_invite_mime_cb, msg);
393 /* Reload Content-Type to get type of the selected message part */
394 content_type = sipmsg_find_header(msg, "Content-Type");
396 #endif
398 if (g_str_has_prefix(content_type, "multipart/mixed")) {
399 if (sipe_mime_parts_contain(content_type, msg->body,
400 "application/ms-filetransfer+xml")) {
401 /* Lync 2010 file transfer */
402 #ifdef HAVE_XDATA
403 process_incoming_invite_ft_lync(sipe_private, msg);
404 #else
405 sip_transport_response(sipe_private, msg,
406 488, "Not Acceptable Here", NULL);
407 #endif
408 return;
412 /* Invitation to join conference */
413 if (g_str_has_prefix(content_type, "application/ms-conf-invite+xml")) {
414 process_incoming_invite_conf(sipe_private, msg);
415 return;
418 #ifdef HAVE_VV
419 /* Application sharing */
420 if (sipe_strcase_equal(content_type, "application/sdp") && msg->body &&
421 strstr(msg->body, "m=applicationsharing") &&
422 sipe_strequal(sipmsg_find_header(msg, "CSeq"), "1 INVITE")) {
423 #if defined(HAVE_XDATA) && defined(HAVE_GIO)
424 process_incoming_invite_appshare(sipe_private, msg);
425 #else
426 sip_transport_response(sipe_private, msg,
427 488, "Not Acceptable Here", NULL);
428 #endif
429 return;
432 /* Invitation to audio call or file transfer */
433 if (msg->body &&
434 (strstr(msg->body, "m=audio") || strstr(msg->body, "m=data") || strstr(msg->body, "m=applicationsharing"))) {
435 process_incoming_invite_call(sipe_private, msg);
436 return;
438 #endif
440 /* Only accept text invitations */
441 if (msg->body && !(strstr(msg->body, "m=message") || strstr(msg->body, "m=x-ms-message"))) {
442 sip_transport_response(sipe_private, msg, 501, "Not implemented", NULL);
443 return;
446 // TODO There *must* be a better way to clean up the To header to add a tag...
447 SIPE_DEBUG_INFO_NOFORMAT("Adding a Tag to the To Header on Invite Request...");
448 oldHeader = sipmsg_find_header(msg, "To");
449 newTag = gentag();
450 newHeader = g_strdup_printf("%s;tag=%s", oldHeader, newTag);
451 g_free(newTag);
452 sipmsg_remove_header_now(msg, "To");
453 sipmsg_add_header_now(msg, "To", newHeader);
454 g_free(newHeader);
456 if (end_points_hdr) {
457 end_points = sipmsg_parse_endpoints_header(end_points_hdr);
459 if (g_slist_length(end_points) > 2) {
460 is_multiparty = TRUE;
463 if (trig_invite && !g_ascii_strcasecmp(trig_invite, "TRUE")) {
464 is_multiparty = TRUE;
467 /* Multiparty session */
468 session = sipe_session_find_chat_by_callid(sipe_private, callid);
469 if (is_multiparty) {
471 if (session) {
472 if (session->chat_session) {
473 /* Update roster manager for existing multiparty session */
474 if (roster_manager)
475 sipe_chat_set_roster_manager(session, roster_manager);
477 } else {
478 gchar *chat_title = sipe_chat_get_name();
480 /* Convert IM session to multiparty session */
481 g_free(session->with);
482 session->with = NULL;
483 was_multiparty = FALSE;
484 session->chat_session = sipe_chat_create_session(SIPE_CHAT_TYPE_MULTIPARTY,
485 roster_manager,
486 chat_title);
488 g_free(chat_title);
490 } else {
491 /* New multiparty session */
492 session = sipe_session_add_chat(sipe_private,
493 NULL,
494 TRUE,
495 roster_manager);
498 /* Create chat */
499 if (!session->chat_session->backend) {
500 gchar *self = sip_uri_self(sipe_private);
501 session->chat_session->backend = sipe_backend_chat_create(SIPE_CORE_PUBLIC,
502 session->chat_session,
503 session->chat_session->title,
504 self);
505 g_free(self);
509 /* IM session */
510 from = parse_from(sipmsg_find_header(msg, "From"));
511 if (!session)
512 session = sipe_session_find_or_add_im(sipe_private, from);
514 /* session is now initialized */
515 g_free(session->callid);
516 session->callid = g_strdup(callid);
518 if (is_multiparty && end_points) {
519 gchar *to = parse_from(sipmsg_find_header(msg, "To"));
520 GSList *entry = end_points;
521 while (entry) {
522 struct sipendpoint *end_point = entry->data;
523 entry = entry->next;
525 if (!g_ascii_strcasecmp(from, end_point->contact) ||
526 !g_ascii_strcasecmp(to, end_point->contact))
527 continue;
529 dialog = sipe_dialog_find(session, end_point->contact);
530 if (dialog) {
531 g_free(dialog->theirepid);
532 dialog->theirepid = end_point->epid;
533 end_point->epid = NULL;
534 } else {
535 dialog = sipe_dialog_add(session);
537 dialog->callid = g_strdup(session->callid);
538 dialog->with = end_point->contact;
539 end_point->contact = NULL;
540 dialog->theirepid = end_point->epid;
541 end_point->epid = NULL;
543 just_joined = TRUE;
545 /* send triggered INVITE */
546 sipe_im_invite(sipe_private, session, dialog->with, NULL, NULL, NULL, TRUE);
549 g_free(to);
552 if (end_points) {
553 GSList *entry = end_points;
554 while (entry) {
555 struct sipendpoint *end_point = entry->data;
556 entry = entry->next;
557 g_free(end_point->contact);
558 g_free(end_point->epid);
559 g_free(end_point);
561 g_slist_free(end_points);
564 dialog = sipe_dialog_find(session, from);
565 if (dialog) {
566 sipe_im_cancel_dangling(sipe_private, session, dialog, from,
567 sipe_im_reenqueue_unconfirmed);
568 /* dialog is no longer valid */
569 } else {
570 just_joined = TRUE;
573 dialog = sipe_dialog_add(session);
574 dialog->with = g_strdup(from);
575 dialog->callid = g_strdup(session->callid);
576 dialog->is_established = TRUE;
577 sipe_dialog_parse(dialog, msg, FALSE);
579 if (is_multiparty && !was_multiparty) {
580 /* add current IM counterparty to chat */
581 sipe_backend_chat_add(session->chat_session->backend,
582 sipe_dialog_first(session)->with,
583 FALSE);
586 /* add inviting party to chat */
587 if (just_joined && session->chat_session) {
588 sipe_backend_chat_add(session->chat_session->backend,
589 from,
590 TRUE);
593 if (!is_multiparty && subject)
594 sipe_im_topic(sipe_private, session, subject);
596 /* ms-text-format: text/plain; charset=UTF-8;msgr=WAAtAE0...DIADQAKAA0ACgA;ms-body=SGk= */
598 /* This used only in 2005 official client, not 2007 or Reuters.
599 Disabled for most cases as interfering with audit of messages which only is applied to regular MESSAGEs.
600 Only enabled for 2005 multiparty chats as otherwise the first message got lost completely.
602 /* also enabled for 2005 file transfer. Didn't work otherwise. */
603 ms_text_format = sipmsg_find_header(msg, "ms-text-format");
604 if (is_multiparty ||
605 (ms_text_format && g_str_has_prefix(ms_text_format, "text/x-msmsgsinvite")) )
607 if (ms_text_format) {
608 if (g_str_has_prefix(ms_text_format, "text/x-msmsgsinvite"))
610 dont_delay = TRUE;
612 else if (g_str_has_prefix(ms_text_format, "text/plain") || g_str_has_prefix(ms_text_format, "text/html"))
614 /* please do not optimize logic inside as this code may be re-enabled for other cases */
615 gchar *html = get_html_message(ms_text_format, NULL);
616 if (html) {
617 if (is_multiparty) {
618 sipe_backend_chat_message(SIPE_CORE_PUBLIC,
619 session->chat_session->backend,
620 from,
622 html);
623 } else {
624 sipe_backend_im_message(SIPE_CORE_PUBLIC,
625 from,
626 html);
628 g_free(html);
629 sipmsg_add_header(msg, "Supported", "ms-text-format"); /* accepts received message */
630 dont_delay = TRUE;
636 g_free(from);
638 sipmsg_add_header(msg, "Supported", "com.microsoft.rtc-multiparty");
640 if (dont_delay || !SIPE_CORE_PRIVATE_FLAG_IS(MPOP)) {
641 send_invite_response(sipe_private, msg);
642 } else {
643 delayed_invite_response(sipe_private, msg, session->callid);
647 void process_incoming_message(struct sipe_core_private *sipe_private,
648 struct sipmsg *msg)
650 gchar *from;
651 const gchar *contenttype;
652 gboolean found = FALSE;
654 from = parse_from(sipmsg_find_header(msg, "From"));
656 if (!from) return;
658 SIPE_DEBUG_INFO("got message from %s: %s", from, msg->body);
660 contenttype = sipmsg_find_header(msg, "Content-Type");
661 if (g_str_has_prefix(contenttype, "text/plain")
662 || g_str_has_prefix(contenttype, "text/html")
663 || g_str_has_prefix(contenttype, "multipart/related")
664 || g_str_has_prefix(contenttype, "multipart/alternative"))
666 const gchar *callid = sipmsg_find_header(msg, "Call-ID");
667 gchar *html = get_html_message(contenttype, msg->body);
669 struct sip_session *session = sipe_session_find_chat_or_im(sipe_private,
670 callid,
671 from);
672 if (session && session->chat_session) {
673 if (session->chat_session->type == SIPE_CHAT_TYPE_CONFERENCE) { /* a conference */
674 gchar *tmp = parse_from(sipmsg_find_header(msg, "Ms-Sender"));
675 gchar *sender = parse_from(tmp);
676 g_free(tmp);
677 sipe_backend_chat_message(SIPE_CORE_PUBLIC,
678 session->chat_session->backend,
679 sender,
681 html);
682 g_free(sender);
683 } else { /* a multiparty chat */
684 sipe_backend_chat_message(SIPE_CORE_PUBLIC,
685 session->chat_session->backend,
686 from,
688 html);
690 } else {
691 sipe_backend_im_message(SIPE_CORE_PUBLIC,
692 from,
693 html);
695 g_free(html);
696 sip_transport_response(sipe_private, msg, 200, "OK", NULL);
697 found = TRUE;
699 } else if (g_str_has_prefix(contenttype, "application/im-iscomposing+xml")) {
700 sipe_xml *isc = sipe_xml_parse(msg->body, msg->bodylen);
701 const sipe_xml *state;
702 gchar *statedata;
704 if (!isc) {
705 SIPE_DEBUG_INFO_NOFORMAT("process_incoming_message: can not parse iscomposing");
706 g_free(from);
707 return;
710 state = sipe_xml_child(isc, "state");
712 if (!state) {
713 SIPE_DEBUG_INFO_NOFORMAT("process_incoming_message: no state found");
714 sipe_xml_free(isc);
715 g_free(from);
716 return;
719 statedata = sipe_xml_data(state);
720 if (statedata) {
721 if (strstr(statedata, "active")) {
722 sipe_backend_user_feedback_typing(SIPE_CORE_PUBLIC,
723 from);
724 } else {
725 sipe_backend_user_feedback_typing_stop(SIPE_CORE_PUBLIC,
726 from);
728 g_free(statedata);
730 sipe_xml_free(isc);
731 sip_transport_response(sipe_private, msg, 200, "OK", NULL);
732 found = TRUE;
733 } else if (g_str_has_prefix(contenttype, "text/x-msmsgsinvite")) {
734 const gchar *callid = sipmsg_find_header(msg, "Call-ID");
735 struct sip_session *session = sipe_session_find_chat_or_im(sipe_private,
736 callid,
737 from);
738 if (session) {
739 struct sip_dialog *dialog = sipe_dialog_find(session, from);
740 GSList *body = sipe_ft_parse_msg_body(msg->body);
741 found = sipe_process_incoming_x_msmsgsinvite(sipe_private, dialog, body);
742 sipe_utils_nameval_free(body);
743 if (found) {
744 sip_transport_response(sipe_private, msg, 200, "OK", NULL);
746 } else {
747 sip_transport_response(sipe_private, msg, 481,
748 "Call Leg/Transaction Does Not Exist", NULL);
749 found = TRUE;
752 if (!found) {
753 const gchar *callid = sipmsg_find_header(msg, "Call-ID");
754 struct sip_session *session = sipe_session_find_chat_or_im(sipe_private,
755 callid,
756 from);
757 if (session) {
758 gchar *errmsg = g_strdup_printf(_("Received a message with unrecognized contents from %s"),
759 from);
760 sipe_user_present_error(sipe_private, session, errmsg);
761 g_free(errmsg);
764 SIPE_DEBUG_INFO("got unknown mime-type '%s'", contenttype);
765 sip_transport_response(sipe_private, msg, 415, "Unsupported media type", NULL);
767 g_free(from);
770 void process_incoming_options(struct sipe_core_private *sipe_private,
771 struct sipmsg *msg)
773 gchar *body;
775 sipmsg_add_header(msg, "Allow", "INVITE, MESSAGE, INFO, SUBSCRIBE, OPTIONS, BYE, CANCEL, NOTIFY, ACK, REFER, BENOTIFY");
776 sipmsg_add_header(msg, "Content-Type", "application/sdp");
778 body = g_strdup_printf(
779 "v=0\r\n"
780 "o=- 0 0 IN IP4 0.0.0.0\r\n"
781 "s=session\r\n"
782 "c=IN IP4 0.0.0.0\r\n"
783 "t=0 0\r\n"
784 "m=%s %d sip sip:%s\r\n"
785 "a=accept-types:" SDP_ACCEPT_TYPES "\r\n",
786 SIPE_CORE_PRIVATE_FLAG_IS(OCS2007) ? "message" : "x-ms-message",
787 sip_transport_port(sipe_private),
788 sipe_private->username);
789 sip_transport_response(sipe_private, msg, 200, "OK", body);
790 g_free(body);
793 void process_incoming_refer(struct sipe_core_private *sipe_private,
794 struct sipmsg *msg)
796 gchar *self = sip_uri_self(sipe_private);
797 const gchar *callid = sipmsg_find_header(msg, "Call-ID");
798 gchar *from = parse_from(sipmsg_find_header(msg, "From"));
799 gchar *refer_to = parse_from(sipmsg_find_header(msg, "Refer-to"));
800 gchar *referred_by = g_strdup(sipmsg_find_header(msg, "Referred-By"));
801 struct sip_session *session;
802 struct sip_dialog *dialog;
804 session = sipe_session_find_chat_by_callid(sipe_private, callid);
805 dialog = sipe_dialog_find(session, from);
807 if (!session || !dialog || !session->chat_session ||
808 (session->chat_session->type != SIPE_CHAT_TYPE_MULTIPARTY) ||
809 !session->chat_session->id ||
810 !sipe_strcase_equal(session->chat_session->id, self)) {
811 sip_transport_response(sipe_private, msg, 500, "Server Internal Error", NULL);
812 } else {
813 sip_transport_response(sipe_private, msg, 202, "Accepted", NULL);
815 sipe_im_invite(sipe_private, session, refer_to, NULL, NULL, referred_by, FALSE);
818 g_free(self);
819 g_free(from);
820 g_free(refer_to);
821 g_free(referred_by);
825 Local Variables:
826 mode: c
827 c-file-style: "bsd"
828 indent-tabs-mode: t
829 tab-width: 8
830 End: