2018-10-18 02:05:02 +02:00
|
|
|
(ns ^{:doc "Mailserver events and API"}
|
|
|
|
status-im.mailserver.core
|
2019-09-04 16:43:01 +02:00
|
|
|
(:require [clojure.string :as string]
|
|
|
|
[re-frame.core :as re-frame]
|
|
|
|
[status-im.data-store.mailservers :as data-store.mailservers]
|
|
|
|
[status-im.ethereum.json-rpc :as json-rpc]
|
2019-10-22 02:05:15 +02:00
|
|
|
[status-im.node.core :as node]
|
2019-09-04 16:43:01 +02:00
|
|
|
[status-im.i18n :as i18n]
|
|
|
|
[status-im.mailserver.constants :as constants]
|
|
|
|
[status-im.mailserver.topics :as mailserver.topics]
|
|
|
|
[status-im.multiaccounts.model :as multiaccounts.model]
|
|
|
|
[status-im.multiaccounts.update.core :as multiaccounts.update]
|
2018-10-18 02:05:02 +02:00
|
|
|
[status-im.native-module.core :as status]
|
2019-09-04 16:43:01 +02:00
|
|
|
[status-im.transport.message.protocol :as protocol]
|
2018-10-18 02:05:02 +02:00
|
|
|
[status-im.transport.utils :as transport.utils]
|
2019-09-04 16:43:01 +02:00
|
|
|
[status-im.ui.screens.mobile-network-settings.utils
|
|
|
|
:as
|
|
|
|
mobile-network-utils]
|
|
|
|
[status-im.ui.screens.navigation :as navigation]
|
2018-10-18 02:05:02 +02:00
|
|
|
[status-im.utils.fx :as fx]
|
2018-12-03 14:19:56 -05:00
|
|
|
[status-im.utils.handlers :as handlers]
|
2019-09-04 16:43:01 +02:00
|
|
|
[status-im.utils.platform :as platform]
|
|
|
|
[status-im.utils.random :as rand]
|
|
|
|
[status-im.utils.utils :as utils]
|
|
|
|
[taoensso.timbre :as log]))
|
2018-06-01 10:04:48 +02:00
|
|
|
|
2018-10-18 02:05:02 +02:00
|
|
|
;; How do mailserver work ?
|
|
|
|
;;
|
|
|
|
;; - We send a request to the mailserver, we are only interested in the
|
|
|
|
;; messages since `last-request` up to the last seven days
|
|
|
|
;; and the last 24 hours for topics that were just joined
|
|
|
|
;; - The mailserver doesn't directly respond to the request and
|
|
|
|
;; instead we start receiving messages in the filters for the requested
|
|
|
|
;; topics.
|
|
|
|
;; - If the mailserver was not ready when we tried for instance to request
|
|
|
|
;; the history of a topic after joining a chat, the request will be done
|
|
|
|
;; as soon as the mailserver becomes available
|
2018-05-29 12:16:22 +02:00
|
|
|
|
2018-06-01 10:04:48 +02:00
|
|
|
|
Adds topic negotiation and partitioned topic
All the code has been implemented in statusgo: status-im/status-go#1466
Basically all the whisper filter management is done at that level.
Technical description
On startup we load all chats and send a list of them to status go:
For a public chat: {:chatId "status"}, we create a single filter, based on the name of the chat.
For each contact added by us, each user in a group chat and each one to one chat open, we send:
{:chatId "0x", :oneToOne true}. This will create a chats, to listen to their contact code.
Any previously negotiated topic is also returned.
Once loaded, we create our filters, and upsert the mailserver topics, both of which are solely based on the filters loaded.
In order to remove a chat, we delete/stopwatching first the the filter in status-react and then ask status-go to remove the filter. For a public chat we always remove, for a one-to-one we remove only if the user is not in our contacts, or in a group chat or we have a chat open. Negotiated topics are never removed, as otherwise the other user won't be able to contact us anymore.
On stopping whisper we don't have to ask status-go to remove filters as they are removed automatically.
Some more logic can be pushed in status-go, but that will be in subsequent PRs.
Signed-off-by: Andrea Maria Piana <andrea.maria.piana@gmail.com>
2019-06-05 13:34:04 +02:00
|
|
|
(def limit (atom constants/default-limit))
|
2019-03-01 10:36:49 +01:00
|
|
|
(def success-counter (atom 0))
|
2018-06-13 15:37:51 +02:00
|
|
|
|
2018-09-24 17:59:02 +02:00
|
|
|
(defn connected? [{:keys [db]} id]
|
2018-10-18 02:05:02 +02:00
|
|
|
(= (:mailserver/current-id db) id))
|
2018-06-01 10:04:48 +02:00
|
|
|
|
2018-09-24 17:59:02 +02:00
|
|
|
(defn fetch [{:keys [db] :as cofx} id]
|
2019-10-22 02:05:15 +02:00
|
|
|
(get-in db [:mailserver/mailservers (node/current-fleet-key db) id]))
|
2018-06-13 15:37:51 +02:00
|
|
|
|
|
|
|
(defn fetch-current [{:keys [db] :as cofx}]
|
2018-10-18 02:05:02 +02:00
|
|
|
(fetch cofx (:mailserver/current-id db)))
|
2018-06-13 15:37:51 +02:00
|
|
|
|
2018-07-13 08:04:02 +02:00
|
|
|
(defn preferred-mailserver-id [{:keys [db] :as cofx}]
|
2019-10-22 02:05:15 +02:00
|
|
|
(get-in db [:multiaccount :settings :mailserver (node/current-fleet-key db)]))
|
2018-07-13 08:04:02 +02:00
|
|
|
|
|
|
|
(defn- round-robin
|
|
|
|
"Find the choice and pick the next one, default to first if not found"
|
|
|
|
[choices current-id]
|
|
|
|
(let [next-index (reduce
|
|
|
|
(fn [index choice]
|
|
|
|
(if (= current-id choice)
|
|
|
|
(reduced (inc index))
|
|
|
|
(inc index)))
|
|
|
|
0
|
|
|
|
choices)]
|
|
|
|
(nth choices
|
|
|
|
(mod
|
|
|
|
next-index
|
|
|
|
(count choices)))))
|
|
|
|
|
2018-06-13 15:37:51 +02:00
|
|
|
(defn selected-or-random-id
|
2018-07-13 08:04:02 +02:00
|
|
|
"Use the preferred mailserver if set & exists, otherwise picks one randomly
|
|
|
|
if current-id is not set, else round-robin"
|
2018-06-13 15:37:51 +02:00
|
|
|
[{:keys [db] :as cofx}]
|
2019-10-22 02:05:15 +02:00
|
|
|
(let [current-fleet (node/current-fleet-key db)
|
2018-10-18 02:05:02 +02:00
|
|
|
current-id (:mailserver/current-id db)
|
2018-09-04 14:15:50 +02:00
|
|
|
preference (preferred-mailserver-id cofx)
|
2018-10-18 02:05:02 +02:00
|
|
|
choices (-> db :mailserver/mailservers current-fleet keys)]
|
2018-06-13 15:37:51 +02:00
|
|
|
(if (and preference
|
2018-09-24 17:59:02 +02:00
|
|
|
(fetch cofx preference))
|
2018-06-13 15:37:51 +02:00
|
|
|
preference
|
2018-07-13 08:04:02 +02:00
|
|
|
(if current-id
|
|
|
|
(round-robin choices current-id)
|
|
|
|
(rand-nth choices)))))
|
2018-06-01 10:04:48 +02:00
|
|
|
|
2018-09-24 17:59:02 +02:00
|
|
|
(fx/defn set-current-mailserver
|
|
|
|
[{:keys [db] :as cofx}]
|
2018-10-18 02:05:02 +02:00
|
|
|
{:db (assoc db :mailserver/current-id
|
2018-09-24 17:59:02 +02:00
|
|
|
(selected-or-random-id cofx))})
|
2018-06-13 15:37:51 +02:00
|
|
|
|
2018-10-18 02:05:02 +02:00
|
|
|
(defn add-peer! [enode]
|
|
|
|
(status/add-peer enode
|
2018-12-03 14:19:56 -05:00
|
|
|
(handlers/response-handler #(log/debug "mailserver: add-peer success" %)
|
|
|
|
#(log/error "mailserver: add-peer error" %))))
|
2018-10-18 02:05:02 +02:00
|
|
|
|
Add mailservers confirmations & use peer count for online status
We now check that we are only connected to some `peers` instead of using `NetInfo` from `react-native`.
This is because it has been reported to be quite flaky at times, not reporting online status after sleeping, and for privacy concerns (on ios it pings `apple.com`, on desktop `google.com`).
Adds a new banner `Wallet Offline` and change `Connecting to peers` to `Chat offline`.
A message will be marked as `Sent` only if it made it to the mailserver you are connected to, which will increase the guarantees that we can make about a message (if you see it as sent, it has reached at least a mailserver), this has the consequence that:
- If you are not connected to any mailserver or the mailserver is non responsive/down, and you send a message, it will be marked as `Not sent`, although it might have been actually made it in the network.
Probably this is something that we would like to communicate to the user through UX (i.e. tick if made it to at least a peer, double tick if it made to a mailserver )
Currently I have only enabled this feature in nightlies & devs, I would give it a run and see how we feel about it.
2018-12-06 11:53:45 +01:00
|
|
|
;; We now wait for a confirmation from the mailserver before marking the message
|
|
|
|
;; as sent.
|
|
|
|
|
|
|
|
(defn update-mailservers! [enodes]
|
|
|
|
(status/update-mailservers
|
|
|
|
(.stringify js/JSON (clj->js enodes))
|
2018-12-03 14:19:56 -05:00
|
|
|
(handlers/response-handler #(log/debug "mailserver: update-mailservers success" %)
|
|
|
|
#(log/error "mailserver: update-mailservers error" %))))
|
Add mailservers confirmations & use peer count for online status
We now check that we are only connected to some `peers` instead of using `NetInfo` from `react-native`.
This is because it has been reported to be quite flaky at times, not reporting online status after sleeping, and for privacy concerns (on ios it pings `apple.com`, on desktop `google.com`).
Adds a new banner `Wallet Offline` and change `Connecting to peers` to `Chat offline`.
A message will be marked as `Sent` only if it made it to the mailserver you are connected to, which will increase the guarantees that we can make about a message (if you see it as sent, it has reached at least a mailserver), this has the consequence that:
- If you are not connected to any mailserver or the mailserver is non responsive/down, and you send a message, it will be marked as `Not sent`, although it might have been actually made it in the network.
Probably this is something that we would like to communicate to the user through UX (i.e. tick if made it to at least a peer, double tick if it made to a mailserver )
Currently I have only enabled this feature in nightlies & devs, I would give it a run and see how we feel about it.
2018-12-06 11:53:45 +01:00
|
|
|
|
2018-10-18 02:05:02 +02:00
|
|
|
(defn remove-peer! [enode]
|
|
|
|
(let [args {:jsonrpc "2.0"
|
|
|
|
:id 2
|
|
|
|
:method "admin_removePeer"
|
|
|
|
:params [enode]}
|
|
|
|
payload (.stringify js/JSON (clj->js args))]
|
|
|
|
(status/call-private-rpc payload
|
2018-12-03 14:19:56 -05:00
|
|
|
(handlers/response-handler #(log/debug "mailserver: remove-peer success" %)
|
|
|
|
#(log/error "mailserver: remove-peer error" %)))))
|
2018-10-18 02:05:02 +02:00
|
|
|
|
|
|
|
(re-frame/reg-fx
|
|
|
|
:mailserver/add-peer
|
|
|
|
(fn [enode]
|
|
|
|
(add-peer! enode)))
|
|
|
|
|
|
|
|
(re-frame/reg-fx
|
|
|
|
:mailserver/remove-peer
|
|
|
|
(fn [enode]
|
|
|
|
(remove-peer! enode)))
|
|
|
|
|
Add mailservers confirmations & use peer count for online status
We now check that we are only connected to some `peers` instead of using `NetInfo` from `react-native`.
This is because it has been reported to be quite flaky at times, not reporting online status after sleeping, and for privacy concerns (on ios it pings `apple.com`, on desktop `google.com`).
Adds a new banner `Wallet Offline` and change `Connecting to peers` to `Chat offline`.
A message will be marked as `Sent` only if it made it to the mailserver you are connected to, which will increase the guarantees that we can make about a message (if you see it as sent, it has reached at least a mailserver), this has the consequence that:
- If you are not connected to any mailserver or the mailserver is non responsive/down, and you send a message, it will be marked as `Not sent`, although it might have been actually made it in the network.
Probably this is something that we would like to communicate to the user through UX (i.e. tick if made it to at least a peer, double tick if it made to a mailserver )
Currently I have only enabled this feature in nightlies & devs, I would give it a run and see how we feel about it.
2018-12-06 11:53:45 +01:00
|
|
|
(re-frame/reg-fx
|
|
|
|
:mailserver/update-mailservers
|
|
|
|
(fn [enodes]
|
|
|
|
(update-mailservers! enodes)))
|
|
|
|
|
2019-03-01 10:36:49 +01:00
|
|
|
(defn decrease-limit []
|
Adds topic negotiation and partitioned topic
All the code has been implemented in statusgo: status-im/status-go#1466
Basically all the whisper filter management is done at that level.
Technical description
On startup we load all chats and send a list of them to status go:
For a public chat: {:chatId "status"}, we create a single filter, based on the name of the chat.
For each contact added by us, each user in a group chat and each one to one chat open, we send:
{:chatId "0x", :oneToOne true}. This will create a chats, to listen to their contact code.
Any previously negotiated topic is also returned.
Once loaded, we create our filters, and upsert the mailserver topics, both of which are solely based on the filters loaded.
In order to remove a chat, we delete/stopwatching first the the filter in status-react and then ask status-go to remove the filter. For a public chat we always remove, for a one-to-one we remove only if the user is not in our contacts, or in a group chat or we have a chat open. Negotiated topics are never removed, as otherwise the other user won't be able to contact us anymore.
On stopping whisper we don't have to ask status-go to remove filters as they are removed automatically.
Some more logic can be pushed in status-go, but that will be in subsequent PRs.
Signed-off-by: Andrea Maria Piana <andrea.maria.piana@gmail.com>
2019-06-05 13:34:04 +02:00
|
|
|
(max constants/min-limit (/ @limit 2)))
|
2019-03-01 10:36:49 +01:00
|
|
|
|
|
|
|
(defn increase-limit []
|
Adds topic negotiation and partitioned topic
All the code has been implemented in statusgo: status-im/status-go#1466
Basically all the whisper filter management is done at that level.
Technical description
On startup we load all chats and send a list of them to status go:
For a public chat: {:chatId "status"}, we create a single filter, based on the name of the chat.
For each contact added by us, each user in a group chat and each one to one chat open, we send:
{:chatId "0x", :oneToOne true}. This will create a chats, to listen to their contact code.
Any previously negotiated topic is also returned.
Once loaded, we create our filters, and upsert the mailserver topics, both of which are solely based on the filters loaded.
In order to remove a chat, we delete/stopwatching first the the filter in status-react and then ask status-go to remove the filter. For a public chat we always remove, for a one-to-one we remove only if the user is not in our contacts, or in a group chat or we have a chat open. Negotiated topics are never removed, as otherwise the other user won't be able to contact us anymore.
On stopping whisper we don't have to ask status-go to remove filters as they are removed automatically.
Some more logic can be pushed in status-go, but that will be in subsequent PRs.
Signed-off-by: Andrea Maria Piana <andrea.maria.piana@gmail.com>
2019-06-05 13:34:04 +02:00
|
|
|
(min constants/max-limit (* @limit 2)))
|
2019-03-01 10:36:49 +01:00
|
|
|
|
2019-02-06 15:09:13 +01:00
|
|
|
(re-frame/reg-fx
|
|
|
|
:mailserver/set-limit
|
|
|
|
(fn [n]
|
|
|
|
(reset! limit n)))
|
|
|
|
|
2019-03-01 10:36:49 +01:00
|
|
|
(re-frame/reg-fx
|
|
|
|
:mailserver/increase-limit
|
|
|
|
(fn []
|
|
|
|
(if (>= @success-counter 2)
|
|
|
|
(reset! limit (increase-limit))
|
|
|
|
(swap! success-counter inc))))
|
2019-02-06 15:09:13 +01:00
|
|
|
|
2019-03-01 10:36:49 +01:00
|
|
|
(re-frame/reg-fx
|
|
|
|
:mailserver/decrease-limit
|
|
|
|
(fn []
|
|
|
|
(reset! limit (decrease-limit))
|
|
|
|
(reset! success-counter 0)))
|
2019-02-06 15:09:13 +01:00
|
|
|
|
2019-08-20 11:53:10 +02:00
|
|
|
(defn mark-trusted-peer! [enode]
|
|
|
|
(json-rpc/call {:method "shh_markTrustedPeer"
|
|
|
|
:params [enode]
|
|
|
|
:on-success #(re-frame/dispatch [:mailserver.callback/mark-trusted-peer-success %])
|
|
|
|
:on-error #(re-frame/dispatch [:mailserver.callback/mark-trusted-peer-error %])}))
|
2018-10-18 02:05:02 +02:00
|
|
|
|
|
|
|
(re-frame/reg-fx
|
|
|
|
:mailserver/mark-trusted-peer
|
2019-08-20 11:53:10 +02:00
|
|
|
mark-trusted-peer!)
|
2018-10-18 02:05:02 +02:00
|
|
|
|
|
|
|
(fx/defn generate-mailserver-symkey
|
|
|
|
[{:keys [db] :as cofx} {:keys [password id] :as mailserver}]
|
2019-10-22 02:05:15 +02:00
|
|
|
(let [current-fleet (node/current-fleet-key db)]
|
2018-10-18 02:05:02 +02:00
|
|
|
{:db (assoc-in db [:mailserver/mailservers current-fleet id :generating-sym-key?] true)
|
|
|
|
:shh/generate-sym-key-from-password
|
|
|
|
[{:password password
|
|
|
|
:on-success (fn [_ sym-key-id]
|
|
|
|
(re-frame/dispatch [:mailserver.callback/generate-mailserver-symkey-success mailserver sym-key-id]))
|
|
|
|
:on-error #(log/error "mailserver: get-sym-key error" %)}]}))
|
|
|
|
|
|
|
|
(defn registered-peer?
|
|
|
|
"truthy if the enode is a registered peer"
|
|
|
|
[peers enode]
|
2018-11-26 11:53:21 +01:00
|
|
|
(let [registered-enodes (into #{} (map :enode) peers)]
|
|
|
|
(contains? registered-enodes enode)))
|
2018-10-18 02:05:02 +02:00
|
|
|
|
|
|
|
(defn update-mailserver-state [db state]
|
|
|
|
(assoc db :mailserver/state state))
|
|
|
|
|
|
|
|
(fx/defn mark-trusted-peer
|
|
|
|
[{:keys [db] :as cofx}]
|
|
|
|
(let [{:keys [address sym-key-id generating-sym-key?] :as mailserver} (fetch-current cofx)]
|
|
|
|
(fx/merge cofx
|
|
|
|
{:db (update-mailserver-state db :added)
|
2019-08-20 11:53:10 +02:00
|
|
|
:mailserver/mark-trusted-peer address}
|
2018-10-18 02:05:02 +02:00
|
|
|
(when-not (or sym-key-id generating-sym-key?)
|
|
|
|
(generate-mailserver-symkey mailserver)))))
|
|
|
|
|
|
|
|
(fx/defn add-peer
|
|
|
|
[{:keys [db] :as cofx}]
|
|
|
|
(let [{:keys [address sym-key-id generating-sym-key?] :as mailserver} (fetch-current cofx)]
|
|
|
|
(fx/merge cofx
|
|
|
|
{:db (-> db
|
|
|
|
(update-mailserver-state :connecting)
|
|
|
|
(update :mailserver/connection-checks inc))
|
|
|
|
:mailserver/add-peer address
|
Add mailservers confirmations & use peer count for online status
We now check that we are only connected to some `peers` instead of using `NetInfo` from `react-native`.
This is because it has been reported to be quite flaky at times, not reporting online status after sleeping, and for privacy concerns (on ios it pings `apple.com`, on desktop `google.com`).
Adds a new banner `Wallet Offline` and change `Connecting to peers` to `Chat offline`.
A message will be marked as `Sent` only if it made it to the mailserver you are connected to, which will increase the guarantees that we can make about a message (if you see it as sent, it has reached at least a mailserver), this has the consequence that:
- If you are not connected to any mailserver or the mailserver is non responsive/down, and you send a message, it will be marked as `Not sent`, although it might have been actually made it in the network.
Probably this is something that we would like to communicate to the user through UX (i.e. tick if made it to at least a peer, double tick if it made to a mailserver )
Currently I have only enabled this feature in nightlies & devs, I would give it a run and see how we feel about it.
2018-12-06 11:53:45 +01:00
|
|
|
;; Any message sent before this takes effect will not be marked as sent
|
|
|
|
;; probably we should improve the UX so that is more transparent to the user
|
|
|
|
:mailserver/update-mailservers [address]
|
Adds topic negotiation and partitioned topic
All the code has been implemented in statusgo: status-im/status-go#1466
Basically all the whisper filter management is done at that level.
Technical description
On startup we load all chats and send a list of them to status go:
For a public chat: {:chatId "status"}, we create a single filter, based on the name of the chat.
For each contact added by us, each user in a group chat and each one to one chat open, we send:
{:chatId "0x", :oneToOne true}. This will create a chats, to listen to their contact code.
Any previously negotiated topic is also returned.
Once loaded, we create our filters, and upsert the mailserver topics, both of which are solely based on the filters loaded.
In order to remove a chat, we delete/stopwatching first the the filter in status-react and then ask status-go to remove the filter. For a public chat we always remove, for a one-to-one we remove only if the user is not in our contacts, or in a group chat or we have a chat open. Negotiated topics are never removed, as otherwise the other user won't be able to contact us anymore.
On stopping whisper we don't have to ask status-go to remove filters as they are removed automatically.
Some more logic can be pushed in status-go, but that will be in subsequent PRs.
Signed-off-by: Andrea Maria Piana <andrea.maria.piana@gmail.com>
2019-06-05 13:34:04 +02:00
|
|
|
:utils/dispatch-later [{:ms constants/connection-timeout
|
2018-10-18 02:05:02 +02:00
|
|
|
:dispatch [:mailserver/check-connection-timeout]}]}
|
|
|
|
(when-not (or sym-key-id generating-sym-key?)
|
|
|
|
(generate-mailserver-symkey mailserver)))))
|
|
|
|
|
2019-04-13 07:08:22 +03:00
|
|
|
(defn executing-gap-request?
|
|
|
|
[{:mailserver/keys [current-request fetching-gaps-in-progress]}]
|
|
|
|
(= (get fetching-gaps-in-progress (:chat-id current-request))
|
|
|
|
(select-keys
|
|
|
|
current-request
|
|
|
|
[:from :to :force-to? :topics :chat-id])))
|
|
|
|
|
2018-10-18 02:05:02 +02:00
|
|
|
(fx/defn connect-to-mailserver
|
|
|
|
"Add mailserver as a peer using `::add-peer` cofx and generate sym-key when
|
|
|
|
it doesn't exists
|
|
|
|
Peer summary will change and we will receive a signal from status go when
|
|
|
|
this is successful
|
|
|
|
A connection-check is made after `connection timeout` is reached and
|
|
|
|
mailserver-state is changed to error if it is not connected by then"
|
|
|
|
[{:keys [db] :as cofx}]
|
2019-04-13 07:08:22 +03:00
|
|
|
(let [{:keys [address]} (fetch-current cofx)
|
2018-10-18 02:05:02 +02:00
|
|
|
{:keys [peers-summary]} db
|
2019-04-13 07:08:22 +03:00
|
|
|
added? (registered-peer? peers-summary address)
|
|
|
|
gap-request? (executing-gap-request? db)]
|
2018-10-18 02:05:02 +02:00
|
|
|
(fx/merge cofx
|
2019-04-13 07:08:22 +03:00
|
|
|
{:db (cond-> (dissoc db :mailserver/current-request)
|
|
|
|
gap-request?
|
2019-04-16 22:09:18 +03:00
|
|
|
(-> (assoc :mailserver/fetching-gaps-in-progress {})
|
|
|
|
(dissoc :mailserver/planned-gap-requests)))}
|
2018-10-18 02:05:02 +02:00
|
|
|
(if added?
|
|
|
|
(mark-trusted-peer)
|
|
|
|
(add-peer)))))
|
|
|
|
|
|
|
|
(fx/defn peers-summary-change
|
|
|
|
"There is only 2 summary changes that require mailserver action:
|
|
|
|
- mailserver disconnected: we try to reconnect
|
|
|
|
- mailserver connected: we mark the mailserver as trusted peer"
|
|
|
|
[{:keys [db] :as cofx} previous-summary]
|
2019-07-03 16:29:01 +02:00
|
|
|
(when (:multiaccount db)
|
2018-10-18 02:05:02 +02:00
|
|
|
(let [{:keys [peers-summary peers-count]} db
|
|
|
|
{:keys [address sym-key-id] :as mailserver} (fetch-current cofx)
|
|
|
|
mailserver-was-registered? (registered-peer? previous-summary
|
|
|
|
address)
|
|
|
|
mailserver-is-registered? (registered-peer? peers-summary
|
|
|
|
address)
|
|
|
|
mailserver-added? (and mailserver-is-registered?
|
|
|
|
(not mailserver-was-registered?))
|
|
|
|
mailserver-removed? (and mailserver-was-registered?
|
|
|
|
(not mailserver-is-registered?))]
|
|
|
|
(cond
|
|
|
|
mailserver-added?
|
|
|
|
(mark-trusted-peer cofx)
|
|
|
|
mailserver-removed?
|
|
|
|
(connect-to-mailserver cofx)))))
|
|
|
|
|
2018-12-12 16:56:57 +01:00
|
|
|
(defn adjust-request-for-transit-time
|
|
|
|
[from]
|
|
|
|
(let [ttl (:ttl protocol/whisper-opts)
|
|
|
|
whisper-tolerance (:whisper-drift-tolerance protocol/whisper-opts)
|
|
|
|
adjustment (+ whisper-tolerance ttl)
|
|
|
|
adjusted-from (- (max from adjustment) adjustment)]
|
|
|
|
(log/debug "Adjusting mailserver request" "from:" from "adjusted-from:" adjusted-from)
|
|
|
|
adjusted-from))
|
|
|
|
|
2019-02-27 18:43:56 +05:45
|
|
|
(defn chats->never-synced-public-chats [chats]
|
|
|
|
(into {} (filter (fn [[k v]] (:might-have-join-time-messages? v)) chats)))
|
|
|
|
|
|
|
|
(fx/defn handle-request-success [{{:keys [chats] :as db} :db}
|
|
|
|
{:keys [request-id topics]}]
|
2019-02-27 12:50:41 +01:00
|
|
|
(when (:mailserver/current-request db)
|
2019-02-27 18:43:56 +05:45
|
|
|
(let [by-topic-never-synced-chats (reduce-kv
|
|
|
|
#(assoc %1 (transport.utils/get-topic %2) %3)
|
|
|
|
{}
|
|
|
|
(chats->never-synced-public-chats chats))
|
|
|
|
never-synced-chats-in-this-request (select-keys by-topic-never-synced-chats (vec topics))]
|
|
|
|
(if (seq never-synced-chats-in-this-request)
|
|
|
|
{:db (-> db
|
|
|
|
((fn [db] (reduce
|
|
|
|
(fn [db chat]
|
|
|
|
(assoc-in db [:chats (:chat-id chat) :join-time-mail-request-id] request-id))
|
|
|
|
db
|
|
|
|
(vals never-synced-chats-in-this-request))))
|
|
|
|
(assoc-in [:mailserver/current-request :request-id] request-id))}
|
|
|
|
{:db (assoc-in db [:mailserver/current-request :request-id] request-id)}))))
|
2019-02-27 12:50:41 +01:00
|
|
|
|
2019-08-20 11:53:10 +02:00
|
|
|
(defn request-messages!
|
|
|
|
[{:keys [sym-key-id address]} {:keys [topics cursor to from force-to?] :as request}]
|
2018-12-12 16:56:57 +01:00
|
|
|
;; Add some room to from, unless we break day boundaries so that messages that have
|
|
|
|
;; been received after the last request are also fetched
|
2019-03-12 14:17:32 +01:00
|
|
|
(let [actual-from (adjust-request-for-transit-time from)
|
2019-02-06 15:09:13 +01:00
|
|
|
actual-limit (or (:limit request)
|
2019-03-12 14:17:32 +01:00
|
|
|
@limit)]
|
2018-12-12 16:56:57 +01:00
|
|
|
(log/info "mailserver: request-messages for: "
|
|
|
|
" topics " topics
|
|
|
|
" from " actual-from
|
2019-04-04 16:55:35 +03:00
|
|
|
" force-to? " force-to?
|
|
|
|
" to " to
|
2019-04-16 10:14:04 +03:00
|
|
|
" range " (- to from)
|
2018-12-12 16:56:57 +01:00
|
|
|
" cursor " cursor
|
2019-03-12 14:17:32 +01:00
|
|
|
" limit " actual-limit)
|
2019-08-20 11:53:10 +02:00
|
|
|
(json-rpc/call {:method "shhext_requestMessages"
|
|
|
|
:params [(cond-> {:topics topics
|
|
|
|
:mailServerPeer address
|
|
|
|
:symKeyID sym-key-id
|
|
|
|
:timeout constants/request-timeout
|
|
|
|
:limit actual-limit
|
|
|
|
:cursor cursor
|
|
|
|
:from actual-from}
|
|
|
|
force-to?
|
|
|
|
(assoc :to to))]
|
|
|
|
:on-success (fn [request-id]
|
|
|
|
(log/info "mailserver: messages request success for topic " topics "from" from "to" to)
|
|
|
|
(re-frame/dispatch [:mailserver.callback/request-success {:request-id request-id :topics topics}]))
|
|
|
|
:on-error (fn [error]
|
|
|
|
(log/error "mailserver: messages request error for topic " topics ": " error)
|
|
|
|
(utils/set-timeout #(re-frame/dispatch [:mailserver.callback/resend-request {:request-id nil}])
|
|
|
|
constants/backoff-interval-ms))})))
|
2018-10-18 02:05:02 +02:00
|
|
|
|
|
|
|
(re-frame/reg-fx
|
|
|
|
:mailserver/request-messages
|
2019-08-20 11:53:10 +02:00
|
|
|
(fn [{:keys [mailserver request]}]
|
|
|
|
(request-messages! mailserver request)))
|
2018-10-18 02:05:02 +02:00
|
|
|
|
|
|
|
(defn get-mailserver-when-ready
|
|
|
|
"return the mailserver if the mailserver is ready"
|
|
|
|
[{:keys [db] :as cofx}]
|
|
|
|
(let [{:keys [sym-key-id] :as mailserver} (fetch-current cofx)
|
|
|
|
mailserver-state (:mailserver/state db)]
|
|
|
|
(when (and (= :connected mailserver-state)
|
|
|
|
sym-key-id)
|
|
|
|
mailserver)))
|
|
|
|
|
2019-04-04 16:55:35 +03:00
|
|
|
(defn topic->request
|
|
|
|
[default-request-to requests-from requests-to]
|
|
|
|
(fn [[topic {:keys [last-request]}]]
|
|
|
|
(let [force-request-from (get requests-from topic)
|
|
|
|
force-request-to (get requests-to topic)]
|
|
|
|
(when (or force-request-from
|
|
|
|
(> default-request-to last-request))
|
|
|
|
(let [from (or force-request-from
|
|
|
|
(max last-request
|
Adds topic negotiation and partitioned topic
All the code has been implemented in statusgo: status-im/status-go#1466
Basically all the whisper filter management is done at that level.
Technical description
On startup we load all chats and send a list of them to status go:
For a public chat: {:chatId "status"}, we create a single filter, based on the name of the chat.
For each contact added by us, each user in a group chat and each one to one chat open, we send:
{:chatId "0x", :oneToOne true}. This will create a chats, to listen to their contact code.
Any previously negotiated topic is also returned.
Once loaded, we create our filters, and upsert the mailserver topics, both of which are solely based on the filters loaded.
In order to remove a chat, we delete/stopwatching first the the filter in status-react and then ask status-go to remove the filter. For a public chat we always remove, for a one-to-one we remove only if the user is not in our contacts, or in a group chat or we have a chat open. Negotiated topics are never removed, as otherwise the other user won't be able to contact us anymore.
On stopping whisper we don't have to ask status-go to remove filters as they are removed automatically.
Some more logic can be pushed in status-go, but that will be in subsequent PRs.
Signed-off-by: Andrea Maria Piana <andrea.maria.piana@gmail.com>
2019-06-05 13:34:04 +02:00
|
|
|
(- default-request-to constants/max-request-range)))
|
2019-04-04 16:55:35 +03:00
|
|
|
to (or force-request-to default-request-to)]
|
Adds topic negotiation and partitioned topic
All the code has been implemented in statusgo: status-im/status-go#1466
Basically all the whisper filter management is done at that level.
Technical description
On startup we load all chats and send a list of them to status go:
For a public chat: {:chatId "status"}, we create a single filter, based on the name of the chat.
For each contact added by us, each user in a group chat and each one to one chat open, we send:
{:chatId "0x", :oneToOne true}. This will create a chats, to listen to their contact code.
Any previously negotiated topic is also returned.
Once loaded, we create our filters, and upsert the mailserver topics, both of which are solely based on the filters loaded.
In order to remove a chat, we delete/stopwatching first the the filter in status-react and then ask status-go to remove the filter. For a public chat we always remove, for a one-to-one we remove only if the user is not in our contacts, or in a group chat or we have a chat open. Negotiated topics are never removed, as otherwise the other user won't be able to contact us anymore.
On stopping whisper we don't have to ask status-go to remove filters as they are removed automatically.
Some more logic can be pushed in status-go, but that will be in subsequent PRs.
Signed-off-by: Andrea Maria Piana <andrea.maria.piana@gmail.com>
2019-06-05 13:34:04 +02:00
|
|
|
{:gap-topics #{topic}
|
2019-04-04 16:55:35 +03:00
|
|
|
:from from
|
|
|
|
:to to
|
|
|
|
:force-to? (not (nil? force-request-to))})))))
|
|
|
|
|
|
|
|
(defn aggregate-requests
|
Adds topic negotiation and partitioned topic
All the code has been implemented in statusgo: status-im/status-go#1466
Basically all the whisper filter management is done at that level.
Technical description
On startup we load all chats and send a list of them to status go:
For a public chat: {:chatId "status"}, we create a single filter, based on the name of the chat.
For each contact added by us, each user in a group chat and each one to one chat open, we send:
{:chatId "0x", :oneToOne true}. This will create a chats, to listen to their contact code.
Any previously negotiated topic is also returned.
Once loaded, we create our filters, and upsert the mailserver topics, both of which are solely based on the filters loaded.
In order to remove a chat, we delete/stopwatching first the the filter in status-react and then ask status-go to remove the filter. For a public chat we always remove, for a one-to-one we remove only if the user is not in our contacts, or in a group chat or we have a chat open. Negotiated topics are never removed, as otherwise the other user won't be able to contact us anymore.
On stopping whisper we don't have to ask status-go to remove filters as they are removed automatically.
Some more logic can be pushed in status-go, but that will be in subsequent PRs.
Signed-off-by: Andrea Maria Piana <andrea.maria.piana@gmail.com>
2019-06-05 13:34:04 +02:00
|
|
|
[acc {:keys [gap-topics from to force-to? gap chat-id]}]
|
2019-04-16 22:09:18 +03:00
|
|
|
(when from
|
|
|
|
(update acc [from to force-to?]
|
|
|
|
(fn [{:keys [topics]}]
|
Adds topic negotiation and partitioned topic
All the code has been implemented in statusgo: status-im/status-go#1466
Basically all the whisper filter management is done at that level.
Technical description
On startup we load all chats and send a list of them to status go:
For a public chat: {:chatId "status"}, we create a single filter, based on the name of the chat.
For each contact added by us, each user in a group chat and each one to one chat open, we send:
{:chatId "0x", :oneToOne true}. This will create a chats, to listen to their contact code.
Any previously negotiated topic is also returned.
Once loaded, we create our filters, and upsert the mailserver topics, both of which are solely based on the filters loaded.
In order to remove a chat, we delete/stopwatching first the the filter in status-react and then ask status-go to remove the filter. For a public chat we always remove, for a one-to-one we remove only if the user is not in our contacts, or in a group chat or we have a chat open. Negotiated topics are never removed, as otherwise the other user won't be able to contact us anymore.
On stopping whisper we don't have to ask status-go to remove filters as they are removed automatically.
Some more logic can be pushed in status-go, but that will be in subsequent PRs.
Signed-off-by: Andrea Maria Piana <andrea.maria.piana@gmail.com>
2019-06-05 13:34:04 +02:00
|
|
|
{:topics ((fnil clojure.set/union #{}) topics gap-topics)
|
2019-04-16 22:09:18 +03:00
|
|
|
:from from
|
|
|
|
:to to
|
|
|
|
;; To is sent to the mailserver only when force-to? is true,
|
|
|
|
;; also we use to calculate when the last-request was sent.
|
|
|
|
:force-to? force-to?
|
|
|
|
:gap gap
|
|
|
|
:chat-id chat-id}))))
|
2018-10-18 02:05:02 +02:00
|
|
|
|
|
|
|
(defn prepare-messages-requests
|
2019-04-04 16:55:35 +03:00
|
|
|
[{{:keys [:mailserver/requests-from
|
|
|
|
:mailserver/requests-to
|
2019-04-16 22:09:18 +03:00
|
|
|
:mailserver/topics
|
|
|
|
:mailserver/planned-gap-requests]} :db}
|
2019-04-04 16:55:35 +03:00
|
|
|
default-request-to]
|
|
|
|
(transduce
|
|
|
|
(keep (topic->request default-request-to requests-from requests-to))
|
|
|
|
(completing aggregate-requests vals)
|
2019-04-16 22:09:18 +03:00
|
|
|
(reduce
|
|
|
|
aggregate-requests
|
|
|
|
{}
|
|
|
|
(vals planned-gap-requests))
|
2019-04-04 16:55:35 +03:00
|
|
|
topics))
|
2018-10-18 02:05:02 +02:00
|
|
|
|
|
|
|
(fx/defn process-next-messages-request
|
|
|
|
[{:keys [db now] :as cofx}]
|
2019-02-01 14:21:23 +02:00
|
|
|
(when (and
|
Adds topic negotiation and partitioned topic
All the code has been implemented in statusgo: status-im/status-go#1466
Basically all the whisper filter management is done at that level.
Technical description
On startup we load all chats and send a list of them to status go:
For a public chat: {:chatId "status"}, we create a single filter, based on the name of the chat.
For each contact added by us, each user in a group chat and each one to one chat open, we send:
{:chatId "0x", :oneToOne true}. This will create a chats, to listen to their contact code.
Any previously negotiated topic is also returned.
Once loaded, we create our filters, and upsert the mailserver topics, both of which are solely based on the filters loaded.
In order to remove a chat, we delete/stopwatching first the the filter in status-react and then ask status-go to remove the filter. For a public chat we always remove, for a one-to-one we remove only if the user is not in our contacts, or in a group chat or we have a chat open. Negotiated topics are never removed, as otherwise the other user won't be able to contact us anymore.
On stopping whisper we don't have to ask status-go to remove filters as they are removed automatically.
Some more logic can be pushed in status-go, but that will be in subsequent PRs.
Signed-off-by: Andrea Maria Piana <andrea.maria.piana@gmail.com>
2019-06-05 13:34:04 +02:00
|
|
|
(:filters/initialized db)
|
2019-02-01 14:21:23 +02:00
|
|
|
(mobile-network-utils/syncing-allowed? cofx)
|
|
|
|
(not (:mailserver/current-request db)))
|
2018-10-18 02:05:02 +02:00
|
|
|
(when-let [mailserver (get-mailserver-when-ready cofx)]
|
|
|
|
(let [request-to (or (:mailserver/request-to db)
|
|
|
|
(quot now 1000))
|
2019-08-20 11:53:10 +02:00
|
|
|
requests (prepare-messages-requests cofx request-to)]
|
2019-04-04 16:55:35 +03:00
|
|
|
(log/debug "Mailserver: planned requests " requests)
|
2018-10-18 02:05:02 +02:00
|
|
|
(if-let [request (first requests)]
|
2019-02-01 14:21:23 +02:00
|
|
|
{:db (assoc db
|
|
|
|
:mailserver/pending-requests (count requests)
|
|
|
|
:mailserver/current-request request
|
|
|
|
:mailserver/request-to request-to)
|
2019-08-20 11:53:10 +02:00
|
|
|
:mailserver/request-messages {:mailserver mailserver
|
2019-02-01 14:21:23 +02:00
|
|
|
:request request}}
|
2018-10-18 02:05:02 +02:00
|
|
|
{:db (dissoc db
|
|
|
|
:mailserver/pending-requests
|
2019-04-04 16:55:35 +03:00
|
|
|
:mailserver/request-to
|
|
|
|
:mailserver/requests-from
|
|
|
|
:mailserver/requests-to)})))))
|
2018-10-18 02:05:02 +02:00
|
|
|
|
|
|
|
(fx/defn add-mailserver-trusted
|
|
|
|
"the current mailserver has been trusted
|
|
|
|
update mailserver status to `:connected` and request messages
|
|
|
|
if mailserver is ready"
|
|
|
|
[{:keys [db] :as cofx}]
|
|
|
|
(fx/merge cofx
|
|
|
|
{:db (update-mailserver-state db :connected)}
|
|
|
|
(process-next-messages-request)))
|
|
|
|
|
|
|
|
(fx/defn add-mailserver-sym-key
|
|
|
|
"the current mailserver sym-key has been generated
|
|
|
|
add sym-key to the mailserver in app-db and request messages if
|
|
|
|
mailserver is ready"
|
|
|
|
[{:keys [db] :as cofx} {:keys [id]} sym-key-id]
|
2019-10-22 02:05:15 +02:00
|
|
|
(let [current-fleet (node/current-fleet-key db)]
|
2018-10-18 02:05:02 +02:00
|
|
|
(fx/merge cofx
|
|
|
|
{:db (-> db
|
|
|
|
(assoc-in [:mailserver/mailservers current-fleet id :sym-key-id] sym-key-id)
|
|
|
|
(update-in [:mailserver/mailservers current-fleet id] dissoc :generating-sym-key?))}
|
|
|
|
(process-next-messages-request))))
|
|
|
|
|
|
|
|
(fx/defn change-mailserver
|
|
|
|
"mark mailserver status as `:error` if custom mailserver is used
|
|
|
|
otherwise try to reconnect to another mailserver"
|
|
|
|
[{:keys [db] :as cofx}]
|
2019-02-26 12:23:58 +01:00
|
|
|
(when-not (zero? (:peers-count db))
|
|
|
|
(if-let [preferred-mailserver (preferred-mailserver-id cofx)]
|
2019-10-22 02:05:15 +02:00
|
|
|
(let [current-fleet (node/current-fleet-key db)]
|
2019-02-26 12:23:58 +01:00
|
|
|
{:db
|
|
|
|
(update-mailserver-state db :error)
|
|
|
|
:ui/show-confirmation
|
|
|
|
{:title (i18n/label :t/mailserver-error-title)
|
|
|
|
:content (i18n/label :t/mailserver-error-content)
|
|
|
|
:confirm-button-text (i18n/label :t/mailserver-pick-another)
|
|
|
|
:on-accept #(re-frame/dispatch
|
2019-03-28 16:48:10 +02:00
|
|
|
[:navigate-to (if platform/desktop?
|
|
|
|
:advanced-settings
|
|
|
|
:offline-messaging-settings)])
|
2019-02-26 12:23:58 +01:00
|
|
|
:extra-options [{:text (i18n/label :t/mailserver-retry)
|
|
|
|
:onPress #(re-frame/dispatch
|
|
|
|
[:mailserver.ui/connect-confirmed
|
|
|
|
current-fleet
|
|
|
|
preferred-mailserver])
|
|
|
|
:style "default"}]}})
|
|
|
|
(let [{:keys [address]} (fetch-current cofx)]
|
|
|
|
(fx/merge cofx
|
|
|
|
{:mailserver/remove-peer address}
|
|
|
|
(set-current-mailserver)
|
|
|
|
(connect-to-mailserver))))))
|
2018-10-18 02:05:02 +02:00
|
|
|
|
|
|
|
(fx/defn check-connection
|
|
|
|
"connection-checks counter is used to prevent changing
|
|
|
|
mailserver on flaky connections
|
|
|
|
if there is more than one connection check pending
|
|
|
|
decrement the connection check counter
|
|
|
|
else
|
|
|
|
change mailserver if mailserver is connected"
|
|
|
|
[{:keys [db] :as cofx}]
|
2019-07-03 16:29:01 +02:00
|
|
|
;; check if logged into multiaccount
|
|
|
|
(when (contains? db :multiaccount)
|
2018-11-26 17:52:29 +02:00
|
|
|
(let [connection-checks (dec (:mailserver/connection-checks db))]
|
|
|
|
(if (>= 0 connection-checks)
|
|
|
|
(fx/merge cofx
|
|
|
|
{:db (dissoc db :mailserver/connection-checks)}
|
|
|
|
(when (= :connecting (:mailserver/state db))
|
2019-02-05 14:04:06 +02:00
|
|
|
(change-mailserver)))
|
2018-11-26 17:52:29 +02:00
|
|
|
{:db (update db :mailserver/connection-checks dec)}))))
|
2018-10-18 02:05:02 +02:00
|
|
|
|
|
|
|
(fx/defn reset-request-to
|
|
|
|
[{:keys [db]}]
|
|
|
|
{:db (dissoc db :mailserver/request-to)})
|
|
|
|
|
2019-04-16 22:09:18 +03:00
|
|
|
(fx/defn remove-gaps
|
2019-09-04 11:41:05 +02:00
|
|
|
[{:keys [db] :as cofx} chat-id]
|
|
|
|
(fx/merge cofx
|
|
|
|
{:db (update db :mailserver/gaps dissoc chat-id)}
|
|
|
|
(data-store.mailservers/delete-gaps-by-chat-id chat-id)))
|
2019-04-16 22:09:18 +03:00
|
|
|
|
|
|
|
(fx/defn remove-range
|
|
|
|
[{:keys [db]} chat-id]
|
|
|
|
{:db (update db :mailserver/ranges dissoc chat-id)
|
2019-09-05 19:33:36 +02:00
|
|
|
::json-rpc/call
|
|
|
|
[{:method "mailservers_deleteChatRequestRange"
|
|
|
|
:params [chat-id]
|
|
|
|
:on-success #(log/debug "deleted chat request range successfully")
|
|
|
|
:on-failure #(log/error "failed to delete chat request range" %)}]})
|
2019-04-16 22:09:18 +03:00
|
|
|
|
|
|
|
(defn update-mailserver-topic
|
|
|
|
[{:keys [last-request] :as config}
|
|
|
|
{:keys [request-to]}]
|
|
|
|
(cond-> config
|
|
|
|
(> request-to last-request)
|
|
|
|
(assoc :last-request request-to)))
|
|
|
|
|
|
|
|
(defn check-existing-gaps
|
|
|
|
[chat-id chat-gaps request]
|
|
|
|
(let [request-from (:from request)
|
|
|
|
request-to (:to request)]
|
|
|
|
(reduce
|
|
|
|
(fn [acc {:keys [from to id] :as gap}]
|
|
|
|
(cond
|
|
|
|
;; F----T
|
|
|
|
;; RF---RT
|
|
|
|
(< to request-from)
|
|
|
|
acc
|
|
|
|
|
|
|
|
;; F----T
|
|
|
|
;; RF---RT
|
|
|
|
(< request-to from)
|
|
|
|
(reduced acc)
|
|
|
|
|
|
|
|
;; F------T
|
|
|
|
;; RF-----RT
|
|
|
|
(and (<= request-from from)
|
|
|
|
(< from request-to to))
|
|
|
|
(let [updated-gap (assoc gap
|
|
|
|
:from request-to
|
|
|
|
:to to)]
|
|
|
|
(reduced
|
|
|
|
(update acc :updated-gaps assoc id updated-gap)))
|
|
|
|
|
|
|
|
;; F------T
|
|
|
|
;; RF----------RT
|
|
|
|
(and (<= request-from from)
|
|
|
|
(<= to request-to))
|
|
|
|
(update acc :deleted-gaps conj (:id gap))
|
|
|
|
|
|
|
|
;; F---------T
|
|
|
|
;; RF-------RT
|
|
|
|
(and (< from request-from to)
|
|
|
|
(<= to request-to))
|
|
|
|
(let [updated-gap (assoc gap
|
|
|
|
:from from
|
|
|
|
:to request-from)]
|
|
|
|
(update acc :updated-gaps assoc id updated-gap))
|
|
|
|
|
|
|
|
;; F---------T
|
|
|
|
;; RF---RT
|
|
|
|
(and (< from request-from)
|
|
|
|
(< request-to to))
|
|
|
|
(reduced
|
|
|
|
(-> acc
|
|
|
|
(update :deleted-gaps conj (:id gap))
|
|
|
|
(update :new-gaps concat [{:chat-id chat-id
|
|
|
|
:from from
|
|
|
|
:to request-from}
|
|
|
|
{:chat-id chat-id
|
|
|
|
:from request-to
|
|
|
|
:to to}])))
|
|
|
|
|
|
|
|
:else acc))
|
|
|
|
{}
|
|
|
|
(sort-by :from (vals chat-gaps)))))
|
|
|
|
|
|
|
|
(defn check-all-gaps
|
|
|
|
[gaps chat-ids request]
|
|
|
|
(transduce
|
|
|
|
(map (fn [chat-id]
|
|
|
|
(let [chat-gaps (get gaps chat-id)]
|
|
|
|
[chat-id (check-existing-gaps chat-id chat-gaps request)])))
|
|
|
|
(completing
|
|
|
|
(fn [acc [chat-id {:keys [new-gaps updated-gaps deleted-gaps]}]]
|
|
|
|
(cond-> acc
|
|
|
|
(seq new-gaps)
|
|
|
|
(assoc-in [:new-gaps chat-id] new-gaps)
|
|
|
|
|
|
|
|
(seq updated-gaps)
|
|
|
|
(assoc-in [:updated-gaps chat-id] updated-gaps)
|
|
|
|
|
|
|
|
(seq deleted-gaps)
|
|
|
|
(assoc-in [:deleted-gaps chat-id] deleted-gaps))))
|
|
|
|
{}
|
|
|
|
chat-ids))
|
|
|
|
|
|
|
|
(fx/defn update-ranges
|
|
|
|
[{:keys [db] :as cofx}]
|
|
|
|
(let [{:keys [topics from to]}
|
|
|
|
(get db :mailserver/current-request)
|
|
|
|
chat-ids (mapcat
|
|
|
|
:chat-ids
|
|
|
|
(-> (:mailserver/topics db)
|
|
|
|
(select-keys topics)
|
|
|
|
vals))
|
|
|
|
ranges (:mailserver/ranges db)
|
|
|
|
updated-ranges (into
|
|
|
|
{}
|
|
|
|
(keep
|
|
|
|
(fn [chat-id]
|
|
|
|
(let [chat-id (str chat-id)
|
|
|
|
{:keys [lowest-request-from
|
|
|
|
highest-request-to]
|
|
|
|
:as range}
|
|
|
|
(get ranges chat-id)]
|
|
|
|
[chat-id
|
|
|
|
(cond-> (assoc range :chat-id chat-id)
|
|
|
|
(or (nil? highest-request-to)
|
|
|
|
(> to highest-request-to))
|
|
|
|
(assoc :highest-request-to to)
|
|
|
|
|
|
|
|
(or (nil? lowest-request-from)
|
|
|
|
(< from lowest-request-from))
|
|
|
|
(assoc :lowest-request-from from))])))
|
|
|
|
chat-ids)]
|
2019-09-05 19:33:36 +02:00
|
|
|
(fx/merge cofx
|
|
|
|
{:db (update db :mailserver/ranges merge updated-ranges)
|
|
|
|
::json-rpc/call
|
|
|
|
(mapv (fn [chat-requests-range]
|
|
|
|
{:method "mailservers_addChatRequestRange"
|
|
|
|
:params [chat-requests-range]
|
|
|
|
:on-success #()
|
|
|
|
:on-failure
|
|
|
|
#(log/error "failed to save chat request range" %)})
|
|
|
|
(vals updated-ranges))})))
|
2019-04-16 22:09:18 +03:00
|
|
|
|
2019-05-01 14:02:13 +03:00
|
|
|
(defn prepare-new-gaps [new-gaps ranges {:keys [from to] :as req} chat-ids]
|
2019-04-16 22:09:18 +03:00
|
|
|
(into
|
|
|
|
{}
|
|
|
|
(comp
|
|
|
|
(map (fn [chat-id]
|
|
|
|
(let [gaps (get new-gaps chat-id)
|
|
|
|
{:keys [highest-request-to lowest-request-from]}
|
|
|
|
(get ranges chat-id)]
|
|
|
|
[chat-id (cond-> gaps
|
|
|
|
(and
|
|
|
|
(not (nil? highest-request-to))
|
|
|
|
(< highest-request-to from))
|
|
|
|
(conj {:chat-id chat-id
|
|
|
|
:from highest-request-to
|
|
|
|
:to from})
|
|
|
|
(and
|
|
|
|
(not (nil? lowest-request-from))
|
|
|
|
(< to lowest-request-from))
|
|
|
|
(conj {:chat-id chat-id
|
|
|
|
:from to
|
|
|
|
:to lowest-request-from}))])))
|
|
|
|
(keep (fn [[chat-id gaps]]
|
|
|
|
[chat-id
|
|
|
|
(into {}
|
|
|
|
(map (fn [gap]
|
|
|
|
(let [id (rand/guid)]
|
|
|
|
[id (assoc gap :id id)])))
|
|
|
|
gaps)])))
|
|
|
|
chat-ids))
|
|
|
|
|
|
|
|
(fx/defn update-gaps
|
2019-09-04 11:41:05 +02:00
|
|
|
[{:keys [db] :as cofx}]
|
2019-04-16 22:09:18 +03:00
|
|
|
(let [{:keys [topics] :as request} (get db :mailserver/current-request)
|
|
|
|
chat-ids (into #{}
|
|
|
|
(comp
|
|
|
|
(keep #(get-in db [:mailserver/topics %]))
|
|
|
|
(mapcat :chat-ids)
|
|
|
|
(map str))
|
|
|
|
topics)
|
|
|
|
|
|
|
|
{:keys [updated-gaps new-gaps deleted-gaps]}
|
|
|
|
(check-all-gaps (get db :mailserver/gaps) chat-ids request)
|
|
|
|
|
|
|
|
ranges (:mailserver/ranges db)
|
|
|
|
prepared-new-gaps (prepare-new-gaps new-gaps ranges request chat-ids)]
|
2019-09-04 11:41:05 +02:00
|
|
|
(fx/merge cofx
|
|
|
|
{:db
|
|
|
|
(reduce (fn [db chat-id]
|
|
|
|
(let [chats-deleted-gaps (get deleted-gaps chat-id)
|
|
|
|
chats-updated-gaps (merge (get updated-gaps chat-id)
|
|
|
|
(get prepared-new-gaps chat-id))]
|
|
|
|
(update-in db [:mailserver/gaps chat-id]
|
|
|
|
(fn [chat-gaps]
|
|
|
|
(-> (apply dissoc chat-gaps chats-deleted-gaps)
|
|
|
|
(merge chats-updated-gaps))))))
|
|
|
|
db
|
|
|
|
chat-ids)}
|
|
|
|
(data-store.mailservers/delete-gaps (mapcat val deleted-gaps))
|
|
|
|
(data-store.mailservers/save-gaps
|
|
|
|
(concat (mapcat vals (vals updated-gaps))
|
|
|
|
(mapcat vals (vals prepared-new-gaps)))))))
|
2019-04-16 22:09:18 +03:00
|
|
|
|
|
|
|
(fx/defn update-chats-and-gaps
|
|
|
|
[cofx cursor]
|
|
|
|
(when (or (nil? cursor)
|
|
|
|
(and (string? cursor)
|
|
|
|
(clojure.string/blank? cursor)))
|
|
|
|
(fx/merge
|
|
|
|
cofx
|
|
|
|
(update-gaps)
|
|
|
|
(update-ranges))))
|
2019-04-04 16:55:35 +03:00
|
|
|
|
|
|
|
(defn get-updated-mailserver-topics [db requested-topics from to]
|
|
|
|
(into
|
|
|
|
{}
|
|
|
|
(keep (fn [topic]
|
|
|
|
(when-let [config (get-in db [:mailserver/topics topic])]
|
2019-04-16 22:09:18 +03:00
|
|
|
[topic (update-mailserver-topic config
|
|
|
|
{:request-from from
|
|
|
|
:request-to to})])))
|
2019-04-04 16:55:35 +03:00
|
|
|
requested-topics))
|
2018-10-18 02:05:02 +02:00
|
|
|
|
|
|
|
(fx/defn update-mailserver-topics
|
|
|
|
"TODO: add support for cursors
|
|
|
|
if there is a cursor, do not update `last-request`"
|
2018-12-13 12:23:16 +01:00
|
|
|
[{:keys [db now] :as cofx} {:keys [request-id cursor]}]
|
2018-10-18 02:05:02 +02:00
|
|
|
(when-let [request (get db :mailserver/current-request)]
|
|
|
|
(let [{:keys [from to topics]} request
|
2019-04-04 16:55:35 +03:00
|
|
|
mailserver-topics (get-updated-mailserver-topics db topics from to)]
|
2018-10-18 02:05:02 +02:00
|
|
|
(log/info "mailserver: message request " request-id
|
|
|
|
"completed for mailserver topics" topics "from" from "to" to)
|
|
|
|
(if (empty? mailserver-topics)
|
|
|
|
;; when topics were deleted (filter was removed while request was pending)
|
|
|
|
(fx/merge cofx
|
|
|
|
{:db (dissoc db :mailserver/current-request)}
|
|
|
|
(process-next-messages-request))
|
2018-12-13 12:23:16 +01:00
|
|
|
;; If a cursor is returned, add cursor and fire request again
|
|
|
|
(if (seq cursor)
|
|
|
|
(when-let [mailserver (get-mailserver-when-ready cofx)]
|
2019-02-04 15:29:47 +01:00
|
|
|
(let [request-with-cursor (assoc request :cursor cursor)]
|
2019-04-16 22:09:18 +03:00
|
|
|
{:db (assoc db :mailserver/current-request request-with-cursor)
|
2019-08-20 11:53:10 +02:00
|
|
|
:mailserver/request-messages {:mailserver mailserver
|
2019-04-16 22:09:18 +03:00
|
|
|
:request request-with-cursor}}))
|
|
|
|
(let [{:keys [gap chat-id]} request]
|
|
|
|
(fx/merge cofx
|
|
|
|
{:db (-> db
|
|
|
|
(dissoc :mailserver/current-request)
|
|
|
|
(update :mailserver/requests-from
|
|
|
|
#(apply dissoc % topics))
|
|
|
|
(update :mailserver/requests-to
|
|
|
|
#(apply dissoc % topics))
|
|
|
|
(update :mailserver/topics merge mailserver-topics)
|
|
|
|
(update :mailserver/fetching-gaps-in-progress
|
|
|
|
(fn [gaps]
|
|
|
|
(if gap
|
|
|
|
(update gaps chat-id dissoc gap)
|
|
|
|
gaps)))
|
|
|
|
(update :mailserver/planned-gap-requests
|
|
|
|
dissoc gap))
|
2019-09-05 00:03:04 +02:00
|
|
|
::json-rpc/call
|
|
|
|
(mapv (fn [[topic mailserver-topic]]
|
|
|
|
{:method "mailservers_addMailserverTopic"
|
|
|
|
:params [(assoc mailserver-topic :topic topic)]
|
|
|
|
:on-success #(log/debug "added mailserver-topic successfully")
|
|
|
|
:on-failure #(log/error "failed to delete mailserver topic" %)})
|
|
|
|
mailserver-topics)}
|
2019-04-16 22:09:18 +03:00
|
|
|
(process-next-messages-request))))))))
|
2018-10-18 02:05:02 +02:00
|
|
|
|
2018-11-22 10:22:02 +01:00
|
|
|
(fx/defn retry-next-messages-request
|
|
|
|
[{:keys [db] :as cofx}]
|
|
|
|
(fx/merge cofx
|
|
|
|
{:db (dissoc db :mailserver/request-error)}
|
|
|
|
(process-next-messages-request)))
|
|
|
|
|
2018-12-13 12:23:16 +01:00
|
|
|
;; At some point we should update `last-request`, as eventually we want to move
|
|
|
|
;; on, rather then keep asking for the same data, say after n amounts of attempts
|
2018-11-22 10:22:02 +01:00
|
|
|
(fx/defn handle-request-error
|
|
|
|
[{:keys [db]} error]
|
|
|
|
{:db (-> db
|
2019-02-27 18:43:56 +05:45
|
|
|
(assoc :mailserver/request-error error)
|
2018-11-22 10:22:02 +01:00
|
|
|
(dissoc :mailserver/current-request
|
|
|
|
:mailserver/pending-requests))})
|
|
|
|
|
|
|
|
(fx/defn handle-request-completed
|
2019-04-04 16:55:35 +03:00
|
|
|
[{{:keys [chats]} :db :as cofx}
|
|
|
|
{:keys [requestID lastEnvelopeHash cursor errorMessage]}]
|
2019-07-03 16:29:01 +02:00
|
|
|
(when (multiaccounts.model/logged-in? cofx)
|
2019-02-27 18:43:56 +05:45
|
|
|
(if (empty? errorMessage)
|
|
|
|
(let [never-synced-chats-in-request
|
|
|
|
(->> (chats->never-synced-public-chats chats)
|
|
|
|
(filter (fn [[k v]] (= requestID (:join-time-mail-request-id v))))
|
|
|
|
keys)]
|
|
|
|
(if (seq never-synced-chats-in-request)
|
|
|
|
(if (= lastEnvelopeHash
|
|
|
|
"0x0000000000000000000000000000000000000000000000000000000000000000")
|
|
|
|
(fx/merge
|
|
|
|
cofx
|
|
|
|
{:mailserver/increase-limit []
|
|
|
|
:dispatch-n (map
|
|
|
|
#(identity [:chat.ui/join-time-messages-checked %])
|
|
|
|
never-synced-chats-in-request)}
|
2019-04-16 22:09:18 +03:00
|
|
|
(update-chats-and-gaps cursor)
|
2019-02-27 18:43:56 +05:45
|
|
|
(update-mailserver-topics {:request-id requestID
|
|
|
|
:cursor cursor}))
|
|
|
|
(fx/merge
|
|
|
|
cofx
|
|
|
|
{:mailserver/increase-limit []
|
|
|
|
:dispatch-later (vec
|
|
|
|
(map
|
|
|
|
#(identity
|
|
|
|
{:ms 1000
|
|
|
|
:dispatch [:chat.ui/join-time-messages-checked %]})
|
|
|
|
never-synced-chats-in-request))}
|
2019-04-16 22:09:18 +03:00
|
|
|
(update-chats-and-gaps cursor)
|
2019-02-27 18:43:56 +05:45
|
|
|
(update-mailserver-topics {:request-id requestID
|
|
|
|
:cursor cursor})))
|
|
|
|
(fx/merge
|
|
|
|
cofx
|
|
|
|
{:mailserver/increase-limit []}
|
2019-04-16 22:09:18 +03:00
|
|
|
(update-chats-and-gaps cursor)
|
2019-02-27 18:43:56 +05:45
|
|
|
(update-mailserver-topics {:request-id requestID
|
|
|
|
:cursor cursor}))))
|
|
|
|
(handle-request-error cofx errorMessage))))
|
2018-11-22 10:22:02 +01:00
|
|
|
|
|
|
|
(fx/defn show-request-error-popup
|
|
|
|
[{:keys [db]}]
|
|
|
|
(let [mailserver-error (:mailserver/request-error db)]
|
|
|
|
{:utils/show-confirmation
|
|
|
|
{:title (i18n/label :t/mailserver-request-error-title)
|
|
|
|
:content (i18n/label :t/mailserver-request-error-content {:error mailserver-error})
|
|
|
|
:on-accept #(re-frame/dispatch [:mailserver.ui/retry-request-pressed])
|
|
|
|
:confirm-button-text (i18n/label :t/mailserver-request-retry)}}))
|
|
|
|
|
2018-12-12 09:55:59 +01:00
|
|
|
(fx/defn fetch-history
|
Adds topic negotiation and partitioned topic
All the code has been implemented in statusgo: status-im/status-go#1466
Basically all the whisper filter management is done at that level.
Technical description
On startup we load all chats and send a list of them to status go:
For a public chat: {:chatId "status"}, we create a single filter, based on the name of the chat.
For each contact added by us, each user in a group chat and each one to one chat open, we send:
{:chatId "0x", :oneToOne true}. This will create a chats, to listen to their contact code.
Any previously negotiated topic is also returned.
Once loaded, we create our filters, and upsert the mailserver topics, both of which are solely based on the filters loaded.
In order to remove a chat, we delete/stopwatching first the the filter in status-react and then ask status-go to remove the filter. For a public chat we always remove, for a one-to-one we remove only if the user is not in our contacts, or in a group chat or we have a chat open. Negotiated topics are never removed, as otherwise the other user won't be able to contact us anymore.
On stopping whisper we don't have to ask status-go to remove filters as they are removed automatically.
Some more logic can be pushed in status-go, but that will be in subsequent PRs.
Signed-off-by: Andrea Maria Piana <andrea.maria.piana@gmail.com>
2019-06-05 13:34:04 +02:00
|
|
|
"Retrive a list of topics given a chat id, set them to the specified time interval
|
|
|
|
and start a mailserver request"
|
2019-04-04 16:55:35 +03:00
|
|
|
[{:keys [db] :as cofx} chat-id {:keys [from to]}]
|
Adds topic negotiation and partitioned topic
All the code has been implemented in statusgo: status-im/status-go#1466
Basically all the whisper filter management is done at that level.
Technical description
On startup we load all chats and send a list of them to status go:
For a public chat: {:chatId "status"}, we create a single filter, based on the name of the chat.
For each contact added by us, each user in a group chat and each one to one chat open, we send:
{:chatId "0x", :oneToOne true}. This will create a chats, to listen to their contact code.
Any previously negotiated topic is also returned.
Once loaded, we create our filters, and upsert the mailserver topics, both of which are solely based on the filters loaded.
In order to remove a chat, we delete/stopwatching first the the filter in status-react and then ask status-go to remove the filter. For a public chat we always remove, for a one-to-one we remove only if the user is not in our contacts, or in a group chat or we have a chat open. Negotiated topics are never removed, as otherwise the other user won't be able to contact us anymore.
On stopping whisper we don't have to ask status-go to remove filters as they are removed automatically.
Some more logic can be pushed in status-go, but that will be in subsequent PRs.
Signed-off-by: Andrea Maria Piana <andrea.maria.piana@gmail.com>
2019-06-05 13:34:04 +02:00
|
|
|
(let [topics (mailserver.topics/topics-for-chat
|
|
|
|
db
|
|
|
|
chat-id)]
|
|
|
|
(log/debug "fetch-history" "chat-id:" chat-id "from-timestamp:" from "topics:" topics)
|
2019-04-04 16:55:35 +03:00
|
|
|
(fx/merge cofx
|
Adds topic negotiation and partitioned topic
All the code has been implemented in statusgo: status-im/status-go#1466
Basically all the whisper filter management is done at that level.
Technical description
On startup we load all chats and send a list of them to status go:
For a public chat: {:chatId "status"}, we create a single filter, based on the name of the chat.
For each contact added by us, each user in a group chat and each one to one chat open, we send:
{:chatId "0x", :oneToOne true}. This will create a chats, to listen to their contact code.
Any previously negotiated topic is also returned.
Once loaded, we create our filters, and upsert the mailserver topics, both of which are solely based on the filters loaded.
In order to remove a chat, we delete/stopwatching first the the filter in status-react and then ask status-go to remove the filter. For a public chat we always remove, for a one-to-one we remove only if the user is not in our contacts, or in a group chat or we have a chat open. Negotiated topics are never removed, as otherwise the other user won't be able to contact us anymore.
On stopping whisper we don't have to ask status-go to remove filters as they are removed automatically.
Some more logic can be pushed in status-go, but that will be in subsequent PRs.
Signed-off-by: Andrea Maria Piana <andrea.maria.piana@gmail.com>
2019-06-05 13:34:04 +02:00
|
|
|
{:db (reduce
|
|
|
|
(fn [db topic]
|
|
|
|
(cond-> (assoc-in db [:mailserver/requests-from topic] from)
|
|
|
|
|
|
|
|
to
|
|
|
|
(assoc-in [:mailserver/requests-to topic] to)))
|
|
|
|
db
|
|
|
|
topics)}
|
2019-04-04 16:55:35 +03:00
|
|
|
(process-next-messages-request))))
|
|
|
|
|
|
|
|
(fx/defn fill-the-gap
|
Adds topic negotiation and partitioned topic
All the code has been implemented in statusgo: status-im/status-go#1466
Basically all the whisper filter management is done at that level.
Technical description
On startup we load all chats and send a list of them to status go:
For a public chat: {:chatId "status"}, we create a single filter, based on the name of the chat.
For each contact added by us, each user in a group chat and each one to one chat open, we send:
{:chatId "0x", :oneToOne true}. This will create a chats, to listen to their contact code.
Any previously negotiated topic is also returned.
Once loaded, we create our filters, and upsert the mailserver topics, both of which are solely based on the filters loaded.
In order to remove a chat, we delete/stopwatching first the the filter in status-react and then ask status-go to remove the filter. For a public chat we always remove, for a one-to-one we remove only if the user is not in our contacts, or in a group chat or we have a chat open. Negotiated topics are never removed, as otherwise the other user won't be able to contact us anymore.
On stopping whisper we don't have to ask status-go to remove filters as they are removed automatically.
Some more logic can be pushed in status-go, but that will be in subsequent PRs.
Signed-off-by: Andrea Maria Piana <andrea.maria.piana@gmail.com>
2019-06-05 13:34:04 +02:00
|
|
|
[{:keys [db] :as cofx} {:keys [gaps topics chat-id]}]
|
2019-04-16 22:09:18 +03:00
|
|
|
(let [mailserver (get-mailserver-when-ready cofx)
|
|
|
|
requests (into {}
|
|
|
|
(map
|
|
|
|
(fn [{:keys [from to id]}]
|
|
|
|
[id
|
|
|
|
{:from (max from
|
Adds topic negotiation and partitioned topic
All the code has been implemented in statusgo: status-im/status-go#1466
Basically all the whisper filter management is done at that level.
Technical description
On startup we load all chats and send a list of them to status go:
For a public chat: {:chatId "status"}, we create a single filter, based on the name of the chat.
For each contact added by us, each user in a group chat and each one to one chat open, we send:
{:chatId "0x", :oneToOne true}. This will create a chats, to listen to their contact code.
Any previously negotiated topic is also returned.
Once loaded, we create our filters, and upsert the mailserver topics, both of which are solely based on the filters loaded.
In order to remove a chat, we delete/stopwatching first the the filter in status-react and then ask status-go to remove the filter. For a public chat we always remove, for a one-to-one we remove only if the user is not in our contacts, or in a group chat or we have a chat open. Negotiated topics are never removed, as otherwise the other user won't be able to contact us anymore.
On stopping whisper we don't have to ask status-go to remove filters as they are removed automatically.
Some more logic can be pushed in status-go, but that will be in subsequent PRs.
Signed-off-by: Andrea Maria Piana <andrea.maria.piana@gmail.com>
2019-06-05 13:34:04 +02:00
|
|
|
(- to constants/max-request-range))
|
2019-04-16 22:09:18 +03:00
|
|
|
:to to
|
|
|
|
:force-to? true
|
Adds topic negotiation and partitioned topic
All the code has been implemented in statusgo: status-im/status-go#1466
Basically all the whisper filter management is done at that level.
Technical description
On startup we load all chats and send a list of them to status go:
For a public chat: {:chatId "status"}, we create a single filter, based on the name of the chat.
For each contact added by us, each user in a group chat and each one to one chat open, we send:
{:chatId "0x", :oneToOne true}. This will create a chats, to listen to their contact code.
Any previously negotiated topic is also returned.
Once loaded, we create our filters, and upsert the mailserver topics, both of which are solely based on the filters loaded.
In order to remove a chat, we delete/stopwatching first the the filter in status-react and then ask status-go to remove the filter. For a public chat we always remove, for a one-to-one we remove only if the user is not in our contacts, or in a group chat or we have a chat open. Negotiated topics are never removed, as otherwise the other user won't be able to contact us anymore.
On stopping whisper we don't have to ask status-go to remove filters as they are removed automatically.
Some more logic can be pushed in status-go, but that will be in subsequent PRs.
Signed-off-by: Andrea Maria Piana <andrea.maria.piana@gmail.com>
2019-06-05 13:34:04 +02:00
|
|
|
:topics topics
|
|
|
|
:gap-topics topics
|
2019-04-16 22:09:18 +03:00
|
|
|
:chat-id chat-id
|
|
|
|
:gap id}]))
|
|
|
|
gaps)
|
|
|
|
first-request (val (first requests))
|
|
|
|
current-request (:mailserver/current-request db)]
|
|
|
|
(cond-> {:db (-> db
|
|
|
|
(assoc :mailserver/planned-gap-requests requests)
|
|
|
|
(update :mailserver/fetching-gaps-in-progress
|
|
|
|
assoc chat-id requests))}
|
|
|
|
(not current-request)
|
|
|
|
(-> (assoc-in [:db :mailserver/current-request] first-request)
|
|
|
|
(assoc :mailserver/request-messages
|
2019-08-20 11:53:10 +02:00
|
|
|
{:mailserver mailserver
|
2019-04-16 22:09:18 +03:00
|
|
|
:request first-request})))))
|
2018-10-18 02:05:02 +02:00
|
|
|
|
|
|
|
(fx/defn resend-request
|
|
|
|
[{:keys [db] :as cofx} {:keys [request-id]}]
|
2019-04-13 07:08:22 +03:00
|
|
|
(let [current-request (:mailserver/current-request db)
|
|
|
|
gap-request? (executing-gap-request? db)]
|
2019-02-27 12:50:41 +01:00
|
|
|
;; no inflight request, do nothing
|
|
|
|
(when (and current-request
|
|
|
|
;; the request was never successful
|
|
|
|
(or (nil? request-id)
|
|
|
|
;; we haven't received the request-id yet, but has expired,
|
|
|
|
;; so we retry even though we are not sure it's the current
|
|
|
|
;; request that failed
|
|
|
|
(nil? (:request-id current-request))
|
|
|
|
;; this is the same request that we are currently processing
|
|
|
|
(= request-id (:request-id current-request))))
|
|
|
|
|
Adds topic negotiation and partitioned topic
All the code has been implemented in statusgo: status-im/status-go#1466
Basically all the whisper filter management is done at that level.
Technical description
On startup we load all chats and send a list of them to status go:
For a public chat: {:chatId "status"}, we create a single filter, based on the name of the chat.
For each contact added by us, each user in a group chat and each one to one chat open, we send:
{:chatId "0x", :oneToOne true}. This will create a chats, to listen to their contact code.
Any previously negotiated topic is also returned.
Once loaded, we create our filters, and upsert the mailserver topics, both of which are solely based on the filters loaded.
In order to remove a chat, we delete/stopwatching first the the filter in status-react and then ask status-go to remove the filter. For a public chat we always remove, for a one-to-one we remove only if the user is not in our contacts, or in a group chat or we have a chat open. Negotiated topics are never removed, as otherwise the other user won't be able to contact us anymore.
On stopping whisper we don't have to ask status-go to remove filters as they are removed automatically.
Some more logic can be pushed in status-go, but that will be in subsequent PRs.
Signed-off-by: Andrea Maria Piana <andrea.maria.piana@gmail.com>
2019-06-05 13:34:04 +02:00
|
|
|
(if (<= constants/maximum-number-of-attempts
|
2019-02-27 12:50:41 +01:00
|
|
|
(:attempts current-request))
|
|
|
|
(fx/merge cofx
|
|
|
|
{:db (update db :mailserver/current-request dissoc :attempts)}
|
|
|
|
(change-mailserver))
|
2019-04-13 07:08:22 +03:00
|
|
|
(let [mailserver (get-mailserver-when-ready cofx)
|
|
|
|
offline? (= :offline (:network-status db))]
|
|
|
|
(cond
|
|
|
|
(and gap-request? offline?)
|
|
|
|
{:db (-> db
|
|
|
|
(dissoc :mailserver/current-request)
|
|
|
|
(update :mailserver/fetching-gaps-in-progress
|
2019-04-16 22:09:18 +03:00
|
|
|
dissoc (:chat-id current-request))
|
|
|
|
(dissoc :mailserver/planned-gap-requests))}
|
2019-04-13 07:08:22 +03:00
|
|
|
|
|
|
|
mailserver
|
2019-08-20 11:53:10 +02:00
|
|
|
(let [{:keys [topics from to cursor limit] :as request} current-request]
|
2019-04-13 07:08:22 +03:00
|
|
|
(log/info "mailserver: message request " request-id "expired for mailserver topic" topics "from" from "to" to "cursor" cursor "limit" (decrease-limit))
|
|
|
|
{:db (update-in db [:mailserver/current-request :attempts] inc)
|
|
|
|
:mailserver/decrease-limit []
|
2019-08-20 11:53:10 +02:00
|
|
|
:mailserver/request-messages {:mailserver mailserver
|
2019-04-13 07:08:22 +03:00
|
|
|
:request (assoc request :limit (decrease-limit))}})
|
|
|
|
|
|
|
|
:else
|
|
|
|
{:mailserver/decrease-limit []}))))))
|
2018-10-18 02:05:02 +02:00
|
|
|
|
|
|
|
(fx/defn initialize-mailserver
|
2019-09-04 16:43:01 +02:00
|
|
|
[cofx]
|
2018-10-18 02:05:02 +02:00
|
|
|
(fx/merge cofx
|
Adds topic negotiation and partitioned topic
All the code has been implemented in statusgo: status-im/status-go#1466
Basically all the whisper filter management is done at that level.
Technical description
On startup we load all chats and send a list of them to status go:
For a public chat: {:chatId "status"}, we create a single filter, based on the name of the chat.
For each contact added by us, each user in a group chat and each one to one chat open, we send:
{:chatId "0x", :oneToOne true}. This will create a chats, to listen to their contact code.
Any previously negotiated topic is also returned.
Once loaded, we create our filters, and upsert the mailserver topics, both of which are solely based on the filters loaded.
In order to remove a chat, we delete/stopwatching first the the filter in status-react and then ask status-go to remove the filter. For a public chat we always remove, for a one-to-one we remove only if the user is not in our contacts, or in a group chat or we have a chat open. Negotiated topics are never removed, as otherwise the other user won't be able to contact us anymore.
On stopping whisper we don't have to ask status-go to remove filters as they are removed automatically.
Some more logic can be pushed in status-go, but that will be in subsequent PRs.
Signed-off-by: Andrea Maria Piana <andrea.maria.piana@gmail.com>
2019-06-05 13:34:04 +02:00
|
|
|
{:mailserver/set-limit constants/default-limit}
|
2018-10-18 02:05:02 +02:00
|
|
|
(set-current-mailserver)))
|
|
|
|
|
|
|
|
(def enode-address-regex #"enode://[a-zA-Z0-9]+\@\b\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\b:(\d{1,5})")
|
|
|
|
(def enode-url-regex #"enode://[a-zA-Z0-9]+:(.+)\@\b\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\b:(\d{1,5})")
|
|
|
|
|
|
|
|
(defn- extract-address-components [address]
|
|
|
|
(rest (re-matches #"enode://(.*)@(.*)" address)))
|
|
|
|
|
|
|
|
(defn- extract-url-components [address]
|
|
|
|
(rest (re-matches #"enode://(.*?):(.*)@(.*)" address)))
|
|
|
|
|
|
|
|
(defn valid-enode-url? [address]
|
|
|
|
(re-matches enode-url-regex address))
|
|
|
|
|
|
|
|
(defn valid-enode-address? [address]
|
|
|
|
(re-matches enode-address-regex address))
|
|
|
|
|
|
|
|
(defn build-url [address password]
|
|
|
|
(let [[initial host] (extract-address-components address)]
|
|
|
|
(str "enode://" initial ":" password "@" host)))
|
|
|
|
|
|
|
|
(fx/defn set-input [{:keys [db]} input-key value]
|
|
|
|
{:db (update
|
|
|
|
db
|
|
|
|
:mailserver.edit/mailserver
|
|
|
|
assoc
|
|
|
|
input-key
|
|
|
|
{:value value
|
|
|
|
:error (case input-key
|
|
|
|
:id false
|
|
|
|
:name (string/blank? value)
|
|
|
|
:url (not (valid-enode-url? value)))})})
|
|
|
|
|
|
|
|
(defn- address->mailserver [address]
|
|
|
|
(let [[enode password url :as response] (extract-url-components address)]
|
|
|
|
(cond-> {:address (if (seq response)
|
|
|
|
(str "enode://" enode "@" url)
|
|
|
|
address)
|
|
|
|
:user-defined true}
|
|
|
|
password (assoc :password password))))
|
|
|
|
|
|
|
|
(defn- build [id mailserver-name address]
|
|
|
|
(assoc (address->mailserver address)
|
|
|
|
:id id
|
|
|
|
:name mailserver-name))
|
|
|
|
|
|
|
|
(def default? (comp not :user-defined fetch))
|
|
|
|
|
2018-09-24 17:59:02 +02:00
|
|
|
(fx/defn edit [{:keys [db] :as cofx} id]
|
2019-09-04 16:43:01 +02:00
|
|
|
(let [{:keys [id address password name]} (fetch cofx id)
|
|
|
|
url (when address (build-url address password))]
|
2018-09-24 17:59:02 +02:00
|
|
|
(fx/merge cofx
|
|
|
|
(set-input :id id)
|
|
|
|
(set-input :url (str url))
|
|
|
|
(set-input :name (str name))
|
|
|
|
(navigation/navigate-to-cofx :edit-mailserver nil))))
|
|
|
|
|
2019-09-04 16:43:01 +02:00
|
|
|
(defn mailserver->rpc
|
|
|
|
[mailserver current-fleet]
|
|
|
|
(-> mailserver
|
|
|
|
(assoc :fleet (name current-fleet))
|
|
|
|
(update :id name)))
|
|
|
|
|
2018-09-24 17:59:02 +02:00
|
|
|
(fx/defn upsert
|
2019-07-03 16:29:01 +02:00
|
|
|
[{{:mailserver.edit/keys [mailserver] :keys [multiaccount] :as db} :db
|
2018-09-24 18:27:04 +02:00
|
|
|
random-id-generator :random-id-generator :as cofx}]
|
2018-10-18 02:05:02 +02:00
|
|
|
(let [{:keys [name url id]} mailserver
|
2019-10-22 02:05:15 +02:00
|
|
|
current-fleet (node/current-fleet-key db)
|
2018-06-01 10:04:48 +02:00
|
|
|
mailserver (build
|
|
|
|
(or (:value id)
|
2018-09-24 18:27:04 +02:00
|
|
|
(keyword (string/replace (random-id-generator) "-" "")))
|
2018-06-01 10:04:48 +02:00
|
|
|
(:value name)
|
|
|
|
(:value url))
|
2018-09-24 17:59:02 +02:00
|
|
|
current (connected? cofx (:id mailserver))]
|
2018-06-01 10:04:48 +02:00
|
|
|
{:db (-> db
|
2018-10-18 02:05:02 +02:00
|
|
|
(dissoc :mailserver.edit/mailserver)
|
|
|
|
(assoc-in [:mailserver/mailservers current-fleet (:id mailserver)] mailserver))
|
2019-09-04 16:43:01 +02:00
|
|
|
::json-rpc/call
|
|
|
|
[{:method "mailservers_addMailserver"
|
|
|
|
:params [(mailserver->rpc mailserver current-fleet)]
|
|
|
|
:on-success (fn []
|
|
|
|
;; we naively logout if the user is connected to
|
|
|
|
;; the edited mailserver
|
|
|
|
(when current
|
|
|
|
(re-frame/dispatch
|
|
|
|
[:multiaccounts.logout.ui/logout-confirmed]))
|
|
|
|
(log/debug "saved mailserver" id "successfuly"))
|
|
|
|
:on-failure #(log/error "failed to save mailserver" id %)}]
|
2018-06-01 10:04:48 +02:00
|
|
|
:dispatch [:navigate-back]}))
|
2018-09-06 12:04:12 +02:00
|
|
|
|
2019-09-04 16:43:01 +02:00
|
|
|
(defn can-delete?
|
|
|
|
[cofx id]
|
|
|
|
(not (or (default? cofx id)
|
|
|
|
(connected? cofx id))))
|
|
|
|
|
2018-10-18 02:05:02 +02:00
|
|
|
(fx/defn delete
|
|
|
|
[{:keys [db] :as cofx} id]
|
2019-09-04 16:43:01 +02:00
|
|
|
(if (can-delete? cofx id)
|
|
|
|
{:db (update-in db
|
2019-10-22 02:05:15 +02:00
|
|
|
[:mailserver/mailservers (node/current-fleet-key db)]
|
2019-09-04 16:43:01 +02:00
|
|
|
dissoc id)
|
|
|
|
::json-rpc/call
|
|
|
|
[{:method "mailservers_deleteMailserver"
|
|
|
|
:params [(name id)]
|
|
|
|
:on-success #(log/debug "deleted mailserver" id)
|
|
|
|
:on-failure #(log/error "failed to delete mailserver" id %)}]
|
|
|
|
:dispatch [:navigate-back]}
|
|
|
|
{:dispatch [:navigate-back]}))
|
2018-10-18 02:05:02 +02:00
|
|
|
|
2018-09-24 17:59:02 +02:00
|
|
|
(fx/defn show-connection-confirmation
|
|
|
|
[{:keys [db]} mailserver-id]
|
2019-10-22 02:05:15 +02:00
|
|
|
(let [current-fleet (node/current-fleet-key db)]
|
2018-09-06 12:04:12 +02:00
|
|
|
{:ui/show-confirmation
|
|
|
|
{:title (i18n/label :t/close-app-title)
|
2018-10-18 02:05:02 +02:00
|
|
|
:content (i18n/label :t/connect-mailserver-content
|
|
|
|
{:name (get-in db [:mailserver/mailservers current-fleet mailserver-id :name])})
|
2018-09-06 12:04:12 +02:00
|
|
|
:confirm-button-text (i18n/label :t/close-app-button)
|
|
|
|
:on-accept #(re-frame/dispatch [:mailserver.ui/connect-confirmed current-fleet mailserver-id])
|
|
|
|
:on-cancel nil}}))
|
|
|
|
|
2018-09-24 17:59:02 +02:00
|
|
|
(fx/defn show-delete-confirmation
|
|
|
|
[{:keys [db]} mailserver-id]
|
2018-09-06 12:04:12 +02:00
|
|
|
{:ui/show-confirmation
|
|
|
|
{:title (i18n/label :t/delete-mailserver-title)
|
|
|
|
:content (i18n/label :t/delete-mailserver-are-you-sure)
|
|
|
|
:confirm-button-text (i18n/label :t/delete-mailserver)
|
|
|
|
:on-accept #(re-frame/dispatch [:mailserver.ui/delete-confirmed mailserver-id])}})
|
|
|
|
|
2018-09-24 17:59:02 +02:00
|
|
|
(fx/defn set-url-from-qr
|
|
|
|
[cofx url]
|
2018-10-18 02:05:02 +02:00
|
|
|
(assoc (set-input cofx :url url)
|
2018-09-06 12:04:12 +02:00
|
|
|
:dispatch [:navigate-back]))
|
2018-10-18 02:05:02 +02:00
|
|
|
|
|
|
|
(fx/defn save-settings
|
|
|
|
[{:keys [db] :as cofx} current-fleet mailserver-id]
|
|
|
|
(let [{:keys [address]} (fetch-current cofx)
|
2019-07-03 16:29:01 +02:00
|
|
|
settings (get-in db [:multiaccount :settings])
|
2019-03-01 10:36:49 +01:00
|
|
|
;; Check if previous mailserver was pinned
|
|
|
|
pinned? (get-in settings [:mailserver current-fleet])]
|
2018-10-18 02:05:02 +02:00
|
|
|
(fx/merge cofx
|
|
|
|
{:db (assoc db :mailserver/current-id mailserver-id)
|
|
|
|
:mailserver/remove-peer address}
|
|
|
|
(connect-to-mailserver)
|
2019-03-01 10:36:49 +01:00
|
|
|
(when pinned?
|
2019-07-03 16:29:01 +02:00
|
|
|
(multiaccounts.update/update-settings (assoc-in settings [:mailserver current-fleet] mailserver-id)
|
|
|
|
{})))))
|
2019-03-01 10:36:49 +01:00
|
|
|
|
|
|
|
(fx/defn unpin
|
|
|
|
[{:keys [db] :as cofx}]
|
2019-10-22 02:05:15 +02:00
|
|
|
(let [current-fleet (node/current-fleet-key db)
|
2019-07-03 16:29:01 +02:00
|
|
|
settings (get-in db [:multiaccount :settings])]
|
2019-03-01 10:36:49 +01:00
|
|
|
(fx/merge cofx
|
2019-07-03 16:29:01 +02:00
|
|
|
(multiaccounts.update/update-settings (update settings :mailserver dissoc current-fleet)
|
|
|
|
{})
|
2019-03-01 10:36:49 +01:00
|
|
|
(change-mailserver))))
|
|
|
|
|
|
|
|
(fx/defn pin
|
|
|
|
[{:keys [db] :as cofx}]
|
2019-10-22 02:05:15 +02:00
|
|
|
(let [current-fleet (node/current-fleet-key db)
|
2019-03-01 10:36:49 +01:00
|
|
|
mailserver-id (:mailserver/current-id db)
|
2019-07-03 16:29:01 +02:00
|
|
|
settings (get-in db [:multiaccount :settings])]
|
2019-03-01 10:36:49 +01:00
|
|
|
(fx/merge cofx
|
2019-07-03 16:29:01 +02:00
|
|
|
(multiaccounts.update/update-settings (assoc-in settings [:mailserver current-fleet] mailserver-id)
|
|
|
|
{}))))
|
2019-04-16 22:09:18 +03:00
|
|
|
|
2019-09-04 11:41:05 +02:00
|
|
|
(fx/defn load-gaps-fx [{:keys [db] :as cofx} chat-id]
|
2019-04-16 22:09:18 +03:00
|
|
|
(when-not (get-in db [:chats chat-id :gaps-loaded?])
|
2019-09-04 11:41:05 +02:00
|
|
|
(let [success-fn #(re-frame/dispatch [::gaps-loaded %1 %2])]
|
|
|
|
(data-store.mailservers/load-gaps cofx chat-id success-fn))))
|
|
|
|
|
|
|
|
(fx/defn load-gaps
|
|
|
|
{:events [::gaps-loaded]}
|
|
|
|
[{:keys [db now] :as cofx} chat-id gaps]
|
|
|
|
(let [now-s (quot now 1000)
|
|
|
|
outdated-gaps (into [] (comp (filter #(< (:to %)
|
|
|
|
(- now-s constants/max-gaps-range)))
|
|
|
|
(map :id))
|
|
|
|
(vals gaps))
|
|
|
|
gaps (apply dissoc gaps outdated-gaps)]
|
|
|
|
(fx/merge
|
|
|
|
cofx
|
|
|
|
{:db
|
|
|
|
(-> db
|
|
|
|
(assoc-in [:chats chat-id :gaps-loaded?] true)
|
|
|
|
(assoc-in [:mailserver/gaps chat-id] gaps))}
|
|
|
|
|
|
|
|
(data-store.mailservers/delete-gaps outdated-gaps))))
|