void sipe_core_ft_incoming_accept(struct sipe_file_transfer *ft, const gchar *who, int fd, unsigned short port) { struct sipe_file_transfer_private *ft_private = SIPE_FILE_TRANSFER_PRIVATE; gchar *body; ft_private->auth_cookie = rand() % 1000000000; body = g_strdup_printf("Invitation-Command: ACCEPT\r\n" "Invitation-Cookie: %s\r\n" "IP-Address: %s\r\n" "Port: %u\r\n" "PortX: 11178\r\n" "AuthCookie: %u\r\n" "Request-Data: IP-Address:\r\n", ft_private->invitation_cookie, sipe_utils_get_suitable_local_ip(fd), port, ft_private->auth_cookie); if (!ft_private->dialog) { struct sip_session *session = sipe_session_find_or_add_im(ft_private->sipe_private, who); ft_private->dialog = sipe_dialog_find(session, who); } if (ft_private->dialog) { sipe_ft_request(ft_private, body); } g_free(body); }
void sipe_core_user_feedback_typing(struct sipe_core_public *sipe_public, const gchar *to, gboolean typing) { struct sipe_core_private *sipe_private = SIPE_CORE_PRIVATE; struct sip_session *session = sipe_session_find_im(sipe_private, to); struct sip_dialog *dialog = sipe_dialog_find(session, to); /* only enable this debug output while testing SIPE_DEBUG_INFO("sipe_core_user_feedback_typing session %p (%s) dialog %p (%s) established %s", session, session ? session->callid : "N/A", dialog, dialog ? dialog->callid : "N/A", (dialog && dialog->is_established) ? "YES" : "NO"); */ if (session && dialog && dialog->is_established) { gchar *body = g_strdup_printf("<?xml version=\"1.0\"?>" "<KeyboardActivity>" " <status status=\"%s\" />" "</KeyboardActivity>", typing ? "type" : "idle"); sip_transport_info(sipe_private, "Content-Type: application/xml\r\n", body, dialog, process_info_typing_response); g_free(body); } }
void process_incoming_refer(struct sipe_core_private *sipe_private, struct sipmsg *msg) { gchar *self = sip_uri_self(sipe_private); const gchar *callid = sipmsg_find_header(msg, "Call-ID"); gchar *from = parse_from(sipmsg_find_header(msg, "From")); gchar *refer_to = parse_from(sipmsg_find_header(msg, "Refer-to")); gchar *referred_by = g_strdup(sipmsg_find_header(msg, "Referred-By")); struct sip_session *session; struct sip_dialog *dialog; session = sipe_session_find_chat_by_callid(sipe_private, callid); dialog = sipe_dialog_find(session, from); if (!session || !dialog || !session->chat_session || (session->chat_session->type != SIPE_CHAT_TYPE_MULTIPARTY) || !session->chat_session->id || !sipe_strcase_equal(session->chat_session->id, self)) { sip_transport_response(sipe_private, msg, 500, "Server Internal Error", NULL); } else { sip_transport_response(sipe_private, msg, 202, "Accepted", NULL); sipe_im_invite(sipe_private, session, refer_to, NULL, NULL, referred_by, FALSE); } g_free(self); g_free(from); g_free(refer_to); g_free(referred_by); }
void sipe_core_ft_outgoing_init(struct sipe_file_transfer *ft, const gchar *filename, gsize size, const gchar *who) { struct sipe_file_transfer_private *ft_private = SIPE_FILE_TRANSFER_PRIVATE; struct sipe_core_private *sipe_private = ft_private->sipe_private; struct sip_dialog *dialog; gchar *body = g_strdup_printf("Application-Name: File Transfer\r\n" "Application-GUID: {5D3E02AB-6190-11d3-BBBB-00C04F795683}\r\n" "Invitation-Command: INVITE\r\n" "Invitation-Cookie: %s\r\n" "Application-File: %s\r\n" "Application-FileSize: %" G_GSIZE_FORMAT "\r\n" //"Connectivity: N\r\n" TODO "Encryption: R\r\n", // TODO: non encrypted file transfer support ft_private->invitation_cookie, filename, size); struct sip_session *session = sipe_session_find_or_add_im(sipe_private, who); // Queue the message sipe_session_enqueue_message(session, body, "text/x-msmsgsinvite"); dialog = sipe_dialog_find(session, who); if (dialog && !dialog->outgoing_invite) { sipe_im_process_queue(sipe_private, session); } else if (!dialog || !dialog->outgoing_invite) { // Need to send the INVITE to get the outgoing dialog setup sipe_invite(sipe_private, session, who, body, "text/x-msmsgsinvite", NULL, FALSE); dialog = sipe_dialog_find(session, who); } dialog->filetransfers = g_slist_append(dialog->filetransfers, ft_private); ft_private->dialog = dialog; g_free(body); }
static gboolean process_info_typing_response(struct sipe_core_private *sipe_private, struct sipmsg *msg, SIPE_UNUSED_PARAMETER struct transaction *trans) { /* Indicates dangling IM session which needs to be dropped */ if (msg->response == 408 || /* Request timeout */ msg->response == 480 || /* Temporarily Unavailable */ msg->response == 481) { /* Call/Transaction Does Not Exist */ gchar *with = parse_from(sipmsg_find_header(msg, "To")); struct sip_session *session = sipe_session_find_im(sipe_private, with); struct sip_dialog *dialog = sipe_dialog_find(session, with); if (dialog) sipe_im_cancel_dangling(sipe_private, session, dialog, with, sipe_im_cancel_unconfirmed); g_free(with); } return(TRUE); }
/** Invite counterparty to join conference callback */ static gboolean process_invite_conf_response(struct sipe_core_private *sipe_private, struct sipmsg *msg, SIPE_UNUSED_PARAMETER struct transaction *trans) { struct sip_dialog *dialog = g_new0(struct sip_dialog, 1); dialog->callid = g_strdup(sipmsg_find_header(msg, "Call-ID")); dialog->cseq = sipmsg_parse_cseq(msg); dialog->with = parse_from(sipmsg_find_header(msg, "To")); sipe_dialog_parse(dialog, msg, TRUE); if (msg->response >= 200) { /* send ACK to counterparty */ dialog->cseq--; sip_transport_ack(sipe_private, dialog); dialog->outgoing_invite = NULL; dialog->is_established = TRUE; } if (msg->response >= 400) { SIPE_DEBUG_INFO("process_invite_conf_response: INVITE response is not 200. Failed to invite %s.", dialog->with); /* @TODO notify user of failure to invite counterparty */ sipe_dialog_free(dialog); return FALSE; } if (msg->response >= 200) { struct sip_session *session = sipe_session_find_im(sipe_private, dialog->with); struct sip_dialog *im_dialog = sipe_dialog_find(session, dialog->with); /* close IM session to counterparty */ if (im_dialog) { sip_transport_bye(sipe_private, im_dialog); sipe_dialog_remove(session, dialog->with); } } sipe_dialog_free(dialog); return TRUE; }
void sipe_process_conference(struct sipe_core_private *sipe_private, struct sipmsg *msg) { sipe_xml *xn_conference_info; const sipe_xml *node; const sipe_xml *xn_subject; const gchar *focus_uri; struct sip_session *session; gboolean just_joined = FALSE; #ifdef HAVE_VV gboolean audio_was_added = FALSE; #endif if (msg->response != 0 && msg->response != 200) return; if (msg->bodylen == 0 || msg->body == NULL || !sipe_strequal(sipmsg_find_header(msg, "Event"), "conference")) return; xn_conference_info = sipe_xml_parse(msg->body, msg->bodylen); if (!xn_conference_info) return; focus_uri = sipe_xml_attribute(xn_conference_info, "entity"); session = sipe_session_find_conference(sipe_private, focus_uri); if (!session) { SIPE_DEBUG_INFO("sipe_process_conference: unable to find conf session with focus=%s", focus_uri); return; } if (!session->chat_session->backend) { gchar *self = sip_uri_self(sipe_private); /* create chat */ session->chat_session->backend = sipe_backend_chat_create(SIPE_CORE_PUBLIC, session->chat_session, session->chat_session->title, self); just_joined = TRUE; /* @TODO ask for full state (re-subscribe) if it was a partial one - * this is to obtain full list of conference participants. */ g_free(self); } /* subject */ if ((xn_subject = sipe_xml_child(xn_conference_info, "conference-description/subject"))) { g_free(session->subject); session->subject = sipe_xml_data(xn_subject); sipe_backend_chat_topic(session->chat_session->backend, session->subject); SIPE_DEBUG_INFO("sipe_process_conference: subject=%s", session->subject ? session->subject : ""); } /* IM MCU URI */ if (!session->im_mcu_uri) { for (node = sipe_xml_child(xn_conference_info, "conference-description/conf-uris/entry"); node; node = sipe_xml_twin(node)) { gchar *purpose = sipe_xml_data(sipe_xml_child(node, "purpose")); if (sipe_strequal("chat", purpose)) { g_free(purpose); session->im_mcu_uri = sipe_xml_data(sipe_xml_child(node, "uri")); SIPE_DEBUG_INFO("sipe_process_conference: im_mcu_uri=%s", session->im_mcu_uri); break; } g_free(purpose); } } /* users */ for (node = sipe_xml_child(xn_conference_info, "users/user"); node; node = sipe_xml_twin(node)) { const gchar *user_uri = sipe_xml_attribute(node, "entity"); const gchar *state = sipe_xml_attribute(node, "state"); gchar *role = sipe_xml_data(sipe_xml_child(node, "roles/entry")); gboolean is_operator = sipe_strequal(role, "presenter"); gboolean is_in_im_mcu = FALSE; gchar *self = sip_uri_self(sipe_private); if (sipe_strequal("deleted", state)) { if (sipe_backend_chat_find(session->chat_session->backend, user_uri)) { sipe_backend_chat_remove(session->chat_session->backend, user_uri); } } else { /* endpoints */ const sipe_xml *endpoint; for (endpoint = sipe_xml_child(node, "endpoint"); endpoint; endpoint = sipe_xml_twin(endpoint)) { const gchar *session_type; gchar *status = sipe_xml_data(sipe_xml_child(endpoint, "status")); gboolean connected = sipe_strequal("connected", status); g_free(status); if (!connected) continue; session_type = sipe_xml_attribute(endpoint, "session-type"); if (sipe_strequal("chat", session_type)) { is_in_im_mcu = TRUE; if (!sipe_backend_chat_find(session->chat_session->backend, user_uri)) { sipe_backend_chat_add(session->chat_session->backend, user_uri, !just_joined && g_ascii_strcasecmp(user_uri, self)); } if (is_operator) { sipe_backend_chat_operator(session->chat_session->backend, user_uri); } } else if (sipe_strequal("audio-video", session_type)) { #ifdef HAVE_VV if (!session->is_call) audio_was_added = TRUE; #endif } } if (!is_in_im_mcu) { if (sipe_backend_chat_find(session->chat_session->backend, user_uri)) { sipe_backend_chat_remove(session->chat_session->backend, user_uri); } } } g_free(role); g_free(self); } #ifdef HAVE_VV if (audio_was_added) { session->is_call = TRUE; ask_accept_voice_conference(sipe_private, focus_uri, NULL, (SipeUserAskCb) call_accept_cb, (SipeUserAskCb) call_decline_cb); } #endif /* entity-view, locked */ for (node = sipe_xml_child(xn_conference_info, "conference-view/entity-view"); node; node = sipe_xml_twin(node)) { const sipe_xml *xn_type = sipe_xml_child(node, "entity-state/media/entry/type"); gchar *tmp = NULL; if (xn_type && sipe_strequal("chat", (tmp = sipe_xml_data(xn_type)))) { const sipe_xml *xn_locked = sipe_xml_child(node, "entity-state/locked"); if (xn_locked) { gchar *locked = sipe_xml_data(xn_locked); gboolean prev_locked = session->locked; session->locked = sipe_strequal(locked, "true"); if (prev_locked && !session->locked) { sipe_user_present_info(sipe_private, session, _("This conference is no longer locked. Additional participants can now join.")); } if (!prev_locked && session->locked) { sipe_user_present_info(sipe_private, session, _("This conference is locked. Nobody else can join the conference while it is locked.")); } SIPE_DEBUG_INFO("sipe_process_conference: session->locked=%s", session->locked ? "TRUE" : "FALSE"); g_free(locked); } } g_free(tmp); } sipe_xml_free(xn_conference_info); if (session->im_mcu_uri) { struct sip_dialog *dialog = sipe_dialog_find(session, session->im_mcu_uri); if (!dialog) { dialog = sipe_dialog_add(session); dialog->callid = g_strdup(session->callid); dialog->with = g_strdup(session->im_mcu_uri); /* send INVITE to IM MCU */ sipe_im_invite(sipe_private, session, dialog->with, NULL, NULL, NULL, FALSE); } } sipe_process_pending_invite_queue(sipe_private, session); }
void process_incoming_message(struct sipe_core_private *sipe_private, struct sipmsg *msg) { gchar *from; const gchar *contenttype; gboolean found = FALSE; from = parse_from(sipmsg_find_header(msg, "From")); if (!from) return; SIPE_DEBUG_INFO("got message from %s: %s", from, msg->body); contenttype = sipmsg_find_header(msg, "Content-Type"); if (g_str_has_prefix(contenttype, "text/plain") || g_str_has_prefix(contenttype, "text/html") || g_str_has_prefix(contenttype, "multipart/related") || g_str_has_prefix(contenttype, "multipart/alternative")) { const gchar *callid = sipmsg_find_header(msg, "Call-ID"); gchar *html = get_html_message(contenttype, msg->body); struct sip_session *session = sipe_session_find_chat_or_im(sipe_private, callid, from); if (session && session->chat_session) { if (session->chat_session->type == SIPE_CHAT_TYPE_CONFERENCE) { /* a conference */ gchar *tmp = parse_from(sipmsg_find_header(msg, "Ms-Sender")); gchar *sender = parse_from(tmp); g_free(tmp); sipe_backend_chat_message(SIPE_CORE_PUBLIC, session->chat_session->backend, sender, 0, html); g_free(sender); } else { /* a multiparty chat */ 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); sip_transport_response(sipe_private, msg, 200, "OK", NULL); found = TRUE; } else if (g_str_has_prefix(contenttype, "application/im-iscomposing+xml")) { sipe_xml *isc = sipe_xml_parse(msg->body, msg->bodylen); const sipe_xml *state; gchar *statedata; if (!isc) { SIPE_DEBUG_INFO_NOFORMAT("process_incoming_message: can not parse iscomposing"); g_free(from); return; } state = sipe_xml_child(isc, "state"); if (!state) { SIPE_DEBUG_INFO_NOFORMAT("process_incoming_message: no state found"); sipe_xml_free(isc); g_free(from); return; } statedata = sipe_xml_data(state); if (statedata) { if (strstr(statedata, "active")) { sipe_backend_user_feedback_typing(SIPE_CORE_PUBLIC, from); } else { sipe_backend_user_feedback_typing_stop(SIPE_CORE_PUBLIC, from); } g_free(statedata); } sipe_xml_free(isc); sip_transport_response(sipe_private, msg, 200, "OK", NULL); found = TRUE; } else if (g_str_has_prefix(contenttype, "text/x-msmsgsinvite")) { const gchar *callid = sipmsg_find_header(msg, "Call-ID"); struct sip_session *session = sipe_session_find_chat_or_im(sipe_private, callid, from); if (session) { struct sip_dialog *dialog = sipe_dialog_find(session, from); GSList *body = sipe_ft_parse_msg_body(msg->body); found = sipe_process_incoming_x_msmsgsinvite(sipe_private, dialog, body); sipe_utils_nameval_free(body); if (found) { sip_transport_response(sipe_private, msg, 200, "OK", NULL); } } else { sip_transport_response(sipe_private, msg, 481, "Call Leg/Transaction Does Not Exist", NULL); found = TRUE; } } if (!found) { const gchar *callid = sipmsg_find_header(msg, "Call-ID"); struct sip_session *session = sipe_session_find_chat_or_im(sipe_private, callid, from); if (session) { gchar *errmsg = g_strdup_printf(_("Received a message with unrecognized contents from %s"), from); sipe_user_present_error(sipe_private, session, errmsg); g_free(errmsg); } SIPE_DEBUG_INFO("got unknown mime-type '%s'", contenttype); sip_transport_response(sipe_private, msg, 415, "Unsupported media type", NULL); } g_free(from); }
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); } }