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