digest: add support for OpenSSL 1.1.0
[siplcs.git] / src / core / sipe-incoming.c
blob4915195b6044f9bc82273afbaf5486d47cd95480
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"
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 = parse_from(sipmsg_find_header(msg, "From"));
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 = parse_from(sipmsg_find_header(msg, "From"));
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 const gchar *type = sipe_utils_nameval_find(fields, "Content-Type");
271 const gchar *cd = sipe_utils_nameval_find(fields, "Content-Disposition");
273 if (!g_str_has_prefix(type, "application/sdp"))
274 return;
276 if (!cd || !strstr(cd, "ms-proxy-2007fallback")) {
277 struct sipmsg *msg = user_data;
278 const gchar* msg_ct = sipmsg_find_header(msg, "Content-Type");
280 if (g_str_has_prefix(msg_ct, "application/sdp")) {
281 /* We have already found suitable alternative and set message's body
282 * and Content-Type accordingly */
283 return;
286 sipmsg_remove_header_now(msg, "Content-Type");
287 sipmsg_add_header_now(msg, "Content-Type", type);
289 /* Replace message body with chosen alternative, so we can continue to
290 * process it as a normal single part message. */
291 g_free(msg->body);
292 msg->body = g_strndup(body, length);
293 msg->bodylen = length;
296 #endif
298 static void send_invite_response(struct sipe_core_private *sipe_private,
299 struct sipmsg *msg)
301 gchar *body = g_strdup_printf(
302 "v=0\r\n"
303 "o=- 0 0 IN IP4 %s\r\n"
304 "s=session\r\n"
305 "c=IN IP4 %s\r\n"
306 "t=0 0\r\n"
307 "m=%s %d sip sip:%s\r\n"
308 "a=accept-types:" SDP_ACCEPT_TYPES "\r\n",
309 sip_transport_ip_address(sipe_private),
310 sip_transport_ip_address(sipe_private),
311 SIPE_CORE_PRIVATE_FLAG_IS(OCS2007) ? "message" : "x-ms-message",
312 sip_transport_port(sipe_private),
313 sipe_private->username);
314 sipmsg_add_header(msg, "Content-Type", "application/sdp");
315 sip_transport_response(sipe_private, msg, 200, "OK", body);
316 g_free(body);
319 struct sipe_delayed_invite {
320 gchar *action;
321 struct sipmsg *msg;
324 static void delayed_invite_destroy(gpointer data)
326 struct sipe_delayed_invite *delayed_invite = data;
327 sipmsg_free(delayed_invite->msg);
328 g_free(delayed_invite->action);
329 g_free(delayed_invite);
332 static void delayed_invite_timeout(struct sipe_core_private *sipe_private,
333 gpointer data)
335 struct sipe_delayed_invite *delayed_invite = data;
336 send_invite_response(sipe_private, delayed_invite->msg);
339 static void delayed_invite_response(struct sipe_core_private *sipe_private,
340 struct sipmsg *msg,
341 const gchar *callid)
343 struct sipe_delayed_invite *delayed_invite = g_new0(struct sipe_delayed_invite, 1);
345 delayed_invite->action = g_strdup_printf("<delayed-invite-%s>", callid);
346 delayed_invite->msg = sipmsg_copy(msg);
347 sipe_schedule_seconds(sipe_private,
348 delayed_invite->action,
349 delayed_invite,
351 delayed_invite_timeout,
352 delayed_invite_destroy);
355 void sipe_incoming_cancel_delayed_invite(struct sipe_core_private *sipe_private,
356 struct sip_dialog *dialog)
358 struct sipe_delayed_invite *delayed_invite = dialog->delayed_invite;
359 dialog->delayed_invite = NULL;
360 send_invite_response(sipe_private, delayed_invite->msg);
361 sipe_schedule_cancel(sipe_private, delayed_invite->action);
364 void process_incoming_invite(struct sipe_core_private *sipe_private,
365 struct sipmsg *msg)
367 gchar *newTag;
368 const gchar *oldHeader;
369 gchar *newHeader;
370 gboolean is_multiparty = FALSE;
371 gboolean was_multiparty = TRUE;
372 gboolean just_joined = FALSE;
373 gchar *from;
374 const gchar *callid = sipmsg_find_header(msg, "Call-ID");
375 const gchar *roster_manager = sipmsg_find_header(msg, "Roster-Manager");
376 const gchar *end_points_hdr = sipmsg_find_header(msg, "EndPoints");
377 const gchar *trig_invite = sipmsg_find_header(msg, "TriggeredInvite");
378 const gchar *content_type = sipmsg_find_header(msg, "Content-Type");
379 const gchar *subject = sipmsg_find_header(msg, "Subject");
380 GSList *end_points = NULL;
381 struct sip_session *session;
382 struct sip_dialog *dialog;
383 const gchar *ms_text_format;
384 gboolean dont_delay = FALSE;
386 #ifdef HAVE_VV
387 if (g_str_has_prefix(content_type, "multipart/alternative")) {
388 sipe_mime_parts_foreach(content_type, msg->body, sipe_invite_mime_cb, msg);
389 /* Reload Content-Type to get type of the selected message part */
390 content_type = sipmsg_find_header(msg, "Content-Type");
392 #endif
394 if (g_str_has_prefix(content_type, "multipart/mixed")) {
395 if (sipe_mime_parts_contain(content_type, msg->body,
396 "application/ms-filetransfer+xml")) {
397 /* Lync 2010 file transfer */
398 #ifdef HAVE_XDATA
399 process_incoming_invite_ft_lync(sipe_private, msg);
400 #else
401 sip_transport_response(sipe_private, msg,
402 488, "Not Acceptable Here", NULL);
403 #endif
404 return;
408 /* Invitation to join conference */
409 if (g_str_has_prefix(content_type, "application/ms-conf-invite+xml")) {
410 process_incoming_invite_conf(sipe_private, msg);
411 return;
414 #ifdef HAVE_VV
415 /* Application sharing */
416 if (sipe_strcase_equal(content_type, "application/sdp") && msg->body &&
417 strstr(msg->body, "m=applicationsharing") &&
418 sipe_strequal(sipmsg_find_header(msg, "CSeq"), "1 INVITE")) {
419 #ifdef HAVE_APPSHARE
420 process_incoming_invite_appshare(sipe_private, msg);
421 #else
422 sip_transport_response(sipe_private, msg,
423 488, "Not Acceptable Here", NULL);
424 #endif
425 return;
428 /* Invitation to audio call or file transfer */
429 if (msg->body &&
430 (strstr(msg->body, "m=audio") || strstr(msg->body, "m=data") || strstr(msg->body, "m=applicationsharing"))) {
431 process_incoming_invite_call(sipe_private, msg, msg->body);
432 return;
434 #endif
436 /* Only accept text invitations */
437 if (msg->body && !(strstr(msg->body, "m=message") || strstr(msg->body, "m=x-ms-message"))) {
438 sip_transport_response(sipe_private, msg, 501, "Not implemented", NULL);
439 return;
442 // TODO There *must* be a better way to clean up the To header to add a tag...
443 SIPE_DEBUG_INFO_NOFORMAT("Adding a Tag to the To Header on Invite Request...");
444 oldHeader = sipmsg_find_header(msg, "To");
445 newTag = gentag();
446 newHeader = g_strdup_printf("%s;tag=%s", oldHeader, newTag);
447 g_free(newTag);
448 sipmsg_remove_header_now(msg, "To");
449 sipmsg_add_header_now(msg, "To", newHeader);
450 g_free(newHeader);
452 if (end_points_hdr) {
453 end_points = sipmsg_parse_endpoints_header(end_points_hdr);
455 if (g_slist_length(end_points) > 2) {
456 is_multiparty = TRUE;
459 if (trig_invite && !g_ascii_strcasecmp(trig_invite, "TRUE")) {
460 is_multiparty = TRUE;
463 /* Multiparty session */
464 session = sipe_session_find_chat_by_callid(sipe_private, callid);
465 if (is_multiparty) {
467 if (session) {
468 if (session->chat_session) {
469 /* Update roster manager for existing multiparty session */
470 if (roster_manager)
471 sipe_chat_set_roster_manager(session, roster_manager);
473 } else {
474 gchar *chat_title = sipe_chat_get_name();
476 /* Convert IM session to multiparty session */
477 g_free(session->with);
478 session->with = NULL;
479 was_multiparty = FALSE;
480 session->chat_session = sipe_chat_create_session(SIPE_CHAT_TYPE_MULTIPARTY,
481 roster_manager,
482 chat_title);
484 g_free(chat_title);
486 } else {
487 /* New multiparty session */
488 session = sipe_session_add_chat(sipe_private,
489 NULL,
490 TRUE,
491 roster_manager);
494 /* Create chat */
495 if (!session->chat_session->backend) {
496 gchar *self = sip_uri_self(sipe_private);
497 session->chat_session->backend = sipe_backend_chat_create(SIPE_CORE_PUBLIC,
498 session->chat_session,
499 session->chat_session->title,
500 self);
501 g_free(self);
505 /* IM session */
506 from = parse_from(sipmsg_find_header(msg, "From"));
507 if (!session)
508 session = sipe_session_find_or_add_im(sipe_private, from);
510 /* session is now initialized */
511 g_free(session->callid);
512 session->callid = g_strdup(callid);
514 if (is_multiparty && end_points) {
515 gchar *to = parse_from(sipmsg_find_header(msg, "To"));
516 GSList *entry = end_points;
517 while (entry) {
518 struct sipendpoint *end_point = entry->data;
519 entry = entry->next;
521 if (!g_ascii_strcasecmp(from, end_point->contact) ||
522 !g_ascii_strcasecmp(to, end_point->contact))
523 continue;
525 dialog = sipe_dialog_find(session, end_point->contact);
526 if (dialog) {
527 g_free(dialog->theirepid);
528 dialog->theirepid = end_point->epid;
529 end_point->epid = NULL;
530 } else {
531 dialog = sipe_dialog_add(session);
533 dialog->callid = g_strdup(session->callid);
534 dialog->with = end_point->contact;
535 end_point->contact = NULL;
536 dialog->theirepid = end_point->epid;
537 end_point->epid = NULL;
539 just_joined = TRUE;
541 /* send triggered INVITE */
542 sipe_im_invite(sipe_private, session, dialog->with, NULL, NULL, NULL, TRUE);
545 g_free(to);
548 if (end_points) {
549 GSList *entry = end_points;
550 while (entry) {
551 struct sipendpoint *end_point = entry->data;
552 entry = entry->next;
553 g_free(end_point->contact);
554 g_free(end_point->epid);
555 g_free(end_point);
557 g_slist_free(end_points);
560 dialog = sipe_dialog_find(session, from);
561 if (dialog) {
562 sipe_im_cancel_dangling(sipe_private, session, dialog, from,
563 sipe_im_reenqueue_unconfirmed);
564 /* dialog is no longer valid */
565 } else {
566 just_joined = TRUE;
569 dialog = sipe_dialog_add(session);
570 dialog->with = g_strdup(from);
571 dialog->callid = g_strdup(session->callid);
572 dialog->is_established = TRUE;
573 sipe_dialog_parse(dialog, msg, FALSE);
575 if (is_multiparty && !was_multiparty) {
576 /* add current IM counterparty to chat */
577 sipe_backend_chat_add(session->chat_session->backend,
578 sipe_dialog_first(session)->with,
579 FALSE);
582 /* add inviting party to chat */
583 if (just_joined && session->chat_session) {
584 sipe_backend_chat_add(session->chat_session->backend,
585 from,
586 TRUE);
589 if (!is_multiparty && subject)
590 sipe_im_topic(sipe_private, session, subject);
592 /* ms-text-format: text/plain; charset=UTF-8;msgr=WAAtAE0...DIADQAKAA0ACgA;ms-body=SGk= */
594 /* This used only in 2005 official client, not 2007 or Reuters.
595 Disabled for most cases as interfering with audit of messages which only is applied to regular MESSAGEs.
596 Only enabled for 2005 multiparty chats as otherwise the first message got lost completely.
598 /* also enabled for 2005 file transfer. Didn't work otherwise. */
599 ms_text_format = sipmsg_find_header(msg, "ms-text-format");
600 if (is_multiparty ||
601 (ms_text_format && g_str_has_prefix(ms_text_format, "text/x-msmsgsinvite")) )
603 if (ms_text_format) {
604 if (g_str_has_prefix(ms_text_format, "text/x-msmsgsinvite"))
606 dont_delay = TRUE;
608 else if (g_str_has_prefix(ms_text_format, "text/plain") || g_str_has_prefix(ms_text_format, "text/html"))
610 /* please do not optimize logic inside as this code may be re-enabled for other cases */
611 gchar *html = get_html_message(ms_text_format, NULL);
612 if (html) {
613 if (is_multiparty) {
614 sipe_backend_chat_message(SIPE_CORE_PUBLIC,
615 session->chat_session->backend,
616 from,
618 html);
619 } else {
620 sipe_backend_im_message(SIPE_CORE_PUBLIC,
621 from,
622 html);
624 g_free(html);
625 sipmsg_add_header(msg, "Supported", "ms-text-format"); /* accepts received message */
626 dont_delay = TRUE;
632 g_free(from);
634 sipmsg_add_header(msg, "Supported", "com.microsoft.rtc-multiparty");
636 if (dont_delay || !SIPE_CORE_PRIVATE_FLAG_IS(MPOP)) {
637 send_invite_response(sipe_private, msg);
638 } else {
639 delayed_invite_response(sipe_private, msg, session->callid);
643 void process_incoming_message(struct sipe_core_private *sipe_private,
644 struct sipmsg *msg)
646 gchar *from;
647 const gchar *contenttype;
648 gboolean found = FALSE;
650 from = parse_from(sipmsg_find_header(msg, "From"));
652 if (!from) return;
654 SIPE_DEBUG_INFO("got message from %s: %s", from, msg->body);
656 contenttype = sipmsg_find_header(msg, "Content-Type");
657 if (g_str_has_prefix(contenttype, "text/plain")
658 || g_str_has_prefix(contenttype, "text/html")
659 || g_str_has_prefix(contenttype, "multipart/related")
660 || g_str_has_prefix(contenttype, "multipart/alternative"))
662 const gchar *callid = sipmsg_find_header(msg, "Call-ID");
663 gchar *html = get_html_message(contenttype, msg->body);
665 struct sip_session *session = sipe_session_find_chat_or_im(sipe_private,
666 callid,
667 from);
668 if (session && session->chat_session) {
669 if (session->chat_session->type == SIPE_CHAT_TYPE_CONFERENCE) { /* a conference */
670 gchar *tmp = parse_from(sipmsg_find_header(msg, "Ms-Sender"));
671 gchar *sender = parse_from(tmp);
672 g_free(tmp);
673 sipe_backend_chat_message(SIPE_CORE_PUBLIC,
674 session->chat_session->backend,
675 sender,
677 html);
678 g_free(sender);
679 } else { /* a multiparty chat */
680 sipe_backend_chat_message(SIPE_CORE_PUBLIC,
681 session->chat_session->backend,
682 from,
684 html);
686 } else {
687 sipe_backend_im_message(SIPE_CORE_PUBLIC,
688 from,
689 html);
691 g_free(html);
692 sip_transport_response(sipe_private, msg, 200, "OK", NULL);
693 found = TRUE;
695 } else if (g_str_has_prefix(contenttype, "application/im-iscomposing+xml")) {
696 sipe_xml *isc = sipe_xml_parse(msg->body, msg->bodylen);
697 const sipe_xml *state;
698 gchar *statedata;
700 if (!isc) {
701 SIPE_DEBUG_INFO_NOFORMAT("process_incoming_message: can not parse iscomposing");
702 g_free(from);
703 return;
706 state = sipe_xml_child(isc, "state");
708 if (!state) {
709 SIPE_DEBUG_INFO_NOFORMAT("process_incoming_message: no state found");
710 sipe_xml_free(isc);
711 g_free(from);
712 return;
715 statedata = sipe_xml_data(state);
716 if (statedata) {
717 if (strstr(statedata, "active")) {
718 sipe_backend_user_feedback_typing(SIPE_CORE_PUBLIC,
719 from);
720 } else {
721 sipe_backend_user_feedback_typing_stop(SIPE_CORE_PUBLIC,
722 from);
724 g_free(statedata);
726 sipe_xml_free(isc);
727 sip_transport_response(sipe_private, msg, 200, "OK", NULL);
728 found = TRUE;
729 } else if (g_str_has_prefix(contenttype, "text/x-msmsgsinvite")) {
730 const gchar *callid = sipmsg_find_header(msg, "Call-ID");
731 struct sip_session *session = sipe_session_find_chat_or_im(sipe_private,
732 callid,
733 from);
734 if (session) {
735 struct sip_dialog *dialog = sipe_dialog_find(session, from);
736 GSList *body = sipe_ft_parse_msg_body(msg->body);
737 found = sipe_process_incoming_x_msmsgsinvite(sipe_private, dialog, body);
738 sipe_utils_nameval_free(body);
739 if (found) {
740 sip_transport_response(sipe_private, msg, 200, "OK", NULL);
742 } else {
743 sip_transport_response(sipe_private, msg, 481,
744 "Call Leg/Transaction Does Not Exist", NULL);
745 found = TRUE;
748 if (!found) {
749 const gchar *callid = sipmsg_find_header(msg, "Call-ID");
750 struct sip_session *session = sipe_session_find_chat_or_im(sipe_private,
751 callid,
752 from);
753 if (session) {
754 gchar *errmsg = g_strdup_printf(_("Received a message with unrecognized contents from %s"),
755 from);
756 sipe_user_present_error(sipe_private, session, errmsg);
757 g_free(errmsg);
760 SIPE_DEBUG_INFO("got unknown mime-type '%s'", contenttype);
761 sip_transport_response(sipe_private, msg, 415, "Unsupported media type", NULL);
763 g_free(from);
766 void process_incoming_options(struct sipe_core_private *sipe_private,
767 struct sipmsg *msg)
769 gchar *body;
771 sipmsg_add_header(msg, "Allow", "INVITE, MESSAGE, INFO, SUBSCRIBE, OPTIONS, BYE, CANCEL, NOTIFY, ACK, REFER, BENOTIFY");
772 sipmsg_add_header(msg, "Content-Type", "application/sdp");
774 body = g_strdup_printf(
775 "v=0\r\n"
776 "o=- 0 0 IN IP4 0.0.0.0\r\n"
777 "s=session\r\n"
778 "c=IN IP4 0.0.0.0\r\n"
779 "t=0 0\r\n"
780 "m=%s %d sip sip:%s\r\n"
781 "a=accept-types:" SDP_ACCEPT_TYPES "\r\n",
782 SIPE_CORE_PRIVATE_FLAG_IS(OCS2007) ? "message" : "x-ms-message",
783 sip_transport_port(sipe_private),
784 sipe_private->username);
785 sip_transport_response(sipe_private, msg, 200, "OK", body);
786 g_free(body);
789 void process_incoming_refer(struct sipe_core_private *sipe_private,
790 struct sipmsg *msg)
792 gchar *self = sip_uri_self(sipe_private);
793 const gchar *callid = sipmsg_find_header(msg, "Call-ID");
794 gchar *from = parse_from(sipmsg_find_header(msg, "From"));
795 gchar *refer_to = parse_from(sipmsg_find_header(msg, "Refer-to"));
796 gchar *referred_by = g_strdup(sipmsg_find_header(msg, "Referred-By"));
797 struct sip_session *session;
798 struct sip_dialog *dialog;
800 session = sipe_session_find_chat_by_callid(sipe_private, callid);
801 dialog = sipe_dialog_find(session, from);
803 if (!session || !dialog || !session->chat_session ||
804 (session->chat_session->type != SIPE_CHAT_TYPE_MULTIPARTY) ||
805 !session->chat_session->id ||
806 !sipe_strcase_equal(session->chat_session->id, self)) {
807 sip_transport_response(sipe_private, msg, 500, "Server Internal Error", NULL);
808 } else {
809 sip_transport_response(sipe_private, msg, 202, "Accepted", NULL);
811 sipe_im_invite(sipe_private, session, refer_to, NULL, NULL, referred_by, FALSE);
814 g_free(self);
815 g_free(from);
816 g_free(refer_to);
817 g_free(referred_by);
821 Local Variables:
822 mode: c
823 c-file-style: "bsd"
824 indent-tabs-mode: t
825 tab-width: 8
826 End: