Commits

Sebastian Sdorra committed 0d0bd52

initial import

  • Participants
  • Parent commits d9f2b60

Comments (0)

Files changed (14)

+<?xml version="1.0"?>
+<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
+    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
+
+  <modelVersion>4.0.0</modelVersion>
+
+  <parent>
+    <artifactId>scm-plugins</artifactId>
+    <groupId>sonia.scm.plugins</groupId>
+    <version>1.5-SNAPSHOT</version>
+  </parent>
+
+  <groupId>sonia.scm.plugins</groupId>
+  <artifactId>scm-bzr-plugin</artifactId>
+  <version>1.5-SNAPSHOT</version>
+  <name>scm-bzr-plugin</name>
+  <url>https://bitbucket.org/sdorra/scm-manager</url>
+  <description>Plugin for the version control system Bazaar</description>
+
+  <dependencies>
+
+    <dependency>
+      <groupId>javax.servlet</groupId>
+      <artifactId>servlet-api</artifactId>
+      <version>${servlet.version}</version>
+      <scope>provided</scope>
+    </dependency>
+
+  </dependencies>
+
+</project>

src/main/java/sonia/scm/api/rest/resources/BzrConfigResource.java

+/**
+ * Copyright (c) 2010, Sebastian Sdorra
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ * 3. Neither the name of SCM-Manager; nor the names of its
+ *    contributors may be used to endorse or promote products derived from this
+ *    software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * http://bitbucket.org/sdorra/scm-manager
+ *
+ */
+
+
+
+package sonia.scm.api.rest.resources;
+
+//~--- non-JDK imports --------------------------------------------------------
+
+import com.google.inject.Inject;
+import com.google.inject.Singleton;
+
+import sonia.scm.repository.BzrConfig;
+import sonia.scm.repository.BzrRepositoryHandler;
+import sonia.scm.web.BzrScriptWriter;
+
+//~--- JDK imports ------------------------------------------------------------
+
+import java.io.IOException;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.GET;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.UriInfo;
+
+/**
+ *
+ * @author Sebastian Sdorra
+ */
+@Singleton
+@Path("config/repositories/bzr")
+public class BzrConfigResource
+{
+
+  /**
+   *  Constructs ...
+   *
+   *
+   *
+   *  @param handler
+   */
+  @Inject
+  public BzrConfigResource(BzrRepositoryHandler handler)
+  {
+    this.handler = handler;
+  }
+
+  //~--- get methods ----------------------------------------------------------
+
+  /**
+   * Method description
+   *
+   *
+   * @return
+   */
+  @GET
+  @Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
+  public BzrConfig getConfig()
+  {
+    BzrConfig config = handler.getConfig();
+
+    if (config == null)
+    {
+      config = new BzrConfig();
+    }
+
+    return config;
+  }
+
+  //~--- set methods ----------------------------------------------------------
+
+  /**
+   * Method description
+   *
+   *
+   * @param uriInfo
+   * @param config
+   *
+   * @return
+   *
+   * @throws IOException
+   */
+  @POST
+  @Consumes({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
+  public Response setConfig(@Context UriInfo uriInfo, BzrConfig config)
+          throws IOException
+  {
+    handler.setConfig(config);
+    handler.storeConfig();
+    new BzrScriptWriter(config).write();
+
+    return Response.created(uriInfo.getRequestUri()).build();
+  }
+
+  //~--- fields ---------------------------------------------------------------
+
+  /** Field description */
+  private BzrRepositoryHandler handler;
+}

src/main/java/sonia/scm/repository/BzrConfig.java

+/**
+ * Copyright (c) 2010, Sebastian Sdorra
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ * 3. Neither the name of SCM-Manager; nor the names of its
+ *    contributors may be used to endorse or promote products derived from this
+ *    software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * http://bitbucket.org/sdorra/scm-manager
+ *
+ */
+
+
+
+package sonia.scm.repository;
+
+//~--- JDK imports ------------------------------------------------------------
+
+import javax.xml.bind.annotation.XmlRootElement;
+
+/**
+ *
+ * @author Sebastian Sdorra
+ */
+@XmlRootElement(name = "config")
+public class BzrConfig extends SimpleRepositoryConfig
+{
+
+  /**
+   * Constructs ...
+   *
+   */
+  public BzrConfig() {}
+
+  //~--- get methods ----------------------------------------------------------
+
+  /**
+   * Method description
+   *
+   *
+   * @return
+   */
+  public String getBzrBinary()
+  {
+    return bzrBinary;
+  }
+
+  /**
+   * Method description
+   *
+   *
+   * @return
+   */
+  public String getPythonBinary()
+  {
+    return pythonBinary;
+  }
+
+  /**
+   * Method description
+   *
+   *
+   * @return
+   */
+  public String getPythonPath()
+  {
+    return pythonPath;
+  }
+
+  //~--- set methods ----------------------------------------------------------
+
+  /**
+   * Method description
+   *
+   *
+   * @param bzrBinary
+   */
+  public void setBzrBinary(String bzrBinary)
+  {
+    this.bzrBinary = bzrBinary;
+  }
+
+  /**
+   * Method description
+   *
+   *
+   * @param pythonBinary
+   */
+  public void setPythonBinary(String pythonBinary)
+  {
+    this.pythonBinary = pythonBinary;
+  }
+
+  /**
+   * Method description
+   *
+   *
+   * @param pythonPath
+   */
+  public void setPythonPath(String pythonPath)
+  {
+    this.pythonPath = pythonPath;
+  }
+
+  //~--- fields ---------------------------------------------------------------
+
+  /** Field description */
+  private String bzrBinary = "bzr";
+
+  /** Field description */
+  private String pythonBinary = "python";
+
+  /** Field description */
+  private String pythonPath = "";
+}

src/main/java/sonia/scm/repository/BzrRepositoryHandler.java

+/**
+ * Copyright (c) 2010, Sebastian Sdorra
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ * 3. Neither the name of SCM-Manager; nor the names of its
+ *    contributors may be used to endorse or promote products derived from this
+ *    software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * http://bitbucket.org/sdorra/scm-manager
+ *
+ */
+
+
+
+package sonia.scm.repository;
+
+//~--- non-JDK imports --------------------------------------------------------
+
+import com.google.inject.Inject;
+import com.google.inject.Provider;
+import com.google.inject.Singleton;
+
+import sonia.scm.Type;
+import sonia.scm.io.ExtendedCommand;
+import sonia.scm.io.FileSystem;
+import sonia.scm.plugin.ext.Extension;
+import sonia.scm.store.StoreFactory;
+import sonia.scm.util.SecurityUtil;
+import sonia.scm.web.security.WebSecurityContext;
+
+//~--- JDK imports ------------------------------------------------------------
+
+import java.io.File;
+
+/**
+ *
+ * @author Sebastian Sdorra
+ */
+@Singleton
+@Extension
+public class BzrRepositoryHandler
+        extends AbstractSimpleRepositoryHandler<BzrConfig>
+{
+
+  /** Field description */
+  public static final String PUBLIC_RESOURCEPATH_BASE = "/public/bzr/";
+
+  /** Field description */
+  public static final String TYPE_DISPLAYNAME = "Bazaar";
+
+  /** Field description */
+  public static final String TYPE_NAME = "bzr";
+
+  /** Field description */
+  public static final Type TYPE = new Type(TYPE_NAME, TYPE_DISPLAYNAME);
+
+  //~--- constructors ---------------------------------------------------------
+
+  /**
+   * Constructs ...
+   *
+   *
+   * @param storeFactory
+   * @param fileSystem
+   * @param securityContextProvider
+   */
+  @Inject
+  public BzrRepositoryHandler(
+          StoreFactory storeFactory, FileSystem fileSystem,
+          Provider<WebSecurityContext> securityContextProvider)
+  {
+    super(storeFactory, fileSystem);
+    this.securityContextProvider = securityContextProvider;
+  }
+
+  //~--- methods --------------------------------------------------------------
+
+  /**
+   * Method description
+   *
+   *
+   * @param repository
+   *
+   * @return
+   */
+  @Override
+  public String createResourcePath(Repository repository)
+  {
+    String resourcePath = null;
+
+    if (SecurityUtil.isAnonymous(securityContextProvider))
+    {
+      resourcePath = PUBLIC_RESOURCEPATH_BASE.concat(repository.getName());
+    }
+    else
+    {
+      resourcePath = super.createResourcePath(repository);
+    }
+
+    return resourcePath;
+  }
+
+  //~--- get methods ----------------------------------------------------------
+
+  /**
+   * Method description
+   *
+   *
+   * @return
+   */
+  @Override
+  public Type getType()
+  {
+    return TYPE;
+  }
+
+  //~--- methods --------------------------------------------------------------
+
+  /**
+   * Method description
+   *
+   *
+   * @param repository
+   * @param directory
+   *
+   * @return
+   */
+  @Override
+  protected ExtendedCommand buildCreateCommand(Repository repository,
+          File directory)
+  {
+    return new ExtendedCommand(config.getBzrBinary(), "init-repo",
+                               "--no-trees", directory.getPath());
+  }
+
+  /**
+   * Method description
+   *
+   *
+   * @return
+   */
+  @Override
+  protected BzrConfig createInitialConfig()
+  {
+    return new BzrConfig();
+  }
+
+  //~--- get methods ----------------------------------------------------------
+
+  /**
+   * Method description
+   *
+   *
+   * @return
+   */
+  @Override
+  protected Class<BzrConfig> getConfigClass()
+  {
+    return BzrConfig.class;
+  }
+
+  //~--- fields ---------------------------------------------------------------
+
+  /** Field description */
+  private Provider<WebSecurityContext> securityContextProvider;
+}

src/main/java/sonia/scm/web/BzrCGIServlet.java

+/**
+ * Copyright (c) 2010, Sebastian Sdorra
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ * 3. Neither the name of SCM-Manager; nor the names of its
+ *    contributors may be used to endorse or promote products derived from this
+ *    software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * http://bitbucket.org/sdorra/scm-manager
+ *
+ */
+
+
+
+package sonia.scm.web;
+
+//~--- non-JDK imports --------------------------------------------------------
+
+import com.google.inject.Inject;
+import com.google.inject.Provider;
+import com.google.inject.Singleton;
+
+import sonia.scm.config.ScmConfiguration;
+import sonia.scm.io.RegexResourceProcessor;
+import sonia.scm.io.ResourceProcessor;
+import sonia.scm.repository.BzrConfig;
+import sonia.scm.repository.BzrRepositoryHandler;
+import sonia.scm.repository.PermissionType;
+import sonia.scm.repository.PermissionUtil;
+import sonia.scm.repository.Repository;
+import sonia.scm.repository.RepositoryManager;
+import sonia.scm.util.AssertUtil;
+import sonia.scm.util.HttpUtil;
+import sonia.scm.util.IOUtil;
+import sonia.scm.web.cgi.AbstractCGIServlet;
+import sonia.scm.web.cgi.EnvList;
+import sonia.scm.web.security.WebSecurityContext;
+
+//~--- JDK imports ------------------------------------------------------------
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+/**
+ *
+ * @author Sebastian Sdorra
+ */
+@Singleton
+public class BzrCGIServlet extends AbstractCGIServlet
+{
+
+  /** Field description */
+  public static final String ENV_PYTHON_PATH = "SCM_PYTHON_PATH";
+
+  /** Field description */
+  public static final String ENV_REPOSITORY_NAME = "REPO_NAME";
+
+  /** Field description */
+  public static final String ENV_REPOSITORY_PATH = "SCM_REPOSITORY_PATH";
+
+  /** Field description */
+  public static final String ENV_REPOSITORY_READONLY =
+    "SCM_REPOSITORY_READONLY";
+
+  /** Field description */
+  public static final String MIMETYPE_HTML = "text/html";
+
+  /** Field description */
+  public static final String RESOURCE_BZRINDEX = "/sonia/scm/bzr.index.html";
+
+  /** Field description */
+  public static final String SMART_SUFFIX = ".bzr/smart";
+
+  /** Field description */
+  private static final long serialVersionUID = 7674689744455227632L;
+
+  /** Field description */
+  private static final Pattern REGEX_REPOSITORYNAME =
+    Pattern.compile("/bzr/([^/]+)/?.*");
+
+  /** Field description */
+  public static final Pattern PATTERN_REPOSITORYNAME =
+    Pattern.compile("/[^/]+/([^/]+)(?:/.*)?");
+
+  //~--- constructors ---------------------------------------------------------
+
+  /**
+   * Constructs ...
+   *
+   *
+   *
+   *
+   * @param configuration
+   * @param securityContextProvider
+   * @param repositoryManager
+   * @param handler
+   */
+  @Inject
+  public BzrCGIServlet(ScmConfiguration configuration,
+                       Provider<WebSecurityContext> securityContextProvider,
+                       RepositoryManager repositoryManager,
+                       BzrRepositoryHandler handler)
+  {
+    super(configuration);
+    this.securityContextProvider = securityContextProvider;
+    this.repositoryManager = repositoryManager;
+    this.handler = handler;
+  }
+
+  //~--- methods --------------------------------------------------------------
+
+  /**
+   * Method description
+   *
+   *
+   * @throws ServletException
+   */
+  @Override
+  public void init() throws ServletException
+  {
+    command = BzrUtil.getCGI();
+    super.init();
+  }
+
+  /**
+   * Method description
+   *
+   *
+   * @param request
+   * @param baseEnvironment
+   *
+   * @return
+   *
+   * @throws ServletException
+   */
+  @Override
+  protected EnvList createRequestEnvironment(HttpServletRequest request,
+          EnvList baseEnvironment)
+          throws ServletException
+  {
+    EnvList list = new EnvList(baseEnvironment);
+    Repository repository = getRepository(request);
+
+    if (repository == null)
+    {
+      throw new ServletException("repository not found");
+    }
+
+    String name = repository.getName();
+    File directory = handler.getDirectory(repository);
+
+    list.set(ENV_REPOSITORY_PATH, directory.getAbsolutePath());
+    list.set(ENV_REPOSITORY_NAME, name);
+
+    String pythonPath = "";
+    BzrConfig config = handler.getConfig();
+
+    if (config != null)
+    {
+      pythonPath = config.getPythonPath();
+
+      if (pythonPath == null)
+      {
+        pythonPath = "";
+      }
+    }
+
+    list.set(ENV_PYTHON_PATH, pythonPath);
+
+    boolean writePermission = hasWritePermission(repository);
+
+    if (writePermission)
+    {
+      list.set(ENV_REPOSITORY_READONLY, "False");
+    }
+    else
+    {
+      list.set(ENV_REPOSITORY_READONLY, "True");
+    }
+
+    return list;
+  }
+
+  /**
+   * Method description
+   *
+   *
+   * @param req
+   * @param resp
+   *
+   * @throws IOException
+   * @throws ServletException
+   */
+  @Override
+  protected void service(HttpServletRequest req, HttpServletResponse resp)
+          throws ServletException, IOException
+  {
+    String uri = req.getRequestURI();
+
+    if (!uri.endsWith(SMART_SUFFIX))
+    {
+      printBazaarInformation(req, resp);
+    }
+    else
+    {
+      super.service(req, resp);
+    }
+  }
+
+  //~--- get methods ----------------------------------------------------------
+
+  /**
+   * Method description
+   *
+   *
+   *
+   * @return
+   *
+   */
+  @Override
+  protected String getCmdPrefix()
+  {
+    BzrConfig config = handler.getConfig();
+
+    AssertUtil.assertIsNotNull(config);
+
+    return config.getPythonBinary();
+  }
+
+  /**
+   * Method description
+   *
+   *
+   * @param request
+   *
+   * @return
+   *
+   * @throws IOException
+   * @throws ServletException
+   */
+  @Override
+  protected File getCommand(HttpServletRequest request)
+          throws ServletException, IOException
+  {
+    return command;
+  }
+
+  //~--- methods --------------------------------------------------------------
+
+  /**
+   * Method description
+   *
+   *
+   * @param request
+   * @param response
+   *
+   * @throws IOException
+   */
+  private void printBazaarInformation(HttpServletRequest request,
+          HttpServletResponse response)
+          throws IOException
+  {
+    String uri = HttpUtil.getStrippedURI(request);
+    Matcher m = REGEX_REPOSITORYNAME.matcher(uri);
+    String name = "unknown";
+
+    if (m.matches())
+    {
+      name = m.group(1);
+    }
+
+    response.setContentType(MIMETYPE_HTML);
+
+    ResourceProcessor resourceProzessor = new RegexResourceProcessor();
+
+    resourceProzessor.addVariable("name", name);
+
+    InputStream input = null;
+    OutputStream output = null;
+
+    try
+    {
+      input = BzrCGIServlet.class.getResourceAsStream(RESOURCE_BZRINDEX);
+      output = response.getOutputStream();
+      resourceProzessor.process(input, output);
+    }
+    finally
+    {
+      IOUtil.close(input);
+      IOUtil.close(output);
+    }
+  }
+
+  //~--- get methods ----------------------------------------------------------
+
+  /**
+   * Method description
+   *
+   *
+   * @param request
+   *
+   * @return
+   */
+  private Repository getRepository(HttpServletRequest request)
+  {
+    Repository repository = null;
+    String uri = request.getRequestURI();
+
+    uri = uri.substring(request.getContextPath().length());
+
+    Matcher m = PATTERN_REPOSITORYNAME.matcher(uri);
+
+    if (m.matches())
+    {
+      String repositoryname = m.group(1);
+
+      repository = getRepository(repositoryname);
+    }
+
+    return repository;
+  }
+
+  /**
+   * Method description
+   *
+   *
+   * @param repositoryname
+   *
+   * @return
+   */
+  private Repository getRepository(String repositoryname)
+  {
+    return repositoryManager.get(BzrRepositoryHandler.TYPE_NAME,
+                                 repositoryname);
+  }
+
+  /**
+   * Method description
+   *
+   *
+   * @param repository
+   *
+   * @return
+   */
+  private boolean hasWritePermission(Repository repository)
+  {
+    return PermissionUtil.hasPermission(repository, securityContextProvider,
+            PermissionType.WRITE);
+  }
+
+  //~--- fields ---------------------------------------------------------------
+
+  /** Field description */
+  private File command;
+
+  /** Field description */
+  private BzrRepositoryHandler handler;
+
+  /** Field description */
+  private RepositoryManager repositoryManager;
+
+  /** Field description */
+  private Provider<WebSecurityContext> securityContextProvider;
+}

src/main/java/sonia/scm/web/BzrPermissionFilter.java

+/**
+ * Copyright (c) 2010, Sebastian Sdorra
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ * 3. Neither the name of SCM-Manager; nor the names of its
+ *    contributors may be used to endorse or promote products derived from this
+ *    software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * http://bitbucket.org/sdorra/scm-manager
+ *
+ */
+
+
+
+package sonia.scm.web;
+
+//~--- non-JDK imports --------------------------------------------------------
+
+import com.google.inject.Inject;
+import com.google.inject.Provider;
+import com.google.inject.Singleton;
+
+import sonia.scm.repository.BzrRepositoryHandler;
+import sonia.scm.repository.RepositoryManager;
+import sonia.scm.web.filter.RegexPermissionFilter;
+import sonia.scm.web.security.WebSecurityContext;
+
+//~--- JDK imports ------------------------------------------------------------
+
+import javax.servlet.http.HttpServletRequest;
+
+/**
+ *
+ * @author Sebastian Sdorra
+ */
+@Singleton
+public class BzrPermissionFilter extends RegexPermissionFilter
+{
+
+  /**
+   * Constructs ...
+   *
+   *
+   * @param securityContextProvider
+   * @param repositoryManager
+   */
+  @Inject
+  public BzrPermissionFilter(
+          Provider<WebSecurityContext> securityContextProvider,
+          RepositoryManager repositoryManager)
+  {
+    super(securityContextProvider, repositoryManager);
+  }
+
+  //~--- get methods ----------------------------------------------------------
+
+  /**
+   * Method description
+   *
+   *
+   * @return
+   */
+  @Override
+  protected String getType()
+  {
+    return BzrRepositoryHandler.TYPE_NAME;
+  }
+
+  /**
+   * Method description
+   *
+   *
+   * @param request
+   *
+   * @return
+   */
+  @Override
+  protected boolean isWriteRequest(HttpServletRequest request)
+  {
+    return false;
+  }
+}

src/main/java/sonia/scm/web/BzrPublicFilter.java

+/**
+ * Copyright (c) 2010, Sebastian Sdorra
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ * 3. Neither the name of SCM-Manager; nor the names of its
+ *    contributors may be used to endorse or promote products derived from this
+ *    software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * http://bitbucket.org/sdorra/scm-manager
+ *
+ */
+
+
+
+package sonia.scm.web;
+
+//~--- non-JDK imports --------------------------------------------------------
+
+import com.google.inject.Inject;
+import com.google.inject.Singleton;
+
+import sonia.scm.repository.BzrRepositoryHandler;
+import sonia.scm.repository.Repository;
+import sonia.scm.repository.RepositoryManager;
+import sonia.scm.util.HttpUtil;
+import sonia.scm.web.filter.HttpFilter;
+
+//~--- JDK imports ------------------------------------------------------------
+
+import java.io.IOException;
+
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import javax.servlet.FilterChain;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+/**
+ *
+ * @author Sebastian Sdorra
+ */
+@Singleton
+public class BzrPublicFilter extends HttpFilter
+{
+
+  /** Field description */
+  private static final Pattern REGEX_REPOSITORYNAME =
+    Pattern.compile("/public/bzr/([^/]+)");
+
+  //~--- constructors ---------------------------------------------------------
+
+  /**
+   * Constructs ...
+   *
+   *
+   * @param repositoryManager
+   */
+  @Inject
+  public BzrPublicFilter(RepositoryManager repositoryManager)
+  {
+    this.repositoryManager = repositoryManager;
+  }
+
+  //~--- methods --------------------------------------------------------------
+
+  /**
+   * Method description
+   *
+   *
+   * @param request
+   * @param response
+   * @param chain
+   *
+   * @throws IOException
+   * @throws ServletException
+   */
+  @Override
+  protected void doFilter(HttpServletRequest request,
+                          HttpServletResponse response, FilterChain chain)
+          throws IOException, ServletException
+  {
+    String requestURI = HttpUtil.getStrippedURI(request);
+    Matcher m = REGEX_REPOSITORYNAME.matcher(requestURI);
+
+    if (m.find())
+    {
+      String name = m.group(1);
+      Repository repository =
+        repositoryManager.get(BzrRepositoryHandler.TYPE_NAME, name);
+
+      if ((repository != null) && repository.isPublicReadable())
+      {
+        chain.doFilter(request, response);
+      }
+      else
+      {
+        response.setStatus(HttpServletResponse.SC_NOT_FOUND);
+      }
+    }
+    else
+    {
+      response.setStatus(HttpServletResponse.SC_NOT_FOUND);
+    }
+  }
+
+  //~--- fields ---------------------------------------------------------------
+
+  /** Field description */
+  private RepositoryManager repositoryManager;
+}

src/main/java/sonia/scm/web/BzrScriptWriter.java

+/**
+ * Copyright (c) 2010, Sebastian Sdorra
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ * 3. Neither the name of SCM-Manager; nor the names of its
+ *    contributors may be used to endorse or promote products derived from this
+ *    software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * http://bitbucket.org/sdorra/scm-manager
+ *
+ */
+
+
+
+package sonia.scm.web;
+
+//~--- non-JDK imports --------------------------------------------------------
+
+import sonia.scm.io.RegexResourceProcessor;
+import sonia.scm.io.ResourceProcessor;
+import sonia.scm.repository.BzrConfig;
+import sonia.scm.util.IOUtil;
+
+//~--- JDK imports ------------------------------------------------------------
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+/**
+ *
+ * @author Sebastian Sdorra
+ */
+public class BzrScriptWriter
+{
+
+  /** Field description */
+  public static final String CGI_TEMPLATE = "/sonia/scm/bzrweb.py";
+
+  //~--- constructors ---------------------------------------------------------
+
+  /**
+   * Constructs ...
+   *
+   *
+   * @param config
+   */
+  public BzrScriptWriter(BzrConfig config)
+  {
+    this.config = config;
+  }
+
+  //~--- methods --------------------------------------------------------------
+
+  /**
+   * Method description
+   *
+   *
+   *
+   * @throws IOException
+   */
+  public void write() throws IOException
+  {
+    File cgiFile = BzrUtil.getCGI();
+
+    writeCGIFile(cgiFile);
+  }
+
+  /**
+   * Method description
+   *
+   *
+   * @param cgiFile
+   *
+   * @throws IOException
+   */
+  private void writeCGIFile(File cgiFile) throws IOException
+  {
+    InputStream input = null;
+    OutputStream output = null;
+
+    try
+    {
+      input = BzrScriptWriter.class.getResourceAsStream(CGI_TEMPLATE);
+      output = new FileOutputStream(cgiFile);
+
+      ResourceProcessor rp = new RegexResourceProcessor();
+
+      rp.addVariable("python", config.getPythonBinary());
+      rp.process(input, output);
+      cgiFile.setExecutable(true);
+    }
+    finally
+    {
+      IOUtil.close(input);
+      IOUtil.close(output);
+    }
+  }
+
+  //~--- fields ---------------------------------------------------------------
+
+  /** Field description */
+  private BzrConfig config;
+}

src/main/java/sonia/scm/web/BzrServletModule.java

+/**
+ * Copyright (c) 2010, Sebastian Sdorra
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ * 3. Neither the name of SCM-Manager; nor the names of its
+ *    contributors may be used to endorse or promote products derived from this
+ *    software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * http://bitbucket.org/sdorra/scm-manager
+ *
+ */
+
+
+
+package sonia.scm.web;
+
+//~--- non-JDK imports --------------------------------------------------------
+
+import com.google.inject.servlet.ServletModule;
+
+import sonia.scm.plugin.ext.Extension;
+import sonia.scm.web.filter.BasicAuthenticationFilter;
+
+/**
+ *
+ * @author Sebastian Sdorra
+ */
+@Extension
+public class BzrServletModule extends ServletModule
+{
+
+  /** Field description */
+  public static final String MAPPING_BZR = "/bzr/*";
+
+  /** Field description */
+  public static final String MAPPING_PUBLICBZR = "/public/bzr/*";
+
+  //~--- methods --------------------------------------------------------------
+
+  /**
+   * Method description
+   *
+   */
+  @Override
+  protected void configureServlets()
+  {
+    filter(MAPPING_BZR,
+           MAPPING_PUBLICBZR).through(BasicAuthenticationFilter.class);
+    filter(MAPPING_BZR).through(BzrPermissionFilter.class);
+    filter(MAPPING_PUBLICBZR).through(BzrPublicFilter.class);
+    serve(MAPPING_BZR, MAPPING_PUBLICBZR).with(BzrCGIServlet.class);
+  }
+}

src/main/java/sonia/scm/web/BzrUtil.java

+/**
+ * Copyright (c) 2010, Sebastian Sdorra
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ * 3. Neither the name of SCM-Manager; nor the names of its
+ *    contributors may be used to endorse or promote products derived from this
+ *    software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * http://bitbucket.org/sdorra/scm-manager
+ *
+ */
+
+
+
+package sonia.scm.web;
+
+//~--- non-JDK imports --------------------------------------------------------
+
+import sonia.scm.SCMContext;
+
+//~--- JDK imports ------------------------------------------------------------
+
+import java.io.File;
+
+/**
+ *
+ * @author Sebastian Sdorra
+ */
+public class BzrUtil
+{
+
+  /** Field description */
+  public static final String CGI_DIRECTORY = "cgi-bin";
+
+  /** Field description */
+  public static final String CGI_NAME = "bzrweb.py";
+
+  //~--- get methods ----------------------------------------------------------
+
+  /**
+   * Method description
+   *
+   *
+   * @return
+   */
+  public static File getCGI()
+  {
+    File cgiDirectory = new File(SCMContext.getContext().getBaseDirectory(),
+                                 CGI_DIRECTORY);
+
+    if (!cgiDirectory.exists() &&!cgiDirectory.mkdirs())
+    {
+      throw new RuntimeException(
+          "could not create directory".concat(cgiDirectory.getPath()));
+    }
+
+    return new File(cgiDirectory, CGI_NAME);
+  }
+}

src/main/resources/META-INF/scm/plugin.xml

+<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+<!--
+
+    Copyright (c) 2010, Sebastian Sdorra
+    All rights reserved.
+
+    Redistribution and use in source and binary forms, with or without
+    modification, are permitted provided that the following conditions are met:
+
+    1. Redistributions of source code must retain the above copyright notice,
+       this list of conditions and the following disclaimer.
+    2. Redistributions in binary form must reproduce the above copyright notice,
+       this list of conditions and the following disclaimer in the documentation
+       and/or other materials provided with the distribution.
+    3. Neither the name of SCM-Manager; nor the names of its
+       contributors may be used to endorse or promote products derived from this
+       software without specific prior written permission.
+
+    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+    AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+    IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+    DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY
+    DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+    (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+    LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+    ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+    (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+    SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+    http://bitbucket.org/sdorra/scm-manager
+
+
+-->
+
+<!--
+    Document   : plugin.xml
+    Created on : October 12, 2010, 8:29 AM
+    Author     : sdorra
+    Description:
+        Purpose of the document follows.
+-->
+
+<plugin>
+
+  <information>
+    <groupId>${project.groupId}</groupId>
+    <artifactId>${project.artifactId}</artifactId>
+    <version>${project.version}</version>
+    <name>${project.name}</name>
+    <description>${project.description}</description>
+    <author>Sebastian Sdorra</author>
+    <url>${project.url}</url>
+  </information>
+
+  <conditions>
+    <min-version>1.1</min-version>
+  </conditions>
+
+  <resources>
+    <script>/sonia/scm/bzr.config.js</script>
+  </resources>
+
+</plugin>

src/main/resources/sonia/scm/bzr.config.js

+/* *
+ * Copyright (c) 2010, Sebastian Sdorra
+ * All rights reserved.
+ * 
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * 
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ * 3. Neither the name of SCM-Manager; nor the names of its
+ *    contributors may be used to endorse or promote products derived from this
+ *    software without specific prior written permission.
+ * 
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ * 
+ * http://bitbucket.org/sdorra/scm-manager
+ * 
+ */
+
+
+Ext.ns("Sonia.bzr");
+
+Sonia.bzr.ConfigPanel = Ext.extend(Sonia.config.SimpleConfigForm, {
+
+  // labels
+  titleText: 'Bazaar Settings',
+  bzrBinaryText: 'Bzr Binary',
+  pythonBinary: 'Python Binary',
+  pythonPath: 'Python Path',
+  repositoryDirectoryText: 'Repository directory',
+
+  // helpTexts
+  bzrBinaryHelpText: 'Location of Bzr binary.',
+  pythonBinaryHelpText: 'Location of Python binary.',
+  pythonPathHelpText: 'Python path.',
+  repositoryDirectoryHelpText: 'Location of Bazaar repositories.',
+
+  initComponent: function(){
+
+    var config = {
+      title : this.titleText,
+      configUrl: restUrl + 'config/repositories/bzr.json',
+      items : [{
+        xtype : 'textfield',
+        fieldLabel : this.bzrBinaryText,
+        name : 'bzrBinary',
+        allowBlank : false,
+        helpText: this.bzrBinaryHelpText
+      },{
+        xtype : 'textfield',
+        fieldLabel : this.pythonBinary,
+        name : 'pythonBinary',
+        allowBlank : false,
+        helpText: this.pythonBinaryHelpText
+      },{
+        xtype : 'textfield',
+        fieldLabel : this.pythonPath,
+        name : 'pythonPath',
+        helpText: this.pythonPathHelpText
+      },{
+        xtype: 'textfield',
+        name: 'repositoryDirectory',
+        fieldLabel: this.repositoryDirectoryText,
+        allowBlank : false,
+        helpText: this.repositoryDirectoryHelpText
+      }]
+    }
+
+    Ext.apply(this, Ext.apply(this.initialConfig, config));
+    Sonia.bzr.ConfigPanel.superclass.initComponent.apply(this, arguments);
+  }
+
+});
+
+Ext.reg("bzrConfigPanel", Sonia.bzr.ConfigPanel);
+
+// i18n
+
+if ( i18n != null && i18n.country == 'de' ){
+
+  Ext.override(Sonia.hg.ConfigPanel, {
+
+    // labels
+    titleText: 'Bazaar Einstellungen',
+    bzrBinaryText: 'Bzr Pfad',
+    pythonBinary: 'Python Pfad',
+    pythonPath: 'Python Modul Suchpfad',
+    repositoryDirectoryText: 'Repository-Verzeichnis',
+
+    // helpTexts
+    bzrBinaryHelpText: 'Pfad zum "bzr" Befehl.',
+    pythonBinaryHelpText: 'Pfad zum "python" Befehl.',
+    pythonPathHelpText: 'Python Modul Suchpfad (PYTHONPATH).',
+    repositoryDirectoryHelpText: 'Pfad der Bazaar-Repositories.'
+
+  });
+
+}
+
+// register panel
+
+registerConfigPanel({
+  xtype : 'bzrConfigPanel'
+});

src/main/resources/sonia/scm/bzr.index.html

+<!--
+
+    Copyright (c) 2010, Sebastian Sdorra
+    All rights reserved.
+
+    Redistribution and use in source and binary forms, with or without
+    modification, are permitted provided that the following conditions are met:
+
+    1. Redistributions of source code must retain the above copyright notice,
+       this list of conditions and the following disclaimer.
+    2. Redistributions in binary form must reproduce the above copyright notice,
+       this list of conditions and the following disclaimer in the documentation
+       and/or other materials provided with the distribution.
+    3. Neither the name of SCM-Manager; nor the names of its
+       contributors may be used to endorse or promote products derived from this
+       software without specific prior written permission.
+
+    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+    AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+    IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+    DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY
+    DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+    (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+    LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+    ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+    (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+    SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+    http://bitbucket.org/sdorra/scm-manager
+
+
+-->
+
+<html>
+  <head>
+    <title>SCM :: Manager - Bazaar Repository - ${name}</title>
+    <style type="text/css">
+      body {
+        background-color: #ffffff;
+        margin: 10px;
+        color: #202020;
+        font-family: Verdana,Helvetica,Arial,sans-serif;
+        font-size: 75%;
+      }
+      h1, h2, h3, h4, h5 {
+        font-family: Arial, "Arial CE", "Lucida Grande CE", lucida, "Helvetica CE", sans-serif;
+        font-weight: bold;
+        margin: 0px;
+        padding: 0px;
+        color: #D20005;
+      }
+      h1 {
+        font-size: 18px;
+        border-bottom: 1px solid #AFAFAF;
+      }
+      h2 {
+        font-size:  14px;
+        border-bottom: 1px solid #AFAFAF;
+      }
+      a:link, a:visited {
+        color: #045491;
+        font-weight: bold;
+        text-decoration: none;
+      }      
+      a:link:hover, a:visited:hover  {
+        color: #045491;
+        font-weight: bold;
+        text-decoration: underline;
+      }
+      table {
+        border: 0 none;
+        border-collapse: collapse;
+        font-size: 100%;
+        margin: 20px 0;
+        padding: 20px;
+        width: 100%;
+      }
+      td, th {
+        padding: 3px;
+        vertical-align: top;
+        border: 1px solid #CCCCCC;
+        text-align: left;
+      }
+      .small {
+        width: 20%;
+      }
+    </style>
+  </head>
+  <body>
+
+    <h1>SCM :: Manager - Bazaar Repository - ${name}</h1>
+
+    <p>&nbsp;</p>
+
+    <h2>Bazaar Informations</h2>
+    <ul>
+      <li><a href="http://bazaar.canonical.com/" target="_blank">Bazaar Homepage</a></li>
+      <li><a href="http://doc.bazaar.canonical.com/" target="_blank">Bazaar Documentation</a></li>
+    </ul>
+
+  </body>
+</html>

src/main/resources/sonia/scm/bzrweb.py

+#!/usr/bin/env ${python}
+
+import os, sys
+pythonPath = os.environ['SCM_PYTHON_PATH']
+
+if len(pythonPath) > 0:
+  pathParts = pythonPath.split(os.pathsep)
+  for i in range(len(pathParts)):
+    sys.path.insert(i, pathParts[i])
+
+repositoryPath = os.environ['SCM_REPOSITORY_PATH']
+repositoryName = os.environ['REPO_NAME']
+readonlyString = os.environ['SCM_REPOSITORY_READONLY']
+readonly = True
+
+if readonlyString == 'False':
+  readonly = False
+
+def run_with_cgi(application):
+
+    environ = dict(os.environ.items())
+    environ['wsgi.input']        = sys.stdin
+    environ['wsgi.errors']       = sys.stderr
+    environ['wsgi.version']      = (1, 0)
+    environ['wsgi.multithread']  = False
+    environ['wsgi.multiprocess'] = True
+    environ['wsgi.run_once']     = True
+
+    if environ.get('HTTPS', 'off') in ('on', '1'):
+        environ['wsgi.url_scheme'] = 'https'
+    else:
+        environ['wsgi.url_scheme'] = 'http'
+
+    headers_set = []
+    headers_sent = []
+
+    def write(data):
+        if not headers_set:
+             raise AssertionError("write() before start_response()")
+
+        elif not headers_sent:
+             # Before the first output, send the stored headers
+             status, response_headers = headers_sent[:] = headers_set
+             sys.stdout.write('Status: %s\r\n' % status)
+             for header in response_headers:
+                 sys.stdout.write('%s: %s\r\n' % header)
+             sys.stdout.write('\r\n')
+
+        sys.stdout.write(data)
+        sys.stdout.flush()
+
+    def start_response(status, response_headers, exc_info=None):
+        if exc_info:
+            try:
+                if headers_sent:
+                    # Re-raise original exception if headers sent
+                    raise exc_info[0], exc_info[1], exc_info[2]
+            finally:
+                exc_info = None     # avoid dangling circular ref
+        elif headers_set:
+            raise AssertionError("Headers already set!")
+
+        headers_set[:] = [status, response_headers]
+        return write
+
+    result = application(environ, start_response)
+    try:
+        for data in result:
+            if data:    # don't send headers until body appears
+                write(data)
+        if not headers_sent:
+            write('')   # send headers now if body was empty
+    finally:
+        if hasattr(result, 'close'):
+            result.close()
+
+from bzrlib.transport.http import wsgi
+
+def application(environ, start_response):
+  app = wsgi.make_app(
+      root=repositoryPath,
+      prefix="/"+repositoryName,
+      path_var='PATH_INFO',
+      readonly=readonly,
+      enable_logging=True)
+  return app(environ, start_response)
+
+run_with_cgi(application)