1 /*
2 * Copyright (C) 2012, Google Inc. and others
3 *
4 * This program and the accompanying materials are made available under the
5 * terms of the Eclipse Distribution License v. 1.0 which is available at
6 * https://www.eclipse.org/org/documents/edl-v10.php.
7 *
8 * SPDX-License-Identifier: BSD-3-Clause
9 */
10
11 package org.eclipse.jgit.http.server;
12
13 import javax.servlet.http.HttpServletRequest;
14
15 /**
16 * Parses Git client User-Agent strings.
17 */
18 public class ClientVersionUtil {
19 /**
20 * An invalid version of Git
21 *
22 * @return maximum version array, indicating an invalid version of Git.
23 */
24 public static int[] invalidVersion() {
25 return new int[] { Integer.MAX_VALUE };
26 }
27
28 /**
29 * Parse a Git client User-Agent header value.
30 *
31 * @param version
32 * git client version string, of the form "git/1.7.9".
33 * @return components of the version string. {@link #invalidVersion()} if
34 * the version string cannot be parsed.
35 */
36 public static int[] parseVersion(String version) {
37 if (version != null && version.startsWith("git/"))
38 return splitVersion(version.substring("git/".length()));
39 return invalidVersion();
40 }
41
42 private static int[] splitVersion(String versionString) {
43 char[] str = versionString.toCharArray();
44 int[] ver = new int[4];
45 int end = 0;
46 int acc = 0;
47 for (int i = 0; i < str.length; i++) {
48 char c = str[i];
49 if ('0' <= c && c <= '9') {
50 acc *= 10;
51 acc += c - '0';
52 } else if (c == '.') {
53 if (end == ver.length)
54 ver = grow(ver);
55 ver[end++] = acc;
56 acc = 0;
57 } else if (c == 'g' && 0 < i && str[i - 1] == '.' && 0 < end) {
58 // Non-tagged builds may contain a mangled git describe output.
59 // "1.7.6.1.45.gbe0cc". The 45 isn't a valid component. Drop it.
60 ver[end - 1] = 0;
61 acc = 0;
62 break;
63 } else if (c == '-' && (i + 2) < str.length
64 && str[i + 1] == 'r' && str[i + 2] == 'c') {
65 // Release candidates aren't the same as a final release.
66 if (acc > 0)
67 acc--;
68 break;
69 } else
70 break;
71 }
72 if (acc != 0) {
73 if (end == ver.length)
74 ver = grow(ver);
75 ver[end++] = acc;
76 } else {
77 while (0 < end && ver[end - 1] == 0)
78 end--;
79 }
80 if (end < ver.length) {
81 int[] n = new int[end];
82 System.arraycopy(ver, 0, n, 0, end);
83 ver = n;
84 }
85 return ver;
86 }
87
88 private static int[] grow(int[] tmp) {
89 int[] n = new int[tmp.length + 1];
90 System.arraycopy(tmp, 0, n, 0, tmp.length);
91 return n;
92 }
93
94 /**
95 * Compare two version strings for natural ordering.
96 *
97 * @param a
98 * first parsed version string.
99 * @param b
100 * second parsed version string.
101 * @return < 0 if a is before b; 0 if a equals b; >0 if a is after b.
102 */
103 public static int compare(int[] a, int[] b) {
104 for (int i = 0; i < a.length && i < b.length; i++) {
105 int cmp = a[i] - b[i];
106 if (cmp != 0)
107 return cmp;
108 }
109 return a.length - b.length;
110 }
111
112 /**
113 * Convert a parsed version back to a string.
114 *
115 * @param ver
116 * the parsed version array.
117 * @return a string, e.g. "1.6.6.0".
118 */
119 public static String toString(int[] ver) {
120 StringBuilder b = new StringBuilder();
121 for (int v : ver) {
122 if (b.length() > 0)
123 b.append('.');
124 b.append(v);
125 }
126 return b.toString();
127 }
128
129 /**
130 * Check if a Git client has the known push status bug.
131 * <p>
132 * These buggy clients do not display the status report from a failed push
133 * over HTTP.
134 *
135 * @param version
136 * parsed version of the Git client software.
137 * @return true if the bug is present.
138 * @deprecated no widely used Git versions need this any more
139 */
140 @Deprecated
141 public static boolean hasPushStatusBug(int[] version) {
142 return false;
143 }
144
145 /**
146 * Check if a Git client has the known chunked request body encoding bug.
147 * <p>
148 * Git 1.7.5 contains a unique bug where chunked requests are malformed.
149 * This applies to both fetch and push.
150 *
151 * @param version
152 * parsed version of the Git client software.
153 * @param request
154 * incoming HTTP request.
155 * @return true if the client has the chunked encoding bug.
156 * @deprecated no widely used Git versions need this any more
157 */
158 @Deprecated
159 public static boolean hasChunkedEncodingRequestBug(
160 int[] version, HttpServletRequest request) {
161 return false;
162 }
163
164 private ClientVersionUtil() {
165 }
166 }