feat: add support for PUSH-TOKEN
authorKyle Fuller <redacted>
Sun, 5 Apr 2020 13:24:00 +0000 (14:24 +0100)
committerGitHub <redacted>
Sun, 5 Apr 2020 13:24:00 +0000 (14:24 +0100)
palaver.cpp
test/test_palaver.py

index 500ee34bf0dfa2d83d4788a81e9c3bcf812ad847..aee901e27433ad771bab1475a86557e1e508362c 100644 (file)
@@ -36,6 +36,7 @@
 
 const char *kPLVCapability = "palaverapp.com";
 const char *kPLVCommand = "PALAVER";
+const char *kPLVPushTokenKey = "PUSH-TOKEN";
 const char *kPLVPushEndpointKey = "PUSH-ENDPOINT";
 const char *kPLVMentionKeywordKey = "MENTION-KEYWORD";
 const char *kPLVMentionChannelKey = "MENTION-CHANNEL";
@@ -203,20 +204,20 @@ private:
 
 class PLVHTTPNotificationSocket : public PLVHTTPSocket {
 public:
-       PLVHTTPNotificationSocket(CModule *pModule, const CString &sToken, const CString &sMethod, const CString &sURL, MCString &mcsHeaders, const CString &sContent) : PLVHTTPSocket(pModule, sMethod, sURL, mcsHeaders, sContent) {
-               m_sToken = sToken;
+       PLVHTTPNotificationSocket(CModule *pModule, const CString &sIdentifier, const CString &sMethod, const CString &sURL, MCString &mcsHeaders, const CString &sContent) : PLVHTTPSocket(pModule, sMethod, sURL, mcsHeaders, sContent) {
+               m_sIdentifier = sIdentifier;
        }
 
        virtual void HandleStatusCode(unsigned int status);
 
 private:
-       CString m_sToken;
+       CString m_sIdentifier;
 };
 
 class CDevice {
 public:
-       CDevice(const CString &sToken) {
-               m_sToken = sToken;
+       CDevice(const CString &sIdentifier) {
+               m_sIdentifier = sIdentifier;
                m_bInNegotiation = false;
                m_uiBadge = 0;
        }
@@ -237,8 +238,16 @@ public:
                m_sVersion = sVersion;
        }
 
-       CString GetToken() const {
-               return m_sToken;
+       CString GetIdentifier() const {
+               return m_sIdentifier;
+       }
+
+       void SetPushToken(const CString &sToken) {
+               m_sPushToken = sToken;
+       }
+
+       CString GetPushToken() const {
+               return m_sPushToken;
        }
 
        void SetPushEndpoint(const CString &sEndpoint) {
@@ -416,6 +425,7 @@ public:
        void ResetDevice() {
                m_bInNegotiation = false;
                m_sVersion = "";
+               m_sPushToken = "";
                m_sPushEndpoint = "";
                m_bShowMessagePreview = true;
 
@@ -596,6 +606,8 @@ public:
                                SetVersion(sValue);
                        } else if (sKey.Equals(kPLVPushEndpointKey)) {
                                SetPushEndpoint(sValue);
+                       } else if (sKey.Equals(kPLVPushTokenKey)) {
+                               SetPushToken(sValue);
                        } else if (sKey.Equals(kPLVShowMessagePreviewKey)) {
                                SetShowMessagePreview(sValue.Equals("true"));
                        }
@@ -629,7 +641,7 @@ public:
        }
 
        void Write(CFile& File) const {
-               File.Write("BEGIN " + GetToken() + "\n");
+               File.Write("BEGIN " + GetIdentifier() + "\n");
 
                if (GetVersion().empty() == false) {
                        File.Write("SET VERSION " + GetVersion() + "\n");
@@ -645,6 +657,10 @@ public:
                        File.Write("SET " + CString(kPLVPushEndpointKey) + " " + GetPushEndpoint() + "\n");
                }
 
+               if (GetPushToken().empty() == false) {
+                       File.Write("SET " + CString(kPLVPushTokenKey) + " " + GetPushToken() + "\n");
+               }
+
                for (VCString::const_iterator it = m_vMentionKeywords.begin();
                                it != m_vMentionKeywords.end(); ++it) {
                        const CString& sKeyword = *it;
@@ -710,7 +726,12 @@ public:
 
                MCString mcsHeaders;
 
-               mcsHeaders["Authorization"] = CString("Bearer " + GetToken());
+               CString token = GetPushToken();
+               if (token.empty()) {
+                       token = GetIdentifier();
+               }
+
+               mcsHeaders["Authorization"] = CString("Bearer " + token);
                mcsHeaders["Content-Type"] = "application/json";
 
                CString sJSON = "{";
@@ -735,7 +756,7 @@ public:
                }
                sJSON += "}";
 
-               PLVHTTPSocket *pSocket = new PLVHTTPNotificationSocket(&module, GetToken(), "POST", GetPushEndpoint(), mcsHeaders, sJSON);
+               PLVHTTPSocket *pSocket = new PLVHTTPNotificationSocket(&module, token, "POST", GetPushEndpoint(), mcsHeaders, sJSON);
                module.AddSocket(pSocket);
        }
 
@@ -743,12 +764,16 @@ public:
                if (m_uiBadge != 0) {
                        MCString mcsHeaders;
 
-                       mcsHeaders["Authorization"] = CString("Bearer " + GetToken());
+                       CString token = GetPushToken();
+                       if (token.empty()) {
+                               token = GetIdentifier();
+                       }
+                       mcsHeaders["Authorization"] = CString("Bearer " + token);
                        mcsHeaders["Content-Type"] = "application/json";
 
                        CString sJSON = "{\"badge\": 0}";
 
-                       PLVHTTPSocket *pSocket = new PLVHTTPNotificationSocket(&module, GetToken(), "POST", GetPushEndpoint(), mcsHeaders, sJSON);
+                       PLVHTTPSocket *pSocket = new PLVHTTPNotificationSocket(&module, token, "POST", GetPushEndpoint(), mcsHeaders, sJSON);
                        module.AddSocket(pSocket);
 
                        m_uiBadge = 0;
@@ -760,9 +785,10 @@ public:
        }
 
 private:
-       CString m_sToken;
+       CString m_sIdentifier;
        CString m_sVersion;
        CString m_sPushEndpoint;
+       CString m_sPushToken;
 
        std::map<CString, MCString> m_msmsNetworks;
 
@@ -862,11 +888,11 @@ public:
                                        pDevice->RemoveClient(*pClient);
                                }
 
-                               CString sToken = sLine.Token(2);
+                               CString sClientIdentifier = sLine.Token(2);
                                CString sVersion = sLine.Token(3);
                                CString sNetworkID = sLine.Token(4);
 
-                               CDevice& device = DeviceWithToken(sToken);
+                               CDevice& device = DeviceWithIdentifier(sClientIdentifier);
 
                                if (device.InNegotiation() == false && device.GetVersion().Equals(sVersion) == false) {
                                        pClient->PutClient("PALAVER REQ *");
@@ -888,10 +914,10 @@ public:
                                        return HALT;
                                }
 
-                               CString sToken = sLine.Token(2);
+                               CString sClientIdentifier = sLine.Token(2);
                                CString sVersion = sLine.Token(3);
 
-                               if (!pDevice->GetToken().Equals(sToken)) {
+                               if (!pDevice->GetIdentifier().Equals(sClientIdentifier)) {
                                        // Setting was for a different device than the one the user registered with
                                        return HALT;
                                }
@@ -955,21 +981,21 @@ public:
 
 #pragma mark -
 
-       CDevice& DeviceWithToken(const CString& sToken) {
+       CDevice& DeviceWithIdentifier(const CString& sIdentifier) {
                CDevice *pDevice = NULL;
 
                for (std::vector<CDevice*>::const_iterator it = m_vDevices.begin();
                                it != m_vDevices.end(); ++it) {
                        CDevice& device = **it;
 
-                       if (device.GetToken().Equals(sToken)) {
+                       if (device.GetIdentifier().Equals(sIdentifier)) {
                                pDevice = &device;
                                break;
                        }
                }
 
                if (pDevice == NULL) {
-                       pDevice = new CDevice(sToken);
+                       pDevice = new CDevice(sIdentifier);
                        m_vDevices.push_back(pDevice);
                }
 
@@ -992,12 +1018,12 @@ public:
                return pDevice;
        }
 
-       bool RemoveDeviceWithToken(const CString& sToken) {
+       bool RemoveDeviceWithIdentifier(const CString& sIdentifier) {
                for (std::vector<CDevice*>::iterator it = m_vDevices.begin();
                                it != m_vDevices.end(); ++it) {
                        CDevice& device = **it;
 
-                       if (device.GetToken().Equals(sToken)) {
+                       if (device.GetIdentifier().Equals(sIdentifier)) {
                                m_vDevices.erase(it);
                                Save();
                                return true;
@@ -1212,7 +1238,7 @@ public:
                                        const CString sNetwork = it3->first;
 
                                        Table.AddRow();
-                                       Table.SetCell("Device", device.GetToken());
+                                       Table.SetCell("Device", device.GetIdentifier());
                                        Table.SetCell("User", sUsername);
                                        Table.SetCell("Network", sNetwork);
                                        Table.SetCell("Negotiating", CString(device.InNegotiation()));
@@ -1220,7 +1246,7 @@ public:
 
                                if (networks.size() == 0) {
                                        Table.AddRow();
-                                       Table.SetCell("Device", device.GetToken());
+                                       Table.SetCell("Device", device.GetIdentifier());
                                        Table.SetCell("User", sUsername);
                                        Table.SetCell("Network", "");
                                        Table.SetCell("Negotiating", CString(device.InNegotiation()));
@@ -1229,7 +1255,7 @@ public:
 
                        if (msmsNetworks.size() == 0) {
                                Table.AddRow();
-                               Table.SetCell("Device", device.GetToken());
+                               Table.SetCell("Device", device.GetIdentifier());
                                Table.SetCell("User", "");
                                Table.SetCell("Network", "");
                                Table.SetCell("Negotiating", CString(device.InNegotiation()));
@@ -1242,7 +1268,7 @@ public:
 
                CDevice *pDevice = DeviceForClient(*m_pClient);
                if (pDevice) {
-                       PutModule("You are connected from Palaver. (" + pDevice->GetToken() + ")");
+                       PutModule("You are connected from Palaver. (" + pDevice->GetIdentifier() + ")");
                } else {
                        PutModule("You are not connected from a Palaver client.");
                }
@@ -1256,7 +1282,7 @@ public:
                PutModule("Palaver ZNC: " + CString(PALAVER_VERSION) + " -- http://palaverapp.com/");
                CDevice *pDevice = DeviceForClient(*m_pClient);
                if (pDevice) {
-                       PutModule("Current device: (" + pDevice->GetToken() + ")");
+                       PutModule("Current device: (" + pDevice->GetIdentifier() + ")");
                }
                PutModule(CString(m_vDevices.size()) + " registered devices");
 
@@ -1272,7 +1298,7 @@ void PLVHTTPNotificationSocket::HandleStatusCode(unsigned int status) {
        if (status == 401) {
                if (CPalaverMod *pModule = dynamic_cast<CPalaverMod *>(m_pModule)) {
                        DEBUG("palaver: Removing device");
-                       pModule->RemoveDeviceWithToken(m_sToken);
+                       pModule->RemoveDeviceWithIdentifier(m_sIdentifier);
                }
        }
 }
index a2a9d94a958efead6979c4809078ed111f1b1562..8d7738e815a868d8340784b1b2fbc8c4b2ff35ca 100644 (file)
@@ -188,3 +188,69 @@ async def test_receiving_notification(event_loop):
     await tearDown(proc)
 
     assert connected.called
+
+
+async def test_receiving_notification_with_push_token(event_loop):
+    (proc, reader, writer) = await setUp(event_loop)
+
+    async def connected(reader, writer):
+        line = await reader.readline()
+        assert line == b'POST /push HTTP/1.1\r\n'
+
+        line = await reader.readline()
+        assert line == b'Host: 127.0.0.1\r\n'
+
+        line = await reader.readline()
+        assert line == b'Authorization: Bearer abcdefg\r\n'
+
+        line = await reader.readline()
+        assert line == b'Connection: close\r\n'
+
+        line = await reader.readline()
+        assert line == b'Content-Length: 109\r\n'
+
+        line = await reader.readline()
+        assert line == b'Content-Type: application/json\r\n'
+
+        line = await reader.readline()
+        assert line == b'User-Agent: ZNC\r\n'
+
+        line = await reader.readline()
+        assert line == b'\r\n'
+
+        line = await reader.readline()
+        assert line == b'{"badge": 1,"message": "Test notification","sender": "palaver","network": "b758eaab1a4611a310642a6e8419fbff"}'
+
+        writer.write(b'HTTP/1.1 204 No Content\r\n\r\n')
+        await writer.drain()
+        writer.close()
+
+        connected.called = True
+
+    server = await asyncio.start_server(connected, host='127.0.0.1', port=0, loop=event_loop)
+    await asyncio.sleep(0.2)
+    addr = server.sockets[0].getsockname()
+    url = f'Serving on http://{addr[0]}:{addr[1]}/push'
+
+    writer.write(b'PALAVER IDENTIFY 9167e47b01598af7423e2ecd3d0a3ec4 611d3a30a3d666fc491cdea0d2e1dd6e b758eaab1a4611a310642a6e8419fbff\r\n')
+    await writer.drain()
+
+    line = await reader.readline()
+    assert line == b'PALAVER REQ *\r\n'
+
+    writer.write(b'PALAVER BEGIN 9167e47b01598af7423e2ecd3d0a3ec4 611d3a30a3d666fc491cdea0d2e1dd6e\r\n')
+    writer.write(f'PALAVER SET PUSH-ENDPOINT {url}\r\n'.encode('utf-8'))
+    writer.write(f'PALAVER SET PUSH-TOKEN abcdefg\r\n'.encode('utf-8'))
+    writer.write(b'PALAVER END\r\n')
+    await writer.drain()
+
+    writer.write(b'PRIVMSG *palaver :test\r\n')
+    await writer.drain()
+
+    await asyncio.sleep(0.2)
+    server.close()
+    await server.wait_closed()
+
+    await tearDown(proc)
+
+    assert connected.called
git clone https://git.99rst.org/PROJECT