diff options
Diffstat (limited to 'gerrit-httpd/src/main/java/com/google/gerrit/httpd/gitweb/GitwebServlet.java')
-rw-r--r-- | gerrit-httpd/src/main/java/com/google/gerrit/httpd/gitweb/GitwebServlet.java | 751 |
1 files changed, 0 insertions, 751 deletions
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/gitweb/GitwebServlet.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/gitweb/GitwebServlet.java deleted file mode 100644 index ec7c4778aa..0000000000 --- a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/gitweb/GitwebServlet.java +++ /dev/null @@ -1,751 +0,0 @@ -// Copyright (C) 2009 The Android Open Source Project -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -// CGI environment and execution management portions are: -// -// ======================================================================== -// Copyright (c) 2006-2009 Mort Bay Consulting Pty. Ltd. -// ------------------------------------------------------------------------ -// All rights reserved. This program and the accompanying materials -// are made available under the terms of the Eclipse Public License v1.0 -// and Apache License v2.0 which accompanies this distribution. -// The Eclipse Public License is available at -// http://www.eclipse.org/legal/epl-v10.html -// The Apache License v2.0 is available at -// http://www.opensource.org/licenses/apache2.0.php -// You may elect to redistribute this code under either of these licenses. -// ======================================================================== - -package com.google.gerrit.httpd.gitweb; - -import static java.nio.charset.StandardCharsets.ISO_8859_1; -import static java.nio.charset.StandardCharsets.UTF_8; - -import com.google.gerrit.common.PageLinks; -import com.google.gerrit.extensions.restapi.AuthException; -import com.google.gerrit.extensions.restapi.Url; -import com.google.gerrit.reviewdb.client.Project; -import com.google.gerrit.server.AnonymousUser; -import com.google.gerrit.server.CurrentUser; -import com.google.gerrit.server.IdentifiedUser; -import com.google.gerrit.server.config.GerritServerConfig; -import com.google.gerrit.server.config.GitwebCgiConfig; -import com.google.gerrit.server.config.GitwebConfig; -import com.google.gerrit.server.config.SitePaths; -import com.google.gerrit.server.git.GitRepositoryManager; -import com.google.gerrit.server.git.LocalDiskRepositoryManager; -import com.google.gerrit.server.permissions.PermissionBackend; -import com.google.gerrit.server.permissions.PermissionBackendException; -import com.google.gerrit.server.permissions.ProjectPermission; -import com.google.gerrit.server.project.ProjectCache; -import com.google.gerrit.server.ssh.SshInfo; -import com.google.gwtexpui.server.CacheHeaders; -import com.google.inject.Inject; -import com.google.inject.Provider; -import com.google.inject.ProvisionException; -import com.google.inject.Singleton; -import java.io.BufferedInputStream; -import java.io.BufferedReader; -import java.io.EOFException; -import java.io.File; -import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.io.OutputStream; -import java.io.PrintWriter; -import java.net.URI; -import java.net.URISyntaxException; -import java.nio.file.Files; -import java.nio.file.Path; -import java.util.Enumeration; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Map; -import java.util.Set; -import java.util.stream.Collectors; -import javax.servlet.http.HttpServlet; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; -import org.eclipse.jgit.errors.RepositoryNotFoundException; -import org.eclipse.jgit.lib.Config; -import org.eclipse.jgit.lib.Repository; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** Invokes {@code gitweb.cgi} for the project given in {@code p}. */ -@Singleton -class GitwebServlet extends HttpServlet { - private static final long serialVersionUID = 1L; - - private static final Logger log = LoggerFactory.getLogger(GitwebServlet.class); - - private static final String PROJECT_LIST_ACTION = "project_list"; - - private final Set<String> deniedActions; - private final int bufferSize = 8192; - private final Path gitwebCgi; - private final URI gitwebUrl; - private final LocalDiskRepositoryManager repoManager; - private final ProjectCache projectCache; - private final PermissionBackend permissionBackend; - private final Provider<AnonymousUser> anonymousUserProvider; - private final Provider<CurrentUser> userProvider; - private final EnvList _env; - - @Inject - GitwebServlet( - GitRepositoryManager repoManager, - ProjectCache projectCache, - PermissionBackend permissionBackend, - Provider<AnonymousUser> anonymousUserProvider, - Provider<CurrentUser> userProvider, - SitePaths site, - @GerritServerConfig Config cfg, - SshInfo sshInfo, - GitwebConfig gitwebConfig, - GitwebCgiConfig gitwebCgiConfig) - throws IOException { - if (!(repoManager instanceof LocalDiskRepositoryManager)) { - throw new ProvisionException("Gitweb can only be used with LocalDiskRepositoryManager"); - } - this.repoManager = (LocalDiskRepositoryManager) repoManager; - this.projectCache = projectCache; - this.permissionBackend = permissionBackend; - this.anonymousUserProvider = anonymousUserProvider; - this.userProvider = userProvider; - this.gitwebCgi = gitwebCgiConfig.getGitwebCgi(); - this.deniedActions = new HashSet<>(); - - final String url = gitwebConfig.getUrl(); - if ((url != null) && (!url.equals("gitweb"))) { - URI uri = null; - try { - uri = new URI(url); - } catch (URISyntaxException e) { - log.error("Invalid gitweb.url: {}", url); - } - gitwebUrl = uri; - } else { - gitwebUrl = null; - } - - deniedActions.add("forks"); - deniedActions.add("opml"); - deniedActions.add("project_index"); - - _env = new EnvList(); - makeSiteConfig(site, cfg, sshInfo); - - if (!_env.envMap.containsKey("SystemRoot")) { - String os = System.getProperty("os.name"); - if (os != null && os.toLowerCase().contains("windows")) { - String sysroot = System.getenv("SystemRoot"); - if (sysroot == null || sysroot.isEmpty()) { - sysroot = "C:\\WINDOWS"; - } - _env.set("SystemRoot", sysroot); - } - } - - if (!_env.envMap.containsKey("PATH")) { - _env.set("PATH", System.getenv("PATH")); - } - } - - private void makeSiteConfig(SitePaths site, Config cfg, SshInfo sshInfo) throws IOException { - if (!Files.exists(site.tmp_dir)) { - Files.createDirectories(site.tmp_dir); - } - Path myconf = Files.createTempFile(site.tmp_dir, "gitweb_config", ".perl"); - - // To make our configuration file only readable or writable by us; - // this reduces the chances of someone tampering with the file. - // - // TODO(dborowitz): Is there a portable way to do this with NIO? - File myconfFile = myconf.toFile(); - myconfFile.setWritable(false, false /* all */); - myconfFile.setReadable(false, false /* all */); - myconfFile.setExecutable(false, false /* all */); - - myconfFile.setWritable(true, true /* owner only */); - myconfFile.setReadable(true, true /* owner only */); - - myconfFile.deleteOnExit(); - - _env.set("GIT_DIR", "."); - _env.set("GITWEB_CONFIG", myconf.toAbsolutePath().toString()); - - try (PrintWriter p = new PrintWriter(Files.newBufferedWriter(myconf, UTF_8))) { - p.print("# Autogenerated by Gerrit Code Review \n"); - p.print("# DO NOT EDIT\n"); - p.print("\n"); - - // We are mounted at the same level in the context as the main - // UI, so we can include the same header and footer scheme. - // - Path hdr = site.site_header; - if (Files.isRegularFile(hdr)) { - p.print("$site_header = " + quoteForPerl(hdr) + ";\n"); - } - Path ftr = site.site_footer; - if (Files.isRegularFile(ftr)) { - p.print("$site_footer = " + quoteForPerl(ftr) + ";\n"); - } - - // Top level should return to Gerrit's UI. - // - p.print("$home_link = $ENV{'GERRIT_CONTEXT_PATH'};\n"); - p.print("$home_link_str = 'Code Review';\n"); - - p.print("$favicon = 'favicon.ico';\n"); - p.print("$logo = 'gitweb-logo.png';\n"); - p.print("$javascript = 'gitweb.js';\n"); - p.print("@stylesheets = ('gitweb-default.css');\n"); - Path css = site.site_css; - if (Files.isRegularFile(css)) { - p.print("push @stylesheets, 'gitweb-site.css';\n"); - } - - // Try to make the title match Gerrit's normal window title - // scheme of host followed by 'Code Review'. - // - p.print("$site_name = $home_link_str;\n"); - p.print("$site_name = qq{$1 $site_name} if "); - p.print("$ENV{'SERVER_NAME'} =~ m,^([^.]+(?:\\.[^.]+)?)(?:\\.|$),;\n"); - - // Assume by default that XSS is a problem, and try to prevent it. - // - p.print("$prevent_xss = 1;\n"); - - // Generate URLs using smart http:// - // - p.print("{\n"); - p.print(" my $secure = $ENV{'HTTPS'} =~ /^ON$/i;\n"); - p.print(" my $http_url = $secure ? 'https://' : 'http://';\n"); - p.print(" $http_url .= qq{$ENV{'GERRIT_USER_NAME'}@}\n"); - p.print(" unless $ENV{'GERRIT_ANONYMOUS_READ'};\n"); - p.print(" $http_url .= $ENV{'SERVER_NAME'};\n"); - p.print(" $http_url .= qq{:$ENV{'SERVER_PORT'}}\n"); - p.print(" if (( $secure && $ENV{'SERVER_PORT'} != 443)\n"); - p.print(" || (!$secure && $ENV{'SERVER_PORT'} != 80)\n"); - p.print(" );\n"); - p.print(" my $context = $ENV{'GERRIT_CONTEXT_PATH'};\n"); - p.print(" chop($context);\n"); - p.print(" $http_url .= qq{$context};\n"); - p.print(" $http_url .= qq{/a}\n"); - p.print(" unless $ENV{'GERRIT_ANONYMOUS_READ'};\n"); - p.print(" push @git_base_url_list, $http_url;\n"); - p.print("}\n"); - - // Generate URLs using anonymous git:// - // - String url = cfg.getString("gerrit", null, "canonicalGitUrl"); - if (url != null) { - if (url.endsWith("/")) { - url = url.substring(0, url.length() - 1); - } - p.print("if ($ENV{'GERRIT_ANONYMOUS_READ'}) {\n"); - p.print(" push @git_base_url_list, "); - p.print(quoteForPerl(url)); - p.print(";\n"); - p.print("}\n"); - } - - // Generate URLs using authenticated ssh:// - // - if (sshInfo != null && !sshInfo.getHostKeys().isEmpty()) { - String sshAddr = sshInfo.getHostKeys().get(0).getHost(); - p.print("if ($ENV{'GERRIT_USER_NAME'}) {\n"); - p.print(" push @git_base_url_list, join('', 'ssh://'"); - p.print(", $ENV{'GERRIT_USER_NAME'}"); - p.print(", '@'"); - if (sshAddr.startsWith("*:") || "".equals(sshAddr)) { - p.print(", $ENV{'SERVER_NAME'}"); - } - if (sshAddr.startsWith("*")) { - sshAddr = sshAddr.substring(1); - } - p.print(", " + quoteForPerl(sshAddr)); - p.print(");\n"); - p.print("}\n"); - } - - // Link back to Gerrit (when possible, to matching review record). - // Supported gitweb's hash values are: - // - (missing), - // - HEAD, - // - refs/heads/<branch>, - // - refs/changes/*/<change>/*, - // - <revision>. - // - p.print("sub add_review_link {\n"); - p.print(" my $h = shift;\n"); - p.print(" my $q;\n"); - p.print(" if (!$h || $h eq 'HEAD') {\n"); - p.print(" $q = qq{#/q/project:$ENV{'GERRIT_PROJECT_NAME'}};\n"); - p.print(" } elsif ($h =~ /^refs\\/heads\\/([-\\w]+)$/) {\n"); - p.print(" $q = qq{#/q/project:$ENV{'GERRIT_PROJECT_NAME'}"); - p.print("+branch:$1};\n"); // wrapped - p.print(" } elsif ($h =~ /^refs\\/changes\\/\\d{2}\\/(\\d+)\\/\\d+$/) "); - p.print("{\n"); // wrapped - p.print(" $q = qq{#/c/$1};\n"); - p.print(" } else {\n"); - p.print(" $q = qq{#/q/$h};\n"); - p.print(" }\n"); - p.print(" my $r = qq{$ENV{'GERRIT_CONTEXT_PATH'}$q};\n"); - p.print(" push @{$feature{'actions'}{'default'}},\n"); - p.print(" ('review',$r,'commitdiff');\n"); - p.print("}\n"); - p.print("if ($cgi->param('hb')) {\n"); - p.print(" add_review_link(scalar $cgi->param('hb'));\n"); - p.print("} elsif ($cgi->param('h')) {\n"); - p.print(" add_review_link(scalar $cgi->param('h'));\n"); - p.print("} else {\n"); - p.print(" add_review_link();\n"); - p.print("}\n"); - - // If the administrator has created a site-specific gitweb_config, - // load that before we perform any final overrides. - // - Path sitecfg = site.site_gitweb; - if (Files.isRegularFile(sitecfg)) { - p.print("$GITWEB_CONFIG = " + quoteForPerl(sitecfg) + ";\n"); - p.print("if (-e $GITWEB_CONFIG) {\n"); - p.print(" do " + quoteForPerl(sitecfg) + ";\n"); - p.print("}\n"); - } - - p.print("$projectroot = $ENV{'GITWEB_PROJECTROOT'};\n"); - - // Permit exporting only the project we were started for. - // We use the name under $projectroot in case symlinks - // were involved in the path. - // - p.print("$export_auth_hook = sub {\n"); - p.print(" my $dir = shift;\n"); - p.print(" my $name = $ENV{'GERRIT_PROJECT_NAME'};\n"); - p.print(" my $allow = qq{$projectroot/$name.git};\n"); - p.print(" return $dir eq $allow;\n"); - p.print(" };\n"); - - // Do not allow the administrator to enable path info, its - // not a URL format we currently support. - // - p.print("$feature{'pathinfo'}{'override'} = 0;\n"); - p.print("$feature{'pathinfo'}{'default'} = [0];\n"); - - // We don't do forking, so don't allow it to be enabled. - // - p.print("$feature{'forks'}{'override'} = 0;\n"); - p.print("$feature{'forks'}{'default'} = [0];\n"); - } - - myconfFile.setReadOnly(); - } - - private static String quoteForPerl(Path value) { - return quoteForPerl(value.toAbsolutePath().toString()); - } - - private static String quoteForPerl(String value) { - if (value == null || value.isEmpty()) { - return "''"; - } - if (!value.contains("'")) { - return "'" + value + "'"; - } - if (!value.contains("{") && !value.contains("}")) { - return "q{" + value + "}"; - } - throw new IllegalArgumentException("Cannot quote in Perl: " + value); - } - - @Override - protected void service(HttpServletRequest req, HttpServletResponse rsp) throws IOException { - if (req.getQueryString() == null || req.getQueryString().isEmpty()) { - // No query string? They want the project list, which we don't - // currently support. Return to Gerrit's own web UI. - // - rsp.sendRedirect(req.getContextPath() + "/"); - return; - } - - final Map<String, String> params = getParameters(req); - String a = params.get("a"); - if (a != null) { - if (deniedActions.contains(a)) { - rsp.sendError(HttpServletResponse.SC_FORBIDDEN); - return; - } - - if (a.equals(PROJECT_LIST_ACTION)) { - rsp.sendRedirect( - req.getContextPath() - + "/#" - + PageLinks.ADMIN_PROJECTS - + "?filter=" - + Url.encode(params.get("pf") + "/")); - return; - } - } - - String name = params.get("p"); - if (name == null) { - rsp.sendError(HttpServletResponse.SC_NOT_FOUND); - return; - } - if (name.endsWith(".git")) { - name = name.substring(0, name.length() - 4); - } - - Project.NameKey nameKey = new Project.NameKey(name); - try { - if (projectCache.checkedGet(nameKey) == null) { - notFound(req, rsp); - return; - } - permissionBackend.user(userProvider).project(nameKey).check(ProjectPermission.READ); - } catch (AuthException e) { - notFound(req, rsp); - return; - } catch (IOException | PermissionBackendException err) { - log.error("cannot load " + name, err); - rsp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR); - return; - } - - try (Repository repo = repoManager.openRepository(nameKey)) { - CacheHeaders.setNotCacheable(rsp); - exec(req, rsp, nameKey); - } catch (RepositoryNotFoundException e) { - getServletContext().log("Cannot open repository", e); - rsp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR); - } - } - - private void notFound(HttpServletRequest req, HttpServletResponse rsp) throws IOException { - if (userProvider.get().isIdentifiedUser()) { - rsp.sendError(HttpServletResponse.SC_NOT_FOUND); - } else { - rsp.sendRedirect(getLoginRedirectUrl(req)); - } - } - - private static String getLoginRedirectUrl(HttpServletRequest req) { - String contextPath = req.getContextPath(); - String loginUrl = contextPath + "/login/"; - String token = req.getRequestURI(); - if (!contextPath.isEmpty()) { - token = token.substring(contextPath.length()); - } - - String queryString = req.getQueryString(); - if (queryString != null && !queryString.isEmpty()) { - token = token.concat("?" + queryString); - } - return (loginUrl + Url.encode(token)); - } - - private static Map<String, String> getParameters(HttpServletRequest req) { - final Map<String, String> params = new HashMap<>(); - for (String pair : req.getQueryString().split("[&;]")) { - final int eq = pair.indexOf('='); - if (0 < eq) { - String name = pair.substring(0, eq); - String value = pair.substring(eq + 1); - - name = Url.decode(name); - value = Url.decode(value); - params.put(name, value); - } - } - return params; - } - - private void exec(HttpServletRequest req, HttpServletResponse rsp, Project.NameKey project) - throws IOException { - final Process proc = - Runtime.getRuntime() - .exec( - new String[] {gitwebCgi.toAbsolutePath().toString()}, - makeEnv(req, project), - gitwebCgi.toAbsolutePath().getParent().toFile()); - - copyStderrToLog(proc.getErrorStream()); - if (0 < req.getContentLength()) { - copyContentToCGI(req, proc.getOutputStream()); - } else { - proc.getOutputStream().close(); - } - - try (InputStream in = new BufferedInputStream(proc.getInputStream(), bufferSize)) { - readCgiHeaders(rsp, in); - - try (OutputStream out = rsp.getOutputStream()) { - final byte[] buf = new byte[bufferSize]; - int n; - while ((n = in.read(buf)) > 0) { - out.write(buf, 0, n); - } - } - } catch (IOException e) { - // The browser has probably closed its input stream. We don't - // want to continue executing this request. - // - proc.destroy(); - return; - } - - try { - proc.waitFor(); - - final int status = proc.exitValue(); - if (0 != status) { - log.error("Non-zero exit status ({}) from {}", status, gitwebCgi); - if (!rsp.isCommitted()) { - rsp.sendError(500); - } - } - } catch (InterruptedException ie) { - log.debug("CGI: interrupted waiting for CGI to terminate"); - } - } - - private String[] makeEnv(HttpServletRequest req, Project.NameKey nameKey) { - final EnvList env = new EnvList(_env); - final int contentLength = Math.max(0, req.getContentLength()); - - // These ones are from "The WWW Common Gateway Interface Version 1.1" - // - env.set("AUTH_TYPE", req.getAuthType()); - env.set("CONTENT_LENGTH", Integer.toString(contentLength)); - env.set("CONTENT_TYPE", req.getContentType()); - env.set("GATEWAY_INTERFACE", "CGI/1.1"); - env.set("PATH_INFO", req.getPathInfo()); - env.set("PATH_TRANSLATED", null); - env.set("QUERY_STRING", req.getQueryString()); - env.set("REMOTE_ADDR", req.getRemoteAddr()); - env.set("REMOTE_HOST", req.getRemoteHost()); - env.set("HTTPS", req.isSecure() ? "ON" : "OFF"); - - // The identity information reported about the connection by a - // RFC 1413 [11] request to the remote agent, if - // available. Servers MAY choose not to support this feature, or - // not to request the data for efficiency reasons. - // "REMOTE_IDENT" => "NYI" - // - env.set("REQUEST_METHOD", req.getMethod()); - env.set("SCRIPT_NAME", req.getContextPath() + req.getServletPath()); - env.set("SCRIPT_FILENAME", gitwebCgi.toAbsolutePath().toString()); - env.set("SERVER_NAME", req.getServerName()); - env.set("SERVER_PORT", Integer.toString(req.getServerPort())); - env.set("SERVER_PROTOCOL", req.getProtocol()); - env.set("SERVER_SOFTWARE", getServletContext().getServerInfo()); - - final Enumeration<String> hdrs = enumerateHeaderNames(req); - while (hdrs.hasMoreElements()) { - final String name = hdrs.nextElement(); - final String value = req.getHeader(name); - env.set("HTTP_" + name.toUpperCase().replace('-', '_'), value); - } - - env.set("GERRIT_CONTEXT_PATH", req.getContextPath() + "/"); - env.set("GERRIT_PROJECT_NAME", nameKey.get()); - - env.set("GITWEB_PROJECTROOT", repoManager.getBasePath(nameKey).toAbsolutePath().toString()); - - if (permissionBackend - .user(anonymousUserProvider) - .project(nameKey) - .testOrFalse(ProjectPermission.READ)) { - env.set("GERRIT_ANONYMOUS_READ", "1"); - } - - String remoteUser = null; - if (userProvider.get().isIdentifiedUser()) { - IdentifiedUser u = userProvider.get().asIdentifiedUser(); - String user = u.getUserName(); - env.set("GERRIT_USER_NAME", user); - if (user != null && !user.isEmpty()) { - remoteUser = user; - } else { - remoteUser = "account-" + u.getAccountId(); - } - } - env.set("REMOTE_USER", remoteUser); - - // Override CGI settings using alternative URI provided by gitweb.url. - // This is required to trick gitweb into thinking that it's served under - // different URL. Setting just $my_uri on the perl's side isn't enough, - // because few actions (atom, blobdiff_plain, commitdiff_plain) rely on - // URL returned by $cgi->self_url(). - // - if (gitwebUrl != null) { - int schemePort = -1; - - if (gitwebUrl.getScheme() != null) { - if (gitwebUrl.getScheme().equals("http")) { - env.set("HTTPS", "OFF"); - schemePort = 80; - } else { - env.set("HTTPS", "ON"); - schemePort = 443; - } - } - - if (gitwebUrl.getHost() != null) { - env.set("SERVER_NAME", gitwebUrl.getHost()); - env.set("HTTP_HOST", gitwebUrl.getHost()); - } - - if (gitwebUrl.getPort() != -1) { - env.set("SERVER_PORT", Integer.toString(gitwebUrl.getPort())); - } else if (schemePort != -1) { - env.set("SERVER_PORT", Integer.toString(schemePort)); - } - - if (gitwebUrl.getPath() != null) { - env.set("SCRIPT_NAME", gitwebUrl.getPath().isEmpty() ? "/" : gitwebUrl.getPath()); - } - } - - return env.getEnvArray(); - } - - private void copyContentToCGI(HttpServletRequest req, OutputStream dst) throws IOException { - final int contentLength = req.getContentLength(); - final InputStream src = req.getInputStream(); - new Thread( - () -> { - try { - try { - final byte[] buf = new byte[bufferSize]; - int remaining = contentLength; - while (0 < remaining) { - final int max = Math.max(buf.length, remaining); - final int n = src.read(buf, 0, max); - if (n < 0) { - throw new EOFException("Expected " + remaining + " more bytes"); - } - dst.write(buf, 0, n); - remaining -= n; - } - } finally { - dst.close(); - } - } catch (IOException e) { - log.error("Unexpected error copying input to CGI", e); - } - }, - "Gitweb-InputFeeder") - .start(); - } - - private void copyStderrToLog(InputStream in) { - new Thread( - () -> { - try (BufferedReader br = - new BufferedReader(new InputStreamReader(in, ISO_8859_1.name()))) { - String err = - br.lines() - .filter(s -> !s.isEmpty()) - .map(s -> "CGI: " + s) - .collect(Collectors.joining("\n")) - .trim(); - if (!err.isEmpty()) { - log.error(err); - } - } catch (IOException e) { - log.error("Unexpected error copying stderr from CGI", e); - } - }, - "Gitweb-ErrorLogger") - .start(); - } - - private static Enumeration<String> enumerateHeaderNames(HttpServletRequest req) { - return req.getHeaderNames(); - } - - private void readCgiHeaders(HttpServletResponse res, InputStream in) throws IOException { - String line; - while (!(line = readLine(in)).isEmpty()) { - if (line.startsWith("HTTP")) { - // CGI believes it is a non-parsed-header CGI. We refuse - // to support that here so abort. - // - throw new IOException("NPH CGI not supported: " + line); - } - - final int sep = line.indexOf(':'); - if (sep < 0) { - throw new IOException("CGI returned invalid header: " + line); - } - - final String key = line.substring(0, sep).trim(); - final String value = line.substring(sep + 1).trim(); - if ("Location".equalsIgnoreCase(key)) { - res.sendRedirect(value); - - } else if ("Status".equalsIgnoreCase(key)) { - final String[] token = value.split(" "); - final int status = Integer.parseInt(token[0]); - res.setStatus(status); - - } else { - res.addHeader(key, value); - } - } - } - - private String readLine(InputStream in) throws IOException { - final StringBuilder buf = new StringBuilder(); - int b; - while ((b = in.read()) != -1 && b != '\n') { - buf.append((char) b); - } - return buf.toString().trim(); - } - - /** private utility class that manages the Environment passed to exec. */ - private static class EnvList { - private Map<String, String> envMap; - - EnvList() { - envMap = new HashMap<>(); - } - - EnvList(EnvList l) { - envMap = new HashMap<>(l.envMap); - } - - /** Set a name/value pair, null values will be treated as an empty String */ - public void set(String name, String value) { - if (value == null) { - value = ""; - } - envMap.put(name, name + "=" + value); - } - - /** Get representation suitable for passing to exec. */ - public String[] getEnvArray() { - return envMap.values().toArray(new String[envMap.size()]); - } - - @Override - public String toString() { - return envMap.toString(); - } - } -} |