TransferConfig.java

  1. /*
  2.  * Copyright (C) 2008, 2020 Google Inc. and others
  3.  *
  4.  * This program and the accompanying materials are made available under the
  5.  * terms of the Eclipse Distribution License v. 1.0 which is available at
  6.  * https://www.eclipse.org/org/documents/edl-v10.php.
  7.  *
  8.  * SPDX-License-Identifier: BSD-3-Clause
  9.  */

  10. package org.eclipse.jgit.transport;

  11. import static org.eclipse.jgit.util.StringUtils.equalsIgnoreCase;
  12. import static org.eclipse.jgit.util.StringUtils.toLowerCase;

  13. import java.io.File;
  14. import java.util.EnumSet;
  15. import java.util.HashMap;
  16. import java.util.Map;

  17. import org.eclipse.jgit.annotations.Nullable;
  18. import org.eclipse.jgit.internal.storage.file.LazyObjectIdSetFile;
  19. import org.eclipse.jgit.lib.Config;
  20. import org.eclipse.jgit.lib.ConfigConstants;
  21. import org.eclipse.jgit.lib.Config.SectionParser;
  22. import org.eclipse.jgit.lib.ObjectChecker;
  23. import org.eclipse.jgit.lib.ObjectIdSet;
  24. import org.eclipse.jgit.lib.Ref;
  25. import org.eclipse.jgit.lib.Repository;
  26. import org.eclipse.jgit.util.SystemReader;

  27. /**
  28.  * The standard "transfer", "fetch", "protocol", "receive", and "uploadpack"
  29.  * configuration parameters.
  30.  */
  31. public class TransferConfig {
  32.     private static final String FSCK = "fsck"; //$NON-NLS-1$

  33.     /** Key for {@link Config#get(SectionParser)}. */
  34.     public static final Config.SectionParser<TransferConfig> KEY =
  35.             TransferConfig::new;

  36.     /**
  37.      * A git configuration value for how to handle a fsck failure of a particular kind.
  38.      * Used in e.g. fsck.missingEmail.
  39.      * @since 4.9
  40.      */
  41.     public enum FsckMode {
  42.         /**
  43.          * Treat it as an error (the default).
  44.          */
  45.         ERROR,
  46.         /**
  47.          * Issue a warning (in fact, jgit treats this like IGNORE, but git itself does warn).
  48.          */
  49.         WARN,
  50.         /**
  51.          * Ignore the error.
  52.          */
  53.         IGNORE;
  54.     }

  55.     /**
  56.      * A git configuration variable for which versions of the Git protocol to
  57.      * prefer. Used in protocol.version.
  58.      *
  59.      * @since 5.9
  60.      */
  61.     public enum ProtocolVersion {
  62.         /**
  63.          * Git wire protocol version 0 (the default).
  64.          */
  65.         V0("0"), //$NON-NLS-1$
  66.         /**
  67.          * Git wire protocol version 2.
  68.          */
  69.         V2("2"); //$NON-NLS-1$

  70.         final String name;

  71.         ProtocolVersion(String name) {
  72.             this.name = name;
  73.         }

  74.         /**
  75.          * Returns version number
  76.          *
  77.          * @return string version
  78.          */
  79.         public String version() {
  80.             return name;
  81.         }

  82.         @Nullable
  83.         static ProtocolVersion parse(@Nullable String name) {
  84.             if (name == null) {
  85.                 return null;
  86.             }
  87.             for (ProtocolVersion v : ProtocolVersion.values()) {
  88.                 if (v.name.equals(name)) {
  89.                     return v;
  90.                 }
  91.             }
  92.             if ("1".equals(name)) { //$NON-NLS-1$
  93.                 return V0;
  94.             }
  95.             return null;
  96.         }
  97.     }

  98.     private final boolean fetchFsck;
  99.     private final boolean receiveFsck;
  100.     private final String fsckSkipList;
  101.     private final EnumSet<ObjectChecker.ErrorType> ignore;
  102.     private final boolean allowInvalidPersonIdent;
  103.     private final boolean safeForWindows;
  104.     private final boolean safeForMacOS;
  105.     private final boolean allowRefInWant;
  106.     private final boolean allowTipSha1InWant;
  107.     private final boolean allowReachableSha1InWant;
  108.     private final boolean allowFilter;
  109.     private final boolean allowSidebandAll;
  110.     private final boolean advertiseSidebandAll;
  111.     final @Nullable ProtocolVersion protocolVersion;
  112.     final String[] hideRefs;

  113.     /**
  114.      * Create a configuration honoring the repository's settings.
  115.      *
  116.      * @param db
  117.      *            the repository to read settings from. The repository is not
  118.      *            retained by the new configuration, instead its settings are
  119.      *            copied during the constructor.
  120.      * @since 5.1.4
  121.      */
  122.     public TransferConfig(Repository db) {
  123.         this(db.getConfig());
  124.     }

  125.     /**
  126.      * Create a configuration honoring settings in a
  127.      * {@link org.eclipse.jgit.lib.Config}.
  128.      *
  129.      * @param rc
  130.      *            the source to read settings from. The source is not retained
  131.      *            by the new configuration, instead its settings are copied
  132.      *            during the constructor.
  133.      * @since 5.1.4
  134.      */
  135.     @SuppressWarnings("nls")
  136.     public TransferConfig(Config rc) {
  137.         boolean fsck = rc.getBoolean("transfer", "fsckobjects", false);
  138.         fetchFsck = rc.getBoolean("fetch", "fsckobjects", fsck);
  139.         receiveFsck = rc.getBoolean("receive", "fsckobjects", fsck);
  140.         fsckSkipList = rc.getString(FSCK, null, "skipList");
  141.         allowInvalidPersonIdent = rc.getBoolean(FSCK, "allowInvalidPersonIdent",
  142.                 false);
  143.         safeForWindows = rc.getBoolean(FSCK, "safeForWindows",
  144.                         SystemReader.getInstance().isWindows());
  145.         safeForMacOS = rc.getBoolean(FSCK, "safeForMacOS",
  146.                         SystemReader.getInstance().isMacOS());

  147.         ignore = EnumSet.noneOf(ObjectChecker.ErrorType.class);
  148.         EnumSet<ObjectChecker.ErrorType> set = EnumSet
  149.                 .noneOf(ObjectChecker.ErrorType.class);
  150.         for (String key : rc.getNames(FSCK)) {
  151.             if (equalsIgnoreCase(key, "skipList")
  152.                     || equalsIgnoreCase(key, "allowLeadingZeroFileMode")
  153.                     || equalsIgnoreCase(key, "allowInvalidPersonIdent")
  154.                     || equalsIgnoreCase(key, "safeForWindows")
  155.                     || equalsIgnoreCase(key, "safeForMacOS")) {
  156.                 continue;
  157.             }

  158.             ObjectChecker.ErrorType id = FsckKeyNameHolder.parse(key);
  159.             if (id != null) {
  160.                 switch (rc.getEnum(FSCK, null, key, FsckMode.ERROR)) {
  161.                 case ERROR:
  162.                     ignore.remove(id);
  163.                     break;
  164.                 case WARN:
  165.                 case IGNORE:
  166.                     ignore.add(id);
  167.                     break;
  168.                 }
  169.                 set.add(id);
  170.             }
  171.         }
  172.         if (!set.contains(ObjectChecker.ErrorType.ZERO_PADDED_FILEMODE)
  173.                 && rc.getBoolean(FSCK, "allowLeadingZeroFileMode", false)) {
  174.             ignore.add(ObjectChecker.ErrorType.ZERO_PADDED_FILEMODE);
  175.         }

  176.         allowRefInWant = rc.getBoolean("uploadpack", "allowrefinwant", false);
  177.         allowTipSha1InWant = rc.getBoolean(
  178.                 "uploadpack", "allowtipsha1inwant", false);
  179.         allowReachableSha1InWant = rc.getBoolean(
  180.                 "uploadpack", "allowreachablesha1inwant", false);
  181.         allowFilter = rc.getBoolean(
  182.                 "uploadpack", "allowfilter", false);
  183.         protocolVersion = ProtocolVersion.parse(rc
  184.                 .getString(ConfigConstants.CONFIG_PROTOCOL_SECTION, null,
  185.                         ConfigConstants.CONFIG_KEY_VERSION));
  186.         hideRefs = rc.getStringList("uploadpack", null, "hiderefs");
  187.         allowSidebandAll = rc.getBoolean(
  188.                 "uploadpack", "allowsidebandall", false);
  189.         advertiseSidebandAll = rc.getBoolean("uploadpack",
  190.                 "advertisesidebandall", false);
  191.     }

  192.     /**
  193.      * Create checker to verify fetched objects
  194.      *
  195.      * @return checker to verify fetched objects, or null if checking is not
  196.      *         enabled in the repository configuration.
  197.      * @since 3.6
  198.      */
  199.     @Nullable
  200.     public ObjectChecker newObjectChecker() {
  201.         return newObjectChecker(fetchFsck);
  202.     }

  203.     /**
  204.      * Create checker to verify objects pushed into this repository
  205.      *
  206.      * @return checker to verify objects pushed into this repository, or null if
  207.      *         checking is not enabled in the repository configuration.
  208.      * @since 4.2
  209.      */
  210.     @Nullable
  211.     public ObjectChecker newReceiveObjectChecker() {
  212.         return newObjectChecker(receiveFsck);
  213.     }

  214.     private ObjectChecker newObjectChecker(boolean check) {
  215.         if (!check) {
  216.             return null;
  217.         }
  218.         return new ObjectChecker()
  219.             .setIgnore(ignore)
  220.             .setAllowInvalidPersonIdent(allowInvalidPersonIdent)
  221.             .setSafeForWindows(safeForWindows)
  222.             .setSafeForMacOS(safeForMacOS)
  223.             .setSkipList(skipList());
  224.     }

  225.     private ObjectIdSet skipList() {
  226.         if (fsckSkipList != null && !fsckSkipList.isEmpty()) {
  227.             return new LazyObjectIdSetFile(new File(fsckSkipList));
  228.         }
  229.         return null;
  230.     }

  231.     /**
  232.      * Whether to allow clients to request non-advertised tip SHA-1s
  233.      *
  234.      * @return allow clients to request non-advertised tip SHA-1s?
  235.      * @since 3.1
  236.      */
  237.     public boolean isAllowTipSha1InWant() {
  238.         return allowTipSha1InWant;
  239.     }

  240.     /**
  241.      * Whether to allow clients to request non-tip SHA-1s
  242.      *
  243.      * @return allow clients to request non-tip SHA-1s?
  244.      * @since 4.1
  245.      */
  246.     public boolean isAllowReachableSha1InWant() {
  247.         return allowReachableSha1InWant;
  248.     }

  249.     /**
  250.      * @return true if clients are allowed to specify a "filter" line
  251.      * @since 5.0
  252.      */
  253.     public boolean isAllowFilter() {
  254.         return allowFilter;
  255.     }

  256.     /**
  257.      * @return true if clients are allowed to specify a "want-ref" line
  258.      * @since 5.1
  259.      */
  260.     public boolean isAllowRefInWant() {
  261.         return allowRefInWant;
  262.     }

  263.     /**
  264.      * @return true if the server accepts sideband-all requests (see
  265.      *         {{@link #isAdvertiseSidebandAll()} for the advertisement)
  266.      * @since 5.5
  267.      */
  268.     public boolean isAllowSidebandAll() {
  269.         return allowSidebandAll;
  270.     }

  271.     /**
  272.      * @return true to advertise sideband all to the clients
  273.      * @since 5.6
  274.      */
  275.     public boolean isAdvertiseSidebandAll() {
  276.         return advertiseSidebandAll && allowSidebandAll;
  277.     }

  278.     /**
  279.      * Get {@link org.eclipse.jgit.transport.RefFilter} respecting configured
  280.      * hidden refs.
  281.      *
  282.      * @return {@link org.eclipse.jgit.transport.RefFilter} respecting
  283.      *         configured hidden refs.
  284.      * @since 3.1
  285.      */
  286.     public RefFilter getRefFilter() {
  287.         if (hideRefs.length == 0)
  288.             return RefFilter.DEFAULT;

  289.         return new RefFilter() {
  290.             @Override
  291.             public Map<String, Ref> filter(Map<String, Ref> refs) {
  292.                 Map<String, Ref> result = new HashMap<>();
  293.                 for (Map.Entry<String, Ref> e : refs.entrySet()) {
  294.                     boolean add = true;
  295.                     for (String hide : hideRefs) {
  296.                         if (e.getKey().equals(hide) || prefixMatch(hide, e.getKey())) {
  297.                             add = false;
  298.                             break;
  299.                         }
  300.                     }
  301.                     if (add)
  302.                         result.put(e.getKey(), e.getValue());
  303.                 }
  304.                 return result;
  305.             }

  306.             private boolean prefixMatch(String p, String s) {
  307.                 return p.charAt(p.length() - 1) == '/' && s.startsWith(p);
  308.             }
  309.         };
  310.     }

  311.     /**
  312.      * Like {@code getRefFilter() == RefFilter.DEFAULT}, but faster.
  313.      *
  314.      * @return {@code true} if no ref filtering is needed because there
  315.      *         are no configured hidden refs.
  316.      */
  317.     boolean hasDefaultRefFilter() {
  318.         return hideRefs.length == 0;
  319.     }

  320.     static class FsckKeyNameHolder {
  321.         private static final Map<String, ObjectChecker.ErrorType> errors;

  322.         static {
  323.             errors = new HashMap<>();
  324.             for (ObjectChecker.ErrorType m : ObjectChecker.ErrorType.values()) {
  325.                 errors.put(keyNameFor(m.name()), m);
  326.             }
  327.         }

  328.         @Nullable
  329.         static ObjectChecker.ErrorType parse(String key) {
  330.             return errors.get(toLowerCase(key));
  331.         }

  332.         private static String keyNameFor(String name) {
  333.             StringBuilder r = new StringBuilder(name.length());
  334.             for (int i = 0; i < name.length(); i++) {
  335.                 char c = name.charAt(i);
  336.                 if (c != '_') {
  337.                     r.append(c);
  338.                 }
  339.             }
  340.             return toLowerCase(r.toString());
  341.         }

  342.         private FsckKeyNameHolder() {
  343.         }
  344.     }
  345. }