diff --git a/src/api/datatransfermodel.h b/src/api/datatransfermodel.h
index 250f6dfd4cb70699d7202134466914e0a378c187..9394cf51c92e06399042a407570bdcc9e6c37b5e 100644
--- a/src/api/datatransfermodel.h
+++ b/src/api/datatransfermodel.h
@@ -77,6 +77,8 @@ public:
                   const QString& fileId,
                   const QString& path);
 
+    void copyTo(const QString& accountId, const QString& convId, const QString& interactionId, const QString& destPath);
+
     void cancel(const QString& accountId, const QString& conversationId, const QString& fileId);
 
     void registerTransferId(const QString& fileId, const QString& interactionId);
diff --git a/src/chatview.cpp b/src/chatview.cpp
index 57f3d5abe07f13093bc7ef7bb683c32526e076e9..536394b3435faf1a0b9db3dcaeb1218ecae18175 100644
--- a/src/chatview.cpp
+++ b/src/chatview.cpp
@@ -41,6 +41,7 @@ getTranslatedStrings(bool qwebview)
         {"Leave video message", QObject::tr("Leave video message")},
         {"Leave audio message", QObject::tr("Leave audio message")},
         {"Block", QObject::tr("Block")},
+        {"Copy to downloads", QObject::tr("Copy to downloads")},
         {"Write to {0}", QObject::tr("Write to {0}")},
         {"Note: an interaction will create a new contact.",
          QObject::tr("Note: an interaction will create a new contact.")},
diff --git a/src/datatransfermodel.cpp b/src/datatransfermodel.cpp
index 146f6ba0249628b277de5c47f28739c5445310cc..2df7baf0c24ae0609b5f2b1e8d057b51ce2f37d4 100644
--- a/src/datatransfermodel.cpp
+++ b/src/datatransfermodel.cpp
@@ -79,7 +79,7 @@ class DataTransferModel::Impl : public QObject
 public:
     Impl(DataTransferModel& up_link);
 
-    QString getUniqueFilePath(const QString& filename);
+    QString getUniqueFilePath(const QString& filename, const QString& path = "");
 
     DataTransferModel& upLink;
     MapStringString file2InteractionId;
@@ -92,16 +92,21 @@ DataTransferModel::Impl::Impl(DataTransferModel& up_link)
 {}
 
 QString
-DataTransferModel::Impl::getUniqueFilePath(const QString& filename)
+DataTransferModel::Impl::getUniqueFilePath(const QString& filename, const QString& path)
 {
-    if (!QFile::exists(filename)) {
-        return filename;
-    }
-    QString base(filename);
-    QString ext = QFileInfo(filename).completeSuffix();
+    auto wantedDest = filename;
+    QString base(wantedDest);
+    QString ext = QFileInfo(wantedDest).completeSuffix();
     if (!ext.isEmpty()) {
         ext = ext.prepend(".");
     }
+    if (!path.isEmpty()) {
+        QFileInfo fi(filename);
+        wantedDest = QDir(path).filePath(fi.baseName() + ext);
+    }
+    if (!QFile::exists(wantedDest)) {
+        return wantedDest;
+    }
     base.chop(ext.size());
     QString ret;
     for (int suffix = 1;; suffix++) {
@@ -202,7 +207,7 @@ QString
 DataTransferModel::accept(const QString& accountId,
                           const QString& fileId,
                           const QString& file_path,
-                          std::size_t offset)
+                          std::size_t)
 {
     auto unique_file_path = pimpl_->getUniqueFilePath(file_path);
     auto dring_id = pimpl_->interactionToFileId[fileId];
@@ -220,12 +225,35 @@ DataTransferModel::download(const QString& accountId,
     ConfigurationManager::instance().downloadFile(accountId, convId, interactionId, fileId, path);
 }
 
+void
+DataTransferModel::copyTo(const QString& accountId, const QString& convId, const QString& interactionId, const QString& destPath)
+{
+    auto fileId = getFileIdFromInteractionId(interactionId);
+    if (fileId.isEmpty()) {
+        qWarning() << "Cannot find any file for " << interactionId;
+        return;
+    }
+    QString path;
+    qlonglong total, progress;
+
+    fileTransferInfo(accountId, convId, fileId, path, total, progress);
+
+    auto src = QFile(path);
+    auto srcfi = QFileInfo(path);
+    if (!src.exists())
+        return;
+
+    auto realPath = srcfi.isSymLink() ? srcfi.symLinkTarget() : path;
+    auto dest = pimpl_->getUniqueFilePath(realPath, destPath);
+    src.copy(dest);
+}
+
+
 void
 DataTransferModel::cancel(const QString& accountId,
                           const QString& conversationId,
                           const QString& interactionId)
 {
-    qWarning() << "@@@ " << accountId << " - " << conversationId << " - " << interactionId;
     ConfigurationManager::instance().cancelDataTransfer(accountId,
                                                         conversationId,
                                                         getFileIdFromInteractionId(interactionId));
diff --git a/src/web-chatview/chatview.css b/src/web-chatview/chatview.css
index 93dc8c4cba15be90cd1b8a852bb417a9aa11c0e4..b316af1c2e059aa52346ed5b00c9ca6b74c556d1 100644
--- a/src/web-chatview/chatview.css
+++ b/src/web-chatview/chatview.css
@@ -831,6 +831,37 @@ input[type=checkbox] {
     opacity: 1;
 }
 
+
+.message_type_call .menu_interaction .dropdown
+.message_type_contact .menu_interaction .dropdown
+{
+    margin-top: -17px;
+}
+
+.dropdown {
+    display: none;
+    z-index: 1;
+    position: absolute;
+    background-color: #fff;
+    padding-top: 3px;
+    padding-bottom: 3px;
+}
+
+.dropdown div
+{
+    color: #111;
+    padding: 10px;
+}
+
+.dropdown div:hover
+{
+    background-color: #ddd;
+}
+
+.showmenu:checked ~ .dropdown{
+    display: block;
+}
+
 /* Buttons */
 
 .flat-button {
diff --git a/src/web-chatview/chatview.js b/src/web-chatview/chatview.js
index 6de8f7b751dc1f6b34e1340f979da80cfc142425..3ff25767d471a8487b284106c83cded4e1f09aff 100644
--- a/src/web-chatview/chatview.js
+++ b/src/web-chatview/chatview.js
@@ -446,7 +446,7 @@ function showInvitation(contactAlias, contactId, isSwarm, isSyncing) {
             invitationNoteText.style.visibility = "visible"
             messageBar.style.visibility = "visible"
         }
-        
+
         invitation.style.display = "flex"
         invitation.style.visibility = "visible"
 
@@ -1324,6 +1324,12 @@ function updateFileInteraction(message_div, message_object, forceTypeToFile = fa
     message_div.querySelector(".full").innerText = message_text
     message_div.querySelector(".filename").innerText = message_text.split("/").pop()
     updateProgressBar(message_div.querySelector(".message_progress_bar"), message_object)
+
+
+    if (delivery_status === "finished") {
+        var message_dropdown = buildMessageDropdown(message_id)
+        message_div.appendChild(message_dropdown)
+    }
 }
 
 /**
@@ -1634,6 +1640,57 @@ function textInteraction(message_id, message_direction, htmlText) {
     return internal_mes_wrapper
 }
 
+/**
+ * Build message dropdown
+ * @return a message dropdown for passed message id
+ */
+function buildMessageDropdown(message_id) {
+    const menu_element = document.createElement("div")
+    menu_element.setAttribute("class", "menu_interaction")
+    menu_element.innerHTML =
+        `<input type="checkbox" id="showmenu${message_id}" class="showmenu">
+     <label for="showmenu${message_id}">
+       <svg fill="#888888" height="12" viewBox="0 0 24 24" width="12" xmlns="http://www.w3.org/2000/svg">
+         <path d="M0 0h24v24H0z" fill="none"/>
+         <path d="M6 10c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2zm12 0c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2zm-6 0c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2z"/>
+       </svg>
+     </label>`
+    menu_element.onclick = function () {
+        const button = this.querySelector(".showmenu")
+        button.checked = !button.checked
+    }
+    menu_element.onmouseleave = function () {
+        const button = this.querySelector(".showmenu")
+        button.checked = false
+    }
+    const dropdown = document.createElement("div")
+    const dropdown_classes = [
+        "dropdown",
+        `dropdown_${message_id}`
+    ]
+    dropdown.setAttribute("class", dropdown_classes.join(" "))
+
+    const save = document.createElement("div")
+    save.setAttribute("class", "menuoption")
+    if (use_qt) {
+        save.innerHTML = "Copy to downloads"
+    } else {
+        save.innerHTML = i18n.gettext("Copy to downloads")
+    }
+    save.msg_id = message_id
+    save.onclick = function () {
+        if (use_qt) {
+            window.jsbridge.copyToDownloads(`${this.msg_id}`)
+        } else {
+            window.prompt(`COPY:${this.msg_id}`)
+        }
+    }
+    dropdown.appendChild(save)
+    menu_element.appendChild(dropdown)
+
+    return menu_element
+}
+
 /**
  * Update a text interaction (text)
  * @param message_div the message to update
@@ -1832,6 +1889,10 @@ function buildNewMessage(message_object) {
             message_div.append(fileInteraction(message_id))
             updateProgressBar(message_div.querySelector(".message_progress_bar"), message_object)
         }
+        if (delivery_status === "finished") {
+            var message_dropdown = buildMessageDropdown(message_id)
+            message_div.appendChild(message_dropdown)
+        }
     } else if (message_type === "text") {
         // TODO add the possibility to update messages (remove and rebuild)
         const htmlText = getMessageHtml(message_text)