From 2f73d497e4d55960b0908ab341d83082bd435a2d Mon Sep 17 00:00:00 2001 From: cztomczak Date: Sat, 1 Dec 2018 21:39:50 +0100 Subject: [PATCH] Embed Mongoose server and PHP CGI (#221) PHP Desktop for Linux now displays index.php with phpinfo(). --- Makefile | 8 ++- build.sh | 5 ++ src/main.cpp | 44 +++++++++++- src/mongoose.c | 12 +++- src/mongoose_server.cpp | 155 ++++++++++++++++++++++++++++++++++++++++ src/mongoose_server.h | 11 +++ src/php.ini | 34 +++++++++ src/utils.cpp | 25 +++++++ src/utils.h | 7 ++ src/version.h | 1 + src/www/index.php | 5 ++ 11 files changed, 300 insertions(+), 7 deletions(-) create mode 100644 src/mongoose_server.cpp create mode 100644 src/mongoose_server.h create mode 100644 src/php.ini create mode 100644 src/utils.cpp create mode 100644 src/utils.h create mode 100644 src/version.h create mode 100644 src/www/index.php diff --git a/Makefile b/Makefile index d374d36..20c1ee7 100644 --- a/Makefile +++ b/Makefile @@ -7,13 +7,16 @@ CCFLAGS += $(shell pkg-config --cflags glib-2.0 gtk+-3.0) CFLAGS_OPTIMIZE = -O3 -fvisibility=hidden -LDFLAGS = -Wl,-rpath,. -Wl,-rpath,"\$$ORIGIN" -lX11 -lcef -lcef_dll_wrapper +LDFLAGS = -Wl,-rpath,. -Wl,-rpath,"\$$ORIGIN" -lX11 -lcef -lcef_dll_wrapper -Wl,--as-needed -ldl LDFLAGS += $(shell pkg-config --libs glib-2.0 gtk+-3.0) OBJS=\ src/main.o \ src/app.o \ src/client_handler.o \ + src/mongoose.o \ + src/mongoose_server.o \ + src/utils.o \ CC=g++ .PHONY: clean release debug @@ -33,6 +36,9 @@ debug: $(TARGET) %.o : %.cpp +$(CC) -c -o $@ -MD -MP -MF $@.deps $(CCFLAGS) $(CFLAGS_OPTIMIZE) $< +%.o : %.c + +gcc -c -o $@ -MD -MP -MF $@.deps -g -std=c99 -O2 -W -Wall -Werror -pedantic -pthread -pipe $< + $(TARGET): $(OBJS) +$(CC) $(CCFLAGS) $(CFLAGS_OPTIMIZE) -o $@ $(OBJS) $(LDFLAGS) diff --git a/build.sh b/build.sh index abdad23..7b7ca6c 100755 --- a/build.sh +++ b/build.sh @@ -24,6 +24,11 @@ make -R -f Makefile "$@" rc=$?; if [[ ${rc} = 0 ]]; then echo "OK phpdesktop was built"; + cp src/php.ini build/bin/ + if [[ -d build/bin/www ]]; then + rm -r build/bin/www + fi + cp -r src/www/ build/bin/ ./build/bin/phpdesktop rm -r blob_storage/ rm -r GPUCache/ diff --git a/src/main.cpp b/src/main.cpp index a9060b3..a9e3161 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -4,6 +4,8 @@ #include "app.h" #include "client_handler.h" +#include "utils.h" +#include "mongoose_server.h" #include "include/base/cef_logging.h" #include "include/wrapper/cef_helpers.h" @@ -16,6 +18,8 @@ #include #include +std::string g_cgi_env_from_argv = ""; + int XErrorHandlerImpl(Display* display, XErrorEvent* event) { LOG(WARNING) << "X error received: " @@ -33,6 +37,24 @@ int XIOErrorHandlerImpl(Display* display) { } int main(int argc, char **argv) { + LOG(INFO) << "Executable directory: " << executable_dir(); + + // Passing ENV variables to PHP using the --cgi-environment + // command line arg passed to app. + if (argv) { + for (int i = 0; i < argc; i++) { + std::string arg = argv[i]; + size_t pos = arg.find("="); + if (pos != std::string::npos) { + std::string name = arg.substr(0, pos); + std::string value = arg.substr(pos+1, std::string::npos); + if (name == "--cgi-environment" && value.length()) { + g_cgi_env_from_argv.assign(value); + } + } + } + } + // Create a copy of |argv| on Linux because Chromium mangles the value // internally (see CEF issue #620). CefScopedArgArray scoped_arg_array(argc, argv); @@ -74,6 +96,15 @@ int main(int argc, char **argv) { return exit_code; } + // Single instance application + // @TODO + + // Main window title + // @TODO from settings.json + + // Start Mongoose server + MongooseStart(); + // Install xlib error handlers so that the application won't be terminated // on non-fatal errors. X11 errors appearing in debug logs usually can be // ignored. @@ -83,8 +114,12 @@ int main(int argc, char **argv) { // Specify CEF global settings here. CefSettings settings; settings.no_sandbox = true; - CefString( &settings.log_file ) = "debug.log"; - settings.log_severity = LOGSEVERITY_INFO; + CefString( &settings.log_file ) = "debug.log"; // @TODO from settings.json + settings.log_severity = LOGSEVERITY_INFO; // @TODO from settings.json + // @TODO cache_path settings.json option + + // Remote debugging port + // @todo from settings.json // App implements application-level callbacks for the browser // process. @@ -113,11 +148,14 @@ int main(int argc, char **argv) { CefBrowserHost::CreateBrowserSync( window_info, ClientHandler::GetInstance(), - "https://www.google.com/", + MongooseGetUrl(), browser_settings, NULL); CefRunMessageLoop(); + + LOG(INFO) << "Stop Mongoose server"; + MongooseStop(); LOG(INFO) << "Shutdown CEF"; CefShutdown(); diff --git a/src/mongoose.c b/src/mongoose.c index 707ce51..4244eb7 100644 --- a/src/mongoose.c +++ b/src/mongoose.c @@ -1471,6 +1471,9 @@ static pid_t spawn_process(struct mg_connection *conn, const char *prog, (void) execle(prog, prog, NULL, envp); cry(conn, "%s: execle(%s): %s", __func__, prog, strerror(ERRNO)); } else { + // char prog_abspath[PATH_MAX]; + // strcpy(prog_abspath, dir); + // strcat(prog_abspath, prog); (void) execle(interp, interp, prog, NULL, envp); cry(conn, "%s: execle(%s %s): %s", __func__, interp, prog, strerror(ERRNO)); @@ -1832,6 +1835,7 @@ static void support_path_info_for_cgi_scripts( struct mg_connection *conn, char *buf, size_t buf_len, struct file *filep) { char *p; + if (buf_len) {} // unused paramter during compilation // Support PATH_INFO for CGI scripts. for (p = buf + strlen(buf); p > buf + 1; p--) { if (*p == '/') { @@ -3387,8 +3391,8 @@ static void prepare_cgi_environment(struct mg_connection *conn, if (prog2[i] == '/') { prog2[i] = '\\'; } -#endif } +#endif addenv(blk, "SCRIPT_FILENAME=%s", prog2); addenv(blk, "PATH_TRANSLATED=%s", prog2); @@ -4689,14 +4693,15 @@ static void close_all_listening_sockets(struct mg_context *ctx) { static int is_valid_port(unsigned int port) { // PHP Desktop Fix: // Allow value of 0, so that OS assigns a random free port. - return port >= 0 && port < 0xffff; + return port < 0xffff; } // Valid listening port specification is: [ip_address:]port[s] // Examples: 80, 443s, 127.0.0.1:3128, 1.2.3.4:8080s // TODO(lsm): add parsing of the IPv6 address static int parse_port_string(const struct vec *vec, struct socket *so) { - unsigned int a, b, c, d, ch, len, port; + unsigned int a, b, c, d, ch, port; + int len; #if defined(USE_IPV6) char buf[100]; #endif @@ -5518,6 +5523,7 @@ void mg_stop(struct mg_context *ctx) { } void mg_stop_immediately(struct mg_context *ctx) { + if (ctx) {} // unused parameter warning // Stopping Mongoose and freeing resources will hang when // used with IE webbrowser control. It seems that IE is not // freeing reources properly, it does it with some delay. diff --git a/src/mongoose_server.cpp b/src/mongoose_server.cpp new file mode 100644 index 0000000..9589c02 --- /dev/null +++ b/src/mongoose_server.cpp @@ -0,0 +1,155 @@ +// Copyright (c) 2018 PHP Desktop, see the Authors file. +// All rights reserved. Licensed under BSD 3-clause license. +// Project website: https://github.com/cztomczak/phpdesktop + +#include "mongoose.h" +#include "utils.h" +#include "version.h" +#include "include/base/cef_logging.h" + +std::string g_mongoose_port = "0"; // @TODO from settings.json +std::string g_mongoose_ip_address = "127.0.0.1"; // @TODO from settings.json +std::string g_mongoose_url = ""; + +struct mg_context* g_mongoose_context = 0; +extern std::string g_cgi_env_from_argv; + +static int mongoose_log_message(const struct mg_connection* conn, + const char *message) { + // Called when mongoose is about to log a message. If callback returns + // non-zero, mongoose does not log anything. + LOG(WARNING) << message; + return 0; +} + +static void mongoose_end_request(const struct mg_connection* conn, + int reply_status_code) { + // Called when mongoose has finished processing request. + mg_request_info* request = mg_get_request_info( + const_cast(conn)); + std::string message; + message.append(request->request_method); + message.append(" "); + std::stringstream ss; + ss << reply_status_code; + message.append(ss.str()); + message.append(" "); + message.append(request->uri); + if (request->query_string) { + message.append("?"); + message.append(request->query_string); + } + LOG(INFO) << message; +} + +bool MongooseStart() { + LOG(INFO) << "Start Mongoose server"; + + // Temp directory + std::string cgi_temp_dir = "/tmp"; + // @TODO load cgi_temp_dir from settings.json + char const* tmp = getenv("TMP"); + if (tmp != NULL) { + cgi_temp_dir.assign(tmp); + } else { + char const* temp = getenv("TEMP"); + if (temp != NULL) { + cgi_temp_dir.assign(temp); + } else { + char const* tmpdir = getenv("TMPDIR"); + if (tmpdir != NULL) { + cgi_temp_dir.assign(tmpdir); + } + } + } + + // CGI environment variables + std::string cgi_env = ""; + cgi_env.append("TMP=").append(cgi_temp_dir).append(","); + cgi_env.append("TEMP=").append(cgi_temp_dir).append(","); + cgi_env.append("TMPDIR=").append(cgi_temp_dir).append(","); + if (g_mongoose_ip_address != "*") { + cgi_env.append("SERVER_NAME=").append(g_mongoose_ip_address) + .append(","); + } + cgi_env.append("PHPDESKTOP_VERSION=").append(PHPDESKTOP_VERSION); + if (g_cgi_env_from_argv.length()) { + cgi_env.append(",").append(g_cgi_env_from_argv); + } + LOG(INFO) << "CGI environment variables set: " << cgi_env; + + // Document root + std::string document_root = executable_dir().append("/www"); + LOG(INFO) << "document_root=" << document_root; + + // Listening ports + // @TODO from settings.json + std::string listening_ports; + if (g_mongoose_ip_address == "*") { + listening_ports = g_mongoose_port; + } else { + listening_ports = g_mongoose_ip_address + ":" + g_mongoose_port; + } + + // CGI interpreter + std::string cgi_interpreter = executable_dir().append("/php-cgi"); + + // Mongoose options + const char* options[] = { + "document_root", document_root.c_str(), + "listening_ports", listening_ports.c_str(), + "index_files", "index.html,index.php", // @TODO from settings.json + "cgi_interpreter", cgi_interpreter.c_str(), + "cgi_pattern", "**.php$", // @TODO from settings.json + "cgi_environment", cgi_env.c_str(), + "404_handler", "/pretty-urls.php", // @TODO from settings.json + "hide_files_patterns", "", // @TODO from settings.json + NULL + }; + + // Start mongoose + mg_callbacks callbacks = {0}; + callbacks.log_message = &mongoose_log_message; + callbacks.end_request = &mongoose_end_request; + g_mongoose_context = mg_start(&callbacks, NULL, options); + if (g_mongoose_context == NULL) { + LOG(ERROR) << "mg_start() failed"; + return false; + } + + // When port was set to 0 then a random free port was assigned + // by OS. + int port = mg_get_listening_port(g_mongoose_context); + std::stringstream port_ss; + port_ss << port; + g_mongoose_port = port_ss.str(); + if (g_mongoose_ip_address == "*") { + g_mongoose_url = "http://127.0.0.1:" + port_ss.str() + "/"; + } else { + g_mongoose_ip_address = g_mongoose_ip_address; + g_mongoose_url = "http://" + g_mongoose_ip_address + ":" + + g_mongoose_port + "/"; + } + LOG(INFO) << "Mongoose url: " << g_mongoose_url; + + return true; +} + +void MongooseStop() { + // Don't call mg_stop(), as there were issues in the past + // with doing so. It is not necessary to stop mongoose server, + // as this is done only when application quits and mongoose + // server will automatically stop when application quits. +} + +std::string MongooseGetPort() { + return g_mongoose_port; +} + +std::string MongooseGetIpAddress() { + return g_mongoose_ip_address; +} + +std::string MongooseGetUrl() { + return g_mongoose_url; +} diff --git a/src/mongoose_server.h b/src/mongoose_server.h new file mode 100644 index 0000000..b7453f1 --- /dev/null +++ b/src/mongoose_server.h @@ -0,0 +1,11 @@ +// Copyright (c) 2018 PHP Desktop, see the Authors file. +// All rights reserved. Licensed under BSD 3-clause license. +// Project website: https://github.com/cztomczak/phpdesktop + +#include + +bool MongooseStart(); +void MongooseStop(); +std::string MongooseGetPort(); +std::string MongooseGetIpAddress(); +std::string MongooseGetUrl(); diff --git a/src/php.ini b/src/php.ini new file mode 100644 index 0000000..94e6940 --- /dev/null +++ b/src/php.ini @@ -0,0 +1,34 @@ +; Date +date.timezone=Europe/Berlin + +; Errors +error_reporting=E_ALL +display_errors=On +display_startup_errors=On +log_errors=Off +report_memleaks=On +report_zend_debug=On + +; General +short_open_tag=On +ignore_user_abort=Off +implicit_flush=Off +output_buffering=0 +default_charset = "UTF-8" + +; Execution time +max_execution_time=30 + +; Memory +memory_limit=128M + +; File uploads +; "post_max_size" must be equal or bigger than "upload_max_filesize" +max_file_uploads=20 +upload_max_filesize=2048M +post_max_size=2048M + +; Smtp server is not included with phpdesktop +SMTP=127.0.0.1 +smtp_port=25 + diff --git a/src/utils.cpp b/src/utils.cpp new file mode 100644 index 0000000..f1596f4 --- /dev/null +++ b/src/utils.cpp @@ -0,0 +1,25 @@ +// Copyright (c) 2018 PHP Desktop, see the Authors file. +// All rights reserved. Licensed under BSD 3-clause license. +// Project website: https://github.com/cztomczak/phpdesktop + +#include "utils.h" +#include + + +std::string executable_dir() { + // Directory in which executable resides. + char app_path[1024] = {}; + ssize_t pplen = readlink("/proc/self/exe", app_path, sizeof(app_path)-1); + if (pplen != -1) { + app_path[pplen] = '\0'; + } + do { + pplen -= 1; + app_path[pplen+1] = '\0'; + } while (app_path[pplen] != '/' && pplen > 0); + // No slash at the end. + if (pplen > 0 && app_path[pplen] == '/') { + app_path[pplen] = '\0'; + } + return app_path; +} diff --git a/src/utils.h b/src/utils.h new file mode 100644 index 0000000..a7a349c --- /dev/null +++ b/src/utils.h @@ -0,0 +1,7 @@ +// Copyright (c) 2018 PHP Desktop, see the Authors file. +// All rights reserved. Licensed under BSD 3-clause license. +// Project website: https://github.com/cztomczak/phpdesktop + +#include + +std::string executable_dir(); diff --git a/src/version.h b/src/version.h new file mode 100644 index 0000000..024f0f5 --- /dev/null +++ b/src/version.h @@ -0,0 +1 @@ +#define PHPDESKTOP_VERSION "70.0" diff --git a/src/www/index.php b/src/www/index.php new file mode 100644 index 0000000..6480abf --- /dev/null +++ b/src/www/index.php @@ -0,0 +1,5 @@ + \ No newline at end of file