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