media: split out SDP parsing
[siplcs.git] / src / core / sipe-incoming.c
blob0ad99a2e0167a98cfc286544972c9858564f212a
1 /**
2 * @file sipe-incoming.c
4 * pidgin-sipe
6 * Copyright (C) 2010-2017 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 "sipmsg.h"
33 #include "sip-csta.h"
34 #include "sip-transport.h"
35 #include "sipe-appshare.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-ft-lync.h"
44 #include "sipe-groupchat.h"
45 #include "sipe-im.h"
46 #include "sipe-incoming.h"
47 #include "sipe-media.h"
48 #include "sipe-mime.h"
49 #include "sipe-nls.h"
50 #include "sipe-schedule.h"
51 #include "sipe-session.h"
52 #include "sipe-user.h"
53 #include "sipe-utils.h"
54 #include "sipe-xml.h"
55 #include "sdpmsg.h"
57 void process_incoming_bye(struct sipe_core_private *sipe_private,
58 struct sipmsg *msg)
60 const gchar *callid = sipmsg_find_header(msg, "Call-ID");
61 gchar *from = parse_from(sipmsg_find_header(msg, "From"));
62 struct sip_session *session;
63 struct sip_dialog *dialog;
65 #ifdef HAVE_VV
66 struct sipe_media_call_private *call_private =
67 g_hash_table_lookup(sipe_private->media_calls, callid);
68 if (is_media_session_msg(call_private, msg)) {
69 // BYE ends a media call
70 sipe_media_hangup(call_private);
72 #endif
74 /* collect dialog identification
75 * we need callid, ourtag and theirtag to unambiguously identify dialog
77 /* take data before 'msg' will be modified by sip_transport_response */
78 dialog = g_new0(struct sip_dialog, 1);
79 dialog->callid = g_strdup(callid);
80 dialog->cseq = sipmsg_parse_cseq(msg);
81 dialog->with = g_strdup(from);
82 sipe_dialog_parse(dialog, msg, FALSE);
84 sip_transport_response(sipe_private, msg, 200, "OK", NULL);
86 session = sipe_session_find_chat_or_im(sipe_private, callid, from);
87 if (!session) {
88 SIPE_DEBUG_INFO_NOFORMAT("process_incoming_bye: couldn't find session. Ignoring");
89 sipe_dialog_free(dialog);
90 g_free(from);
91 return;
94 SIPE_DEBUG_INFO("process_incoming_bye: session found (chat ID %s)",
95 (session->chat_session && session->chat_session->id) ?
96 session->chat_session->id : "<NO CHAT>");
98 if (session->chat_session &&
99 (session->chat_session->type == SIPE_CHAT_TYPE_MULTIPARTY) &&
100 session->chat_session->id &&
101 !g_ascii_strcasecmp(from, session->chat_session->id))
102 sipe_chat_set_roster_manager(session, NULL);
104 sipe_im_cancel_unconfirmed(sipe_private, session, callid, from);
106 /* This what BYE is essentially for - terminating dialog */
107 sipe_dialog_remove_3(session, dialog);
108 sipe_dialog_free(dialog);
109 if (session->chat_session) {
110 if ((session->chat_session->type == SIPE_CHAT_TYPE_CONFERENCE) &&
111 !g_ascii_strcasecmp(from, session->im_mcu_uri)) {
112 SIPE_DEBUG_INFO("process_incoming_bye: disconnected from conference %s",
113 session->im_mcu_uri);
114 sipe_conf_immcu_closed(sipe_private, session);
115 } else if (session->chat_session->type == SIPE_CHAT_TYPE_MULTIPARTY) {
116 SIPE_DEBUG_INFO_NOFORMAT("process_incoming_bye: disconnected from multiparty chat");
117 sipe_backend_chat_remove(session->chat_session->backend,
118 from);
122 g_free(from);
125 void process_incoming_cancel(struct sipe_core_private *sipe_private,
126 struct sipmsg *msg)
128 const gchar *callid = sipmsg_find_header(msg, "Call-ID");
130 #ifdef HAVE_VV
131 struct sipe_media_call_private *call_private =
132 g_hash_table_lookup(sipe_private->media_calls, callid);
133 if (is_media_session_msg(call_private, msg)) {
134 process_incoming_cancel_call(call_private, msg);
135 return;
137 #endif
139 if (!sipe_session_find_chat_by_callid(sipe_private, callid))
140 sipe_conf_cancel_unaccepted(sipe_private, msg);
143 void process_incoming_info(struct sipe_core_private *sipe_private,
144 struct sipmsg *msg)
146 const gchar *contenttype = sipmsg_find_header(msg, "Content-Type");
147 const gchar *callid = sipmsg_find_header(msg, "Call-ID");
148 gchar *from;
149 struct sip_session *session;
151 SIPE_DEBUG_INFO_NOFORMAT("process_incoming_info");
153 /* Call Control protocol */
154 if (g_str_has_prefix(contenttype, "application/csta+xml"))
156 process_incoming_info_csta(sipe_private, msg);
157 return;
159 else if (g_str_has_prefix(contenttype, "application/xml+conversationinfo"))
161 process_incoming_info_conversation(sipe_private, msg);
162 return;
164 #ifdef HAVE_XDATA
165 else if (g_str_has_prefix(contenttype, "application/ms-filetransfer+xml"))
167 process_incoming_info_ft_lync(sipe_private, msg);
168 return;
170 #endif
172 from = parse_from(sipmsg_find_header(msg, "From"));
173 session = sipe_session_find_chat_or_im(sipe_private, callid, from);
174 if (!session) {
175 g_free(from);
176 return;
179 /* Group Chat uses text/plain */
180 if (session->is_groupchat) {
181 process_incoming_info_groupchat(sipe_private, msg, session);
182 g_free(from);
183 return;
186 if (g_str_has_prefix(contenttype, "application/x-ms-mim"))
188 sipe_xml *xn_action = sipe_xml_parse(msg->body, msg->bodylen);
189 const sipe_xml *xn_request_rm = sipe_xml_child(xn_action, "RequestRM");
190 const sipe_xml *xn_set_rm = sipe_xml_child(xn_action, "SetRM");
192 sipmsg_add_header(msg, "Content-Type", "application/x-ms-mim");
194 if (xn_request_rm) {
195 //const char *rm = sipe_xml_attribute(xn_request_rm, "uri");
196 int bid = sipe_xml_int_attribute(xn_request_rm, "bid", 0);
197 gchar *body = g_strdup_printf(
198 "<?xml version=\"1.0\"?>\r\n"
199 "<action xmlns=\"http://schemas.microsoft.com/sip/multiparty/\">"
200 "<RequestRMResponse uri=\"sip:%s\" allow=\"%s\"/></action>\r\n",
201 sipe_private->username,
202 session->bid < bid ? "true" : "false");
203 sip_transport_response(sipe_private, msg, 200, "OK", body);
204 g_free(body);
205 } else if (xn_set_rm) {
206 gchar *body;
208 sipe_chat_set_roster_manager(session,
209 sipe_xml_attribute(xn_set_rm, "uri"));
211 body = g_strdup_printf(
212 "<?xml version=\"1.0\"?>\r\n"
213 "<action xmlns=\"http://schemas.microsoft.com/sip/multiparty/\">"
214 "<SetRMResponse uri=\"sip:%s\"/></action>\r\n",
215 sipe_private->username);
216 sip_transport_response(sipe_private, msg, 200, "OK", body);
217 g_free(body);
219 sipe_xml_free(xn_action);
222 else
224 /* looks like purple lacks typing notification for chat */
225 if (!session->chat_session) {
226 sipe_xml *xn_keyboard_activity = sipe_xml_parse(msg->body, msg->bodylen);
227 const char *status = sipe_xml_attribute(sipe_xml_child(xn_keyboard_activity, "status"),
228 "status");
229 if (sipe_strequal(status, "type")) {
230 sipe_backend_user_feedback_typing(SIPE_CORE_PUBLIC,
231 from);
232 } else if (sipe_strequal(status, "idle")) {
233 sipe_backend_user_feedback_typing_stop(SIPE_CORE_PUBLIC,
234 from);
236 sipe_xml_free(xn_keyboard_activity);
239 sip_transport_response(sipe_private, msg, 200, "OK", NULL);
241 g_free(from);
244 static gboolean sipe_process_incoming_x_msmsgsinvite(struct sipe_core_private *sipe_private,
245 struct sip_dialog *dialog,
246 GSList *parsed_body)
248 gboolean found = FALSE;
250 if (parsed_body) {
251 const gchar *invitation_command = sipe_utils_nameval_find(parsed_body, "Invitation-Command");
253 if (sipe_strequal(invitation_command, "INVITE")) {
254 sipe_ft_incoming_transfer(sipe_private, dialog, parsed_body);
255 found = TRUE;
256 } else if (sipe_strequal(invitation_command, "CANCEL")) {
257 sipe_ft_incoming_cancel(dialog, parsed_body);
258 found = TRUE;
259 } else if (sipe_strequal(invitation_command, "ACCEPT")) {
260 sipe_ft_incoming_accept(dialog, parsed_body);
261 found = TRUE;
264 return found;
267 #ifdef HAVE_VV
268 static void sipe_invite_mime_cb(gpointer user_data, const GSList *fields,
269 const gchar *body, gsize length)
271 const gchar *type = sipe_utils_nameval_find(fields, "Content-Type");
272 const gchar *cd = sipe_utils_nameval_find(fields, "Content-Disposition");
274 if (!g_str_has_prefix(type, "application/sdp"))
275 return;
277 if (!cd || !strstr(cd, "ms-proxy-2007fallback")) {
278 struct sipmsg *msg = user_data;
279 const gchar* msg_ct = sipmsg_find_header(msg, "Content-Type");
281 if (g_str_has_prefix(msg_ct, "application/sdp")) {
282 /* We have already found suitable alternative and set message's body
283 * and Content-Type accordingly */
284 return;
287 sipmsg_remove_header_now(msg, "Content-Type");
288 sipmsg_add_header_now(msg, "Content-Type", type);
290 /* Replace message body with chosen alternative, so we can continue to
291 * process it as a normal single part message. */
292 g_free(msg->body);
293 msg->body = g_strndup(body, length);
294 msg->bodylen = length;
297 #endif
299 static void send_invite_response(struct sipe_core_private *sipe_private,
300 struct sipmsg *msg)
302 gchar *body = g_strdup_printf(
303 "v=0\r\n"
304 "o=- 0 0 IN IP4 %s\r\n"
305 "s=session\r\n"
306 "c=IN IP4 %s\r\n"
307 "t=0 0\r\n"
308 "m=%s %d sip sip:%s\r\n"
309 "a=accept-types:" SDP_ACCEPT_TYPES "\r\n",
310 sip_transport_ip_address(sipe_private),
311 sip_transport_ip_address(sipe_private),
312 SIPE_CORE_PRIVATE_FLAG_IS(OCS2007) ? "message" : "x-ms-message",
313 sip_transport_port(sipe_private),
314 sipe_private->username);
315 sipmsg_add_header(msg, "Content-Type", "application/sdp");
316 sip_transport_response(sipe_private, msg, 200, "OK", body);
317 g_free(body);
320 struct sipe_delayed_invite {
321 gchar *action;
322 struct sipmsg *msg;
325 static void delayed_invite_destroy(gpointer data)
327 struct sipe_delayed_invite *delayed_invite = data;
328 sipmsg_free(delayed_invite->msg);
329 g_free(delayed_invite->action);
330 g_free(delayed_invite);
333 static void delayed_invite_timeout(struct sipe_core_private *sipe_private,
334 gpointer data)
336 struct sipe_delayed_invite *delayed_invite = data;
337 send_invite_response(sipe_private, delayed_invite->msg);
340 static void delayed_invite_response(struct sipe_core_private *sipe_private,
341 struct sipmsg *msg,
342 const gchar *callid)
344 struct sipe_delayed_invite *delayed_invite = g_new0(struct sipe_delayed_invite, 1);
346 delayed_invite->action = g_strdup_printf("<delayed-invite-%s>", callid);
347 delayed_invite->msg = sipmsg_copy(msg);
348 sipe_schedule_seconds(sipe_private,
349 delayed_invite->action,
350 delayed_invite,
352 delayed_invite_timeout,
353 delayed_invite_destroy);
356 void sipe_incoming_cancel_delayed_invite(struct sipe_core_private *sipe_private,
357 struct sip_dialog *dialog)
359 struct sipe_delayed_invite *delayed_invite = dialog->delayed_invite;
360 dialog->delayed_invite = NULL;
361 send_invite_response(sipe_private, delayed_invite->msg);
362 sipe_schedule_cancel(sipe_private, delayed_invite->action);
365 void process_incoming_invite(struct sipe_core_private *sipe_private,
366 struct sipmsg *msg)
368 gchar *newTag;
369 const gchar *oldHeader;
370 gchar *newHeader;
371 gboolean is_multiparty = FALSE;
372 gboolean was_multiparty = TRUE;
373 gboolean just_joined = FALSE;
374 gchar *from;
375 const gchar *callid = sipmsg_find_header(msg, "Call-ID");
376 const gchar *roster_manager = sipmsg_find_header(msg, "Roster-Manager");
377 const gchar *end_points_hdr = sipmsg_find_header(msg, "EndPoints");
378 const gchar *trig_invite = sipmsg_find_header(msg, "TriggeredInvite");
379 const gchar *content_type = sipmsg_find_header(msg, "Content-Type");
380 const gchar *subject = sipmsg_find_header(msg, "Subject");
381 GSList *end_points = NULL;
382 struct sip_session *session;
383 struct sip_dialog *dialog;
384 const gchar *ms_text_format;
385 gboolean dont_delay = FALSE;
387 #ifdef HAVE_VV
388 if (g_str_has_prefix(content_type, "multipart/alternative")) {
389 sipe_mime_parts_foreach(content_type, msg->body, sipe_invite_mime_cb, msg);
390 /* Reload Content-Type to get type of the selected message part */
391 content_type = sipmsg_find_header(msg, "Content-Type");
393 #endif
395 if (g_str_has_prefix(content_type, "multipart/mixed")) {
396 if (sipe_mime_parts_contain(content_type, msg->body,
397 "application/ms-filetransfer+xml")) {
398 /* Lync 2010 file transfer */
399 #ifdef HAVE_XDATA
400 process_incoming_invite_ft_lync(sipe_private, msg);
401 #else
402 sip_transport_response(sipe_private, msg,
403 488, "Not Acceptable Here", NULL);
404 #endif
405 return;
409 /* Invitation to join conference */
410 if (g_str_has_prefix(content_type, "application/ms-conf-invite+xml")) {
411 process_incoming_invite_conf(sipe_private, msg);
412 return;
415 #ifdef HAVE_VV
416 /* Application sharing */
417 if (sipe_strcase_equal(content_type, "application/sdp") && msg->body &&
418 strstr(msg->body, "m=applicationsharing") &&
419 sipe_strequal(sipmsg_find_header(msg, "CSeq"), "1 INVITE")) {
420 #ifdef HAVE_APPSHARE
421 process_incoming_invite_appshare(sipe_private, msg);
422 #else
423 sip_transport_response(sipe_private, msg,
424 488, "Not Acceptable Here", NULL);
425 #endif
426 return;
429 /* Invitation to audio call or file transfer */
430 if (msg->body &&
431 (strstr(msg->body, "m=audio") || strstr(msg->body, "m=data") || strstr(msg->body, "m=applicationsharing"))) {
432 process_incoming_invite_call(sipe_private, msg,
433 sdpmsg_parse_msg(msg->body));
434 return;
436 #endif
438 /* Only accept text invitations */
439 if (msg->body && !(strstr(msg->body, "m=message") || strstr(msg->body, "m=x-ms-message"))) {
440 sip_transport_response(sipe_private, msg, 501, "Not implemented", NULL);
441 return;
444 // TODO There *must* be a better way to clean up the To header to add a tag...
445 SIPE_DEBUG_INFO_NOFORMAT("Adding a Tag to the To Header on Invite Request...");
446 oldHeader = sipmsg_find_header(msg, "To");
447 newTag = gentag();
448 newHeader = g_strdup_printf("%s;tag=%s", oldHeader, newTag);
449 g_free(newTag);
450 sipmsg_remove_header_now(msg, "To");
451 sipmsg_add_header_now(msg, "To", newHeader);
452 g_free(newHeader);
454 if (end_points_hdr) {
455 end_points = sipmsg_parse_endpoints_header(end_points_hdr);
457 if (g_slist_length(end_points) > 2) {
458 is_multiparty = TRUE;
461 if (trig_invite && !g_ascii_strcasecmp(trig_invite, "TRUE")) {
462 is_multiparty = TRUE;
465 /* Multiparty session */
466 session = sipe_session_find_chat_by_callid(sipe_private, callid);
467 if (is_multiparty) {
469 if (session) {
470 if (session->chat_session) {
471 /* Update roster manager for existing multiparty session */
472 if (roster_manager)
473 sipe_chat_set_roster_manager(session, roster_manager);
475 } else {
476 gchar *chat_title = sipe_chat_get_name();
478 /* Convert IM session to multiparty session */
479 g_free(session->with);
480 session->with = NULL;
481 was_multiparty = FALSE;
482 session->chat_session = sipe_chat_create_session(SIPE_CHAT_TYPE_MULTIPARTY,
483 roster_manager,
484 chat_title);
486 g_free(chat_title);
488 } else {
489 /* New multiparty session */
490 session = sipe_session_add_chat(sipe_private,
491 NULL,
492 TRUE,
493 roster_manager);
496 /* Create chat */
497 if (!session->chat_session->backend) {
498 gchar *self = sip_uri_self(sipe_private);
499 session->chat_session->backend = sipe_backend_chat_create(SIPE_CORE_PUBLIC,
500 session->chat_session,
501 session->chat_session->title,
502 self);
503 g_free(self);
507 /* IM session */
508 from = parse_from(sipmsg_find_header(msg, "From"));
509 if (!session)
510 session = sipe_session_find_or_add_im(sipe_private, from);
512 /* session is now initialized */
513 g_free(session->callid);
514 session->callid = g_strdup(callid);
516 if (is_multiparty && end_points) {
517 gchar *to = parse_from(sipmsg_find_header(msg, "To"));
518 GSList *entry = end_points;
519 while (entry) {
520 struct sipendpoint *end_point = entry->data;
521 entry = entry->next;
523 if (!g_ascii_strcasecmp(from, end_point->contact) ||
524 !g_ascii_strcasecmp(to, end_point->contact))
525 continue;
527 dialog = sipe_dialog_find(session, end_point->contact);
528 if (dialog) {
529 g_free(dialog->theirepid);
530 dialog->theirepid = end_point->epid;
531 end_point->epid = NULL;
532 } else {
533 dialog = sipe_dialog_add(session);
535 dialog->callid = g_strdup(session->callid);
536 dialog->with = end_point->contact;
537 end_point->contact = NULL;
538 dialog->theirepid = end_point->epid;
539 end_point->epid = NULL;
541 just_joined = TRUE;
543 /* send triggered INVITE */
544 sipe_im_invite(sipe_private, session, dialog->with, NULL, NULL, NULL, TRUE);
547 g_free(to);
550 if (end_points) {
551 GSList *entry = end_points;
552 while (entry) {
553 struct sipendpoint *end_point = entry->data;
554 entry = entry->next;
555 g_free(end_point->contact);
556 g_free(end_point->epid);
557 g_free(end_point);
559 g_slist_free(end_points);
562 dialog = sipe_dialog_find(session, from);
563 if (dialog) {
564 sipe_im_cancel_dangling(sipe_private, session, dialog, from,
565 sipe_im_reenqueue_unconfirmed);
566 /* dialog is no longer valid */
567 } else {
568 just_joined = TRUE;
571 dialog = sipe_dialog_add(session);
572 dialog->with = g_strdup(from);
573 dialog->callid = g_strdup(session->callid);
574 dialog->is_established = TRUE;
575 sipe_dialog_parse(dialog, msg, FALSE);
577 if (is_multiparty && !was_multiparty) {
578 /* add current IM counterparty to chat */
579 sipe_backend_chat_add(session->chat_session->backend,
580 sipe_dialog_first(session)->with,
581 FALSE);
584 /* add inviting party to chat */
585 if (just_joined && session->chat_session) {
586 sipe_backend_chat_add(session->chat_session->backend,
587 from,
588 TRUE);
591 if (!is_multiparty && subject)
592 sipe_im_topic(sipe_private, session, subject);
594 /* ms-text-format: text/plain; charset=UTF-8;msgr=WAAtAE0...DIADQAKAA0ACgA;ms-body=SGk= */
596 /* This used only in 2005 official client, not 2007 or Reuters.
597 Disabled for most cases as interfering with audit of messages which only is applied to regular MESSAGEs.
598 Only enabled for 2005 multiparty chats as otherwise the first message got lost completely.
600 /* also enabled for 2005 file transfer. Didn't work otherwise. */
601 ms_text_format = sipmsg_find_header(msg, "ms-text-format");
602 if (is_multiparty ||
603 (ms_text_format && g_str_has_prefix(ms_text_format, "text/x-msmsgsinvite")) )
605 if (ms_text_format) {
606 if (g_str_has_prefix(ms_text_format, "text/x-msmsgsinvite"))
608 dont_delay = TRUE;
610 else if (g_str_has_prefix(ms_text_format, "text/plain") || g_str_has_prefix(ms_text_format, "text/html"))
612 /* please do not optimize logic inside as this code may be re-enabled for other cases */
613 gchar *html = get_html_message(ms_text_format, NULL);
614 if (html) {
615 if (is_multiparty) {
616 sipe_backend_chat_message(SIPE_CORE_PUBLIC,
617 session->chat_session->backend,
618 from,
620 html);
621 } else {
622 sipe_backend_im_message(SIPE_CORE_PUBLIC,
623 from,
624 html);
626 g_free(html);
627 sipmsg_add_header(msg, "Supported", "ms-text-format"); /* accepts received message */
628 dont_delay = TRUE;
634 g_free(from);
636 sipmsg_add_header(msg, "Supported", "com.microsoft.rtc-multiparty");
638 if (dont_delay || !SIPE_CORE_PRIVATE_FLAG_IS(MPOP)) {
639 send_invite_response(sipe_private, msg);
640 } else {
641 delayed_invite_response(sipe_private, msg, session->callid);
645 void process_incoming_message(struct sipe_core_private *sipe_private,
646 struct sipmsg *msg)
648 gchar *from;
649 const gchar *contenttype;
650 gboolean found = FALSE;
652 from = parse_from(sipmsg_find_header(msg, "From"));
654 if (!from) return;
656 SIPE_DEBUG_INFO("got message from %s: %s", from, msg->body);
658 contenttype = sipmsg_find_header(msg, "Content-Type");
659 if (g_str_has_prefix(contenttype, "text/plain")
660 || g_str_has_prefix(contenttype, "text/html")
661 || g_str_has_prefix(contenttype, "multipart/related")
662 || g_str_has_prefix(contenttype, "multipart/alternative"))
664 const gchar *callid = sipmsg_find_header(msg, "Call-ID");
665 gchar *html = get_html_message(contenttype, msg->body);
667 struct sip_session *session = sipe_session_find_chat_or_im(sipe_private,
668 callid,
669 from);
670 if (session && session->chat_session) {
671 if (session->chat_session->type == SIPE_CHAT_TYPE_CONFERENCE) { /* a conference */
672 gchar *tmp = parse_from(sipmsg_find_header(msg, "Ms-Sender"));
673 gchar *sender = parse_from(tmp);
674 g_free(tmp);
675 sipe_backend_chat_message(SIPE_CORE_PUBLIC,
676 session->chat_session->backend,
677 sender,
679 html);
680 g_free(sender);
681 } else { /* a multiparty chat */
682 sipe_backend_chat_message(SIPE_CORE_PUBLIC,
683 session->chat_session->backend,
684 from,
686 html);
688 } else {
689 sipe_backend_im_message(SIPE_CORE_PUBLIC,
690 from,
691 html);
693 g_free(html);
694 sip_transport_response(sipe_private, msg, 200, "OK", NULL);
695 found = TRUE;
697 } else if (g_str_has_prefix(contenttype, "application/im-iscomposing+xml")) {
698 sipe_xml *isc = sipe_xml_parse(msg->body, msg->bodylen);
699 const sipe_xml *state;
700 gchar *statedata;
702 if (!isc) {
703 SIPE_DEBUG_INFO_NOFORMAT("process_incoming_message: can not parse iscomposing");
704 g_free(from);
705 return;
708 state = sipe_xml_child(isc, "state");
710 if (!state) {
711 SIPE_DEBUG_INFO_NOFORMAT("process_incoming_message: no state found");
712 sipe_xml_free(isc);
713 g_free(from);
714 return;
717 statedata = sipe_xml_data(state);
718 if (statedata) {
719 if (strstr(statedata, "active")) {
720 sipe_backend_user_feedback_typing(SIPE_CORE_PUBLIC,
721 from);
722 } else {
723 sipe_backend_user_feedback_typing_stop(SIPE_CORE_PUBLIC,
724 from);
726 g_free(statedata);
728 sipe_xml_free(isc);
729 sip_transport_response(sipe_private, msg, 200, "OK", NULL);
730 found = TRUE;
731 } else if (g_str_has_prefix(contenttype, "text/x-msmsgsinvite")) {
732 const gchar *callid = sipmsg_find_header(msg, "Call-ID");
733 struct sip_session *session = sipe_session_find_chat_or_im(sipe_private,
734 callid,
735 from);
736 if (session) {
737 struct sip_dialog *dialog = sipe_dialog_find(session, from);
738 GSList *body = sipe_ft_parse_msg_body(msg->body);
739 found = sipe_process_incoming_x_msmsgsinvite(sipe_private, dialog, body);
740 sipe_utils_nameval_free(body);
741 if (found) {
742 sip_transport_response(sipe_private, msg, 200, "OK", NULL);
744 } else {
745 sip_transport_response(sipe_private, msg, 481,
746 "Call Leg/Transaction Does Not Exist", NULL);
747 found = TRUE;
750 if (!found) {
751 const gchar *callid = sipmsg_find_header(msg, "Call-ID");
752 struct sip_session *session = sipe_session_find_chat_or_im(sipe_private,
753 callid,
754 from);
755 if (session) {
756 gchar *errmsg = g_strdup_printf(_("Received a message with unrecognized contents from %s"),
757 from);
758 sipe_user_present_error(sipe_private, session, errmsg);
759 g_free(errmsg);
762 SIPE_DEBUG_INFO("got unknown mime-type '%s'", contenttype);
763 sip_transport_response(sipe_private, msg, 415, "Unsupported media type", NULL);
765 g_free(from);
768 void process_incoming_options(struct sipe_core_private *sipe_private,
769 struct sipmsg *msg)
771 gchar *body;
773 sipmsg_add_header(msg, "Allow", "INVITE, MESSAGE, INFO, SUBSCRIBE, OPTIONS, BYE, CANCEL, NOTIFY, ACK, REFER, BENOTIFY");
774 sipmsg_add_header(msg, "Content-Type", "application/sdp");
776 body = g_strdup_printf(
777 "v=0\r\n"
778 "o=- 0 0 IN IP4 0.0.0.0\r\n"
779 "s=session\r\n"
780 "c=IN IP4 0.0.0.0\r\n"
781 "t=0 0\r\n"
782 "m=%s %d sip sip:%s\r\n"
783 "a=accept-types:" SDP_ACCEPT_TYPES "\r\n",
784 SIPE_CORE_PRIVATE_FLAG_IS(OCS2007) ? "message" : "x-ms-message",
785 sip_transport_port(sipe_private),
786 sipe_private->username);
787 sip_transport_response(sipe_private, msg, 200, "OK", body);
788 g_free(body);
791 void process_incoming_refer(struct sipe_core_private *sipe_private,
792 struct sipmsg *msg)
794 gchar *self = sip_uri_self(sipe_private);
795 const gchar *callid = sipmsg_find_header(msg, "Call-ID");
796 gchar *from = parse_from(sipmsg_find_header(msg, "From"));
797 gchar *refer_to = parse_from(sipmsg_find_header(msg, "Refer-to"));
798 gchar *referred_by = g_strdup(sipmsg_find_header(msg, "Referred-By"));
799 struct sip_session *session;
800 struct sip_dialog *dialog;
802 session = sipe_session_find_chat_by_callid(sipe_private, callid);
803 dialog = sipe_dialog_find(session, from);
805 if (!session || !dialog || !session->chat_session ||
806 (session->chat_session->type != SIPE_CHAT_TYPE_MULTIPARTY) ||
807 !session->chat_session->id ||
808 !sipe_strcase_equal(session->chat_session->id, self)) {
809 sip_transport_response(sipe_private, msg, 500, "Server Internal Error", NULL);
810 } else {
811 sip_transport_response(sipe_private, msg, 202, "Accepted", NULL);
813 sipe_im_invite(sipe_private, session, refer_to, NULL, NULL, referred_by, FALSE);
816 g_free(self);
817 g_free(from);
818 g_free(refer_to);
819 g_free(referred_by);
823 Local Variables:
824 mode: c
825 c-file-style: "bsd"
826 indent-tabs-mode: t
827 tab-width: 8
828 End: