void WebServer::get(BaseLib::HTTP& http, std::shared_ptr<BaseLib::SocketOperations> socket) { try { if(!socket) { _out.printError("Error: Socket is nullptr."); return; } std::string path = http.getHeader()->path; BaseLib::EventHandlers eventHandlers = getEventHandlers(); for(BaseLib::EventHandlers::const_iterator i = eventHandlers.begin(); i != eventHandlers.end(); ++i) { i->second->lock(); if(i->second->handler() && ((BaseLib::Rpc::IWebserverEventSink*)i->second->handler())->onGet(_serverInfo, http, socket, path)) { i->second->unlock(); return; } i->second->unlock(); } std::vector<std::string> headers; std::vector<char> content; if(!path.empty() && path.front() == '/') path = path.substr(1); bool isDirectory = false; BaseLib::Io::isDirectory(_serverInfo->contentPath + path, isDirectory); if(isDirectory) { if(!path.empty() && path.back() != '/') { path.push_back('/'); std::vector<std::string> additionalHeaders({std::string("Location: ") + path}); getError(301, "Moved Permanently", "The document has moved <a href=\"" + path + "\">here</a>.", content, additionalHeaders); send(socket, content); return; } if(GD::bl->io.fileExists(_serverInfo->contentPath + path + "index.php")) path += "index.php"; else if(GD::bl->io.fileExists(_serverInfo->contentPath + path + "index.php5")) path += "index.php5"; else if(GD::bl->io.fileExists(_serverInfo->contentPath + path + "index.php7")) path += "index.php7"; else if(GD::bl->io.fileExists(_serverInfo->contentPath + path + "index.html")) path += "index.html"; else if(GD::bl->io.fileExists(_serverInfo->contentPath + path + "index.htm")) path += "index.htm"; else { getError(404, "Not Found", "The requested URL / was not found on this server.", content); send(socket, content); return; } } if(!BaseLib::Io::fileExists(_serverInfo->contentPath + path)) { getError(404, _http.getStatusText(404), "The requested URL " + path + " was not found on this server.", content); send(socket, content); return; } try { _out.printInfo("Client is requesting: " + http.getHeader()->path + " (translated to " + _serverInfo->contentPath + path + ", method: GET)"); std::string ending = ""; int32_t pos = path.find_last_of('.'); if(pos != (signed)std::string::npos && (unsigned)pos < path.size() - 1) ending = path.substr(pos + 1); GD::bl->hf.toLower(ending); std::string contentString; #ifdef SCRIPTENGINE if(ending == "php" || ending == "php5" || ending == "php7") { GD::scriptEngine->executeWebRequest(_serverInfo->contentPath + path, http, _serverInfo, socket); socket->close(); return; } #endif std::string contentType = _http.getMimeType(ending); if(contentType.empty()) contentType = "application/octet-stream"; //Don't return content when method is "HEAD" if(http.getHeader()->method == "GET") contentString = GD::bl->io.getFileContent(_serverInfo->contentPath + path); std::string header; _http.constructHeader(contentString.size(), contentType, 200, "OK", headers, header); content.insert(content.end(), header.begin(), header.end()); if(!contentString.empty()) content.insert(content.end(), contentString.begin(), contentString.end()); send(socket, content); } catch(const std::exception& ex) { getError(404, _http.getStatusText(404), "The requested URL " + path + " was not found on this server.", content); send(socket, content); return; } catch(BaseLib::Exception& ex) { getError(404, _http.getStatusText(404), "The requested URL " + path + " was not found on this server.", content); send(socket, content); return; } } catch(const std::exception& ex) { _out.printEx(__FILE__, __LINE__, __PRETTY_FUNCTION__, ex.what()); } catch(BaseLib::Exception& ex) { _out.printEx(__FILE__, __LINE__, __PRETTY_FUNCTION__, ex.what()); } catch(...) { _out.printEx(__FILE__, __LINE__, __PRETTY_FUNCTION__); } }
bool Auth::basicServer(BaseLib::HTTP& httpPacket) { if(!_initialized) throw AuthException("Not initialized."); _http.reset(); uint32_t bufferLength = 1024; char buffer[bufferLength + 1]; if(httpPacket.getHeader()->authorization.empty()) { if(_basicAuthHTTPHeader.empty()) { _basicAuthHTTPHeader.append("HTTP/1.1 401 Authorization Required\r\n"); _basicAuthHTTPHeader.append("WWW-Authenticate: Basic realm=\"Authentication Required\"\r\n"); _basicAuthHTTPHeader.append("Connection: Keep-Alive\r\n"); std::string content("<!DOCTYPE html PUBLIC \"-//W3C//DTD HTML 4.01//EN\" \"http://www.w3.org/TR/html4/strict.dtd\"><html><head><meta http-equiv=\"content-type\" content=\"text/html; charset=utf-8\"><title>Authorization Required</title></head><body>Authorization Required</body></html>"); _basicAuthHTTPHeader.append("Content-Length: " + std::to_string(content.size()) + "\r\n\r\n"); _basicAuthHTTPHeader.append(content); } std::shared_ptr<std::vector<char>> data(new std::vector<char>()); data->insert(data->begin(), _basicAuthHTTPHeader.begin(), _basicAuthHTTPHeader.end()); try { _socket->proofwrite(data); int32_t bytesRead = _socket->proofread(buffer, bufferLength); //Some clients send only one byte in the first packet if(bytesRead == 1) bytesRead += _socket->proofread(&buffer[1], bufferLength - 1); buffer[bytesRead] = '\0'; try { _http.process(buffer, bufferLength); } catch(BaseLib::HTTPException& ex) { throw AuthException("Authorization failed because of HTTP exception: " + ex.what()); } } catch(const BaseLib::SocketOperationException& ex) { throw AuthException("Authorization failed because of socket exception: " + ex.what()); } } else _http = httpPacket; if(_http.getHeader()->authorization.empty()) { sendBasicUnauthorized(false); throw AuthException("No header field \"Authorization\""); } std::pair<std::string, std::string> authData = BaseLib::HelperFunctions::splitLast(_http.getHeader()->authorization, ' '); BaseLib::HelperFunctions::toLower(authData.first); if(authData.first != "basic") { sendBasicUnauthorized(false); throw AuthException("Authorization type is not basic but: " + authData.first); } std::string decodedData; BaseLib::Base64::decode(authData.second, decodedData); std::pair<std::string, std::string> credentials = BaseLib::HelperFunctions::splitLast(decodedData, ':'); BaseLib::HelperFunctions::toLower(credentials.first); if(std::find(_validUsers.begin(), _validUsers.end(), credentials.first) == _validUsers.end()) { sendBasicUnauthorized(false); throw AuthException("User name " + credentials.first + " is not in the list of valid users in /etc/homegear/rpcservers.conf."); } if(User::verify(credentials.first, credentials.second)) return true; sendBasicUnauthorized(false); return false; }
void WebServer::post(BaseLib::HTTP& http, std::shared_ptr<BaseLib::SocketOperations> socket) { try { std::vector<char> content; #ifdef SCRIPTENGINE if(!socket) { _out.printError("Error: Socket is nullptr."); return; } std::string path = http.getHeader()->path; BaseLib::EventHandlers eventHandlers = getEventHandlers(); for(BaseLib::EventHandlers::const_iterator i = eventHandlers.begin(); i != eventHandlers.end(); ++i) { i->second->lock(); if(i->second->handler() && ((BaseLib::Rpc::IWebserverEventSink*)i->second->handler())->onGet(_serverInfo, http, socket, path)) { i->second->unlock(); return; } i->second->unlock(); } if(!path.empty() && path.front() == '/') path = path.substr(1); bool isDirectory = false; BaseLib::Io::isDirectory(_serverInfo->contentPath + path, isDirectory); if(isDirectory) { if(!path.empty() && path.back() != '/') { path.push_back('/'); std::vector<std::string> additionalHeaders({std::string("Location: ") + path}); getError(301, "Moved Permanently", "The document has moved <a href=\"" + path + "\">here</a>.", content, additionalHeaders); send(socket, content); return; } if(GD::bl->io.fileExists(_serverInfo->contentPath + path + "index.php")) path += "index.php"; else if(GD::bl->io.fileExists(_serverInfo->contentPath + path + "index.php5")) path += "index.php5"; else if(GD::bl->io.fileExists(_serverInfo->contentPath + path + "index.php7")) path += "index.php7"; else { getError(404, _http.getStatusText(404), "The requested URL / was not found on this server.", content); send(socket, content); return; } } if(!BaseLib::Io::fileExists(_serverInfo->contentPath + path)) { getError(404, _http.getStatusText(404), "The requested URL " + path + " was not found on this server.", content); send(socket, content); return; } try { _out.printInfo("Client is requesting: " + http.getHeader()->path + " (translated to: \"" + _serverInfo->contentPath + path + "\", method: POST)"); GD::scriptEngine->executeWebRequest(_serverInfo->contentPath + path, http, _serverInfo, socket); socket->close(); } catch(const std::exception& ex) { getError(404, _http.getStatusText(404), "The requested URL " + path + " was not found on this server.", content); send(socket, content); return; } catch(BaseLib::Exception& ex) { getError(404, _http.getStatusText(404), "The requested URL " + path + " was not found on this server.", content); send(socket, content); return; } #else getError(304, _http.getStatusText(304), "Homegear is compiled without script engine.", content); send(socket, content); #endif } catch(const std::exception& ex) { _out.printEx(__FILE__, __LINE__, __PRETTY_FUNCTION__, ex.what()); } catch(BaseLib::Exception& ex) { _out.printEx(__FILE__, __LINE__, __PRETTY_FUNCTION__, ex.what()); } catch(...) { _out.printEx(__FILE__, __LINE__, __PRETTY_FUNCTION__); } }
int32_t ScriptEngine::executeWebRequest(const std::string& path, BaseLib::HTTP& request, std::shared_ptr<BaseLib::Rpc::ServerInfo::Info>& serverInfo, std::shared_ptr<BaseLib::SocketOperations>& socket) { if(_disposing) return 1; if(!GD::bl->io.fileExists(path)) { GD::out.printError("Error: PHP script \"" + path + "\" does not exist."); return -1; } ts_resource_ex(0, NULL); //Replaces TSRMLS_FETCH() try { zend_file_handle script; script.type = ZEND_HANDLE_FILENAME; script.filename = path.c_str(); script.opened_path = NULL; script.free_filename = 0; zend_homegear_globals* globals = php_homegear_get_globals(); if(!globals) { ts_free_thread(); return -1; } globals->socket = socket.get(); globals->http = &request; if(!tsrm_get_ls_cache()) { GD::out.printCritical("Critical: Error in PHP: No thread safe resource exists (1)."); ts_free_thread(); return -1; } if(!((sapi_globals_struct *) (*((void ***) tsrm_get_ls_cache()))[((sapi_globals_id)-1)])) { GD::out.printCritical("Critical: Error in PHP: No thread safe resource exists (2)."); ts_free_thread(); return -1; } SG(server_context) = (void*)serverInfo.get(); //Must be defined! Otherwise POST data is not processed. SG(sapi_headers).http_response_code = 200; SG(default_mimetype) = nullptr; SG(default_charset) = nullptr; SG(request_info).content_length = request.getHeader()->contentLength; if(!request.getHeader()->contentType.empty()) SG(request_info).content_type = request.getHeader()->contentType.c_str(); SG(request_info).request_method = request.getHeader()->method.c_str(); SG(request_info).proto_num = request.getHeader()->protocol == BaseLib::HTTP::Protocol::http10 ? 1000 : 1001; std::string uri = request.getHeader()->path + request.getHeader()->pathInfo; if(!request.getHeader()->args.empty()) uri.append('?' + request.getHeader()->args); if(!request.getHeader()->args.empty()) SG(request_info).query_string = estrndup(&request.getHeader()->args.at(0), request.getHeader()->args.size()); if(!uri.empty()) SG(request_info).request_uri = estrndup(&uri.at(0), uri.size()); std::string pathTranslated = serverInfo->contentPath.substr(0, serverInfo->contentPath.size() - 1) + request.getHeader()->pathInfo; SG(request_info).path_translated = estrndup(&pathTranslated.at(0), pathTranslated.size()); if (php_request_startup() == FAILURE) { GD::bl->out.printError("Error calling php_request_startup..."); ts_free_thread(); return 1; } php_execute_script(&script); int32_t exitCode = EG(exit_status); if(SG(request_info).query_string) { efree(SG(request_info).query_string); SG(request_info).query_string = nullptr; } if(SG(request_info).request_uri) { efree(SG(request_info).request_uri); SG(request_info).request_uri = nullptr; } if(SG(request_info).path_translated) { efree(SG(request_info).path_translated); SG(request_info).path_translated = nullptr; } php_request_shutdown(NULL); ts_free_thread(); return exitCode; } catch(const std::exception& ex) { GD::bl->out.printEx(__FILE__, __LINE__, __PRETTY_FUNCTION__, ex.what()); } catch(BaseLib::Exception& ex) { GD::bl->out.printEx(__FILE__, __LINE__, __PRETTY_FUNCTION__, ex.what()); } catch(...) { GD::bl->out.printEx(__FILE__, __LINE__, __PRETTY_FUNCTION__); } std::string error("Error executing script. Check Homegear log for more details."); if(socket) php_homegear_write_socket(socket.get(), error.c_str(), error.length()); if(SG(request_info).query_string) { efree(SG(request_info).query_string); SG(request_info).query_string = nullptr; } if(SG(request_info).request_uri) { efree(SG(request_info).request_uri); SG(request_info).request_uri = nullptr; } if(SG(request_info).path_translated) { efree(SG(request_info).path_translated); SG(request_info).path_translated = nullptr; } ts_free_thread(); return 1; }