core: factor out code that adds tag to To: header
[siplcs.git] / src / core / sipe-incoming.c
blob82982228df6c2a4dc82a0fa744d4f501443f3942
1 /**
2 * @file sipe-incoming.c
4 * pidgin-sipe
6 * Copyright (C) 2010-2019 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-backend.h"
36 #include "sipe-chat.h"
37 #include "sipe-conf.h"
38 #include "sipe-core.h"
39 #include "sipe-core-private.h"
40 #include "sipe-appshare.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"
56 void process_incoming_bye(struct sipe_core_private *sipe_private,
57 struct sipmsg *msg)
59 const gchar *callid = sipmsg_find_header(msg, "Call-ID");
60 gchar *from = sipmsg_parse_from_address(msg);
61 struct sip_session *session;
62 struct sip_dialog *dialog;
64 #ifdef HAVE_VV
65 struct sipe_media_call_private *call_private =
66 g_hash_table_lookup(sipe_private->media_calls, callid);
67 if (is_media_session_msg(call_private, msg)) {
68 // BYE ends a media call
69 sipe_media_hangup(call_private);
71 #endif
73 /* collect dialog identification
74 * we need callid, ourtag and theirtag to unambiguously identify dialog
76 /* take data before 'msg' will be modified by sip_transport_response */
77 dialog = g_new0(struct sip_dialog, 1);
78 dialog->callid = g_strdup(callid);
79 dialog->cseq = sipmsg_parse_cseq(msg);
80 dialog->with = g_strdup(from);
81 sipe_dialog_parse(dialog, msg, FALSE);
83 sip_transport_response(sipe_private, msg, 200, "OK", NULL);
85 session = sipe_session_find_chat_or_im(sipe_private, callid, from);
86 if (!session) {
87 SIPE_DEBUG_INFO_NOFORMAT("process_incoming_bye: couldn't find session. Ignoring");
88 sipe_dialog_free(dialog);
89 g_free(from);
90 return;
93 SIPE_DEBUG_INFO("process_incoming_bye: session found (chat ID %s)",
94 (session->chat_session && session->chat_session->id) ?
95 session->chat_session->id : "<NO CHAT>");
97 if (session->chat_session &&
98 (session->chat_session->type == SIPE_CHAT_TYPE_MULTIPARTY) &&
99 session->chat_session->id &&
100 !g_ascii_strcasecmp(from, session->chat_session->id))
101 sipe_chat_set_roster_manager(session, NULL);
103 sipe_im_cancel_unconfirmed(sipe_private, session, callid, from);
105 /* This what BYE is essentially for - terminating dialog */
106 sipe_dialog_remove_3(session, dialog);
107 sipe_dialog_free(dialog);
108 if (session->chat_session) {
109 if ((session->chat_session->type == SIPE_CHAT_TYPE_CONFERENCE) &&
110 !g_ascii_strcasecmp(from, session->im_mcu_uri)) {
111 SIPE_DEBUG_INFO("process_incoming_bye: disconnected from conference %s",
112 session->im_mcu_uri);
113 sipe_conf_immcu_closed(sipe_private, session);
114 } else if (session->chat_session->type == SIPE_CHAT_TYPE_MULTIPARTY) {
115 SIPE_DEBUG_INFO_NOFORMAT("process_incoming_bye: disconnected from multiparty chat");
116 sipe_backend_chat_remove(session->chat_session->backend,
117 from);
121 g_free(from);
124 void process_incoming_cancel(struct sipe_core_private *sipe_private,
125 struct sipmsg *msg)
127 const gchar *callid = sipmsg_find_header(msg, "Call-ID");
129 #ifdef HAVE_VV
130 struct sipe_media_call_private *call_private =
131 g_hash_table_lookup(sipe_private->media_calls, callid);
132 if (is_media_session_msg(call_private, msg)) {
133 process_incoming_cancel_call(call_private, msg);
134 return;
136 #endif
138 if (!sipe_session_find_chat_by_callid(sipe_private, callid))
139 sipe_conf_cancel_unaccepted(sipe_private, msg);
142 void process_incoming_info(struct sipe_core_private *sipe_private,
143 struct sipmsg *msg)
145 const gchar *contenttype = sipmsg_find_header(msg, "Content-Type");
146 const gchar *callid = sipmsg_find_header(msg, "Call-ID");
147 gchar *from;
148 struct sip_session *session;
150 SIPE_DEBUG_INFO_NOFORMAT("process_incoming_info");
152 /* Call Control protocol */
153 if (g_str_has_prefix(contenttype, "application/csta+xml"))
155 process_incoming_info_csta(sipe_private, msg);
156 return;
158 else if (g_str_has_prefix(contenttype, "application/xml+conversationinfo"))
160 process_incoming_info_conversation(sipe_private, msg);
161 return;
163 #ifdef HAVE_XDATA
164 else if (g_str_has_prefix(contenttype, "application/ms-filetransfer+xml"))
166 process_incoming_info_ft_lync(sipe_private, msg);
167 return;
169 #endif
171 from = sipmsg_parse_from_address(msg);
172 session = sipe_session_find_chat_or_im(sipe_private, callid, from);
173 if (!session) {
174 g_free(from);
175 return;
178 /* Group Chat uses text/plain */
179 if (session->is_groupchat) {
180 process_incoming_info_groupchat(sipe_private, msg, session);
181 g_free(from);
182 return;
185 if (g_str_has_prefix(contenttype, "application/x-ms-mim"))
187 sipe_xml *xn_action = sipe_xml_parse(msg->body, msg->bodylen);
188 const sipe_xml *xn_request_rm = sipe_xml_child(xn_action, "RequestRM");
189 const sipe_xml *xn_set_rm = sipe_xml_child(xn_action, "SetRM");
191 sipmsg_add_header(msg, "Content-Type", "application/x-ms-mim");
193 if (xn_request_rm) {
194 //const char *rm = sipe_xml_attribute(xn_request_rm, "uri");
195 int bid = sipe_xml_int_attribute(xn_request_rm, "bid", 0);
196 gchar *body = g_strdup_printf(
197 "<?xml version=\"1.0\"?>\r\n"
198 "<action xmlns=\"http://schemas.microsoft.com/sip/multiparty/\">"
199 "<RequestRMResponse uri=\"sip:%s\" allow=\"%s\"/></action>\r\n",
200 sipe_private->username,
201 session->bid < bid ? "true" : "false");
202 sip_transport_response(sipe_private, msg, 200, "OK", body);
203 g_free(body);
204 } else if (xn_set_rm) {
205 gchar *body;
207 sipe_chat_set_roster_manager(session,
208 sipe_xml_attribute(xn_set_rm, "uri"));
210 body = g_strdup_printf(
211 "<?xml version=\"1.0\"?>\r\n"
212 "<action xmlns=\"http://schemas.microsoft.com/sip/multiparty/\">"
213 "<SetRMResponse uri=\"sip:%s\"/></action>\r\n",
214 sipe_private->username);
215 sip_transport_response(sipe_private, msg, 200, "OK", body);
216 g_free(body);
218 sipe_xml_free(xn_action);
221 else
223 /* looks like purple lacks typing notification for chat */
224 if (!session->chat_session) {
225 sipe_xml *xn_keyboard_activity = sipe_xml_parse(msg->body, msg->bodylen);
226 const char *status = sipe_xml_attribute(sipe_xml_child(xn_keyboard_activity, "status"),
227 "status");
228 if (sipe_strequal(status, "type")) {
229 sipe_backend_user_feedback_typing(SIPE_CORE_PUBLIC,
230 from);
231 } else if (sipe_strequal(status, "idle")) {
232 sipe_backend_user_feedback_typing_stop(SIPE_CORE_PUBLIC,
233 from);
235 sipe_xml_free(xn_keyboard_activity);
238 sip_transport_response(sipe_private, msg, 200, "OK", NULL);
240 g_free(from);
243 static gboolean sipe_process_incoming_x_msmsgsinvite(struct sipe_core_private *sipe_private,
244 struct sip_dialog *dialog,
245 GSList *parsed_body)
247 gboolean found = FALSE;
249 if (parsed_body) {
250 const gchar *invitation_command = sipe_utils_nameval_find(parsed_body, "Invitation-Command");
252 if (sipe_strequal(invitation_command, "INVITE")) {
253 sipe_ft_incoming_transfer(sipe_private, dialog, parsed_body);
254 found = TRUE;
255 } else if (sipe_strequal(invitation_command, "CANCEL")) {
256 sipe_ft_incoming_cancel(dialog, parsed_body);
257 found = TRUE;
258 } else if (sipe_strequal(invitation_command, "ACCEPT")) {
259 sipe_ft_incoming_accept(dialog, parsed_body);
260 found = TRUE;
263 return found;
266 #ifdef HAVE_VV
267 static void sipe_invite_mime_cb(gpointer user_data, const GSList *fields,
268 const gchar *body, gsize length)
270 struct sipmsg *msg = user_data;
271 const gchar *type;
272 gchar *body_lcase;
274 if (g_str_has_prefix(sipmsg_find_header(msg, "Content-Type"),
275 "application/sdp")) {
276 /* We have already found suitable alternative and set message's body
277 * and Content-Type accordingly. */
278 return;
281 type = sipe_utils_nameval_find(fields, "Content-Type");
283 if (!body || !g_str_has_prefix(type, "application/sdp"))
284 return;
286 body_lcase = g_ascii_strdown(body, length);
288 if (strstr(body_lcase, " typ host") || strstr(body_lcase, " typ relay") ||
289 strstr(body_lcase, " typ srflx") || strstr(body_lcase, " typ prflx")) {
290 /* RFC 5245 SDP body */
291 sipmsg_remove_header_now(msg, "Content-Type");
292 sipmsg_add_header_now(msg, "Content-Type", type);
294 /* Replace message body with chosen alternative, so we can continue to
295 * process it as a normal single part message. */
296 g_free(msg->body);
297 msg->body = g_strndup(body, length);
298 msg->bodylen = length;
301 g_free(body_lcase);
303 #endif
305 static void send_invite_response(struct sipe_core_private *sipe_private,
306 struct sipmsg *msg)
308 gchar *body = g_strdup_printf(
309 "v=0\r\n"
310 "o=- 0 0 IN %s %s\r\n"
311 "s=session\r\n"
312 "c=IN %s %s\r\n"
313 "t=0 0\r\n"
314 "m=%s %d sip sip:%s\r\n"
315 "a=accept-types:" SDP_ACCEPT_TYPES "\r\n",
316 sip_transport_sdp_address_marker(sipe_private),
317 sip_transport_ip_address(sipe_private),
318 sip_transport_sdp_address_marker(sipe_private),
319 sip_transport_ip_address(sipe_private),
320 SIPE_CORE_PRIVATE_FLAG_IS(OCS2007) ? "message" : "x-ms-message",
321 sip_transport_port(sipe_private),
322 sipe_private->username);
323 sipmsg_add_header(msg, "Content-Type", "application/sdp");
324 sip_transport_response(sipe_private, msg, 200, "OK", body);
325 g_free(body);
328 struct sipe_delayed_invite {
329 gchar *action;
330 struct sipmsg *msg;
333 static void delayed_invite_destroy(gpointer data)
335 struct sipe_delayed_invite *delayed_invite = data;
336 sipmsg_free(delayed_invite->msg);
337 g_free(delayed_invite->action);
338 g_free(delayed_invite);
341 static void delayed_invite_timeout(struct sipe_core_private *sipe_private,
342 gpointer data)
344 struct sipe_delayed_invite *delayed_invite = data;
345 send_invite_response(sipe_private, delayed_invite->msg);
348 static void delayed_invite_response(struct sipe_core_private *sipe_private,
349 struct sipmsg *msg,
350 const gchar *callid)
352 struct sipe_delayed_invite *delayed_invite = g_new0(struct sipe_delayed_invite, 1);
354 delayed_invite->action = g_strdup_printf("<delayed-invite-%s>", callid);
355 delayed_invite->msg = sipmsg_copy(msg);
356 sipe_schedule_seconds(sipe_private,
357 delayed_invite->action,
358 delayed_invite,
360 delayed_invite_timeout,
361 delayed_invite_destroy);
364 void sipe_incoming_cancel_delayed_invite(struct sipe_core_private *sipe_private,
365 struct sip_dialog *dialog)
367 struct sipe_delayed_invite *delayed_invite = dialog->delayed_invite;
368 dialog->delayed_invite = NULL;
369 send_invite_response(sipe_private, delayed_invite->msg);
370 sipe_schedule_cancel(sipe_private, delayed_invite->action);
373 void process_incoming_invite(struct sipe_core_private *sipe_private,
374 struct sipmsg *msg)
376 gboolean is_multiparty = FALSE;
377 gboolean was_multiparty = TRUE;
378 gboolean just_joined = FALSE;
379 gchar *from;
380 const gchar *callid = sipmsg_find_header(msg, "Call-ID");
381 const gchar *roster_manager = sipmsg_find_header(msg, "Roster-Manager");
382 const gchar *end_points_hdr = sipmsg_find_header(msg, "EndPoints");
383 const gchar *trig_invite = sipmsg_find_header(msg, "TriggeredInvite");
384 const gchar *content_type = sipmsg_find_header(msg, "Content-Type");
385 const gchar *subject = sipmsg_find_header(msg, "Subject");
386 GSList *end_points = NULL;
387 struct sip_session *session;
388 struct sip_dialog *dialog;
389 const gchar *ms_text_format;
390 gboolean dont_delay = FALSE;
392 #ifdef HAVE_VV
393 if (g_str_has_prefix(content_type, "multipart/alternative")) {
394 sipe_mime_parts_foreach(content_type, msg->body, sipe_invite_mime_cb, msg);
395 /* Reload Content-Type to get type of the selected message part */
396 content_type = sipmsg_find_header(msg, "Content-Type");
398 #endif
400 if (g_str_has_prefix(content_type, "multipart/mixed")) {
401 if (sipe_mime_parts_contain(content_type, msg->body,
402 "application/ms-filetransfer+xml")) {
403 /* Lync 2010 file transfer */
404 #ifdef HAVE_XDATA
405 process_incoming_invite_ft_lync(sipe_private, msg);
406 #else
407 sip_transport_response(sipe_private, msg,
408 488, "Not Acceptable Here", NULL);
409 #endif
410 return;
414 /* Invitation to join conference */
415 if (g_str_has_prefix(content_type, "application/ms-conf-invite+xml")) {
416 process_incoming_invite_conf(sipe_private, msg);
417 return;
420 #ifdef HAVE_VV
421 /* Application sharing */
422 if (sipe_strcase_equal(content_type, "application/sdp") && msg->body &&
423 strstr(msg->body, "m=applicationsharing") &&
424 sipe_strequal(sipmsg_find_header(msg, "CSeq"), "1 INVITE")) {
425 #ifdef HAVE_APPSHARE
426 process_incoming_invite_appshare(sipe_private, msg);
427 #else
428 sip_transport_response(sipe_private, msg,
429 488, "Not Acceptable Here", NULL);
430 #endif
431 return;
434 /* Invitation to audio call or file transfer */
435 if (msg->body &&
436 (strstr(msg->body, "m=audio") || strstr(msg->body, "m=data") || strstr(msg->body, "m=applicationsharing"))) {
437 process_incoming_invite_call(sipe_private, msg, msg->body);
438 return;
440 #endif
442 /* Only accept text invitations */
443 if (msg->body && !(strstr(msg->body, "m=message") || strstr(msg->body, "m=x-ms-message"))) {
444 sip_transport_response(sipe_private, msg, 501, "Not implemented", NULL);
445 return;
448 // TODO There *must* be a better way to clean up the To header to add a tag...
449 sipmsg_update_to_header_tag(msg);
451 if (end_points_hdr) {
452 end_points = sipmsg_parse_endpoints_header(end_points_hdr);
454 if (g_slist_length(end_points) > 2) {
455 is_multiparty = TRUE;
458 if (trig_invite && !g_ascii_strcasecmp(trig_invite, "TRUE")) {
459 is_multiparty = TRUE;
462 /* Multiparty session */
463 session = sipe_session_find_chat_by_callid(sipe_private, callid);
464 if (is_multiparty) {
466 if (session) {
467 if (session->chat_session) {
468 /* Update roster manager for existing multiparty session */
469 if (roster_manager)
470 sipe_chat_set_roster_manager(session, roster_manager);
472 } else {
473 gchar *chat_title = sipe_chat_get_name();
475 /* Convert IM session to multiparty session */
476 g_free(session->with);
477 session->with = NULL;
478 was_multiparty = FALSE;
479 session->chat_session = sipe_chat_create_session(SIPE_CHAT_TYPE_MULTIPARTY,
480 roster_manager,
481 chat_title);
483 g_free(chat_title);
485 } else {
486 /* New multiparty session */
487 session = sipe_session_add_chat(sipe_private,
488 NULL,
489 TRUE,
490 roster_manager);
493 /* Create chat */
494 if (!session->chat_session->backend) {
495 gchar *self = sip_uri_self(sipe_private);
496 session->chat_session->backend = sipe_backend_chat_create(SIPE_CORE_PUBLIC,
497 session->chat_session,
498 session->chat_session->title,
499 self);
500 g_free(self);
504 /* IM session */
505 from = sipmsg_parse_from_address(msg);
506 if (!session)
507 session = sipe_session_find_or_add_im(sipe_private, from);
509 /* session is now initialized */
510 g_free(session->callid);
511 session->callid = g_strdup(callid);
513 if (is_multiparty && end_points) {
514 gchar *to = sipmsg_parse_to_address(msg);
515 GSList *entry = end_points;
516 while (entry) {
517 struct sipendpoint *end_point = entry->data;
518 entry = entry->next;
520 if (!g_ascii_strcasecmp(from, end_point->contact) ||
521 !g_ascii_strcasecmp(to, end_point->contact))
522 continue;
524 dialog = sipe_dialog_find(session, end_point->contact);
525 if (dialog) {
526 g_free(dialog->theirepid);
527 dialog->theirepid = end_point->epid;
528 end_point->epid = NULL;
529 } else {
530 dialog = sipe_dialog_add(session);
532 dialog->callid = g_strdup(session->callid);
533 dialog->with = end_point->contact;
534 end_point->contact = NULL;
535 dialog->theirepid = end_point->epid;
536 end_point->epid = NULL;
538 just_joined = TRUE;
540 /* send triggered INVITE */
541 sipe_im_invite(sipe_private, session, dialog->with, NULL, NULL, NULL, TRUE);
544 g_free(to);
547 if (end_points) {
548 GSList *entry = end_points;
549 while (entry) {
550 struct sipendpoint *end_point = entry->data;
551 entry = entry->next;
552 g_free(end_point->contact);
553 g_free(end_point->epid);
554 g_free(end_point);
556 g_slist_free(end_points);
559 dialog = sipe_dialog_find(session, from);
560 if (dialog) {
561 sipe_im_cancel_dangling(sipe_private, session, dialog, from,
562 sipe_im_reenqueue_unconfirmed);
563 /* dialog is no longer valid */
564 } else {
565 just_joined = TRUE;
568 dialog = sipe_dialog_add(session);
569 dialog->with = g_strdup(from);
570 dialog->callid = g_strdup(session->callid);
571 dialog->is_established = TRUE;
572 sipe_dialog_parse(dialog, msg, FALSE);
574 if (is_multiparty && !was_multiparty) {
575 /* add current IM counterparty to chat */
576 sipe_backend_chat_add(session->chat_session->backend,
577 sipe_dialog_first(session)->with,
578 FALSE);
581 /* add inviting party to chat */
582 if (just_joined && session->chat_session) {
583 sipe_backend_chat_add(session->chat_session->backend,
584 from,
585 TRUE);
588 if (!is_multiparty && subject)
589 sipe_im_topic(sipe_private, session, subject);
591 /* ms-text-format: text/plain; charset=UTF-8;msgr=WAAtAE0...DIADQAKAA0ACgA;ms-body=SGk= */
593 /* This used only in 2005 official client, not 2007 or Reuters.
594 Disabled for most cases as interfering with audit of messages which only is applied to regular MESSAGEs.
595 Only enabled for 2005 multiparty chats as otherwise the first message got lost completely.
597 /* also enabled for 2005 file transfer. Didn't work otherwise. */
598 ms_text_format = sipmsg_find_header(msg, "ms-text-format");
599 if (is_multiparty ||
600 (ms_text_format && g_str_has_prefix(ms_text_format, "text/x-msmsgsinvite")) )
602 if (ms_text_format) {
603 if (g_str_has_prefix(ms_text_format, "text/x-msmsgsinvite"))
605 dont_delay = TRUE;
607 else if (g_str_has_prefix(ms_text_format, "text/plain") ||
608 g_str_has_prefix(ms_text_format, "text/html") ||
609 g_str_has_prefix(ms_text_format, "text/rtf"))
611 /* please do not optimize logic inside as this code may be re-enabled for other cases */
612 gchar *html = get_html_message(ms_text_format, NULL);
613 if (html) {
614 if (is_multiparty) {
615 sipe_backend_chat_message(SIPE_CORE_PUBLIC,
616 session->chat_session->backend,
617 from,
619 html);
620 } else {
621 sipe_backend_im_message(SIPE_CORE_PUBLIC,
622 from,
623 html);
625 g_free(html);
626 sipmsg_add_header(msg, "Supported", "ms-text-format"); /* accepts received message */
627 dont_delay = TRUE;
633 g_free(from);
635 sipmsg_add_header(msg, "Supported", "com.microsoft.rtc-multiparty");
637 if (dont_delay || !SIPE_CORE_PRIVATE_FLAG_IS(MPOP)) {
638 send_invite_response(sipe_private, msg);
639 } else {
640 delayed_invite_response(sipe_private, msg, session->callid);
644 void process_incoming_message(struct sipe_core_private *sipe_private,
645 struct sipmsg *msg)
647 gchar *from;
648 const gchar *contenttype;
649 gboolean found = FALSE;
651 from = sipmsg_parse_from_address(msg);
653 if (!from) return;
655 SIPE_DEBUG_INFO("got message from %s: %s", from, msg->body);
657 contenttype = sipmsg_find_header(msg, "Content-Type");
658 if (g_str_has_prefix(contenttype, "text/plain") ||
659 g_str_has_prefix(contenttype, "text/html") ||
660 g_str_has_prefix(contenttype, "text/rtf") ||
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 = sipmsg_parse_address_from_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 = sipmsg_parse_from_address(msg);
797 gchar *refer_to = sipmsg_parse_address_from_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: