From cd42059358aa55588d12c73f0a92b4b4c6d586e2 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Adrien=20B=C3=A9raud?= <adrien.beraud@savoirfairelinux.com>
Date: Fri, 10 Jul 2015 14:41:25 -0400
Subject: [PATCH] sip: add out-of-call text message support

Change-Id: I71fecc3943c0dcf1df7c2e7873f235ce2cf74e8b
---
 src/dring/account_const.h  |  6 +++
 src/sip/sipaccount.cpp     | 76 ++++++++++++++++++++++++++++++++++++++
 src/sip/sipaccount.h       |  2 +
 src/sip/sipaccountbase.cpp |  7 ++++
 src/sip/sipaccountbase.h   |  2 +
 src/sip/sipvoiplink.cpp    | 20 +++++++---
 6 files changed, 107 insertions(+), 6 deletions(-)

diff --git a/src/dring/account_const.h b/src/dring/account_const.h
index a2c6ca215f..ab2aded9d2 100644
--- a/src/dring/account_const.h
+++ b/src/dring/account_const.h
@@ -86,6 +86,12 @@ constexpr static const char STATE_DESC                [] = "Transport.statusDesc
 
 } //namespace DRing::VolatileProperties::Transport
 
+namespace InstantMessaging {
+
+constexpr static const char OFF_CALL                 [] = "IM.offCall";
+
+}
+
 } //namespace DRing::Account::VolatileProperties
 
 namespace ConfProperties {
diff --git a/src/sip/sipaccount.cpp b/src/sip/sipaccount.cpp
index 7f519de04a..cb7a2b1756 100644
--- a/src/sip/sipaccount.cpp
+++ b/src/sip/sipaccount.cpp
@@ -667,6 +667,7 @@ SIPAccount::getVolatileAccountDetails() const
     auto a = SIPAccountBase::getVolatileAccountDetails();
     a.emplace(Conf::CONFIG_ACCOUNT_REGISTRATION_STATE_CODE, ring::to_string(registrationStateDetailed_.first));
     a.emplace(Conf::CONFIG_ACCOUNT_REGISTRATION_STATE_DESC, registrationStateDetailed_.second);
+    a.emplace(DRing::Account::VolatileProperties::InstantMessaging::OFF_CALL, TRUE_STR);
 
     if (presence_) {
         a.emplace(Conf::CONFIG_PRESENCE_STATUS,     presence_->isOnline() ? TRUE_STR : FALSE_STR);
@@ -2113,4 +2114,79 @@ void SIPAccount::updateDialogViaSentBy(pjsip_dialog *dlg)
         pjsip_dlg_set_via_sent_by(dlg, &via_addr_, via_tp_);
 }
 
+/**
+ * Create Accept header for MESSAGE.
+ */
+static pjsip_accept_hdr* im_create_accept(pj_pool_t *pool)
+{
+    /* Create Accept header. */
+    pjsip_accept_hdr *accept;
+
+    accept = pjsip_accept_hdr_create(pool);
+    accept->values[0] = CONST_PJ_STR("text/plain");
+    accept->values[1] = CONST_PJ_STR("application/im-iscomposing+xml");
+    accept->count = 2;
+
+    return accept;
+}
+
+void
+SIPAccount::sendTextMessage(const std::string& to, const std::string& msg)
+{
+    if (to.empty() or msg.empty())
+        return;
+
+    std::string toUri;
+    if (to.find("sip:") != std::string::npos or
+        to.find("sips:") != std::string::npos)
+        toUri = to;
+    else
+        toUri = getToUri(to);
+
+    const pjsip_method msg_method = { PJSIP_OTHER_METHOD, CONST_PJ_STR("MESSAGE") };
+    std::string from(getFromUri());
+    pj_str_t pjFrom = pj_str((char*) from.c_str());
+    pj_str_t pjTo = pj_str((char*) toUri.c_str());
+
+    /* Create request. */
+    pjsip_tx_data *tdata;
+    pj_status_t status = pjsip_endpt_create_request(link_->getEndpoint(), &msg_method,
+                                        &pjTo, &pjFrom, &pjTo, NULL, NULL, -1, NULL, &tdata);
+    if (status != PJ_SUCCESS) {
+        char err_msg[128];
+        err_msg[0] = '\0';
+        pj_str_t descr = pj_strerror(status, err_msg, sizeof(err_msg));
+        RING_ERR("Unable to create request: %.*s", descr.slen, descr.ptr);
+        return;
+    }
+
+    const pjsip_tpselector tp_sel = getTransportSelector();
+    pjsip_tx_data_set_transport(tdata, &tp_sel);
+
+    /* Add accept header. */
+    pjsip_msg_add_hdr( tdata->msg, (pjsip_hdr*)im_create_accept(tdata->pool));
+
+    /* Set default media type if none is specified */
+    static const constexpr pj_str_t type = CONST_PJ_STR("text");
+    static const constexpr pj_str_t subtype = CONST_PJ_STR("plain");
+
+    pj_str_t message = pj_str((char*) msg.c_str());
+
+    tdata->msg->body = pjsip_msg_body_create(tdata->pool, &type, &subtype, &message);
+    if (tdata->msg->body == NULL) {
+        RING_ERR("Unable to create msg body");
+        pjsip_tx_data_dec_ref(tdata);
+        return;
+    }
+
+    status = pjsip_endpt_send_request(link_->getEndpoint(), tdata, -1, nullptr, nullptr);
+    if (status != PJ_SUCCESS) {
+        char err_msg[128];
+        err_msg[0] = '\0';
+        pj_str_t descr = pj_strerror(status, err_msg, sizeof(err_msg));
+        RING_ERR("Unable to send request: %.*s", descr.slen, descr.ptr);
+        return;
+    }
+}
+
 } // namespace ring
diff --git a/src/sip/sipaccount.h b/src/sip/sipaccount.h
index f01fe69dd3..38009b8a38 100644
--- a/src/sip/sipaccount.h
+++ b/src/sip/sipaccount.h
@@ -513,6 +513,8 @@ class SIPAccount : public SIPAccountBase {
 
         void onRegister(pjsip_regc_cbparam *param);
 
+        virtual void sendTextMessage(const std::string& /* to */, const std::string& /* message */);
+
     private:
         void doRegister1_();
         void doRegister2_();
diff --git a/src/sip/sipaccountbase.cpp b/src/sip/sipaccountbase.cpp
index 5a4d1fe97a..09c122d102 100644
--- a/src/sip/sipaccountbase.cpp
+++ b/src/sip/sipaccountbase.cpp
@@ -328,4 +328,11 @@ SIPAccountBase::getIceOptions() const noexcept
     return opts;
 }
 
+void
+SIPAccountBase::onTextMessage(const std::string& from, const std::string& msg)
+{
+    RING_WARN("Text message received ! %s -> %s",  from.c_str(), msg.c_str());
+    emitSignal<DRing::ConfigurationSignal::IncomingAccountMessage>(accountID_, from, msg);
+}
+
 } // namespace ring
diff --git a/src/sip/sipaccountbase.h b/src/sip/sipaccountbase.h
index 7d9f9f80a2..6728a3c9c1 100644
--- a/src/sip/sipaccountbase.h
+++ b/src/sip/sipaccountbase.h
@@ -244,6 +244,8 @@ public:
 
     const IceTransportOptions getIceOptions() const noexcept override;
 
+    void onTextMessage(const std::string& from, const std::string& msg);
+
 protected:
     virtual void serialize(YAML::Emitter &out);
     virtual void serializeTls(YAML::Emitter &out);
diff --git a/src/sip/sipvoiplink.cpp b/src/sip/sipvoiplink.cpp
index b409ecdeea..57070c5b6a 100644
--- a/src/sip/sipvoiplink.cpp
+++ b/src/sip/sipvoiplink.cpp
@@ -63,8 +63,6 @@
 #include "client/videomanager.h"
 #endif
 
-#include "client/ring_signal.h"
-
 #include "pres_sub_server.h"
 
 #include "array_size.h"
@@ -223,6 +221,10 @@ transaction_request_cb(pjsip_rx_data *rdata)
     std::string viaHostname(sip_via.host.ptr, sip_via.host.slen);
     const std::string remote_user(sip_from_uri->user.ptr, sip_from_uri->user.slen);
     const std::string remote_hostname(sip_from_uri->host.ptr, sip_from_uri->host.slen);
+    char tmp[PJSIP_MAX_URL_SIZE];
+    size_t length = pjsip_uri_print(PJSIP_URI_IN_FROMTO_HDR, sip_from_uri, tmp, PJSIP_MAX_URL_SIZE);
+    std::string peerNumber(tmp, length);
+    sip_utils::stripSipUriPrefix(peerNumber);
 
     auto link = getSIPVoIPLink();
     if (not link) {
@@ -252,6 +254,13 @@ transaction_request_cb(pjsip_rx_data *rdata)
                 if (ret == 1 and voicemail != 0)
                     Manager::instance().startVoiceMessageNotification(account_id, voicemail);
             }
+        } else if (request.find("MESSAGE") != std::string::npos) {
+            if (body) {
+                pjsip_endpt_respond( endpt_, NULL, rdata, PJSIP_SC_OK, NULL, NULL, NULL, NULL);
+                std::string text {(char*)body->data, (char*)body->data+body->len};
+                account->onTextMessage(peerNumber, text);
+                return PJ_FALSE;
+            }
         }
 
         try_respond_stateless(endpt_, rdata, PJSIP_SC_OK, NULL, NULL, NULL);
@@ -291,10 +300,6 @@ transaction_request_cb(pjsip_rx_data *rdata)
         return PJ_FALSE;
     }
 
-    char tmp[PJSIP_MAX_URL_SIZE];
-    size_t length = pjsip_uri_print(PJSIP_URI_IN_FROMTO_HDR, sip_from_uri, tmp, PJSIP_MAX_URL_SIZE);
-    std::string peerNumber(tmp, length);
-    sip_utils::stripSipUriPrefix(peerNumber);
 
     if (not remote_user.empty() and not remote_hostname.empty())
         peerNumber = remote_user + "@" + remote_hostname;
@@ -581,6 +586,9 @@ SIPVoIPLink::SIPVoIPLink()
     static const pj_str_t accepted = CONST_PJ_STR("application/sdp");
     pjsip_endpt_add_capability(endpt_, &mod_ua_, PJSIP_H_ACCEPT, nullptr, 1, &accepted);
 
+    static const pj_str_t iscomposing = CONST_PJ_STR("application/im-iscomposing+xml");
+    pjsip_endpt_add_capability(endpt_, &mod_ua_, PJSIP_H_ACCEPT, nullptr, 1, &iscomposing);
+
     TRY(pjsip_replaces_init_module(endpt_));
 #undef TRY
 
-- 
GitLab