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 public static boolean isDelimiter(String s) {
263 return s == DELIM;
264 }
265
266
267
268
269
270
271
272
273 static String delimiter() {
274 return DELIM;
275 }
276
277
278
279
280
281
282
283
284 static String end() {
285 return END;
286 }
287
288
289
290
291
292
293
294
295
296 public static boolean isEnd(String s) {
297 return s == END;
298 }
299
300 void discardUntilEnd() throws IOException {
301 for (;;) {
302 int n = readLength();
303 if (n == 0) {
304 break;
305 }
306 IO.skipFully(in, n - 4);
307 }
308 }
309
310 int readLength() throws IOException {
311 IO.readFully(in, lineBuffer, 0, 4);
312 int len;
313 try {
314 len = RawParseUtils.parseHexInt16(lineBuffer, 0);
315 } catch (ArrayIndexOutOfBoundsException err) {
316 throw invalidHeader();
317 }
318
319 if (len == 0) {
320 return 0;
321 } else if (len == 1) {
322 return 1;
323 } else if (len < 4) {
324 throw invalidHeader();
325 }
326
327 if (limit != 0) {
328 int n = len - 4;
329 if (limit < n) {
330 limit = -1;
331 try {
332 IO.skipFully(in, n);
333 } catch (IOException e) {
334
335 }
336 throw new InputOverLimitIOException();
337 }
338
339 limit = n < limit ? limit - n : -1;
340 }
341 return len;
342 }
343
344 private IOException invalidHeader() {
345 return new IOException(MessageFormat.format(JGitText.get().invalidPacketLineHeader,
346 "" + (char) lineBuffer[0] + (char) lineBuffer[1]
347 + (char) lineBuffer[2] + (char) lineBuffer[3]));
348 }
349
350
351
352
353
354
355 public static class InputOverLimitIOException extends IOException {
356 private static final long serialVersionUID = 1L;
357 }
358
359
360
361
362
363
364
365
366
367
368 public static class PacketLineInIterator implements Iterable<String> {
369 private PacketLineIn in;
370
371 private String current;
372
373 PacketLineInIterator(PacketLineIn in) throws IOException {
374 this.in = in;
375 current = in.readString();
376 }
377
378 @Override
379 public Iterator<String> iterator() {
380 return new Iterator<String>() {
381 @Override
382 public boolean hasNext() {
383 return !PacketLineIn.isEnd(current);
384 }
385
386 @Override
387 public String next() {
388 String next = current;
389 try {
390 current = in.readString();
391 } catch (IOException e) {
392 throw new UncheckedIOException(e);
393 }
394 return next;
395 }
396 };
397 }
398
399 }
400 }