diff --git a/src/iax/iaxcall.cpp b/src/iax/iaxcall.cpp
index 5397a7fa59eb1e6b4a1d9dc293b632906d57f6d2..5cd366f869e42b91585946114439b1e4603a1c2f 100644
--- a/src/iax/iaxcall.cpp
+++ b/src/iax/iaxcall.cpp
@@ -241,12 +241,12 @@ IAXCall::carryingDTMFdigits(char code)
 
 #if HAVE_INSTANT_MESSAGING
 void
-IAXCall::sendTextMessage(const std::map<std::string, std::string>& messages,
+IAXCall::sendTextMessage(const std::map<std::string, std::string>& /*messages */,
                          const std::string& /*from*/)
 {
-    std::lock_guard<std::mutex> lock(IAXVoIPLink::mutexIAX);
-    const auto& msgs = InstantMessaging::appendMimePayloads(messages);
-    InstantMessaging::sendIaxMessage(session, getCallId(), msgs);
+    // std::lock_guard<std::mutex> lock(IAXVoIPLink::mutexIAX);
+    //TODO: implement multipart messages for IAX
+    // InstantMessaging::sendIaxMessage(session, getCallId(), msgs);
 }
 #endif
 
diff --git a/src/im/instant_messaging.cpp b/src/im/instant_messaging.cpp
index 9656b91e87fa9dde69f87489bf1d1bcc02d26d91..f5d3829547f3b1a93fc133d6de5c820e59969be6 100644
--- a/src/im/instant_messaging.cpp
+++ b/src/im/instant_messaging.cpp
@@ -3,6 +3,7 @@
  *  Author: Emmanuel Milou <emmanuel.milou@savoirfairelinux.com>
  *  Author: Emmanuel Lepage <elv1313@gmail.com>
  *  Author: Alexandre Savard <alexandre.savard@savoirfairelinux.com>
+ *  Author: Stepan Salenikovich <stepan.salenikovich@savoirfairelinux.com>
  *
  *  This program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License as published by
@@ -36,69 +37,269 @@
 
 #include <expat.h>
 #include <pjsip_ua.h>
+#include <pjsip.h>
 
 namespace ring {
 
-static void XMLCALL
-startElementCallback(void* userData, const char* name, const char** atts)
+/**
+ * the pair<string, string> we receive is expected to be in the format <mime type, payload>
+ * the mime type is in the format "type/subtype"
+ * in the header it will be presented as "Content-Type: type/subtype"
+ * following the RFC spec, this header line can also contain other parameters in the format:
+ *     Content-Type: type/subtype; arg=value; arg=value; ...
+ * thus we also accept the key of the map to be in such a format:
+ *     type/subtype; arg=value; arg=value; ...
+ */
+static void
+createMessageBody(pj_pool_t* pool, const std::pair<std::string, std::string>& payload,
+                  pjsip_msg_body** body_p)
 {
-    if (strcmp(name, "entry"))
+    /* parse the key:
+     * 1. split by ';'
+     * 2. parse the first result by spliting by '/' into a type and subtype
+     * 3. parse any following strings into arg=value by splitting by '='
+     */
+
+    // NOTE: we duplicate all the c_str when creating pj_str_t strings because we're potentially
+    // working with local vars which might be destroyed before the message is sent and thus the
+    // the mem they pointed to could be something else at the time the message is actually sent
+
+    std::string mimeType, parameters;
+    auto sep = payload.first.find(';');
+    if (std::string::npos == sep) {
+        mimeType = payload.first;
+    } else {
+        mimeType = payload.first.substr(0, sep);
+        parameters = payload.first.substr(sep + 1);
+    }
+
+    // split mime type to type and subtype
+    sep = mimeType.find('/');
+    if (std::string::npos == sep) {
+        RING_DBG("bad mime type: '%.30s'", mimeType.c_str());
+        throw InstantMessaging::InstantMessageException("invalid mime type");
+    }
+
+    const auto& type = mimeType.substr(0, sep);
+    const auto& subtype = mimeType.substr(sep + 1);
+
+    // create part
+    auto type_pj = pj_strdup3(pool, type.c_str());
+    auto subtype_pj = pj_strdup3(pool, subtype.c_str());
+    auto message_pj = pj_strdup3(pool, payload.second.c_str());
+    *body_p = pjsip_msg_body_create(pool, &type_pj, &subtype_pj, &message_pj);
+
+    if (not parameters.size())
         return;
 
-    InstantMessaging::UriEntry entry = InstantMessaging::UriEntry();
+    // now try to add parameters one by one
+    do {
+        sep = parameters.find(';');
+        const auto& paramPair = parameters.substr(0, sep);
+        if (not paramPair.size())
+            break;
 
-    for (const char **att = atts; *att; att += 2)
-        entry.insert(std::pair<std::string, std::string> (*att, *(att+1)));
+        // split paramPair into arg and value by '='
+        auto paramSplit = paramPair.find('=');
+        if (std::string::npos == paramSplit) {
+            RING_DBG("bad parameter: '%.30s'", paramPair.c_str());
+            throw InstantMessaging::InstantMessageException("invalid parameter");
+        }
 
-    static_cast<InstantMessaging::UriList *>(userData)->push_back(entry);
-}
+        const auto& arg = paramPair.substr(0, paramSplit);
+        const auto& value = paramPair.substr(paramSplit + 1);
 
-static void XMLCALL
-endElementCallback(void * /*userData*/, const char* /*name*/)
-{}
+        // add to the body content type
+        auto arg_pj = pj_strdup3(pool, arg.c_str());
+        pj_strtrim(&arg_pj);
+        auto value_pj = pj_strdup3(pool, value.c_str());
+        pj_strtrim(&value_pj);
+
+        pjsip_param* param = PJ_POOL_ALLOC_T(pool, pjsip_param);
+        param->name = arg_pj;
+        param->value = value_pj;
+        pj_list_push_back(&(*body_p)->content_type.param, param);
+
+        // next parameter?
+        if (std::string::npos != sep)
+            parameters = parameters.substr(sep + 1);
+    } while (std::string::npos != sep);
+}
 
 void
-InstantMessaging::sendSipMessage(pjsip_inv_session* session, const std::string& id,
-                                 const std::vector<std::string>& chunks)
+InstantMessaging::sendSipMessage(pjsip_inv_session* session,
+                                 const std::map<std::string, std::string>& payloads)
 {
-    for (const auto& text: chunks) {
-        const pjsip_method msg_method = { PJSIP_OTHER_METHOD, pj_str((char*)"MESSAGE") };
-        pjsip_tx_data* tdata;
+    if (payloads.empty()) {
+        RING_WARN("the payloads argument is empty; ignoring message");
+        return;
+    }
+
+    const pjsip_method msg_method = {PJSIP_OTHER_METHOD, {const_cast<char*>("MESSAGE"), 7}};
 
+    {
         auto dialog = session->dlg;
-        pjsip_dlg_inc_lock(dialog);
+        sip_utils::PJDialogLock dialog_lock {dialog};
+
+        pjsip_tx_data* tdata = nullptr;
+        auto status = pjsip_dlg_create_request(dialog, &msg_method, -1, &tdata);
+        if (status != PJ_SUCCESS) {
+            RING_ERR("pjsip_dlg_create_request failed: %s",
+                      sip_utils::sip_strerror(status).c_str());
+            throw InstantMessageException("Internal SIP error");
+        }
 
-        if (pjsip_dlg_create_request(dialog, &msg_method, -1, &tdata) != PJ_SUCCESS) {
-            pjsip_dlg_dec_lock(dialog);
-            return;
+        // multi-part body?
+        if (payloads.size() > 1) {
+            /* if ctype is not specified "multipart/mixed" will be used
+             * if the boundary is not specified, a random one will be generateAudioPort
+             * FIXME: generate boundary and check that none of the message parts contain it before
+             *        calling this function; however the probability of this happenings if quite low as
+             *        the randomly generated string is fairly long
+             */
+            tdata->msg->body = pjsip_multipart_create(tdata->pool, nullptr, nullptr);
+
+            for (const auto& pair: payloads) {
+                auto part = pjsip_multipart_create_part(tdata->pool);
+                if (not part) {
+                    RING_ERR("pjsip_multipart_create_part failed: %s",
+                              sip_utils::sip_strerror(status).c_str());
+                    throw InstantMessageException("Internal SIP error");
+                }
+
+                createMessageBody(tdata->pool, pair, &part->body);
+
+                status = pjsip_multipart_add_part(tdata->pool, tdata->msg->body, part);
+                if (status != PJ_SUCCESS) {
+                    RING_ERR("pjsip_multipart_add_part failed: %s",
+                             sip_utils::sip_strerror(status).c_str());
+                    throw InstantMessageException("Internal SIP error");
+                }
+            }
+        } else {
+            createMessageBody(tdata->pool, *payloads.begin(), &tdata->msg->body);
         }
 
-        //TODO multipart/mixed and multipart/related need to be handled separately
-        //Use the "is_mixed" sendMessage() API
-//         const auto type = pj_str((char*) "multipart");
-//         const auto subtype = pj_str((char*) "related");
-        const auto type = pj_str((char*) "text");
-        const auto subtype = pj_str((char*) "plain");
-        const auto message = pj_str((char*) text.c_str());
-
-        tdata->msg->body = pjsip_msg_body_create(tdata->pool, &type, &subtype, &message);
-        auto ret = pjsip_dlg_send_request(dialog, tdata, -1, nullptr);
-        if (ret != PJ_SUCCESS)
-            RING_WARN("SIP send message failed: %s", sip_utils::sip_strerror(ret).c_str());
-        pjsip_dlg_dec_lock(dialog);
+        status = pjsip_dlg_send_request(dialog, tdata, -1, nullptr);
+        if (status != PJ_SUCCESS) {
+            RING_ERR("pjsip_dlg_send_request failed: %s",
+                     sip_utils::sip_strerror(status).c_str());
+            throw InstantMessageException("Internal SIP error");
+        }
     }
 }
 
+/**
+ * Creates std::pair with the Content-Type header contents as the first value and the message
+ * payload as the second value.
+ *
+ * The format of the first value will be:
+ *     type/subtype[; *[; arg=value]]
+ *     eg: "text/plain;id=1234;part=2;of=1001"
+ */
+static std::pair<std::string, std::string>
+parseMessageBody(const pjsip_msg_body* body)
+{
+    const std::string type {body->content_type.type.ptr, (size_t)body->content_type.type.slen};
+    const std::string subtype {body->content_type.subtype.ptr, (size_t)body->content_type.subtype.slen};
+    std::string header = type + "/" + subtype;
+
+    // iterate over parameters
+    auto param = body->content_type.param.next;
+    while (param != &body->content_type.param) {
+        const std::string arg {param->name.ptr, (size_t)param->name.slen};
+        const std::string value {param->value.ptr, (size_t)param->value.slen};
+
+        header += ";" + arg + "=" + value;
+
+        param = param->next;
+    }
+
+    // get the payload, assume we can interpret it as chars
+    const std::string payload {static_cast<char*>(body->data), body->len};
+
+    return std::make_pair(header, payload);
+}
+
+/**
+ * Parses given SIP message into a map where the key is the contents of the Content-Type header
+ * (along with any parameters) and the value is the message payload.
+ *
+ * @param msg received SIP message
+ *
+ * @return map of content types and message payloads
+ */
+std::map<std::string, std::string>
+InstantMessaging::parseSipMessage(const pjsip_msg* msg)
+{
+    std::map<std::string, std::string> ret;
+
+    if (!msg->body) {
+        RING_WARN("message body is empty");
+        return ret;
+    }
+
+    // check if its a multipart message
+    pj_str_t typeMultipart {const_cast<char*>("multipart"), 9};
+
+    if (pj_strcmp(&typeMultipart, &msg->body->content_type.type) != 0) {
+        // treat as single content type message
+        ret.insert(parseMessageBody(msg->body));
+    } else {
+        /* multipart type message, we will treat it as multipart/mixed even if the subtype is
+         * something else, eg: related
+         */
+        auto part = pjsip_multipart_get_first_part(msg->body);
+        while (part != nullptr) {
+            ret.insert(parseMessageBody(part->body));
+            part = pjsip_multipart_get_next_part(msg->body, part);
+        }
+    }
+
+    return ret;
+}
+
 #if HAVE_IAX
 void
 InstantMessaging::sendIaxMessage(iax_session* session, const std::string& /* id */,
                                  const std::vector<std::string>& chunks)
 {
+    //TODO: implement multipart message creation for IAX via the pjsip api and then convert
+    //      into string for sending
     for (const auto& msg: chunks)
         iax_send_text(session, msg.c_str());
 }
 #endif
 
+
+/*
+ * The following functions are for creating and parsing XML URI lists which are appended to
+ * instant messages in order to be able to send a message to multiple recipients as defined in
+ * RFC 5365
+ *
+ * These functions are not currently used, but are left for now, along with the Expat library
+ * dependance, in case this functionality is implemented later. Note that it may be possible to
+ * replace the Expat XML parser library by using the pjsip xml functions.
+ */
+
+static void XMLCALL
+startElementCallback(void* userData, const char* name, const char** atts)
+{
+    if (strcmp(name, "entry"))
+        return;
+
+    InstantMessaging::UriEntry entry = InstantMessaging::UriEntry();
+
+    for (const char **att = atts; *att; att += 2)
+        entry.insert(std::pair<std::string, std::string> (*att, *(att+1)));
+
+    static_cast<InstantMessaging::UriList *>(userData)->push_back(entry);
+}
+
+static void XMLCALL
+endElementCallback(void * /*userData*/, const char* /*name*/)
+{}
+
 std::string
 InstantMessaging::generateXmlUriList(const UriList& list)
 {
@@ -115,7 +316,6 @@ InstantMessaging::generateXmlUriList(const UriList& list)
     return xmlbuffer + "</list></resource-lists>";
 }
 
-
 InstantMessaging::UriList
 InstantMessaging::parseXmlUriList(const std::string& urilist)
 {
@@ -134,184 +334,4 @@ InstantMessaging::parseXmlUriList(const std::string& urilist)
     return list;
 }
 
-///See rfc2046#section-5.1.4
-static std::string
-buildMimeMultipartPart(const std::string& type, const std::string& dispo,
-                       const std::string& content)
-{
-    return
-    "--boundary\n"
-    "Content-Type: " + type + "\n" +
-    (!dispo.empty() ? "Content-Disposition: "+ dispo + "\n" : "") +
-    "\n" +
-    content + "\n";
-}
-
-std::vector<std::string>
-InstantMessaging::appendMimePayloads(const std::map<std::string, std::string>& payloads,
-                                     const UriList& list)
-{
-    static const std::string footer = "--boundary--";
-    std::vector<std::string> ret;
-    std::string chunk;
-
-    const auto& urilist = not list.empty() ? buildMimeMultipartPart("application/resource-lists+xml",
-                                                                    "recipient-list",
-                                                                    generateXmlUriList(list)) : "";
-
-    const size_t max_message_size = MAXIMUM_MESSAGE_LENGTH - urilist.size() - footer.size();
-
-    for (const auto& pair : payloads) {
-        const auto& m = buildMimeMultipartPart(pair.first, {}, pair.second);
-        if (m.size() > max_message_size) {
-            RING_DBG("An %s payload is too large to be sent, the maximum lenght is %zu",
-                     m.c_str(), max_message_size);
-            continue;
-        }
-        if (m.size() + chunk.size() > max_message_size) {
-            RING_DBG("Some MIME payloads don't fit into the packet, splitting, max size is %zu, the payload would be %zu %zu %zu",
-                     max_message_size, m.size() + chunk.size(), m.size() , chunk.size()
-            );
-            chunk += urilist + footer;
-            ret.push_back(chunk);
-            chunk = "";
-        }
-        chunk += m;
-    }
-
-    if (chunk.size())
-        ret.push_back(chunk);
-
-    return ret;
-}
-
-std::string
-InstantMessaging::findTextUriList(const std::string& text)
-{
-    static const std::string ctype("Content-Type: application/resource-lists+xml");
-    static const std::string cdispo("Content-Disposition: recipient-list");
-    static const std::string boundary("--boundary--");
-
-    // init position pointer
-    size_t pos = 0;
-
-    // find the content type
-    if ((pos = text.find(ctype)) == std::string::npos)
-        throw InstantMessageException("Could not find Content-Type tag while parsing sip message for recipient-list");
-
-    // find the content disposition
-    if ((pos = text.find(cdispo, pos)) == std::string::npos)
-        throw InstantMessageException("Could not find Content-Disposition tag while parsing sip message for recipient-list");
-
-    // xml content start after content disposition tag
-    size_t begin = pos + cdispo.size();
-
-    //Remove arbitrary number of empty lines, otherwise XML_Parse will return XML_STATUS_ERROR
-    while (text[begin] == '\n' || text[begin] == '\r')
-        begin++;
-
-    // find final boundary
-    size_t end;
-    if ((end = text.find(boundary, begin)) == std::string::npos)
-        throw InstantMessageException("Could not find final \"boundary\" while parsing sip message for recipient-list");
-
-    return text.substr(begin, end - begin);
-}
-
-/*
- * From RFC2046:
- *
- * MIME-Version: 1.0
- * Content-Type: multipart/alternative; boundary=boundary42
- *
- * --boundary42
- * Content-Type: text/plain; charset=us-ascii
- *
- *    ... plain text version of message goes here ...
- *
- * --boundary42
- * Content-Type: text/html
- *
- *    ... RFC 1896 text/enriched version of same message
- *       goes here ...
- *
- * --boundary42
- * Content-Type: application/x-whatever
- *
- *    ... fanciest version of same message goes here ...
- *
- * --boundary42--
- */
-
-std::string
-InstantMessaging::findMimePayload(const std::string& encodedPayloads, const std::string& mime)
-{
-    const std::string ctype = "Content-Type: " + mime;
-    const size_t pos = encodedPayloads.find(ctype);
-    if (pos == std::string::npos)
-        return {};
-    const size_t begin = pos + ctype.size();
-
-    const size_t end = encodedPayloads.find("--boundary", begin);
-    if (end == std::string::npos) {
-        RING_DBG("Could not find end of text \"boundary\" while parsing sip message for text");
-        return {};
-    }
-
-    return encodedPayloads.substr(begin, end - begin);
-}
-
-std::map<std::string, std::string>
-InstantMessaging::parsePayloads(const std::string& encodedPayloads)
-{
-    //Constants
-    static const std::string boud  = "--boundary"           ;
-    static const std::string type  = "Content-Type: "       ;
-    static const std::string dispo = "Content-Disposition: ";
-    const size_t end = encodedPayloads.find("--boundary--");
-
-    size_t next_start = encodedPayloads.find(boud);
-
-    std::map< std::string, std::string > ret;
-
-    do {
-        size_t currentStart = next_start;
-
-        next_start = encodedPayloads.find(boud, currentStart+1);
-
-        //Get the mime type
-        size_t context_pos = encodedPayloads.find(type, currentStart+1);
-        if (context_pos == std::string::npos)
-            break;
-        else if (context_pos >= next_start)
-            continue;
-
-        context_pos += type.size();
-
-        size_t mimeTypeEnd = encodedPayloads.find('\n', context_pos+1);
-        if (encodedPayloads[context_pos-1] == '\r')
-            mimeTypeEnd--;
-
-        std::string mimeType = encodedPayloads.substr(context_pos, mimeTypeEnd - context_pos);
-        currentStart = mimeTypeEnd+1;
-
-        //Remove the disposition
-        const size_t dispoPos = encodedPayloads.find(dispo, currentStart);
-        if (dispoPos != std::string::npos && dispoPos < next_start) {
-            currentStart = encodedPayloads.find('\n', dispoPos);
-            while (encodedPayloads[currentStart] == '\n' || encodedPayloads[currentStart] == '\r')
-                currentStart++;
-        }
-
-        //Get the payload
-        std::string payload = encodedPayloads.substr(currentStart, next_start - currentStart);
-
-        //WARNING assume only one message per payload exist
-        ret[mimeType] = payload;
-
-    } while(next_start < end);
-
-    return ret;
-}
-
 } // namespace ring
diff --git a/src/im/instant_messaging.h b/src/im/instant_messaging.h
index 43b62d7598b0c6b648cc416e52db2d5f7a4380fb..407e24d63b3ff5487b6943c97da9eb14a033688f 100644
--- a/src/im/instant_messaging.h
+++ b/src/im/instant_messaging.h
@@ -2,6 +2,7 @@
  *  Copyright (C) 2004-2015 Savoir-faire Linux Inc.
  *  Author: Emmanuel Milou <emmanuel.milou@savoirfairelinux.com>
  *  Author: Alexandre Savard <alexandre.savard@savoirfairelinux.com>
+ *  Author: Stepan Salenikovich <stepan.salenikovich@savoirfairelinux.com>
  *
  *  This program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License as published by
@@ -44,20 +45,11 @@
 #endif
 
 struct pjsip_inv_session;
+struct pjsip_rx_data;
+struct pjsip_msg;
 
 namespace ring { namespace InstantMessaging {
 
-/* PJSIP's sip message limit, PJSIP_MAX_PKT_LEN is the total, but most
-   is used for the SIP header
-
-   Ring currently split the messages into smaller ones and send them. Obviously
-   it is invalid as some messages wont have the MIME boundary section.
-
-   The number set here is arbitrary, the theoretical limit is around 3000,
-   but some messages may fail.
-*/
-constexpr static unsigned MAXIMUM_MESSAGE_LENGTH = 1800;
-
 constexpr static const char* IM_XML_URI = "uri";
 
 struct InstantMessageException : std::runtime_error
@@ -69,8 +61,36 @@ struct InstantMessageException : std::runtime_error
 using UriEntry = std::map<std::string, std::string>;
 using UriList = std::list<UriEntry>;
 
-void sendSipMessage(pjsip_inv_session* session, const std::string& id,
-                    const std::vector<std::string>& chunks);
+/**
+ * Constructs and sends a SIP message.
+ *
+ * The expected format of the map key is:
+ *     type/subtype[; *[; arg=value]]
+ *     eg: "text/plain; id=1234;part=2;of=1001"
+ *     note: all whitespace is optional
+ *
+ * If the map contains more than one pair, then a multipart/mixed message type will be created
+ * containing multiple message parts. Note that all of the message parts must be able to fit into
+ * one message... they will not be split into multiple messages.
+ *
+ * @param session SIP session
+ * @param payloads a map where the mime type and optional parameters are the key
+ *                 and the message payload is the value
+ *
+ * Exception: throw InstantMessageException if no message sent
+ */
+void sendSipMessage(pjsip_inv_session* session, const std::map<std::string, std::string>& payloads);
+
+/**
+ * Parses given SIP message into a map where the key is the contents of the Content-Type header
+ * (along with any parameters) and the value is the message payload.
+ *
+ * @param msg received SIP message
+ *
+ * @return map of content types and message payloads
+ */
+std::map<std::string, std::string> parseSipMessage(const pjsip_msg* msg);
+
 #if HAVE_IAX
 void sendIaxMessage(iax_session* session, const std::string& id,
                     const std::vector<std::string>& chunks);
@@ -93,45 +113,6 @@ std::string generateXmlUriList(const UriList& list);
  *
  * @return An UriList of UriEntry containing parsed XML information as a map.
  */
-UriList parseXmlUriList(const std::string &urilist);
-
-/**
- * Format text message according to RFC 5365, append recipient-list to the message
- *
- * @param Key/Value MIME pairs to be sent. If a payload doesn't fit, the message will be split
- * @param list containing the recipients
- *
- * @return formated text stored into a string to be included in sip MESSAGE
- */
-std::vector<std::string> appendMimePayloads(const std::map<std::string, std::string>& payloads,
-                                            const UriList& list = {});
-
-/**
- * Retreive the xml formated uri list in formated text data according to RFC 5365
- *
- * @param text The formated text message as retreived in the SIP message
- *
- * @return A string containing the XML content
- */
-std::string findTextUriList(const std::string& text);
-
-/**
- * Retrieve a MIME payload from the SIP container RFC5365
- *
- * @param mime the mime type
- *
- * @param encodedPayloads a MIME encoded set of payloads
- *
- * @return A string containing the actual message
- */
-std::string findMimePayload(const std::string& encodedPayloads,
-                            const std::string& mime = "text/plain");
-
-/**
- * Retrieve all MIME payloads from encodedPayloads
- *
- * @param encodedPayloads a MIME encoded set of payloads
- */
-std::map< std::string, std::string > parsePayloads(const std::string& encodedPayloads);
+UriList parseXmlUriList(const std::string& urilist);
 
 }} // namespace ring::InstantMessaging
diff --git a/src/manager.cpp b/src/manager.cpp
index 7f75ae71d2aacf6b0783168a5a09252abdf885fc..f1b92c3b42c3ca6cb8f6eea6af303f5a671ae13e 100644
--- a/src/manager.cpp
+++ b/src/manager.cpp
@@ -1607,101 +1607,86 @@ Manager::incomingCall(Call &call, const std::string& accountId)
 
 //THREAD=VoIP
 #if HAVE_INSTANT_MESSAGING
+void
+Manager::sendTextMessageToConference(const Conference& conf,
+                                     const std::map<std::string, std::string>& messages,
+                                     const std::string& from) const noexcept
+{
+    ParticipantSet participants(conf.getParticipantList());
+    for (const auto& call_id: participants) {
+        try {
+            auto call = getCallFromCallID(call_id);
+            if (not call)
+                throw std::runtime_error("no associated call");
+            call->sendTextMessage(messages, from);
+        } catch (const std::exception& e) {
+            RING_ERR("Failed to send message to conference participant %s: %s",
+                     call_id.c_str(), e.what());
+        }
+    }
+}
 
 void
 Manager::incomingMessage(const std::string& callID,
-                             const std::string& from,
-                             const std::map<std::string, std::string>& messages)
+                         const std::string& from,
+                         const std::map<std::string, std::string>& messages)
 {
     if (isConferenceParticipant(callID)) {
         auto conf = getConferenceFromCallID(callID);
-
-        ParticipantSet participants(conf->getParticipantList());
-
-        for (const auto &item_p : participants) {
-
-            if (item_p == callID)
-                continue;
-
-            RING_DBG("Send message to %s", item_p.c_str());
-
-            if (auto call = getCallFromCallID(item_p)) {
-                call->sendTextMessage(messages, from);
-            } else {
-                RING_ERR("Failed to get call while sending instant message");
-                return;
-            }
+        if (not conf) {
+            RING_ERR("no conference associated to ID %s", callID.c_str());
+            return;
         }
 
+        RING_DBG("Is a conference, send incoming message to everyone");
+        sendTextMessageToConference(*conf, messages, from);
+
         // in case of a conference we must notify client using conference id
         emitSignal<DRing::CallSignal::IncomingMessage>(conf->getConfID(), from, messages);
     } else
         emitSignal<DRing::CallSignal::IncomingMessage>(callID, from, messages);
 }
 
-//THREAD=VoIP
-bool
+void
 Manager::sendCallTextMessage(const std::string& callID,
                              const std::map<std::string, std::string>& messages,
                              const std::string& from,
                              bool /*isMixed TODO: use it */)
 {
     if (isConference(callID)) {
-        RING_DBG("Is a conference, send instant message to everyone");
-        ConferenceMap::iterator it = conferenceMap_.find(callID);
-
-        if (it == conferenceMap_.end())
-            return false;
-
-        auto conf = it->second;
-
-        if (!conf)
-            return false;
-
-        ParticipantSet participants(conf->getParticipantList());
-
-        for (const auto &participant_id : participants) {
-
-            if (auto call = getCallFromCallID(participant_id)) {
-                call->sendTextMessage(messages, from);
-            } else {
-                RING_ERR("Failed to get call while sending instant message");
-                return false;
-            }
+        const auto& it = conferenceMap_.find(callID);
+        if (it == conferenceMap_.cend() or not it->second) {
+            RING_ERR("no conference associated to ID %s", callID.c_str());
+            return;
         }
 
-        return true;
-    }
+        RING_DBG("Is a conference, send instant message to everyone");
+        sendTextMessageToConference(*it->second, messages, from);
 
-    if (isConferenceParticipant(callID)) {
-        RING_DBG("Call is participant in a conference, send instant message to everyone");
+    } else if (isConferenceParticipant(callID)) {
         auto conf = getConferenceFromCallID(callID);
+        if (not conf) {
+            RING_ERR("no conference associated to call ID %s", callID.c_str());
+            return;
+        }
 
-        if (!conf)
-            return false;
-
-        ParticipantSet participants(conf->getParticipantList());
-
-        for (const auto &participant_id : participants) {
+        RING_DBG("Call is participant in a conference, send instant message to everyone");
+        sendTextMessageToConference(*conf, messages, from);
 
-            if (auto call = getCallFromCallID(participant_id)) {
-                call->sendTextMessage(messages, from);
-            } else {
-                RING_ERR("Failed to get call while sending instant message");
-                return false;
-            }
-        }
     } else {
-        if (auto call = getCallFromCallID(callID)) {
+        auto call = getCallFromCallID(callID);
+        if (not call) {
+            RING_ERR("Failed to send message to %s: inexistant call ID", call->getCallId().c_str());
+            return;
+        }
+
+        try {
             call->sendTextMessage(messages, from);
-        } else {
-            RING_ERR("Failed to get call while sending instant message");
-            return false;
+        } catch (const InstantMessaging::InstantMessageException& e) {
+            RING_ERR("Failed to send message to call %s: %s", call->getCallId().c_str(), e.what());
         }
     }
-    return true;
 }
-
 #endif // HAVE_INSTANT_MESSAGING
 
 //THREAD=VoIP CALL=Outgoing
diff --git a/src/manager.h b/src/manager.h
index 355aaca4a4805bf6f14a9b26b9b72e02b4317b3c..1cf2257c35a5f8407f3fe99d5b2245f07b19c1f3 100644
--- a/src/manager.h
+++ b/src/manager.h
@@ -399,9 +399,11 @@ class Manager {
          * Send a new text message to the call, if participate to a conference, send to all participant.
          * @param callID        The call to send the message
          * @param message       A list of pair of mime types and payloads
-        * @param from           The sender of this message (could be another participant of a conference)
+         * @param from           The sender of this message (could be another participant of a conference)
          */
-        bool sendCallTextMessage(const std::string& callID, const std::map<std::string, std::string>& messages, const std::string& from, bool isMixed);
+        void sendCallTextMessage(const std::string& callID,
+                                 const std::map<std::string, std::string>& messages,
+                                 const std::string& from, bool isMixed);
 #endif // HAVE_INSTANT_MESSAGING
 
         /**
@@ -1008,6 +1010,12 @@ class Manager {
 
         /* Sink ID mapping */
         std::map<std::string, std::weak_ptr<video::SinkClient>> sinkMap_;
+
+#if HAVE_INSTANT_MESSAGING
+        void sendTextMessageToConference(const Conference& conf,
+                                         const std::map<std::string, std::string>& messages,
+                                         const std::string& from) const noexcept;
+#endif
 };
 
 // Helper to install a callback to be called once by the main event loop
diff --git a/src/sip/sip_utils.cpp b/src/sip/sip_utils.cpp
index c37674050f050665be547dc5b77fe799b8e8a2b3..0710501f4baa5399a4bcf7ba4add7d386e87157f 100644
--- a/src/sip/sip_utils.cpp
+++ b/src/sip/sip_utils.cpp
@@ -190,4 +190,15 @@ sip_strerror(pj_status_t code)
     return std::string{err_msg};
 }
 
+PJDialogLock::PJDialogLock(pjsip_dialog* dialog)
+    : dialog_(dialog)
+{
+    pjsip_dlg_inc_lock(dialog_);
+}
+
+PJDialogLock::~PJDialogLock()
+{
+    pjsip_dlg_dec_lock(dialog_);
+}
+
 }} // namespace ring::sip_utils
diff --git a/src/sip/sip_utils.h b/src/sip/sip_utils.h
index 7f2aa28838c9dabdd64551d7b931b4aca4bbe8f5..1364f76ba1f11d7eaf15dc857bd5bbca94504f4f 100644
--- a/src/sip/sip_utils.h
+++ b/src/sip/sip_utils.h
@@ -45,6 +45,7 @@
 #include <cstring> // strcmp
 
 struct pjsip_msg;
+struct pjsip_dialog;
 
 namespace ring { namespace sip_utils {
 
@@ -91,6 +92,20 @@ constexpr const pj_str_t CONST_PJ_STR(T (&a)[N]) noexcept {
     return {const_cast<char*>(a), N-1};
 }
 
+// PJSIP dialog locking in RAII way
+// Usage: declare local variable like this: sip_utils::PJDialogLock lock {dialog};
+// The lock is kept until the local variable is deleted
+class PJDialogLock {
+public:
+    explicit PJDialogLock(pjsip_dialog* dialog);
+    ~PJDialogLock();
+    PJDialogLock() = delete;
+    PJDialogLock(const PJDialogLock&) = delete; // enough to disable all cp/mv stuff
+
+private:
+    pjsip_dialog* dialog_;
+};
+
 }} // namespace ring::sip_utils
 
 #endif // SIP_UTILS_H_
diff --git a/src/sip/sipcall.cpp b/src/sip/sipcall.cpp
index b6ee5644690b97324b159d38132daa0d67ae622d..83c4021f00db113e560880507fd1507f43c0edd0 100644
--- a/src/sip/sipcall.cpp
+++ b/src/sip/sipcall.cpp
@@ -682,18 +682,16 @@ SIPCall::carryingDTMFdigits(char code)
 #if HAVE_INSTANT_MESSAGING
 void
 SIPCall::sendTextMessage(const std::map<std::string, std::string>& messages,
-                         const std::string& from)
+                         const std::string& /* from */)
 {
     if (not inv)
         throw VoipLinkException("No invite session for this call");
 
-    /* Send IM message */
-    InstantMessaging::UriList list;
-    InstantMessaging::UriEntry entry;
-    entry[InstantMessaging::IM_XML_URI] = std::string("\"" + from + "\"");  // add double quotes for xml formating
-    list.push_front(entry);
-    const auto& msgs = InstantMessaging::appendMimePayloads(messages, list);
-    InstantMessaging::sendSipMessage(inv.get(), getCallId(), msgs);
+    //TODO: for now we ignore the "from" (the previous implementation for sending this info was
+    //      buggy and verbose), another way to send the original message sender will be implemented
+    //      in the future
+
+    InstantMessaging::sendSipMessage(inv.get(), messages);
 }
 #endif // HAVE_INSTANT_MESSAGING
 
diff --git a/src/sip/sipcall.h b/src/sip/sipcall.h
index fa805a2af4a9be788f72c0e3c496026675979dca..82656196322e27945a17b7a1181deb1d0dbb5fd5 100644
--- a/src/sip/sipcall.h
+++ b/src/sip/sipcall.h
@@ -168,7 +168,7 @@ class SIPCall : public Call
 
 #if HAVE_INSTANT_MESSAGING
         void sendTextMessage(const std::map<std::string, std::string>& messages,
-                             const std::string &from) override;
+                             const std::string& from) override;
 #endif
 
         SIPAccountBase& getSIPAccount() const;
diff --git a/src/sip/sipvoiplink.cpp b/src/sip/sipvoiplink.cpp
index 81579ef9afcf8072c98f2e24498bb2de0be83f55..c19dc2695b2a8e6c250bc9e40550134e84002e7d 100644
--- a/src/sip/sipvoiplink.cpp
+++ b/src/sip/sipvoiplink.cpp
@@ -1117,48 +1117,19 @@ onRequestNotify(pjsip_inv_session* /*inv*/, pjsip_rx_data* /*rdata*/, pjsip_msg*
 }
 
 static void
-onRequestMessage(pjsip_inv_session* inv, pjsip_rx_data* rdata, pjsip_msg* msg, SIPCall& call)
+onRequestMessage(pjsip_inv_session* /*inv*/, pjsip_rx_data* /*rdata*/, pjsip_msg* msg,
+                 SIPCall& call)
 {
 #if HAVE_INSTANT_MESSAGING
     if (!msg->body)
         return;
 
-    const auto formattedMsgPtr = static_cast<const char*>(msg->body->data);
-    if (!formattedMsgPtr)
-        return;
-
-    const std::string formattedMessage {formattedMsgPtr};
-
-    // retreive the recipient-list of this message
-    InstantMessaging::UriList list;
-    try {
-        const auto& urilist = InstantMessaging::findTextUriList(formattedMessage);
-        auto list = InstantMessaging::parseXmlUriList(urilist);
-    } catch (const InstantMessaging::InstantMessageException& e) {
-        RING_DBG("[call:%s] Empty urilist", call.getCallId().c_str());
-    }
-
-    // If no item present in the list, peer is considered as the sender
-    std::string from;
-    if (list.empty()) {
-        from = call.getPeerNumber();
-    } else {
-        from = list.front()[InstantMessaging::IM_XML_URI];
-        if (from == "Me")
-            from = call.getPeerNumber();
-    }
-
-    // strip < and > characters in case of an IP address
-    if (from[0] == '<' and from[from.size() - 1] == '>')
-        from = from.substr(1, from.size() - 2);
+    //TODO: for now we assume that the "from" is the message sender, this may not be true in the
+    //      case of conferences; a content type containing this info will be added to the messages
+    //      in the future
+    Manager::instance().incomingMessage(call.getCallId(), call.getPeerNumber(),
+                                        InstantMessaging::parseSipMessage(msg));
 
-    try {
-        const auto& messages = InstantMessaging::parsePayloads(formattedMessage);
-        Manager::instance().incomingMessage(call.getCallId(), from, messages);
-        replyToRequest(inv, rdata, PJSIP_SC_OK);
-    } catch (const InstantMessaging::InstantMessageException& except) {
-        RING_ERR("Exception during SIP message parsing: %s", except.what());
-    }
 #endif // HAVE_INSTANT_MESSAGING
 }