001 /*
002 * Licensed to the Apache Software Foundation (ASF) under one
003 * or more contributor license agreements. See the NOTICE file
004 * distributed with this work for additional information
005 * regarding copyright ownership. The ASF licenses this file
006 * to you under the Apache License, Version 2.0 (the
007 * "License"); you may not use this file except in compliance
008 * with the License. You may obtain a copy of the License at
009 *
010 * http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing,
013 * software distributed under the License is distributed on an
014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015 * KIND, either express or implied. See the License for the
016 * specific language governing permissions and limitations
017 * under the License.
018 */
019
020 package javax.mail;
021
022 import java.io.ByteArrayOutputStream;
023 import java.net.MalformedURLException;
024 import java.net.URI;
025 import java.net.URISyntaxException;
026 import java.net.URL;
027
028 /**
029 * @version $Rev: 593290 $ $Date: 2007-11-08 15:18:29 -0500 (Thu, 08 Nov 2007) $
030 */
031 public class URLName {
032 private static final String nonEncodedChars = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_-.*";
033
034 private String file;
035 private String host;
036 private String password;
037 private int port;
038 private String protocol;
039 private String ref;
040 private String username;
041 protected String fullURL;
042 private int hashCode;
043
044 public URLName(String url) {
045 parseString(url);
046 }
047
048 protected void parseString(String url) {
049 URI uri;
050 try {
051 if (url == null) {
052 uri = null;
053 } else {
054 uri = new URI(url);
055 }
056 } catch (URISyntaxException e) {
057 uri = null;
058 }
059 if (uri == null) {
060 protocol = null;
061 host = null;
062 port = -1;
063 file = null;
064 ref = null;
065 username = null;
066 password = null;
067 return;
068 }
069
070 protocol = checkBlank(uri.getScheme());
071 host = checkBlank(uri.getHost());
072 port = uri.getPort();
073 file = checkBlank(uri.getPath());
074 // if the file starts with "/", we need to strip that off.
075 // URL and URLName do not have the same behavior when it comes
076 // to keeping that there.
077 if (file != null && file.length() > 1 && file.startsWith("/")) {
078 file = checkBlank(file.substring(1));
079 }
080
081 ref = checkBlank(uri.getFragment());
082 String userInfo = checkBlank(uri.getUserInfo());
083 if (userInfo == null) {
084 username = null;
085 password = null;
086 } else {
087 int pos = userInfo.indexOf(':');
088 if (pos == -1) {
089 username = userInfo;
090 password = null;
091 } else {
092 username = userInfo.substring(0, pos);
093 password = userInfo.substring(pos + 1);
094 }
095 }
096 updateFullURL();
097 }
098
099 public URLName(String protocol, String host, int port, String file, String username, String password) {
100 this.protocol = checkBlank(protocol);
101 this.host = checkBlank(host);
102 this.port = port;
103 if (file == null || file.length() == 0) {
104 this.file = null;
105 ref = null;
106 } else {
107 int pos = file.indexOf('#');
108 if (pos == -1) {
109 this.file = file;
110 ref = null;
111 } else {
112 this.file = file.substring(0, pos);
113 ref = file.substring(pos + 1);
114 }
115 }
116 this.username = checkBlank(username);
117 if (this.username != null) {
118 this.password = checkBlank(password);
119 } else {
120 this.password = null;
121 }
122 username = encode(username);
123 password = encode(password);
124 updateFullURL();
125 }
126
127 public URLName(URL url) {
128 protocol = checkBlank(url.getProtocol());
129 host = checkBlank(url.getHost());
130 port = url.getPort();
131 file = checkBlank(url.getFile());
132 ref = checkBlank(url.getRef());
133 String userInfo = checkBlank(url.getUserInfo());
134 if (userInfo == null) {
135 username = null;
136 password = null;
137 } else {
138 int pos = userInfo.indexOf(':');
139 if (pos == -1) {
140 username = userInfo;
141 password = null;
142 } else {
143 username = userInfo.substring(0, pos);
144 password = userInfo.substring(pos + 1);
145 }
146 }
147 updateFullURL();
148 }
149
150 private static String checkBlank(String target) {
151 if (target == null || target.length() == 0) {
152 return null;
153 } else {
154 return target;
155 }
156 }
157
158 private void updateFullURL() {
159 hashCode = 0;
160 StringBuffer buf = new StringBuffer(100);
161 if (protocol != null) {
162 buf.append(protocol).append(':');
163 if (host != null) {
164 buf.append("//");
165 if (username != null) {
166 buf.append(encode(username));
167 if (password != null) {
168 buf.append(':').append(encode(password));
169 }
170 buf.append('@');
171 }
172 buf.append(host);
173 if (port != -1) {
174 buf.append(':').append(port);
175 }
176 if (file != null) {
177 buf.append('/').append(file);
178 }
179 hashCode = buf.toString().hashCode();
180 if (ref != null) {
181 buf.append('#').append(ref);
182 }
183 }
184 }
185 fullURL = buf.toString();
186 }
187
188 public boolean equals(Object o) {
189 if (o instanceof URLName == false) {
190 return false;
191 }
192 URLName other = (URLName) o;
193 // check same protocol - false if either is null
194 if (protocol == null || other.protocol == null || !protocol.equals(other.protocol)) {
195 return false;
196 }
197
198 if (port != other.port) {
199 return false;
200 }
201
202 // check host - false if not (both null or both equal)
203 return areSame(host, other.host) && areSame(file, other.file) && areSame(username, other.username) && areSame(password, other.password);
204 }
205
206 private static boolean areSame(String s1, String s2) {
207 if (s1 == null) {
208 return s2 == null;
209 } else {
210 return s1.equals(s2);
211 }
212 }
213
214 public int hashCode() {
215 return hashCode;
216 }
217
218 public String toString() {
219 return fullURL;
220 }
221
222 public String getFile() {
223 return file;
224 }
225
226 public String getHost() {
227 return host;
228 }
229
230 public String getPassword() {
231 return password;
232 }
233
234 public int getPort() {
235 return port;
236 }
237
238 public String getProtocol() {
239 return protocol;
240 }
241
242 public String getRef() {
243 return ref;
244 }
245
246 public URL getURL() throws MalformedURLException {
247 return new URL(fullURL);
248 }
249
250 public String getUsername() {
251 return username;
252 }
253
254 /**
255 * Perform an HTTP encoding to the username and
256 * password elements of the URLName.
257 *
258 * @param v The input (uncoded) string.
259 *
260 * @return The HTTP encoded version of the string.
261 */
262 private static String encode(String v) {
263 // make sure we don't operate on a null string
264 if (v == null) {
265 return null;
266 }
267 boolean needsEncoding = false;
268 for (int i = 0; i < v.length(); i++) {
269 // not in the list of things that don't need encoding?
270 if (nonEncodedChars.indexOf(v.charAt(i)) == -1) {
271 // got to do this the hard way
272 needsEncoding = true;
273 break;
274 }
275 }
276 // just fine the way it is.
277 if (!needsEncoding) {
278 return v;
279 }
280
281 // we know we're going to be larger, but not sure by how much.
282 // just give a little extra
283 StringBuffer encoded = new StringBuffer(v.length() + 10);
284
285 // we get the bytes so that we can have the default encoding applied to
286 // this string. This will flag the ones we need to give special processing to.
287 byte[] data = v.getBytes();
288
289 for (int i = 0; i < data.length; i++) {
290 // pick this up as a one-byte character The 7-bit ascii ones will be fine
291 // here.
292 char ch = (char)(data[i] & 0xff);
293 // blanks get special treatment
294 if (ch == ' ') {
295 encoded.append('+');
296 }
297 // not in the list of things that don't need encoding?
298 else if (nonEncodedChars.indexOf(ch) == -1) {
299 // forDigit() uses the lowercase letters for the radix. The HTML specifications
300 // require the uppercase letters.
301 char firstChar = Character.toUpperCase(Character.forDigit((ch >> 4) & 0xf, 16));
302 char secondChar = Character.toUpperCase(Character.forDigit(ch & 0xf, 16));
303
304 // now append the encoded triplet.
305 encoded.append('%');
306 encoded.append(firstChar);
307 encoded.append(secondChar);
308 }
309 else {
310 // just add this one to the buffer
311 encoded.append(ch);
312 }
313 }
314 // convert to string form.
315 return encoded.toString();
316 }
317 }