diff --git a/CMakeLists.txt b/CMakeLists.txt index 1aa5c52816832c998b63a046ebaae7042f740be8..b9dbf8f82c295f358f31b484eb396fe029d7f7f4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -223,6 +223,13 @@ list (APPEND opendht_SOURCES src/thread_pool.cpp ) +if (APPLE) + list (APPEND opendht_SOURCES + src/compat/apple/apple_utils.h + src/compat/apple/apple_utils.mm + ) +endif() + list (APPEND opendht_HEADERS include/opendht/def.h include/opendht/utils.h diff --git a/c/opendht.cpp b/c/opendht.cpp index a6de6b9769a63bcb47b54c969bc6a6d0e479a856..dd61b3cf45a702e916b385081923a250af9974a7 100644 --- a/c/opendht.cpp +++ b/c/opendht.cpp @@ -342,6 +342,7 @@ int dht_runner_run_config(dht_runner* r, in_port_t port, const dht_runner_config config.proxy_server = conf->proxy_server ? std::string(conf->proxy_server) : std::string{}; config.push_node_id = conf->push_node_id ? std::string(conf->push_node_id) : std::string{}; config.push_token = conf->push_token ? std::string(conf->push_token) : std::string{}; + config.push_topic = conf->push_topic ? std::string(conf->push_topic) : std::string{}; config.peer_discovery = conf->peer_discovery; config.peer_publish = conf->peer_publish; diff --git a/c/opendht_c.h b/c/opendht_c.h index cf67a0f896520f52e844d2c14aa6719e142720dd..5cdad798ead947551af3112133794c6a0a06843e 100644 --- a/c/opendht_c.h +++ b/c/opendht_c.h @@ -146,6 +146,7 @@ struct OPENDHT_PUBLIC dht_runner_config { const char* proxy_server; const char* push_node_id; const char* push_token; + const char* push_topic; bool peer_discovery; bool peer_publish; dht_certificate* server_ca; diff --git a/configure.ac b/configure.ac index ee9f1805206b0a188db472d97f116e0739488609..48f4aa528beabedf49cb9e06fd965d5b70ed6582 100644 --- a/configure.ac +++ b/configure.ac @@ -32,6 +32,7 @@ LT_INIT() LT_LANG(C++) AC_LANG(C++) AC_PROG_CXX +AC_PROG_OBJCXX AX_CXX_COMPILE_STDCXX(17,[noext],[mandatory]) dnl Check for logs @@ -91,6 +92,7 @@ case "${host_os}" in SYS=linux ;; darwin*) + APPLE=1 SYS=darwin ;; mingw32*) @@ -110,6 +112,7 @@ case "${host_os}" in ;; esac +AM_CONDITIONAL(APPLE, [test "x$SYS" = "xdarwin"]) AM_CONDITIONAL(WIN32, [test "x$SYS" = "xmingw32"]) AS_IF([test "x$SYS" = "xandroid"], [], [LDFLAGS="${LDFLAGS} -lpthread"]) @@ -188,6 +191,7 @@ AM_COND_IF([PROXY_CLIENT_OR_SERVER], [ ]) CXXFLAGS="${CXXFLAGS} -DMSGPACK_NO_BOOST -DMSGPACK_DISABLE_LEGACY_NIL -DMSGPACK_DISABLE_LEGACY_CONVERT" +OBJCXX="$CXX $CXXFLAGS" AC_ARG_ENABLE([tools], AS_HELP_STRING([--disable-tools],[Disable tools (CLI DHT node)]),,build_tools=yes) AM_CONDITIONAL(ENABLE_TOOLS, test x$build_tools == xyes) diff --git a/include/opendht/dht_interface.h b/include/opendht/dht_interface.h index bb0362f071859d79113c9e45a1caeec1b7230c7b..41ab734d7c618fe924d97a25b36f2fca8372705b 100644 --- a/include/opendht/dht_interface.h +++ b/include/opendht/dht_interface.h @@ -265,6 +265,8 @@ public: virtual void setPushNotificationToken(const std::string&) {}; + virtual void setPushNotificationTopic(const std::string&) {}; + /** * Call linked callback with a push notification * @param notification to process diff --git a/include/opendht/dht_proxy_client.h b/include/opendht/dht_proxy_client.h index 7be198f9c4708de35171852307f9c1e3c4edcf18..ce70a48d093bdacf473e34f050f1931d5df0ddc0 100644 --- a/include/opendht/dht_proxy_client.h +++ b/include/opendht/dht_proxy_client.h @@ -67,6 +67,14 @@ public: #endif } + virtual void setPushNotificationTopic(const std::string& topic) override { +#ifdef OPENDHT_PUSH_NOTIFICATIONS + notificationTopic_ = topic; +#else + (void) topic; +#endif + } + virtual ~DhtProxyClient(); /** @@ -400,6 +408,11 @@ private: */ std::string deviceKey_ {}; + /** + * Notification topic for ios notifications. + */ + std::string notificationTopic_ {}; + const std::function<void()> loopSignal_; #ifdef OPENDHT_PUSH_NOTIFICATIONS diff --git a/include/opendht/dht_proxy_server.h b/include/opendht/dht_proxy_server.h index e1facd70aee390834e3aa7096f5e92aef09be721..ac4cb248535f09e865abcdf778ddd28b55f9582f 100644 --- a/include/opendht/dht_proxy_server.h +++ b/include/opendht/dht_proxy_server.h @@ -40,7 +40,8 @@ namespace dht { enum class PushType { None = 0, Android, - iOS + iOS, + iOSLegacy }; } MSGPACK_ADD_ENUM(dht::PushType) @@ -273,6 +274,7 @@ private: restinio::router::route_params_t params); #ifdef OPENDHT_PUSH_NOTIFICATIONS + PushType getTypeFromString(const std::string& type); /** * Subscribe to push notifications for an iOS or Android device. * Method: SUBSCRIBE "/{InfoHash: .*}" @@ -300,7 +302,7 @@ private: * @param key of the device * @param json, the content to send */ - void sendPushNotification(const std::string& key, Json::Value&& json, PushType type, bool highPriority); + void sendPushNotification(const std::string& key, Json::Value&& json, PushType type, bool highPriority, const std::string& topic); /** * Send push notification with an expire timeout. @@ -308,9 +310,10 @@ private: * @param pushToken * @param json * @param type + * @param topic */ void handleNotifyPushListenExpire(const asio::error_code &ec, const std::string pushToken, - std::function<Json::Value()> json, PushType type); + std::function<Json::Value()> json, PushType type, const std::string& topic); /** * Remove a push listener between a client and a hash @@ -382,6 +385,7 @@ private: std::unique_ptr<asio::steady_timer> expireNotifyTimer; Sp<Value> value; PushType type; + std::string topic; template <typename Packer> void msgpack_pack(Packer& p) const @@ -425,6 +429,7 @@ private: std::unique_ptr<asio::steady_timer> expireTimer; std::unique_ptr<asio::steady_timer> expireNotifyTimer; PushType type; + std::string topic; template <typename Packer> void msgpack_pack(Packer& p) const diff --git a/include/opendht/dhtrunner.h b/include/opendht/dhtrunner.h index 0fdcf8d005106376f9a8a4fac1d5e856bbb5c91a..2ea3c4f83e35422d41b653def84852dbbbc8e422 100644 --- a/include/opendht/dhtrunner.h +++ b/include/opendht/dhtrunner.h @@ -61,6 +61,7 @@ public: std::string proxy_server {}; std::string push_node_id {}; std::string push_token {}; + std::string push_topic {}; bool peer_discovery {false}; bool peer_publish {false}; std::shared_ptr<dht::crypto::Certificate> server_ca; @@ -447,6 +448,11 @@ public: */ void setPushNotificationToken(const std::string& token); + /** + * Sets the push notification topic + */ + void setPushNotificationTopic(const std::string& topic); + /** * Insert a push notification to process for OpenDHT */ diff --git a/rust/src/dhtrunner.rs b/rust/src/dhtrunner.rs index 114614d1ec31426ccfeffa12e5b1fc7c4bffdcf4..3470e3db4f553deb625b4d63fd3b42592bc5fbbb 100644 --- a/rust/src/dhtrunner.rs +++ b/rust/src/dhtrunner.rs @@ -47,6 +47,7 @@ impl DhtRunnerConfig { proxy_server: ptr::null(), push_node_id: ptr::null(), push_token: ptr::null(), + push_topic: ptr::null(), peer_discovery: false, peer_publish: false, server_ca: ptr::null_mut(), @@ -73,6 +74,10 @@ impl DhtRunnerConfig { self.push_token = CString::new(push_token).unwrap().as_ptr(); } + pub fn set_push_topic(&mut self, push_topic: &str) { + self.push_topic = CString::new(push_topic).unwrap().as_ptr(); + } + pub fn set_identity(&mut self, certificate: Box<DhtCertificate>, privatekey: Box<PrivateKey>) { self.dht_config.id.privatekey = Box::into_raw(privatekey); self.dht_config.id.certificate = Box::into_raw(certificate); diff --git a/rust/src/ffi.rs b/rust/src/ffi.rs index 7470cc26b89632b91f27a17b72d90d4eb5a0fb0f..97cac9acfa587ada4c9d6b15e384c9423a336b6b 100644 --- a/rust/src/ffi.rs +++ b/rust/src/ffi.rs @@ -118,6 +118,7 @@ pub struct DhtRunnerConfig pub proxy_server: *const c_char, pub push_node_id: *const c_char, pub push_token: *const c_char, + pub push_topic: *const c_char, pub peer_discovery: bool, pub peer_publish: bool, pub server_ca: *mut DhtCertificate, diff --git a/src/Makefile.am b/src/Makefile.am index acaccf79620963c78278408aeca154b1c830cf2b..e7a23ad7b9edf128d2a9fb6691599248a85acab5 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -30,6 +30,12 @@ libopendht_la_SOURCES = \ network_utils.cpp \ thread_pool.cpp +if APPLE +libopendht_la_SOURCES += \ + compat/apple/apple_utils.h \ + compat/apple/apple_utils.mm +endif + if WIN32 libopendht_la_SOURCES += rng.cpp endif diff --git a/src/compat/apple/apple_utils.h b/src/compat/apple/apple_utils.h new file mode 100644 index 0000000000000000000000000000000000000000..e308398a30146f4b10827abad30bc5f4c239d0de --- /dev/null +++ b/src/compat/apple/apple_utils.h @@ -0,0 +1,26 @@ +/* + * Copyright (C) 2014-2022 Savoir-faire Linux Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +#include <string> + +namespace dht { +namespace apple_utils { + +std::string getPlatformVersion(); + +} +} diff --git a/src/compat/apple/apple_utils.mm b/src/compat/apple/apple_utils.mm new file mode 100644 index 0000000000000000000000000000000000000000..427f4f1ca84b517ca486a2ae99f6ab2c22f3fb68 --- /dev/null +++ b/src/compat/apple/apple_utils.mm @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2014-2022 Savoir-faire Linux Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +#include "apple_utils.h" + +#ifdef __APPLE__ +#include <Foundation/Foundation.h> +#include "TargetConditionals.h" +#endif + +namespace dht { +namespace apple_utils { + +std::string +getPlatformVersion() { +#ifdef __APPLE__ + #if TARGET_OS_IPHONE + if (@available(iOS 14.5, *)) { + return "ios"; + } + return "apple"; + #elif TARGET_OS_MAC + return "macos"; + #endif +#endif + return ""; +} + +} +} /* dht */ diff --git a/src/dht_proxy_client.cpp b/src/dht_proxy_client.cpp index 71d01aee30e7f6f25d9ca17ff6f5039eaa3c792a..1385076ab5724716e3066789678d47202bc04f86 100644 --- a/src/dht_proxy_client.cpp +++ b/src/dht_proxy_client.cpp @@ -22,10 +22,14 @@ #include "dhtrunner.h" #include "op_cache.h" #include "utils.h" +#ifdef __APPLE__ +#include "compat/apple/apple_utils.h" +#endif #include <http_parser.h> #include <deque> + namespace dht { struct DhtProxyClient::InfoState { @@ -1303,7 +1307,8 @@ DhtProxyClient::getPushRequest(Json::Value& body) const body["platform"] = "android"; #endif #ifdef __APPLE__ - body["platform"] = "apple"; + body["topic"] = notificationTopic_; + body["platform"] = apple_utils::getPlatformVersion(); #endif } diff --git a/src/dht_proxy_server.cpp b/src/dht_proxy_server.cpp index 92c5148566b37e5c5c26b125b1396f628c70d55a..56d3ec65946b40105594bf817956f9dbca98ca7e 100644 --- a/src/dht_proxy_server.cpp +++ b/src/dht_proxy_server.cpp @@ -372,7 +372,7 @@ DhtProxyServer::loadState(Is& is, size_t size) { pput.second.expireNotifyTimer = std::make_unique<asio::steady_timer>(io_context(), pput.second.expiration - proxy::OP_MARGIN); pput.second.expireNotifyTimer->async_wait(std::bind( &DhtProxyServer::handleNotifyPushListenExpire, this, - std::placeholders::_1, pput.second.pushToken, std::move(jsonProvider), pput.second.type)); + std::placeholders::_1, pput.second.pushToken, std::move(jsonProvider), pput.second.type, pput.second.topic)); } #endif dht_->put(put.first, pput.second.value, DoneCallbackSimple{}, time_point::max(), true); @@ -392,7 +392,7 @@ DhtProxyServer::loadState(Is& is, size_t size) { for (auto& listeners : pushListener.second.listeners) { for (auto& listener : listeners.second) { listener.internalToken = dht_->listen(listeners.first, - [this, infoHash=listeners.first, pushToken=pushListener.first, type=listener.type, clientId=listener.clientId, sessionCtx = listener.sessionCtx] + [this, infoHash=listeners.first, pushToken=pushListener.first, type=listener.type, clientId=listener.clientId, sessionCtx = listener.sessionCtx, topic=listener.topic] (const std::vector<std::shared_ptr<Value>>& values, bool expired) { // Build message content Json::Value json; @@ -414,7 +414,7 @@ DhtProxyServer::loadState(Is& is, size_t size) { auto maxPrio = 1000u; for (const auto& v : values) maxPrio = std::min(maxPrio, v->priority); - sendPushNotification(pushToken, std::move(json), type, !expired and maxPrio == 0); + sendPushNotification(pushToken, std::move(json), type, !expired and maxPrio == 0, topic); return true; } ); @@ -429,7 +429,7 @@ DhtProxyServer::loadState(Is& is, size_t size) { return json; }; listener.expireNotifyTimer->async_wait(std::bind(&DhtProxyServer::handleNotifyPushListenExpire, this, - std::placeholders::_1, pushListener.first, std::move(jsonProvider), listener.type)); + std::placeholders::_1, pushListener.first, std::move(jsonProvider), listener.type, listener.topic)); // cancel push listen listener.expireTimer = std::make_unique<asio::steady_timer>(io_context(), listener.expiration); listener.expireTimer->async_wait(std::bind(&DhtProxyServer::handleCancelPushListen, this, @@ -745,6 +745,21 @@ DhtProxyServer::listen(restinio::request_handle_t request, #ifdef OPENDHT_PUSH_NOTIFICATIONS +PushType +DhtProxyServer::getTypeFromString(const std::string& type) { + if (type == "android") { + return PushType::Android; + } + if (type == "apple") { + // proxy_client is not updated or using ios < 14.5 + return PushType::iOSLegacy; + } + if (type == "ios") { + return PushType::iOS; + } + return PushType::None; +} + RequestStatus DhtProxyServer::subscribe(restinio::request_handle_t request, restinio::router::route_params_t params) @@ -771,7 +786,8 @@ DhtProxyServer::subscribe(restinio::request_handle_t request, response.set_body(RESP_MSG_NO_TOKEN); return response.done(); } - auto type = root["platform"].asString() == "android" ? PushType::Android : PushType::iOS; + auto type = getTypeFromString(root["platform"].asString()); + auto topic = root["topic"].asString(); auto clientId = root["client_id"].asString(); auto sessionId = root["session_id"].asString(); @@ -802,6 +818,7 @@ DhtProxyServer::subscribe(restinio::request_handle_t request, auto timeout = std::chrono::steady_clock::now() + proxy::OP_TIMEOUT; listener.expiration = timeout; listener.type = type; + listener.topic = topic; if (listener.expireNotifyTimer) listener.expireNotifyTimer->expires_at(timeout - proxy::OP_MARGIN); else @@ -815,7 +832,7 @@ DhtProxyServer::subscribe(restinio::request_handle_t request, return json; }; listener.expireNotifyTimer->async_wait(std::bind(&DhtProxyServer::handleNotifyPushListenExpire, this, - std::placeholders::_1, pushToken, std::move(jsonProvider), listener.type)); + std::placeholders::_1, pushToken, std::move(jsonProvider), listener.type, listener.topic)); if (!listener.expireTimer) listener.expireTimer = std::make_unique<asio::steady_timer>(io_context(), timeout); else @@ -850,7 +867,7 @@ DhtProxyServer::subscribe(restinio::request_handle_t request, // =========== No existing listener for an infoHash ============ // Add listen on dht listener.internalToken = dht_->listen(infoHash, - [this, infoHash, pushToken, type, clientId, sessionCtx = listener.sessionCtx] + [this, infoHash, pushToken, type, clientId, sessionCtx = listener.sessionCtx, topic] (const std::vector<std::shared_ptr<Value>>& values, bool expired){ // Build message content Json::Value json; @@ -872,7 +889,7 @@ DhtProxyServer::subscribe(restinio::request_handle_t request, auto maxPrio = 1000u; for (const auto& v : values) maxPrio = std::min(maxPrio, v->priority); - sendPushNotification(pushToken, std::move(json), type, !expired and maxPrio == 0); + sendPushNotification(pushToken, std::move(json), type, !expired and maxPrio == 0, topic); return true; } ); @@ -928,7 +945,7 @@ DhtProxyServer::unsubscribe(restinio::request_handle_t request, void DhtProxyServer::handleNotifyPushListenExpire(const asio::error_code &ec, const std::string pushToken, - std::function<Json::Value()> jsonProvider, PushType type) + std::function<Json::Value()> jsonProvider, PushType type, const std::string& topic) { if (ec == asio::error::operation_aborted) return; @@ -938,7 +955,7 @@ DhtProxyServer::handleNotifyPushListenExpire(const asio::error_code &ec, const s } if (logger_) logger_->d("[proxy:server] [subscribe] sending put refresh to %s token", pushToken.c_str()); - sendPushNotification(pushToken, jsonProvider(), type, false); + sendPushNotification(pushToken, jsonProvider(), type, false, topic); } void @@ -980,7 +997,7 @@ DhtProxyServer::handleCancelPushListen(const asio::error_code &ec, const std::st } void -DhtProxyServer::sendPushNotification(const std::string& token, Json::Value&& json, PushType type, bool highPriority) +DhtProxyServer::sendPushNotification(const std::string& token, Json::Value&& json, PushType type, bool highPriority, const std::string& topic) { if (pushServer_.empty()) return; @@ -1011,6 +1028,22 @@ DhtProxyServer::sendPushNotification(const std::string& token, Json::Value&& jso const auto expiration = std::chrono::system_clock::now() + std::chrono::hours(24); uint32_t exp = std::chrono::duration_cast<std::chrono::seconds>(expiration.time_since_epoch()).count(); notification["expiration"] = exp; + if (!topic.empty()) + notification["topic"] = topic; + if (type == PushType::iOS) { + if (highPriority) { + Json::Value alert(Json::objectValue); + alert["title"]="hello"; + notification["push_type"] = "alert"; + notification["alert"] = alert; + notification["mutable_content"] = true; + } else { + notification["push_type"] = "background"; + notification["content_available"] = true; + } + } else if (type == PushType::iOSLegacy) { + notification["push_type"] = "voip"; + } } Json::Value notifications(Json::arrayValue); @@ -1104,13 +1137,14 @@ DhtProxyServer::put(restinio::request_handle_t request, logger_->d("[proxy:server] [put %s] %s %s", infoHash.toString().c_str(), value->toString().c_str(), (permanent ? "permanent" : "")); if (permanent) { - std::string pushToken, clientId, sessionId, platform; + std::string pushToken, clientId, sessionId, platform, topic; auto& pVal = root["permanent"]; if (pVal.isObject()){ pushToken = pVal["key"].asString(); clientId = pVal["client_id"].asString(); platform = pVal["platform"].asString(); sessionId = pVal["session_id"].asString(); + topic = pVal["topic"].asString(); } std::lock_guard<std::mutex> lock(lockSearchPuts_); auto timeout = std::chrono::steady_clock::now() + proxy::OP_TIMEOUT; @@ -1153,7 +1187,8 @@ DhtProxyServer::put(restinio::request_handle_t request, bool isAndroid = platform == "android"; pput.pushToken = pushToken; pput.clientId = clientId; - pput.type = isAndroid ? PushType::Android : PushType::iOS; + pput.type = getTypeFromString(platform); + pput.topic = topic; pput.sessionCtx = std::make_shared<PushSessionContext>(sessionId); // notify push listen expire auto jsonProvider = [infoHash, clientId, vid, sessionCtx = pput.sessionCtx](){ @@ -1172,7 +1207,7 @@ DhtProxyServer::put(restinio::request_handle_t request, pput.expireNotifyTimer->expires_at(timeout - proxy::OP_MARGIN); pput.expireNotifyTimer->async_wait(std::bind( &DhtProxyServer::handleNotifyPushListenExpire, this, - std::placeholders::_1, pushToken, std::move(jsonProvider), pput.type)); + std::placeholders::_1, pushToken, std::move(jsonProvider), pput.type, pput.topic)); } #endif } else { diff --git a/src/dhtrunner.cpp b/src/dhtrunner.cpp index d82532d07ed0b3e2a168e84aff11ec28076b216d..210a5499b3e459238466090f91a04e748d4a6c6c 100644 --- a/src/dhtrunner.cpp +++ b/src/dhtrunner.cpp @@ -1109,6 +1109,8 @@ DhtRunner::enableProxy(bool proxify) config_.proxy_server, config_.push_node_id, logger_); if (not config_.push_token.empty()) dht_via_proxy->setPushNotificationToken(config_.push_token); + if (not config_.push_topic.empty()) + dht_via_proxy->setPushNotificationTopic(config_.push_topic); dht_ = std::make_unique<SecureDht>(std::move(dht_via_proxy), config_.dht_config, identityAnnouncedCb_, logger_); // and use it use_proxy = proxify; @@ -1144,6 +1146,18 @@ DhtRunner::setPushNotificationToken(const std::string& token) { #endif } +void +DhtRunner::setPushNotificationTopic(const std::string& topic) { + std::lock_guard<std::mutex> lck(dht_mtx); +#if defined(OPENDHT_PROXY_CLIENT) && defined(OPENDHT_PUSH_NOTIFICATIONS) + config_.push_topic = topic; + if (dht_) + dht_->setPushNotificationTopic(topic); +#else + (void) topic; +#endif +} + void DhtRunner::pushNotificationReceived(const std::map<std::string, std::string>& data) {