Commit a713ae92 authored by Emmanuel Milou's avatar Emmanuel Milou
Browse files

[#3619] Split messages that exceed a limit size + Unit tests

parent b00dd446
......@@ -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;
}
}
......@@ -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
*/
......
......@@ -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 ( "",
......
......@@ -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;
}
......@@ -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;
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment