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