Commit dc183842 authored by DanmeiChen's avatar DanmeiChen
Browse files

Merge branch 'feature/auto_download_incoming_messages_files' into dev_lime_v2

parents f96d2d85 54cd32b9
......@@ -1443,6 +1443,8 @@ static void sip_config_read(LinphoneCore *lc) {
tmp=lp_config_get_int(lc->config,"sip","delayed_timeout",4);
linphone_core_set_delayed_timeout(lc,tmp);
tmp=lp_config_get_int(lc->config,"app","auto_download_incoming_files_max_size",-1);
linphone_core_set_max_size_for_auto_download_incoming_files(lc, tmp);
/*In case of remote provisionning, function sip_config_read is initialy called in core_init, then in state ConfiguringSuccessfull*/
/*Accordingly, to avoid proxy_config to be added twice, it is mandatory to reset proxy config list from LinphoneCore*/
......@@ -3978,6 +3980,15 @@ void linphone_core_set_delayed_timeout(LinphoneCore *lc, int seconds){
lc->sip_conf.delayed_timeout=seconds;
}
int linphone_core_get_max_size_for_auto_download_incoming_files(LinphoneCore *lc) {
return lc->auto_download_incoming_files_max_size;
}
void linphone_core_set_max_size_for_auto_download_incoming_files(LinphoneCore *lc, int size) {
lc->auto_download_incoming_files_max_size = size;
lp_config_set_int(lc->config, "app", "auto_download_incoming_files_max_size", size);
}
void linphone_core_set_presence_info(LinphoneCore *lc, int minutes_away, const char *contact, LinphoneOnlineStatus os) {
LinphonePresenceModel *presence = NULL;
LinphonePresenceActivity *activity = NULL;
......
......@@ -821,6 +821,7 @@ namespace LinphonePrivate {
bctbx_list_t *chat_rooms; \
bctbx_list_t *callsCache; \
bool_t dns_set_by_app; \
int auto_download_incoming_files_max_size; \
#define LINPHONE_CORE_STRUCT_FIELDS \
LINPHONE_CORE_STRUCT_BASE_FIELDS \
......
......@@ -5675,7 +5675,21 @@ LINPHONE_PUBLIC void linphone_core_notify_notify_presence_received(LinphoneCore
*/
LINPHONE_PUBLIC void linphone_core_notify_notify_presence_received_for_uri_or_tel(LinphoneCore *lc, LinphoneFriend *lf, const char *uri_or_tel, const LinphonePresenceModel *presence_model);
/**
* Sets the size under which incoming files in chat messages will be downloaded automatically.
* @param[in] lc #LinphoneCore object
* @param[in] size The size in bytes, -1 to disable the autodownload feature, 0 to download them all no matter the size
* @ingroup chat
**/
LINPHONE_PUBLIC void linphone_core_set_max_size_for_auto_download_incoming_files(LinphoneCore *lc, int size);
/**
* Gets the size under which incoming files in chat messages will be downloaded automatically.
* @param[in] lc #LinphoneCore object
* @return The size in bytes, -1 if autodownload feature is disabled, 0 to download them all no matter the size
* @ingroup chat
**/
LINPHONE_PUBLIC int linphone_core_get_max_size_for_auto_download_incoming_files(LinphoneCore *lc);
/**
* @}
......
......@@ -54,8 +54,9 @@ public:
Encryption = 1 << 3,
Cpim = 1 << 4,
Started = 1 << 5,
FileDownload = 1 << 6,
AutoFileDownload = 1 << 7,
Sent = 1 << 6,
FileDownload = 1 << 7,
};
void setApplyModifiers (bool value) { applyModifiers = value; }
......@@ -103,6 +104,14 @@ public:
return contents;
}
void setAutoFileTransferDownloadHappened(bool yesno) {
isAutoDownloadAttachedFilesHappened = yesno;
}
bool isAutoFileTransferDownloadHappened() const {
return isAutoDownloadAttachedFilesHappened;
}
belle_http_request_t *getHttpRequest () const;
void setHttpRequest (belle_http_request_t *request);
......@@ -133,6 +142,9 @@ public:
void setEncryptionPrevented (bool value) { encryptionPrevented = value; }
void doNotRetryAutoDownload() {
currentRecvStep |= ChatMessagePrivate::Step::AutoFileDownload;
}
void enableSenderAuthentication (bool value) { senderAuthenticationEnabled = value; }
void setUnencryptedContentWarning (bool value) { unencryptedContentWarning = value; }
......@@ -202,6 +214,7 @@ private:
bool isSecured = false;
mutable bool isReadOnly = false;
Content internalContent;
bool isAutoDownloadAttachedFilesHappened = false;
// TODO: to replace salCustomheaders
std::unordered_map<std::string, std::string> customHeaders;
......
......@@ -586,6 +586,27 @@ LinphoneReason ChatMessagePrivate::receive () {
currentRecvStep |= ChatMessagePrivate::Step::FileDownload;
}
if ((currentRecvStep & ChatMessagePrivate::Step::AutoFileDownload) == ChatMessagePrivate::Step::AutoFileDownload) {
lInfo() << "Auto file download step already done, skipping";
} else {
for (Content *c : contents) {
if (c->isFileTransfer()) {
int max_size = linphone_core_get_max_size_for_auto_download_incoming_files(q->getCore()->getCCore());
if (max_size >= 0) {
FileTransferContent *ftc = static_cast<FileTransferContent *>(c);
if (max_size == 0 || ftc->getFileSize() <= (size_t)max_size) {
ftc->setFilePath(q->getCore()->getDownloadPath() + ftc->getFileName());
setAutoFileTransferDownloadHappened(true);
q->downloadFile(ftc);
return LinphoneReasonNone;
}
}
}
}
currentRecvStep |= ChatMessagePrivate::Step::AutoFileDownload;
q->getChatRoom()->getPrivate()->removeTransientChatMessage(q->getSharedFromThis());
}
if (contents.size() == 0) {
// All previous modifiers only altered the internal content, let's fill the content list
contents.push_back(new Content(internalContent));
......@@ -606,7 +627,9 @@ LinphoneReason ChatMessagePrivate::receive () {
setState(ChatMessage::State::Delivered);
if (errorCode <= 0) {
if (errorCode <= 0 && !isAutoFileTransferDownloadHappened()) {
// if auto download happened and message contains only file transfer,
// the following will state that the content type of the file is unsupported
bool foundSupportContentType = false;
for (Content *c : contents) {
ContentType ct(c->getContentType());
......@@ -623,6 +646,8 @@ LinphoneReason ChatMessagePrivate::receive () {
lError() << "No content-type in the contents list is supported...";
}
}
// If auto download failed, reset this flag so the user can normally download the file later
setAutoFileTransferDownloadHappened(false);
// Check if this is in fact an outgoing message (case where this is a message sent by us from an other device).
if (
......
......@@ -54,6 +54,9 @@ public:
virtual LinphoneReason onSipMessageReceived (SalOp *op, const SalMessage *message) = 0;
virtual void onChatMessageReceived (const std::shared_ptr<ChatMessage> &chatMessage) = 0;
virtual void addTransientChatMessage (const std::shared_ptr<ChatMessage> &message) = 0;
virtual void removeTransientChatMessage (const std::shared_ptr<ChatMessage> &message) = 0;
};
LINPHONE_END_NAMESPACE
......
......@@ -57,6 +57,9 @@ public:
void addTransientEvent (const std::shared_ptr<EventLog> &eventLog) override;
void removeTransientEvent (const std::shared_ptr<EventLog> &eventLog) override;
void addTransientChatMessage (const std::shared_ptr<ChatMessage> &message) override;
void removeTransientChatMessage (const std::shared_ptr<ChatMessage> &message) override;
std::shared_ptr<ChatMessage> createChatMessage (ChatMessage::Direction direction);
std::shared_ptr<ImdnMessage> createImdnMessage (
const std::list<std::shared_ptr<ChatMessage>> &deliveredMessages,
......@@ -91,6 +94,7 @@ public:
std::list<IdentityAddress> remoteIsComposing;
std::list<std::shared_ptr<EventLog>> transientEvents;
std::list<std::shared_ptr<ChatMessage>> transientMessages;
ConferenceId conferenceId;
......
......@@ -20,6 +20,7 @@
#include <algorithm>
#include "linphone/utils/utils.h"
#include "linphone/utils/algorithm.h"
#include "c-wrapper/c-wrapper.h"
#include "chat/chat-message/chat-message-p.h"
......@@ -92,17 +93,29 @@ void ChatRoomPrivate::addEvent (const shared_ptr<EventLog> &eventLog) {
}
void ChatRoomPrivate::addTransientEvent (const shared_ptr<EventLog> &eventLog) {
auto it = find(transientEvents.begin(), transientEvents.end(), eventLog);
auto it = find(transientEvents, eventLog);
if (it == transientEvents.end())
transientEvents.push_back(eventLog);
}
void ChatRoomPrivate::removeTransientEvent (const shared_ptr<EventLog> &eventLog) {
auto it = find(transientEvents.begin(), transientEvents.end(), eventLog);
auto it = find(transientEvents, eventLog);
if (it != transientEvents.end())
transientEvents.erase(it);
}
void ChatRoomPrivate::addTransientChatMessage (const shared_ptr<ChatMessage> &message) {
auto it = find(transientMessages, message);
if (it == transientMessages.end())
transientMessages.push_back(message);
}
void ChatRoomPrivate::removeTransientChatMessage (const shared_ptr<ChatMessage> &message) {
auto it = find(transientMessages, message);
if (it != transientMessages.end())
transientMessages.erase(it);
}
// -----------------------------------------------------------------------------
shared_ptr<ChatMessage> ChatRoomPrivate::createChatMessage (ChatMessage::Direction direction) {
......@@ -278,6 +291,7 @@ LinphoneReason ChatRoomPrivate::onSipMessageReceived (SalOp *op, const SalMessag
if (ch)
msg->getPrivate()->setSalCustomHeaders(sal_custom_header_clone(ch));
addTransientChatMessage(msg);
reason = msg->getPrivate()->receive();
return reason;
}
......
......@@ -57,6 +57,14 @@ public:
chatRoom->getPrivate()->removeTransientEvent(eventLog);
}
inline void addTransientChatMessage (const std::shared_ptr<ChatMessage> &message) override {
chatRoom->getPrivate()->addTransientChatMessage(message);
}
inline void removeTransientChatMessage (const std::shared_ptr<ChatMessage> &message) override {
chatRoom->getPrivate()->removeTransientChatMessage(message);
}
inline void sendDeliveryNotifications () override {
chatRoom->getPrivate()->sendDeliveryNotifications();
}
......
......@@ -800,7 +800,12 @@ void FileTransferChatMessageModifier::onRecvEnd (belle_sip_user_body_handler_t *
}
}
}
message->getPrivate()->setState(ChatMessage::State::FileTransferDone);
if (message->getPrivate()->isAutoFileTransferDownloadHappened()) {
releaseHttpRequest();
message->getPrivate()->receive();
} else {
message->getPrivate()->setState(ChatMessage::State::FileTransferDone);
}
}
}
......@@ -873,6 +878,20 @@ void FileTransferChatMessageModifier::processResponseHeadersFromGetFile (const b
}
}
void FileTransferChatMessageModifier::onDownloadFailed() {
shared_ptr<ChatMessage> message = chatMessage.lock();
if (!message)
return;
if (message->getPrivate()->isAutoFileTransferDownloadHappened()) {
message->getPrivate()->doNotRetryAutoDownload();
releaseHttpRequest();
message->getPrivate()->receive();
} else {
message->getPrivate()->setState(ChatMessage::State::FileTransferError);
releaseHttpRequest();
}
}
static void _chat_message_process_auth_requested_download (void *data, belle_sip_auth_event *event) {
FileTransferChatMessageModifier *d = (FileTransferChatMessageModifier *)data;
d->processAuthRequestedDownload(event);
......@@ -880,11 +899,7 @@ static void _chat_message_process_auth_requested_download (void *data, belle_sip
void FileTransferChatMessageModifier::processAuthRequestedDownload (const belle_sip_auth_event *event) {
lError() << "Error during file download : auth requested for msg [" << this << "]";
shared_ptr<ChatMessage> message = chatMessage.lock();
if (!message)
return;
message->getPrivate()->setState(ChatMessage::State::FileTransferError);
releaseHttpRequest();
onDownloadFailed();
}
static void _chat_message_process_io_error_download (void *data, const belle_sip_io_error_event_t *event) {
......@@ -894,11 +909,7 @@ static void _chat_message_process_io_error_download (void *data, const belle_sip
void FileTransferChatMessageModifier::processIoErrorDownload (const belle_sip_io_error_event_t *event) {
lError() << "I/O Error during file download msg [" << this << "]";
shared_ptr<ChatMessage> message = chatMessage.lock();
if (!message)
return;
message->getPrivate()->setState(ChatMessage::State::FileTransferError);
releaseHttpRequest();
onDownloadFailed();
}
static void _chat_message_process_response_from_get_file (void *data, const belle_http_response_event_t *event) {
......@@ -916,11 +927,10 @@ void FileTransferChatMessageModifier::processResponseFromGetFile (const belle_ht
int code = belle_http_response_get_status_code(event->response);
if (code >= 400 && code < 500) {
lWarning() << "File transfer failed with code " << code;
message->getPrivate()->setState(ChatMessage::State::FileTransferError);
onDownloadFailed();
} else if (code != 200) {
lWarning() << "Unhandled HTTP code response " << code << " for file transfer";
}
releaseHttpRequest();
}
}
......
......@@ -71,6 +71,7 @@ private:
void fileUploadBeginBackgroundTask ();
void fileUploadEndBackgroundTask ();
void onDownloadFailed ();
void releaseHttpRequest ();
std::weak_ptr<ChatMessage> chatMessage;
......
/*
* core.cpp
* Copyright (C) 2010-2018 Belledonne Communications SARL
*
* 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 2
* 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.
*/
#include <mediastreamer2/mscommon.h>
#include <xercesc/util/PlatformUtils.hpp>
#include "address/address-p.h"
#include "call/call.h"
#include "chat/encryption/lime-x3dh-encryption-engine.h"
#include "conference/handlers/local-conference-list-event-handler.h"
#include "conference/handlers/remote-conference-list-event-handler.h"
#include "core/core-listener.h"
#include "core/core-p.h"
#include "logger/logger.h"
#include "paths/paths.h"
// TODO: Remove me later.
#include "c-wrapper/c-wrapper.h"
#include "private.h"
#define LINPHONE_DB "linphone.db"
// =============================================================================
using namespace std;
LINPHONE_BEGIN_NAMESPACE
void CorePrivate::init () {
L_Q();
mainDb.reset(new MainDb(q->getSharedFromThis()));
remoteListEventHandler = makeUnique<RemoteConferenceListEventHandler>(q->getSharedFromThis());
localListEventHandler = makeUnique<LocalConferenceListEventHandler>(q->getSharedFromThis());
AbstractDb::Backend backend;
string uri = L_C_TO_STRING(lp_config_get_string(linphone_core_get_config(L_GET_C_BACK_PTR(q)), "storage", "uri", nullptr));
if (!uri.empty())
backend = strcmp(lp_config_get_string(linphone_core_get_config(L_GET_C_BACK_PTR(q)), "storage", "backend", nullptr), "mysql") == 0
? MainDb::Mysql
: MainDb::Sqlite3;
else {
backend = AbstractDb::Sqlite3;
uri = q->getDataPath() + LINPHONE_DB;
/*
* core.cpp
* Copyright (C) 2010-2018 Belledonne Communications SARL
*
* 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 2
* 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.
*/
#include <mediastreamer2/mscommon.h>
#include <xercesc/util/PlatformUtils.hpp>
#include "address/address-p.h"
#include "call/call.h"
#include "chat/encryption/lime-x3dh-encryption-engine.h"
#include "conference/handlers/local-conference-list-event-handler.h"
#include "conference/handlers/remote-conference-list-event-handler.h"
#include "core/core-listener.h"
#include "core/core-p.h"
#include "logger/logger.h"
#include "paths/paths.h"
// TODO: Remove me later.
#include "c-wrapper/c-wrapper.h"
#include "private.h"
#define LINPHONE_DB "linphone.db"
// =============================================================================
using namespace std;
LINPHONE_BEGIN_NAMESPACE
void CorePrivate::init () {
L_Q();
mainDb.reset(new MainDb(q->getSharedFromThis()));
remoteListEventHandler = makeUnique<RemoteConferenceListEventHandler>(q->getSharedFromThis());
localListEventHandler = makeUnique<LocalConferenceListEventHandler>(q->getSharedFromThis());
AbstractDb::Backend backend;
string uri = L_C_TO_STRING(lp_config_get_string(linphone_core_get_config(L_GET_C_BACK_PTR(q)), "storage", "uri", nullptr));
if (!uri.empty())
backend = strcmp(lp_config_get_string(linphone_core_get_config(L_GET_C_BACK_PTR(q)), "storage", "backend", nullptr), "mysql") == 0
? MainDb::Mysql
: MainDb::Sqlite3;
else {
backend = AbstractDb::Sqlite3;
uri = q->getDataPath() + LINPHONE_DB;
}
lInfo() << "Opening linphone database: " << uri;
if (!mainDb->connect(backend, uri))
lFatal() << "Unable to open linphone database.";
loadChatRooms();
}
lInfo() << "Opening linphone database: " << uri;
if (!mainDb->connect(backend, uri))
lFatal() << "Unable to open linphone database.";
void CorePrivate::registerListener (CoreListener *listener) {
listeners.push_back(listener);
}
loadChatRooms();
}
void CorePrivate::unregisterListener (CoreListener *listener) {
listeners.remove(listener);
}
void CorePrivate::registerListener (CoreListener *listener) {
listeners.push_back(listener);
}
void CorePrivate::uninit () {
L_Q();
while (!calls.empty()) {
calls.front()->terminate();
linphone_core_iterate(L_GET_C_BACK_PTR(q));
ms_usleep(10000);
}
void CorePrivate::unregisterListener (CoreListener *listener) {
listeners.remove(listener);
}
chatRooms.clear();
chatRoomsById.clear();
noCreatedClientGroupChatRooms.clear();
void CorePrivate::uninit () {
L_Q();
while (!calls.empty()) {
calls.front()->terminate();
linphone_core_iterate(L_GET_C_BACK_PTR(q));
ms_usleep(10000);
}
remoteListEventHandler = nullptr;
localListEventHandler = nullptr;
chatRooms.clear();
chatRoomsById.clear();
noCreatedClientGroupChatRooms.clear();
remoteListEventHandler = nullptr;
localListEventHandler = nullptr;
AddressPrivate::clearSipAddressesCache();
}
AddressPrivate::clearSipAddressesCache();
}
// -----------------------------------------------------------------------------
// -----------------------------------------------------------------------------
void CorePrivate::notifyGlobalStateChanged (LinphoneGlobalState state) {
auto listenersCopy = listeners; // Allow removable of a listener in its own call
for (const auto &listener : listenersCopy)
listener->onGlobalStateChanged(state);
}
void CorePrivate::notifyGlobalStateChanged (LinphoneGlobalState state) {
auto listenersCopy = listeners; // Allow removable of a listener in its own call
for (const auto &listener : listenersCopy)
listener->onGlobalStateChanged(state);
}
void CorePrivate::notifyNetworkReachable (bool sipNetworkReachable, bool mediaNetworkReachable) {
auto listenersCopy = listeners; // Allow removable of a listener in its own call
for (const auto &listener : listenersCopy)
listener->onNetworkReachable(sipNetworkReachable, mediaNetworkReachable);
}
void CorePrivate::notifyNetworkReachable (bool sipNetworkReachable, bool mediaNetworkReachable) {
auto listenersCopy = listeners; // Allow removable of a listener in its own call
for (const auto &listener : listenersCopy)
listener->onNetworkReachable(sipNetworkReachable, mediaNetworkReachable);
}
void CorePrivate::notifyRegistrationStateChanged (LinphoneProxyConfig *cfg, LinphoneRegistrationState state, const string &message) {
auto listenersCopy = listeners; // Allow removable of a listener in its own call
for (const auto &listener : listenersCopy)
listener->onRegistrationStateChanged(cfg, state, message);
}
void CorePrivate::notifyRegistrationStateChanged (LinphoneProxyConfig *cfg, LinphoneRegistrationState state, const string &message) {
auto listenersCopy = listeners; // Allow removable of a listener in its own call
for (const auto &listener : listenersCopy)
listener->onRegistrationStateChanged(cfg, state, message);
}
void CorePrivate::notifyEnteringBackground () {
if (isInBackground)
return;
void CorePrivate::notifyEnteringBackground () {
if (isInBackground)
return;
isInBackground = true;
auto listenersCopy = listeners; // Allow removable of a listener in its own call
for (const auto &listener : listenersCopy)
listener->onEnteringBackground();
}
isInBackground = true;
auto listenersCopy = listeners; // Allow removable of a listener in its own call
for (const auto &listener : listenersCopy)
listener->onEnteringBackground();
}
void CorePrivate::notifyEnteringForeground () {
if (!isInBackground)
return;
void CorePrivate::notifyEnteringForeground () {
if (!isInBackground)
return;
isInBackground = false;
auto listenersCopy = listeners; // Allow removable of a listener in its own call
for (const auto &listener : listenersCopy)
listener->onEnteringForeground();
}
isInBackground = false;
auto listenersCopy = listeners; // Allow removable of a listener in its own call
for (const auto &listener : listenersCopy)
listener->onEnteringForeground();
}
// =============================================================================
// =============================================================================
Core::Core () : Object(*new CorePrivate) {
L_D();
d->imee.reset();
xercesc::XMLPlatformUtils::Initialize();
}
Core::Core () : Object(*new CorePrivate) {
L_D();
d->imee.reset();
xercesc::XMLPlatformUtils::Initialize();
}
Core::~Core () {
lInfo() << "Destroying core: " << this;
xercesc::XMLPlatformUtils::Terminate();
}
Core::~Core () {
lInfo() << "Destroying core: " << this;
xercesc::XMLPlatformUtils::Terminate();
}
shared_ptr<Core> Core::create (LinphoneCore *cCore) {
// Do not use `make_shared` => Private constructor.
shared_ptr<Core> core = shared_ptr<Core>(new Core);