1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package org.eclipse.jgit.transport;
17
18 import static java.nio.charset.StandardCharsets.UTF_8;
19
20 import java.io.ByteArrayOutputStream;
21 import java.io.File;
22 import java.io.Serializable;
23 import java.net.URISyntaxException;
24 import java.net.URL;
25 import java.util.BitSet;
26 import java.util.regex.Matcher;
27 import java.util.regex.Pattern;
28
29 import org.eclipse.jgit.internal.JGitText;
30 import org.eclipse.jgit.lib.Constants;
31 import org.eclipse.jgit.util.RawParseUtils;
32 import org.eclipse.jgit.util.References;
33 import org.eclipse.jgit.util.StringUtils;
34
35
36
37
38
39
40
41 public class URIish implements Serializable {
42
43
44
45
46
47 private static final String SCHEME_P = "([a-z][a-z0-9+-]+)://"; //$NON-NLS-1$
48
49
50
51
52
53
54
55 private static final String OPT_USER_PWD_P = "(?:([^/:]+)(?::([^\\\\/]+))?@)?";
56
57
58
59
60
61 private static final String HOST_P = "((?:[^\\\\/:]+)|(?:\\[[0-9a-f:]+\\]))";
62
63
64
65
66
67 private static final String OPT_PORT_P = "(?::(\\d*))?";
68
69
70
71
72
73 private static final String USER_HOME_P = "(?:/~(?:[^\\\\/]+))";
74
75
76
77
78
79 private static final String OPT_DRIVE_LETTER_P = "(?:[A-Za-z]:)?";
80
81
82
83
84
85 private static final String RELATIVE_PATH_P = "(?:(?:[^\\\\/]+[\\\\/]+)*[^\\\\/]+[\\\\/]*)";
86
87
88
89
90
91 private static final String PATH_P = "(" + OPT_DRIVE_LETTER_P + "[\\\\/]?"
92 + RELATIVE_PATH_P + ")";
93
94 private static final long serialVersionUID = 1L;
95
96
97
98
99
100 private static final Pattern FULL_URI = Pattern.compile("^"
101 + SCHEME_P
102 + "(?:"
103
104 + OPT_USER_PWD_P
105 + HOST_P
106 + OPT_PORT_P
107 + "("
108 + (USER_HOME_P + "?")
109 + "(?:"
110
111 + "[\\\\/])|$"
112 + ")"
113
114 + ")?"
115 + "(.+)?"
116 + "$");
117
118
119
120
121
122 private static final Pattern LOCAL_FILE = Pattern.compile("^"
123 + "([\\\\/]?" + PATH_P + ")"
124 + "$");
125
126
127
128
129
130
131 private static final Pattern SINGLE_SLASH_FILE_URI = Pattern.compile("^"
132 + "(file):([\\\\/](?![\\\\/])"
133 + PATH_P
134 + ")$");
135
136
137
138
139 private static final Pattern RELATIVE_SCP_URI = Pattern.compile("^"
140 + OPT_USER_PWD_P
141 + HOST_P
142 + ":("
143 + ("(?:" + USER_HOME_P + "[\\\\/])?")
144 + RELATIVE_PATH_P
145 + ")$");
146
147
148
149
150 private static final Pattern ABSOLUTE_SCP_URI = Pattern.compile("^"
151 + OPT_USER_PWD_P
152 + "([^\\\\/:]{2,})"
153 + ":("
154 + "[\\\\/]" + RELATIVE_PATH_P
155 + ")$");
156
157 private String scheme;
158
159 private String path;
160
161 private String rawPath;
162
163 private String user;
164
165 private String pass;
166
167 private int port = -1;
168
169 private String host;
170
171
172
173
174
175
176
177
178
179 public URIish(String s) throws URISyntaxException {
180 if (StringUtils.isEmptyOrNull(s)) {
181 throw new URISyntaxException("The uri was empty or null",
182 JGitText.get().cannotParseGitURIish);
183 }
184 Matcher matcher = SINGLE_SLASH_FILE_URI.matcher(s);
185 if (matcher.matches()) {
186 scheme = matcher.group(1);
187 rawPath = cleanLeadingSlashes(matcher.group(2), scheme);
188 path = unescape(rawPath);
189 return;
190 }
191 matcher = FULL_URI.matcher(s);
192 if (matcher.matches()) {
193 scheme = matcher.group(1);
194 user = unescape(matcher.group(2));
195 pass = unescape(matcher.group(3));
196
197
198
199 String portString = matcher.group(5);
200 if ("file".equals(scheme) && "".equals(portString)) {
201 rawPath = cleanLeadingSlashes(
202 n2e(matcher.group(4)) + ":" + portString
203 + n2e(matcher.group(6)) + n2e(matcher.group(7)),
204 scheme);
205 } else {
206 host = unescape(matcher.group(4));
207 if (portString != null && portString.length() > 0) {
208 port = Integer.parseInt(portString);
209 }
210 rawPath = cleanLeadingSlashes(
211 n2e(matcher.group(6)) + n2e(matcher.group(7)), scheme);
212 }
213 path = unescape(rawPath);
214 return;
215 }
216 matcher = RELATIVE_SCP_URI.matcher(s);
217 if (matcher.matches()) {
218 user = matcher.group(1);
219 pass = matcher.group(2);
220 host = matcher.group(3);
221 rawPath = matcher.group(4);
222 path = rawPath;
223 return;
224 }
225 matcher = ABSOLUTE_SCP_URI.matcher(s);
226 if (matcher.matches()) {
227 user = matcher.group(1);
228 pass = matcher.group(2);
229 host = matcher.group(3);
230 rawPath = matcher.group(4);
231 path = rawPath;
232 return;
233 }
234 matcher = LOCAL_FILE.matcher(s);
235 if (matcher.matches()) {
236 rawPath = matcher.group(1);
237 path = rawPath;
238 return;
239 }
240 throw new URISyntaxException(s, JGitText.get().cannotParseGitURIish);
241 }
242
243 private static int parseHexByte(byte c1, byte c2) {
244 return ((RawParseUtils.parseHexInt4(c1) << 4)
245 | RawParseUtils.parseHexInt4(c2));
246 }
247
248 private static String unescape(String s) throws URISyntaxException {
249 if (s == null)
250 return null;
251 if (s.indexOf('%') < 0)
252 return s;
253
254 byte[] bytes = s.getBytes(UTF_8);
255
256 byte[] os = new byte[bytes.length];
257 int j = 0;
258 for (int i = 0; i < bytes.length; ++i) {
259 byte c = bytes[i];
260 if (c == '%') {
261 if (i + 2 >= bytes.length)
262 throw new URISyntaxException(s, JGitText.get().cannotParseGitURIish);
263 byte c1 = bytes[i + 1];
264 byte c2 = bytes[i + 2];
265 int val;
266 try {
267 val = parseHexByte(c1, c2);
268 } catch (ArrayIndexOutOfBoundsException e) {
269 URISyntaxException use = new URISyntaxException(s,
270 JGitText.get().cannotParseGitURIish);
271 use.initCause(e);
272 throw use;
273 }
274 os[j++] = (byte) val;
275 i += 2;
276 } else
277 os[j++] = c;
278 }
279 return RawParseUtils.decode(os, 0, j);
280 }
281
282 private static final BitSet reservedChars = new BitSet(127);
283
284 static {
285 for (byte b : Constants.encodeASCII("!*'();:@&=+$,/?#[]"))
286 reservedChars.set(b);
287 }
288
289
290
291
292
293
294
295
296
297
298
299
300 private static String escape(String s, boolean escapeReservedChars,
301 boolean encodeNonAscii) {
302 if (s == null)
303 return null;
304 ByteArrayOutputStream os = new ByteArrayOutputStream(s.length());
305 byte[] bytes = s.getBytes(UTF_8);
306 for (byte c : bytes) {
307 int b = c & 0xFF;
308 if (b <= 32 || (encodeNonAscii && b > 127) || b == '%'
309 || (escapeReservedChars && reservedChars.get(b))) {
310 os.write('%');
311 byte[] tmp = Constants.encodeASCII(String.format("%02x",
312 Integer.valueOf(b)));
313 os.write(tmp[0]);
314 os.write(tmp[1]);
315 } else {
316 os.write(b);
317 }
318 }
319 byte[] buf = os.toByteArray();
320 return RawParseUtils.decode(buf, 0, buf.length);
321 }
322
323 private String n2e(String s) {
324 return s == null ? "" : s;
325 }
326
327
328
329 private String cleanLeadingSlashes(String p, String s) {
330 if (p.length() >= 3
331 && p.charAt(0) == '/'
332 && p.charAt(2) == ':'
333 && ((p.charAt(1) >= 'A' && p.charAt(1) <= 'Z')
334 || (p.charAt(1) >= 'a' && p.charAt(1) <= 'z')))
335 return p.substring(1);
336 else if (s != null && p.length() >= 2 && p.charAt(0) == '/'
337 && p.charAt(1) == '~')
338 return p.substring(1);
339 else
340 return p;
341 }
342
343
344
345
346
347
348
349 public URIish(URL u) {
350 scheme = u.getProtocol();
351 path = u.getPath();
352 path = cleanLeadingSlashes(path, scheme);
353 try {
354 rawPath = u.toURI().getRawPath();
355 rawPath = cleanLeadingSlashes(rawPath, scheme);
356 } catch (URISyntaxException e) {
357 throw new RuntimeException(e);
358 }
359
360 final String ui = u.getUserInfo();
361 if (ui != null) {
362 final int d = ui.indexOf(':');
363 user = d < 0 ? ui : ui.substring(0, d);
364 pass = d < 0 ? null : ui.substring(d + 1);
365 }
366
367 port = u.getPort();
368 host = u.getHost();
369 }
370
371
372
373
374 public URIish() {
375
376 }
377
378 private URIishsh" href="../../../../org/eclipse/jgit/transport/URIish.html#URIish">URIish(URIish u) {
379 this.scheme = u.scheme;
380 this.rawPath = u.rawPath;
381 this.path = u.path;
382 this.user = u.user;
383 this.pass = u.pass;
384 this.port = u.port;
385 this.host = u.host;
386 }
387
388
389
390
391
392
393 public boolean isRemote() {
394 return getHost() != null;
395 }
396
397
398
399
400
401
402 public String getHost() {
403 return host;
404 }
405
406
407
408
409
410
411
412
413 public URIish setHost(String n) {
414 final URIishort/URIish.html#URIish">URIish r = new URIish(this);
415 r.host = n;
416 return r;
417 }
418
419
420
421
422
423
424 public String getScheme() {
425 return scheme;
426 }
427
428
429
430
431
432
433
434
435 public URIish setScheme(String n) {
436 final URIishort/URIish.html#URIish">URIish r = new URIish(this);
437 r.scheme = n;
438 return r;
439 }
440
441
442
443
444
445
446 public String getPath() {
447 return path;
448 }
449
450
451
452
453
454
455 public String getRawPath() {
456 return rawPath;
457 }
458
459
460
461
462
463
464
465
466 public URIish setPath(String n) {
467 final URIishort/URIish.html#URIish">URIish r = new URIish(this);
468 r.path = n;
469 r.rawPath = n;
470 return r;
471 }
472
473
474
475
476
477
478
479
480
481 public URIish setRawPath(String n) throws URISyntaxException {
482 final URIishort/URIish.html#URIish">URIish r = new URIish(this);
483 r.path = unescape(n);
484 r.rawPath = n;
485 return r;
486 }
487
488
489
490
491
492
493 public String getUser() {
494 return user;
495 }
496
497
498
499
500
501
502
503
504 public URIish setUser(String n) {
505 final URIishort/URIish.html#URIish">URIish r = new URIish(this);
506 r.user = n;
507 return r;
508 }
509
510
511
512
513
514
515 public String getPass() {
516 return pass;
517 }
518
519
520
521
522
523
524
525
526 public URIish setPass(String n) {
527 final URIishort/URIish.html#URIish">URIish r = new URIish(this);
528 r.pass = n;
529 return r;
530 }
531
532
533
534
535
536
537 public int getPort() {
538 return port;
539 }
540
541
542
543
544
545
546
547
548 public URIish setPort(int n) {
549 final URIishort/URIish.html#URIish">URIish r = new URIish(this);
550 r.port = n > 0 ? n : -1;
551 return r;
552 }
553
554
555 @Override
556 public int hashCode() {
557 int hc = 0;
558 if (getScheme() != null)
559 hc = hc * 31 + getScheme().hashCode();
560 if (getUser() != null)
561 hc = hc * 31 + getUser().hashCode();
562 if (getPass() != null)
563 hc = hc * 31 + getPass().hashCode();
564 if (getHost() != null)
565 hc = hc * 31 + getHost().hashCode();
566 if (getPort() > 0)
567 hc = hc * 31 + getPort();
568 if (getPath() != null)
569 hc = hc * 31 + getPath().hashCode();
570 return hc;
571 }
572
573
574 @Override
575 public boolean equals(Object obj) {
576 if (!(obj instanceof URIish))
577 return false;
578 final URIishref="../../../../org/eclipse/jgit/transport/URIish.html#URIish">URIish b = (URIish) obj;
579 if (!eq(getScheme(), b.getScheme()))
580 return false;
581 if (!eq(getUser(), b.getUser()))
582 return false;
583 if (!eq(getPass(), b.getPass()))
584 return false;
585 if (!eq(getHost(), b.getHost()))
586 return false;
587 if (getPort() != b.getPort())
588 return false;
589 if (!eq(getPath(), b.getPath()))
590 return false;
591 return true;
592 }
593
594 private static boolean eq(String a, String b) {
595 if (References.isSameObject(a, b)) {
596 return true;
597 }
598 if (StringUtils.isEmptyOrNull(a) && StringUtils.isEmptyOrNull(b))
599 return true;
600 if (a == null || b == null)
601 return false;
602 return a.equals(b);
603 }
604
605
606
607
608
609
610 public String toPrivateString() {
611 return format(true, false);
612 }
613
614
615 @Override
616 public String toString() {
617 return format(false, false);
618 }
619
620 private String format(boolean includePassword, boolean escapeNonAscii) {
621 final StringBuilder r = new StringBuilder();
622 if (getScheme() != null) {
623 r.append(getScheme());
624 r.append("://"); //$NON-NLS-1$
625 }
626
627 if (getUser() != null) {
628 r.append(escape(getUser(), true, escapeNonAscii));
629 if (includePassword && getPass() != null) {
630 r.append(':');
631 r.append(escape(getPass(), true, escapeNonAscii));
632 }
633 }
634
635 if (getHost() != null) {
636 if (getUser() != null && getUser().length() > 0)
637 r.append('@');
638 r.append(escape(getHost(), false, escapeNonAscii));
639 if (getScheme() != null && getPort() > 0) {
640 r.append(':');
641 r.append(getPort());
642 }
643 }
644
645 if (getPath() != null) {
646 if (getScheme() != null) {
647 if (!getPath().startsWith("/") && !getPath().isEmpty())
648 r.append('/');
649 } else if (getHost() != null)
650 r.append(':');
651 if (getScheme() != null)
652 if (escapeNonAscii)
653 r.append(escape(getPath(), false, escapeNonAscii));
654 else
655 r.append(getRawPath());
656 else
657 r.append(getPath());
658 }
659
660 return r.toString();
661 }
662
663
664
665
666
667
668 public String toASCIIString() {
669 return format(false, true);
670 }
671
672
673
674
675
676
677
678
679 public String toPrivateASCIIString() {
680 return format(true, true);
681 }
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721 public String getHumanishName() throws IllegalArgumentException {
722 String s = getPath();
723 if ("/".equals(s) || "".equals(s))
724 s = getHost();
725 if (s == null)
726 throw new IllegalArgumentException();
727
728 String[] elements;
729 if ("file".equals(scheme) || LOCAL_FILE.matcher(s).matches())
730 elements = s.split("[\\" + File.separatorChar + "/]");
731 else
732 elements = s.split("/+");
733 if (elements.length == 0)
734 throw new IllegalArgumentException();
735 String result = elements[elements.length - 1];
736 if (Constants.DOT_GIT.equals(result))
737 result = elements[elements.length - 2];
738 else if (result.endsWith(Constants.DOT_GIT_EXT))
739 result = result.substring(0, result.length()
740 - Constants.DOT_GIT_EXT.length());
741 if (("file".equals(scheme) || LOCAL_FILE.matcher(s)
742 .matches())
743 && result.endsWith(Constants.DOT_BUNDLE_EXT)) {
744 result = result.substring(0,
745 result.length() - Constants.DOT_BUNDLE_EXT.length());
746 }
747 return result;
748 }
749
750 }