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