Commit d5296a74 authored by Tristan Matthews's avatar Tristan Matthews

* #7264: gnome client now saves/loads history as List of Dicts.

parent 68bac528
......@@ -33,7 +33,7 @@
#include "historyitem.h"
#include <sstream>
#include <cstdlib>
#include "manager.h"
#include "config/config.h"
const char * const HistoryItem::ACCOUNT_ID_KEY = "accountid";
const char * const HistoryItem::CALLID_KEY = "callid";
......@@ -41,7 +41,6 @@ const char * const HistoryItem::CONFID_KEY = "confid";
const char * const HistoryItem::PEER_NAME_KEY = "peer_name";
const char * const HistoryItem::PEER_NUMBER_KEY = "peer_number";
const char * const HistoryItem::RECORDING_PATH_KEY = "recordfile";
const char * const HistoryItem::TIME_ADDED_KEY = "timeadded";
const char * const HistoryItem::TIMESTAMP_START_KEY = "timestamp_start";
const char * const HistoryItem::TIMESTAMP_STOP_KEY = "timestamp_stop";
const char * const HistoryItem::STATE_KEY = "state";
......@@ -63,7 +62,6 @@ HistoryItem::HistoryItem(const std::string &item, Conf::ConfigTree &historyList)
PEER_NAME_KEY,
PEER_NUMBER_KEY,
RECORDING_PATH_KEY,
TIME_ADDED_KEY,
TIMESTAMP_START_KEY,
TIMESTAMP_STOP_KEY,
STATE_KEY,
......@@ -95,7 +93,9 @@ bool HistoryItem::youngerThan(int otherTime) const
}
std::string HistoryItem::getTimestampStart() const {
std::map<std::string, std::string>::const_iterator iter(entryMap_.find(TIMESTAMP_START_KEY));
using std::map;
using std::string;
map<string, string>::const_iterator iter(entryMap_.find(TIMESTAMP_START_KEY));
if (iter != entryMap_.end())
return iter->second;
else
......
......@@ -48,7 +48,6 @@ class HistoryItem {
static const char * const PEER_NAME_KEY;
static const char * const PEER_NUMBER_KEY;
static const char * const RECORDING_PATH_KEY;
static const char * const TIME_ADDED_KEY;
static const char * const TIMESTAMP_START_KEY;
static const char * const TIMESTAMP_STOP_KEY;
static const char * const STATE_KEY;
......
......@@ -96,7 +96,6 @@ int HistoryManager::loadHistoryItemsMap(Conf::ConfigTree &historyList, int limit
return nb_items;
}
bool HistoryManager::saveHistoryToFile(const Conf::ConfigTree &historyList) const
{
DEBUG("HistoryManager: Saving history in XDG directory: %s", history_path_.c_str());
......@@ -124,7 +123,7 @@ void HistoryManager::createHistoryPath(const std::string &path)
// Else we 'll the standard one, ie: XDG_DATA_HOME = $HOMEDIR/.local/share/sflphone
if (XDG_DATA_HOME != NULL) {
std::string xdg_env(XDG_DATA_HOME);
(xdg_env.length() > 0) ? userdata = xdg_env : userdata = xdg_data;
(!xdg_env.empty()) ? userdata = xdg_env : userdata = xdg_data;
} else
userdata = xdg_data;
......
......@@ -63,55 +63,8 @@
#include "unused.h"
#include "widget/imwidget.h"
static GHashTable * ip2ip_profile;
static gchar ** sflphone_order_history_hash_table(GHashTable *result)
{
GHashTableIter iter;
gint size = 0;
gchar **ordered_list = NULL;
g_assert(result);
while (g_hash_table_size(result)) {
gpointer key, value;
gpointer key_to_min = NULL;
// find lowest timestamp in map
g_hash_table_iter_init(&iter, result);
gint min_timestamp = G_MAXINT;
while (g_hash_table_iter_next(&iter, &key, &value)) {
gint timestamp = atoi((gchar*) key);
if (timestamp < min_timestamp) {
min_timestamp = timestamp;
key_to_min = key;
}
}
if (g_hash_table_lookup_extended(result, key_to_min, &key, &value)) {
GSList *llist = (GSList *)value;
while (llist) {
ordered_list = (gchar **) g_realloc(ordered_list, (size + 1) * sizeof(gchar *));
*(ordered_list + size) = g_strdup((gchar *)llist->data);
size++;
llist = g_slist_next(llist);
}
g_hash_table_remove(result, key_to_min);
}
}
ordered_list = (gchar **) g_realloc(ordered_list, (size + 1) * sizeof(gchar *));
ordered_list[size] = NULL;
return ordered_list;
}
void
sflphone_notify_voice_mail(const gchar* accountID , guint count)
{
......@@ -174,8 +127,8 @@ status_bar_display_account()
if (acc) {
msg = g_markup_printf_escaped("%s %s (%s)" ,
_("Using account"),
(gchar*) g_hash_table_lookup(acc->properties , ACCOUNT_ALIAS),
(gchar*) g_hash_table_lookup(acc->properties , ACCOUNT_TYPE));
(gchar*) g_hash_table_lookup(acc->properties, ACCOUNT_ALIAS),
(gchar*) g_hash_table_lookup(acc->properties, ACCOUNT_TYPE));
} else {
msg = g_markup_printf_escaped(_("No registered accounts"));
}
......@@ -451,7 +404,7 @@ sflphone_pick_up()
break;
case CALL_STATE_INCOMING:
selectedCall->_history_state = INCOMING;
selectedCall->_history_state = g_strdup(INCOMING_STRING);
calltree_update_call(history_tab, selectedCall);
// if instant messaging window is visible, create new tab (deleted automatically if not used)
......@@ -597,7 +550,7 @@ sflphone_display_transfer_status(const gchar* message)
void
sflphone_incoming_call(callable_obj_t * c)
{
c->_history_state = MISSED;
c->_history_state = g_strdup(MISSED_STRING);
calllist_add_call(current_calls_tab, c);
calltree_add_call(current_calls_tab, c, NULL);
......@@ -688,7 +641,7 @@ sflphone_new_call()
callable_obj_t *c = create_new_call(CALL, CALL_STATE_DIALING, "", "", "", "");
c->_history_state = OUTGOING;
c->_history_state = g_strdup(OUTGOING_STRING);
calllist_add_call(current_calls_tab, c);
calltree_add_call(current_calls_tab, c, NULL);
......@@ -741,7 +694,7 @@ sflphone_keypad(guint keyval, gchar * key)
switch (keyval) {
case GDK_Return:
case GDK_KP_Enter:
c->_history_state = INCOMING;
c->_history_state = g_strdup(INCOMING_STRING);
calltree_update_call(history_tab, c);
dbus_accept(c);
break;
......@@ -867,7 +820,7 @@ static int place_registered_call(callable_obj_t * c)
notify_current_account(current);
}
c->_history_state = OUTGOING;
c->_history_state = g_strdup(OUTGOING_STRING);
return 0;
}
......@@ -924,8 +877,6 @@ sflphone_add_participant(const gchar* callID, const gchar* confID)
return;
}
time(&call->_time_added);
dbus_add_participant(callID, confID);
}
......@@ -1082,7 +1033,7 @@ create_callable_from_entry(gpointer data, gpointer user_data UNUSED)
callable_obj_t *history_call = create_history_entry_from_hashtable(entry);
/* Add it and update the GUI */
calllist_add_call(history_tab, history_call);
calllist_add_call_to_front(history_tab, history_call);
}
static void fill_treeview_with_calls(void)
......@@ -1106,28 +1057,34 @@ void sflphone_fill_history(void)
fill_treeview_with_calls();
}
#if ! (GLIB_CHECK_VERSION(2,28,0))
static void
g_slist_free_full(GSList *list,
GDestroyNotify free_func)
{
g_slist_foreach(list, (GFunc) free_func, NULL);
g_slist_free(list);
}
#endif
static void hist_free_elt(gpointer list)
/* Ordered from highest timestamp (most recent) to lowest (oldest) */
static gint
history_compare_func(gconstpointer a, gconstpointer b)
{
g_slist_free_full((GSList *)list, g_free);
gconstpointer first_value = g_hash_table_lookup(* (GHashTable **) a, TIMESTAMP_START_KEY);
gconstpointer second_value = g_hash_table_lookup(* (GHashTable **) b, TIMESTAMP_START_KEY);
/* treat NULL values as less than non-NULL values, like g_strcmp0 does */
if (!first_value)
return -(first_value != second_value);
else if (!second_value)
return first_value != second_value;
long f = atol(first_value);
long s = atol(second_value);
if (f > s)
return -1;
else if (f == s)
return 0;
else
return 1;
}
void sflphone_save_history(void)
{
GHashTable *result = g_hash_table_new_full(NULL, g_str_equal, g_free, hist_free_elt);
gint size = calllist_get_size(history_tab);
GPtrArray *sorted_history = g_ptr_array_new();
/* For each entry in our call history */
for (gint i = 0; i < size; ++i) {
QueueElement *current = calllist_get_nth(history_tab, i);
......@@ -1136,23 +1093,17 @@ void sflphone_save_history(void)
break;
}
gchar *value;
if (current->type == HIST_CALL) {
value = serialize_history_call_entry(current->elem.call);
gchar *key = g_strdup_printf("%i", (int) current->elem.call->_time_start);
g_hash_table_replace(result, (gpointer) key,
g_slist_append(g_hash_table_lookup(result, key),(gpointer) value));
GHashTable *value = create_hashtable_from_history_entry(current->elem.call);
g_ptr_array_add(sorted_history, (gpointer) value);
}
else
ERROR("SFLphone: Error: Unknown type for serialization");
}
gchar **ordered_result = sflphone_order_history_hash_table(result);
dbus_set_history(ordered_result);
g_strfreev(ordered_result);
g_hash_table_unref(result);
g_ptr_array_sort(sorted_history, history_compare_func);
dbus_set_history(sorted_history);
g_ptr_array_free(sorted_history, TRUE);
}
void
......
......@@ -164,16 +164,6 @@ callable_obj_t *create_new_call_from_details(const gchar *call_id, GHashTable *d
return c;
}
static history_state_t get_history_state_from_id(gchar *indice)
{
history_state_t state = atoi(indice);
if (state > LAST)
state = MISSED;
return state;
}
static gconstpointer get_str(GHashTable *entry, gconstpointer key)
{
gconstpointer result = g_hash_table_lookup(entry, key);
......@@ -182,26 +172,33 @@ static gconstpointer get_str(GHashTable *entry, gconstpointer key)
return result;
}
/* FIXME:tmatth: These need to be in sync with the daemon */
static const char * const ACCOUNT_ID_KEY = "accountid";
static const char * const CALLID_KEY = "callid";
static const char * const CONFID_KEY = "confid";
static const char * const PEER_NAME_KEY = "peer_name";
static const char * const PEER_NUMBER_KEY = "peer_number";
static const char * const RECORDING_PATH_KEY = "recordfile";
static const char * const TIMESTAMP_STOP_KEY = "timestamp_stop";
static const char * const STATE_KEY = "state";
callable_obj_t *create_history_entry_from_hashtable(GHashTable *entry)
{
gconstpointer callID = get_str(entry, "callid");
gconstpointer accountID = get_str(entry, "accountid");
gconstpointer peer_name = get_str(entry, "peer_name");
gconstpointer peer_number = get_str(entry, "peer_number");
gconstpointer callID = get_str(entry, CALLID_KEY);
gconstpointer accountID = get_str(entry, ACCOUNT_ID_KEY);
gconstpointer peer_name = get_str(entry, PEER_NAME_KEY);
gconstpointer peer_number = get_str(entry, PEER_NUMBER_KEY);
callable_obj_t *new_call = create_new_call(HISTORY_ENTRY, CALL_STATE_DIALING, callID, accountID, peer_name, peer_number);
gconstpointer value = g_hash_table_lookup(entry, "state");
new_call->_history_state = value ? atoi(value) : MISSED;
value = g_hash_table_lookup(entry, "timestamp_start");
new_call->_history_state = g_strdup(get_str(entry, STATE_KEY));
gconstpointer value = g_hash_table_lookup(entry, TIMESTAMP_START_KEY);
new_call->_time_start = value ? atoi(value) : 0;
value = g_hash_table_lookup(entry, "timestamp_stop");
value = g_hash_table_lookup(entry, TIMESTAMP_STOP_KEY);
new_call->_time_stop = value ? atoi(value) : 0;
value = g_hash_table_lookup(entry, "recordfile");
value = g_hash_table_lookup(entry, RECORDING_PATH_KEY);
new_call->_recordfile = g_strdup(value);
value = g_hash_table_lookup(entry, "confid");
value = g_hash_table_lookup(entry, CONFID_KEY);
new_call->_confID = g_strdup(value);
new_call->_historyConfID = g_strdup(value);
value = g_hash_table_lookup(entry, "timeadded");
new_call->_time_added = value ? atoi(value) : 0;
new_call->_record_is_playing = FALSE;
return new_call;
......@@ -239,49 +236,40 @@ gchar* get_call_duration(callable_obj_t *obj)
return g_strdup_printf("<small>Duration:</small> %.2ld:%.2ld" , duration/60 , duration%60);
}
static const gchar* get_history_id_from_state(history_state_t state)
static
void add_to_hashtable(GHashTable *hashtable, const gchar *key, const gchar *value)
{
if (state >= LAST)
return "";
return state + "0";
g_hash_table_insert(hashtable, g_strdup(key), g_strdup(value));
}
gchar* serialize_history_call_entry(callable_obj_t *entry)
GHashTable* create_hashtable_from_history_entry(callable_obj_t *entry)
{
// "0|514-276-5468|Savoir-faire Linux|144562458" for instance
gchar *peer_number, *peer_name, *account_id;
static const gchar * const separator = "|";
gchar *record_file;
gchar *confID;
// Need the string form for the history state
const gchar *history_state = get_history_id_from_state(entry->_history_state);
const gchar *history_state = entry->_history_state ? entry->_history_state : "";
// and the timestamps
gchar *time_start = g_strdup_printf("%i", (int) entry->_time_start);
gchar *time_stop = g_strdup_printf("%i", (int) entry->_time_stop);
gchar *time_added = g_strdup_printf("%i", (int) entry->_time_added);
peer_number = entry->_peer_number ? entry->_peer_number : "";
peer_name = (entry->_peer_name && *entry->_peer_name) ? entry->_peer_name : "empty";
account_id = (entry->_accountID && *entry->_accountID) ? entry->_accountID : "empty";
confID = entry->_historyConfID ? entry->_historyConfID : "";
record_file = entry->_recordfile ? entry->_recordfile : "";
gchar *result = g_strconcat(history_state, separator,
peer_number, separator,
peer_name, separator,
time_start, separator,
time_stop, separator,
entry->_callID, separator,
account_id, separator,
record_file, separator,
confID, separator,
time_added, NULL);
g_free(time_start);
g_free(time_stop);
g_free(time_added);
const gchar *call_id = entry->_callID ? entry->_callID : "";
const gchar *peer_number = entry->_peer_number ? entry->_peer_number : "";
const gchar *peer_name = (entry->_peer_name && *entry->_peer_name) ? entry->_peer_name : "empty";
const gchar *account_id = (entry->_accountID && *entry->_accountID) ? entry->_accountID : "empty";
const gchar *conf_id = entry->_historyConfID ? entry->_historyConfID : "";
const gchar *recording_path = entry->_recordfile ? entry->_recordfile : "";
GHashTable *result = g_hash_table_new(NULL, g_str_equal);
add_to_hashtable(result, CALLID_KEY, call_id);
add_to_hashtable(result, CONFID_KEY, conf_id);
add_to_hashtable(result, PEER_NUMBER_KEY, peer_number);
add_to_hashtable(result, PEER_NAME_KEY, peer_name);
add_to_hashtable(result, RECORDING_PATH_KEY, recording_path);
add_to_hashtable(result, ACCOUNT_ID_KEY, account_id);
add_to_hashtable(result, TIMESTAMP_START_KEY, time_start);
add_to_hashtable(result, TIMESTAMP_STOP_KEY, time_stop);
add_to_hashtable(result, STATE_KEY, history_state);
/* These values were already allocated dynamically */
g_hash_table_insert(result, g_strdup(TIMESTAMP_START_KEY), time_start);
g_hash_table_insert(result, g_strdup(TIMESTAMP_STOP_KEY), time_stop);
return result;
}
......
......@@ -35,17 +35,6 @@
#include <time.h>
#include <gtk/gtk.h>
/**
* @enum history_state
* This enum have all the state a call can take in the history
*/
typedef enum {
MISSED = 0,
INCOMING = 1,
OUTGOING = 2,
LAST = 3,
} history_state_t;
/**
* @enum contact_type
* This enum have all types of contacts: HOME phone, cell phone, etc...
......@@ -83,6 +72,11 @@ typedef enum {
CALL_STATE_RECORD,
} call_state_t;
static const char * const TIMESTAMP_START_KEY = "timestamp_start";
static const char * const MISSED_STRING = "missed";
static const char * const INCOMING_STRING = "incoming";
static const char * const OUTGOING_STRING = "outgoing";
typedef enum {
SRTP_STATE_UNLOCKED = 0,
SRTP_STATE_SDES_SUCCESS,
......@@ -103,11 +97,11 @@ typedef struct {
gchar* _state_code_description; // A textual description of _state_code
gchar* _callID; // The call ID
gchar* _confID; // The conference ID (NULL if don't participate to a conference)
gchar* _historyConfID; // Persistent conf id to be stored in history
gchar* _historyConfID; // Persistent conf id to be stored in history
gchar* _accountID; // The account the call is made with
time_t _time_start; // The timestamp the call was initiating
time_t _time_stop; // The timestamp the call was over
history_state_t _history_state; // The history state if necessary
gchar *_history_state; // The history state if necessary
srtp_state_t _srtp_state; // The state of security on the call
gchar* _srtp_cipher; // Cipher used for the srtp session
gchar* _sas; // The Short Authentication String that should be displayed
......@@ -158,21 +152,21 @@ typedef struct {
/* Associated IM widget */
GtkWidget *_im_widget;
time_t _time_added;
} callable_obj_t;
callable_obj_t *create_new_call (callable_type_t, call_state_t, const gchar* const, const gchar* const, const gchar* const, const gchar* const);
callable_obj_t *create_new_call(callable_type_t, call_state_t, const gchar* const, const gchar* const, const gchar* const, const gchar* const);
callable_obj_t *create_new_call_from_details (const gchar *, GHashTable *);
callable_obj_t *create_new_call_from_details(const gchar *, GHashTable *);
callable_obj_t *create_history_entry_from_hashtable (GHashTable *entry);
callable_obj_t *create_history_entry_from_hashtable(GHashTable *entry);
void call_add_error (callable_obj_t * call, gpointer dialog);
GHashTable* create_hashtable_from_history_entry(callable_obj_t *entry);
void call_remove_error (callable_obj_t * call, gpointer dialog);
void call_add_error(callable_obj_t * call, gpointer dialog);
void call_remove_all_errors (callable_obj_t * call);
void call_remove_error(callable_obj_t * call, gpointer dialog);
void call_remove_all_errors(callable_obj_t * call);
/*
* GCompareFunc to compare a callID (gchar* and a callable_obj_t)
......@@ -182,32 +176,30 @@ void call_remove_all_errors (callable_obj_t * call);
/*
* GCompareFunc to get current call (gchar* and a callable_obj_t)
*/
gint get_state_callstruct (gconstpointer, gconstpointer);
gint get_state_callstruct(gconstpointer, gconstpointer);
/**
* This function parse the callable_obj_t.from field to return the name
* @param c The call
* @return The full name of the caller or an empty string
*/
gchar* call_get_peer_name (const gchar*);
gchar* call_get_peer_name(const gchar*);
/**
* This function parse the callable_obj_t.from field to return the number
* @param c The call
* @return The number of the caller
*/
gchar* call_get_peer_number (const gchar*);
void free_callable_obj_t (callable_obj_t *c);
gchar* call_get_peer_number(const gchar*);
gchar* get_peer_info (const gchar* const, const gchar* const);
void free_callable_obj_t(callable_obj_t *c);
gchar* get_call_duration (callable_obj_t *obj);
gchar* get_peer_info(const gchar* const, const gchar* const);
gchar* serialize_history_call_entry(callable_obj_t *entry);
gchar* get_call_duration(callable_obj_t *obj);
gchar* get_formatted_start_timestamp (time_t);
gchar* get_formatted_start_timestamp(time_t);
gchar* call_get_audio_codec (callable_obj_t *obj);
gchar* call_get_audio_codec(callable_obj_t *obj);
#endif
......@@ -120,6 +120,15 @@ calllist_add_call(calltab_t* tab, callable_obj_t * c)
g_queue_push_tail(tab->callQueue, (gpointer) element);
}
void
calllist_add_call_to_front(calltab_t* tab, callable_obj_t * c)
{
QueueElement *element = g_new0(QueueElement, 1);
element->type = HIST_CALL;
element->elem.call = c;
g_queue_push_head(tab->callQueue, (gpointer) element);
}
void
calllist_clean_history(void)
{
......
......@@ -67,59 +67,65 @@ typedef struct {
} calltab_t;
void
calllist_add_contact (gchar *, gchar *, contact_type_t, GdkPixbuf *);
calllist_add_contact(gchar *, gchar *, contact_type_t, GdkPixbuf *);
/** This function empty and free the call list. */
void
calllist_clean (calltab_t* tab);
calllist_clean(calltab_t* tab);
/** This function empty, free the call list and allocate a new one. */
void
calllist_reset (calltab_t* tab);
calllist_reset(calltab_t* tab);
/** Get the maximun number of calls in the history calltab */
gdouble
call_history_get_max_calls (void);
call_history_get_max_calls(void);
/** Set the maximun number of calls in the history calltab */
void
call_history_set_max_calls (const gdouble number);
call_history_set_max_calls(const gdouble number);
/** This function append a call to list.
* @param c The call you want to add
* */
void
calllist_add_call (calltab_t* tab, callable_obj_t * c);
calllist_add_call(calltab_t* tab, callable_obj_t * c);
/** This function inserts a call to front of list.
* @param c The call you want to add
* */
void
calllist_add_call_to_front(calltab_t* tab, callable_obj_t * c);
/** This function remove a call from list.
* @param callID The callID of the call you want to remove
*/
void
calllist_remove_call (calltab_t* tab, const gchar * callID);
calllist_remove_call(calltab_t* tab, const gchar * callID);
/** Return the first call that corresponds to the state.
* This is usefull for unique states as DIALING and CURRENT.
* @param state The state
* @return A call or NULL */
callable_obj_t *
calllist_get_by_state (calltab_t* tab, call_state_t state);
calllist_get_by_state(calltab_t* tab, call_state_t state);
/** Return the number of calls in the list
* @return The number of calls in the list */
guint
calllist_get_size (const calltab_t* tab);
calllist_get_size(const calltab_t* tab);
/** Return the call at the nth position in the list
* @param n The position of the call you want
* @return A call or NULL */
QueueElement *
calllist_get_nth (calltab_t* tab, guint n);
calllist_get_nth(calltab_t* tab, guint n);
/** Return the call corresponding to the callID
* @param n The callID of the call you want
* @return A call or NULL */
callable_obj_t *