diff --git a/sflphone-common/src/history/historyitem.cpp b/sflphone-common/src/history/historyitem.cpp index c21498321e22ff6cb504cb4d641b15c5f0b3a4bf..9b025ea7bb78b2d2443f010c4b244f19d690c884 100644 --- a/sflphone-common/src/history/historyitem.cpp +++ b/sflphone-common/src/history/historyitem.cpp @@ -20,12 +20,55 @@ #include <historyitem.h> #include <sstream> +#include "stdlib.h" -HistoryItem::HistoryItem (int timestamp, CallType call_type, std::string to, std::string from, std::string caller_id, std::string account_id) +#define ITEM_SEPARATOR "|" + +HistoryItem::HistoryItem (std::string timestamp, CallType call_type, std::string to, std::string from, std::string caller_id, std::string account_id) : _timestamp (timestamp), _call_type (call_type), _to (to), _from (from), _caller_id (caller_id), _account_id (account_id) { } + +HistoryItem::HistoryItem (std::string timestamp, std::string serialized_form) + : _timestamp (timestamp) +{ + size_t pos; + std::string tmp, id, to, from, callerid; + int indice=0; + + while (serialized_form.find(ITEM_SEPARATOR, 0) != std::string::npos) + { + pos = serialized_form.find (ITEM_SEPARATOR, 0); + tmp = serialized_form.substr (0, pos); + serialized_form.erase (0, pos + 1); + switch (indice) + { + case 0: // The call type + id = tmp; + break; + case 1: // The to field + to = tmp; + break; + case 2: // The from field + from = tmp; + break; + case 3: // The calller id information + callerid = tmp; + break; + default: // error + std::cout <<"[ERROR] unserialized form not recognized."<<std::endl; + break; + } + indice ++; + } + + _call_type = (CallType)atoi (id.c_str()); + _to = to; + _from = from; + _caller_id = serialized_form; +} + HistoryItem::~HistoryItem () { // TODO @@ -33,24 +76,32 @@ HistoryItem::~HistoryItem () bool HistoryItem::save (Conf::ConfigTree **history){ - std::stringstream section, call_type, timestamp; + std::string section, timestamp; + std::stringstream call_type; bool res; // The section is : "[" + timestamp = "]" - section << _timestamp ; + section = get_timestamp (); + timestamp = get_timestamp (); call_type << _call_type; - timestamp << _timestamp; - - res = ( (*history)->setConfigTreeItem(section.str(), "type", call_type.str()) - && (*history)->setConfigTreeItem(section.str(), "timestamp", timestamp.str()) - && (*history)->setConfigTreeItem(section.str(), "to", _to) - && (*history)->setConfigTreeItem(section.str(), "from", _from) - && (*history)->setConfigTreeItem(section.str(), "id", _caller_id) ); + + res = ( (*history)->setConfigTreeItem(section, "type", call_type.str()) + && (*history)->setConfigTreeItem(section, "timestamp", timestamp) + && (*history)->setConfigTreeItem(section, "to", _to) + && (*history)->setConfigTreeItem(section, "from", _from) + && (*history)->setConfigTreeItem(section, "id", _caller_id) ); return res; } +std::string HistoryItem::serialize (void) +{ + std::stringstream res; + std::string separator = ITEM_SEPARATOR; + res << _call_type << separator << _to << separator << _from << separator << _caller_id; + return res.str(); +} diff --git a/sflphone-common/src/history/historyitem.h b/sflphone-common/src/history/historyitem.h index ff3286731ffe3dbbdc3891e27d1ff3bf8ffff987..007cf13b4ae331a2e70cbe02c6f77afd5d2507d2 100644 --- a/sflphone-common/src/history/historyitem.h +++ b/sflphone-common/src/history/historyitem.h @@ -23,6 +23,7 @@ #include <string> #include <config/config.h> +#include <iostream> typedef enum CallType { CALL_MISSED, @@ -37,25 +38,32 @@ class HistoryItem { /* * Constructor */ - HistoryItem (int, CallType, std::string, std::string, std::string, std::string=""); + HistoryItem (std::string, CallType, std::string, std::string, std::string, std::string=""); + + /* + * Constructor from a serialized form + */ + HistoryItem (std::string, std::string=""); /* * Destructor */ ~HistoryItem (); - inline int get_timestamp () { + inline std::string get_timestamp () { return _timestamp; } bool save (Conf::ConfigTree **history); + std::string serialize (void); + private: /* * Timestamp representing the date of the call */ - int _timestamp; + std::string _timestamp; /* * Represents the type of call diff --git a/sflphone-common/src/history/historymanager.cpp b/sflphone-common/src/history/historymanager.cpp index e237aab67a1c2d2c846f1d6b44edd0887d8f7eb3..fd93ce5cc56f3bd284fcb0dc3eb62e4df9d9262c 100644 --- a/sflphone-common/src/history/historymanager.cpp +++ b/sflphone-common/src/history/historymanager.cpp @@ -32,13 +32,21 @@ HistoryManager::~HistoryManager () { _history_items.clear (); } -bool HistoryManager::init (void) +int HistoryManager::load_history (std::string path) { Conf::ConfigTree history_list; - create_history_path (); + create_history_path (path); load_history_from_file (&history_list); - load_history_items_map (&history_list); + return load_history_items_map (&history_list); +} + +bool HistoryManager::save_history (void) +{ + Conf::ConfigTree history_list; + + save_history_items_map (&history_list); + return save_history_to_file (&history_list); } bool HistoryManager::load_history_from_file (Conf::ConfigTree *history_list) @@ -58,8 +66,7 @@ int HistoryManager::load_history_items_map (Conf::ConfigTree *history_list) Conf::TokenList sections; HistoryItem *item; Conf::TokenList::iterator iter; - std::string to, from, caller_id, accountID; - int timestamp; + std::string to, from, caller_id, accountID, timestamp; CallType type; sections = history_list->getSections(); @@ -68,7 +75,7 @@ int HistoryManager::load_history_items_map (Conf::ConfigTree *history_list) while(iter != sections.end()) { type = (CallType) getConfigInt (*iter, "type", history_list); - timestamp = getConfigInt (*iter, "timestamp", history_list); + timestamp = getConfigString (*iter, "timestamp", history_list); to = getConfigString (*iter, "to", history_list); from = getConfigString (*iter, "from", history_list); caller_id = getConfigString (*iter, "id", history_list); @@ -86,7 +93,7 @@ int HistoryManager::load_history_items_map (Conf::ConfigTree *history_list) bool HistoryManager::save_history_to_file (Conf::ConfigTree *history_list) { - return history_list->saveConfigTree(_history_path.data()); + return history_list->saveConfigTree (_history_path.data()); } @@ -119,22 +126,29 @@ void HistoryManager::add_new_history_entry (HistoryItem *new_item) _history_items [new_item->get_timestamp ()] = new_item; } -int HistoryManager::create_history_path (void) { +int HistoryManager::create_history_path (std::string path) { - std::string path; + std::string filename; - path = std::string(HOMEDIR) + DIR_SEPARATOR_STR + "." + PROGDIR; - - if (mkdir (path.data(), 0755) != 0) { - // If directory creation failed - if (errno != EEXIST) { - _debug("Cannot create directory: %s\n", strerror(errno)); - return -1; + if (path == "") + { + filename = std::string(HOMEDIR) + DIR_SEPARATOR_STR + "." + PROGDIR; + + if (mkdir (filename.data(), 0755) != 0) { + // If directory creation failed + if (errno != EEXIST) { + _debug("Cannot create directory: %s\n", strerror(errno)); + return -1; + } } + + // Load user's history + _history_path = filename + DIR_SEPARATOR_STR + "history"; } + else + set_history_path (path); + - // Load user's history - _history_path = path + DIR_SEPARATOR_STR + "history"; return 0; } @@ -161,3 +175,49 @@ HistoryManager::getConfigString(const std::string& section, const std::string& n return ""; } + +std::map <std::string, std::string> HistoryManager::get_history_serialized (void) +{ + std::map <std::string, std::string> serialized; + HistoryItemMap::iterator iter; + HistoryItem *current; + std::string res, key; + + iter = _history_items.begin (); + while (iter != _history_items.end()) + { + current = iter->second; + if (current) + { + key = current->get_timestamp (); + res = current->serialize (); + serialized [key] = res; + } + iter ++; + } + + return serialized; +} + + +int HistoryManager::set_serialized_history (std::map <std::string, std::string> history) +{ + std::map <std::string, std::string>::iterator iter; + HistoryItem *new_item; + int items_added; + + // Clear the existing history + _history_items.clear (); + + iter = history.begin (); + while (iter != history.end ()) + { + new_item = new HistoryItem (iter->first, iter->second); + add_new_history_entry (new_item); + items_added ++; + iter ++; + } + + return items_added; +} + diff --git a/sflphone-common/src/history/historymanager.h b/sflphone-common/src/history/historymanager.h index 6089f671a5e0011a0df841b30c66f3b1a4f59fcb..119bbd2b53caad5399163a7f047bb874f4e7d00f 100644 --- a/sflphone-common/src/history/historymanager.h +++ b/sflphone-common/src/history/historymanager.h @@ -25,7 +25,7 @@ #include <global.h> #include <user_cfg.h> -typedef std::map <int, HistoryItem*> HistoryItemMap; +typedef std::map <std::string, HistoryItem*> HistoryItemMap; class HistoryManager { @@ -40,7 +40,17 @@ class HistoryManager { */ ~HistoryManager (); - bool init (void); + /** + *@param path A specific file to use; if empty, use the global one + * + *@return int The number of history items succesfully loaded + */ + int load_history (std::string path=""); + + /** + *@return bool True if the history has been successfully saved in the file + */ + bool save_history (void); /* * Load the history from a file to the dedicated data structure @@ -80,6 +90,10 @@ class HistoryManager { return _history_items.size (); } + std::map <std::string, std::string> get_history_serialized (void); + + int set_serialized_history (std::map <std::string, std::string> history); + private: @@ -88,8 +102,10 @@ class HistoryManager { /* * Set the path to the history file + * + * @param path A specific file to use; if empty, use the global one */ - int create_history_path (void); + int create_history_path (std::string path=""); /* * Add a new history item in the data structure */ diff --git a/sflphone-common/src/managerimpl.cpp b/sflphone-common/src/managerimpl.cpp index 1e9f84e97d7883804a334ae112abc39464f2e127..5b4807629282f8aa9a6f458685e63288847e2dad 100644 --- a/sflphone-common/src/managerimpl.cpp +++ b/sflphone-common/src/managerimpl.cpp @@ -83,12 +83,14 @@ ManagerImpl::ManagerImpl (void) , _callConfigMap() , _accountMap() , _cleaner (NULL) + , _history (NULL) { // initialize random generator for call id srand (time(NULL)); _cleaner = new NumberCleaner (); + _history = new HistoryManager (); #ifdef TEST testAccountMap(); @@ -142,6 +144,10 @@ ManagerImpl::init() if (audiolayer == 0) audiolayer->stopStream(); + + + // Load the history + _history->load_history (); } void ManagerImpl::terminate() @@ -2851,3 +2857,19 @@ std::map< std::string, std::string > ManagerImpl::getCallDetails(const CallID& c return call_details; } + + + +std::map<std::string, std::string> ManagerImpl::send_history_to_client (void) +{ + return _history->get_history_serialized (); +} + +void ManagerImpl::receive_history_from_client (std::map<std::string, std::string> history) +{ + _history->set_serialized_history (history); + _history->save_history (); +} + + + diff --git a/sflphone-common/src/managerimpl.h b/sflphone-common/src/managerimpl.h index 10caf62b48c5e85df232aa12820e1398424bb9ae..c5f3ce760fc5332b16c8b0b283e8238d0f7b7644 100644 --- a/sflphone-common/src/managerimpl.h +++ b/sflphone-common/src/managerimpl.h @@ -37,6 +37,7 @@ #include "account.h" #include "call.h" #include "numbercleaner.h" +#include <history/historymanager.h> #include "audio/tonelist.h" // for Tone::TONEID declaration #include "audio/audiofile.h" @@ -1090,6 +1091,9 @@ class ManagerImpl { */ bool accountExists(const AccountID& accountID); + std::map<std::string, std::string> send_history_to_client (void); + + void receive_history_from_client (std::map<std::string, std::string> history); public: /** @@ -1136,6 +1140,11 @@ private: NumberCleaner *_cleaner; + /** + * To handle the persistent history + */ + HistoryManager *_history; + /** * Check if the call is a classic call or a direct IP-to-IP call */ diff --git a/sflphone-common/test/history-sample b/sflphone-common/test/history-sample index d181709de558215b886b3d00ea2ca96a5fe99052..5831fcb8f330b17368100a16e722e4598beed632 100644 --- a/sflphone-common/test/history-sample +++ b/sflphone-common/test/history-sample @@ -14,7 +14,7 @@ type=2 [775354456] from=5143848557 -id=Chez wam +id=Chez wam timestamp=775354456 to= type=1 diff --git a/sflphone-common/test/historyTest.cpp b/sflphone-common/test/historyTest.cpp index b6639008fd2a2f3d58c9f08243b74fe48a8d3a21..279060ec3e6da719f4bef3dd6bc022e0aac0fcc8 100644 --- a/sflphone-common/test/historyTest.cpp +++ b/sflphone-common/test/historyTest.cpp @@ -89,6 +89,8 @@ void HistoryTest::test_save_history_to_file () { std::string path; Conf::ConfigTree history_list, history_list2; + std::map <std::string, std::string> res; + std::map <std::string, std::string>::iterator iter; history->set_history_path (HISTORY_SAMPLE); history->load_history_from_file (&history_list); @@ -97,6 +99,64 @@ void HistoryTest::test_save_history_to_file () CPPUNIT_ASSERT (history->save_history_to_file (&history_list2)); } +void HistoryTest::test_get_history_serialized () +{ + std::map <std::string, std::string> res; + std::map <std::string, std::string>::iterator iter; + std::string tmp; + + CPPUNIT_ASSERT (history->load_history (HISTORY_SAMPLE) == HISTORY_SAMPLE_SIZE); + res = history->get_history_serialized (); + CPPUNIT_ASSERT (res.size()==HISTORY_SAMPLE_SIZE); + + // Warning - If you change the history-sample file, you must change the following lines also so that the tests could work + // The reference here is the file history-sample in this test directory + // The serialized form is: calltype%to%from%callid + + // Check the first + tmp = "0||514-276-5468|Savoir-faire Linux"; + CPPUNIT_ASSERT (tmp == res ["144562436"]); + + tmp = "2|136||Emmanuel Milou"; + CPPUNIT_ASSERT (tmp == res ["747638685"]); + + tmp = "1||5143848557|Chez wam"; + CPPUNIT_ASSERT (tmp == res ["775354456"]); +} + +void HistoryTest::test_set_serialized_history () +{ + // We build a map to have an efficient test + std::map <std::string, std::string> map_test; + std::string tmp; + Conf::ConfigTree history_list; + + map_test["144562436"] = "0||514-276-5468|Savoir-faire Linux"; + map_test["747638685"] = "2|136||Emmanuel Milou"; + map_test["775354456"] = "1||5143848557|Chez wam"; + + CPPUNIT_ASSERT (history->load_history (HISTORY_SAMPLE) == HISTORY_SAMPLE_SIZE); + CPPUNIT_ASSERT (history->set_serialized_history (map_test) == 3); + CPPUNIT_ASSERT (history->get_history_size () == 3); + + map_test.clear (); + map_test = history->get_history_serialized (); + CPPUNIT_ASSERT (map_test.size()==HISTORY_SAMPLE_SIZE); + + // Check the first + tmp = "0||514-276-5468|Savoir-faire Linux"; + CPPUNIT_ASSERT (tmp == map_test ["144562436"]); + + tmp = "2|136||Emmanuel Milou"; + CPPUNIT_ASSERT (tmp == map_test ["747638685"]); + + tmp = "1||5143848557|Chez wam"; + CPPUNIT_ASSERT (tmp == map_test ["775354456"]); + + history->save_history_items_map (&history_list); + CPPUNIT_ASSERT (history->save_history_to_file (&history_list)); +} + void HistoryTest::tearDown(){ // Delete the history object delete history; history=0; diff --git a/sflphone-common/test/historyTest.h b/sflphone-common/test/historyTest.h index c76623a9d64dd620862280a0fa2d8c99ec6c68c7..5b30f58b8292316a45f22cc5eeb6e4277bbc0abd 100644 --- a/sflphone-common/test/historyTest.h +++ b/sflphone-common/test/historyTest.h @@ -47,6 +47,8 @@ class HistoryTest : public CppUnit::TestCase { CPPUNIT_TEST (test_load_history_items_map); CPPUNIT_TEST (test_save_history_items_map); CPPUNIT_TEST (test_save_history_to_file); + CPPUNIT_TEST (test_get_history_serialized); + CPPUNIT_TEST (test_set_serialized_history); CPPUNIT_TEST_SUITE_END (); public: @@ -67,7 +69,11 @@ class HistoryTest : public CppUnit::TestCase { void test_save_history_items_map (); void test_save_history_to_file (); + + void test_get_history_serialized (); + void test_set_serialized_history (); + /* * Code factoring - Common resources can be released here. * This method is called by unitcpp after each test