diff --git a/DDV_Controller/main.cpp b/DDV_Controller/main.cpp index d41eddbb..3856a303 100644 --- a/DDV_Controller/main.cpp +++ b/DDV_Controller/main.cpp @@ -90,63 +90,110 @@ void Authorize( Json::Value & Request, Json::Value & Storage, Json::Value & Resp } void CheckConfig(Json::Value & in, Json::Value & out){ - + Json::ValueIterator jit; + if (in.isObject()){ + for (jit = in.begin(); jit != in.end(); jit++){ + if (out.isObject() && out.isMember(jit.key().asString())){ + Log("CONF", "Updated configuration value "+jit.key().asString(), out); + }else{ + Log("CONF", "New configuration value "+jit.key().asString(), out); + } + } + if (out.isObject()){ + for (jit = out.begin(); jit != out.end(); jit++){ + if (!in.isMember(jit.key().asString())){ + Log("CONF", "Deleted configuration value "+jit.key().asString(), out); + } + } + } + } + out = in; } void CheckStreams(Json::Value & in, Json::Value & out){ - + Json::ValueIterator jit; + if (in.isObject()){ + for (jit = in.begin(); jit != in.end(); jit++){ + if (out.isObject() && out.isMember(jit.key().asString())){ + Log("STRM", "Updated stream "+jit.key().asString(), out); + }else{ + Log("STRM", "New stream "+jit.key().asString(), out); + } + } + if (out.isObject()){ + for (jit = out.begin(); jit != out.end(); jit++){ + if (!in.isMember(jit.key().asString())){ + Log("STRM", "Deleted stream "+jit.key().asString(), out); + } + } + } + } + out = in; } int main() { - Socket::Server API_Socket = Socket::Server( 4242, "0.0.0.0", true ); - Socket::Connection TempConn; + time_t lastuplink = 0; + Socket::Server API_Socket = Socket::Server(4242, "0.0.0.0", true); + Socket::Server Stats_Socket = Socket::Server("/tmp/ddv_statistics", true); + Socket::Connection Incoming; std::vector< ConnectedUser > users; - HTTP::Parser HTTP_R, HTTP_S; - Json::Value Request; - Json::Value Response; - Json::Value Storage; + Json::Value Request = Json::Value(Json::objectValue); + Json::Value Response = Json::Value(Json::objectValue); + Json::Value Storage = Json::Value(Json::objectValue); Json::Reader JsonParse; + std::string jsonp; JsonParse.parse(ReadFile("config.json"), Storage, false); + Storage["config"] = Json::Value(Json::objectValue); Storage["account"]["gearbox"]["password"] = Json::Value("7e0f87b116377621a75a6440ac74dcf4"); while (API_Socket.connected()){ usleep(10000); //sleep for 10 ms - prevents 100% CPU time - TempConn = API_Socket.accept(); - if (TempConn.connected()){users.push_back(TempConn);} + Incoming = API_Socket.accept(); + if (Incoming.connected()){users.push_back(Incoming);} if (users.size() > 0){ - for( std::vector< ConnectedUser >::iterator it = users.end() - 1; it >= users.begin(); it-- ) { - if( !(*it).C.connected() ) { - (*it).C.close(); - users.erase( it ); + for( std::vector< ConnectedUser >::iterator it = users.end() - 1; it >= users.begin(); it--) { + if (!it->C.connected()){ + it->C.close(); + users.erase(it); } - if ((*it).H.Read((*it).C)){ + if (it->H.Read(it->C)){ Response.clear(); //make sure no data leaks from previous requests - std::cout << "Body: " << HTTP_R.body << std::endl; - std::cout << "Command: " << HTTP_R.GetVar("command") << std::endl; - JsonParse.parse(HTTP_R.GetVar("command"), Request, false); - std::cout << Request.toStyledString() << std::endl; - Authorize(Request, Storage, Response, (*it)); - if ((*it).Authorized){ - //Parse config and streams from the request. - if (Request.isMember("config")){CheckConfig(Request["config"], Storage["config"]);} - if (Request.isMember("streams")){CheckStreams(Request["streams"], Storage["streams"]);} - //sent current configuration, no matter if it was changed or not - Response["streams"] = Storage["streams"]; - Response["config"] = Storage["config"]; - //add the current unix time to the config, for syncing reasons - Response["config"]["time"] = (Json::Value::UInt)time(0); - //sent any available logs and statistics - Response["log"] = Storage["log"]; - Response["statistics"] = Storage["statistics"]; - //clear log and statistics to prevent useless data transfer - Storage["log"].clear(); - Storage["statistics"].clear(); + if (!JsonParse.parse(it->H.GetVar("command"), Request, false)){ + Log("HTTP", "Failed to parse JSON: "+it->H.GetVar("command"), Storage); + Response["authorize"]["status"] = "INVALID"; + }else{ + std::cout << "Request: " << Request.toStyledString() << std::endl; + Authorize(Request, Storage, Response, (*it)); + if (it->Authorized){ + //Parse config and streams from the request. + if (Request.isMember("config")){CheckConfig(Request["config"], Storage["config"]);} + //if (Request.isMember("streams")){CheckStreams(Request["streams"], Storage["streams"]);} + //sent current configuration, no matter if it was changed or not + //Response["streams"] = Storage["streams"]; + Response["config"] = Storage["config"]; + //add required data to the current unix time to the config, for syncing reasons + Response["config"]["time"] = (Json::Value::UInt)time(0); + if (!Response["config"].isMember("serverid")){Response["config"]["serverid"] = "";} + //sent any available logs and statistics + Response["log"] = Storage["log"]; + Response["statistics"] = Storage["statistics"]; + //clear log and statistics to prevent useless data transfer + Storage["log"].clear(); + Storage["statistics"].clear(); + } } - (*it).H.Clean(); - (*it).H.protocol = "HTTP/1.1"; - (*it).H.SetHeader( "Content-Type", "text/javascript" ); - (*it).H.SetBody( Response.toStyledString() + "\n\n" ); - (*it).C.write( (*it).H.BuildResponse( "200", "OK" ) ); - (*it).H.Clean(); + jsonp = ""; + if (it->H.GetVar("callback") != ""){jsonp = it->H.GetVar("callback");} + if (it->H.GetVar("jsonp") != ""){jsonp = it->H.GetVar("jsonp");} + it->H.Clean(); + it->H.protocol = "HTTP/1.0"; + it->H.SetHeader("Content-Type", "text/javascript"); + if (jsonp == ""){ + it->H.SetBody(Response.toStyledString()+"\n\n"); + }else{ + it->H.SetBody(jsonp+"("+Response.toStyledString()+");\n\n"); + } + it->C.write(it->H.BuildResponse("200", "OK")); + it->H.Clean(); } } } diff --git a/util/http_parser.cpp b/util/http_parser.cpp index 274cb3c3..da800130 100644 --- a/util/http_parser.cpp +++ b/util/http_parser.cpp @@ -14,9 +14,9 @@ void HTTP::Parser::Clean(){ method = "GET"; url = "/"; protocol = "HTTP/1.1"; - body = ""; + body.clear(); length = 0; - HTTPbuffer = ""; + HTTPbuffer.clear(); headers.clear(); vars.clear(); } @@ -172,7 +172,10 @@ bool HTTP::Parser::parse(){ if (f != std::string::npos){url = tmpA.substr(0, f); tmpA.erase(0, f+1);} f = tmpA.find(' '); if (f != std::string::npos){protocol = tmpA.substr(0, f); tmpA.erase(0, f+1);} - /// \todo Include GET variable parsing? + if (url.find('?') != std::string::npos){ + std::string queryvars = url.substr(url.find('?')+1); + parseVars(queryvars); //parse GET variables + } }else{ if (tmpA.size() == 0){ seenHeaders = true; @@ -191,22 +194,7 @@ bool HTTP::Parser::parse(){ if (HTTPbuffer.length() >= length){ body = HTTPbuffer.substr(0, length); HTTPbuffer.erase(0, length); - std::string tmppost = body; - std::string varname; - std::string varval; - while (tmppost.find('=') != std::string::npos){ - size_t found = tmppost.find('='); - varname = urlunescape((char*)tmppost.substr(0, found).c_str()); - tmppost.erase(0, found+1); - found = tmppost.find('&'); - varval = urlunescape((char*)tmppost.substr(0, found).c_str()); - SetVar(varname, varval); - if (found == std::string::npos){ - tmppost.clear(); - }else{ - tmppost.erase(0, found+1); - } - } + parseVars(body); //parse POST variables return true; }else{ return false; @@ -229,6 +217,26 @@ void HTTP::Parser::SendResponse(Socket::Connection & conn, std::string code, std conn.write(tmp); } +/// Parses GET or POST-style variable data. +/// Saves to internal variable structure using HTTP::Parser::SetVar. +void HTTP::Parser::parseVars(std::string & data){ + std::string varname; + std::string varval; + while (data.find('=') != std::string::npos){ + size_t found = data.find('='); + varname = urlunescape(data.substr(0, found)); + data.erase(0, found+1); + found = data.find('&'); + varval = urlunescape(data.substr(0, found)); + SetVar(varname, varval); + if (found == std::string::npos){ + data.clear(); + }else{ + data.erase(0, found+1); + } + } +} + /// Sends data as HTTP/1.1 bodypart to conn. /// HTTP/1.1 chunked encoding is automatically applied if needed. /// \param conn The Socket::Connection to send the part over. @@ -257,24 +265,26 @@ void HTTP::Parser::SendBodyPart(Socket::Connection & conn, std::string bodypart) } } -/// Unescapes URLencoded C-strings to a std::string. -/// This function *will* destroy the input data! -std::string HTTP::Parser::urlunescape(char *s){ - char *p; - for (p = s; *s != '\0'; ++s){ - if (*s == '%'){ - if (*++s != '\0'){ - *p = unhex(*s) << 4; +/// Unescapes URLencoded std::strings. +std::string HTTP::Parser::urlunescape(std::string in){ + std::string out; + for (unsigned int i = 0; i < in.length(); ++i){ + if (in[i] == '%'){ + char tmp = 0; + ++i; + if (i < in.length()){ + tmp = unhex(in[i]) << 4; } - if (*++s != '\0'){ - *p++ += unhex(*s); + ++i; + if (i < in.length()){ + tmp += unhex(in[i]); } + out += tmp; } else { - if (*s == '+'){*p++ = ' ';}else{*p++ = *s;} + if (in[i] == '+'){out += ' ';}else{out += in[i];} } } - *p = '\0'; - return std::string(s); + return out; } /// Helper function for urlunescape. diff --git a/util/http_parser.h b/util/http_parser.h index 542c06e6..a38b94e1 100644 --- a/util/http_parser.h +++ b/util/http_parser.h @@ -30,7 +30,7 @@ namespace HTTP{ void SendBodyPart(Socket::Connection & conn, std::string bodypart); void Clean(); bool CleanForNext(); - std::string urlunescape(char *s); ///< Unescapes URLencoded C-strings to a std::string. + std::string urlunescape(std::string in); std::string body; std::string method; std::string url; @@ -40,6 +40,7 @@ namespace HTTP{ bool seenHeaders; bool seenReq; bool parse(); + void parseVars(std::string & data); std::string HTTPbuffer; std::map headers; std::map vars; diff --git a/util/proc.cpp b/util/proc.cpp index b5257065..bfcd3210 100644 --- a/util/proc.cpp +++ b/util/proc.cpp @@ -10,10 +10,35 @@ #if DEBUG >= 1 #include #endif +#include +#include std::map Util::Procs::plist; bool Util::Procs::handler_set = false; +/// Sets the current process' running user +void Util::setUser(std::string username){ + if (username != "root"){ + struct passwd * user_info = getpwnam(username.c_str()); + if (!user_info){ + #if DEBUG >= 1 + fprintf(stderr, "Error: could not setuid %s: could not get PID\n", username.c_str()); + #endif + return 1; + }else{ + if (setuid(user_info->pw_uid) != 0){ + #if DEBUG >= 1 + fprintf(stderr, "Error: could not setuid %s: not allowed\n", username.c_str()); + #endif + }else{ + #if DEBUG >= 3 + fprintf(stderr, "Changed user to %s\n", username.c_str()); + #endif + } + } + } +} + /// Used internally to capture child signals and update plist. void Util::Procs::childsig_handler(int signum){ if (signum != SIGCHLD){return;} diff --git a/util/proc.h b/util/proc.h index a74d75bd..4b117f0c 100644 --- a/util/proc.h +++ b/util/proc.h @@ -27,4 +27,7 @@ namespace Util{ static pid_t getPid(std::string name); static std::string getName(pid_t name); }; + + static setUser(std::string user); + };