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.transport.GitProtocolConstants.CAPABILITY_ATOMIC;
47 import static org.eclipse.jgit.transport.GitProtocolConstants.CAPABILITY_DELETE_REFS;
48 import static org.eclipse.jgit.transport.GitProtocolConstants.CAPABILITY_OFS_DELTA;
49 import static org.eclipse.jgit.transport.GitProtocolConstants.CAPABILITY_PUSH_OPTIONS;
50 import static org.eclipse.jgit.transport.GitProtocolConstants.CAPABILITY_QUIET;
51 import static org.eclipse.jgit.transport.GitProtocolConstants.CAPABILITY_REPORT_STATUS;
52 import static org.eclipse.jgit.transport.GitProtocolConstants.CAPABILITY_SIDE_BAND_64K;
53 import static org.eclipse.jgit.transport.GitProtocolConstants.OPTION_AGENT;
54 import static org.eclipse.jgit.transport.SideBandOutputStream.CH_DATA;
55 import static org.eclipse.jgit.transport.SideBandOutputStream.CH_ERROR;
56 import static org.eclipse.jgit.transport.SideBandOutputStream.CH_PROGRESS;
57 import static org.eclipse.jgit.transport.SideBandOutputStream.MAX_BUF;
58
59 import java.io.EOFException;
60 import java.io.IOException;
61 import java.io.InputStream;
62 import java.io.OutputStream;
63 import java.text.MessageFormat;
64 import java.util.ArrayList;
65 import java.util.Collections;
66 import java.util.HashSet;
67 import java.util.List;
68 import java.util.Map;
69 import java.util.Set;
70 import java.util.concurrent.TimeUnit;
71
72 import org.eclipse.jgit.annotations.Nullable;
73 import org.eclipse.jgit.errors.InvalidObjectIdException;
74 import org.eclipse.jgit.errors.MissingObjectException;
75 import org.eclipse.jgit.errors.PackProtocolException;
76 import org.eclipse.jgit.errors.TooLargePackException;
77 import org.eclipse.jgit.internal.JGitText;
78 import org.eclipse.jgit.internal.storage.file.PackLock;
79 import org.eclipse.jgit.lib.BatchRefUpdate;
80 import org.eclipse.jgit.lib.Config;
81 import org.eclipse.jgit.lib.Constants;
82 import org.eclipse.jgit.lib.NullProgressMonitor;
83 import org.eclipse.jgit.lib.ObjectChecker;
84 import org.eclipse.jgit.lib.ObjectId;
85 import org.eclipse.jgit.lib.ObjectIdSubclassMap;
86 import org.eclipse.jgit.lib.ObjectInserter;
87 import org.eclipse.jgit.lib.PersonIdent;
88 import org.eclipse.jgit.lib.ProgressMonitor;
89 import org.eclipse.jgit.lib.Ref;
90 import org.eclipse.jgit.lib.Repository;
91 import org.eclipse.jgit.revwalk.ObjectWalk;
92 import org.eclipse.jgit.revwalk.RevBlob;
93 import org.eclipse.jgit.revwalk.RevCommit;
94 import org.eclipse.jgit.revwalk.RevFlag;
95 import org.eclipse.jgit.revwalk.RevObject;
96 import org.eclipse.jgit.revwalk.RevSort;
97 import org.eclipse.jgit.revwalk.RevTree;
98 import org.eclipse.jgit.revwalk.RevWalk;
99 import org.eclipse.jgit.transport.PacketLineIn.InputOverLimitIOException;
100 import org.eclipse.jgit.transport.ReceiveCommand.Result;
101 import org.eclipse.jgit.util.io.InterruptTimer;
102 import org.eclipse.jgit.util.io.LimitedInputStream;
103 import org.eclipse.jgit.util.io.TimeoutInputStream;
104 import org.eclipse.jgit.util.io.TimeoutOutputStream;
105
106
107
108
109
110
111
112
113 public abstract class BaseReceivePack {
114
115 public static class FirstLine {
116 private final String line;
117 private final Set<String> capabilities;
118
119
120
121
122
123
124
125 public FirstLine(String line) {
126 final HashSet<String> caps = new HashSet<>();
127 final int nul = line.indexOf('\0');
128 if (nul >= 0) {
129 for (String c : line.substring(nul + 1).split(" "))
130 caps.add(c);
131 this.line = line.substring(0, nul);
132 } else
133 this.line = line;
134 this.capabilities = Collections.unmodifiableSet(caps);
135 }
136
137
138 public String getLine() {
139 return line;
140 }
141
142
143 public Set<String> getCapabilities() {
144 return capabilities;
145 }
146 }
147
148
149 private final Repository db;
150
151
152 private final RevWalk walk;
153
154
155
156
157
158
159
160
161
162
163
164
165 private boolean biDirectionalPipe = true;
166
167
168 private boolean expectDataAfterPackFooter;
169
170
171 private ObjectChecker objectChecker;
172
173
174 private boolean allowCreates;
175
176
177 private boolean allowAnyDeletes;
178 private boolean allowBranchDeletes;
179
180
181 private boolean allowNonFastForwards;
182
183
184 private boolean allowPushOptions;
185
186
187
188
189
190 private boolean atomic;
191
192 private boolean allowOfsDelta;
193 private boolean allowQuiet = true;
194
195
196 private PersonIdent refLogIdent;
197
198
199 private AdvertiseRefsHook advertiseRefsHook;
200
201
202 private RefFilter refFilter;
203
204
205 private int timeout;
206
207
208 private InterruptTimer timer;
209
210 private TimeoutInputStream timeoutIn;
211
212
213
214 private OutputStream origOut;
215
216
217 protected InputStream rawIn;
218
219
220 protected OutputStream rawOut;
221
222
223 protected OutputStream msgOut;
224 private SideBandOutputStream errOut;
225
226
227 protected PacketLineIn pckIn;
228
229
230 protected PacketLineOut pckOut;
231
232 private final MessageOutputWrapper msgOutWrapper = new MessageOutputWrapper();
233
234 private PackParser parser;
235
236
237 private Map<String, Ref> refs;
238
239
240 private Set<ObjectId> advertisedHaves;
241
242
243 private Set<String> enabledCapabilities;
244 String userAgent;
245 private Set<ObjectId> clientShallowCommits;
246 private List<ReceiveCommand> commands;
247 private long maxCommandBytes;
248 private long maxDiscardBytes;
249
250 private StringBuilder advertiseError;
251
252
253 private boolean sideBand;
254
255 private boolean quiet;
256
257
258 private PackLock packLock;
259
260 private boolean checkReferencedIsReachable;
261
262
263 private long maxObjectSizeLimit;
264
265
266 private long maxPackSizeLimit = -1;
267
268
269 private Long packSize;
270
271 private PushCertificateParser pushCertificateParser;
272 private SignedPushConfig signedPushConfig;
273 private PushCertificate pushCert;
274 private ReceivedPackStatistics stats;
275
276
277
278
279
280
281
282
283
284
285 public PushCertificate getPushCertificate() {
286 return pushCert;
287 }
288
289
290
291
292
293
294
295
296
297
298
299 public void setPushCertificate(PushCertificate cert) {
300 pushCert = cert;
301 }
302
303
304
305
306
307
308
309 protected BaseReceivePack(Repository into) {
310 db = into;
311 walk = new RevWalk(db);
312
313 TransferConfig tc = db.getConfig().get(TransferConfig.KEY);
314 objectChecker = tc.newReceiveObjectChecker();
315
316 ReceiveConfig rc = db.getConfig().get(ReceiveConfig::new);
317 allowCreates = rc.allowCreates;
318 allowAnyDeletes = true;
319 allowBranchDeletes = rc.allowDeletes;
320 allowNonFastForwards = rc.allowNonFastForwards;
321 allowOfsDelta = rc.allowOfsDelta;
322 allowPushOptions = rc.allowPushOptions;
323 maxCommandBytes = rc.maxCommandBytes;
324 maxDiscardBytes = rc.maxDiscardBytes;
325 advertiseRefsHook = AdvertiseRefsHook.DEFAULT;
326 refFilter = RefFilter.DEFAULT;
327 advertisedHaves = new HashSet<>();
328 clientShallowCommits = new HashSet<>();
329 signedPushConfig = rc.signedPush;
330 }
331
332
333 protected static class ReceiveConfig {
334 final boolean allowCreates;
335 final boolean allowDeletes;
336 final boolean allowNonFastForwards;
337 final boolean allowOfsDelta;
338 final boolean allowPushOptions;
339 final long maxCommandBytes;
340 final long maxDiscardBytes;
341 final SignedPushConfig signedPush;
342
343 ReceiveConfig(Config config) {
344 allowCreates = true;
345 allowDeletes = !config.getBoolean("receive", "denydeletes", false);
346 allowNonFastForwards = !config.getBoolean("receive",
347 "denynonfastforwards", false);
348 allowOfsDelta = config.getBoolean("repack", "usedeltabaseoffset",
349 true);
350 allowPushOptions = config.getBoolean("receive", "pushoptions",
351 false);
352 maxCommandBytes = config.getLong("receive",
353 "maxCommandBytes",
354 3 << 20);
355 maxDiscardBytes = config.getLong("receive",
356 "maxCommandDiscardBytes",
357 -1);
358 signedPush = SignedPushConfig.KEY.parse(config);
359 }
360 }
361
362
363
364
365
366
367
368 class MessageOutputWrapper extends OutputStream {
369 @Override
370 public void write(int ch) {
371 if (msgOut != null) {
372 try {
373 msgOut.write(ch);
374 } catch (IOException e) {
375
376 }
377 }
378 }
379
380 @Override
381 public void write(byte[] b, int off, int len) {
382 if (msgOut != null) {
383 try {
384 msgOut.write(b, off, len);
385 } catch (IOException e) {
386
387 }
388 }
389 }
390
391 @Override
392 public void write(byte[] b) {
393 write(b, 0, b.length);
394 }
395
396 @Override
397 public void flush() {
398 if (msgOut != null) {
399 try {
400 msgOut.flush();
401 } catch (IOException e) {
402
403 }
404 }
405 }
406 }
407
408
409
410
411
412
413 protected abstract String getLockMessageProcessName();
414
415
416
417
418
419
420 public final Repository getRepository() {
421 return db;
422 }
423
424
425
426
427
428
429 public final RevWalk getRevWalk() {
430 return walk;
431 }
432
433
434
435
436
437
438
439 public final Map<String, Ref> getAdvertisedRefs() {
440 return refs;
441 }
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460 public void setAdvertisedRefs(Map<String, Ref> allRefs, Set<ObjectId> additionalHaves) {
461 refs = allRefs != null ? allRefs : db.getAllRefs();
462 refs = refFilter.filter(refs);
463 advertisedHaves.clear();
464
465 Ref head = refs.get(Constants.HEAD);
466 if (head != null && head.isSymbolic())
467 refs.remove(Constants.HEAD);
468
469 for (Ref ref : refs.values()) {
470 if (ref.getObjectId() != null)
471 advertisedHaves.add(ref.getObjectId());
472 }
473 if (additionalHaves != null)
474 advertisedHaves.addAll(additionalHaves);
475 else
476 advertisedHaves.addAll(db.getAdditionalHaves());
477 }
478
479
480
481
482
483
484
485
486 public final Set<ObjectId> getAdvertisedObjects() {
487 return advertisedHaves;
488 }
489
490
491
492
493
494
495
496
497
498 public boolean isCheckReferencedObjectsAreReachable() {
499 return checkReferencedIsReachable;
500 }
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523 public void setCheckReferencedObjectsAreReachable(boolean b) {
524 this.checkReferencedIsReachable = b;
525 }
526
527
528
529
530
531
532
533
534 public boolean isBiDirectionalPipe() {
535 return biDirectionalPipe;
536 }
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551 public void setBiDirectionalPipe(boolean twoWay) {
552 biDirectionalPipe = twoWay;
553 }
554
555
556
557
558
559
560 public boolean isExpectDataAfterPackFooter() {
561 return expectDataAfterPackFooter;
562 }
563
564
565
566
567
568
569
570
571 public void setExpectDataAfterPackFooter(boolean e) {
572 expectDataAfterPackFooter = e;
573 }
574
575
576
577
578
579
580
581
582
583 public boolean isCheckReceivedObjects() {
584 return objectChecker != null;
585 }
586
587
588
589
590
591
592
593
594
595 public void setCheckReceivedObjects(boolean check) {
596 if (check && objectChecker == null)
597 setObjectChecker(new ObjectChecker());
598 else if (!check && objectChecker != null)
599 setObjectChecker(null);
600 }
601
602
603
604
605
606
607
608
609
610 public void setObjectChecker(ObjectChecker impl) {
611 objectChecker = impl;
612 }
613
614
615
616
617
618
619 public boolean isAllowCreates() {
620 return allowCreates;
621 }
622
623
624
625
626
627
628
629 public void setAllowCreates(boolean canCreate) {
630 allowCreates = canCreate;
631 }
632
633
634
635
636
637
638 public boolean isAllowDeletes() {
639 return allowAnyDeletes;
640 }
641
642
643
644
645
646
647
648 public void setAllowDeletes(boolean canDelete) {
649 allowAnyDeletes = canDelete;
650 }
651
652
653
654
655
656
657
658 public boolean isAllowBranchDeletes() {
659 return allowBranchDeletes;
660 }
661
662
663
664
665
666
667
668
669
670
671 public void setAllowBranchDeletes(boolean canDelete) {
672 allowBranchDeletes = canDelete;
673 }
674
675
676
677
678
679
680
681
682 public boolean isAllowNonFastForwards() {
683 return allowNonFastForwards;
684 }
685
686
687
688
689
690
691
692
693
694 public void setAllowNonFastForwards(boolean canRewind) {
695 allowNonFastForwards = canRewind;
696 }
697
698
699
700
701
702
703
704
705
706 public boolean isAtomic() {
707 return atomic;
708 }
709
710
711
712
713
714
715
716
717
718
719 public void setAtomic(boolean atomic) {
720 this.atomic = atomic;
721 }
722
723
724
725
726
727
728 public PersonIdent getRefLogIdent() {
729 return refLogIdent;
730 }
731
732
733
734
735
736
737
738
739
740
741
742
743
744 public void setRefLogIdent(PersonIdent pi) {
745 refLogIdent = pi;
746 }
747
748
749
750
751
752
753 public AdvertiseRefsHook getAdvertiseRefsHook() {
754 return advertiseRefsHook;
755 }
756
757
758
759
760
761
762 public RefFilter getRefFilter() {
763 return refFilter;
764 }
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781 public void setAdvertiseRefsHook(AdvertiseRefsHook advertiseRefsHook) {
782 if (advertiseRefsHook != null)
783 this.advertiseRefsHook = advertiseRefsHook;
784 else
785 this.advertiseRefsHook = AdvertiseRefsHook.DEFAULT;
786 }
787
788
789
790
791
792
793
794
795
796
797
798 public void setRefFilter(RefFilter refFilter) {
799 this.refFilter = refFilter != null ? refFilter : RefFilter.DEFAULT;
800 }
801
802
803
804
805
806
807 public int getTimeout() {
808 return timeout;
809 }
810
811
812
813
814
815
816
817
818
819 public void setTimeout(int seconds) {
820 timeout = seconds;
821 }
822
823
824
825
826
827
828
829
830 public void setMaxCommandBytes(long limit) {
831 maxCommandBytes = limit;
832 }
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851 public void setMaxCommandDiscardBytes(long limit) {
852 maxDiscardBytes = limit;
853 }
854
855
856
857
858
859
860
861
862
863
864 public void setMaxObjectSizeLimit(long limit) {
865 maxObjectSizeLimit = limit;
866 }
867
868
869
870
871
872
873
874
875
876
877 public void setMaxPackSizeLimit(long limit) {
878 if (limit < 0)
879 throw new IllegalArgumentException(MessageFormat.format(
880 JGitText.get().receivePackInvalidLimit, Long.valueOf(limit)));
881 maxPackSizeLimit = limit;
882 }
883
884
885
886
887
888
889
890
891
892
893
894
895 public boolean isSideBand() throws RequestNotYetReadException {
896 checkRequestWasRead();
897 return enabledCapabilities.contains(CAPABILITY_SIDE_BAND_64K);
898 }
899
900
901
902
903
904
905
906 public boolean isAllowQuiet() {
907 return allowQuiet;
908 }
909
910
911
912
913
914
915
916
917
918
919
920 public void setAllowQuiet(boolean allow) {
921 allowQuiet = allow;
922 }
923
924
925
926
927
928
929
930 public boolean isAllowPushOptions() {
931 return allowPushOptions;
932 }
933
934
935
936
937
938
939
940
941 public void setAllowPushOptions(boolean allow) {
942 allowPushOptions = allow;
943 }
944
945
946
947
948
949
950
951
952
953
954
955
956 public boolean isQuiet() throws RequestNotYetReadException {
957 checkRequestWasRead();
958 return quiet;
959 }
960
961
962
963
964
965
966
967
968
969
970 public void setSignedPushConfig(SignedPushConfig cfg) {
971 signedPushConfig = cfg;
972 }
973
974 private PushCertificateParser getPushCertificateParser() {
975 if (pushCertificateParser == null) {
976 pushCertificateParser = new PushCertificateParser(db, signedPushConfig);
977 }
978 return pushCertificateParser;
979 }
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996 public String getPeerUserAgent() {
997 return UserAgent.getAgent(enabledCapabilities, userAgent);
998 }
999
1000
1001
1002
1003
1004
1005 public List<ReceiveCommand> getAllCommands() {
1006 return Collections.unmodifiableList(commands);
1007 }
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034 public void sendError(String what) {
1035 if (refs == null) {
1036 if (advertiseError == null)
1037 advertiseError = new StringBuilder();
1038 advertiseError.append(what).append('\n');
1039 } else {
1040 msgOutWrapper.write(Constants.encode("error: " + what + "\n"));
1041 }
1042 }
1043
1044 private void fatalError(String msg) {
1045 if (errOut != null) {
1046 try {
1047 errOut.write(Constants.encode(msg));
1048 errOut.flush();
1049 } catch (IOException e) {
1050
1051 }
1052 } else {
1053 sendError(msg);
1054 }
1055 }
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067 public void sendMessage(String what) {
1068 msgOutWrapper.write(Constants.encode(what + "\n"));
1069 }
1070
1071
1072
1073
1074
1075
1076 public OutputStream getMessageOutputStream() {
1077 return msgOutWrapper;
1078 }
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090 public long getPackSize() {
1091 if (packSize != null)
1092 return packSize.longValue();
1093 throw new IllegalStateException(JGitText.get().packSizeNotSetYet);
1094 }
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104 protected Set<ObjectId> getClientShallowCommits() {
1105 return clientShallowCommits;
1106 }
1107
1108
1109
1110
1111
1112
1113 protected boolean hasCommands() {
1114 return !commands.isEmpty();
1115 }
1116
1117
1118
1119
1120
1121
1122 protected boolean hasError() {
1123 return advertiseError != null;
1124 }
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143 protected void init(final InputStream input, final OutputStream output,
1144 final OutputStream messages) {
1145 origOut = output;
1146 rawIn = input;
1147 rawOut = output;
1148 msgOut = messages;
1149
1150 if (timeout > 0) {
1151 final Thread caller = Thread.currentThread();
1152 timer = new InterruptTimer(caller.getName() + "-Timer");
1153 timeoutIn = new TimeoutInputStream(rawIn, timer);
1154 TimeoutOutputStream o = new TimeoutOutputStream(rawOut, timer);
1155 timeoutIn.setTimeout(timeout * 1000);
1156 o.setTimeout(timeout * 1000);
1157 rawIn = timeoutIn;
1158 rawOut = o;
1159 }
1160
1161 pckIn = new PacketLineIn(rawIn);
1162 pckOut = new PacketLineOut(rawOut);
1163 pckOut.setFlushOnEnd(false);
1164
1165 enabledCapabilities = new HashSet<>();
1166 commands = new ArrayList<>();
1167 }
1168
1169
1170
1171
1172
1173
1174 protected Map<String, Ref> getAdvertisedOrDefaultRefs() {
1175 if (refs == null)
1176 setAdvertisedRefs(null, null);
1177 return refs;
1178 }
1179
1180
1181
1182
1183
1184
1185
1186 protected void receivePackAndCheckConnectivity() throws IOException {
1187 receivePack();
1188 if (needCheckConnectivity())
1189 checkConnectivity();
1190 parser = null;
1191 }
1192
1193
1194
1195
1196
1197
1198
1199 protected void unlockPack() throws IOException {
1200 if (packLock != null) {
1201 packLock.unlock();
1202 packLock = null;
1203 }
1204 }
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216 public void sendAdvertisedRefs(RefAdvertiser adv)
1217 throws IOException, ServiceMayNotContinueException {
1218 if (advertiseError != null) {
1219 adv.writeOne("ERR " + advertiseError);
1220 return;
1221 }
1222
1223 try {
1224 advertiseRefsHook.advertiseRefs(this);
1225 } catch (ServiceMayNotContinueException fail) {
1226 if (fail.getMessage() != null) {
1227 adv.writeOne("ERR " + fail.getMessage());
1228 fail.setOutput();
1229 }
1230 throw fail;
1231 }
1232
1233 adv.init(db);
1234 adv.advertiseCapability(CAPABILITY_SIDE_BAND_64K);
1235 adv.advertiseCapability(CAPABILITY_DELETE_REFS);
1236 adv.advertiseCapability(CAPABILITY_REPORT_STATUS);
1237 if (allowQuiet)
1238 adv.advertiseCapability(CAPABILITY_QUIET);
1239 String nonce = getPushCertificateParser().getAdvertiseNonce();
1240 if (nonce != null) {
1241 adv.advertiseCapability(nonce);
1242 }
1243 if (db.getRefDatabase().performsAtomicTransactions())
1244 adv.advertiseCapability(CAPABILITY_ATOMIC);
1245 if (allowOfsDelta)
1246 adv.advertiseCapability(CAPABILITY_OFS_DELTA);
1247 if (allowPushOptions) {
1248 adv.advertiseCapability(CAPABILITY_PUSH_OPTIONS);
1249 }
1250 adv.advertiseCapability(OPTION_AGENT, UserAgent.get());
1251 adv.send(getAdvertisedOrDefaultRefs());
1252 for (ObjectId obj : advertisedHaves)
1253 adv.advertiseHave(obj);
1254 if (adv.isEmpty())
1255 adv.advertiseId(ObjectId.zeroId(), "capabilities^{}");
1256 adv.end();
1257 }
1258
1259
1260
1261
1262
1263
1264
1265
1266 @Nullable
1267 public ReceivedPackStatistics getReceivedPackStatistics() {
1268 return stats;
1269 }
1270
1271
1272
1273
1274
1275
1276 protected void recvCommands() throws IOException {
1277 PacketLineIn pck = maxCommandBytes > 0
1278 ? new PacketLineIn(rawIn, maxCommandBytes)
1279 : pckIn;
1280 PushCertificateParser certParser = getPushCertificateParser();
1281 boolean firstPkt = true;
1282 try {
1283 for (;;) {
1284 String line;
1285 try {
1286 line = pck.readString();
1287 } catch (EOFException eof) {
1288 if (commands.isEmpty())
1289 return;
1290 throw eof;
1291 }
1292 if (line == PacketLineIn.END) {
1293 break;
1294 }
1295
1296 if (line.length() >= 48 && line.startsWith("shallow ")) {
1297 parseShallow(line.substring(8, 48));
1298 continue;
1299 }
1300
1301 if (firstPkt) {
1302 firstPkt = false;
1303 FirstLine firstLine = new FirstLine(line);
1304 enabledCapabilities = firstLine.getCapabilities();
1305 line = firstLine.getLine();
1306 enableCapabilities();
1307
1308 if (line.equals(GitProtocolConstants.OPTION_PUSH_CERT)) {
1309 certParser.receiveHeader(pck, !isBiDirectionalPipe());
1310 continue;
1311 }
1312 }
1313
1314 if (line.equals(PushCertificateParser.BEGIN_SIGNATURE)) {
1315 certParser.receiveSignature(pck);
1316 continue;
1317 }
1318
1319 ReceiveCommand cmd = parseCommand(line);
1320 if (cmd.getRefName().equals(Constants.HEAD)) {
1321 cmd.setResult(Result.REJECTED_CURRENT_BRANCH);
1322 } else {
1323 cmd.setRef(refs.get(cmd.getRefName()));
1324 }
1325 commands.add(cmd);
1326 if (certParser.enabled()) {
1327 certParser.addCommand(cmd);
1328 }
1329 }
1330 pushCert = certParser.build();
1331 if (hasCommands()) {
1332 readPostCommands(pck);
1333 }
1334 } catch (PackProtocolException e) {
1335 discardCommands();
1336 fatalError(e.getMessage());
1337 throw e;
1338 } catch (InputOverLimitIOException e) {
1339 String msg = JGitText.get().tooManyCommands;
1340 discardCommands();
1341 fatalError(msg);
1342 throw new PackProtocolException(msg);
1343 }
1344 }
1345
1346 private void discardCommands() {
1347 if (sideBand) {
1348 long max = maxDiscardBytes;
1349 if (max < 0) {
1350 max = Math.max(3 * maxCommandBytes, 3L << 20);
1351 }
1352 try {
1353 new PacketLineIn(rawIn, max).discardUntilEnd();
1354 } catch (IOException e) {
1355
1356 }
1357 }
1358 }
1359
1360 private void parseShallow(String idStr) throws PackProtocolException {
1361 ObjectId id;
1362 try {
1363 id = ObjectId.fromString(idStr);
1364 } catch (InvalidObjectIdException e) {
1365 throw new PackProtocolException(e.getMessage(), e);
1366 }
1367 clientShallowCommits.add(id);
1368 }
1369
1370 static ReceiveCommand parseCommand(String line) throws PackProtocolException {
1371 if (line == null || line.length() < 83) {
1372 throw new PackProtocolException(
1373 JGitText.get().errorInvalidProtocolWantedOldNewRef);
1374 }
1375 String oldStr = line.substring(0, 40);
1376 String newStr = line.substring(41, 81);
1377 ObjectId oldId, newId;
1378 try {
1379 oldId = ObjectId.fromString(oldStr);
1380 newId = ObjectId.fromString(newStr);
1381 } catch (InvalidObjectIdException e) {
1382 throw new PackProtocolException(
1383 JGitText.get().errorInvalidProtocolWantedOldNewRef, e);
1384 }
1385 String name = line.substring(82);
1386 if (!Repository.isValidRefName(name)) {
1387 throw new PackProtocolException(
1388 JGitText.get().errorInvalidProtocolWantedOldNewRef);
1389 }
1390 return new ReceiveCommand(oldId, newId, name);
1391 }
1392
1393
1394
1395
1396
1397
1398
1399 void readPostCommands(PacketLineIn in) throws IOException {
1400
1401 }
1402
1403
1404
1405
1406 protected void enableCapabilities() {
1407 sideBand = isCapabilityEnabled(CAPABILITY_SIDE_BAND_64K);
1408 quiet = allowQuiet && isCapabilityEnabled(CAPABILITY_QUIET);
1409 if (sideBand) {
1410 OutputStream out = rawOut;
1411
1412 rawOut = new SideBandOutputStream(CH_DATA, MAX_BUF, out);
1413 msgOut = new SideBandOutputStream(CH_PROGRESS, MAX_BUF, out);
1414 errOut = new SideBandOutputStream(CH_ERROR, MAX_BUF, out);
1415
1416 pckOut = new PacketLineOut(rawOut);
1417 pckOut.setFlushOnEnd(false);
1418 }
1419 }
1420
1421
1422
1423
1424
1425
1426
1427
1428 protected boolean isCapabilityEnabled(String name) {
1429 return enabledCapabilities.contains(name);
1430 }
1431
1432 void checkRequestWasRead() {
1433 if (enabledCapabilities == null)
1434 throw new RequestNotYetReadException();
1435 }
1436
1437
1438
1439
1440
1441
1442 protected boolean needPack() {
1443 for (ReceiveCommand cmd : commands) {
1444 if (cmd.getType() != ReceiveCommand.Type.DELETE)
1445 return true;
1446 }
1447 return false;
1448 }
1449
1450
1451
1452
1453
1454
1455
1456 private void receivePack() throws IOException {
1457
1458
1459
1460
1461 if (timeoutIn != null)
1462 timeoutIn.setTimeout(10 * timeout * 1000);
1463
1464 ProgressMonitor receiving = NullProgressMonitor.INSTANCE;
1465 ProgressMonitor resolving = NullProgressMonitor.INSTANCE;
1466 if (sideBand && !quiet)
1467 resolving = new SideBandProgressMonitor(msgOut);
1468
1469 try (ObjectInserter ins = db.newObjectInserter()) {
1470 String lockMsg = "jgit receive-pack";
1471 if (getRefLogIdent() != null)
1472 lockMsg += " from " + getRefLogIdent().toExternalString();
1473
1474 parser = ins.newPackParser(packInputStream());
1475 parser.setAllowThin(true);
1476 parser.setNeedNewObjectIds(checkReferencedIsReachable);
1477 parser.setNeedBaseObjectIds(checkReferencedIsReachable);
1478 parser.setCheckEofAfterPackFooter(!biDirectionalPipe
1479 && !isExpectDataAfterPackFooter());
1480 parser.setExpectDataAfterPackFooter(isExpectDataAfterPackFooter());
1481 parser.setObjectChecker(objectChecker);
1482 parser.setLockMessage(lockMsg);
1483 parser.setMaxObjectSizeLimit(maxObjectSizeLimit);
1484 packLock = parser.parse(receiving, resolving);
1485 packSize = Long.valueOf(parser.getPackSize());
1486 stats = parser.getReceivedPackStatistics();
1487 ins.flush();
1488 }
1489
1490 if (timeoutIn != null)
1491 timeoutIn.setTimeout(timeout * 1000);
1492 }
1493
1494 private InputStream packInputStream() {
1495 InputStream packIn = rawIn;
1496 if (maxPackSizeLimit >= 0) {
1497 packIn = new LimitedInputStream(packIn, maxPackSizeLimit) {
1498 @Override
1499 protected void limitExceeded() throws TooLargePackException {
1500 throw new TooLargePackException(limit);
1501 }
1502 };
1503 }
1504 return packIn;
1505 }
1506
1507 private boolean needCheckConnectivity() {
1508 return isCheckReceivedObjects()
1509 || isCheckReferencedObjectsAreReachable()
1510 || !getClientShallowCommits().isEmpty();
1511 }
1512
1513 private void checkConnectivity() throws IOException {
1514 ObjectIdSubclassMap<ObjectId> baseObjects = null;
1515 ObjectIdSubclassMap<ObjectId> providedObjects = null;
1516 ProgressMonitor checking = NullProgressMonitor.INSTANCE;
1517 if (sideBand && !quiet) {
1518 SideBandProgressMonitor m = new SideBandProgressMonitor(msgOut);
1519 m.setDelayStart(750, TimeUnit.MILLISECONDS);
1520 checking = m;
1521 }
1522
1523 if (checkReferencedIsReachable) {
1524 baseObjects = parser.getBaseObjectIds();
1525 providedObjects = parser.getNewObjectIds();
1526 }
1527 parser = null;
1528
1529 try (ObjectWalk ow = new ObjectWalk(db)) {
1530 if (baseObjects != null) {
1531 ow.sort(RevSort.TOPO);
1532 if (!baseObjects.isEmpty())
1533 ow.sort(RevSort.BOUNDARY, true);
1534 }
1535
1536 for (ReceiveCommand cmd : commands) {
1537 if (cmd.getResult() != Result.NOT_ATTEMPTED)
1538 continue;
1539 if (cmd.getType() == ReceiveCommand.Type.DELETE)
1540 continue;
1541 ow.markStart(ow.parseAny(cmd.getNewId()));
1542 }
1543 for (ObjectId have : advertisedHaves) {
1544 RevObject o = ow.parseAny(have);
1545 ow.markUninteresting(o);
1546
1547 if (baseObjects != null && !baseObjects.isEmpty()) {
1548 o = ow.peel(o);
1549 if (o instanceof RevCommit)
1550 o = ((RevCommit) o).getTree();
1551 if (o instanceof RevTree)
1552 ow.markUninteresting(o);
1553 }
1554 }
1555
1556 checking.beginTask(JGitText.get().countingObjects,
1557 ProgressMonitor.UNKNOWN);
1558 RevCommit c;
1559 while ((c = ow.next()) != null) {
1560 checking.update(1);
1561 if (providedObjects != null
1562 && !c.has(RevFlag.UNINTERESTING)
1563 && !providedObjects.contains(c))
1564 throw new MissingObjectException(c, Constants.TYPE_COMMIT);
1565 }
1566
1567 RevObject o;
1568 while ((o = ow.nextObject()) != null) {
1569 checking.update(1);
1570 if (o.has(RevFlag.UNINTERESTING))
1571 continue;
1572
1573 if (providedObjects != null) {
1574 if (providedObjects.contains(o))
1575 continue;
1576 else
1577 throw new MissingObjectException(o, o.getType());
1578 }
1579
1580 if (o instanceof RevBlob && !db.hasObject(o))
1581 throw new MissingObjectException(o, Constants.TYPE_BLOB);
1582 }
1583 checking.endTask();
1584
1585 if (baseObjects != null) {
1586 for (ObjectId id : baseObjects) {
1587 o = ow.parseAny(id);
1588 if (!o.has(RevFlag.UNINTERESTING))
1589 throw new MissingObjectException(o, o.getType());
1590 }
1591 }
1592 }
1593 }
1594
1595
1596
1597
1598 protected void validateCommands() {
1599 for (ReceiveCommand cmd : commands) {
1600 final Ref ref = cmd.getRef();
1601 if (cmd.getResult() != Result.NOT_ATTEMPTED)
1602 continue;
1603
1604 if (cmd.getType() == ReceiveCommand.Type.DELETE) {
1605 if (!isAllowDeletes()) {
1606
1607 cmd.setResult(Result.REJECTED_NODELETE);
1608 continue;
1609 }
1610 if (!isAllowBranchDeletes()
1611 && ref.getName().startsWith(Constants.R_HEADS)) {
1612
1613 cmd.setResult(Result.REJECTED_NODELETE);
1614 continue;
1615 }
1616 }
1617
1618 if (cmd.getType() == ReceiveCommand.Type.CREATE) {
1619 if (!isAllowCreates()) {
1620 cmd.setResult(Result.REJECTED_NOCREATE);
1621 continue;
1622 }
1623
1624 if (ref != null && !isAllowNonFastForwards()) {
1625
1626
1627
1628 cmd.setResult(Result.REJECTED_NONFASTFORWARD);
1629 continue;
1630 }
1631
1632 if (ref != null) {
1633
1634
1635
1636 cmd.setResult(Result.REJECTED_OTHER_REASON,
1637 JGitText.get().refAlreadyExists);
1638 continue;
1639 }
1640 }
1641
1642 if (cmd.getType() == ReceiveCommand.Type.DELETE && ref != null) {
1643 ObjectId id = ref.getObjectId();
1644 if (id == null) {
1645 id = ObjectId.zeroId();
1646 }
1647 if (!ObjectId.zeroId().equals(cmd.getOldId())
1648 && !id.equals(cmd.getOldId())) {
1649
1650
1651
1652
1653 cmd.setResult(Result.REJECTED_OTHER_REASON,
1654 JGitText.get().invalidOldIdSent);
1655 continue;
1656 }
1657 }
1658
1659 if (cmd.getType() == ReceiveCommand.Type.UPDATE) {
1660 if (ref == null) {
1661
1662
1663 cmd.setResult(Result.REJECTED_OTHER_REASON, JGitText.get().noSuchRef);
1664 continue;
1665 }
1666 ObjectId id = ref.getObjectId();
1667 if (id == null) {
1668
1669 cmd.setResult(Result.REJECTED_OTHER_REASON,
1670 JGitText.get().cannotUpdateUnbornBranch);
1671 continue;
1672 }
1673
1674 if (!id.equals(cmd.getOldId())) {
1675
1676
1677
1678 cmd.setResult(Result.REJECTED_OTHER_REASON,
1679 JGitText.get().invalidOldIdSent);
1680 continue;
1681 }
1682
1683
1684
1685 RevObject oldObj, newObj;
1686 try {
1687 oldObj = walk.parseAny(cmd.getOldId());
1688 } catch (IOException e) {
1689 cmd.setResult(Result.REJECTED_MISSING_OBJECT, cmd
1690 .getOldId().name());
1691 continue;
1692 }
1693
1694 try {
1695 newObj = walk.parseAny(cmd.getNewId());
1696 } catch (IOException e) {
1697 cmd.setResult(Result.REJECTED_MISSING_OBJECT, cmd
1698 .getNewId().name());
1699 continue;
1700 }
1701
1702 if (oldObj instanceof RevCommit && newObj instanceof RevCommit) {
1703 try {
1704 if (walk.isMergedInto((RevCommit) oldObj,
1705 (RevCommit) newObj))
1706 cmd.setTypeFastForwardUpdate();
1707 else
1708 cmd.setType(ReceiveCommand.Type.UPDATE_NONFASTFORWARD);
1709 } catch (MissingObjectException e) {
1710 cmd.setResult(Result.REJECTED_MISSING_OBJECT, e
1711 .getMessage());
1712 } catch (IOException e) {
1713 cmd.setResult(Result.REJECTED_OTHER_REASON);
1714 }
1715 } else {
1716 cmd.setType(ReceiveCommand.Type.UPDATE_NONFASTFORWARD);
1717 }
1718
1719 if (cmd.getType() == ReceiveCommand.Type.UPDATE_NONFASTFORWARD
1720 && !isAllowNonFastForwards()) {
1721 cmd.setResult(Result.REJECTED_NONFASTFORWARD);
1722 continue;
1723 }
1724 }
1725
1726 if (!cmd.getRefName().startsWith(Constants.R_REFS)
1727 || !Repository.isValidRefName(cmd.getRefName())) {
1728 cmd.setResult(Result.REJECTED_OTHER_REASON, JGitText.get().funnyRefname);
1729 }
1730 }
1731 }
1732
1733
1734
1735
1736
1737
1738
1739 protected boolean anyRejects() {
1740 for (ReceiveCommand cmd : commands) {
1741 if (cmd.getResult() != Result.NOT_ATTEMPTED && cmd.getResult() != Result.OK)
1742 return true;
1743 }
1744 return false;
1745 }
1746
1747
1748
1749
1750
1751
1752 protected void failPendingCommands() {
1753 ReceiveCommand.abort(commands);
1754 }
1755
1756
1757
1758
1759
1760
1761
1762
1763
1764 protected List<ReceiveCommand> filterCommands(Result want) {
1765 return ReceiveCommand.filter(commands, want);
1766 }
1767
1768
1769
1770
1771 protected void executeCommands() {
1772 List<ReceiveCommand> toApply = filterCommands(Result.NOT_ATTEMPTED);
1773 if (toApply.isEmpty())
1774 return;
1775
1776 ProgressMonitor updating = NullProgressMonitor.INSTANCE;
1777 if (sideBand) {
1778 SideBandProgressMonitor pm = new SideBandProgressMonitor(msgOut);
1779 pm.setDelayStart(250, TimeUnit.MILLISECONDS);
1780 updating = pm;
1781 }
1782
1783 BatchRefUpdate batch = db.getRefDatabase().newBatchUpdate();
1784 batch.setAllowNonFastForwards(isAllowNonFastForwards());
1785 batch.setAtomic(isAtomic());
1786 batch.setRefLogIdent(getRefLogIdent());
1787 batch.setRefLogMessage("push", true);
1788 batch.addCommand(toApply);
1789 try {
1790 batch.setPushCertificate(getPushCertificate());
1791 batch.execute(walk, updating);
1792 } catch (IOException err) {
1793 for (ReceiveCommand cmd : toApply) {
1794 if (cmd.getResult() == Result.NOT_ATTEMPTED)
1795 cmd.reject(err);
1796 }
1797 }
1798 }
1799
1800
1801
1802
1803
1804
1805
1806
1807
1808
1809
1810
1811
1812
1813 protected void sendStatusReport(final boolean forClient,
1814 final Throwable unpackError, final Reporter out) throws IOException {
1815 if (unpackError != null) {
1816 out.sendString("unpack error " + unpackError.getMessage());
1817 if (forClient) {
1818 for (ReceiveCommand cmd : commands) {
1819 out.sendString("ng " + cmd.getRefName()
1820 + " n/a (unpacker error)");
1821 }
1822 }
1823 return;
1824 }
1825
1826 if (forClient)
1827 out.sendString("unpack ok");
1828 for (ReceiveCommand cmd : commands) {
1829 if (cmd.getResult() == Result.OK) {
1830 if (forClient)
1831 out.sendString("ok " + cmd.getRefName());
1832 continue;
1833 }
1834
1835 final StringBuilder r = new StringBuilder();
1836 if (forClient)
1837 r.append("ng ").append(cmd.getRefName()).append(" ");
1838 else
1839 r.append(" ! [rejected] ").append(cmd.getRefName()).append(" (");
1840
1841 switch (cmd.getResult()) {
1842 case NOT_ATTEMPTED:
1843 r.append("server bug; ref not processed");
1844 break;
1845
1846 case REJECTED_NOCREATE:
1847 r.append("creation prohibited");
1848 break;
1849
1850 case REJECTED_NODELETE:
1851 r.append("deletion prohibited");
1852 break;
1853
1854 case REJECTED_NONFASTFORWARD:
1855 r.append("non-fast forward");
1856 break;
1857
1858 case REJECTED_CURRENT_BRANCH:
1859 r.append("branch is currently checked out");
1860 break;
1861
1862 case REJECTED_MISSING_OBJECT:
1863 if (cmd.getMessage() == null)
1864 r.append("missing object(s)");
1865 else if (cmd.getMessage().length() == Constants.OBJECT_ID_STRING_LENGTH) {
1866 r.append("object ");
1867 r.append(cmd.getMessage());
1868 r.append(" missing");
1869 } else
1870 r.append(cmd.getMessage());
1871 break;
1872
1873 case REJECTED_OTHER_REASON:
1874 if (cmd.getMessage() == null)
1875 r.append("unspecified reason");
1876 else
1877 r.append(cmd.getMessage());
1878 break;
1879
1880 case LOCK_FAILURE:
1881 r.append("failed to lock");
1882 break;
1883
1884 case OK:
1885
1886 continue;
1887 }
1888 if (!forClient)
1889 r.append(")");
1890 out.sendString(r.toString());
1891 }
1892 }
1893
1894
1895
1896
1897
1898
1899 protected void close() throws IOException {
1900 if (sideBand) {
1901
1902
1903
1904
1905
1906
1907 ((SideBandOutputStream) msgOut).flushBuffer();
1908 ((SideBandOutputStream) rawOut).flushBuffer();
1909
1910 PacketLineOut plo = new PacketLineOut(origOut);
1911 plo.setFlushOnEnd(false);
1912 plo.end();
1913 }
1914
1915 if (biDirectionalPipe) {
1916
1917
1918
1919
1920 if (!sideBand && msgOut != null)
1921 msgOut.flush();
1922 rawOut.flush();
1923 }
1924 }
1925
1926
1927
1928
1929
1930
1931
1932 protected void release() throws IOException {
1933 walk.close();
1934 unlockPack();
1935 timeoutIn = null;
1936 rawIn = null;
1937 rawOut = null;
1938 msgOut = null;
1939 pckIn = null;
1940 pckOut = null;
1941 refs = null;
1942
1943
1944
1945 commands = null;
1946 if (timer != null) {
1947 try {
1948 timer.terminate();
1949 } finally {
1950 timer = null;
1951 }
1952 }
1953 }
1954
1955
1956 static abstract class Reporter {
1957 abstract void sendString(String s) throws IOException;
1958 }
1959 }