1 /*
2  * Copyright (C) 2006 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package android.net.compatibility;
18 
19 import static android.util.Patterns.GOOD_IRI_CHAR;
20 
21 import java.util.Locale;
22 import java.util.regex.Matcher;
23 import java.util.regex.Pattern;
24 
25 /**
26  * Web Address Parser
27  *
28  * This is called WebAddress, rather than URL or URI, because it
29  * attempts to parse the stuff that a user will actually type into a
30  * browser address widget.
31  *
32  * Unlike java.net.uri, this parser will not choke on URIs missing
33  * schemes.  It will only throw a IllegalArgumentException if the input is
34  * really hosed.
35  *
36  * If given an https scheme but no port, fills in port
37  *
38  */
39 public class WebAddress {
40 
41     private String mScheme;
42     private String mHost;
43     private int mPort;
44     private String mPath;
45     private String mAuthInfo;
46 
47     static final int MATCH_GROUP_SCHEME = 1;
48     static final int MATCH_GROUP_AUTHORITY = 2;
49     static final int MATCH_GROUP_HOST = 3;
50     static final int MATCH_GROUP_PORT = 4;
51     static final int MATCH_GROUP_PATH = 5;
52 
53     static Pattern sAddressPattern = Pattern.compile(
54             /* scheme    */ "(?:(http|https|file)\\:\\/\\/)?" +
55             /* authority */ "(?:([-A-Za-z0-9$_.+!*'(),;?&=]+(?:\\:[-A-Za-z0-9$_.+!*'(),;?&=]+)?)@)?" +
56             /* host      */ "([" + GOOD_IRI_CHAR + "%_-][" + GOOD_IRI_CHAR + "%_\\.-]*|\\[[0-9a-fA-F:\\.]+\\])?" +
57             /* port      */ "(?:\\:([0-9]*))?" +
58             /* path      */ "(\\/?[^#]*)?" +
59             /* anchor    */ ".*", Pattern.CASE_INSENSITIVE);
60 
61     /** parses given uriString. */
WebAddress(String address)62     public WebAddress(String address) throws IllegalArgumentException {
63         if (address == null) {
64             throw new NullPointerException();
65         }
66 
67         // android.util.Log.d(LOGTAG, "WebAddress: " + address);
68 
69         mScheme = "";
70         mHost = "";
71         mPort = -1;
72         mPath = "/";
73         mAuthInfo = "";
74 
75         Matcher m = sAddressPattern.matcher(address);
76         String t;
77         if (m.matches()) {
78             t = m.group(MATCH_GROUP_SCHEME);
79             if (t != null) mScheme = t.toLowerCase(Locale.ROOT);
80             t = m.group(MATCH_GROUP_AUTHORITY);
81             if (t != null) mAuthInfo = t;
82             t = m.group(MATCH_GROUP_HOST);
83             if (t != null) mHost = t;
84             t = m.group(MATCH_GROUP_PORT);
85             if (t != null && t.length() > 0) {
86                 // The ':' character is not returned by the regex.
87                 try {
88                     mPort = Integer.parseInt(t);
89                 } catch (NumberFormatException ex) {
90                     throw new IllegalArgumentException("Bad port");
91                 }
92             }
93             t = m.group(MATCH_GROUP_PATH);
94             if (t != null && t.length() > 0) {
95                 /* handle busted myspace frontpage redirect with
96                    missing initial "/" */
97                 if (t.charAt(0) == '/') {
98                     mPath = t;
99                 } else {
100                     mPath = "/" + t;
101                 }
102             }
103 
104         } else {
105             // nothing found... outa here
106             throw new IllegalArgumentException("Bad address");
107         }
108 
109         /* Get port from scheme or scheme from port, if necessary and
110            possible */
111         if (mPort == 443 && mScheme.equals("")) {
112             mScheme = "https";
113         } else if (mPort == -1) {
114             if (mScheme.equals("https"))
115                 mPort = 443;
116             else
117                 mPort = 80; // default
118         }
119         if (mScheme.equals("")) mScheme = "http";
120     }
121 
122     @Override
toString()123     public String toString() {
124         String port = "";
125         if ((mPort != 443 && mScheme.equals("https")) ||
126             (mPort != 80 && mScheme.equals("http"))) {
127             port = ":" + Integer.toString(mPort);
128         }
129         String authInfo = "";
130         if (mAuthInfo.length() > 0) {
131             authInfo = mAuthInfo + "@";
132         }
133 
134         return mScheme + "://" + authInfo + mHost + port + mPath;
135     }
136 
setScheme(String scheme)137     public void setScheme(String scheme) {
138       mScheme = scheme;
139     }
140 
getScheme()141     public String getScheme() {
142       return mScheme;
143     }
144 
setHost(String host)145     public void setHost(String host) {
146       mHost = host;
147     }
148 
getHost()149     public String getHost() {
150       return mHost;
151     }
152 
setPort(int port)153     public void setPort(int port) {
154       mPort = port;
155     }
156 
getPort()157     public int getPort() {
158       return mPort;
159     }
160 
setPath(String path)161     public void setPath(String path) {
162       mPath = path;
163     }
164 
getPath()165     public String getPath() {
166       return mPath;
167     }
168 
setAuthInfo(String authInfo)169     public void setAuthInfo(String authInfo) {
170       mAuthInfo = authInfo;
171     }
172 
getAuthInfo()173     public String getAuthInfo() {
174       return mAuthInfo;
175     }
176 }
177