1
2
3
4
5
6
7
8
9
10
11 package org.eclipse.jgit.transport;
12
13 import static org.eclipse.jgit.util.StringUtils.equalsIgnoreCase;
14 import static org.eclipse.jgit.util.StringUtils.toLowerCase;
15
16 import java.io.File;
17 import java.util.EnumSet;
18 import java.util.HashMap;
19 import java.util.Map;
20
21 import org.eclipse.jgit.annotations.Nullable;
22 import org.eclipse.jgit.internal.storage.file.LazyObjectIdSetFile;
23 import org.eclipse.jgit.lib.Config;
24 import org.eclipse.jgit.lib.Config.SectionParser;
25 import org.eclipse.jgit.lib.ObjectChecker;
26 import org.eclipse.jgit.lib.ObjectIdSet;
27 import org.eclipse.jgit.lib.Ref;
28 import org.eclipse.jgit.lib.Repository;
29 import org.eclipse.jgit.util.SystemReader;
30
31
32
33
34
35 public class TransferConfig {
36 private static final String FSCK = "fsck";
37
38
39 public static final Config.SectionParser<TransferConfig> KEY =
40 TransferConfig::new;
41
42
43
44
45
46
47 public enum FsckMode {
48
49
50
51 ERROR,
52
53
54
55 WARN,
56
57
58
59 IGNORE;
60 }
61
62
63
64
65
66 enum ProtocolVersion {
67 V0("0"),
68 V2("2");
69
70 final String name;
71
72 ProtocolVersion(String name) {
73 this.name = name;
74 }
75
76 @Nullable
77 static ProtocolVersion parse(@Nullable String name) {
78 if (name == null) {
79 return null;
80 }
81 for (ProtocolVersion v : ProtocolVersion.values()) {
82 if (v.name.equals(name)) {
83 return v;
84 }
85 }
86 return null;
87 }
88 }
89
90 private final boolean fetchFsck;
91 private final boolean receiveFsck;
92 private final String fsckSkipList;
93 private final EnumSet<ObjectChecker.ErrorType> ignore;
94 private final boolean allowInvalidPersonIdent;
95 private final boolean safeForWindows;
96 private final boolean safeForMacOS;
97 private final boolean allowRefInWant;
98 private final boolean allowTipSha1InWant;
99 private final boolean allowReachableSha1InWant;
100 private final boolean allowFilter;
101 private final boolean allowSidebandAll;
102 private final boolean advertiseSidebandAll;
103 final @Nullable ProtocolVersion protocolVersion;
104 final String[] hideRefs;
105
106
107
108
109
110
111
112
113
114
115 public TransferConfig(Repository db) {
116 this(db.getConfig());
117 }
118
119
120
121
122
123
124
125
126
127
128
129 @SuppressWarnings("nls")
130 public TransferConfig(Config rc) {
131 boolean fsck = rc.getBoolean("transfer", "fsckobjects", false);
132 fetchFsck = rc.getBoolean("fetch", "fsckobjects", fsck);
133 receiveFsck = rc.getBoolean("receive", "fsckobjects", fsck);
134 fsckSkipList = rc.getString(FSCK, null, "skipList");
135 allowInvalidPersonIdent = rc.getBoolean(FSCK, "allowInvalidPersonIdent",
136 false);
137 safeForWindows = rc.getBoolean(FSCK, "safeForWindows",
138 SystemReader.getInstance().isWindows());
139 safeForMacOS = rc.getBoolean(FSCK, "safeForMacOS",
140 SystemReader.getInstance().isMacOS());
141
142 ignore = EnumSet.noneOf(ObjectChecker.ErrorType.class);
143 EnumSet<ObjectChecker.ErrorType> set = EnumSet
144 .noneOf(ObjectChecker.ErrorType.class);
145 for (String key : rc.getNames(FSCK)) {
146 if (equalsIgnoreCase(key, "skipList")
147 || equalsIgnoreCase(key, "allowLeadingZeroFileMode")
148 || equalsIgnoreCase(key, "allowInvalidPersonIdent")
149 || equalsIgnoreCase(key, "safeForWindows")
150 || equalsIgnoreCase(key, "safeForMacOS")) {
151 continue;
152 }
153
154 ObjectChecker.ErrorType id = FsckKeyNameHolder.parse(key);
155 if (id != null) {
156 switch (rc.getEnum(FSCK, null, key, FsckMode.ERROR)) {
157 case ERROR:
158 ignore.remove(id);
159 break;
160 case WARN:
161 case IGNORE:
162 ignore.add(id);
163 break;
164 }
165 set.add(id);
166 }
167 }
168 if (!set.contains(ObjectChecker.ErrorType.ZERO_PADDED_FILEMODE)
169 && rc.getBoolean(FSCK, "allowLeadingZeroFileMode", false)) {
170 ignore.add(ObjectChecker.ErrorType.ZERO_PADDED_FILEMODE);
171 }
172
173 allowRefInWant = rc.getBoolean("uploadpack", "allowrefinwant", false);
174 allowTipSha1InWant = rc.getBoolean(
175 "uploadpack", "allowtipsha1inwant", false);
176 allowReachableSha1InWant = rc.getBoolean(
177 "uploadpack", "allowreachablesha1inwant", false);
178 allowFilter = rc.getBoolean(
179 "uploadpack", "allowfilter", false);
180 protocolVersion = ProtocolVersion.parse(rc.getString("protocol", null, "version"));
181 hideRefs = rc.getStringList("uploadpack", null, "hiderefs");
182 allowSidebandAll = rc.getBoolean(
183 "uploadpack", "allowsidebandall", false);
184 advertiseSidebandAll = rc.getBoolean("uploadpack",
185 "advertisesidebandall", false);
186 }
187
188
189
190
191
192
193
194
195 @Nullable
196 public ObjectChecker newObjectChecker() {
197 return newObjectChecker(fetchFsck);
198 }
199
200
201
202
203
204
205
206
207 @Nullable
208 public ObjectChecker newReceiveObjectChecker() {
209 return newObjectChecker(receiveFsck);
210 }
211
212 private ObjectChecker newObjectChecker(boolean check) {
213 if (!check) {
214 return null;
215 }
216 return new ObjectChecker()
217 .setIgnore(ignore)
218 .setAllowInvalidPersonIdent(allowInvalidPersonIdent)
219 .setSafeForWindows(safeForWindows)
220 .setSafeForMacOS(safeForMacOS)
221 .setSkipList(skipList());
222 }
223
224 private ObjectIdSet skipList() {
225 if (fsckSkipList != null && !fsckSkipList.isEmpty()) {
226 return new LazyObjectIdSetFile(new File(fsckSkipList));
227 }
228 return null;
229 }
230
231
232
233
234
235
236
237 public boolean isAllowTipSha1InWant() {
238 return allowTipSha1InWant;
239 }
240
241
242
243
244
245
246
247 public boolean isAllowReachableSha1InWant() {
248 return allowReachableSha1InWant;
249 }
250
251
252
253
254
255 public boolean isAllowFilter() {
256 return allowFilter;
257 }
258
259
260
261
262
263 public boolean isAllowRefInWant() {
264 return allowRefInWant;
265 }
266
267
268
269
270
271
272 public boolean isAllowSidebandAll() {
273 return allowSidebandAll;
274 }
275
276
277
278
279
280 public boolean isAdvertiseSidebandAll() {
281 return advertiseSidebandAll && allowSidebandAll;
282 }
283
284
285
286
287
288
289
290
291
292 public RefFilter getRefFilter() {
293 if (hideRefs.length == 0)
294 return RefFilter.DEFAULT;
295
296 return new RefFilter() {
297 @Override
298 public Map<String, Ref> filter(Map<String, Ref> refs) {
299 Map<String, Ref> result = new HashMap<>();
300 for (Map.Entry<String, Ref> e : refs.entrySet()) {
301 boolean add = true;
302 for (String hide : hideRefs) {
303 if (e.getKey().equals(hide) || prefixMatch(hide, e.getKey())) {
304 add = false;
305 break;
306 }
307 }
308 if (add)
309 result.put(e.getKey(), e.getValue());
310 }
311 return result;
312 }
313
314 private boolean prefixMatch(String p, String s) {
315 return p.charAt(p.length() - 1) == '/' && s.startsWith(p);
316 }
317 };
318 }
319
320
321
322
323
324
325
326 boolean hasDefaultRefFilter() {
327 return hideRefs.length == 0;
328 }
329
330 static class FsckKeyNameHolder {
331 private static final Map<String, ObjectChecker.ErrorType> errors;
332
333 static {
334 errors = new HashMap<>();
335 for (ObjectChecker.ErrorType m : ObjectChecker.ErrorType.values()) {
336 errors.put(keyNameFor(m.name()), m);
337 }
338 }
339
340 @Nullable
341 static ObjectChecker.ErrorType parse(String key) {
342 return errors.get(toLowerCase(key));
343 }
344
345 private static String keyNameFor(String name) {
346 StringBuilder r = new StringBuilder(name.length());
347 for (int i = 0; i < name.length(); i++) {
348 char c = name.charAt(i);
349 if (c != '_') {
350 r.append(c);
351 }
352 }
353 return toLowerCase(r.toString());
354 }
355
356 private FsckKeyNameHolder() {
357 }
358 }
359 }