static void send_invite_response(struct sipe_core_private *sipe_private,
				 struct sipmsg *msg)
{
	gchar *body = g_strdup_printf(
		"v=0\r\n"
		"o=- 0 0 IN IP4 %s\r\n"
		"s=session\r\n"
		"c=IN IP4 %s\r\n"
		"t=0 0\r\n"
		"m=%s %d sip sip:%s\r\n"
		"a=accept-types:" SDP_ACCEPT_TYPES "\r\n",
		sipe_backend_network_ip_address(SIPE_CORE_PUBLIC),
		sipe_backend_network_ip_address(SIPE_CORE_PUBLIC),
		SIPE_CORE_PRIVATE_FLAG_IS(OCS2007) ? "message" : "x-ms-message",
		sip_transport_port(sipe_private),
		sipe_private->username);
	sipmsg_add_header(msg, "Content-Type", "application/sdp");
	sip_transport_response(sipe_private, msg, 200, "OK", body);
	g_free(body);
}
void process_incoming_options(struct sipe_core_private *sipe_private,
			      struct sipmsg *msg)
{
	gchar *body;

	sipmsg_add_header(msg, "Allow", "INVITE, MESSAGE, INFO, SUBSCRIBE, OPTIONS, BYE, CANCEL, NOTIFY, ACK, REFER, BENOTIFY");
	sipmsg_add_header(msg, "Content-Type", "application/sdp");

	body = g_strdup_printf(
		"v=0\r\n"
		"o=- 0 0 IN IP4 0.0.0.0\r\n"
		"s=session\r\n"
		"c=IN IP4 0.0.0.0\r\n"
		"t=0 0\r\n"
		"m=%s %d sip sip:%s\r\n"
		"a=accept-types:" SDP_ACCEPT_TYPES "\r\n",
		SIPE_CORE_PRIVATE_FLAG_IS(OCS2007) ? "message" : "x-ms-message",
		sip_transport_port(sipe_private),
		sipe_private->username);
	sip_transport_response(sipe_private, msg, 200, "OK", body);
	g_free(body);
}
Ejemplo n.º 3
0
void sipe_ocs2005_user_info_has_updated(struct sipe_core_private *sipe_private,
                                        const sipe_xml *xn_userinfo)
{
    const sipe_xml *xn_states;

    g_free(sipe_private->ocs2005_user_states);
    sipe_private->ocs2005_user_states = NULL;
    if ((xn_states = sipe_xml_child(xn_userinfo, "states")) != NULL) {
        gchar *orig = sipe_private->ocs2005_user_states = sipe_xml_stringify(xn_states);

        /* this is a hack-around to remove added newline after inner element,
         * state in this case, where it shouldn't be.
         * After several use of sipe_xml_stringify, amount of added newlines
         * grows significantly.
         */
        if (orig) {
            gchar c, *stripped = orig;
            while ((c = *orig++)) {
                if ((c != '\n') /* && (c != '\r') */) {
                    *stripped++ = c;
                }
            }
            *stripped = '\0';
        }
    }

    /* Publish initial state if not yet.
     * Assuming this happens on initial responce to self subscription
     * so we've already updated our UserInfo.
     */
    if (!SIPE_CORE_PRIVATE_FLAG_IS(INITIAL_PUBLISH)) {
        sipe_ocs2005_presence_publish(sipe_private, FALSE);
        /* dalayed run */
        sipe_cal_delayed_calendar_update(sipe_private);
    }
}
Ejemplo n.º 4
0
void sipe_ocs2005_apply_calendar_status(struct sipe_core_private *sipe_private,
                                        struct sipe_buddy *sbuddy,
                                        const char *status_id)
{
    time_t cal_avail_since;
    int cal_status = sipe_cal_get_status(sbuddy, time(NULL), &cal_avail_since);
    int avail;
    gchar *self_uri;

    if (!sbuddy) return;

    if (cal_status < SIPE_CAL_NO_DATA) {
        SIPE_DEBUG_INFO("sipe_apply_calendar_status: cal_status      : %d for %s", cal_status, sbuddy->name);
        SIPE_DEBUG_INFO("sipe_apply_calendar_status: cal_avail_since : %s", asctime(localtime(&cal_avail_since)));
    }

    /* scheduled Cal update call */
    if (!status_id) {
        status_id = sbuddy->last_non_cal_status_id;
        g_free(sbuddy->activity);
        sbuddy->activity = g_strdup(sbuddy->last_non_cal_activity);
    }

    if (!status_id) {
        SIPE_DEBUG_INFO("sipe_apply_calendar_status: status_id is NULL for %s, exiting.",
                        sbuddy->name ? sbuddy->name : "" );
        return;
    }

    /* adjust to calendar status */
    if (cal_status != SIPE_CAL_NO_DATA) {
        SIPE_DEBUG_INFO("sipe_apply_calendar_status: user_avail_since: %s", asctime(localtime(&sbuddy->user_avail_since)));

        if ((cal_status == SIPE_CAL_BUSY) &&
                (cal_avail_since > sbuddy->user_avail_since) &&
                sipe_ocs2007_status_is_busy(status_id)) {
            status_id = sipe_status_activity_to_token(SIPE_ACTIVITY_BUSY);
            g_free(sbuddy->activity);
            sbuddy->activity = g_strdup(sipe_core_activity_description(SIPE_ACTIVITY_IN_MEETING));
        }
        avail = sipe_ocs2007_availability_from_status(status_id, NULL);

        SIPE_DEBUG_INFO("sipe_apply_calendar_status: activity_since  : %s", asctime(localtime(&sbuddy->activity_since)));
        if (cal_avail_since > sbuddy->activity_since) {
            if ((cal_status == SIPE_CAL_OOF) &&
                    sipe_ocs2007_availability_is_away(avail)) {
                g_free(sbuddy->activity);
                sbuddy->activity = g_strdup(sipe_core_activity_description(SIPE_ACTIVITY_OOF));
            }
        }
    }

    /* then set status_id actually */
    SIPE_DEBUG_INFO("sipe_apply_calendar_status: to %s for %s", status_id, sbuddy->name ? sbuddy->name : "" );
    sipe_backend_buddy_set_status(SIPE_CORE_PUBLIC, sbuddy->name,
                                  sipe_status_token_to_activity(status_id));

    /* set our account state to the one in roaming (including calendar info) */
    self_uri = sip_uri_self(sipe_private);
    if (SIPE_CORE_PRIVATE_FLAG_IS(INITIAL_PUBLISH) &&
            sipe_strcase_equal(sbuddy->name, self_uri)) {
        if (sipe_strequal(status_id, sipe_status_activity_to_token(SIPE_ACTIVITY_OFFLINE))) {
            /* do not let offline status switch us off */
            status_id = sipe_status_activity_to_token(SIPE_ACTIVITY_INVISIBLE);
        }

        sipe_status_and_note(sipe_private, status_id);
    }
    g_free(self_uri);
}
Ejemplo n.º 5
0
static void send_presence_soap(struct sipe_core_private *sipe_private,
                               gboolean do_publish_calendar,
                               gboolean do_reset_status)
{
    struct sipe_calendar* cal = sipe_private->calendar;
    gchar *body;
    gchar *tmp;
    gchar *tmp2 = NULL;
    gchar *res_note = NULL;
    gchar *res_oof = NULL;
    const gchar *note_pub = NULL;
    gchar *states = NULL;
    gchar *calendar_data = NULL;
    gchar *epid = get_epid(sipe_private);
    gchar *from = sip_uri_self(sipe_private);
    time_t now = time(NULL);
    gchar *since_time_str = sipe_utils_time_to_str(now);
    const gchar *oof_note = cal ? sipe_ews_get_oof_note(cal) : NULL;
    const char *user_input;
    gboolean pub_oof = cal && oof_note && (!sipe_private->note || cal->updated > sipe_private->note_since);

    if (oof_note && sipe_private->note) {
        SIPE_DEBUG_INFO("cal->oof_start           : %s", asctime(localtime(&(cal->oof_start))));
        SIPE_DEBUG_INFO("sipe_private->note_since : %s", asctime(localtime(&(sipe_private->note_since))));
    }

    SIPE_DEBUG_INFO("sipe_private->note  : %s", sipe_private->note ? sipe_private->note : "");

    if (!SIPE_CORE_PRIVATE_FLAG_IS(INITIAL_PUBLISH) ||
            do_reset_status)
        sipe_status_set_activity(sipe_private, SIPE_ACTIVITY_AVAILABLE);

    /* Note */
    if (pub_oof) {
        note_pub = oof_note;
        res_oof = SIPE_SOAP_SET_PRESENCE_OOF_XML;
        cal->published = TRUE;
    } else if (sipe_private->note) {
        if (SIPE_CORE_PRIVATE_FLAG_IS(OOF_NOTE) &&
                !oof_note) { /* stale OOF note, as it's not present in cal already */
            g_free(sipe_private->note);
            sipe_private->note = NULL;
            SIPE_CORE_PRIVATE_FLAG_UNSET(OOF_NOTE);
            sipe_private->note_since = 0;
        } else {
            note_pub = sipe_private->note;
            res_oof = SIPE_CORE_PRIVATE_FLAG_IS(OOF_NOTE) ? SIPE_SOAP_SET_PRESENCE_OOF_XML : "";
        }
    }

    if (note_pub)
    {
        /* to protocol internal plain text format */
        tmp = sipe_backend_markup_strip_html(note_pub);
        res_note = g_markup_printf_escaped(SIPE_SOAP_SET_PRESENCE_NOTE_XML, tmp);
        g_free(tmp);
    }

    /* User State */
    if (!do_reset_status) {
        if (sipe_status_changed_by_user(sipe_private) &&
                !do_publish_calendar &&
                SIPE_CORE_PRIVATE_FLAG_IS(INITIAL_PUBLISH)) {
            const gchar *activity_token;
            int avail_2007 = sipe_ocs2007_availability_from_status(sipe_private->status,
                             &activity_token);

            states = g_strdup_printf(SIPE_SOAP_SET_PRESENCE_STATES,
                                     avail_2007,
                                     since_time_str,
                                     epid,
                                     activity_token);
        }
        else /* preserve existing publication */
        {
            if (sipe_private->ocs2005_user_states) {
                states = g_strdup(sipe_private->ocs2005_user_states);
            }
        }
    } else {
        /* do nothing - then User state will be erased */
    }
    SIPE_CORE_PRIVATE_FLAG_SET(INITIAL_PUBLISH);

    /* CalendarInfo */
    if (cal && (!is_empty(cal->legacy_dn) || !is_empty(cal->email)) && cal->fb_start && !is_empty(cal->free_busy))
    {
        char *fb_start_str = sipe_utils_time_to_str(cal->fb_start);
        char *free_busy_base64 = sipe_cal_get_freebusy_base64(cal->free_busy);
        calendar_data = g_strdup_printf(SIPE_SOAP_SET_PRESENCE_CALENDAR,
                                        !is_empty(cal->legacy_dn) ? cal->legacy_dn : cal->email,
                                        fb_start_str,
                                        free_busy_base64);
        g_free(fb_start_str);
        g_free(free_busy_base64);
    }

    user_input = (sipe_status_changed_by_user(sipe_private) ||
                  sipe_is_user_available(sipe_private)) ?
                 "active" : "idle";

    /* generate XML */
    body = g_strdup_printf(SIPE_SOAP_SET_PRESENCE,
                           sipe_private->username,
                           sipe_ocs2005_availability_from_status(sipe_private),
                           sipe_ocs2005_activity_from_status(sipe_private),
                           (tmp = g_ascii_strup(g_get_host_name(), -1)),
                           res_note ? res_note : "",
                           res_oof ? res_oof : "",
                           states ? states : "",
                           calendar_data ? calendar_data : "",
                           epid,
                           since_time_str,
                           since_time_str,
                           user_input);
    g_free(tmp);
    g_free(tmp2);
    g_free(res_note);
    g_free(states);
    g_free(calendar_data);
    g_free(since_time_str);
    g_free(epid);

    sip_soap_raw_request_cb(sipe_private, from, body, NULL, NULL);

    g_free(body);
}
Ejemplo n.º 6
0
static void
sipe_domino_process_calendar_response(int return_code,
				 const char *body,
				 const char *content_type,
				 HttpConn *conn,
				 void *data)
{
	struct sipe_calendar *cal = data;

	SIPE_DEBUG_INFO_NOFORMAT("sipe_domino_process_calendar_response: cb started.");

	http_conn_set_close(conn);
	cal->http_conn = NULL;

	if (content_type && !g_str_has_prefix(content_type, "text/xml")) {
		cal->is_domino_disabled = TRUE;
		SIPE_DEBUG_INFO_NOFORMAT("sipe_domino_process_calendar_response: not XML, disabling.");
		return;
	}

	if (return_code == 200 && body) {
		struct sipe_core_private *sipe_private = cal->sipe_private;
		const sipe_xml *node, *node2, *node3;
		sipe_xml *xml;

		SIPE_DEBUG_INFO("sipe_domino_process_calendar_response: SUCCESS, ret=%d", return_code);
		xml = sipe_xml_parse(body, strlen(body));

		sipe_cal_events_free(cal->cal_events);
		cal->cal_events = NULL;
		/* viewentry */
		for (node = sipe_xml_child(xml, "viewentry");
		     node;
		     node = sipe_xml_twin(node))
		{
			struct sipe_cal_event *cal_event = g_new0(struct sipe_cal_event, 1);
			cal->cal_events = g_slist_append(cal->cal_events, cal_event);
			cal_event->cal_status = SIPE_CAL_BUSY;
			cal_event->is_meeting = TRUE;

			/* SIPE_DEBUG_INFO("viewentry unid=%s", sipe_xml_attribute(node, "unid")); */

			/* entrydata */
			for (node2 = sipe_xml_child(node, "entrydata");
			     node2;
			     node2 = sipe_xml_twin(node2))
			{
				const char *name = sipe_xml_attribute(node2, "name");

				SIPE_DEBUG_INFO("\tentrydata name=%s", name);

				if (sipe_strequal(name, VIEWENTITY_START0_TIME) ||
				    sipe_strequal(name, VIEWENTITY_START_TIME) ||
				    sipe_strequal(name, VIEWENTITY_END_TIME))
				{
					char *tmp = sipe_xml_data(sipe_xml_child(node2, "datetime"));
					time_t time_val = sipe_utils_str_to_time(tmp);

					if (sipe_strequal(name, VIEWENTITY_START_TIME)) {
						cal_event->start_time = time_val;
					} else if (sipe_strequal(name, VIEWENTITY_END_TIME)) {
						cal_event->end_time = time_val;
					}

					SIPE_DEBUG_INFO("\t\tdatetime=%s", asctime(gmtime(&time_val)));
					g_free(tmp);
				} else if (sipe_strequal(name, VIEWENTITY_TEXT_LIST)) {
					int i = 0;

					/* test */
					for (node3 = sipe_xml_child(node2, "textlist/text");
					     node3;
					     node3 = sipe_xml_twin(node3))
					{
						char *tmp = sipe_xml_data(node3);

						if (!tmp) continue;

						SIPE_DEBUG_INFO("\t\ttext=%s", tmp);
						if (i == 0) {
							cal_event->subject = g_strdup(tmp);
							SIPE_DEBUG_INFO("\t\t*Subj.=%s", tmp);
						} else {
							/* plain English, don't localize! */
							if (!g_ascii_strncasecmp(tmp, "Location:", 9)) {
								if (strlen(tmp) > 9) {
									cal_event->location = g_strdup(g_strstrip(tmp+9));
									SIPE_DEBUG_INFO("\t\t*Loc.=%s", cal_event->location);
								}
							/* Translators: (!) should be as in localized Lotus Notes to be able to extract meeting location */
							} else if (g_str_has_prefix(tmp, _("Location:"))) {
								guint len = strlen(_("Location:"));
								if (strlen(tmp) > len) {
									cal_event->location = g_strdup(g_strstrip(tmp+len));
									SIPE_DEBUG_INFO("\t\t*Loc.=%s", cal_event->location);
								}
							}
						}
						i++;
						g_free(tmp);
					}
				}
			}
		}
		sipe_xml_free(xml);

		/* creates FreeBusy from cal->cal_events */
		g_free(cal->free_busy);
		cal->free_busy = sipe_domino_get_free_busy(cal->fb_start, cal->cal_events);

		/* update SIP server */
		cal->is_updated = TRUE;
		if (SIPE_CORE_PRIVATE_FLAG_IS(OCS2007)) {
			/* sipe.h */
			publish_calendar_status_self(sipe_private,
						     NULL);
		} else {
			/* sipe.h */
			send_presence_soap(sipe_private, TRUE);
		}

	} else if (return_code < 0) {
void process_incoming_invite(struct sipe_core_private *sipe_private,
			     struct sipmsg *msg)
{
	gchar *newTag;
	const gchar *oldHeader;
	gchar *newHeader;
	gboolean is_multiparty = FALSE;
	gboolean was_multiparty = TRUE;
	gboolean just_joined = FALSE;
	gchar *from;
	const gchar *callid         = sipmsg_find_header(msg, "Call-ID");
	const gchar *roster_manager = sipmsg_find_header(msg, "Roster-Manager");
	const gchar *end_points_hdr = sipmsg_find_header(msg, "EndPoints");
	const gchar *trig_invite    = sipmsg_find_header(msg, "TriggeredInvite");
	const gchar *content_type   = sipmsg_find_header(msg, "Content-Type");
	const gchar *subject        = sipmsg_find_header(msg, "Subject");
	GSList *end_points = NULL;
	struct sip_session *session;
	struct sip_dialog *dialog;
	const gchar *ms_text_format;
	gboolean dont_delay = FALSE;

#ifdef HAVE_VV
	if (g_str_has_prefix(content_type, "multipart/alternative")) {
		sipe_mime_parts_foreach(content_type, msg->body, sipe_invite_mime_cb, msg);
		/* Reload Content-Type to get type of the selected message part */
		content_type = sipmsg_find_header(msg, "Content-Type");
	}
#endif

	if (g_str_has_prefix(content_type, "multipart/mixed")) {
		sipe_mime_parts_foreach(content_type, msg->body, sipe_invite_mime_mixed_cb, msg);
		/* Reload Content-Type to get type of the selected message part */
		content_type = sipmsg_find_header(msg, "Content-Type");
	}

	/* Lync 2010 file transfer */
	if (g_str_has_prefix(content_type, "application/ms-filetransfer+xml")) {
		sip_transport_response(sipe_private, msg, 488, "Not Acceptable Here", NULL);
		return;
	}

	/* Invitation to join conference */
	if (g_str_has_prefix(content_type, "application/ms-conf-invite+xml")) {
		process_incoming_invite_conf(sipe_private, msg);
		return;
	}

#ifdef HAVE_VV
	/* Invitation to audio call */
	if (msg->body && strstr(msg->body, "m=audio")) {
		process_incoming_invite_call(sipe_private, msg);
		return;
	}
#endif

	/* Only accept text invitations */
	if (msg->body && !(strstr(msg->body, "m=message") || strstr(msg->body, "m=x-ms-message"))) {
		sip_transport_response(sipe_private, msg, 501, "Not implemented", NULL);
		return;
	}

	// TODO There *must* be a better way to clean up the To header to add a tag...
	SIPE_DEBUG_INFO_NOFORMAT("Adding a Tag to the To Header on Invite Request...");
	oldHeader = sipmsg_find_header(msg, "To");
	newTag = gentag();
	newHeader = g_strdup_printf("%s;tag=%s", oldHeader, newTag);
	g_free(newTag);
	sipmsg_remove_header_now(msg, "To");
	sipmsg_add_header_now(msg, "To", newHeader);
	g_free(newHeader);

	if (end_points_hdr) {
		end_points = sipmsg_parse_endpoints_header(end_points_hdr);

		if (g_slist_length(end_points) > 2) {
			is_multiparty = TRUE;
		}
	}
	if (trig_invite && !g_ascii_strcasecmp(trig_invite, "TRUE")) {
		is_multiparty = TRUE;
	}

	/* Multiparty session */
	session = sipe_session_find_chat_by_callid(sipe_private, callid);
	if (is_multiparty) {

		if (session) {
			if (session->chat_session) {
				/* Update roster manager for existing multiparty session */
				if (roster_manager)
					sipe_chat_set_roster_manager(session, roster_manager);

			} else {
				gchar *chat_title = sipe_chat_get_name();

				/* Convert IM session to multiparty session */
				g_free(session->with);
				session->with = NULL;
				was_multiparty = FALSE;
				session->chat_session = sipe_chat_create_session(SIPE_CHAT_TYPE_MULTIPARTY,
										 roster_manager,
										 chat_title);

				g_free(chat_title);
			}
		} else {
			/* New multiparty session */
			session = sipe_session_add_chat(sipe_private,
							NULL,
							TRUE,
							roster_manager);
		}

		/* Create chat */
		if (!session->chat_session->backend) {
			gchar *self = sip_uri_self(sipe_private);
			session->chat_session->backend = sipe_backend_chat_create(SIPE_CORE_PUBLIC,
										  session->chat_session,
										  session->chat_session->title,
										  self);
			g_free(self);
		}
	}

	/* IM session */
	from = parse_from(sipmsg_find_header(msg, "From"));
	if (!session)
		session = sipe_session_find_or_add_im(sipe_private, from);

	/* session is now initialized */
	g_free(session->callid);
	session->callid = g_strdup(callid);

	if (is_multiparty && end_points) {
		gchar *to = parse_from(sipmsg_find_header(msg, "To"));
		GSList *entry = end_points;
		while (entry) {
			struct sipendpoint *end_point = entry->data;
			entry = entry->next;

			if (!g_ascii_strcasecmp(from, end_point->contact) ||
			    !g_ascii_strcasecmp(to,   end_point->contact))
				continue;

			dialog = sipe_dialog_find(session, end_point->contact);
			if (dialog) {
				g_free(dialog->theirepid);
				dialog->theirepid = end_point->epid;
				end_point->epid = NULL;
			} else {
				dialog = sipe_dialog_add(session);

				dialog->callid = g_strdup(session->callid);
				dialog->with = end_point->contact;
				end_point->contact = NULL;
				dialog->theirepid = end_point->epid;
				end_point->epid = NULL;

				just_joined = TRUE;

				/* send triggered INVITE */
				sipe_im_invite(sipe_private, session, dialog->with, NULL, NULL, NULL, TRUE);
			}
		}
		g_free(to);
	}

	if (end_points) {
		GSList *entry = end_points;
		while (entry) {
			struct sipendpoint *end_point = entry->data;
			entry = entry->next;
			g_free(end_point->contact);
			g_free(end_point->epid);
			g_free(end_point);
		}
		g_slist_free(end_points);
	}

	dialog = sipe_dialog_find(session, from);
	if (dialog) {
		sipe_im_cancel_dangling(sipe_private, session, dialog, from,
					sipe_im_reenqueue_unconfirmed);
		/* dialog is no longer valid */
	} else {
		just_joined = TRUE;
	}

	dialog = sipe_dialog_add(session);
	dialog->with = g_strdup(from);
	dialog->callid = g_strdup(session->callid);
	dialog->is_established = TRUE;
	sipe_dialog_parse(dialog, msg, FALSE);

	if (is_multiparty && !was_multiparty) {
		/* add current IM counterparty to chat */
		sipe_backend_chat_add(session->chat_session->backend,
				      sipe_dialog_first(session)->with,
				      FALSE);
	}

	/* add inviting party to chat */
	if (just_joined && session->chat_session) {
		sipe_backend_chat_add(session->chat_session->backend,
				      from,
				      TRUE);
	}

	if (!is_multiparty && subject)
		sipe_im_topic(sipe_private, session, subject);

	/* ms-text-format: text/plain; charset=UTF-8;msgr=WAAtAE0...DIADQAKAA0ACgA;ms-body=SGk= */

	/* This used only in 2005 official client, not 2007 or Reuters.
	   Disabled for most cases as interfering with audit of messages which only is applied to regular MESSAGEs.
	   Only enabled for 2005 multiparty chats as otherwise the first message got lost completely.
	*/
	/* also enabled for 2005 file transfer. Didn't work otherwise. */
	ms_text_format = sipmsg_find_header(msg, "ms-text-format");
	if (is_multiparty ||
	    (ms_text_format && g_str_has_prefix(ms_text_format, "text/x-msmsgsinvite")) )
	{
		if (ms_text_format) {
			if (g_str_has_prefix(ms_text_format, "text/x-msmsgsinvite"))
			{
				dont_delay = TRUE;
			}
			else if (g_str_has_prefix(ms_text_format, "text/plain") || g_str_has_prefix(ms_text_format, "text/html"))
			{
				/* please do not optimize logic inside as this code may be re-enabled for other cases */
				gchar *html = get_html_message(ms_text_format, NULL);
				if (html) {
					if (is_multiparty) {
						sipe_backend_chat_message(SIPE_CORE_PUBLIC,
									  session->chat_session->backend,
									  from,
									  0,
									  html);
					} else {
						sipe_backend_im_message(SIPE_CORE_PUBLIC,
									from,
									html);
					}
					g_free(html);
					sipmsg_add_header(msg, "Supported", "ms-text-format"); /* accepts received message */
					dont_delay = TRUE;
				}
			}
		}
	}

	g_free(from);

	sipmsg_add_header(msg, "Supported", "com.microsoft.rtc-multiparty");

	if (dont_delay || !SIPE_CORE_PRIVATE_FLAG_IS(MPOP)) {
		send_invite_response(sipe_private, msg);
	} else {
		delayed_invite_response(sipe_private, msg, session->callid);
	}
}