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