diff --git a/daemon/bin/dbus/configurationmanager-introspec.xml b/daemon/bin/dbus/configurationmanager-introspec.xml index 3c219b4809747d6db78122aadce6a29fddadd29e..72091673fcf76103f20981c39f39723e4e6ecdaf 100644 --- a/daemon/bin/dbus/configurationmanager-introspec.xml +++ b/daemon/bin/dbus/configurationmanager-introspec.xml @@ -516,9 +516,21 @@ </arg> </method> + <method name="getHistory" tp:name-for-bindings="getHistory"> + <annotation name="org.qtproject.QtDBus.QtTypeName.Out0" value="VectorMapStringString"/> + <!-- Return a List of type Dict<string, string> >...a List of Dicts --> + <arg type="aa{ss}" name="entries" direction="out"/> + </method> + + <method name="clearHistory" tp:name-for-bindings="clearHistory"> + </method> + <signal name="accountsChanged" tp:name-for-bindings="accountsChanged"> </signal> + <signal name="historyChanged" tp:name-for-bindings="historyChanged"> + </signal> + <!-- FIXME: we should rethink these two signals --> <!-- Used by IAX and SIP accounts --> <signal name="registrationStateChanged" tp:name-for-bindings="registrationStateChanged"> diff --git a/daemon/bin/dbus/dbusclient.cpp b/daemon/bin/dbus/dbusclient.cpp index 6cc83ddc35baa0bbe2b8a994ee7b22caa80deaa3..7b809186e50aeb28d5fb8315f095e0f520fdb7a2 100644 --- a/daemon/bin/dbus/dbusclient.cpp +++ b/daemon/bin/dbus/dbusclient.cpp @@ -175,6 +175,7 @@ int DBusClient::initLibrary(int sflphFlags) DRing::EventHandlerKey::CONFIG, { exportable_callback<ConfigurationSignal::VolumeChanged>(bind(&DBusConfigurationManager::volumeChanged, confM, _1, _2)), exportable_callback<ConfigurationSignal::AccountsChanged>(bind(&DBusConfigurationManager::accountsChanged, confM)), + exportable_callback<ConfigurationSignal::HistoryChanged>(bind(&DBusConfigurationManager::historyChanged, confM)), exportable_callback<ConfigurationSignal::StunStatusFailed>(bind(&DBusConfigurationManager::stunStatusFailure, confM, _1)), exportable_callback<ConfigurationSignal::RegistrationStateChanged>(bind(&DBusConfigurationManager::registrationStateChanged, confM, _1, _2)), exportable_callback<ConfigurationSignal::SipRegistrationStateChanged>(bind(&DBusConfigurationManager::sipRegistrationStateChanged, confM, _1, _2, _3)), diff --git a/daemon/bin/dbus/dbusconfigurationmanager.cpp b/daemon/bin/dbus/dbusconfigurationmanager.cpp index 856a0be490c8edd92d51ff9a2940c0588132484a..219510344ebe529b05b0cdfdcc88eabcc65ed7ba 100644 --- a/daemon/bin/dbus/dbusconfigurationmanager.cpp +++ b/daemon/bin/dbus/dbusconfigurationmanager.cpp @@ -330,6 +330,12 @@ DBusConfigurationManager::getHistoryLimit() -> decltype(DRing::getHistoryLimit() return DRing::getHistoryLimit(); } +void +DBusConfigurationManager::clearHistory() +{ + DRing::clearHistory(); +} + void DBusConfigurationManager::setAccountsOrder(const std::string& order) { @@ -348,6 +354,12 @@ DBusConfigurationManager::setHookSettings(const std::map<std::string, std::strin DRing::setHookSettings(settings); } +auto +DBusConfigurationManager::getHistory() -> decltype(DRing::getHistory()) +{ + return DRing::getHistory(); +} + auto DBusConfigurationManager::getTlsSettings() -> decltype(DRing::getTlsSettings()) { diff --git a/daemon/bin/dbus/dbusconfigurationmanager.h b/daemon/bin/dbus/dbusconfigurationmanager.h index 18447aa96c15c21899501316bf064408488a8989..0dad0d1d9461ec90f8e80eb75c1eb39ac7ce9ba2 100644 --- a/daemon/bin/dbus/dbusconfigurationmanager.h +++ b/daemon/bin/dbus/dbusconfigurationmanager.h @@ -112,9 +112,11 @@ class DBusConfigurationManager : void setIsAlwaysRecording(const bool& rec); void setHistoryLimit(const int32_t& days); int32_t getHistoryLimit(); + void clearHistory(); void setAccountsOrder(const std::string& order); std::map<std::string, std::string> getHookSettings(); void setHookSettings(const std::map<std::string, std::string>& settings); + std::vector<std::map<std::string, std::string>> getHistory(); std::map<std::string, std::string> getTlsSettings(); void setTlsSettings(const std::map<std::string, std::string>& details); std::map<std::string, std::string> getIp2IpDetails(); diff --git a/daemon/configure.ac b/daemon/configure.ac index 6d2c7b970ab2ddb5a3cd3e42a6e45acf6cc43159..349c19d0032c6cc845384d6183eb60c04288424c 100644 --- a/daemon/configure.ac +++ b/daemon/configure.ac @@ -591,6 +591,7 @@ AC_CONFIG_FILES([Makefile \ src/config/Makefile \ src/client/Makefile \ src/hooks/Makefile \ + src/history/Makefile \ src/media/video/Makefile \ src/media/video/v4l2/Makefile \ src/media/video/test/Makefile \ diff --git a/daemon/src/Makefile.am b/daemon/src/Makefile.am index 7f1bd34a6a37baf0e9e565310ae99e78ede21ed0..d0b6c9ff7f1f8c0f9537fca3d2292fb8fe572a56 100644 --- a/daemon/src/Makefile.am +++ b/daemon/src/Makefile.am @@ -35,7 +35,7 @@ TLS_LIB = @GNUTLS_LIBS@ TLS_CFLAGS = @GNUTLS_CFLAGS@ endif -SUBDIRS = client media config hooks sip upnp $(IAX_SUBDIR) $(RINGACC_SUBDIR) $(INSTANT_MESSAGING_SUBDIR) $(RING_VIDEO_SUBDIR) +SUBDIRS = client media config hooks history sip upnp $(IAX_SUBDIR) $(RINGACC_SUBDIR) $(INSTANT_MESSAGING_SUBDIR) $(RING_VIDEO_SUBDIR) # libring @@ -47,6 +47,7 @@ libring_la_LIBADD = \ ./client/libclient.la \ ./config/libconfig.la \ ./hooks/libhooks.la \ + ./history/libhistory.la \ ./upnp/libupnpcontrol.la \ $(RINGACC_LIBA) \ $(IAX_LIBA) \ diff --git a/daemon/src/call.cpp b/daemon/src/call.cpp index 2cb97602d59d870fb40c267b06d0534e3420787e..8180628a393ded9414bca9a5db956bdbba861023 100644 --- a/daemon/src/call.cpp +++ b/daemon/src/call.cpp @@ -34,6 +34,7 @@ #include "account.h" #include "manager.h" #include "audio/ringbufferpool.h" +#include "history/historyitem.h" #include "sip/sip_utils.h" #include "ip_utils.h" @@ -243,6 +244,35 @@ timestamp_to_string(const time_t ×tamp) return time_str.str(); } +std::map<std::string, std::string> Call::createHistoryEntry() const +{ + std::map<std::string, std::string> result; + + result[HistoryItem::ACCOUNT_ID_KEY] = getAccountId(); + result[HistoryItem::CONFID_KEY] = confID_; + result[HistoryItem::CALLID_KEY] = id_; + result[HistoryItem::DISPLAY_NAME_KEY] = displayName_; + result[HistoryItem::PEER_NUMBER_KEY] = peerNumber_; + result[HistoryItem::RECORDING_PATH_KEY] = recAudio_.fileExists() ? getFilename() : ""; + result[HistoryItem::TIMESTAMP_START_KEY] = timestamp_to_string(timestamp_start_); + result[HistoryItem::TIMESTAMP_STOP_KEY] = timestamp_to_string(timestamp_stop_); + + // FIXME: state will no longer exist, it will be split into + // a boolean field called "missed" and a direction field "incoming" or "outgoing" + if (connectionState_ == RINGING) { + result[HistoryItem::STATE_KEY] = HistoryItem::MISSED_STRING; + result[HistoryItem::MISSED_KEY] = "true"; + } else { + result[HistoryItem::STATE_KEY] = getTypeStr(); + result[HistoryItem::MISSED_KEY] = "false"; + } + + // now "missed" and direction are independent + result[HistoryItem::DIRECTION_KEY] = getTypeStr(); + + return result; +} + std::map<std::string, std::string> Call::getDetails() { diff --git a/daemon/src/call.h b/daemon/src/call.h index aeacc08183c333472357a869ce356a9d29b7c5cc..d1bd08e1b9e43a8f1291eeeef89e2ede7a5c8dac 100644 --- a/daemon/src/call.h +++ b/daemon/src/call.h @@ -234,6 +234,9 @@ class Call : public Recordable, public std::enable_shared_from_this<Call> { virtual std::map<std::string, std::string> getDetails(); static std::map<std::string, std::string> getNullDetails(); + virtual std::map<std::string, std::string> + createHistoryEntry() const; + virtual bool toggleRecording(); /** diff --git a/daemon/src/client/configurationmanager.cpp b/daemon/src/client/configurationmanager.cpp index 4f73face4411ffc190d6fd9204e2ecf01c3d0597..e3c2d7cd550ebe0a461afb8258f968c988b44d9c 100644 --- a/daemon/src/client/configurationmanager.cpp +++ b/daemon/src/client/configurationmanager.cpp @@ -435,6 +435,12 @@ getHistoryLimit() return ring::Manager::instance().getHistoryLimit(); } +void +clearHistory() +{ + return ring::Manager::instance().clearHistory(); +} + void setHistoryLimit(int32_t days) { @@ -556,6 +562,12 @@ void setAccountsOrder(const std::string& order) ring::Manager::instance().setAccountsOrder(order); } +std::vector<std::map<std::string, std::string>> +getHistory() +{ + return ring::Manager::instance().getHistory(); +} + std::string getAddrFromInterfaceName(const std::string& interface) { diff --git a/daemon/src/client/configurationmanager.h b/daemon/src/client/configurationmanager.h deleted file mode 100644 index 3e626b61bd15d645ef0d9e81516bf5ea254801bb..0000000000000000000000000000000000000000 --- a/daemon/src/client/configurationmanager.h +++ /dev/null @@ -1,170 +0,0 @@ -/* - * Copyright (C) 2004-2015 Savoir-Faire Linux Inc. - * Author: Pierre-Luc Beaudoin <pierre-luc.beaudoin@savoirfairelinux.com> - * Author: Alexandre Bourget <alexandre.bourget@savoirfairelinux.com> - * Author: Emmanuel Milou <emmanuel.milou@savoirfairelinux.com> - * Author: Guillaume Carmel-Archambault <guillaume.carmel-archambault@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 - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Additional permission under GNU GPL version 3 section 7: - * - * If you modify this program, or any covered work, by linking or - * combining it with the OpenSSL project's OpenSSL library (or a - * modified version of that library), containing parts covered by the - * terms of the OpenSSL or SSLeay licenses, Savoir-Faire Linux Inc. - * grants you additional permission to convey the resulting work. - * Corresponding Source for a non-source form of such a combination - * shall include the source code for the parts of OpenSSL used as well - * as that of the covered work. - */ - -#ifndef CONFIGURATIONMANAGER_H -#define CONFIGURATIONMANAGER_H - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#include <vector> -#include <map> -#include <string> - -#include "dring.h" - -namespace ring { - -class ConfigurationManager -{ - public: - void registerEvHandlers(struct ring_config_ev_handlers* evHandlers); - - // Methods - public: - std::map< std::string, std::string > getAccountDetails(const std::string& accountID); - std::map<std::string, std::string> getVolatileAccountDetails(const std::string& accountID); - void setAccountDetails(const std::string& accountID, const std::map< std::string, std::string >& details); - std::map<std::string, std::string> getAccountTemplate(); - std::string addAccount(const std::map< std::string, std::string >& details); - void removeAccount(const std::string& accoundID); - std::vector< std::string > getAccountList(); - void sendRegister(const std::string& accoundID, bool enable); - void registerAllAccounts(void); - - std::map< std::string, std::string > getTlsSettingsDefault(); - - std::vector< int32_t > getAudioCodecList(); - std::vector< std::string > getSupportedTlsMethod(); - std::vector< std::string > getSupportedCiphers(const std::string& accountID) const; - std::vector< std::string > getAudioCodecDetails(int32_t payload); - std::vector< int32_t > getActiveAudioCodecList(const std::string& accountID); - - void setActiveAudioCodecList(const std::vector< std::string >& list, const std::string& accountID); - - std::vector< std::string > getAudioPluginList(); - void setAudioPlugin(const std::string& audioPlugin); - std::vector< std::string > getAudioOutputDeviceList(); - void setAudioOutputDevice(int32_t index); - void setAudioInputDevice(int32_t index); - void setAudioRingtoneDevice(int32_t index); - std::vector< std::string > getAudioInputDeviceList(); - std::vector< std::string > getCurrentAudioDevicesIndex(); - int32_t getAudioInputDeviceIndex(const std::string& name); - int32_t getAudioOutputDeviceIndex(const std::string& name); - std::string getCurrentAudioOutputPlugin(); - bool getNoiseSuppressState(); - void setNoiseSuppressState(bool state); - - bool isAgcEnabled(); - void setAgcState(bool enabled); - - void muteDtmf(bool mute); - bool isDtmfMuted(); - - bool isCaptureMuted(); - void muteCapture(bool mute); - bool isPlaybackMuted(); - void mutePlayback(bool mute); - - std::map<std::string, std::string> getRingtoneList(); - - std::string getAudioManager(); - bool setAudioManager(const std::string& api); - - int32_t isIax2Enabled(); - std::string getRecordPath(); - void setRecordPath(const std::string& recPath); - bool getIsAlwaysRecording(); - void setIsAlwaysRecording(bool rec); - - void setHistoryLimit(int32_t days); - int32_t getHistoryLimit(); - - void setAccountsOrder(const std::string& order); - - std::map<std::string, std::string> getHookSettings(); - void setHookSettings(const std::map<std::string, std::string>& settings); - - std::map<std::string, std::string> getTlsSettings(); - void setTlsSettings(const std::map< std::string, std::string >& details); - std::map< std::string, std::string > getIp2IpDetails(); - - std::vector< std::map< std::string, std::string > > getCredentials(const std::string& accountID); - void setCredentials(const std::string& accountID, const std::vector< std::map< std::string, std::string > >& details); - - std::string getAddrFromInterfaceName(const std::string& interface); - - std::vector<std::string> getAllIpInterface(); - std::vector<std::string> getAllIpInterfaceByName(); - - std::map<std::string, std::string> getShortcuts(); - void setShortcuts(const std::map<std::string, std::string> &shortcutsMap); - - void setVolume(const std::string& device, double value); - double getVolume(const std::string& device); - - /* - * Security - */ - std::map<std::string, std::string> validateCertificate(const std::string& accountId, - const std::string& certificate, const std::string& privateKey); - std::map<std::string, std::string> getCertificateDetails(const std::string& certificate); - - - // Signals - public: - void volumeChanged(const std::string& device, double value); - - void accountsChanged(); - - void stunStatusFailure(const std::string& accoundID); - - void registrationStateChanged(const std::string& accoundID, int state); - void sipRegistrationStateChanged(const std::string&, const std::string&, int32_t state); - void volatileAccountDetailsChanged(const std::string& accountID, const std::map<std::string, std::string> &details); - void errorAlert(int alert); - - std::vector< int32_t > getHardwareAudioFormat(); - - private: -#pragma GCC diagnostic ignored "-Wmissing-field-initializers" - // Event handlers; needed by the library API - ring_config_ev_handlers evHandlers_{}; -#pragma GCC diagnostic warning "-Wmissing-field-initializers" -}; - -} // namespace ring - -#endif //CONFIGURATIONMANAGER_H diff --git a/daemon/src/dring/dring.h b/daemon/src/dring/dring.h index e15d02809efe8a599e0151e280154282edd86765..818fa0183425265d2834705d678db00d80628695 100644 --- a/daemon/src/dring/dring.h +++ b/daemon/src/dring/dring.h @@ -160,124 +160,6 @@ void fini(void) noexcept; */ void poll_events(void); -/* call API */ -bool ring_call_place(const std::string& account_id, const std::string& call_id, const std::string& to); -bool ring_call_refuse(const std::string& call_id); -bool ring_call_accept(const std::string& call_id); -bool ring_call_hang_up(const std::string& call_id); -bool ring_call_hold(const std::string& call_id); -bool ring_call_unhold(const std::string& call_id); -bool ring_call_transfer(const std::string& call_id, const std::string& to); -bool ring_call_attended_transfer(const std::string& transfer_id, const std::string& target_id); -std::map<std::string, std::string> ring_call_get_call_details(const std::string& call_id); -std::vector<std::string> ring_call_get_call_list(void); -void ring_call_remove_conference(const std::string& conf_id); -bool ring_call_join_participant(const std::string& sel_call_id, const std::string& drag_call_id); -void ring_call_create_conf_from_participant_list(const std::vector<std::string>& participants); -bool ring_call_is_conference_participant(const std::string& call_id); -bool ring_call_add_participant(const std::string& call_id, const std::string& conf_id); -bool ring_call_add_main_participant(const std::string& conf_id); -bool ring_call_detach_participant(const std::string& call_id); -bool ring_call_join_conference(const std::string& sel_conf_id, const std::string& drag_conf_id); -bool ring_call_hang_up_conference(const std::string& conf_id); -bool ring_call_hold_conference(const std::string& conf_id); -bool ring_call_unhold_conference(const std::string& conf_id); -std::vector<std::string> ring_call_get_conference_list(void); -std::vector<std::string> ring_call_get_participant_list(const std::string& conf_id); -std::vector<std::string> ring_call_get_display_names(const std::string& conf_id); -std::string ring_call_get_conference_id(const std::string& call_id); -std::map<std::string, std::string> ring_call_get_conference_details(const std::string& call_id); -bool ring_call_play_recorded_file(const std::string& path); -void ring_call_stop_recorded_file(const std::string& path); -bool ring_call_toggle_recording(const std::string& call_id); -void ring_call_set_recording(const std::string& call_id); -void ring_call_record_playback_seek(double pos); -bool ring_call_is_recording(const std::string& call_id); -std::string ring_call_get_current_audio_codec_name(const std::string& call_id); -void ring_call_play_dtmf(const std::string& key); -void ring_call_start_tone(int start, int type); -void ring_call_set_sas_verified(const std::string& call_id); -void ring_call_reset_sas_verified(const std::string& call_id); -void ring_call_set_confirm_go_clear(const std::string& call_id); -void ring_call_request_go_clear(const std::string& call_id); -void ring_call_accept_enrollment(const std::string& call_id, bool accepted); -void ring_call_send_text_message(const std::string& call_id, const std::string& message); - -/* configuration API */ -std::map<std::string, std::string> ring_config_get_account_details(const std::string& account_id); -std::map<std::string, std::string> ring_config_get_volatile_account_details(const std::string& account_id); -void ring_config_set_account_details(const std::string& account_id, const std::map<std::string, std::string>& details); -std::map<std::string, std::string> ring_config_get_account_template(void); -std::string ring_config_add_account(const std::map<std::string, std::string>& details); -void ring_config_remove_account(const std::string& account_id); -std::vector<std::string> ring_config_get_account_list(void); -void ring_config_send_register(const std::string& account_id, bool enable); -void ring_config_register_all_accounts(void); -std::map<std::string, std::string> ring_config_get_tls_default_settings(void); -std::vector<int> ring_config_get_audio_codec_list(void); -std::vector<std::string> ring_config_get_supported_tls_method(void); -std::vector<std::string> ring_config_get_supported_ciphers(const std::string& account_id); -std::vector<std::string> ring_config_get_audio_codec_details(int payload); -std::vector<int> ring_config_get_active_audio_codec_list(const std::string& account_id); -void ring_config_set_active_audio_codec_list(const std::vector<std::string>& list, const std::string& account_id); -std::vector<std::string> ring_config_get_audio_plugin_list(void); -void ring_config_set_audio_plugin(const std::string& audio_plugin); -std::vector<std::string> ring_config_get_audio_output_device_list(); -void ring_config_set_audio_output_device(int index); -void ring_config_set_audio_input_device(int index); -void ring_config_set_audio_ringtone_device(int index); -std::vector<std::string> ring_config_get_audio_input_device_list(void); -std::vector<std::string> ring_config_get_current_audio_devices_index(void); -int ring_config_get_audio_input_device_index(const std::string& name); -int ring_config_get_audio_output_device_index(const std::string& name); -std::string ring_config_get_current_audio_output_plugin(void); -bool ring_config_get_noise_suppress_state(void); -void ring_config_set_noise_suppress_state(bool state); -bool ring_config_is_agc_enabled(void); -void ring_config_enable_agc(bool enabled); -void ring_config_mute_dtmf(bool mute); -bool ring_config_is_dtmf_muted(void); -bool ring_config_is_capture_muted(void); -void ring_config_mute_capture(bool mute); -bool ring_config_is_playback_muted(void); -void ring_config_mute_playback(int mute); -std::map<std::string, std::string> ring_config_get_ringtone_list(void); -std::string ring_config_get_audio_manager(void); -bool ring_config_set_audio_manager(const std::string& api); -std::vector<std::string> ring_config_get_supported_audio_managers(void); -int ring_config_is_iax2_enabled(void); -std::string ring_config_get_record_path(void); -void ring_config_set_record_path(const std::string& path); -bool ring_config_is_always_recording(void); -void ring_config_set_always_recording(bool rec); -void ring_config_set_history_limit(int days); -int ring_config_get_history_limit(void); -void ring_config_set_accounts_order(const std::string& order); -std::map<std::string, std::string> ring_config_get_hook_settings(void); -void ring_config_set_hook_settings(const std::map<std::string, std::string>& settings); -std::map<std::string, std::string> ring_config_get_tls_settings(); -std::map<std::string, std::string> ring_config_validate_certificate(const std::string& accountId, - const std::string& certificate, const std::string& private_key); -std::map<std::string, std::string> ring_config_get_certificate_details(const std::string& certificate); -void ring_config_set_tls_settings(const std::map< std::string, std::string >& settings); -std::map<std::string, std::string> ring_config_get_ip2ip_details(void); -std::vector<std::map<std::string, std::string>> ring_config_get_credentials(const std::string& account_id); -void ring_config_set_credentials(const std::string& account_id, const std::vector<std::map<std::string, std::string>>& details); -std::string ring_config_get_addr_from_interface_name(const std::string& interface); -std::vector<std::string> ring_config_get_all_ip_interface(void); -std::vector<std::string> ring_config_get_all_ip_interface_by_name(void); -std::map<std::string, std::string> ring_config_get_shortcuts(); -void ring_config_set_shortcuts(const std::map<std::string, std::string>& shortcuts); -void ring_config_set_volume(const std::string& device, double value); -double ring_config_get_volume(const std::string& device); - -/* presence API */ -void ring_pres_publish(const std::string& account_id, int status, const std::string& note); -void ring_pres_answer_server_request(const std::string& uri, int flag); -void ring_pres_subscribe_buddy(const std::string& account_id, const std::string& uri, int flag); -std::vector<std::map<std::string, std::string>> ring_pres_get_subscriptions(const std::string& account_id); -void ring_pres_set_subscriptions(const std::string& account_id, const std::vector<std::string>& uris); - } // namespace DRing #endif /* DRING_H */ diff --git a/daemon/src/history/Makefile.am b/daemon/src/history/Makefile.am new file mode 100644 index 0000000000000000000000000000000000000000..4a405ae6307527070fa1dc1ee29aefacf605b7c8 --- /dev/null +++ b/daemon/src/history/Makefile.am @@ -0,0 +1,9 @@ +include ../../globals.mak + +noinst_LTLIBRARIES = libhistory.la + +libhistory_la_SOURCES = \ + historyitem.h \ + historyitem.cpp \ + history.h \ + history.cpp diff --git a/daemon/src/history/history.cpp b/daemon/src/history/history.cpp new file mode 100644 index 0000000000000000000000000000000000000000..48a6ce25e8db9dd62e3333ef9e0f8450cde77d17 --- /dev/null +++ b/daemon/src/history/history.cpp @@ -0,0 +1,172 @@ +/* + * Copyright (C) 2004-2015 Savoir-Faire Linux Inc. + * + * Author: Emmanuel Milou <emmanuel.milou@savoirfairelinux.com> + * Author: Alexandre Savard <alexandre.savard@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 + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Additional permission under GNU GPL version 3 section 7: + * + * If you modify this program, or any covered work, by linking or + * combining it with the OpenSSL project's OpenSSL library (or a + * modified version of that library), containing parts covered by the + * terms of the OpenSSL or SSLeay licenses, Savoir-Faire Linux Inc. + * grants you additional permission to convey the resulting work. + * Corresponding Source for a non-source form of such a combination + * shall include the source code for the parts of OpenSSL used as well + * as that of the covered work. + */ + +#include "history.h" +#include <cerrno> +#include <algorithm> +#include <fstream> +#include <sys/stat.h> // for mkdir +#include <ctime> +#include <cstring> +#include "fileutils.h" +#include "logger.h" +#include "call.h" + +namespace ring { + +History::History() : historyItemsMutex_(), items_(), path_() {} + + +using std::map; +using std::string; +using std::vector; + +bool History::load(int limit) +{ + // load only once + if (!items_.empty()) + return true; + + ensurePath(); + std::ifstream infile(path_.c_str()); + if (!infile) { + RING_DBG("No history file to load"); + return false; + } + + while (!infile.eof()) { + HistoryItem item(infile); + addEntry(item, limit); + } + + return true; +} + +bool History::save() +{ + std::lock_guard<std::mutex> lock(historyItemsMutex_); + RING_DBG("Saving history in XDG directory: %s", path_.c_str()); + ensurePath(); + std::sort(items_.begin(), items_.end()); + std::ofstream outfile(path_.c_str()); + if (outfile.fail()) + return false; + for (const auto &item : items_) + outfile << item << std::endl; + return true; +} + +void History::addEntry(const HistoryItem &item, int oldest) +{ + std::lock_guard<std::mutex> lock(historyItemsMutex_); + if (item.hasPeerNumber() and item.youngerThan(oldest)) { + items_.push_back(item); + auto im = item.toMap(); + string name(im["display_name"]); + string account(im["accountid"]); + string number(im["peer_number"]); + if (nameCache_[account][number].empty() and not name.empty() and not number.empty()) + nameCache_[account][number] = name; + } +} + +void History::ensurePath() +{ + if (path_.empty()) { + const string userdata = fileutils::get_data_dir(); + if (!fileutils::check_dir(userdata.c_str())) { + RING_DBG("Cannot create directory: %s", userdata.c_str()); + return; + } + path_ = userdata + DIR_SEPARATOR_STR + "history"; + } +} + +vector<map<string, string> > History::getSerialized() +{ + std::lock_guard<std::mutex> lock(historyItemsMutex_); + vector<map<string, string> > result; + for (const auto &item : items_) + result.push_back(item.toMap()); + + return result; +} + +void History::setPath(const std::string &path) +{ + path_ = path; +} + +void History::addCall(Call *call, int limit) +{ + if (!call) { + RING_ERR("Call is NULL, ignoring"); + return; + } + call->time_stop(); + HistoryItem item(call->createHistoryEntry()); + addEntry(item, limit); +} + +void History::clear() +{ + std::lock_guard<std::mutex> lock(historyItemsMutex_); + items_.clear(); +} + +bool History::empty() +{ + std::lock_guard<std::mutex> lock(historyItemsMutex_); + return items_.empty(); +} + + +size_t History::numberOfItems() +{ + std::lock_guard<std::mutex> lock(historyItemsMutex_); + return items_.size(); +} + +std::string +History::getNameFromHistory(const std::string &number, + const std::string &accountid) const { + const auto& it1 = nameCache_.find(accountid); + if (it1 == nameCache_.cend()) + return ""; + const auto& map = it1->second; + const auto& it2 = map.find(number); + if (it2 == map.cend()) + return ""; + return it2->second; +} + +} // namespace ring diff --git a/daemon/src/history/history.h b/daemon/src/history/history.h new file mode 100644 index 0000000000000000000000000000000000000000..6e4c2acab70fc5e54f0209576776e1bd55d05cce --- /dev/null +++ b/daemon/src/history/history.h @@ -0,0 +1,106 @@ +/* + * Copyright (C) 2004-2015 Savoir-Faire Linux Inc. + * + * Author: Emmanuel Milou <emmanuel.milou@savoirfairelinux.com> + * Author: Alexandre Savard <alexandre.savard@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 + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Additional permission under GNU GPL version 3 section 7: + * + * If you modify this program, or any covered work, by linking or + * combining it with the OpenSSL project's OpenSSL library (or a + * modified version of that library), containing parts covered by the + * terms of the OpenSSL or SSLeay licenses, Savoir-Faire Linux Inc. + * grants you additional permission to convey the resulting work. + * Corresponding Source for a non-source form of such a combination + * shall include the source code for the parts of OpenSSL used as well + * as that of the covered work. + */ + +#ifndef HISTORY_ +#define HISTORY_ + +#include "historyitem.h" +#include <mutex> +#include <vector> + +namespace ring { + +namespace test { +class HistoryTest; +} + +class Call; + +class History { + + public: + History(); + /** Load history from file */ + bool load(int limit); + + /** + *@return True if the history has been successfully saved in the file + */ + bool save(); + + /* + *@return The number of items found in the history file + */ + size_t numberOfItems(); + + bool empty(); + + std::vector<std::map<std::string, std::string> > getSerialized(); + + void addCall(Call *call, int limit); + void clear(); + void setPath(const std::string &path); + + std::string getNameFromHistory(const std::string &number, + const std::string &accountid) const; + + private: + /* Mutex to protect the history items */ + std::mutex historyItemsMutex_; + + /* If no path has been set, this will initialize path to a + * system-dependent location */ + void ensurePath(); + /* + * Add a new history item in the data structure + */ + void addEntry(const HistoryItem &new_item, int limit); + + /* + * Vector containing the history items + */ + std::vector<HistoryItem> items_; + + /* + * Reverse mapping of display name for given account id and peer number. + */ + std::map<std::string, std::map<std::string, std::string>> nameCache_ {}; + + /* The path to the history file */ + std::string path_; + + friend class test::HistoryTest; +}; + +} + +#endif // HISTORY_ diff --git a/daemon/src/history/historyitem.cpp b/daemon/src/history/historyitem.cpp new file mode 100644 index 0000000000000000000000000000000000000000..2bbc702df5fa8cf543de04badb22f3a689d1a9d7 --- /dev/null +++ b/daemon/src/history/historyitem.cpp @@ -0,0 +1,124 @@ +/* + * Copyright (C) 2004-2015 Savoir-Faire Linux Inc. + * + * Author: Emmanuel Milou <emmanuel.milou@savoirfairelinux.com> + * Author: Alexandre Savard <alexandre.savard@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 + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Additional permission under GNU GPL version 3 section 7: + * + * If you modify this program, or any covered work, by linking or + * combining it with the OpenSSL project's OpenSSL library (or a + * modified version of that library), containing parts covered by the + * terms of the OpenSSL or SSLeay licenses, Savoir-Faire Linux Inc. + * grants you additional permission to convey the resulting work. + * Corresponding Source for a non-source form of such a combination + * shall include the source code for the parts of OpenSSL used as well + * as that of the covered work. + */ + +#include "historyitem.h" +#include <unistd.h> +#include <cstdlib> +#include <istream> + +namespace ring { + +const char * const HistoryItem::ACCOUNT_ID_KEY = "accountid"; +const char * const HistoryItem::CALLID_KEY = "callid"; +const char * const HistoryItem::CONFID_KEY = "confid"; +const char * const HistoryItem::DISPLAY_NAME_KEY = "display_name"; +const char * const HistoryItem::PEER_NUMBER_KEY = "peer_number"; +const char * const HistoryItem::RECORDING_PATH_KEY = "recordfile"; +// FIXME: Deprecated +const char * const HistoryItem::STATE_KEY = "state"; +// New version: +const char * const HistoryItem::MISSED_KEY = "missed"; +const char * const HistoryItem::DIRECTION_KEY = "direction"; +const char * const HistoryItem::TIMESTAMP_START_KEY = "timestamp_start"; +const char * const HistoryItem::TIMESTAMP_STOP_KEY = "timestamp_stop"; +const char * const HistoryItem::AUDIO_CODEC_KEY = "audio_codec"; +const char * const HistoryItem::VIDEO_CODEC_KEY = "video_codec"; + +const char * const HistoryItem::MISSED_STRING = "missed"; +const char * const HistoryItem::INCOMING_STRING = "incoming"; +const char * const HistoryItem::OUTGOING_STRING = "outgoing"; + +using std::map; +using std::string; + +static bool +file_exists(const std::string &str) +{ + return access(str.c_str(), F_OK) != -1; +} + +HistoryItem::HistoryItem(const map<string, string> &args) : entryMap_(args), + timestampStart_(std::atol(entryMap_[TIMESTAMP_START_KEY].c_str())) +{} + +HistoryItem::HistoryItem(std::istream &entry) : entryMap_(), timestampStart_(0) +{ + string tmp; + while (std::getline(entry, tmp, '\n')) { + size_t pos = tmp.find('='); + if (pos == string::npos) + break; + else if (pos < tmp.length() - 1) { + string key(tmp.substr(0, pos)); + string val(tmp.substr(pos + 1, tmp.length() - pos - 1)); + if (key == RECORDING_PATH_KEY and not file_exists(val)) + val = ""; + entryMap_[key] = val; + } + } + timestampStart_ = std::atol(entryMap_[TIMESTAMP_START_KEY].c_str()); +} + +map<string, string> HistoryItem::toMap() const +{ + return entryMap_; +} + +bool HistoryItem::youngerThan(unsigned long otherTime) const +{ + return timestampStart_ > otherTime; +} + +bool HistoryItem::hasPeerNumber() const +{ + return entryMap_.find(PEER_NUMBER_KEY) != entryMap_.end(); +} + +void HistoryItem::print(std::ostream &o) const +{ + // every entry starts with "[" + random integer = "]" + for (const auto &item : entryMap_) { + // if the file does not exist anymore, we do not save it + if (item.first == RECORDING_PATH_KEY and not file_exists(item.second)) + o << item.first << "=" << "" << std::endl; + else + o << item.first << "=" << item.second << std::endl; + } +} + +std::ostream& operator << (std::ostream& o, const HistoryItem& item) +{ + item.print(o); + return o; +} + +} // namespace ring diff --git a/daemon/src/history/historyitem.h b/daemon/src/history/historyitem.h new file mode 100644 index 0000000000000000000000000000000000000000..a5cc81c4c1a06e627e331dfdfaac952f7f993697 --- /dev/null +++ b/daemon/src/history/historyitem.h @@ -0,0 +1,86 @@ +/* + * Copyright (C) 2004-2015 Savoir-Faire Linux Inc. + * + * Author: Emmanuel Milou <emmanuel.milou@savoirfairelinux.com> + * Author: Alexamdre Savard <alexandre.savard@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 + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Additional permission under GNU GPL version 3 section 7: + * + * If you modify this program, or any covered work, by linking or + * combining it with the OpenSSL project's OpenSSL library (or a + * modified version of that library), containing parts covered by the + * terms of the OpenSSL or SSLeay licenses, Savoir-Faire Linux Inc. + * grants you additional permission to convey the resulting work. + * Corresponding Source for a non-source form of such a combination + * shall include the source code for the parts of OpenSSL used as well + * as that of the covered work. + */ + +#ifndef HISTORY_ITEM_H_ +#define HISTORY_ITEM_H_ + +#include <string> +#include <map> + +namespace ring { + +class HistoryItem { + public: + static const char * const ACCOUNT_ID_KEY; + static const char * const CONFID_KEY; + static const char * const CALLID_KEY; + static const char * const DISPLAY_NAME_KEY; + static const char * const PEER_NUMBER_KEY; + static const char * const RECORDING_PATH_KEY; + static const char * const TIMESTAMP_START_KEY; + static const char * const TIMESTAMP_STOP_KEY; + static const char * const AUDIO_CODEC_KEY; + static const char * const VIDEO_CODEC_KEY; + static const char * const STATE_KEY; + static const char * const MISSED_KEY; + static const char * const DIRECTION_KEY; + + static const char * const MISSED_STRING; + static const char * const INCOMING_STRING; + static const char * const OUTGOING_STRING; + HistoryItem(const std::map<std::string, std::string> &args); + HistoryItem(std::istream &stream); + + bool hasPeerNumber() const; + + bool youngerThan(unsigned long otherTime) const; + + std::map<std::string, std::string> toMap() const; + void print(std::ostream &o) const; + bool operator< (const HistoryItem &other) const { + return timestampStart_ > other.timestampStart_; + } + + bool operator> (const HistoryItem &other) const { + return not (*this < other); + } + + private: + std::map<std::string, std::string> entryMap_; + unsigned long timestampStart_; // cached as we use this a lot, avoids string ops +}; + +std::ostream& operator << (std::ostream& o, const HistoryItem& item); + +} // namespace ring + +#endif // HISTORY_ITEM diff --git a/daemon/src/managerimpl.cpp b/daemon/src/managerimpl.cpp index c42b89b1399a768ced2f4ca7a9af7bbed31da54e..790718c3ecf8061da2b55258ade850cc5d7c5900 100644 --- a/daemon/src/managerimpl.cpp +++ b/daemon/src/managerimpl.cpp @@ -68,6 +68,7 @@ #include "audio/sound/audiofile.h" #include "audio/sound/dtmf.h" #include "audio/ringbufferpool.h" +#include "history/history.h" #include "manager.h" #ifdef RING_VIDEO @@ -161,7 +162,7 @@ ManagerImpl::ManagerImpl() : toneMutex_(), telephoneTone_(), audiofile_(), audioLayerMutex_(), waitingCalls_(), waitingCallsMutex_(), path_() , ringbufferpool_(new RingBufferPool) - , callFactory(), conferenceMap_() + , callFactory(), conferenceMap_(), history_() , accountFactory_(), ice_tf_() { // initialize random generator @@ -268,13 +269,15 @@ ManagerImpl::init(const std::string &config_file) } } + history_.load(preferences.getHistoryLimit()); + registerAccounts(); } void -ManagerImpl::setPath(const std::string&) +ManagerImpl::setPath(const std::string &path) { - // FIME: needed? + history_.setPath(path); } void @@ -294,7 +297,9 @@ ManagerImpl::finish() noexcept hangupCall(call->getCallId()); callFactory.clear(); + // Save accounts config and call's history saveConfig(); + saveHistory(); // Disconnect accounts, close link stacks and free allocated ressources unregisterAccounts(); @@ -403,20 +408,14 @@ ManagerImpl::outgoingCall(const std::string& preferred_account_id, detachParticipant(RingBufferPool::DEFAULT_ID); } - try { - /* RING_WARN: after this call the account_id is obsolete - * as the factory may decide to use another account (like IP2IP). - */ - RING_DBG("New outgoing call to %s", to_cleaned.c_str()); - call = newOutgoingCall(to_cleaned, preferred_account_id); - } catch (const std::exception &e) { - RING_ERR("%s", e.what()); - return {}; + // try to reverse match the peer name using the cache + if (call->getDisplayName().empty()) { + const auto& name = history_.getNameFromHistory(call->getPeerNumber(), + call->getAccountId()); + const std::string pseudo_contact_name(name); + if (not pseudo_contact_name.empty()) + call->setDisplayName(pseudo_contact_name); } - - if (not call) - return {}; - switchCall(call); call->setConfId(conf_id); @@ -527,8 +526,10 @@ ManagerImpl::hangupCall(const std::string& callId) } try { + history_.addCall(call.get(), preferences.getHistoryLimit()); call->hangup(0); checkAudio(); + saveHistory(); } catch (const VoipLinkException &e) { RING_ERR("%s", e.what()); return false; @@ -1673,7 +1674,9 @@ ManagerImpl::peerHungupCall(Call& call) unsetCurrentCall(); } + history_.addCall(&call, preferences.getHistoryLimit()); call.peerHungup(); + saveHistory(); emitSignal<DRing::CallSignal::StateChange>(call_id, "HUNGUP"); @@ -2609,6 +2612,12 @@ ManagerImpl::getCallDetails(const std::string &callID) } } +std::vector<std::map<std::string, std::string> > +ManagerImpl::getHistory() +{ + return history_.getSerialized(); +} + std::vector<std::string> ManagerImpl::getCallList() const { @@ -2678,6 +2687,21 @@ ManagerImpl::getConferenceId(const std::string& callID) return ""; } +void +ManagerImpl::saveHistory() +{ + if (!history_.save()) + RING_ERR("Could not save history!"); + else + emitSignal<DRing::ConfigurationSignal::AccountsChanged>(); +} + +void +ManagerImpl::clearHistory() +{ + history_.clear(); +} + void ManagerImpl::startAudioDriverStream() { diff --git a/daemon/src/managerimpl.h b/daemon/src/managerimpl.h index 572af9ebca812799824e3b0ddc25859b39f6a48d..6ca8eb607f7cbd2127e33fb875402bd7cab6fa24 100644 --- a/daemon/src/managerimpl.h +++ b/daemon/src/managerimpl.h @@ -58,6 +58,7 @@ #include "audio/sound/tone.h" // for Tone::TONEID declaration #include "preferences.h" +#include "history/history.h" #include "noncopyable.h" namespace ring { @@ -864,6 +865,9 @@ class ManagerImpl { */ bool hasCurrentCall() const; + std::vector<std::map<std::string, std::string> > getHistory(); + void clearHistory(); + /** * Get an account pointer, looks for both SIP and IAX * @param accountID account ID to get @@ -898,6 +902,8 @@ class ManagerImpl { */ void registerAccounts(); + void saveHistory(); + /** * Suspends Ring's audio processing if no calls remain, allowing * other applications to resume audio. @@ -964,6 +970,11 @@ class ManagerImpl { // Map containing conference pointers ConferenceMap conferenceMap_; + /** + * To handle the persistent history + * TODO: move this to ConfigurationManager + */ + History history_; std::atomic_bool finished_ {false}; AccountFactory accountFactory_; diff --git a/daemon/src/sip/sipcall.cpp b/daemon/src/sip/sipcall.cpp index 1de9ce876cfc921ad80a86c2f85a1309ec111619..2e06135ccc580772239cdd005682bdd1162a167a 100644 --- a/daemon/src/sip/sipcall.cpp +++ b/daemon/src/sip/sipcall.cpp @@ -183,6 +183,13 @@ void SIPCall::setContactHeader(pj_str_t *contact) pj_strcpy(&contactHeader_, contact); } +std::map<std::string, std::string> +SIPCall::createHistoryEntry() const +{ + std::map<std::string, std::string> entry(Call::createHistoryEntry()); + return entry; +} + /** * Send a reINVITE inside an active dialog to modify its state * Local SDP session should be modified before calling this method diff --git a/daemon/src/sip/sipcall.h b/daemon/src/sip/sipcall.h index a3499dd243ec06e12bc194b8801631e5d00425d3..c9278e6135c2a8a95cb5ad5c6fe9b0db3a155ff1 100644 --- a/daemon/src/sip/sipcall.h +++ b/daemon/src/sip/sipcall.h @@ -196,6 +196,10 @@ class SIPCall : public Call private: NON_COPYABLE(SIPCall); + // override of Call::createHistoryEntry + std::map<std::string, std::string> + createHistoryEntry() const; + void stopAllMedia(); /**