diff options
Diffstat (limited to 'org.eclipse.jgit/src/org/eclipse/jgit/transport/ReceivePack.java')
-rw-r--r-- | org.eclipse.jgit/src/org/eclipse/jgit/transport/ReceivePack.java | 127 |
1 files changed, 103 insertions, 24 deletions
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/ReceivePack.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/ReceivePack.java index 6602de49ed..0d79110289 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/ReceivePack.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/ReceivePack.java @@ -134,6 +134,9 @@ public class ReceivePack { /** Identity to record action as within the reflog. */ private PersonIdent refLogIdent; + /** Hook used while advertising the refs to the client. */ + private AdvertiseRefsHook advertiseRefsHook; + /** Filter used while advertising the refs to the client. */ private RefFilter refFilter; @@ -213,6 +216,7 @@ public class ReceivePack { allowDeletes = cfg.allowDeletes; allowNonFastForwards = cfg.allowNonFastForwards; allowOfsDelta = cfg.allowOfsDelta; + advertiseRefsHook = AdvertiseRefsHook.DEFAULT; refFilter = RefFilter.DEFAULT; preReceive = PreReceiveHook.NULL; postReceive = PostReceiveHook.NULL; @@ -304,27 +308,59 @@ public class ReceivePack { return walk; } - /** @return all refs which were advertised to the client. */ + /** + * Get refs which were advertised to the client. + * + * @return all refs which were advertised to the client, or null if + * {@link #setAdvertisedRefs(Map, Set)} has not been called yet. + */ public final Map<String, Ref> getAdvertisedRefs() { - if (refs == null) { - refs = refFilter.filter(db.getAllRefs()); - - Ref head = refs.get(Constants.HEAD); - if (head != null && head.isSymbolic()) - refs.remove(Constants.HEAD); + return refs; + } - for (Ref ref : refs.values()) { - if (ref.getObjectId() != null) - advertisedHaves.add(ref.getObjectId()); - } - advertisedHaves.addAll(db.getAdditionalHaves()); + /** + * Set the refs advertised by this ReceivePack. + * <p> + * Intended to be called from a {@link PreReceiveHook}. + * + * @param allRefs + * explicit set of references to claim as advertised by this + * ReceivePack instance. This overrides any references that + * may exist in the source repository. The map is passed + * to the configured {@link #getRefFilter()}. If null, assumes + * all refs were advertised. + * @param additionalHaves + * explicit set of additional haves to claim as advertised. If + * null, assumes the default set of additional haves from the + * repository. + */ + public void setAdvertisedRefs(Map<String, Ref> allRefs, + Set<ObjectId> additionalHaves) { + refs = allRefs != null ? allRefs : db.getAllRefs(); + refs = refFilter.filter(refs); + + Ref head = refs.get(Constants.HEAD); + if (head != null && head.isSymbolic()) + refs.remove(Constants.HEAD); + + for (Ref ref : refs.values()) { + if (ref.getObjectId() != null) + advertisedHaves.add(ref.getObjectId()); } - return refs; + if (additionalHaves != null) + advertisedHaves.addAll(additionalHaves); + else + advertisedHaves.addAll(db.getAdditionalHaves()); } - /** @return the set of objects advertised as present in this repository. */ + /** + * Get objects advertised to the client. + * + * @return the set of objects advertised to the as present in this repository, + * or null if {@link #setAdvertisedRefs(Map, Set)} has not been called + * yet. + */ public final Set<ObjectId> getAdvertisedObjects() { - getAdvertisedRefs(); return advertisedHaves; } @@ -342,13 +378,13 @@ public class ReceivePack { * <p> * If enabled, this instance will verify that references to objects not * contained within the received pack are already reachable through at least - * one other reference selected by the {@link #getRefFilter()} and displayed - * as part of {@link #getAdvertisedRefs()}. + * one other reference displayed as part of {@link #getAdvertisedRefs()}. * <p> * This feature is useful when the application doesn't trust the client to * not provide a forged SHA-1 reference to an object, in an attempt to * access parts of the DAG that they aren't allowed to see and which have - * been hidden from them via the configured {@link RefFilter}. + * been hidden from them via the configured {@link AdvertiseRefsHook} or + * {@link RefFilter}. * <p> * Enabling this feature may imply at least some, if not all, of the same * functionality performed by {@link #setCheckReceivedObjects(boolean)}. @@ -464,18 +500,42 @@ public class ReceivePack { refLogIdent = pi; } + /** @return the hook used while advertising the refs to the client */ + public AdvertiseRefsHook getAdvertiseRefsHook() { + return advertiseRefsHook; + } + /** @return the filter used while advertising the refs to the client */ public RefFilter getRefFilter() { return refFilter; } /** + * Set the hook used while advertising the refs to the client. + * <p> + * If the {@link AdvertiseRefsHook} chooses to call + * {@link #setAdvertisedRefs(Map,Set)}, only refs set by this hook + * <em>and</em> selected by the {@link RefFilter} will be shown to the client. + * Clients may still attempt to create or update a reference not advertised by + * the configured {@link AdvertiseRefsHook}. These attempts should be rejected + * by a matching {@link PreReceiveHook}. + * + * @param advertiseRefsHook + * the hook; may be null to show all refs. + */ + public void setAdvertiseRefsHook(final AdvertiseRefsHook advertiseRefsHook) { + if (advertiseRefsHook != null) + this.advertiseRefsHook = advertiseRefsHook; + else + this.advertiseRefsHook = AdvertiseRefsHook.DEFAULT; + } + + /** * Set the filter used while advertising the refs to the client. * <p> * Only refs allowed by this filter will be shown to the client. - * Clients may still attempt to create or update a reference hidden - * by the configured {@link RefFilter}. These attempts should be - * rejected by a matching {@link PreReceiveHook}. + * The filter is run against the refs specified by the + * {@link AdvertiseRefsHook} (if applicable). * * @param refFilter * the filter; may be null to show all refs. @@ -706,12 +766,18 @@ public class ReceivePack { } } + private Map<String, Ref> getAdvertisedOrDefaultRefs() { + if (refs == null) + setAdvertisedRefs(null, null); + return refs; + } + private void service() throws IOException { if (biDirectionalPipe) { sendAdvertisedRefs(new PacketLineOutRefAdvertiser(pckOut)); pckOut.flush(); } else - getAdvertisedRefs(); + getAdvertisedOrDefaultRefs(); if (advertiseError != null) return; recvCommands(); @@ -776,20 +842,33 @@ public class ReceivePack { * the advertisement formatter. * @throws IOException * the formatter failed to write an advertisement. + * @throws ServiceMayNotContinueException + * the hook denied advertisement. */ - public void sendAdvertisedRefs(final RefAdvertiser adv) throws IOException { + public void sendAdvertisedRefs(final RefAdvertiser adv) throws IOException, + ServiceMayNotContinueException { if (advertiseError != null) { adv.writeOne("ERR " + advertiseError); return; } + try { + advertiseRefsHook.advertiseRefs(this); + } catch (ServiceMayNotContinueException fail) { + if (fail.getMessage() != null) { + adv.writeOne("ERR " + fail.getMessage()); + fail.setOutput(); + } + throw fail; + } + adv.init(db); adv.advertiseCapability(CAPABILITY_SIDE_BAND_64K); adv.advertiseCapability(CAPABILITY_DELETE_REFS); adv.advertiseCapability(CAPABILITY_REPORT_STATUS); if (allowOfsDelta) adv.advertiseCapability(CAPABILITY_OFS_DELTA); - adv.send(getAdvertisedRefs()); + adv.send(getAdvertisedOrDefaultRefs()); for (ObjectId obj : advertisedHaves) adv.advertiseHave(obj); if (adv.isEmpty()) |