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    }