im: trigger delayed invite response when user sends a message
[siplcs.git] / src / core / sipe-incoming.c
blob04cda39844dd0998316ff5902800ee447bae1d1f
1 /**
2 * @file sipe-incoming.c
4 * pidgin-sipe
6 * Copyright (C) 2010-2011 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_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_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 send_invite_response(struct sipe_core_private *sipe_private,
288 struct sipmsg *msg)
290 gchar *body = g_strdup_printf(
291 "v=0\r\n"
292 "o=- 0 0 IN IP4 %s\r\n"
293 "s=session\r\n"
294 "c=IN IP4 %s\r\n"
295 "t=0 0\r\n"
296 "m=%s %d sip sip:%s\r\n"
297 "a=accept-types:" SDP_ACCEPT_TYPES "\r\n",
298 sipe_backend_network_ip_address(),
299 sipe_backend_network_ip_address(),
300 SIPE_CORE_PRIVATE_FLAG_IS(OCS2007) ? "message" : "x-ms-message",
301 sip_transport_port(sipe_private),
302 sipe_private->username);
303 sipmsg_add_header(msg, "Content-Type", "application/sdp");
304 sip_transport_response(sipe_private, msg, 200, "OK", body);
305 g_free(body);
308 struct sipe_delayed_invite {
309 gchar *action;
310 struct sipmsg *msg;
313 static void delayed_invite_destroy(gpointer data)
315 struct sipe_delayed_invite *delayed_invite = data;
316 sipmsg_free(delayed_invite->msg);
317 g_free(delayed_invite->action);
318 g_free(delayed_invite);
321 static void delayed_invite_timeout(struct sipe_core_private *sipe_private,
322 gpointer data)
324 struct sipe_delayed_invite *delayed_invite = data;
325 send_invite_response(sipe_private, delayed_invite->msg);
326 delayed_invite_destroy(data);
329 static void delayed_invite_response(struct sipe_core_private *sipe_private,
330 struct sipmsg *msg,
331 const gchar *callid)
333 struct sipe_delayed_invite *delayed_invite = g_new0(struct sipe_delayed_invite, 1);
335 delayed_invite->action = g_strdup_printf("<delayed-invite-%s>", callid);
336 delayed_invite->msg = sipmsg_copy(msg);
337 sipe_schedule_seconds(sipe_private,
338 delayed_invite->action,
339 delayed_invite,
341 delayed_invite_timeout,
342 delayed_invite_destroy);
345 void sipe_incoming_cancel_delayed_invite(struct sipe_core_private *sipe_private,
346 struct sip_dialog *dialog)
348 struct sipe_delayed_invite *delayed_invite = dialog->delayed_invite;
349 dialog->delayed_invite = NULL;
350 send_invite_response(sipe_private, delayed_invite->msg);
351 sipe_schedule_cancel(sipe_private, delayed_invite->action);
354 void process_incoming_invite(struct sipe_core_private *sipe_private,
355 struct sipmsg *msg)
357 gchar *newTag;
358 const gchar *oldHeader;
359 gchar *newHeader;
360 gboolean is_multiparty = FALSE;
361 gboolean was_multiparty = TRUE;
362 gboolean just_joined = FALSE;
363 gchar *from;
364 const gchar *callid = sipmsg_find_header(msg, "Call-ID");
365 const gchar *roster_manager = sipmsg_find_header(msg, "Roster-Manager");
366 const gchar *end_points_hdr = sipmsg_find_header(msg, "EndPoints");
367 const gchar *trig_invite = sipmsg_find_header(msg, "TriggeredInvite");
368 const gchar *content_type = sipmsg_find_header(msg, "Content-Type");
369 const gchar *subject = sipmsg_find_header(msg, "Subject");
370 GSList *end_points = NULL;
371 struct sip_session *session;
372 struct sip_dialog *dialog;
373 const gchar *ms_text_format;
374 gboolean dont_delay = FALSE;
376 #ifdef HAVE_VV
377 if (g_str_has_prefix(content_type, "multipart/alternative")) {
378 sipe_mime_parts_foreach(content_type, msg->body, sipe_invite_mime_cb, msg);
379 /* Reload Content-Type to get type of the selected message part */
380 content_type = sipmsg_find_header(msg, "Content-Type");
382 #endif
384 /* Invitation to join conference */
385 if (g_str_has_prefix(content_type, "application/ms-conf-invite+xml")) {
386 process_incoming_invite_conf(sipe_private, msg);
387 return;
390 #ifdef HAVE_VV
391 /* Invitation to audio call */
392 if (msg->body && strstr(msg->body, "m=audio")) {
393 process_incoming_invite_call(sipe_private, msg);
394 return;
396 #endif
398 /* Only accept text invitations */
399 if (msg->body && !(strstr(msg->body, "m=message") || strstr(msg->body, "m=x-ms-message"))) {
400 sip_transport_response(sipe_private, msg, 501, "Not implemented", NULL);
401 return;
404 // TODO There *must* be a better way to clean up the To header to add a tag...
405 SIPE_DEBUG_INFO_NOFORMAT("Adding a Tag to the To Header on Invite Request...");
406 oldHeader = sipmsg_find_header(msg, "To");
407 newTag = gentag();
408 newHeader = g_strdup_printf("%s;tag=%s", oldHeader, newTag);
409 g_free(newTag);
410 sipmsg_remove_header_now(msg, "To");
411 sipmsg_add_header_now(msg, "To", newHeader);
412 g_free(newHeader);
414 if (end_points_hdr) {
415 end_points = sipmsg_parse_endpoints_header(end_points_hdr);
417 if (g_slist_length(end_points) > 2) {
418 is_multiparty = TRUE;
421 if (trig_invite && !g_strcasecmp(trig_invite, "TRUE")) {
422 is_multiparty = TRUE;
425 /* Multiparty session */
426 session = sipe_session_find_chat_by_callid(sipe_private, callid);
427 if (is_multiparty) {
429 if (session) {
430 if (session->chat_session) {
431 /* Update roster manager for existing multiparty session */
432 if (roster_manager)
433 sipe_chat_set_roster_manager(session, roster_manager);
435 } else {
436 gchar *chat_title = sipe_chat_get_name();
438 /* Convert IM session to multiparty session */
439 g_free(session->with);
440 session->with = NULL;
441 was_multiparty = FALSE;
442 session->chat_session = sipe_chat_create_session(SIPE_CHAT_TYPE_MULTIPARTY,
443 roster_manager,
444 chat_title);
446 g_free(chat_title);
448 } else {
449 /* New multiparty session */
450 session = sipe_session_add_chat(sipe_private,
451 NULL,
452 TRUE,
453 roster_manager);
456 /* Create chat */
457 if (!session->chat_session->backend) {
458 gchar *self = sip_uri_self(sipe_private);
459 session->chat_session->backend = sipe_backend_chat_create(SIPE_CORE_PUBLIC,
460 session->chat_session,
461 session->chat_session->title,
462 self);
463 g_free(self);
467 /* IM session */
468 from = parse_from(sipmsg_find_header(msg, "From"));
469 if (!session)
470 session = sipe_session_find_or_add_im(sipe_private, from);
472 /* session is now initialized */
473 g_free(session->callid);
474 session->callid = g_strdup(callid);
476 if (is_multiparty && end_points) {
477 gchar *to = parse_from(sipmsg_find_header(msg, "To"));
478 GSList *entry = end_points;
479 while (entry) {
480 struct sipendpoint *end_point = entry->data;
481 entry = entry->next;
483 if (!g_strcasecmp(from, end_point->contact) ||
484 !g_strcasecmp(to, end_point->contact))
485 continue;
487 dialog = sipe_dialog_find(session, end_point->contact);
488 if (dialog) {
489 g_free(dialog->theirepid);
490 dialog->theirepid = end_point->epid;
491 end_point->epid = NULL;
492 } else {
493 dialog = sipe_dialog_add(session);
495 dialog->callid = g_strdup(session->callid);
496 dialog->with = end_point->contact;
497 end_point->contact = NULL;
498 dialog->theirepid = end_point->epid;
499 end_point->epid = NULL;
501 just_joined = TRUE;
503 /* send triggered INVITE */
504 sipe_im_invite(sipe_private, session, dialog->with, NULL, NULL, NULL, TRUE);
507 g_free(to);
510 if (end_points) {
511 GSList *entry = end_points;
512 while (entry) {
513 struct sipendpoint *end_point = entry->data;
514 entry = entry->next;
515 g_free(end_point->contact);
516 g_free(end_point->epid);
517 g_free(end_point);
519 g_slist_free(end_points);
522 dialog = sipe_dialog_find(session, from);
523 if (dialog) {
524 sipe_im_cancel_dangling(sipe_private, session, dialog, from,
525 sipe_im_reenqueue_unconfirmed);
526 /* dialog is no longer valid */
527 } else {
528 just_joined = TRUE;
531 dialog = sipe_dialog_add(session);
532 dialog->with = g_strdup(from);
533 dialog->callid = g_strdup(session->callid);
534 sipe_dialog_parse(dialog, msg, FALSE);
536 if (is_multiparty && !was_multiparty) {
537 /* add current IM counterparty to chat */
538 sipe_backend_chat_add(session->chat_session->backend,
539 sipe_dialog_first(session)->with,
540 FALSE);
543 /* add inviting party to chat */
544 if (just_joined && session->chat_session) {
545 sipe_backend_chat_add(session->chat_session->backend,
546 from,
547 TRUE);
550 if (!is_multiparty && subject)
551 sipe_im_topic(sipe_private, session, subject);
553 /* ms-text-format: text/plain; charset=UTF-8;msgr=WAAtAE0...DIADQAKAA0ACgA;ms-body=SGk= */
555 /* This used only in 2005 official client, not 2007 or Reuters.
556 Disabled for most cases as interfering with audit of messages which only is applied to regular MESSAGEs.
557 Only enabled for 2005 multiparty chats as otherwise the first message got lost completely.
559 /* also enabled for 2005 file transfer. Didn't work otherwise. */
560 ms_text_format = sipmsg_find_header(msg, "ms-text-format");
561 if (is_multiparty ||
562 (ms_text_format && g_str_has_prefix(ms_text_format, "text/x-msmsgsinvite")) )
564 if (ms_text_format) {
565 if (g_str_has_prefix(ms_text_format, "text/x-msmsgsinvite"))
567 gchar *tmp = sipmsg_find_part_of_header(ms_text_format, "ms-body=", NULL, NULL);
568 if (tmp) {
569 gsize len;
570 gchar *body = (gchar *) g_base64_decode(tmp, &len);
571 GSList *parsed_body = sipe_ft_parse_msg_body(body);
573 dialog = sipe_dialog_find(session, from);
574 sipe_process_incoming_x_msmsgsinvite(sipe_private, dialog, parsed_body);
575 sipe_utils_nameval_free(parsed_body);
576 sipmsg_add_header(msg, "Supported", "ms-text-format"); /* accepts received message */
577 dont_delay = TRUE;
579 g_free(tmp);
581 else if (g_str_has_prefix(ms_text_format, "text/plain") || g_str_has_prefix(ms_text_format, "text/html"))
583 /* please do not optimize logic inside as this code may be re-enabled for other cases */
584 gchar *html = get_html_message(ms_text_format, NULL);
585 if (html) {
586 if (is_multiparty) {
587 sipe_backend_chat_message(SIPE_CORE_PUBLIC,
588 session->chat_session->backend,
589 from,
590 html);
591 } else {
592 sipe_backend_im_message(SIPE_CORE_PUBLIC,
593 from,
594 html);
596 g_free(html);
597 sipmsg_add_header(msg, "Supported", "ms-text-format"); /* accepts received message */
598 dont_delay = TRUE;
604 g_free(from);
606 sipmsg_add_header(msg, "Supported", "com.microsoft.rtc-multiparty");
608 if (dont_delay || !SIPE_CORE_PRIVATE_FLAG_IS(MPOP)) {
609 send_invite_response(sipe_private, msg);
610 } else {
611 delayed_invite_response(sipe_private, msg, session->callid);
615 void process_incoming_message(struct sipe_core_private *sipe_private,
616 struct sipmsg *msg)
618 gchar *from;
619 const gchar *contenttype;
620 gboolean found = FALSE;
622 from = parse_from(sipmsg_find_header(msg, "From"));
624 if (!from) return;
626 SIPE_DEBUG_INFO("got message from %s: %s", from, msg->body);
628 contenttype = sipmsg_find_header(msg, "Content-Type");
629 if (g_str_has_prefix(contenttype, "text/plain")
630 || g_str_has_prefix(contenttype, "text/html")
631 || g_str_has_prefix(contenttype, "multipart/related")
632 || g_str_has_prefix(contenttype, "multipart/alternative"))
634 const gchar *callid = sipmsg_find_header(msg, "Call-ID");
635 gchar *html = get_html_message(contenttype, msg->body);
637 struct sip_session *session = sipe_session_find_chat_or_im(sipe_private,
638 callid,
639 from);
640 if (session && session->chat_session) {
641 if (session->chat_session->type == SIPE_CHAT_TYPE_CONFERENCE) { /* a conference */
642 gchar *tmp = parse_from(sipmsg_find_header(msg, "Ms-Sender"));
643 gchar *sender = parse_from(tmp);
644 g_free(tmp);
645 sipe_backend_chat_message(SIPE_CORE_PUBLIC,
646 session->chat_session->backend,
647 sender,
648 html);
649 g_free(sender);
650 } else { /* a multiparty chat */
651 sipe_backend_chat_message(SIPE_CORE_PUBLIC,
652 session->chat_session->backend,
653 from,
654 html);
656 } else {
657 sipe_backend_im_message(SIPE_CORE_PUBLIC,
658 from,
659 html);
661 g_free(html);
662 sip_transport_response(sipe_private, msg, 200, "OK", NULL);
663 found = TRUE;
665 } else if (g_str_has_prefix(contenttype, "application/im-iscomposing+xml")) {
666 sipe_xml *isc = sipe_xml_parse(msg->body, msg->bodylen);
667 const sipe_xml *state;
668 gchar *statedata;
670 if (!isc) {
671 SIPE_DEBUG_INFO_NOFORMAT("process_incoming_message: can not parse iscomposing");
672 g_free(from);
673 return;
676 state = sipe_xml_child(isc, "state");
678 if (!state) {
679 SIPE_DEBUG_INFO_NOFORMAT("process_incoming_message: no state found");
680 sipe_xml_free(isc);
681 g_free(from);
682 return;
685 statedata = sipe_xml_data(state);
686 if (statedata) {
687 if (strstr(statedata, "active")) {
688 sipe_backend_user_feedback_typing(SIPE_CORE_PUBLIC,
689 from);
690 } else {
691 sipe_backend_user_feedback_typing_stop(SIPE_CORE_PUBLIC,
692 from);
694 g_free(statedata);
696 sipe_xml_free(isc);
697 sip_transport_response(sipe_private, msg, 200, "OK", NULL);
698 found = TRUE;
699 } else if (g_str_has_prefix(contenttype, "text/x-msmsgsinvite")) {
700 const gchar *callid = sipmsg_find_header(msg, "Call-ID");
701 struct sip_session *session = sipe_session_find_chat_or_im(sipe_private,
702 callid,
703 from);
704 struct sip_dialog *dialog = sipe_dialog_find(session, from);
705 GSList *body = sipe_ft_parse_msg_body(msg->body);
706 found = sipe_process_incoming_x_msmsgsinvite(sipe_private, dialog, body);
707 sipe_utils_nameval_free(body);
708 if (found) {
709 sip_transport_response(sipe_private, msg, 200, "OK", NULL);
712 if (!found) {
713 const gchar *callid = sipmsg_find_header(msg, "Call-ID");
714 struct sip_session *session = sipe_session_find_chat_or_im(sipe_private,
715 callid,
716 from);
717 if (session) {
718 gchar *errmsg = g_strdup_printf(_("Received a message with unrecognized contents from %s"),
719 from);
720 sipe_user_present_error(sipe_private, session, errmsg);
721 g_free(errmsg);
724 SIPE_DEBUG_INFO("got unknown mime-type '%s'", contenttype);
725 sip_transport_response(sipe_private, msg, 415, "Unsupported media type", NULL);
727 g_free(from);
730 void process_incoming_options(struct sipe_core_private *sipe_private,
731 struct sipmsg *msg)
733 gchar *body;
735 sipmsg_add_header(msg, "Allow", "INVITE, MESSAGE, INFO, SUBSCRIBE, OPTIONS, BYE, CANCEL, NOTIFY, ACK, REFER, BENOTIFY");
736 sipmsg_add_header(msg, "Content-Type", "application/sdp");
738 body = g_strdup_printf(
739 "v=0\r\n"
740 "o=- 0 0 IN IP4 0.0.0.0\r\n"
741 "s=session\r\n"
742 "c=IN IP4 0.0.0.0\r\n"
743 "t=0 0\r\n"
744 "m=%s %d sip sip:%s\r\n"
745 "a=accept-types:" SDP_ACCEPT_TYPES "\r\n",
746 SIPE_CORE_PRIVATE_FLAG_IS(OCS2007) ? "message" : "x-ms-message",
747 sip_transport_port(sipe_private),
748 sipe_private->username);
749 sip_transport_response(sipe_private, msg, 200, "OK", body);
750 g_free(body);
753 void process_incoming_refer(struct sipe_core_private *sipe_private,
754 struct sipmsg *msg)
756 gchar *self = sip_uri_self(sipe_private);
757 const gchar *callid = sipmsg_find_header(msg, "Call-ID");
758 gchar *from = parse_from(sipmsg_find_header(msg, "From"));
759 gchar *refer_to = parse_from(sipmsg_find_header(msg, "Refer-to"));
760 gchar *referred_by = g_strdup(sipmsg_find_header(msg, "Referred-By"));
761 struct sip_session *session;
762 struct sip_dialog *dialog;
764 session = sipe_session_find_chat_by_callid(sipe_private, callid);
765 dialog = sipe_dialog_find(session, from);
767 if (!session || !dialog || !session->chat_session ||
768 (session->chat_session->type != SIPE_CHAT_TYPE_MULTIPARTY) ||
769 !session->chat_session->id ||
770 !sipe_strcase_equal(session->chat_session->id, self)) {
771 sip_transport_response(sipe_private, msg, 500, "Server Internal Error", NULL);
772 } else {
773 sip_transport_response(sipe_private, msg, 202, "Accepted", NULL);
775 sipe_im_invite(sipe_private, session, refer_to, NULL, NULL, referred_by, FALSE);
778 g_free(self);
779 g_free(from);
780 g_free(refer_to);
781 g_free(referred_by);
785 Local Variables:
786 mode: c
787 c-file-style: "bsd"
788 indent-tabs-mode: t
789 tab-width: 8
790 End: