diff --git a/sflphone-common/src/sip/im/InstantMessaging.cpp b/sflphone-common/src/sip/im/InstantMessaging.cpp index ceb868745b280a408cc578fd788e62c41f3d386d..badbc09738158166beaea17f9c23c9f16a5c8cde 100644 --- a/sflphone-common/src/sip/im/InstantMessaging.cpp +++ b/sflphone-common/src/sip/im/InstantMessaging.cpp @@ -3,127 +3,174 @@ namespace sfl { -InstantMessaging::InstantMessaging() - : imFiles () {} + InstantMessaging::InstantMessaging() + : imFiles () {} -InstantMessaging::~InstantMessaging() {} + InstantMessaging::~InstantMessaging() {} -bool InstantMessaging::init () -{ - return true; -} + bool InstantMessaging::init () + { + return true; + } -int InstantMessaging::openArchive (CallID& id) -{ + int InstantMessaging::openArchive (CallID& id) + { - // Create a new file stream - std::ofstream File (id.c_str (), std::ios::out | std::ios::app); - imFiles[id] = &File; + // Create a new file stream + std::ofstream File (id.c_str (), std::ios::out | std::ios::app); + imFiles[id] = &File; - // Attach it to the call ID - return (int) imFiles.size (); -} + // Attach it to the call ID + return (int) imFiles.size (); + } -int InstantMessaging::closeArchive (CallID& id) -{ + int InstantMessaging::closeArchive (CallID& id) + { - // Erase it from the map - imFiles.erase (id); - return (int) imFiles.size (); -} + // Erase it from the map + imFiles.erase (id); + return (int) imFiles.size (); + } -bool InstantMessaging::saveMessage (const std::string& message, const std::string& author, CallID& id, int mode) -{ + bool InstantMessaging::saveMessage (const std::string& message, const std::string& author, CallID& id, int mode) + { - // We need here to write the text message in the right file. - // We will use the Call ID + // We need here to write the text message in the right file. + // We will use the Call ID - std::ofstream File; - std::string filename = "sip:"; + std::ofstream File; + std::string filename = "sip:"; - filename.append (id); - File.open (filename.c_str (), (std::_Ios_Openmode) mode); + filename.append (id); + File.open (filename.c_str (), (std::_Ios_Openmode) mode); - if (!File.good () || !File.is_open ()) - return false; + if (!File.good () || !File.is_open ()) + return false; - File << "[" << author << "] " << message << '\n'; - File.close (); + File << "[" << author << "] " << message << '\n'; + File.close (); - return true; -} + return true; + } -std::string InstantMessaging::receive (const std::string& message, const std::string& author, CallID& id) -{ + std::string InstantMessaging::receive (const std::string& message, const std::string& author, CallID& id) + { - // We just receive a TEXT message. Before sent it to the recipient, we must assure that the message is complete. - // We should use a queue to push these messages in + // We just receive a TEXT message. Before sent it to the recipient, we must assure that the message is complete. + // We should use a queue to push these messages in - _debug ("New message : %s", message.c_str ()); + _debug ("New message : %s", message.c_str ()); - // TODO Security check - // TODO String cleaning + // TODO Security check + // TODO String cleaning - // Archive the message - this->saveMessage (message, author, id); + // Archive the message + this->saveMessage (message, author, id); - return message; + return message; -} + } -pj_status_t InstantMessaging::notify (CallID& id) -{ + pj_status_t InstantMessaging::notify (CallID& id) + { + // Notify the clients through a D-Bus signal + return PJ_SUCCESS; + } - // Notify the clients through a D-Bus signal - return PJ_SUCCESS; + pj_status_t InstantMessaging::send (pjsip_inv_session *session, CallID& id, const std::string& text) + { -} + pjsip_method msg_method; + const pj_str_t type = STR_TEXT; + const pj_str_t subtype = STR_PLAIN; + pjsip_tx_data *tdata; + pj_status_t status; + pjsip_dialog* dialog; + pj_str_t message; -pj_status_t InstantMessaging::send (pjsip_inv_session *session, CallID& id, const std::string& text) -{ + msg_method.id = PJSIP_OTHER_METHOD; + msg_method.name = METHOD_NAME ; - pjsip_method msg_method; - const pj_str_t type = STR_TEXT; - const pj_str_t subtype = STR_PLAIN; - pjsip_tx_data *tdata; - pj_status_t status; - pjsip_dialog* dialog; - pj_str_t message; + // Get the dialog associated to the call + dialog = session->dlg; + // Convert the text into a format readable by pjsip + message = pj_str ( (char*) text.c_str ()); - msg_method.id = PJSIP_OTHER_METHOD; - msg_method.name = METHOD_NAME ; + // Must lock dialog + pjsip_dlg_inc_lock (dialog); - // Get the dialog associated to the call - dialog = session->dlg; - // Convert the text into a format readable by pjsip - message = pj_str ( (char*) text.c_str ()); + // Create the message request + status = pjsip_dlg_create_request (dialog, &msg_method, -1, &tdata); + PJ_ASSERT_RETURN (status == PJ_SUCCESS, 1); - // Must lock dialog - pjsip_dlg_inc_lock (dialog); + // Attach "text/plain" body + tdata->msg->body = pjsip_msg_body_create (tdata->pool, &type, &subtype, &message); - // Create the message request - status = pjsip_dlg_create_request (dialog, &msg_method, -1, &tdata); - PJ_ASSERT_RETURN (status == PJ_SUCCESS, 1); + // Send the request + status = pjsip_dlg_send_request (dialog, tdata, -1, NULL); + PJ_ASSERT_RETURN (status == PJ_SUCCESS, 1); - // Attach "text/plain" body - tdata->msg->body = pjsip_msg_body_create (tdata->pool, &type, &subtype, &message); + // Done + pjsip_dlg_dec_lock (dialog); - // Send the request - status = pjsip_dlg_send_request (dialog, tdata, -1, NULL); - PJ_ASSERT_RETURN (status == PJ_SUCCESS, 1); + // Archive the message + this->saveMessage (text, "Me", id); - // Done - pjsip_dlg_dec_lock (dialog); + printf ("SIPVoIPLink::sendTextMessage %s %s\n", id.c_str(), text.c_str()); + return PJ_SUCCESS; + } - // Archive the message - this->saveMessage (text, "Me", id); + pj_status_t InstantMessaging::send_message (pjsip_inv_session *session, CallID& id, const std::string& message) + { - printf ("SIPVoIPLink::sendTextMessage %s %s\n", id.c_str(), text.c_str()); + /* Check the length of the message */ + if (message.length() < MAXIMUM_MESSAGE_LENGTH) { + /* No problem here */ + send (session, id, message); + } - return PJ_SUCCESS; -} + else { + /* It exceeds the size limit of a SIP MESSAGE (1300 bytes), o plit it and send multiple messages */ + std::vector<std::string> multiple_messages = split_message (message); + /* Send multiple messages */ + int size = multiple_messages.size(); + int i = 0; + for (i=0; i<size; i++) { + send (session, id, multiple_messages[i]); + } + } + return PJ_SUCCESS; + } + + + std::vector<std::string> InstantMessaging::split_message (const std::string& text) + { + + std::vector<std::string> messages; + std::string text_to_split = text; + + /* Iterate over the message length */ + while (text_to_split.length() > MAXIMUM_MESSAGE_LENGTH) + { + /* The remaining string is still too long */ + + /* Compute the substring */ + std::string split_message = text_to_split.substr (0, (size_t)MAXIMUM_MESSAGE_LENGTH); + /* Append our split character \n\n */ + split_message.append (DELIMITER_CHAR); + /* Append in the vector */ + messages.push_back (split_message); + /* Use the remaining string to not loop forever */ + text_to_split = text_to_split.substr ((size_t) MAXIMUM_MESSAGE_LENGTH); + } + /* Push the last message */ + /* If the message length does not exceed the maximum size of a SIP MESSAGE, we go directly here */ + messages.push_back (text_to_split); + + return messages; + } } diff --git a/sflphone-common/src/sip/im/InstantMessaging.h b/sflphone-common/src/sip/im/InstantMessaging.h index 238ce1687fd375b4d2631b876293b9e717d6b93c..70a9b271be2b68f9e5a62fad7fa3e5930a647dc7 100644 --- a/sflphone-common/src/sip/im/InstantMessaging.h +++ b/sflphone-common/src/sip/im/InstantMessaging.h @@ -16,6 +16,8 @@ #define STR_TEXT pj_str((char*)"text") #define STR_PLAIN pj_str((char*)"plain") #define METHOD_NAME pj_str((char*)"MESSAGE") +#define MAXIMUM_MESSAGE_LENGTH 10 /* ~1300/8 */ +#define DELIMITER_CHAR "\n\n" #define MODE_APPEND std::ios::out || std::ios::app #define MODE_TEST std::ios::out @@ -85,6 +87,11 @@ namespace sfl { */ pj_status_t send (pjsip_inv_session*, CallID& id, const std::string&); + pj_status_t send_message (pjsip_inv_session*, CallID& id, const std::string&); + + std::vector<std::string> split_message (const std::string&); + + /** * Notify the clients, through D-Bus, that a new message has arrived * @@ -92,6 +99,7 @@ namespace sfl { */ pj_status_t notify (CallID& id); + /* * Add a pair file stream / call ID to the private std::map */ diff --git a/sflphone-common/src/sip/sipvoiplink.cpp b/sflphone-common/src/sip/sipvoiplink.cpp index aed03315e310dced47c5d69998ad7ac8bd30e10f..b5985d51305ecd58717dafc66fdb92b3360e74ae 100644 --- a/sflphone-common/src/sip/sipvoiplink.cpp +++ b/sflphone-common/src/sip/sipvoiplink.cpp @@ -1073,7 +1073,7 @@ SIPVoIPLink::sendTextMessage (const std::string& callID, const std::string& mess if (call) { /* Send IM message */ - status = imModule->send (call->getInvSession (), (CallID&) callID, message); + status = imModule->send_message (call->getInvSession (), (CallID&) callID, message); } else { /* Notify the client of an error */ /*Manager::instance ().incomingMessage ( "", diff --git a/sflphone-common/test/instantmessagingtest.cpp b/sflphone-common/test/instantmessagingtest.cpp index e86bbe9eb993af0e402891ee8a4617470b448054..028fdffcf2510b985c13dba9c77217af73911d8e 100644 --- a/sflphone-common/test/instantmessagingtest.cpp +++ b/sflphone-common/test/instantmessagingtest.cpp @@ -34,65 +34,104 @@ #include "instantmessagingtest.h" +#define MAXIMUM_SIZE 10 +#define DELIMITER_CHAR "\n\n" + using std::cout; using std::endl; void InstantMessagingTest::setUp() { - _im = new sfl::InstantMessaging (); - _im->init (); + _im = new sfl::InstantMessaging (); + _im->init (); } void InstantMessagingTest::testSaveSingleMessage () { - _debug ("-------------------- InstantMessagingTest::testSaveSingleMessage --------------------\n"); + _debug ("-------------------- InstantMessagingTest::testSaveSingleMessage --------------------\n"); - std::string input, tmp; - std::string callID = "testfile1.txt"; + std::string input, tmp; + std::string callID = "testfile1.txt"; - // Open a file stream and try to write in it - CPPUNIT_ASSERT (_im->saveMessage ("Bonjour, c'est un test d'archivage de message", "Manu", callID, std::ios::out) == true); + // Open a file stream and try to write in it + CPPUNIT_ASSERT (_im->saveMessage ("Bonjour, c'est un test d'archivage de message", "Manu", callID, std::ios::out) == true); - // Read it to check it has been successfully written - std::ifstream testfile (callID.c_str (), std::ios::in); - CPPUNIT_ASSERT (testfile.is_open () == true); + // Read it to check it has been successfully written + std::ifstream testfile (callID.c_str (), std::ios::in); + CPPUNIT_ASSERT (testfile.is_open () == true); - while (!testfile.eof ()) { - std::getline (testfile, tmp); - input.append (tmp); - } + while (!testfile.eof ()) { + std::getline (testfile, tmp); + input.append (tmp); + } - testfile.close (); - CPPUNIT_ASSERT (input == "[Manu] Bonjour, c'est un test d'archivage de message"); + testfile.close (); + CPPUNIT_ASSERT (input == "[Manu] Bonjour, c'est un test d'archivage de message"); } void InstantMessagingTest::testSaveMultipleMessage () { - _debug ("-------------------- InstantMessagingTest::testSaveMultipleMessage --------------------\n"); + _debug ("-------------------- InstantMessagingTest::testSaveMultipleMessage --------------------\n"); + + std::string input, tmp; + std::string callID = "testfile2.txt"; - std::string input, tmp; - std::string callID = "testfile2.txt"; + // Open a file stream and try to write in it + CPPUNIT_ASSERT (_im->saveMessage ("Bonjour, c'est un test d'archivage de message", "Manu", callID, std::ios::out) == true); + CPPUNIT_ASSERT (_im->saveMessage ("Cool", "Alex", callID, std::ios::out || std::ios::app) == true); - // Open a file stream and try to write in it - CPPUNIT_ASSERT (_im->saveMessage ("Bonjour, c'est un test d'archivage de message", "Manu", callID, std::ios::out) == true); - CPPUNIT_ASSERT (_im->saveMessage ("Cool", "Alex", callID, std::ios::out || std::ios::app) == true); + // Read it to check it has been successfully written + std::ifstream testfile (callID.c_str (), std::ios::in); + CPPUNIT_ASSERT (testfile.is_open () == true); - // Read it to check it has been successfully written - std::ifstream testfile (callID.c_str (), std::ios::in); - CPPUNIT_ASSERT (testfile.is_open () == true); + while (!testfile.eof ()) { + std::getline (testfile, tmp); + input.append (tmp); + } - while (!testfile.eof ()) { - std::getline (testfile, tmp); - input.append (tmp); - } + testfile.close (); + printf ("%s\n", input.c_str()); + CPPUNIT_ASSERT (input == "[Manu] Bonjour, c'est un test d'archivage de message[Alex] Cool"); +} + +void InstantMessagingTest::testSplitMessage () +{ - testfile.close (); - printf ("%s\n", input.c_str()); - CPPUNIT_ASSERT (input == "[Manu] Bonjour, c'est un test d'archivage de message[Alex] Cool"); + /* A message that does not need to be split */ + std::string short_message = "Salut"; + std::vector<std::string> messages = _im->split_message (short_message); + CPPUNIT_ASSERT (messages.size() == short_message.length()/MAXIMUM_SIZE + 1); + CPPUNIT_ASSERT (messages[0] == short_message); + + /* A message that needs to be split into two messages */ + std::string long_message = "A message too long"; + messages = _im->split_message (long_message); + int size = messages.size (); + int i = 0; + CPPUNIT_ASSERT (size == (int)long_message.length()/MAXIMUM_SIZE + 1); + /* If only one element, do not enter the loop */ + for (i = 0; i < size - 1; i++) { + CPPUNIT_ASSERT (messages[i] == long_message.substr ((MAXIMUM_SIZE * i), MAXIMUM_SIZE)+ DELIMITER_CHAR); + } + + /* Works for the last element, or for the only element */ + CPPUNIT_ASSERT (messages[size- 1] == long_message.substr (MAXIMUM_SIZE * (size-1))); + + /* A message that needs to be split into four messages */ + std::string very_long_message = "A message that needs to be split into many messages"; + messages = _im->split_message (very_long_message); + size = messages.size (); + /* If only one element, do not enter the loop */ + for (i = 0; i < size - 1; i++) { + CPPUNIT_ASSERT (messages[i] ==very_long_message.substr ((MAXIMUM_SIZE * i), MAXIMUM_SIZE)+ DELIMITER_CHAR); + } + + /* Works for the last element, or for the only element */ + CPPUNIT_ASSERT (messages[size- 1] == very_long_message.substr (MAXIMUM_SIZE * (size-1))); } void InstantMessagingTest::tearDown() { - delete _im; - _im = 0; + delete _im; + _im = 0; } diff --git a/sflphone-common/test/instantmessagingtest.h b/sflphone-common/test/instantmessagingtest.h index dc97ba727c403a8f2fa4a083a6f6ef928557f788..e349096b1189459b9ac28af3c2c86457779e047e 100644 --- a/sflphone-common/test/instantmessagingtest.h +++ b/sflphone-common/test/instantmessagingtest.h @@ -55,6 +55,7 @@ class InstantMessagingTest : public CppUnit::TestCase { CPPUNIT_TEST_SUITE( InstantMessagingTest ); CPPUNIT_TEST (testSaveSingleMessage); CPPUNIT_TEST (testSaveMultipleMessage); + CPPUNIT_TEST (testSplitMessage); CPPUNIT_TEST_SUITE_END(); public: @@ -75,6 +76,8 @@ class InstantMessagingTest : public CppUnit::TestCase { void testSaveSingleMessage (); void testSaveMultipleMessage (); + + void testSplitMessage (); private: sfl::InstantMessaging *_im;