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 static @Nullable ProtocolVersion parse(@Nullable String name) {
110 if (name == null) {
111 return null;
112 }
113 for (ProtocolVersion v : ProtocolVersion.values()) {
114 if (v.name.equals(name)) {
115 return v;
116 }
117 }
118 return null;
119 }
120 }
121
122 private final boolean fetchFsck;
123 private final boolean receiveFsck;
124 private final String fsckSkipList;
125 private final EnumSet<ObjectChecker.ErrorType> ignore;
126 private final boolean allowInvalidPersonIdent;
127 private final boolean safeForWindows;
128 private final boolean safeForMacOS;
129 private final boolean allowRefInWant;
130 private final boolean allowTipSha1InWant;
131 private final boolean allowReachableSha1InWant;
132 private final boolean allowFilter;
133 final @Nullable ProtocolVersion protocolVersion;
134 final String[] hideRefs;
135
136 TransferConfig(Repository db) {
137 this(db.getConfig());
138 }
139
140 @SuppressWarnings("nls")
141 TransferConfig(Config rc) {
142 boolean fsck = rc.getBoolean("transfer", "fsckobjects", false);
143 fetchFsck = rc.getBoolean("fetch", "fsckobjects", fsck);
144 receiveFsck = rc.getBoolean("receive", "fsckobjects", fsck);
145 fsckSkipList = rc.getString(FSCK, null, "skipList");
146 allowInvalidPersonIdent = rc.getBoolean(FSCK, "allowInvalidPersonIdent",
147 false);
148 safeForWindows = rc.getBoolean(FSCK, "safeForWindows",
149 SystemReader.getInstance().isWindows());
150 safeForMacOS = rc.getBoolean(FSCK, "safeForMacOS",
151 SystemReader.getInstance().isMacOS());
152
153 ignore = EnumSet.noneOf(ObjectChecker.ErrorType.class);
154 EnumSet<ObjectChecker.ErrorType> set = EnumSet
155 .noneOf(ObjectChecker.ErrorType.class);
156 for (String key : rc.getNames(FSCK)) {
157 if (equalsIgnoreCase(key, "skipList")
158 || equalsIgnoreCase(key, "allowLeadingZeroFileMode")
159 || equalsIgnoreCase(key, "allowInvalidPersonIdent")
160 || equalsIgnoreCase(key, "safeForWindows")
161 || equalsIgnoreCase(key, "safeForMacOS")) {
162 continue;
163 }
164
165 ObjectChecker.ErrorType id = FsckKeyNameHolder.parse(key);
166 if (id != null) {
167 switch (rc.getEnum(FSCK, null, key, FsckMode.ERROR)) {
168 case ERROR:
169 ignore.remove(id);
170 break;
171 case WARN:
172 case IGNORE:
173 ignore.add(id);
174 break;
175 }
176 set.add(id);
177 }
178 }
179 if (!set.contains(ObjectChecker.ErrorType.ZERO_PADDED_FILEMODE)
180 && rc.getBoolean(FSCK, "allowLeadingZeroFileMode", false)) {
181 ignore.add(ObjectChecker.ErrorType.ZERO_PADDED_FILEMODE);
182 }
183
184 allowRefInWant = rc.getBoolean("uploadpack", "allowrefinwant", false);
185 allowTipSha1InWant = rc.getBoolean(
186 "uploadpack", "allowtipsha1inwant", false);
187 allowReachableSha1InWant = rc.getBoolean(
188 "uploadpack", "allowreachablesha1inwant", false);
189 allowFilter = rc.getBoolean(
190 "uploadpack", "allowfilter", false);
191 protocolVersion = ProtocolVersion.parse(rc.getString("protocol", null, "version"));
192 hideRefs = rc.getStringList("uploadpack", null, "hiderefs");
193 }
194
195
196
197
198
199
200
201
202 @Nullable
203 public ObjectChecker newObjectChecker() {
204 return newObjectChecker(fetchFsck);
205 }
206
207
208
209
210
211
212
213
214 @Nullable
215 public ObjectChecker newReceiveObjectChecker() {
216 return newObjectChecker(receiveFsck);
217 }
218
219 private ObjectChecker newObjectChecker(boolean check) {
220 if (!check) {
221 return null;
222 }
223 return new ObjectChecker()
224 .setIgnore(ignore)
225 .setAllowInvalidPersonIdent(allowInvalidPersonIdent)
226 .setSafeForWindows(safeForWindows)
227 .setSafeForMacOS(safeForMacOS)
228 .setSkipList(skipList());
229 }
230
231 private ObjectIdSet skipList() {
232 if (fsckSkipList != null && !fsckSkipList.isEmpty()) {
233 return new LazyObjectIdSetFile(new File(fsckSkipList));
234 }
235 return null;
236 }
237
238
239
240
241
242
243
244 public boolean isAllowTipSha1InWant() {
245 return allowTipSha1InWant;
246 }
247
248
249
250
251
252
253
254 public boolean isAllowReachableSha1InWant() {
255 return allowReachableSha1InWant;
256 }
257
258
259
260
261
262 public boolean isAllowFilter() {
263 return allowFilter;
264 }
265
266
267
268
269
270 public boolean isAllowRefInWant() {
271 return allowRefInWant;
272 }
273
274
275
276
277
278
279
280
281
282 public RefFilter getRefFilter() {
283 if (hideRefs.length == 0)
284 return RefFilter.DEFAULT;
285
286 return new RefFilter() {
287 @Override
288 public Map<String, Ref> filter(Map<String, Ref> refs) {
289 Map<String, Ref> result = new HashMap<>();
290 for (Map.Entry<String, Ref> e : refs.entrySet()) {
291 boolean add = true;
292 for (String hide : hideRefs) {
293 if (e.getKey().equals(hide) || prefixMatch(hide, e.getKey())) {
294 add = false;
295 break;
296 }
297 }
298 if (add)
299 result.put(e.getKey(), e.getValue());
300 }
301 return result;
302 }
303
304 private boolean prefixMatch(String p, String s) {
305 return p.charAt(p.length() - 1) == '/' && s.startsWith(p);
306 }
307 };
308 }
309
310 static class FsckKeyNameHolder {
311 private static final Map<String, ObjectChecker.ErrorType> errors;
312
313 static {
314 errors = new HashMap<>();
315 for (ObjectChecker.ErrorType m : ObjectChecker.ErrorType.values()) {
316 errors.put(keyNameFor(m.name()), m);
317 }
318 }
319
320 @Nullable
321 static ObjectChecker.ErrorType parse(String key) {
322 return errors.get(toLowerCase(key));
323 }
324
325 private static String keyNameFor(String name) {
326 StringBuilder r = new StringBuilder(name.length());
327 for (int i = 0; i < name.length(); i++) {
328 char c = name.charAt(i);
329 if (c != '_') {
330 r.append(c);
331 }
332 }
333 return toLowerCase(r.toString());
334 }
335
336 private FsckKeyNameHolder() {
337 }
338 }
339 }