From b4038017503574ad310ffde79e75ec601c254407 Mon Sep 17 00:00:00 2001 From: Daniel Stein Date: Thu, 11 Sep 2014 14:16:50 +0200 Subject: [PATCH] First commit --- .gitignore | 5 + LICENSE | 13 +++ README.md | 20 ++++ src/main.cpp | 242 ++++++++++++++++++++++++++++++++++++++++++++ src/pushhandler.cpp | 232 ++++++++++++++++++++++++++++++++++++++++++ src/pushhandler.h | 59 +++++++++++ 6 files changed, 571 insertions(+) create mode 100644 .gitignore create mode 100644 LICENSE create mode 100644 README.md create mode 100644 src/main.cpp create mode 100644 src/pushhandler.cpp create mode 100644 src/pushhandler.h diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..fbbcfe7 --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +# executable +pusher + +# makefile +makefile diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..985eba7 --- /dev/null +++ b/LICENSE @@ -0,0 +1,13 @@ +Copyright (c) 2014 HackHerz + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + + 1) Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + + 2) Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + + 3) If you modify the source in any way, you have to remove the authors PushNotifier API-token and replace it with your own. It is the same for the package-name. + + 4) You have to make sure that the modified program doesn't break the PushNotifer API rules. + +THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS “AS IS” AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..82315af --- /dev/null +++ b/README.md @@ -0,0 +1,20 @@ +# pusher +Pusher is a program to notify you. + + +# Building +## Requirements + + * boost + * curl + +## Compile + + + + +# Usage + + + +# Documentation diff --git a/src/main.cpp b/src/main.cpp new file mode 100644 index 0000000..cdf454f --- /dev/null +++ b/src/main.cpp @@ -0,0 +1,242 @@ +/* + * pusher + * (c) 2014 Daniel Stein + * github link.. + * + * TODO + * syslog + * content type detection + * gitignore of lines + * secret input of password + */ + +#include +#include +#include +#include + +// Boost +#include +#include +#include +#include + +// own header files +#include "pushhandler.h" + + +#define CONFIG_FILE "/etc/pusher.conf" + +// namespaces +using namespace std; +namespace po = boost::program_options; + + +// number of digit +int numDigits(int number) +{ + int digits = 0; + while(number != 0) + { + number /= 10; + digits++; + } + return digits; +} + + +// main +int main(int argc, char **argv) +{ + + po::options_description desc("Allowed options"); + + desc.add_options() + ("help,h", "Print help message") + ("token,t", "Request your token") // TODO + ("list,l", "List of all your devices") // TODO + ("pipe,p", "Input via pipe") // TODO + ("quiet,q", "Outputs are redirected to syslog") // TODO + ("verify,v", "Checks if token is still valid") // TODO + ("id,i", po::value(), "Specify device ID"); // TODO + + po::variables_map vm; + po::store(po::parse_command_line(argc, argv, desc), vm); + + try + { + string message; + int id; + + + // help + if(vm.count("help")) + { + cout << desc << endl; + return 0; + } + + + // request token + if(vm.count("token")) + { + //request token + string username, password, token; + + cout << "Username: "; + cin >> username; + + cout << "Password: "; + cin >> password; + + PushHandler buf(username); + token = buf.login(password); + + // generate config file + stringstream conffile; + conffile << boost::format("[pusher]\nusername=%1%\nappToken=%2%") + % username + % token; + + // attempt to save token + ofstream dat_aus; + dat_aus.open(CONFIG_FILE, ios_base::out); + + if(!dat_aus.is_open()) + { + cout << "Try running pusher as root or save the following in " << CONFIG_FILE << "\n" << endl; + cout << conffile.str() << endl; + return 1; + } + + // save + dat_aus << conffile.str(); + dat_aus.close(); + + return 0; + } + + + // load conf and check if token is specified + boost::property_tree::ptree pt; + boost::property_tree::ini_parser::read_ini(CONFIG_FILE, pt); + string username = pt.get("pusher.username"); + string appToken = pt.get("pusher.appToken"); + + + // loading values + PushHandler pusherInstance(username, appToken); + + + // verify token + if(vm.count("verify")) + { + if(pusherInstance.verifyToken()) + { + cout << "appToken is valid" << endl; + return 0; + } + else + { + cout << "appToken is invalid" << endl; + return 1; + } + } + + + // list devices + if(vm.count("list")) + { + vector devices; + devices = pusherInstance.getDevices(); + + int titleLength = 5; + int modelLength = 5; + int idLength = 2; + + for(int i = 0; i < devices.size(); i++) + { + if(devices[i].title.length() > titleLength) { titleLength = devices[i].title.length(); } + if(devices[i].model.length() > modelLength) { modelLength = devices[i].model.length(); } + if(numDigits(devices[i].id) > idLength) { idLength = numDigits(devices[i].id); } + } + + cout + << "ID\033[" << (idLength - 2 + 2) << "C" + << "Title\033[" << (titleLength - 5 + 2) << "C" + << "Model" << endl; + + for(int x = 0; x < (titleLength + modelLength + idLength + 4); x++) { cout << "-"; } + cout << endl; + + for(int i = 0; i < devices.size(); i++) + { + cout + << devices[i].id << "\033[" << (idLength - numDigits(devices[i].id) + 2) << "C" + << devices[i].title << "\033[" << (titleLength - devices[i].title.length() + 2) << "C" + << devices[i].model << endl; + } + + return 0; + } + + + // device id + if(vm.count("id")) + { + id = vm["id"].as(); + } + else + { + cout << desc << endl; + return 1; + } + + + // load message + if(vm.count("pipe")) + { + message = "pipe"; + } + else + { + stringstream lastArgument; + lastArgument << argv[argc-1]; + + message = lastArgument.str(); + + cout << message << endl; + } + + + // send the message + pusherInstance.sendToDevice(id, message); + } + + + /* ERROR HANDLING */ + + + // errors thrown by pushhandler + catch(PusherError& e) + { + cout << "Error: " << e.what() << endl; + } + + + // boos ptree + catch(const boost::property_tree::ptree_error &e) + { + cout << "Maybe you need to request your appToken first?\n" << endl; + cout << e.what() << "\n" << desc << endl; + } + + + // other errors + catch(exception& e) + { + cout << "Some kind of error occured: " << e.what() << endl; + } + + return 0; +} diff --git a/src/pushhandler.cpp b/src/pushhandler.cpp new file mode 100644 index 0000000..a9c7899 --- /dev/null +++ b/src/pushhandler.cpp @@ -0,0 +1,232 @@ +#include "pushhandler.h" + +#include +#include +#include +#include +#include +#include + +#include + +using namespace std; + +// needed for handling curl output +static size_t WriteCallback(void *contents, size_t size, size_t nmemb, void *userp) +{ + ((std::string*)userp)->append((char*)contents, size * nmemb); + return size * nmemb; +} + + +// curl wrapper +string curlHandler(string data, const char* url) +{ + CURL *curl; + CURLcode res; + string readBuffer; + curl = curl_easy_init(); + + if(curl) + { + curl_easy_setopt(curl, CURLOPT_URL, url); + curl_easy_setopt(curl, CURLOPT_USERAGENT, USER_AGENT); + curl_easy_setopt(curl, CURLOPT_POSTFIELDS, data.c_str()); + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteCallback); + curl_easy_setopt(curl, CURLOPT_WRITEDATA, &readBuffer); + res = curl_easy_perform(curl); + curl_easy_cleanup(curl); + + if(res != CURLE_OK) + { + throw PusherError("Network Error"); + } + } + + + return readBuffer; +} + + +// constructor only username +PushHandler::PushHandler(string username) +{ + this->username = username; +} + + +// constructor with appToken +PushHandler::PushHandler(string username, string appToken) +{ + this->username = username; + this->appToken = appToken; +} + + +// login +string PushHandler::login(string password) +{ + // build request data + stringstream requestData; + requestData << boost::format("apiToken=%1%&username=%2%&password=%3%") + % API_TOKEN + % this->username + % password; + + // network request + string readBuffer; + readBuffer = curlHandler(requestData.str(), URL_PN_LOGIN); + + // json parsing + stringstream jsonData; + jsonData << readBuffer; + + boost::property_tree::ptree pt; + boost::property_tree::read_json(jsonData, pt); + + if(pt.get("status") != "ok") + { + throw PusherError("wrong credentials"); + } + + this->appToken = pt.get("appToken"); + return this->appToken; +} + + +// get list of devices +vector PushHandler::getDevices() +{ + // build request data + stringstream requestData; + requestData << boost::format("apiToken=%1%&appToken=%2%") + % API_TOKEN + % this->appToken; + + // network request + string readBuffer; + readBuffer = curlHandler(requestData.str(), URL_PN_GET_DEVICES); + + // json parsing + stringstream jsonData; + jsonData << readBuffer; + + boost::property_tree::ptree pt; + boost::property_tree::read_json(jsonData, pt); + + // handle the codes + switch(pt.get("code")) + { + case 1: throw PusherError("Invalid API Token"); + break; + case 2: throw PusherError("App Token missing"); + break; + case 3: throw PusherError("App Token invalid"); + } + + vector buffer; + + BOOST_FOREACH(boost::property_tree::ptree::value_type& v, pt.get_child("devices")) + { + Device buf; + + buf.title = v.second.get("title"); + buf.id = v.second.get("id"); + buf.model = v.second.get("model"); + + buffer.push_back(buf); + } + + return buffer; +} + + +// verify token +bool PushHandler::verifyToken() +{ + // build request data + stringstream requestData; + requestData << boost::format("apiToken=%1%&username=%2%&appToken=%3%") + % API_TOKEN + % this->username + % this->appToken; + + // network request + string readBuffer; + readBuffer = curlHandler(requestData.str(), URL_PN_CHECK_TOKEN); + + // json parser + stringstream jsonData; + jsonData << readBuffer; + + boost::property_tree::ptree pt; + boost::property_tree::read_json(jsonData, pt); + + + switch(pt.get("code")) + { + case 0: return true; + break; + case 1: throw PusherError("Invalid API-Token"); + break; + case 2: return false; + default: throw PusherError("Invalid server response"); + } +} + + +// send to device +void PushHandler::sendToDevice(int id, string message) +{ + // analyze content-type + // ...... + + + // actual sending + // build request data + stringstream requestData; + requestData << boost::format("apiToken=%1%&appToken=%2%&app=%3%&deviceID=%4%&type=%5%&content=%6%") + % API_TOKEN + % this->appToken + % APP_PACKAGE + % id + % "MESSAGE" + % message; + + // network request + string readBuffer; + readBuffer = curlHandler(requestData.str(), URL_PN_SEND_TO_DEVICE); + + // json parsing + stringstream jsonData; + jsonData << readBuffer; + + boost::property_tree::ptree pt; + boost::property_tree::read_json(jsonData, pt); + + + switch(pt.get("id")) + { + case 0: //return 0; + break; + case 1: throw PusherError("Invalid API Token"); + break; + case 2: throw PusherError("App Token missing"); + break; + case 3: throw PusherError("App Token invalid"); + break; + case 4: throw PusherError("Package Name missing"); + break; + case 5: throw PusherError("Package Name invalid"); + break; + case 6: throw PusherError("Package Name is not linked with the provided API Token"); + break; + case 7: throw PusherError("Device ID missing"); + break; + case 8: throw PusherError("Device ID invalid"); + break; + case 9: throw PusherError("Type missing or invalid"); + break; + default: throw PusherError("Invalid server response"); + } +} diff --git a/src/pushhandler.h b/src/pushhandler.h new file mode 100644 index 0000000..82babf2 --- /dev/null +++ b/src/pushhandler.h @@ -0,0 +1,59 @@ +#ifndef H_PUSHHANDLER +#define H_PUSHHANDLER + +#include +#include + + +#define API_TOKEN "1234" // change this line + +#define APP_PACKAGE "com.hackherz.pusher" +#define USER_AGENT "pusher/0.2" + +#define URL_PN_LOGIN "http://a.pushnotifier.de/1/login/" +#define URL_PN_CHECK_TOKEN "http://a.pushnotifier.de/1/checkToken/" +#define URL_PN_GET_DEVICES "http://a.pushnotifier.de/1/getDevices/" +#define URL_PN_SEND_TO_DEVICE "http://a.pushnotifier.de/1/sendToDevice/" + + + +class PushHandler +{ +public: + PushHandler(std::string username); + PushHandler(std::string username, std::string appToken); + + + typedef struct + { + std::string title; + int id; + std::string model; + } Device; + + + std::string login(std::string password); + std::vector getDevices(); + void sendToDevice(int deviceID, std::string message); + bool verifyToken(); + + +private: + std::string username; + std::string appToken; +}; + + +// class for exceptions +class PusherError +{ +public: + PusherError(std::string content) { this->content = content; } + std::string what() { return this->content; } +private: + std::string content; +}; + + +#endif +