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 }