Exemple #1
0
void HttpProtocol::PrepareRequestVariables(Array& request,
        Array& get,
        Array& post,
        Variant& raw_post,
        Array& files,
        Array& cookie,
        Transport *transport,
        const RequestURI &r) {

    // $_GET and $_REQUEST
    if (!r.queryString().empty()) {
        PrepareGetVariable(get, r);
        CopyParams(request, get);
    }

    // $_POST and $_REQUEST
    if (transport->getMethod() == Transport::Method::POST) {
        PreparePostVariables(post, raw_post, files, transport);
        CopyParams(request, post);
    }

    // $_COOKIE
    if (PrepareCookieVariable(cookie, transport)) {
        CopyParams(request, cookie);
    }
}
/**
 * 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
}
Exemple #3
0
static void CopyPathInfo(Array& server,
                         Transport *transport,
                         const RequestURI& r,
                         const VirtualHost *vhost) {
    server.set(s_REQUEST_URI, String(transport->getUrl(), CopyString));
    server.set(s_SCRIPT_URL, r.originalURL());
    String prefix(transport->isSSL() ? "https://" : "http://");

    // Need to append port
    assert(server.exists(s_SERVER_PORT));
    std::string serverPort = "80";
    if (server.exists(s_SERVER_PORT)) {
        Variant port = server[s_SERVER_PORT];
        always_assert(port.isInteger() || port.isString());
        if (port.isInteger()) {
            serverPort = folly::to<std::string>(port.toInt32());
        } else {
            serverPort = port.toString().data();
        }
    }

    String port_suffix("");
    if (!transport->isSSL() && serverPort != "80") {
        port_suffix = folly::format(":{}", serverPort).str();
    }

    string hostHeader;
    if (server.exists(s_HTTP_HOST)) {
        hostHeader = server[s_HTTP_HOST].toCStrRef().data();
    }
    String hostName;
    if (server.exists(s_SERVER_NAME)) {
        assert(server[s_SERVER_NAME].isString());
        hostName = server[s_SERVER_NAME].toCStrRef();
    }
    server.set(s_SCRIPT_URI,
               String(prefix + (hostHeader.empty() ? hostName + port_suffix :
                                String(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(s_SCRIPT_NAME, name);
    } else {
        server.set(s_SCRIPT_NAME, r.resolvedURL());
    }

    if (r.rewritten()) {
        server.set(s_PHP_SELF, r.originalURL());
    } else {
        server.set(s_PHP_SELF, r.resolvedURL() + r.origPathInfo());
    }

    String documentRoot = transport->getDocumentRoot();
    if (documentRoot.empty()) {
        // Right now this is just RuntimeOption::SourceRoot but mwilliams wants to
        // fix it so it is settable, so I'll leave this for now
        documentRoot = vhost->getDocumentRoot();
    }
    server.set(s_DOCUMENT_ROOT, documentRoot);
    server.set(s_SCRIPT_FILENAME, r.absolutePath());

    if (r.pathInfo().empty()) {
        server.set(s_PATH_TRANSLATED, r.absolutePath());
    } else {
        assert(server.exists(s_DOCUMENT_ROOT));
        assert(server[s_DOCUMENT_ROOT].isString());
        // reset path_translated back to the transport if it has it.
        auto const& pathTranslated = transport->getPathTranslated();
        if (!pathTranslated.empty()) {
            if (documentRoot == s_forwardslash) {
                // path outside document root or / is document root
                server.set(s_PATH_TRANSLATED, String(pathTranslated));
            } else {
                server.set(s_PATH_TRANSLATED,
                           String(server[s_DOCUMENT_ROOT].toCStrRef() +
                                  pathTranslated));
            }
        } else {
            server.set(s_PATH_TRANSLATED,
                       String(server[s_DOCUMENT_ROOT].toCStrRef() +
                              server[s_SCRIPT_NAME].toCStrRef() +
                              r.pathInfo().data()));
        }
        server.set(s_PATH_INFO, r.pathInfo());
    }

    switch (transport->getMethod()) {
    case Transport::Method::GET:
        server.set(s_REQUEST_METHOD, s_GET);
        break;
    case Transport::Method::HEAD:
        server.set(s_REQUEST_METHOD, s_HEAD);
        break;
    case Transport::Method::POST:
        if (transport->getExtendedMethod() == nullptr) {
            server.set(s_REQUEST_METHOD, s_POST);
        } else {
            server.set(s_REQUEST_METHOD, transport->getExtendedMethod());
        }
        break;
    default:
        server.set(s_REQUEST_METHOD, empty_string);
        break;
    }
    server.set(s_HTTPS, transport->isSSL() ? s_on : empty_string);
    server.set(s_QUERY_STRING, r.queryString());

    server.set(s_argv, make_packed_array(r.queryString()));
    server.set(s_argc, 1);
}
Exemple #4
0
void HttpProtocol::PrepareGetVariable(Array& get,
                                      const RequestURI &r) {
    DecodeParameters(get,
                     r.queryString().data(),
                     r.queryString().size());
}
Exemple #5
0
/**
 * 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) {

  auto const vhost = VirtualHost::GetCurrent();
  auto const g = get_global_variables()->asArrayData();
  Variant emptyArr(staticEmptyArray());
  for (auto& key : s_arraysToClear) {
    g->remove(key.get(), false);
    g->set(key.get(), emptyArr, false);
  }
  for (auto& key : s_arraysToUnset) {
    g->remove(key.get(), false);
  }

  // according to doc if content type is multipart/form-data
  // $HTTP_RAW_POST_DATA should always not available
  bool shouldSetHttpRawPostData = false;
  if (RuntimeOption::AlwaysPopulateRawPostData) {
    std::string dummy;
    if (!IsRfc1867(transport->getHeader("Content-Type"), dummy)) {
      shouldSetHttpRawPostData = true;
    }
  }

  if (shouldSetHttpRawPostData) {
    g->set(s_HTTP_RAW_POST_DATA, empty_string_variant_ref, false);
  }

#define X(name)                                       \
  Array name##arr(Array::Create());                   \
  SCOPE_EXIT { g->set(s__##name, name##arr, false); };

  X(ENV)
  X(GET)
  X(POST)
  X(COOKIE)
  X(FILES)
  X(SERVER)
  X(REQUEST)

#undef X

  Variant HTTP_RAW_POST_DATA;
  SCOPE_EXIT {
    if (shouldSetHttpRawPostData) {
      g->set(s_HTTP_RAW_POST_DATA.get(), HTTP_RAW_POST_DATA, false);
    }
  };

  auto variablesOrder = ThreadInfo::s_threadInfo.getNoCheck()
    ->m_reqInjectionData.getVariablesOrder();

  auto requestOrder = ThreadInfo::s_threadInfo.getNoCheck()
    ->m_reqInjectionData.getRequestOrder();

  if (requestOrder.empty()) {
    requestOrder = variablesOrder;
  }

  bool postPopulated = false;

  for (char& c : variablesOrder) {
    switch(c) {
      case 'e':
      case 'E':
        PrepareEnv(ENVarr, transport);
        break;
      case 'g':
      case 'G':
        if (!r.queryString().empty()) {
          PrepareGetVariable(GETarr, r);
        }
        break;
      case 'p':
      case 'P':
        postPopulated = true;
        PreparePostVariables(POSTarr, HTTP_RAW_POST_DATA,
                             FILESarr, transport);
        break;
      case 'c':
      case 'C':
        PrepareCookieVariable(COOKIEarr, transport);
        break;
      case 's':
      case 'S':
        StartRequest(SERVERarr);
        PrepareServerVariable(SERVERarr,
            transport,
            r,
            sri,
            vhost);
        break;
    }
  }

  if (!postPopulated && shouldSetHttpRawPostData) {
    // Always try to populate $HTTP_RAW_POST_DATA if not populated
    Array dummyPost(Array::Create());
    Array dummyFiles(Array::Create());
    PreparePostVariables(dummyPost, HTTP_RAW_POST_DATA,
                         dummyFiles, transport);
  }

  PrepareRequestVariables(REQUESTarr,
                          GETarr,
                          POSTarr,
                          COOKIEarr,
                          requestOrder);
}