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