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
45
46 package org.eclipse.jgit.transport;
47
48 import static java.nio.charset.StandardCharsets.UTF_8;
49
50 import java.io.IOException;
51 import java.io.InputStream;
52 import java.io.UncheckedIOException;
53 import java.text.MessageFormat;
54 import java.util.Iterator;
55
56 import org.eclipse.jgit.errors.PackProtocolException;
57 import org.eclipse.jgit.internal.JGitText;
58 import org.eclipse.jgit.lib.MutableObjectId;
59 import org.eclipse.jgit.util.IO;
60 import org.eclipse.jgit.util.RawParseUtils;
61 import org.slf4j.Logger;
62 import org.slf4j.LoggerFactory;
63
64
65
66
67
68
69
70
71
72
73
74 public class PacketLineIn {
75 private static final Logger log = LoggerFactory.getLogger(PacketLineIn.class);
76
77
78
79
80
81
82
83
84
85 @Deprecated
86 public static final String END = new StringBuilder(0).toString();
87
88
89
90
91
92
93
94
95 @Deprecated
96 public static final String DELIM = new StringBuilder(0).toString();
97
98 static enum AckNackResult {
99
100 NAK,
101
102 ACK,
103
104 ACK_CONTINUE,
105
106 ACK_COMMON,
107
108 ACK_READY;
109 }
110
111 private final byte[] lineBuffer = new byte[SideBandOutputStream.SMALL_BUF];
112 private final InputStream in;
113 private long limit;
114
115
116
117
118
119
120
121 public PacketLineIn(InputStream in) {
122 this(in, 0);
123 }
124
125
126
127
128
129
130
131
132
133
134 public PacketLineIn(InputStream in, long limit) {
135 this.in = in;
136 this.limit = limit;
137 }
138
139 AckNackResult readACK(MutableObjectId returnedId) throws IOException {
140 final String line = readString();
141 if (line.length() == 0)
142 throw new PackProtocolException(JGitText.get().expectedACKNAKFoundEOF);
143 if ("NAK".equals(line))
144 return AckNackResult.NAK;
145 if (line.startsWith("ACK ")) {
146 returnedId.fromString(line.substring(4, 44));
147 if (line.length() == 44)
148 return AckNackResult.ACK;
149
150 final String arg = line.substring(44);
151 if (arg.equals(" continue"))
152 return AckNackResult.ACK_CONTINUE;
153 else if (arg.equals(" common"))
154 return AckNackResult.ACK_COMMON;
155 else if (arg.equals(" ready"))
156 return AckNackResult.ACK_READY;
157 }
158 if (line.startsWith("ERR "))
159 throw new PackProtocolException(line.substring(4));
160 throw new PackProtocolException(MessageFormat.format(JGitText.get().expectedACKNAKGot, line));
161 }
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176 public String readString() throws IOException {
177 int len = readLength();
178 if (len == 0) {
179 log.debug("git< 0000");
180 return END;
181 }
182 if (len == 1) {
183 log.debug("git< 0001");
184 return DELIM;
185 }
186
187 len -= 4;
188 if (len == 0) {
189 log.debug("git< ");
190 return "";
191 }
192
193 byte[] raw;
194 if (len <= lineBuffer.length)
195 raw = lineBuffer;
196 else
197 raw = new byte[len];
198
199 IO.readFully(in, raw, 0, len);
200 if (raw[len - 1] == '\n')
201 len--;
202
203 String s = RawParseUtils.decode(UTF_8, raw, 0, len);
204 log.debug("git< " + s);
205 return s;
206 }
207
208
209
210
211
212
213
214
215
216
217
218 public PacketLineInIterator readStrings() throws IOException {
219 return new PacketLineInIterator(this);
220 }
221
222
223
224
225
226
227
228
229
230
231
232 public String readStringRaw() throws IOException {
233 int len = readLength();
234 if (len == 0) {
235 log.debug("git< 0000");
236 return END;
237 }
238
239 len -= 4;
240
241 byte[] raw;
242 if (len <= lineBuffer.length)
243 raw = lineBuffer;
244 else
245 raw = new byte[len];
246
247 IO.readFully(in, raw, 0, len);
248
249 String s = RawParseUtils.decode(UTF_8, raw, 0, len);
250 log.debug("git< " + s);
251 return s;
252 }
253
254
255
256
257
258
259
260
261
262 @SuppressWarnings({ "ReferenceEquality", "StringEquality" })
263 public static boolean isDelimiter(String s) {
264 return s == DELIM;
265 }
266
267
268
269
270
271
272
273
274 static String delimiter() {
275 return DELIM;
276 }
277
278
279
280
281
282
283
284
285 static String end() {
286 return END;
287 }
288
289
290
291
292
293
294
295
296
297 @SuppressWarnings({ "ReferenceEquality", "StringEquality" })
298 public static boolean isEnd(String s) {
299 return s == END;
300 }
301
302 void discardUntilEnd() throws IOException {
303 for (;;) {
304 int n = readLength();
305 if (n == 0) {
306 break;
307 }
308 IO.skipFully(in, n - 4);
309 }
310 }
311
312 int readLength() throws IOException {
313 IO.readFully(in, lineBuffer, 0, 4);
314 int len;
315 try {
316 len = RawParseUtils.parseHexInt16(lineBuffer, 0);
317 } catch (ArrayIndexOutOfBoundsException err) {
318 throw invalidHeader();
319 }
320
321 if (len == 0) {
322 return 0;
323 } else if (len == 1) {
324 return 1;
325 } else if (len < 4) {
326 throw invalidHeader();
327 }
328
329 if (limit != 0) {
330 int n = len - 4;
331 if (limit < n) {
332 limit = -1;
333 try {
334 IO.skipFully(in, n);
335 } catch (IOException e) {
336
337 }
338 throw new InputOverLimitIOException();
339 }
340
341 limit = n < limit ? limit - n : -1;
342 }
343 return len;
344 }
345
346 private IOException invalidHeader() {
347 return new IOException(MessageFormat.format(JGitText.get().invalidPacketLineHeader,
348 "" + (char) lineBuffer[0] + (char) lineBuffer[1]
349 + (char) lineBuffer[2] + (char) lineBuffer[3]));
350 }
351
352
353
354
355
356
357 public static class InputOverLimitIOException extends IOException {
358 private static final long serialVersionUID = 1L;
359 }
360
361
362
363
364
365
366
367
368
369
370 public static class PacketLineInIterator implements Iterable<String> {
371 private PacketLineIn in;
372
373 private String current;
374
375 PacketLineInIterator(PacketLineIn in) throws IOException {
376 this.in = in;
377 current = in.readString();
378 }
379
380 @Override
381 public Iterator<String> iterator() {
382 return new Iterator<String>() {
383 @Override
384 public boolean hasNext() {
385 return !PacketLineIn.isEnd(current);
386 }
387
388 @Override
389 public String next() {
390 String next = current;
391 try {
392 current = in.readString();
393 } catch (IOException e) {
394 throw new UncheckedIOException(e);
395 }
396 return next;
397 }
398 };
399 }
400
401 }
402 }