mirror of
https://github.com/HackHerz/pusher
synced 2025-12-06 02:10:19 +00:00
First commit
This commit is contained in:
commit
b403801750
6 changed files with 571 additions and 0 deletions
242
src/main.cpp
Normal file
242
src/main.cpp
Normal file
|
|
@ -0,0 +1,242 @@
|
|||
/*
|
||||
* pusher
|
||||
* (c) 2014 Daniel Stein
|
||||
* github link..
|
||||
*
|
||||
* TODO
|
||||
* syslog
|
||||
* content type detection
|
||||
* gitignore of lines
|
||||
* secret input of password
|
||||
*/
|
||||
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <fstream>
|
||||
#include <sstream>
|
||||
|
||||
// Boost
|
||||
#include <boost/program_options.hpp>
|
||||
#include <boost/format.hpp>
|
||||
#include <boost/property_tree/ptree.hpp>
|
||||
#include <boost/property_tree/ini_parser.hpp>
|
||||
|
||||
// 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<int>(), "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<std::string>("pusher.username");
|
||||
string appToken = pt.get<std::string>("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<PushHandler::Device> 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<int>();
|
||||
}
|
||||
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;
|
||||
}
|
||||
232
src/pushhandler.cpp
Normal file
232
src/pushhandler.cpp
Normal file
|
|
@ -0,0 +1,232 @@
|
|||
#include "pushhandler.h"
|
||||
|
||||
#include <sstream>
|
||||
#include <curl/curl.h>
|
||||
#include <boost/format.hpp>
|
||||
#include <boost/foreach.hpp>
|
||||
#include <boost/property_tree/ptree.hpp>
|
||||
#include <boost/property_tree/json_parser.hpp>
|
||||
|
||||
#include <iostream>
|
||||
|
||||
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<string>("status") != "ok")
|
||||
{
|
||||
throw PusherError("wrong credentials");
|
||||
}
|
||||
|
||||
this->appToken = pt.get<string>("appToken");
|
||||
return this->appToken;
|
||||
}
|
||||
|
||||
|
||||
// get list of devices
|
||||
vector<PushHandler::Device> 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<int>("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<Device> buffer;
|
||||
|
||||
BOOST_FOREACH(boost::property_tree::ptree::value_type& v, pt.get_child("devices"))
|
||||
{
|
||||
Device buf;
|
||||
|
||||
buf.title = v.second.get<string>("title");
|
||||
buf.id = v.second.get<int>("id");
|
||||
buf.model = v.second.get<string>("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<int>("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<int>("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");
|
||||
}
|
||||
}
|
||||
59
src/pushhandler.h
Normal file
59
src/pushhandler.h
Normal file
|
|
@ -0,0 +1,59 @@
|
|||
#ifndef H_PUSHHANDLER
|
||||
#define H_PUSHHANDLER
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
|
||||
#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<Device> 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
|
||||
|
||||
Loading…
Add table
Add a link
Reference in a new issue