diff --git a/snikket/Client.hx b/snikket/Client.hx index a967bb3..7285c6f 100644 --- a/snikket/Client.hx +++ b/snikket/Client.hx @@ -522,6 +522,18 @@ class Client extends EventEmitter { }); } + /** + Destroy local data for this account + + @param completely if true chats, messages, etc will be deleted as well + **/ + public function logout(completely: Bool) { + // TODO: unregister from all push notifications + stream.disconnect(); + // TODO: FAST invalidate https://xmpp.org/extensions/xep-0484.html#invalidation + persistence.removeAccount(accountId(), completely); + } + /** Sets the password to be used in response to the password needed event diff --git a/snikket/GenericStream.hx b/snikket/GenericStream.hx index 5fe7b63..cb11984 100644 --- a/snikket/GenericStream.hx +++ b/snikket/GenericStream.hx @@ -21,6 +21,7 @@ abstract class GenericStream extends EventEmitter { /* Connections and streams */ abstract public function connect(jid:String, sm:Null):Void; + abstract public function disconnect():Void; abstract public function sendStanza(stanza:Stanza):Void; abstract public function newId():String; abstract public function onIq(type:IqRequestType, tag:String, xmlns:String, handler:(Stanza)->IqResult):Void; diff --git a/snikket/Persistence.hx b/snikket/Persistence.hx index e8ab6ad..03eaddb 100644 --- a/snikket/Persistence.hx +++ b/snikket/Persistence.hx @@ -26,6 +26,7 @@ interface Persistence { public function getCaps(ver:String, callback: (Null)->Void):Void; public function storeLogin(login:String, clientId:String, displayName:String, token:Null):Void; public function getLogin(login:String, callback:(clientId:Null, token:Null, fastCount: Int, displayName:Null)->Void):Void; + public function removeAccount(accountId: String, completely:Bool):Void; public function storeStreamManagement(accountId:String, data:BytesData):Void; public function getStreamManagement(accountId:String, callback: (Null)->Void):Void; public function storeService(accountId:String, serviceId:String, name:Null, node:Null, caps:Caps):Void; diff --git a/snikket/persistence/Custom.hx b/snikket/persistence/Custom.hx index 469e490..df489d3 100644 --- a/snikket/persistence/Custom.hx +++ b/snikket/persistence/Custom.hx @@ -123,6 +123,11 @@ class Custom implements Persistence { backing.storeStreamManagement(accountId, sm); } + @HaxeCBridge.noemit + public function removeAccount(accountId:String, completely:Bool) { + backing.removeAccount(accountId, completely); + } + @HaxeCBridge.noemit public function getStreamManagement(accountId:String, callback: (BytesData)->Void) { backing.getStreamManagement(accountId, callback); diff --git a/snikket/persistence/Dummy.hx b/snikket/persistence/Dummy.hx index eddfd9f..28e5641 100644 --- a/snikket/persistence/Dummy.hx +++ b/snikket/persistence/Dummy.hx @@ -97,6 +97,9 @@ class Dummy implements Persistence { callback(null, null, 0, null); } + @HaxeCBridge.noemit + public function removeAccount(accountId:String, completely:Bool) { } + @HaxeCBridge.noemit public function storeStreamManagement(accountId:String, sm:BytesData) { } diff --git a/snikket/persistence/Sqlite.hx b/snikket/persistence/Sqlite.hx index 66b5f95..1312424 100644 --- a/snikket/persistence/Sqlite.hx +++ b/snikket/persistence/Sqlite.hx @@ -412,6 +412,32 @@ class Sqlite implements Persistence { callback(null, null, 0, null); } + @HaxeCBridge.noemit + public function removeAccount(accountId:String, completely:Bool) { + var q = new StringBuf(); + q.add("DELETE FROM logins WHERE login="); + db.addValue(q, accountId); + db.request(q.toString()); + // TODO stream managemento + + if (!completely) return; + + var q = new StringBuf(); + q.add("DELETE FROM messages WHERE account_id="); + db.addValue(q, accountId); + db.request(q.toString()); + + var q = new StringBuf(); + q.add("DELETE FROM chats WHERE account_id="); + db.addValue(q, accountId); + db.request(q.toString()); + + var q = new StringBuf(); + q.add("DELETE FROM services WHERE account_id="); + db.addValue(q, accountId); + db.request(q.toString()); + } + @HaxeCBridge.noemit public function storeStreamManagement(accountId:String, sm:BytesData) { // TODO diff --git a/snikket/persistence/browser.js b/snikket/persistence/browser.js index 0e81fd8..e33a03d 100644 --- a/snikket/persistence/browser.js +++ b/snikket/persistence/browser.js @@ -592,6 +592,54 @@ const browser = (dbname, tokenize, stemmer) => { }); }, + removeAccount(account, completely) { + const tx = db.transaction(["keyvaluepairs", "services", "messages", "chats", "reactions"], "readwrite"); + const store = tx.objectStore("keyvaluepairs"); + store.delete("login:clientId:" + account); + store.delete("login:token:" + account); + store.delete("login:fastCount:" + account); + store.delete("fn:" + account); + store.delete("sm:" + account); + + if (!completely) return; + + const servicesStore = tx.objectStore("services"); + const servicesCursor = servicesStore.openCursor(IDBKeyRange.bound([account], [account, []])); + servicesCursor.onsuccess = (event) => { + if (event.target.result) { + event.target.result.delete(); + event.target.result.continue(); + } + }; + + const messagesStore = tx.objectStore("messages"); + const messagesCursor = messagesStore.openCursor(IDBKeyRange.bound([account], [account, []])); + messagesCursor.onsuccess = (event) => { + if (event.target.result) { + event.target.result.delete(); + event.target.result.continue(); + } + }; + + const chatsStore = tx.objectStore("chats"); + const chatsCursor = chatsStore.openCursor(IDBKeyRange.bound([account], [account, []])); + chatsCursor.onsuccess = (event) => { + if (event.target.result) { + event.target.result.delete(); + event.target.result.continue(); + } + }; + + const reactionsStore = tx.objectStore("reactions"); + const reactionsCursor = reactionsStore.openCursor(IDBKeyRange.bound([account], [account, []])); + reactionsCursor.onsuccess = (event) => { + if (event.target.result) { + event.target.result.delete(); + event.target.result.continue(); + } + }; + }, + storeService(account, serviceId, name, node, caps) { this.storeCaps(caps); diff --git a/snikket/streams/XmppJsStream.hx b/snikket/streams/XmppJsStream.hx index 7b3fc21..102ddca 100644 --- a/snikket/streams/XmppJsStream.hx +++ b/snikket/streams/XmppJsStream.hx @@ -21,6 +21,7 @@ extern class XmppJsScramSha1 { extern class XmppJsClient { function new(options:Dynamic); function start():Promise; + function stop():Promise; function on(eventName:String, callback:(Dynamic)->Void):Void; function send(stanza:XmppJsXml):Void; var jid:XmppJsJID; @@ -277,6 +278,11 @@ class XmppJsStream extends GenericStream { resolveConnectionURI(this.jid.domain, this.connectWithURI); } + public function disconnect() { + if (client == null) return; + client.stop(); + } + private function convertFromStanza(el:Stanza):XmppJsXml { var xml = new XmppJsXml(el.name, el.attr); if(el.children.length > 0) { diff --git a/snikket/streams/XmppStropheStream.hx b/snikket/streams/XmppStropheStream.hx index 0c9fe85..9223023 100644 --- a/snikket/streams/XmppStropheStream.hx +++ b/snikket/streams/XmppStropheStream.hx @@ -76,6 +76,9 @@ extern class StropheConn { userdata:RawPointer ):cpp.Int32; + @:native("xmpp_disconnect") + static function disconnect(conn:StropheConn):Void; + @:native("xmpp_handler_add") static function handler_add( conn:StropheConn, @@ -268,6 +271,10 @@ class XmppStropheStream extends GenericStream { poll(); } + public function disconnect() { + StropheConn.disconnect(conn); + } + private function poll() { sys.thread.Thread.current().events.run(() -> { StropheCtx.run_once(ctx, 1);