incoming: set dialog established flag on INVITE
[siplcs.git] / src / core / sipe-incoming.c
blob32e4dceb70e8f60b262d214c2a0e07a655806303
1 /**
2 * @file sipe-incoming.c
4 * pidgin-sipe
6 * Copyright (C) 2010-2013 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 #include "sipe-common.h"
33 #include "sipmsg.h"
34 #include "sip-csta.h"
35 #include "sip-transport.h"
36 #include "sipe-backend.h"
37 #include "sipe-chat.h"
38 #include "sipe-conf.h"
39 #include "sipe-core.h"
40 #include "sipe-core-private.h"
41 #include "sipe-dialog.h"
42 #include "sipe-ft.h"
43 #include "sipe-groupchat.h"
44 #include "sipe-im.h"
45 #include "sipe-incoming.h"
46 #include "sipe-media.h"
47 #include "sipe-mime.h"
48 #include "sipe-nls.h"
49 #include "sipe-schedule.h"
50 #include "sipe-session.h"
51 #include "sipe-user.h"
52 #include "sipe-utils.h"
53 #include "sipe-xml.h"
55 void process_incoming_bye(struct sipe_core_private *sipe_private,
56 struct sipmsg *msg)
58 const gchar *callid = sipmsg_find_header(msg, "Call-ID");
59 gchar *from = parse_from(sipmsg_find_header(msg, "From"));
60 struct sip_session *session;
61 struct sip_dialog *dialog;
63 #ifdef HAVE_VV
64 if (is_media_session_msg(sipe_private->media_call, msg)) {
65 // BYE ends a media call
66 sipe_media_hangup(sipe_private->media_call);
68 #endif
70 /* collect dialog identification
71 * we need callid, ourtag and theirtag to unambiguously identify dialog
73 /* take data before 'msg' will be modified by sip_transport_response */
74 dialog = g_new0(struct sip_dialog, 1);
75 dialog->callid = g_strdup(callid);
76 dialog->cseq = sipmsg_parse_cseq(msg);
77 dialog->with = g_strdup(from);
78 sipe_dialog_parse(dialog, msg, FALSE);
80 sip_transport_response(sipe_private, msg, 200, "OK", NULL);
82 session = sipe_session_find_chat_or_im(sipe_private, callid, from);
83 if (!session) {
84 SIPE_DEBUG_INFO_NOFORMAT("process_incoming_bye: couldn't find session. Ignoring");
85 sipe_dialog_free(dialog);
86 g_free(from);
87 return;
90 SIPE_DEBUG_INFO("process_incoming_bye: session found (chat ID %s)",
91 (session->chat_session && session->chat_session->id) ?
92 session->chat_session->id : "<NO CHAT>");
94 if (session->chat_session &&
95 (session->chat_session->type == SIPE_CHAT_TYPE_MULTIPARTY) &&
96 session->chat_session->id &&
97 !g_ascii_strcasecmp(from, session->chat_session->id))
98 sipe_chat_set_roster_manager(session, NULL);
100 sipe_im_cancel_unconfirmed(sipe_private, session, callid, from);
102 /* This what BYE is essentially for - terminating dialog */
103 sipe_dialog_remove_3(session, dialog);
104 sipe_dialog_free(dialog);
105 if (session->chat_session) {
106 if ((session->chat_session->type == SIPE_CHAT_TYPE_CONFERENCE) &&
107 !g_ascii_strcasecmp(from, session->im_mcu_uri)) {
108 SIPE_DEBUG_INFO("process_incoming_bye: disconnected from conference %s",
109 session->im_mcu_uri);
110 sipe_conf_immcu_closed(sipe_private, session);
111 } else if (session->chat_session->type == SIPE_CHAT_TYPE_MULTIPARTY) {
112 SIPE_DEBUG_INFO_NOFORMAT("process_incoming_bye: disconnected from multiparty chat");
113 sipe_backend_chat_remove(session->chat_session->backend,
114 from);
118 g_free(from);
121 void process_incoming_cancel(struct sipe_core_private *sipe_private,
122 struct sipmsg *msg)
124 const gchar *callid;
126 #ifdef HAVE_VV
127 if (is_media_session_msg(sipe_private->media_call, msg)) {
128 process_incoming_cancel_call(sipe_private, msg);
129 return;
131 #endif
132 callid = sipmsg_find_header(msg, "Call-ID");
134 if (!sipe_session_find_chat_by_callid(sipe_private, callid))
135 sipe_conf_cancel_unaccepted(sipe_private, msg);
138 void process_incoming_info(struct sipe_core_private *sipe_private,
139 struct sipmsg *msg)
141 const gchar *contenttype = sipmsg_find_header(msg, "Content-Type");
142 const gchar *callid = sipmsg_find_header(msg, "Call-ID");
143 gchar *from;
144 struct sip_session *session;
146 SIPE_DEBUG_INFO_NOFORMAT("process_incoming_info");
148 /* Call Control protocol */
149 if (g_str_has_prefix(contenttype, "application/csta+xml"))
151 process_incoming_info_csta(sipe_private, msg);
152 return;
154 else if (g_str_has_prefix(contenttype, "application/xml+conversationinfo"))
156 process_incoming_info_conversation(sipe_private, msg);
157 return;
160 from = parse_from(sipmsg_find_header(msg, "From"));
161 session = sipe_session_find_chat_or_im(sipe_private, callid, from);
162 if (!session) {
163 g_free(from);
164 return;
167 /* Group Chat uses text/plain */
168 if (session->is_groupchat) {
169 process_incoming_info_groupchat(sipe_private, msg, session);
170 g_free(from);
171 return;
174 if (g_str_has_prefix(contenttype, "application/x-ms-mim"))
176 sipe_xml *xn_action = sipe_xml_parse(msg->body, msg->bodylen);
177 const sipe_xml *xn_request_rm = sipe_xml_child(xn_action, "RequestRM");
178 const sipe_xml *xn_set_rm = sipe_xml_child(xn_action, "SetRM");
180 sipmsg_add_header(msg, "Content-Type", "application/x-ms-mim");
182 if (xn_request_rm) {
183 //const char *rm = sipe_xml_attribute(xn_request_rm, "uri");
184 int bid = sipe_xml_int_attribute(xn_request_rm, "bid", 0);
185 gchar *body = g_strdup_printf(
186 "<?xml version=\"1.0\"?>\r\n"
187 "<action xmlns=\"http://schemas.microsoft.com/sip/multiparty/\">"
188 "<RequestRMResponse uri=\"sip:%s\" allow=\"%s\"/></action>\r\n",
189 sipe_private->username,
190 session->bid < bid ? "true" : "false");
191 sip_transport_response(sipe_private, msg, 200, "OK", body);
192 g_free(body);
193 } else if (xn_set_rm) {
194 gchar *body;
196 sipe_chat_set_roster_manager(session,
197 sipe_xml_attribute(xn_set_rm, "uri"));
199 body = g_strdup_printf(
200 "<?xml version=\"1.0\"?>\r\n"
201 "<action xmlns=\"http://schemas.microsoft.com/sip/multiparty/\">"
202 "<SetRMResponse uri=\"sip:%s\"/></action>\r\n",
203 sipe_private->username);
204 sip_transport_response(sipe_private, msg, 200, "OK", body);
205 g_free(body);
207 sipe_xml_free(xn_action);
210 else
212 /* looks like purple lacks typing notification for chat */
213 if (!session->chat_session) {
214 sipe_xml *xn_keyboard_activity = sipe_xml_parse(msg->body, msg->bodylen);
215 const char *status = sipe_xml_attribute(sipe_xml_child(xn_keyboard_activity, "status"),
216 "status");
217 if (sipe_strequal(status, "type")) {
218 sipe_backend_user_feedback_typing(SIPE_CORE_PUBLIC,
219 from);
220 } else if (sipe_strequal(status, "idle")) {
221 sipe_backend_user_feedback_typing_stop(SIPE_CORE_PUBLIC,
222 from);
224 sipe_xml_free(xn_keyboard_activity);
227 sip_transport_response(sipe_private, msg, 200, "OK", NULL);
229 g_free(from);
232 static gboolean sipe_process_incoming_x_msmsgsinvite(struct sipe_core_private *sipe_private,
233 struct sip_dialog *dialog,
234 GSList *parsed_body)
236 gboolean found = FALSE;
238 if (parsed_body) {
239 const gchar *invitation_command = sipe_utils_nameval_find(parsed_body, "Invitation-Command");
241 if (sipe_strequal(invitation_command, "INVITE")) {
242 sipe_ft_incoming_transfer(sipe_private, dialog, parsed_body);
243 found = TRUE;
244 } else if (sipe_strequal(invitation_command, "CANCEL")) {
245 sipe_ft_incoming_cancel(dialog, parsed_body);
246 found = TRUE;
247 } else if (sipe_strequal(invitation_command, "ACCEPT")) {
248 sipe_ft_incoming_accept(dialog, parsed_body);
249 found = TRUE;
252 return found;
255 #ifdef HAVE_VV
256 static void sipe_invite_mime_cb(gpointer user_data, const GSList *fields,
257 const gchar *body, gsize length)
259 const gchar *type = sipe_utils_nameval_find(fields, "Content-Type");
260 const gchar *cd = sipe_utils_nameval_find(fields, "Content-Disposition");
262 if (!g_str_has_prefix(type, "application/sdp"))
263 return;
265 if (!cd || !strstr(cd, "ms-proxy-2007fallback")) {
266 struct sipmsg *msg = user_data;
267 const gchar* msg_ct = sipmsg_find_header(msg, "Content-Type");
269 if (g_str_has_prefix(msg_ct, "application/sdp")) {
270 /* We have already found suitable alternative and set message's body
271 * and Content-Type accordingly */
272 return;
275 sipmsg_remove_header_now(msg, "Content-Type");
276 sipmsg_add_header_now(msg, "Content-Type", type);
278 /* Replace message body with chosen alternative, so we can continue to
279 * process it as a normal single part message. */
280 g_free(msg->body);
281 msg->body = g_strndup(body, length);
282 msg->bodylen = length;
285 #endif
287 static void sipe_invite_mime_mixed_cb(gpointer user_data, const GSList *fields,
288 SIPE_UNUSED_PARAMETER const gchar *body, SIPE_UNUSED_PARAMETER gsize length)
290 const gchar *ctype = sipe_utils_nameval_find(fields, "Content-Type");
292 /* Lync 2010 file transfer */
293 if (g_str_has_prefix(ctype, "application/ms-filetransfer+xml")) {
294 struct sipmsg *msg = user_data;
296 sipmsg_remove_header_now(msg, "Content-Type");
297 sipmsg_add_header_now(msg, "Content-Type", ctype);
299 /* Right now, we do not care about the message body, only detect new
300 * file transfer protocol from Content-Type and reply with
301 * 488 Not Acceptable Here to force the old MSOC behavior.
303 * TODO: Extend sipmsg so that it supports multipart messages, as to
304 * implement the new protocol, we need access to both parts of the
305 * message for further processing. */
309 static void send_invite_response(struct sipe_core_private *sipe_private,
310 struct sipmsg *msg)
312 gchar *body = g_strdup_printf(
313 "v=0\r\n"
314 "o=- 0 0 IN IP4 %s\r\n"
315 "s=session\r\n"
316 "c=IN IP4 %s\r\n"
317 "t=0 0\r\n"
318 "m=%s %d sip sip:%s\r\n"
319 "a=accept-types:" SDP_ACCEPT_TYPES "\r\n",
320 sipe_backend_network_ip_address(SIPE_CORE_PUBLIC),
321 sipe_backend_network_ip_address(SIPE_CORE_PUBLIC),
322 SIPE_CORE_PRIVATE_FLAG_IS(OCS2007) ? "message" : "x-ms-message",
323 sip_transport_port(sipe_private),
324 sipe_private->username);
325 sipmsg_add_header(msg, "Content-Type", "application/sdp");
326 sip_transport_response(sipe_private, msg, 200, "OK", body);
327 g_free(body);
330 struct sipe_delayed_invite {
331 gchar *action;
332 struct sipmsg *msg;
335 static void delayed_invite_destroy(gpointer data)
337 struct sipe_delayed_invite *delayed_invite = data;
338 sipmsg_free(delayed_invite->msg);
339 g_free(delayed_invite->action);
340 g_free(delayed_invite);
343 static void delayed_invite_timeout(struct sipe_core_private *sipe_private,
344 gpointer data)
346 struct sipe_delayed_invite *delayed_invite = data;
347 send_invite_response(sipe_private, delayed_invite->msg);
350 static void delayed_invite_response(struct sipe_core_private *sipe_private,
351 struct sipmsg *msg,
352 const gchar *callid)
354 struct sipe_delayed_invite *delayed_invite = g_new0(struct sipe_delayed_invite, 1);
356 delayed_invite->action = g_strdup_printf("<delayed-invite-%s>", callid);
357 delayed_invite->msg = sipmsg_copy(msg);
358 sipe_schedule_seconds(sipe_private,
359 delayed_invite->action,
360 delayed_invite,
362 delayed_invite_timeout,
363 delayed_invite_destroy);
366 void sipe_incoming_cancel_delayed_invite(struct sipe_core_private *sipe_private,
367 struct sip_dialog *dialog)
369 struct sipe_delayed_invite *delayed_invite = dialog->delayed_invite;
370 dialog->delayed_invite = NULL;
371 send_invite_response(sipe_private, delayed_invite->msg);
372 sipe_schedule_cancel(sipe_private, delayed_invite->action);
375 void process_incoming_invite(struct sipe_core_private *sipe_private,
376 struct sipmsg *msg)
378 gchar *newTag;
379 const gchar *oldHeader;
380 gchar *newHeader;
381 gboolean is_multiparty = FALSE;
382 gboolean was_multiparty = TRUE;
383 gboolean just_joined = FALSE;
384 gchar *from;
385 const gchar *callid = sipmsg_find_header(msg, "Call-ID");
386 const gchar *roster_manager = sipmsg_find_header(msg, "Roster-Manager");
387 const gchar *end_points_hdr = sipmsg_find_header(msg, "EndPoints");
388 const gchar *trig_invite = sipmsg_find_header(msg, "TriggeredInvite");
389 const gchar *content_type = sipmsg_find_header(msg, "Content-Type");
390 const gchar *subject = sipmsg_find_header(msg, "Subject");
391 GSList *end_points = NULL;
392 struct sip_session *session;
393 struct sip_dialog *dialog;
394 const gchar *ms_text_format;
395 gboolean dont_delay = FALSE;
397 #ifdef HAVE_VV
398 if (g_str_has_prefix(content_type, "multipart/alternative")) {
399 sipe_mime_parts_foreach(content_type, msg->body, sipe_invite_mime_cb, msg);
400 /* Reload Content-Type to get type of the selected message part */
401 content_type = sipmsg_find_header(msg, "Content-Type");
403 #endif
405 if (g_str_has_prefix(content_type, "multipart/mixed")) {
406 sipe_mime_parts_foreach(content_type, msg->body, sipe_invite_mime_mixed_cb, msg);
407 /* Reload Content-Type to get type of the selected message part */
408 content_type = sipmsg_find_header(msg, "Content-Type");
411 /* Lync 2010 file transfer */
412 if (g_str_has_prefix(content_type, "application/ms-filetransfer+xml")) {
413 sip_transport_response(sipe_private, msg, 488, "Not Acceptable Here", NULL);
414 return;
417 /* Invitation to join conference */
418 if (g_str_has_prefix(content_type, "application/ms-conf-invite+xml")) {
419 process_incoming_invite_conf(sipe_private, msg);
420 return;
423 #ifdef HAVE_VV
424 /* Invitation to audio call */
425 if (msg->body && strstr(msg->body, "m=audio")) {
426 process_incoming_invite_call(sipe_private, msg);
427 return;
429 #endif
431 /* Only accept text invitations */
432 if (msg->body && !(strstr(msg->body, "m=message") || strstr(msg->body, "m=x-ms-message"))) {
433 sip_transport_response(sipe_private, msg, 501, "Not implemented", NULL);
434 return;
437 // TODO There *must* be a better way to clean up the To header to add a tag...
438 SIPE_DEBUG_INFO_NOFORMAT("Adding a Tag to the To Header on Invite Request...");
439 oldHeader = sipmsg_find_header(msg, "To");
440 newTag = gentag();
441 newHeader = g_strdup_printf("%s;tag=%s", oldHeader, newTag);
442 g_free(newTag);
443 sipmsg_remove_header_now(msg, "To");
444 sipmsg_add_header_now(msg, "To", newHeader);
445 g_free(newHeader);
447 if (end_points_hdr) {
448 end_points = sipmsg_parse_endpoints_header(end_points_hdr);
450 if (g_slist_length(end_points) > 2) {
451 is_multiparty = TRUE;
454 if (trig_invite && !g_ascii_strcasecmp(trig_invite, "TRUE")) {
455 is_multiparty = TRUE;
458 /* Multiparty session */
459 session = sipe_session_find_chat_by_callid(sipe_private, callid);
460 if (is_multiparty) {
462 if (session) {
463 if (session->chat_session) {
464 /* Update roster manager for existing multiparty session */
465 if (roster_manager)
466 sipe_chat_set_roster_manager(session, roster_manager);
468 } else {
469 gchar *chat_title = sipe_chat_get_name();
471 /* Convert IM session to multiparty session */
472 g_free(session->with);
473 session->with = NULL;
474 was_multiparty = FALSE;
475 session->chat_session = sipe_chat_create_session(SIPE_CHAT_TYPE_MULTIPARTY,
476 roster_manager,
477 chat_title);
479 g_free(chat_title);
481 } else {
482 /* New multiparty session */
483 session = sipe_session_add_chat(sipe_private,
484 NULL,
485 TRUE,
486 roster_manager);
489 /* Create chat */
490 if (!session->chat_session->backend) {
491 gchar *self = sip_uri_self(sipe_private);
492 session->chat_session->backend = sipe_backend_chat_create(SIPE_CORE_PUBLIC,
493 session->chat_session,
494 session->chat_session->title,
495 self);
496 g_free(self);
500 /* IM session */
501 from = parse_from(sipmsg_find_header(msg, "From"));
502 if (!session)
503 session = sipe_session_find_or_add_im(sipe_private, from);
505 /* session is now initialized */
506 g_free(session->callid);
507 session->callid = g_strdup(callid);
509 if (is_multiparty && end_points) {
510 gchar *to = parse_from(sipmsg_find_header(msg, "To"));
511 GSList *entry = end_points;
512 while (entry) {
513 struct sipendpoint *end_point = entry->data;
514 entry = entry->next;
516 if (!g_ascii_strcasecmp(from, end_point->contact) ||
517 !g_ascii_strcasecmp(to, end_point->contact))
518 continue;
520 dialog = sipe_dialog_find(session, end_point->contact);
521 if (dialog) {
522 g_free(dialog->theirepid);
523 dialog->theirepid = end_point->epid;
524 end_point->epid = NULL;
525 } else {
526 dialog = sipe_dialog_add(session);
528 dialog->callid = g_strdup(session->callid);
529 dialog->with = end_point->contact;
530 end_point->contact = NULL;
531 dialog->theirepid = end_point->epid;
532 end_point->epid = NULL;
534 just_joined = TRUE;
536 /* send triggered INVITE */
537 sipe_im_invite(sipe_private, session, dialog->with, NULL, NULL, NULL, TRUE);
540 g_free(to);
543 if (end_points) {
544 GSList *entry = end_points;
545 while (entry) {
546 struct sipendpoint *end_point = entry->data;
547 entry = entry->next;
548 g_free(end_point->contact);
549 g_free(end_point->epid);
550 g_free(end_point);
552 g_slist_free(end_points);
555 dialog = sipe_dialog_find(session, from);
556 if (dialog) {
557 sipe_im_cancel_dangling(sipe_private, session, dialog, from,
558 sipe_im_reenqueue_unconfirmed);
559 /* dialog is no longer valid */
560 } else {
561 just_joined = TRUE;
564 dialog = sipe_dialog_add(session);
565 dialog->with = g_strdup(from);
566 dialog->callid = g_strdup(session->callid);
567 dialog->is_established = TRUE;
568 sipe_dialog_parse(dialog, msg, FALSE);
570 if (is_multiparty && !was_multiparty) {
571 /* add current IM counterparty to chat */
572 sipe_backend_chat_add(session->chat_session->backend,
573 sipe_dialog_first(session)->with,
574 FALSE);
577 /* add inviting party to chat */
578 if (just_joined && session->chat_session) {
579 sipe_backend_chat_add(session->chat_session->backend,
580 from,
581 TRUE);
584 if (!is_multiparty && subject)
585 sipe_im_topic(sipe_private, session, subject);
587 /* ms-text-format: text/plain; charset=UTF-8;msgr=WAAtAE0...DIADQAKAA0ACgA;ms-body=SGk= */
589 /* This used only in 2005 official client, not 2007 or Reuters.
590 Disabled for most cases as interfering with audit of messages which only is applied to regular MESSAGEs.
591 Only enabled for 2005 multiparty chats as otherwise the first message got lost completely.
593 /* also enabled for 2005 file transfer. Didn't work otherwise. */
594 ms_text_format = sipmsg_find_header(msg, "ms-text-format");
595 if (is_multiparty ||
596 (ms_text_format && g_str_has_prefix(ms_text_format, "text/x-msmsgsinvite")) )
598 if (ms_text_format) {
599 if (g_str_has_prefix(ms_text_format, "text/x-msmsgsinvite"))
601 dont_delay = TRUE;
603 else if (g_str_has_prefix(ms_text_format, "text/plain") || g_str_has_prefix(ms_text_format, "text/html"))
605 /* please do not optimize logic inside as this code may be re-enabled for other cases */
606 gchar *html = get_html_message(ms_text_format, NULL);
607 if (html) {
608 if (is_multiparty) {
609 sipe_backend_chat_message(SIPE_CORE_PUBLIC,
610 session->chat_session->backend,
611 from,
613 html);
614 } else {
615 sipe_backend_im_message(SIPE_CORE_PUBLIC,
616 from,
617 html);
619 g_free(html);
620 sipmsg_add_header(msg, "Supported", "ms-text-format"); /* accepts received message */
621 dont_delay = TRUE;
627 g_free(from);
629 sipmsg_add_header(msg, "Supported", "com.microsoft.rtc-multiparty");
631 if (dont_delay || !SIPE_CORE_PRIVATE_FLAG_IS(MPOP)) {
632 send_invite_response(sipe_private, msg);
633 } else {
634 delayed_invite_response(sipe_private, msg, session->callid);
638 void process_incoming_message(struct sipe_core_private *sipe_private,
639 struct sipmsg *msg)
641 gchar *from;
642 const gchar *contenttype;
643 gboolean found = FALSE;
645 from = parse_from(sipmsg_find_header(msg, "From"));
647 if (!from) return;
649 SIPE_DEBUG_INFO("got message from %s: %s", from, msg->body);
651 contenttype = sipmsg_find_header(msg, "Content-Type");
652 if (g_str_has_prefix(contenttype, "text/plain")
653 || g_str_has_prefix(contenttype, "text/html")
654 || g_str_has_prefix(contenttype, "multipart/related")
655 || g_str_has_prefix(contenttype, "multipart/alternative"))
657 const gchar *callid = sipmsg_find_header(msg, "Call-ID");
658 gchar *html = get_html_message(contenttype, msg->body);
660 struct sip_session *session = sipe_session_find_chat_or_im(sipe_private,
661 callid,
662 from);
663 if (session && session->chat_session) {
664 if (session->chat_session->type == SIPE_CHAT_TYPE_CONFERENCE) { /* a conference */
665 gchar *tmp = parse_from(sipmsg_find_header(msg, "Ms-Sender"));
666 gchar *sender = parse_from(tmp);
667 g_free(tmp);
668 sipe_backend_chat_message(SIPE_CORE_PUBLIC,
669 session->chat_session->backend,
670 sender,
672 html);
673 g_free(sender);
674 } else { /* a multiparty chat */
675 sipe_backend_chat_message(SIPE_CORE_PUBLIC,
676 session->chat_session->backend,
677 from,
679 html);
681 } else {
682 sipe_backend_im_message(SIPE_CORE_PUBLIC,
683 from,
684 html);
686 g_free(html);
687 sip_transport_response(sipe_private, msg, 200, "OK", NULL);
688 found = TRUE;
690 } else if (g_str_has_prefix(contenttype, "application/im-iscomposing+xml")) {
691 sipe_xml *isc = sipe_xml_parse(msg->body, msg->bodylen);
692 const sipe_xml *state;
693 gchar *statedata;
695 if (!isc) {
696 SIPE_DEBUG_INFO_NOFORMAT("process_incoming_message: can not parse iscomposing");
697 g_free(from);
698 return;
701 state = sipe_xml_child(isc, "state");
703 if (!state) {
704 SIPE_DEBUG_INFO_NOFORMAT("process_incoming_message: no state found");
705 sipe_xml_free(isc);
706 g_free(from);
707 return;
710 statedata = sipe_xml_data(state);
711 if (statedata) {
712 if (strstr(statedata, "active")) {
713 sipe_backend_user_feedback_typing(SIPE_CORE_PUBLIC,
714 from);
715 } else {
716 sipe_backend_user_feedback_typing_stop(SIPE_CORE_PUBLIC,
717 from);
719 g_free(statedata);
721 sipe_xml_free(isc);
722 sip_transport_response(sipe_private, msg, 200, "OK", NULL);
723 found = TRUE;
724 } else if (g_str_has_prefix(contenttype, "text/x-msmsgsinvite")) {
725 const gchar *callid = sipmsg_find_header(msg, "Call-ID");
726 struct sip_session *session = sipe_session_find_chat_or_im(sipe_private,
727 callid,
728 from);
729 if (session) {
730 struct sip_dialog *dialog = sipe_dialog_find(session, from);
731 GSList *body = sipe_ft_parse_msg_body(msg->body);
732 found = sipe_process_incoming_x_msmsgsinvite(sipe_private, dialog, body);
733 sipe_utils_nameval_free(body);
734 if (found) {
735 sip_transport_response(sipe_private, msg, 200, "OK", NULL);
737 } else {
738 sip_transport_response(sipe_private, msg, 481,
739 "Call Leg/Transaction Does Not Exist", NULL);
740 found = TRUE;
743 if (!found) {
744 const gchar *callid = sipmsg_find_header(msg, "Call-ID");
745 struct sip_session *session = sipe_session_find_chat_or_im(sipe_private,
746 callid,
747 from);
748 if (session) {
749 gchar *errmsg = g_strdup_printf(_("Received a message with unrecognized contents from %s"),
750 from);
751 sipe_user_present_error(sipe_private, session, errmsg);
752 g_free(errmsg);
755 SIPE_DEBUG_INFO("got unknown mime-type '%s'", contenttype);
756 sip_transport_response(sipe_private, msg, 415, "Unsupported media type", NULL);
758 g_free(from);
761 void process_incoming_options(struct sipe_core_private *sipe_private,
762 struct sipmsg *msg)
764 gchar *body;
766 sipmsg_add_header(msg, "Allow", "INVITE, MESSAGE, INFO, SUBSCRIBE, OPTIONS, BYE, CANCEL, NOTIFY, ACK, REFER, BENOTIFY");
767 sipmsg_add_header(msg, "Content-Type", "application/sdp");
769 body = g_strdup_printf(
770 "v=0\r\n"
771 "o=- 0 0 IN IP4 0.0.0.0\r\n"
772 "s=session\r\n"
773 "c=IN IP4 0.0.0.0\r\n"
774 "t=0 0\r\n"
775 "m=%s %d sip sip:%s\r\n"
776 "a=accept-types:" SDP_ACCEPT_TYPES "\r\n",
777 SIPE_CORE_PRIVATE_FLAG_IS(OCS2007) ? "message" : "x-ms-message",
778 sip_transport_port(sipe_private),
779 sipe_private->username);
780 sip_transport_response(sipe_private, msg, 200, "OK", body);
781 g_free(body);
784 void process_incoming_refer(struct sipe_core_private *sipe_private,
785 struct sipmsg *msg)
787 gchar *self = sip_uri_self(sipe_private);
788 const gchar *callid = sipmsg_find_header(msg, "Call-ID");
789 gchar *from = parse_from(sipmsg_find_header(msg, "From"));
790 gchar *refer_to = parse_from(sipmsg_find_header(msg, "Refer-to"));
791 gchar *referred_by = g_strdup(sipmsg_find_header(msg, "Referred-By"));
792 struct sip_session *session;
793 struct sip_dialog *dialog;
795 session = sipe_session_find_chat_by_callid(sipe_private, callid);
796 dialog = sipe_dialog_find(session, from);
798 if (!session || !dialog || !session->chat_session ||
799 (session->chat_session->type != SIPE_CHAT_TYPE_MULTIPARTY) ||
800 !session->chat_session->id ||
801 !sipe_strcase_equal(session->chat_session->id, self)) {
802 sip_transport_response(sipe_private, msg, 500, "Server Internal Error", NULL);
803 } else {
804 sip_transport_response(sipe_private, msg, 202, "Accepted", NULL);
806 sipe_im_invite(sipe_private, session, refer_to, NULL, NULL, referred_by, FALSE);
809 g_free(self);
810 g_free(from);
811 g_free(refer_to);
812 g_free(referred_by);
816 Local Variables:
817 mode: c
818 c-file-style: "bsd"
819 indent-tabs-mode: t
820 tab-width: 8
821 End: