Variant f_constant(CStrRef name) { if (!name.get()) return null; const char *data = name.data(); int len = name.length(); char *colon; if ((colon = (char*)memchr(data, ':', len)) && colon[1] == ':') { // class constant int classNameLen = colon - data; char *constantName = colon + 2; String className(data, classNameLen, CopyString); // translate "self" or "parent" if (className == "self") { String this_class = hhvm ? g_vmContext->getContextClassName() : FrameInjection::GetClassName(true); if (this_class.empty()) { throw FatalErrorException("Cannot access self:: " "when no class scope is active"); } else { className = this_class; } } else if (className == "parent") { String parent_class = hhvm ? g_vmContext->getParentContextClassName() : FrameInjection::GetParentClassName(true); if (parent_class.empty()) { throw FatalErrorException("Cannot access parent"); } else { className = parent_class; } } // taking care of volatile class if (class_exists(className)) { return get_class_constant(className, constantName, false); } else { return null; } } else { if (hhvm) { TypedValue* cns = g_vmContext->getCns(name.get()); if (cns == NULL) { if (AutoloadHandler::s_instance->autoloadConstant(name)) { cns = g_vmContext->getCns(name.get()); } } if (cns) return tvAsVariant(cns); return null; } else { const ClassInfo::ConstantInfo *cinfo = ClassInfo::FindConstant(name); // system/uniquely defined scalar constant (must be valid) if (cinfo) return cinfo->getValue(); if (!((Globals*)get_global_variables())->defined(name)) { AutoloadHandler::s_instance->autoloadConstant(name); } // dynamic/redeclared constant return ((Globals*)get_global_variables())->getConstant(name.data()); } } }
void XDebugProfiler::writeProfilingResults() { // If we're appending to the file, start a new section if (m_profilingOpts & k_XDEBUG_PROFILE_APPEND) { fprintf(m_profilingFile, "\n==== NEW PROFILING FILE =======================" "=======================\n"); } // Grab $_SERVER['SCRIPT_NAME'] so we can match xdebug %s filename format // option Array server = get_global_variables()->asArrayData()->get(s_SERVER).toArray(); const char* scriptname = server[s_SCRIPT_NAME].toString().data(); // Print the header and body fprintf(m_profilingFile, "version: 1\ncreator: xdebug %s\n", XDEBUG_VERSION); fprintf(m_profilingFile, "cmd: %s\npart: 1\npositions: line\n\n", scriptname); fprintf(m_profilingFile, "events: Time\n\n"); if (m_frameBufferSize > 0 && writeProfilingFrame(0) < 0) { fprintf(stderr, "Error when writing xdebug profiling file %s. Frame buffer " "invalid.", m_profilingFilename.data()); } // Cleanup m_profilingEnabled = false; fclose(m_profilingFile); }
void DummySandbox::run() { while (!m_stopped) { try { char *argv[] = {"", NULL}; execute_command_line_begin(1, argv, 0); DECLARE_THREAD_INFO; FRAME_INJECTION_FLAGS(empty_string, _, FrameInjection::PseudoMain); DSandboxInfo sandbox = m_proxy->getSandbox(); string msg; if (m_inited) { SystemGlobals *g = (SystemGlobals *)get_global_variables(); SourceRootInfo sri(sandbox.m_user, sandbox.m_name); if (sandbox.m_path.empty()) { sandbox.m_path = sri.path(); } if (!sri.sandboxOn()) { msg = "Invalid sandbox was specified. " "PHP files may not be loaded properly.\n"; // force HPHP_SANDBOX_ID to be set, so we can still talk to client g->GV(_SERVER).set("HPHP_SANDBOX_ID", sandbox.id()); } else { sri.setServerVariables(g->GV(_SERVER)); } std::string doc = getStartupDoc(sandbox); bool error; string errorMsg; bool ret = hphp_invoke(g_context.get(), doc, false, null_array, null, "", "", "", error, errorMsg); if (!ret || error) { msg += "Unable to pre-load " + doc; if (!errorMsg.empty()) { msg += ": " + errorMsg; } } } m_inited = true; Debugger::RegisterSandbox(sandbox); Debugger::InterruptSessionStarted(NULL, msg.c_str()); // Blocking until Ctrl-C is issued by end user and DebuggerProxy cannot // find a real sandbox thread to handle it. { Lock lock(this); while (!m_stopped && m_signum != CmdSignal::SignalBreak) { wait(1); } m_signum = CmdSignal::SignalNone; } } catch (const DebuggerException &e) {} execute_command_line_end(0, false, NULL); } }
string& SourceRootInfo::initPhpRoot() { SystemGlobals *g = (SystemGlobals*)get_global_variables(); Variant &server = g->GV(_SERVER); Variant v = server.rvalAt("PHP_ROOT"); if (v.isString()) { *s_phproot.getCheck() = string(v.asCStrRef().data()) + string("/"); } else { // Our best guess at the source root. *s_phproot.getCheck() = GetCurrentSourceRoot(); } return *s_phproot.getCheck(); }
string& SourceRootInfo::initPhpRoot() { GlobalVariables *g = get_global_variables(); CVarRef server = g->get(s_SERVER); CVarRef v = server.rvalAt(s_PHP_ROOT); if (v.isString()) { *s_phproot.getCheck() = string(v.asCStrRef().data()) + string("/"); } else { // Our best guess at the source root. *s_phproot.getCheck() = GetCurrentSourceRoot(); } return *s_phproot.getCheck(); }
bool XDebugServer::isNeeded() { if (!XDEBUG_GLOBAL(RemoteEnable) || XDEBUG_GLOBAL(RemoteMode) == "jit") { return false; } else if (XDEBUG_GLOBAL(RemoteAutostart)) { return true; } else { // Check $_COOKIE[XDEBUG_SESSION] const ArrayData* globals = get_global_variables()->asArrayData(); Array cookie = globals->get(s_COOKIE).toArray(); return !cookie[s_SESSION].isNull(); } }
bool UrlFile::open(CStrRef url, CStrRef mode) { const char* modestr = mode.c_str(); if (strchr(modestr, '+') || strchr(modestr, 'a') || strchr(modestr, 'w')) { std::string msg = "cannot open a url stream for write/append operation: "; msg += url.c_str(); m_error = msg; return false; } HttpClient http(m_timeout, m_maxRedirect); m_response.reset(); HeaderMap *pHeaders = nullptr; HeaderMap requestHeaders; if (!m_headers.empty()) { pHeaders = &requestHeaders; for (ArrayIter iter(m_headers); iter; ++iter) { requestHeaders[string(iter.first().toString().data())]. push_back(iter.second().toString().data()); } } int code; vector<String> responseHeaders; if (m_get) { code = http.get(url.c_str(), m_response, pHeaders, &responseHeaders); } else { code = http.post(url.c_str(), m_postData.data(), m_postData.size(), m_response, pHeaders, &responseHeaders); } SystemGlobals *g = (SystemGlobals*)get_global_variables(); Variant &r = g->GV(http_response_header); r = Array::Create(); for (unsigned int i = 0; i < responseHeaders.size(); i++) { r.append(responseHeaders[i]); } m_responseHeaders = r; if (code == 200) { m_name = (std::string) url; m_data = const_cast<char*>(m_response.data()); m_len = m_response.size(); return true; } else { m_error = http.getLastError().c_str(); return false; } }
bool UrlFile::open(CStrRef url, CStrRef mode) { if (strchr(mode, '+') || strchr(mode, 'a') || strchr(mode, 'w')) { string msg("cannot open a url stream for write/append operation: "); msg += url.c_str(); raise_warning(msg.c_str()); return false; } HttpClient http(m_timeout, m_maxRedirect); StringBuffer response; HeaderMap *pHeaders = NULL; HeaderMap requestHeaders; if (!m_headers.empty()) { pHeaders = &requestHeaders; for (ArrayIter iter(m_headers); iter; ++iter) { requestHeaders[string(iter.first().toString().data())]. push_back(iter.second().toString().data()); } } int code; vector<String> responseHeaders; if (m_get) { code = http.get(url.c_str(), response, pHeaders, &responseHeaders); } else { code = http.post(url.c_str(), m_postData.data(), m_postData.size(), response, pHeaders, &responseHeaders); } SystemGlobals *g = (SystemGlobals*)get_global_variables(); Variant &r = g->gv_http_response_header; r = Array::Create(); for (unsigned int i = 0; i < responseHeaders.size(); i++) { r.append(responseHeaders[i]); } if (code == 200) { int len = m_len; m_name = url; m_data = response.detach(len); m_len = len; m_malloced = true; return true; } else { return false; } }
void XDebugServer::onRequestInit() { if (!XDEBUG_GLOBAL(RemoteEnable)) { return; } // Need to turn on debugging regardless of the remote mode in order to // capture exceptions/errors if (!DebugHookHandler::attach<XDebugHookHandler>()) { raise_warning("Could not attach xdebug remote debugger to the current " "thread. A debugger is already attached."); return; } // Grab $_GET, $_COOKIE, and the transport const ArrayData* globals = get_global_variables()->asArrayData(); Array get = globals->get(s_GET).toArray(); Array cookie = globals->get(s_COOKIE).toArray(); Transport* transport = g_context->getTransport(); // Need to check $_GET[XDEBUG_SESSION_STOP]. If set, delete the session // cookie const Variant sess_stop_var = get[s_SESSION_STOP]; if (!sess_stop_var.isNull()) { cookie.set(s_SESSION, init_null()); if (transport != nullptr) { transport->setCookie(s_SESSION, empty_string()); } } // Need to check $_GET[XDEBUG_SESSION_START]. If set, store the session // cookie with $_GET[XDEBUG_SESSION_START] as the value const Variant sess_start_var = get[s_SESSION_START]; if (sess_start_var.isString()) { String sess_start = sess_start_var.toString(); cookie.set(s_SESSION, sess_start); if (transport != nullptr) { int64_t expire = XDEBUG_GLOBAL(RemoteCookieExpireTime); if (expire > 0) { timespec ts; Timer::GetRealtimeTime(ts); expire += ts.tv_sec; } transport->setCookie(s_SESSION, sess_start, expire); } } }
void XDebugServer::onRequestInit() { if (!XDEBUG_GLOBAL(RemoteEnable)) { return; } // TODO(#4489053) Enable this when debugger internals have been refactored // Need to turn on debugging regardless of the remote mode in order to // capture exceptions/errors // ThreadInfo *ti = ThreadInfo::s_threadInfo.getNoCheck(); // ti->m_reqInjectionData.setDebugger(true); // Grab $_GET, $_COOKIE, and the transport const ArrayData* globals = get_global_variables()->asArrayData(); Array get = globals->get(s_GET).toArray(); Array cookie = globals->get(s_COOKIE).toArray(); Transport* transport = g_context->getTransport(); // Need to check $_GET[XDEBUG_SESSION_STOP]. If set, delete the session // cookie const Variant sess_stop_var = get[s_SESSION_STOP]; if (!sess_stop_var.isNull()) { cookie.set(s_SESSION, init_null()); if (transport != nullptr) { transport->setCookie(s_SESSION, empty_string()); } } // Need to check $_GET[XDEBUG_SESSION_START]. If set, store the session // cookie with $_GET[XDEBUG_SESSION_START] as the value const Variant sess_start_var = get[s_SESSION_START]; if (sess_start_var.isString()) { String sess_start = sess_start_var.toString(); cookie.set(s_SESSION, sess_start); if (transport != nullptr) { transport->setCookie(s_SESSION, sess_start, XDEBUG_GLOBAL(RemoteCookieExpireTime)); } } }
Variant invoke_file(CStrRef path, bool once /* = false */, LVariableTable* variables /* = NULL */, const char *currentDir /* = NULL */) { String cmd = canonicalize_path(path, "", 0); if (path == "string") { echo("Hello, world!"); return true; } if (cmd == "pageletserver") { SystemGlobals *g = (SystemGlobals*)get_global_variables(); echo("pagelet postparam: "); echo(g->GV(HTTP_RAW_POST_DATA)); echo("pagelet getparam: "); echo(g->GV(_GET)["getparam"]); echo("pagelet header: "); echo(g->GV(_SERVER)["HTTP_MYHEADER"]); f_header("ResponseHeader: okay"); sleep(1); // give status check time to happen return true; } return false; }
Array f_getopt(CStrRef options, CVarRef longopts /* = null_variant */) { opt_struct *opts, *orig_opts; int len = parse_opts(options.data(), options.size(), &opts); if (!longopts.isNull()) { Array arropts = longopts.toArray(); int count = arropts.size(); /* the first <len> slots are filled by the one short ops * we now extend our array and jump to the new added structs */ opts = (opt_struct *)realloc(opts, sizeof(opt_struct) * (len + count + 1)); orig_opts = opts; opts += len; memset(opts, 0, count * sizeof(opt_struct)); for (ArrayIter iter(arropts); iter; ++iter) { String entry = iter.second().toString(); opts->need_param = 0; opts->opt_name = strdup(entry.data()); len = strlen(opts->opt_name); if ((len > 0) && (opts->opt_name[len - 1] == ':')) { opts->need_param++; opts->opt_name[len - 1] = '\0'; if ((len > 1) && (opts->opt_name[len - 2] == ':')) { opts->need_param++; opts->opt_name[len - 2] = '\0'; } } opts->opt_char = 0; opts++; } } else { opts = (opt_struct*) realloc(opts, sizeof(opt_struct) * (len + 1)); orig_opts = opts; opts += len; } /* php_getopt want to identify the last param */ opts->opt_char = '-'; opts->need_param = 0; opts->opt_name = NULL; static const StaticString s_argv("argv"); GlobalVariables *g = get_global_variables(); Array vargv = g->get(s_argv).toArray(); int argc = vargv.size(); char **argv = (char **)malloc((argc+1) * sizeof(char*)); vector<String> holders; int index = 0; for (ArrayIter iter(vargv); iter; ++iter) { String arg = iter.second().toString(); holders.push_back(arg); argv[index++] = (char*)arg.data(); } argv[index] = NULL; Array ret = Array::Create(); /* after our pointer arithmetic jump back to the first element */ opts = orig_opts; int o; char *php_optarg = NULL; int php_optind = 1; Variant val; int optchr = 0; int dash = 0; /* have already seen the - */ char opt[2] = { '\0' }; char *optname; int optname_len = 0; int php_optidx; while ((o = php_getopt(argc, argv, opts, &php_optarg, &php_optind, 0, 1, optchr, dash, php_optidx)) != -1) { /* Skip unknown arguments. */ if (o == '?') { continue; } /* Prepare the option character and the argument string. */ if (o == 0) { optname = opts[php_optidx].opt_name; } else { if (o == 1) { o = '-'; } opt[0] = o; optname = opt; } if (php_optarg != NULL) { /* keep the arg as binary, since the encoding is not known */ val = String(php_optarg, CopyString); } else { val = false; } /* Add this option / argument pair to the result hash. */ optname_len = strlen(optname); if (!(optname_len > 1 && optname[0] == '0') && is_numeric_string(optname, optname_len, NULL, NULL, 0) == KindOfInt64) { /* numeric string */ int optname_int = atoi(optname); if (ret.exists(optname_int)) { Variant &e = ret.lvalAt(optname_int); if (!e.isArray()) { ret.set(optname_int, CREATE_VECTOR2(e, val)); } else { e.append(val); } } else { ret.set(optname_int, val); } } else { /* other strings */ String key(optname, strlen(optname), CopyString); if (ret.exists(key)) { Variant &e = ret.lvalAt(key); if (!e.isArray()) { ret.set(key, CREATE_VECTOR2(e, val)); } else { e.append(val); } } else { ret.set(key, val); } } php_optarg = NULL; } free_longopts(orig_opts); free(orig_opts); free(argv); return ret; }
void DummySandbox::run() { TRACE(2, "DummySandbox::run\n"); ThreadInfo *ti = ThreadInfo::s_threadInfo.getNoCheck(); Debugger::RegisterThread(); while (!m_stopped) { try { CLISession hphpSession; DSandboxInfo sandbox = m_proxy->getSandbox(); string msg; if (sandbox.valid()) { GlobalVariables *g = get_global_variables(); SourceRootInfo sri(sandbox.m_user, sandbox.m_name); if (sandbox.m_path.empty()) { sandbox.m_path = sri.path(); } if (!sri.sandboxOn()) { msg = "Invalid sandbox was specified. " "PHP files may not be loaded properly.\n"; } else { sri.setServerVariables(g->getRef(s__SERVER)); } Debugger::RegisterSandbox(sandbox); g_context->setSandboxId(sandbox.id()); std::string doc = getStartupDoc(sandbox); if (!doc.empty()) { char cwd[PATH_MAX]; getcwd(cwd, sizeof(cwd)); Logger::Info("Start loading startup doc '%s', pwd = '%s'", doc.c_str(), cwd); bool error; string errorMsg; bool ret = hphp_invoke(g_context.getNoCheck(), doc, false, null_array, uninit_null(), "", "", error, errorMsg, true, false, true); if (!ret || error) { msg += "Unable to pre-load " + doc; if (!errorMsg.empty()) { msg += ": " + errorMsg; } } Logger::Info("Startup doc " + doc + " loaded"); } } else { g_context->setSandboxId(m_proxy->getDummyInfo().id()); } ti->m_reqInjectionData.setDebugger(true); { DebuggerDummyEnv dde; // This is really the entire point of having the dummy sandbox. This // fires the initial session started interrupt to the client after // it first attaches. Debugger::InterruptSessionStarted(nullptr, msg.c_str()); } // Blocking until Ctrl-C is issued by end user and DebuggerProxy cannot // find a real sandbox thread to handle it. { Lock lock(this); while (!m_stopped && m_signum != CmdSignal::SignalBreak) { wait(1); } if (m_stopped) { // stopped by worker thread break; } m_signum = CmdSignal::SignalNone; } } catch (const DebuggerClientExitException &e) { // stopped by the dummy sandbox thread itself break; } catch (const DebuggerException &e) { } } }
// Helper used to create an absolute filename using the passed // directory and xdebug-specific format string static String format_filename(folly::StringPiece dir, folly::StringPiece formatFile, bool addSuffix) { // Create a string buffer and append the directory name auto const formatlen = formatFile.size(); StringBuffer buf(formatlen * 2); // Slightly larger than formatlen if (!dir.empty()) { buf.append(dir); buf.append('/'); } // Append the filename auto globals = get_global_variables()->asArrayData(); for (int pos = 0; pos < formatlen; pos++) { auto c = formatFile[pos]; if (c != '%' || pos + 1 == formatlen) { buf.append(c); continue; } c = formatFile[++pos]; switch (c) { // crc32 of current working directory case 'c': { auto const crc32 = HHVM_FN(crc32)(g_context->getCwd()); buf.append(crc32); break; } // process id case 'p': buf.append(getpid()); break; // Random number case 'r': buf.printf("%lx", (long)HHVM_FN(rand)()); break; // Script name case 's': { auto server = globals->get(s_SERVER).toArray(); if (server.exists(s_SCRIPT_NAME) && server[s_SCRIPT_NAME].isString()) { const String scriptname(server[s_SCRIPT_NAME].toString(), CopyString); replace_special_chars(scriptname.get()); buf.append(scriptname); } break; } // Timestamp (seconds) case 't': { auto const sec = (int64_t)time(nullptr); if (sec != -1) { buf.append(sec); } break; } // Timestamp (microseconds) case 'u': { struct timeval tv; if (gettimeofday(&tv, 0) != -1) { buf.printf("%ld_%ld", long(tv.tv_sec), long(tv.tv_usec)); } break; } // $_SERVER['HTTP_HOST'] case 'H': { Array server = globals->get(s_SERVER).toArray(); if (server.exists(s_HTTP_HOST) && server[s_HTTP_HOST].isString()) { const String hostname(server[s_HTTP_HOST].toString(), CopyString); replace_special_chars(hostname.get()); buf.append(hostname); } break; } // $_SERVER['REQUEST_URI'] case 'R': { auto server = globals->get(s_SERVER).toArray(); if (globals->exists(s_REQUEST_URI)) { const String requri(server[s_REQUEST_URI].toString(), CopyString); replace_special_chars(requri.get()); buf.append(requri); } break; } // $_SERVER['UNIQUE_ID'] case 'U': { auto server = globals->get(s_SERVER).toArray(); if (server.exists(s_UNIQUE_ID) && server[s_UNIQUE_ID].isString()) { const String uniqueid(server[s_UNIQUE_ID].toString(), CopyString); replace_special_chars(uniqueid.get()); buf.append(uniqueid); } break; } // session id case 'S': { // First we grab the session name from the ini settings, then the id // from the cookies String session_name; if (IniSetting::Get(s_SESSION_NAME, session_name)) { auto cookies = globals->get(s_COOKIE).toArray(); if (cookies.exists(session_name) && cookies[session_name].isString()) { const String sessionstr(cookies[session_name].toString(), CopyString); replace_special_chars(sessionstr.get()); buf.append(sessionstr); } break; } } // Literal case '%': buf.append('%'); break; default: buf.append('%'); buf.append(c); break; } } // Optionally add .xt file extension if (addSuffix) { buf.append(".xt"); } return buf.copy(); }
void init_global_variables() { ThreadInfo::s_threadInfo->m_globals = get_global_variables(); GlobalVariables::initialize(); }
void DummySandbox::run() { ThreadInfo *ti = ThreadInfo::s_threadInfo.getNoCheck(); Debugger::RegisterThread(); ti->m_reqInjectionData.dummySandbox = true; while (!m_stopped) { try { CLISession hphpSession; FUNCTION_INJECTION_FS("_", FrameInjection::PseudoMain); DSandboxInfo sandbox = m_proxy->getSandbox(); string msg; if (sandbox.valid()) { SystemGlobals *g = (SystemGlobals *)get_global_variables(); SourceRootInfo sri(sandbox.m_user, sandbox.m_name); if (sandbox.m_path.empty()) { sandbox.m_path = sri.path(); } if (!sri.sandboxOn()) { msg = "Invalid sandbox was specified. " "PHP files may not be loaded properly.\n"; } else { sri.setServerVariables(g->GV(_SERVER)); } Debugger::RegisterSandbox(sandbox); g_context->setSandboxId(sandbox.id()); Logger::Info("Start loading startup doc"); std::string doc = getStartupDoc(sandbox); bool error; string errorMsg; bool ret = hphp_invoke(g_context.getNoCheck(), doc, false, null_array, null, "", "", error, errorMsg, true, false, true); if (!ret || error) { msg += "Unable to pre-load " + doc; if (!errorMsg.empty()) { msg += ": " + errorMsg; } } Logger::Info("Startup doc " + doc + " loaded"); } else { g_context->setSandboxId(m_proxy->getDummyInfo().id()); } ti->m_reqInjectionData.debugger = true; { DebuggerDummyEnv dde; Debugger::InterruptSessionStarted(NULL, msg.c_str()); } // Blocking until Ctrl-C is issued by end user and DebuggerProxy cannot // find a real sandbox thread to handle it. { Lock lock(this); while (!m_stopped && m_signum != CmdSignal::SignalBreak) { wait(1); } if (m_stopped) { // stopped by worker thread break; } m_signum = CmdSignal::SignalNone; } } catch (const DebuggerClientExitException &e) { // stopped by the dummy sandbox thread itself break; } catch (const DebuggerException &e) { } } }
Globals* get_globals() { return get_global_variables(); }
bool XDebugServer::initDbgp() { // Initialize the status and reason switch (m_mode) { case Mode::REQ: setStatus(Status::Starting, Reason::Ok); break; case Mode::JIT: setStatus(Status::Break, Reason::Error); break; } // Create the response auto response = xdebug_xml_node_init("init"); addXmlns(*response); // Add the engine info auto child = xdebug_xml_node_init("engine"); xdebug_xml_add_attribute(child, "version", XDEBUG_VERSION); xdebug_xml_add_text(child, XDEBUG_NAME, 0); xdebug_xml_add_child(response, child); // Add the author child = xdebug_xml_node_init("author"); xdebug_xml_add_text(child, XDEBUG_AUTHOR, 0); xdebug_xml_add_child(response, child); // Add the url child = xdebug_xml_node_init("url"); xdebug_xml_add_text(child, XDEBUG_URL, 0); xdebug_xml_add_child(response, child); // Add the copyright child = xdebug_xml_node_init("copyright"); xdebug_xml_add_text(child, XDEBUG_COPYRIGHT, 0); xdebug_xml_add_child(response, child); // Grab the absolute path of the script filename auto globals = get_global_variables()->asArrayData(); Variant scriptname_var = globals->get(s_SERVER).toArray()[s_SCRIPT_FILENAME]; assert(scriptname_var.isString()); auto scriptname = scriptname_var.toString().get()->mutableData(); auto fileuri = XDebugUtils::pathToUrl(scriptname); // Add attributes to the root init node xdebug_xml_add_attribute_ex(response, "fileuri", fileuri, 0, 1); xdebug_xml_add_attribute(response, "language", "PHP"); xdebug_xml_add_attribute(response, "protocol_version", DBGP_VERSION); xdebug_xml_add_attribute(response, "appid", getpid()); // Add the DBGP_COOKIE environment variable const String dbgp_cookie = g_context->getenv(s_DBGP_COOKIE); if (!dbgp_cookie.empty()) { xdebug_xml_add_attribute(response, "session", dbgp_cookie.data()); } // Add the idekey if (XDEBUG_GLOBAL(IdeKey).size() > 0) { xdebug_xml_add_attribute(response, "idekey", XDEBUG_GLOBAL(IdeKey).c_str()); } // Sent the response sendMessage(*response); xdebug_xml_node_dtor(response); // Wait for a response from the client return doCommandLoop(); }
void init_global_variables() { GlobalVariables *g = get_global_variables(); ThreadInfo::s_threadInfo->m_globals = g; g->initialize(); }
XDebugServer::XDebugServer(Mode mode) : m_mode(mode) { // Attempt to open optional log file if (XDEBUG_GLOBAL(RemoteLog).size() > 0) { m_logFile = fopen(XDEBUG_GLOBAL(RemoteLog).c_str(), "a"); if (m_logFile == nullptr) { raise_warning("XDebug could not open the remote debug file '%s'.", XDEBUG_GLOBAL(RemoteLog).c_str()); } log("Log opened at"); XDebugUtils::fprintTimestamp(m_logFile); log("\n"); logFlush(); } // Grab the hostname and port to connect to const char* hostname = XDEBUG_GLOBAL(RemoteHost).c_str(); int port = XDEBUG_GLOBAL(RemotePort); if (XDEBUG_GLOBAL(RemoteConnectBack)) { const ArrayData* globals = get_global_variables()->asArrayData(); Array server = globals->get(s_SERVER).toArray(); log("I: Checking remote connect back address.\n"); // Grab $_SERVER[HTTP_X_FORWARDED_FOR] then $_SERVER[REMOTE_ADDR] bool host_found = true; if (server[s_HTTP_X_FORWARDED_FOR].isString()) { hostname = server[s_HTTP_X_FORWARDED_FOR].toString().data(); } else if (server[s_REMOTE_ADDR].isString()) { hostname = server[s_REMOTE_ADDR].toString().data(); } else { host_found = false; } // Did we find a host? if (host_found) { log("I: Remote address found, connecting to %s:%d.\n", hostname, port); } else { log("W: Remote address not found, connecting to configured address/port: " "%s:%d. :-|\n", hostname, port); } } else { log("I: Connecting to configured address/port: %s:%d.\n", hostname, port); } // Create the socket m_socket = createSocket(hostname, port); if (m_socket == -1) { log("E: Could not connect to client. :-(\n"); goto failure; } else if (m_socket == -2) { log("E: Time-out connecting to client. :-(\n"); goto failure; } // Get the requested handler log("I: Connected to client. :-)\n"); if (XDEBUG_GLOBAL(RemoteHandler) != "dbgp") { log("E: The remote debug handler '%s' is not supported. :-(\n", XDEBUG_GLOBAL(RemoteHandler).c_str()); goto failure; } // php5 xdebug has an error case here, but initializing the dbgp handler never // actually fails initDbgp(); return; // Failure cleanup. A goto is used to prevent duplication failure: destroySocket(); closeLog(); // Allows the guarantee that any instance of an xdebug server is valid throw Exception("XDebug Server construction failed"); }
void XDebugServer::initDbgp() { // Initialize the status and reason switch (m_mode) { case Mode::REQ: setStatus(Status::STARTING, Reason::OK); break; case Mode::JIT: setStatus(Status::BREAK, Reason::ERROR); break; } // Create the response xdebug_xml_node* response = xdebug_xml_node_init("init"); addXmnls(*response); // Add the engine info xdebug_xml_node* child = xdebug_xml_node_init("engine"); xdebug_xml_add_attribute(child, "version", XDEBUG_VERSION); xdebug_xml_add_text(child, XDEBUG_NAME, 0); xdebug_xml_add_child(response, child); // Add the author child = xdebug_xml_node_init("author"); xdebug_xml_add_text(child, XDEBUG_AUTHOR, 0); xdebug_xml_add_child(response, child); // Add the url child = xdebug_xml_node_init("url"); xdebug_xml_add_text(child, XDEBUG_URL, 0); xdebug_xml_add_child(response, child); // Add the copyright child = xdebug_xml_node_init("copyright"); xdebug_xml_add_text(child, XDEBUG_COPYRIGHT, 0); xdebug_xml_add_child(response, child); // Grab the absolute path of the script filename const ArrayData* globals = get_global_variables()->asArrayData(); Variant scriptname_var = globals->get(s_SERVER).toArray()[s_SCRIPT_FILENAME]; assert(scriptname_var.isString()); char* scriptname = scriptname_var.toString().get()->mutableData(); char* fileuri = XDebugUtils::pathToUrl(scriptname); // Grab the app id (pid) // TODO(#4489053) Specification mentions the parent app id as well, xdebug // doesn't include it. char* appid = xdebug_sprintf("%d", getpid()); // Add attributes to the root init node xdebug_xml_add_attribute_ex(response, "fileuri", fileuri, 0, 1); xdebug_xml_add_attribute_ex(response, "language", "PHP", 0, 0); xdebug_xml_add_attribute_ex(response, "protocol_version", DBGP_VERSION, 0, 0); xdebug_xml_add_attribute_ex(response, "appid", appid, 0, 1); // Add the DBGP_COOKIE environment variable char* dbgp_cookie = getenv("DBGP_COOKIE"); if (dbgp_cookie != nullptr) { xdebug_xml_add_attribute_ex(response, "session", dbgp_cookie, 0, 0); } // Add the idekey if (XDEBUG_GLOBAL(IdeKey).size() > 0) { // TODO(#4489053) Change this when xml api is changed char* idekey = const_cast<char*>(XDEBUG_GLOBAL(IdeKey).c_str()); xdebug_xml_add_attribute_ex(response, "idekey", idekey, 0, 0); } // Sent the response sendMessage(*response); xdebug_xml_node_dtor(response); // Wait for a response from the client doCommandLoop(); }
SystemGlobals* get_system_globals() { return get_global_variables(); }
bool f_defined(CStrRef name, bool autoload /* = true */) { if (!name.get()) return false; const char *data = name.data(); int len = name.length(); char *colon; if ((colon = (char*)memchr(data, ':', len)) && colon[1] == ':') { // class constant int classNameLen = colon - data; char *constantName = colon + 2; String className(data, classNameLen, CopyString); // translate "self" or "parent" if (className == "self") { String this_class = hhvm ? g_vmContext->getContextClassName() : FrameInjection::GetClassName(true); if (this_class.empty()) { throw FatalErrorException("Cannot access self:: " "when no class scope is active"); } else { className = this_class; } } else if (className == "parent") { String parent_class = hhvm ? g_vmContext->getParentContextClassName() : FrameInjection::GetParentClassName(true); if (parent_class.empty()) { throw FatalErrorException("Cannot access parent"); } else { className = parent_class; } } if (class_exists(className)) { // taking care of volatile class const ClassInfo *info; for (String parentClass = className; !parentClass.empty(); parentClass = info->getParentClass()) { info = ClassInfo::FindClass(parentClass); if (!info) { assert(false); } if (info->hasConstant(constantName)) return true; } return false; } else { return false; } } else { // system/uniquely defined scalar constant if (ClassInfo::FindConstant(name)) return true; if (hhvm ? g_vmContext->defined(name) : ((Globals*)get_global_variables())->defined(name)) { return true; } if (!autoload || !AutoloadHandler::s_instance->autoloadConstant(name)) { return false; } if (hhvm) return g_vmContext->defined(name); if (ClassInfo::FindConstant(name)) return true; return ((Globals*)get_global_variables())->defined(name); } }
DebuggerProxyPtr Debugger::GetProxy() { SystemGlobals *g = (SystemGlobals*)get_global_variables(); String id = g->GV(_SERVER)["HPHP_SANDBOX_ID"]; return s_debugger.findProxy(id.data()); }
Array php_globals_as_array() { return Array(get_global_variables()->asArrayData()); }
/** * PHP has "EGPCS" processing order of these global variables, and this * order is important in preparing $_REQUEST that needs to know which to * overwrite what when name happens to be the same. */ void HttpProtocol::PrepareSystemVariables(Transport *transport, const RequestURI &r, const SourceRootInfo &sri) { SystemGlobals *g = (SystemGlobals*)get_global_variables(); const VirtualHost *vhost = VirtualHost::GetCurrent(); // reset global symbols to nulls or empty arrays pm_php$globals$symbols_php(); Variant &server = g->GV(_SERVER); server.set("REQUEST_START_TIME", time(NULL)); // $_ENV process_env_variables(g->GV(_ENV)); g->GV(_ENV).set("HPHP", 1); g->GV(_ENV).set("HPHP_SERVER", 1); #ifdef HOTPROFILER g->GV(_ENV).set("HPHP_HOTPROFILER", 1); #endif Variant &request = g->GV(_REQUEST); // $_GET and $_REQUEST if (!r.queryString().empty()) { DecodeParameters(g->GV(_GET), r.queryString().data(), r.queryString().size()); CopyParams(request, g->GV(_GET)); } string contentType = transport->getHeader("Content-Type"); string contentLength = transport->getHeader("Content-Length"); // $_POST and $_REQUEST if (transport->getMethod() == Transport::POST) { bool needDelete = false; int size = 0; const void *data = transport->getPostData(size); if (data && size) { ASSERT(((char*)data)[size] == 0); // we need a NULL terminated string string boundary; int content_length = atoi(contentLength.c_str()); bool rfc1867Post = IsRfc1867(contentType, boundary); string files; if (rfc1867Post) { if (content_length > VirtualHost::GetMaxPostSize()) { // $_POST and $_FILES are empty Logger::Warning("POST Content-Length of %d bytes exceeds " "the limit of %lld bytes", content_length, VirtualHost::GetMaxPostSize()); needDelete = read_all_post_data(transport, data, size); } else { if (transport->hasMorePostData()) { needDelete = true; data = Util::buffer_duplicate(data, size); } DecodeRfc1867(transport, g->GV(_POST), g->GV(_FILES), content_length, data, size, boundary); } ASSERT(!transport->getFiles(files)); } else { needDelete = read_all_post_data(transport, data, size); bool decodeData = strncasecmp(contentType.c_str(), DEFAULT_POST_CONTENT_TYPE, sizeof(DEFAULT_POST_CONTENT_TYPE)-1) == 0; // Always decode data for now. (macvicar) decodeData = true; if (decodeData) { DecodeParameters(g->GV(_POST), (const char*)data, size, true); } bool ret = transport->getFiles(files); if (ret) { g->GV(_FILES) = f_unserialize(files); } } CopyParams(request, g->GV(_POST)); if (needDelete) { if (RuntimeOption::AlwaysPopulateRawPostData) { g->GV(HTTP_RAW_POST_DATA) = String((char*)data, size, AttachString); } else { free((void *)data); } } else { // For literal we disregard RuntimeOption::AlwaysPopulateRawPostData g->GV(HTTP_RAW_POST_DATA) = String((char*)data, size, AttachLiteral); } } } // $_COOKIE string cookie_data = transport->getHeader("Cookie"); if (!cookie_data.empty()) { StringBuffer sb; sb.append(cookie_data); DecodeCookies(g->GV(_COOKIE), (char*)sb.data()); CopyParams(request, g->GV(_COOKIE)); } // $_SERVER // HTTP_ headers -- we don't exclude headers we handle elsewhere (e.g., // Content-Type, Authorization), since the CGI "spec" merely says the server // "may" exclude them; this is not what APE does, but it's harmless. HeaderMap headers; transport->getHeaders(headers); for (HeaderMap::const_iterator iter = headers.begin(); iter != headers.end(); ++iter) { const vector<string> &values = iter->second; for (unsigned int i = 0; i < values.size(); i++) { String key = "HTTP_"; key += StringUtil::ToUpper(iter->first).replace("-", "_"); server.set(key, String(values[i])); } } string host = transport->getHeader("Host"); String hostName(VirtualHost::GetCurrent()->serverName(host)); string hostHeader(host); if (hostHeader.empty()) { server.set("HTTP_HOST", hostName); StackTraceNoHeap::AddExtraLogging("Server", hostName.data()); } else { StackTraceNoHeap::AddExtraLogging("Server", hostHeader.c_str()); } if (hostName.empty() || RuntimeOption::ForceServerNameToHeader) { hostName = hostHeader; // _SERVER['SERVER_NAME'] shouldn't contain the port number int colonPos = hostName.find(':'); if (colonPos != String::npos) { hostName = hostName.substr(0, colonPos); } } // APE sets CONTENT_TYPE and CONTENT_LENGTH without HTTP_ if (!contentType.empty()) { server.set("CONTENT_TYPE", String(contentType)); } if (!contentLength.empty()) { server.set("CONTENT_LENGTH", String(contentLength)); } // APE processes Authorization: Basic into PHP_AUTH_USER and PHP_AUTH_PW string authorization = transport->getHeader("Authorization"); if (!authorization.empty()) { if (strncmp(authorization.c_str(), "Basic ", 6) == 0) { // it's safe to pass this as a string literal since authorization // outlives decodedAuth; this saves us a superfluous copy. String decodedAuth = StringUtil::Base64Decode(String(authorization.c_str() + 6)); int colonPos = decodedAuth.find(':'); if (colonPos != String::npos) { server.set("PHP_AUTH_USER", decodedAuth.substr(0, colonPos)); server.set("PHP_AUTH_PW", decodedAuth.substr(colonPos + 1)); } } } server.set("REQUEST_URI", String(transport->getUrl(), CopyString)); server.set("SCRIPT_URL", r.originalURL()); String prefix(transport->isSSL() ? "https://" : "http://"); String port_suffix(""); // Need to append port if (!transport->isSSL() && RuntimeOption::ServerPort != 80) { port_suffix = ":" + RuntimeOption::ServerPort; } server.set("SCRIPT_URI", String(prefix + (hostHeader.empty() ? hostName + port_suffix : hostHeader) + r.originalURL())); if (r.rewritten()) { // when URL is rewritten, PHP decided to put original URL as SCRIPT_NAME String name = r.originalURL(); if (!r.pathInfo().empty()) { int pos = name.find(r.pathInfo()); if (pos >= 0) { name = name.substr(0, pos); } } if (r.defaultDoc()) { if (!name.empty() && name[name.length() - 1] != '/') { name += "/"; } name += String(RuntimeOption::DefaultDocument); } server.set("SCRIPT_NAME", name); } else { server.set("SCRIPT_NAME", r.resolvedURL()); } if (!r.rewritten() && r.pathInfo().empty()) { server.set("PHP_SELF", r.resolvedURL()); } else { // when URL is rewritten, or pathinfo is not empty, use original URL server.set("PHP_SELF", r.originalURL()); } server.set("SCRIPT_FILENAME", r.absolutePath()); if (r.pathInfo().empty()) { server.set("PATH_TRANSLATED", r.absolutePath()); } else { server.set("PATH_TRANSLATED", String(vhost->getDocumentRoot() + r.pathInfo().data())); server.set("PATH_INFO", r.pathInfo()); } server.set("argv", r.queryString()); server.set("argc", 0); server.set("GATEWAY_INTERFACE", "CGI/1.1"); server.set("SERVER_ADDR", String(RuntimeOption::ServerPrimaryIP)); server.set("SERVER_NAME", hostName); server.set("SERVER_PORT", RuntimeOption::ServerPort); server.set("SERVER_SOFTWARE", "HPHP"); server.set("SERVER_PROTOCOL", "HTTP/" + transport->getHTTPVersion()); server.set("SERVER_ADMIN", ""); server.set("SERVER_SIGNATURE", ""); switch (transport->getMethod()) { case Transport::GET: server.set("REQUEST_METHOD", "GET"); break; case Transport::HEAD: server.set("REQUEST_METHOD", "HEAD"); break; case Transport::POST: if (transport->getExtendedMethod() == NULL) { server.set("REQUEST_METHOD", "POST"); } else { server.set("REQUEST_METHOD", transport->getExtendedMethod()); } break; default: server.set("REQUEST_METHOD", ""); break; } server.set("HTTPS", transport->isSSL() ? "1" : ""); server.set("REQUEST_TIME", time(NULL)); server.set("QUERY_STRING", r.queryString()); server.set("REMOTE_ADDR", String(transport->getRemoteHost(), CopyString)); server.set("REMOTE_HOST", ""); // I don't think we need to nslookup server.set("REMOTE_PORT", 0); // TODO: quite useless server.set("DOCUMENT_ROOT", String(vhost->getDocumentRoot())); for (map<string, string>::const_iterator iter = RuntimeOption::ServerVariables.begin(); iter != RuntimeOption::ServerVariables.end(); ++iter) { server.set(String(iter->first), String(iter->second)); } const map<string, string> &vServerVars = vhost->getServerVars(); for (map<string, string>::const_iterator iter = vServerVars.begin(); iter != vServerVars.end(); ++iter) { server.set(String(iter->first), String(iter->second)); } sri.setServerVariables(server); const char *threadType = transport->getThreadTypeName(); server.set("THREAD_TYPE", threadType); StackTraceNoHeap::AddExtraLogging("ThreadType", threadType); #ifdef TAINTED taint_array_variant(g->GV(_GET), "$_GET"); taint_array_variant(g->GV(_POST), "$_POST"); taint_array_variant(g->GV(_SERVER), "$_SERVER"); taint_array_variant(g->GV(_COOKIE), "$_COOKIE"); #endif }
bool RPCRequestHandler::executePHPFunction(Transport *transport, SourceRootInfo &sourceRootInfo) { // reset timeout counter ThreadInfo::s_threadInfo->m_reqInjectionData.started = time(0); string rpcFunc = transport->getCommand(); { ServerStatsHelper ssh("input"); RequestURI reqURI(rpcFunc); HttpProtocol::PrepareSystemVariables(transport, reqURI, sourceRootInfo); SystemGlobals *g = (SystemGlobals*)get_global_variables(); g->GV(_ENV).set(s_HPHP_RPC, 1); } bool isFile = rpcFunc.rfind('.') != string::npos; string rpcFile; bool error = false; Array params; string sparams = transport->getParam("params"); if (!sparams.empty()) { Variant jparams = f_json_decode(String(sparams), true); if (jparams.isArray()) { params = jparams.toArray(); } else { error = true; } } else { vector<string> sparams; transport->getArrayParam("p", sparams); if (!sparams.empty()) { for (unsigned int i = 0; i < sparams.size(); i++) { Variant jparams = f_json_decode(String(sparams[i]), true); if (same(jparams, false)) { error = true; break; } params.append(jparams); } } else { // single string parameter, used by xbox to avoid any en/decoding int size; const void *data = transport->getPostData(size); if (data && size) { params.append(String((char*)data, size, AttachLiteral)); } } } if (transport->getIntParam("reset") == 1) { m_reset = true; } int output = transport->getIntParam("output"); int code; if (!error) { Variant funcRet; string errorMsg = "Internal Server Error"; string reqInitFunc, reqInitDoc; reqInitDoc = transport->getHeader("ReqInitDoc"); if (reqInitDoc.empty() && m_serverInfo) { reqInitFunc = m_serverInfo->getReqInitFunc(); reqInitDoc = m_serverInfo->getReqInitDoc(); } if (!reqInitDoc.empty()) { reqInitDoc = (std::string)canonicalize_path(reqInitDoc, "", 0); } if (!reqInitDoc.empty()) { reqInitDoc = getSourceFilename(reqInitDoc, sourceRootInfo); } bool runOnce = false; bool ret = true; if (isFile) { rpcFile = rpcFunc; rpcFunc.clear(); } else { rpcFile = transport->getParam("include"); if (rpcFile.empty()) { rpcFile = transport->getParam("include_once"); runOnce = true; } } if (!rpcFile.empty()) { // invoking a file through rpc bool forbidden = false; if (!RuntimeOption::ForbiddenFileExtensions.empty()) { const char *ext = rpcFile.c_str() + rpcFile.rfind('.') + 1; if (RuntimeOption::ForbiddenFileExtensions.find(ext) != RuntimeOption::ForbiddenFileExtensions.end()) { forbidden = true; } } if (!forbidden) { rpcFile = (std::string) canonicalize_path(rpcFile, "", 0); rpcFile = getSourceFilename(rpcFile, sourceRootInfo); ret = hphp_invoke(m_context, rpcFile, false, Array(), uninit_null(), reqInitFunc, reqInitDoc, error, errorMsg, runOnce); } // no need to do the initialization for a second time reqInitFunc.clear(); reqInitDoc.clear(); } if (ret && !rpcFunc.empty()) { ret = hphp_invoke(m_context, rpcFunc, true, params, ref(funcRet), reqInitFunc, reqInitDoc, error, errorMsg); } if (ret) { bool serializeFailed = false; String response; switch (output) { case 0: { assert(m_returnEncodeType == Json || m_returnEncodeType == Serialize); try { response = (m_returnEncodeType == Json) ? f_json_encode(funcRet) : f_serialize(funcRet); } catch (...) { serializeFailed = true; } break; } case 1: response = m_context->obDetachContents(); break; case 2: response = f_json_encode(CREATE_MAP2(s_output, m_context->obDetachContents(), s_return, f_json_encode(funcRet))); break; case 3: response = f_serialize(funcRet); break; } if (serializeFailed) { code = 500; transport->sendString( "Serialization of the return value failed", 500); m_reset = true; } else { transport->sendRaw((void*)response.data(), response.size()); code = transport->getResponseCode(); } } else if (error) { code = 500; transport->sendString(errorMsg, 500); m_reset = true; } else { code = 404; transport->sendString("Not Found", 404); } } else { code = 400; transport->sendString("Bad Request", 400); } params.reset(); sourceRootInfo.clear(); transport->onSendEnd(); ServerStats::LogPage(isFile ? rpcFile : rpcFunc, code); m_context->onShutdownPostSend(); m_context->obClean(); // in case postsend/cleanup output something m_context->restoreSession(); return !error; }