Commit 59c172cb authored by armin's avatar armin
Browse files

APNS via HTTP2 REST API

parent 3df70e5a
.git
.gitmodule
.idea
cmake-build-debug
cmake-build-release
[submodule "models/PusherCpp"]
path = models/PusherCpp
url = https://github.com/joshuakuai/PusherCpp.git
[submodule "libs/cpp-jwt"]
path = libs/cpp-jwt
url = https://github.com/arun11299/cpp-jwt.git
[submodule "libs/cpp-base64"]
path = libs/cpp-base64
url = https://github.com/ReneNyffenegger/cpp-base64.git
......@@ -11,8 +11,12 @@ set(CMAKE_CXX_FLAGS_RELEASE "-O3 -flto")
set(CMAKE_CXX_FLAGS_DEBUG "-g")
include_directories(/usr/local/include/r3/)
include_directories(libs/cpp-jwt/include)
include_directories(libs/cpp-base64)
set(SOURCE_FILES main.cpp HandlerFactory.cpp handlers/NorFoundHandler.cpp utils.cpp utils.h handlers/ApplePushHandler.cpp handlers/ApplePushHandler.h handlers/GooglePushHandler.cpp handlers/GooglePushHandler.h models/GooglePushModel.cpp models/GooglePushModel.h models/ApplePushModel.cpp models/ApplePushModel.h
models/PusherCpp/Pusher.cpp models/PusherCpp/Pusher.h)
add_executable(rocketChatMobilePushGateway ${SOURCE_FILES} utils.cpp utils.h models/GooglePushModel.cpp models/GooglePushModel.h)
libs/cpp-jwt/include/jwt/jwt.hpp libs/cpp-base64/base64.h libs/cpp-base64/base64.cpp)
add_executable(rocketChatMobilePushGateway ${SOURCE_FILES})
target_link_libraries(rocketChatMobilePushGateway -lwangle -lfolly -lz -lssl -lcrypto -levent -lgflags -lglog -L/usr/lib64 -lpthread -pthread -lfolly -lglog -ldouble-conversion -lboost_system -lboost_thread -lproxygenhttpserver -ljsoncpp -lcurl)
......@@ -18,6 +18,8 @@
* *
********************************************************************************************************************/
#define UNUSED(x) (void)x;
#include "HandlerFactory.h"
void HandlerFactory::onServerStart(folly::EventBase* /*evb*/) noexcept{
......@@ -30,6 +32,7 @@ void HandlerFactory::onServerStop() noexcept{
}
RequestHandler* HandlerFactory::onRequest(RequestHandler* requestHandler, HTTPMessage* httpMessage) noexcept{
UNUSED(requestHandler);
if(httpMessage) {
std::vector<std::string> segments = utils::getSegments(httpMessage->getURL());
if(segments.size()>2){
......
......@@ -19,8 +19,21 @@ which then has to be used by the other servers. In that case you should use this
- make
- place the credentials in the servers "credentials" directory:
- FCM -> google/serverKey.txt
- APNS -> apple/cred.pem (see https://github.com/joshuakuai/PusherCpp)
- APNS ->
1. create key (see https://developer.apple.com/documentation/usernotifications/setting_up_a_remote_notification_server/establishing_a_token_based_connection_to_apns)
2. convert the *.p8 to pem
- wget https://github.com/web-token/jwt-app/raw/gh-pages/jose.phar
- chmod +x jose.phar
- ./jose.phar key:convert:pkcs1 $(./jose.phar key:load:key ./AuthKey_*.p8) > key.pem
3. save the pem file apple/key.pem
4. create apple/setting.json file, that looks like:
``{
"teamId":"YOUR_APPLE_DEVELOPER_TEAM_ID",
"key": "THE_KEY_RELATED_TO_THE_p8(also part of the name AuthKey_[KEY].p8)",
"appId": "YOUR_APP_ID"
}``
### Docker
- pass port 11000
- mount your credentials folder into the container with -v /certs:/yourCertsFolder
......
......@@ -18,11 +18,15 @@
* *
********************************************************************************************************************/
#define UNUSED(x) (void)x;
#include "ApplePushHandler.h"
#include "../models/ApplePushModel.h"
void ApplePushHandler::onRequest(std::unique_ptr<HTTPMessage> headers) noexcept {
void ApplePushHandler::onRequest(std::unique_ptr<HTTPMessage> headers) noexcept {
UNUSED(headers);
}
void ApplePushHandler::onBody(std::unique_ptr<folly::IOBuf> body) noexcept {
......@@ -34,7 +38,7 @@ void ApplePushHandler::onBody(std::unique_ptr<folly::IOBuf> body) noexcept {
}
void ApplePushHandler::onUpgrade(proxygen::UpgradeProtocol prot) noexcept {
UNUSED(prot);
}
void ApplePushHandler::onEOM() noexcept {
......@@ -65,5 +69,5 @@ void ApplePushHandler::requestComplete() noexcept {
}
void ApplePushHandler::onError(ProxygenError err) noexcept {
UNUSED(err);
}
......@@ -17,12 +17,12 @@
* along with RocketChatMobilePushGateway. If not, see <http://www.gnu.org/licenses/>. *
* *
********************************************************************************************************************/
#define UNUSED(x) (void)x;
#include "GooglePushHandler.h"
#include "../models/GooglePushModel.h"
void GooglePushHandler::onRequest(std::unique_ptr<HTTPMessage> headers) noexcept {
UNUSED(headers)
}
void GooglePushHandler::onBody(std::unique_ptr<folly::IOBuf> body) noexcept {
......@@ -34,7 +34,7 @@ void GooglePushHandler::onBody(std::unique_ptr<folly::IOBuf> body) noexcept {
}
void GooglePushHandler::onUpgrade(proxygen::UpgradeProtocol prot) noexcept {
UNUSED(prot)
}
void GooglePushHandler::onEOM() noexcept {
......@@ -65,5 +65,5 @@ void GooglePushHandler::requestComplete() noexcept {
}
void GooglePushHandler::onError(ProxygenError err) noexcept {
UNUSED(err)
}
......@@ -17,6 +17,7 @@
* along with RocketChatMobilePushGateway. If not, see <http://www.gnu.org/licenses/>. *
* *
********************************************************************************************************************/
#define UNUSED(x) (void)x;
#include "NorFoundHandler.h"
......@@ -25,7 +26,7 @@ NotFoundHandler::NotFoundHandler() {
}
void NotFoundHandler::onRequest(std::unique_ptr<proxygen::HTTPMessage> headers) noexcept {
UNUSED(headers);
}
void NotFoundHandler::onBody(std::unique_ptr<folly::IOBuf> body) noexcept {
......@@ -42,7 +43,7 @@ void NotFoundHandler::onEOM() noexcept {
}
void NotFoundHandler::onUpgrade(proxygen::UpgradeProtocol proto) noexcept {
UNUSED(proto);
}
void NotFoundHandler::requestComplete() noexcept {
......@@ -50,5 +51,5 @@ void NotFoundHandler::requestComplete() noexcept {
}
void NotFoundHandler::onError(proxygen::ProxygenError err) noexcept {
UNUSED(err);
}
\ No newline at end of file
Subproject commit 6420804f7ba10e8c3049c6e3b59ec88c88d808a6
Subproject commit 87dcef903f48a8c33df0bd9658d775d6d5db1918
......@@ -26,6 +26,7 @@
#include <unistd.h>
#include "HandlerFactory.h"
#include "models/GooglePushModel.h"
#include "models/ApplePushModel.h"
using namespace proxygen;
......@@ -51,6 +52,7 @@ int main(int argc, char* argv[]) {
google::InstallFailureSignalHandler();
GooglePushModel::loadApiKey();
ApplePushModel::loadApiKey();
......
......@@ -18,21 +18,41 @@
* *
********************************************************************************************************************/
#include <exception>
#include <jsoncpp/json/json.h>
#include <iostream>
#include <fstream>
#include <algorithm>
#include <cassert>
#include <chrono>
#include <boost/uuid/uuid.hpp>
#include <boost/uuid/uuid_generators.hpp>
#include <boost/uuid/string_generator.hpp>
#include <boost/lexical_cast.hpp>
#include <boost/uuid/uuid_io.hpp>
#include <proxygen/lib/utils/CryptUtil.h>
#include "ApplePushModel.h"
#include "../libs/cpp-jwt/include/jwt/jwt.hpp"
#include "../libs/cpp-jwt/include/jwt/algorithm.hpp"
#include "../libs/cpp-jwt/include/jwt/parameters.hpp"
#include "../libs/cpp-base64/base64.h"
ApplePushModel::ApplePushModel(const std::string &pJson):mPusher("/certs/apple/cred.pem") {
std::string ApplePushModel::mPem;
std::string ApplePushModel::mTeamId;
std::string ApplePushModel::mAppId;
std::string ApplePushModel::mKey;
ApplePushModel::ApplePushModel(const std::string &pJson) {
Json::Reader reader;
Json::Value obj;
Json::FastWriter fast;
if(pJson.length()) {
if (pJson.length()) {
reader.parse(pJson, obj);
if (obj.isMember("token") && obj.isMember("options")) {
......@@ -42,11 +62,11 @@ ApplePushModel::ApplePushModel(const std::string &pJson):mPusher("/certs/apple/c
Json::Value apn = options["apn"];
std::string test(fast.write(options));
if (options.isMember("text")) {
std::string temp = std::move(options["text"].asString());
std::string temp = std::move(options["text"].asString());
unsigned long index = 0;
while(true){
index = temp.find('\n',index);
if(index == std::string::npos){
while (true) {
index = temp.find('\n', index);
if (index == std::string::npos) {
break;
}
temp.replace(index, 1, "\\r\\n");
......@@ -69,6 +89,10 @@ ApplePushModel::ApplePushModel(const std::string &pJson):mPusher("/certs/apple/c
mBadge = options["badge"].asInt();
}
if (options.isMember("payload")) {
//std::string test = fast.write(options["payload"]);
// std::string test4 = Json::valueToQuotedString(test.c_str());
mPayload = std::move(fast.write(options["payload"]));
}
if (options.isMember("topic")) {
......@@ -90,25 +114,108 @@ bool ApplePushModel::sendMessage() {
Json::FastWriter fast;
tokens.push_back(mDeviceToken);
//tokens.push_back(mDeviceToken);
PusherContent content;
if(mPayload.length()){
content.userData = "\"ejson\":"+mPayload+"";
}else{
content.userData = "";
}
CURL *curl;
CURLcode res;
curl = curl_easy_init();
if (curl) {
struct curl_slist *chunk = nullptr;
try {
jwt::jwt_object obj{jwt::params::algorithm("ES256"),
jwt::params::headers({
{"alg", "ES256"},
{"kid", "3VYNV8J29D"}
}),
jwt::params::secret(mPem)
};
auto n = std::chrono::system_clock::now();
auto in_time_t = std::chrono::system_clock::to_time_t(n);
obj.add_claim("iss", mTeamId).add_claim("iat", in_time_t);
std::string encoded_jwt = obj.signature();
boost::uuids::uuid uuidObj = boost::uuids::random_generator()();
std::string uuidString = boost::lexical_cast<std::string>(uuidObj);
content.badge = mBadge;
content.content = mText;
content.sound = mSound;
chunk = curl_slist_append(chunk, std::string("Authorization: Bearer "+obj.signature()).c_str());
chunk = curl_slist_append(chunk, std::string("apns-id: "+uuidString).c_str());
chunk = curl_slist_append(chunk, std::string("apns-expiration: 0").c_str());
chunk = curl_slist_append(chunk, std::string("apns-priority: 10").c_str());
chunk = curl_slist_append(chunk, std::string("apns-topic: "+mAppId).c_str());
mPusher.isSandBox = true;
mPusher.pushNotification(content, tokens);
std::string url = mApiUrl+mDeviceToken;
// curl_easy_setopt(curl, CURLOPT_DEBUGFUNCTION, trace );
curl_easy_setopt(curl, CURLOPT_VERBOSE, false);
curl_easy_setopt(curl, CURLOPT_URL,url.c_str());
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, mPayload.c_str());
curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE,mPayload.size());
curl_easy_setopt(curl, CURLOPT_POST, true);
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, chunk);
curl_easy_setopt(curl, CURLOPT_USE_SSL, true);
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, false);
curl_easy_setopt(curl, CURLOPT_TCP_KEEPALIVE, true);
curl_easy_setopt(curl, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_2_0);
//TODO: shoud be fixed, pusherCPP does not give a return value
return true;
res = curl_easy_perform(curl);
if(res != CURLE_OK){
std::cerr<<"curl error: "<<curl_easy_strerror(res)<<std::endl;
}else{
std::cout<<"result: "<<res<<std::endl;
curl_easy_cleanup(curl);
curl_slist_free_all(chunk);
return true;
}
curl_easy_cleanup(curl);
curl_slist_free_all(chunk);
} catch (std::exception &e) {
std::cout<<e.what()<<std::endl;
curl_easy_cleanup(curl);
curl_slist_free_all(chunk);
return false;
}
}
return false;
}
void ApplePushModel::loadApiKey() {
std::ifstream ifsPem("/certs/apple/key.pem");
std::ifstream ifsSettings("/certs/apple/settings.json");
std::string pemContent((std::istreambuf_iterator<char>(ifsPem)),(std::istreambuf_iterator<char>()));
std::string settingsContent((std::istreambuf_iterator<char>(ifsSettings)),(std::istreambuf_iterator<char>()));
if(pemContent.length()&&settingsContent.length()){
mPem = pemContent;
Json::Reader reader;
Json::Value obj;
reader.parse(settingsContent,obj);
if(obj.isMember("appId")&&obj.isMember("teamId")&&obj.isMember("key")){
std::string appId = std::move(obj["appId"].asString());
std::string teamId = std::move(obj["teamId"].asString());
std::string key = std::move(obj["key"].asString());
if(appId.length()&&teamId.length()&&key.length()){
mAppId = std::move(appId);
mTeamId = std::move(teamId);
mKey = std::move(key);
}else{
std::cout<<"Error JSON data invalid"<<std::endl;
exit(EXIT_FAILURE);
}
}
}else{
std::cout<<"Error loading APNS credentials, check if the settings.json, and key.pem exists"<<std::endl;
exit(EXIT_FAILURE);
}
}
......@@ -22,7 +22,6 @@
#define ROCKETCHATMOBILEPUSHGATEWAY_APPLEPUSHMODEL_H
#include <curl/curl.h>
#include "PusherCpp/Pusher.h"
class ApplePushModel {
......@@ -32,9 +31,17 @@ public:
bool sendMessage();
static void loadApiKey();
private:
Pusher mPusher;
// Pusher mPusher;
const std::string mApiUrl{"https://api.push.apple.com/3/device/"};
static std::string mPem;
static std::string mTeamId;
static std::string mAppId;
static std::string mKey;
std::string mTitle;
std::string mText;
std::string mDeviceToken;
......
......@@ -18,6 +18,8 @@
* *
********************************************************************************************************************/
#define UNUSED(x) (void)x;
#include <fstream>
#include <iostream>
#include <fstream>
......@@ -109,6 +111,8 @@ int GooglePushModel::trace(CURL *handle, curl_infotype type,
char *data, size_t size,
void *userp){
UNUSED(size);
const char *text;
(void)handle; /* prevent compiler warning */
(void)userp;
......
Subproject commit 5072100114573dd2d01d8ac7a95ef5121c1c95bb
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment