1 /*
2 * Copyright (C) 2009, 2010 Apple Inc. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 *
13 * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
17 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24 */
25
26 #include "config.h"
27 #include "platform/URLPatternMatcher.h"
28
29 #include "platform/weborigin/KURL.h"
30 #include "wtf/StdLibExtras.h"
31
32 namespace blink {
33
matchesPatterns(const KURL & url,const Vector<String> & whitelist)34 bool URLPatternMatcher::matchesPatterns(const KURL& url, const Vector<String>& whitelist)
35 {
36 // If there is no whitelist at all, then all URLs are assumed to be in the whitelist.
37 if (whitelist.isEmpty())
38 return true;
39
40 for (size_t i = 0; i < whitelist.size(); ++i) {
41 URLPatternMatcher contentPattern(whitelist[i]);
42 if (contentPattern.matches(url))
43 return true;
44 }
45
46 return false;
47 }
48
parse(const String & pattern)49 bool URLPatternMatcher::parse(const String& pattern)
50 {
51 DEFINE_STATIC_LOCAL(const String, schemeSeparator, ("://"));
52
53 size_t schemeEndPos = pattern.find(schemeSeparator);
54 if (schemeEndPos == kNotFound)
55 return false;
56
57 m_scheme = pattern.left(schemeEndPos);
58
59 unsigned hostStartPos = schemeEndPos + schemeSeparator.length();
60 if (hostStartPos >= pattern.length())
61 return false;
62
63 int pathStartPos = 0;
64
65 if (equalIgnoringCase(m_scheme, "file")) {
66 pathStartPos = hostStartPos;
67 } else {
68 size_t hostEndPos = pattern.find("/", hostStartPos);
69 if (hostEndPos == kNotFound)
70 return false;
71
72 m_host = pattern.substring(hostStartPos, hostEndPos - hostStartPos);
73 m_matchSubdomains = false;
74
75 if (m_host == "*") {
76 // The pattern can be just '*', which means match all domains.
77 m_host = "";
78 m_matchSubdomains = true;
79 } else if (m_host.startsWith("*.")) {
80 // The first component can be '*', which means to match all subdomains.
81 m_host = m_host.substring(2); // Length of "*."
82 m_matchSubdomains = true;
83 }
84
85 // No other '*' can occur in the host.
86 if (m_host.find("*") != kNotFound)
87 return false;
88
89 pathStartPos = hostEndPos;
90 }
91
92 m_path = pattern.right(pattern.length() - pathStartPos);
93
94 return true;
95 }
96
matches(const KURL & test) const97 bool URLPatternMatcher::matches(const KURL& test) const
98 {
99 if (m_invalid)
100 return false;
101
102 if (!equalIgnoringCase(test.protocol(), m_scheme))
103 return false;
104
105 if (!equalIgnoringCase(m_scheme, "file") && !matchesHost(test))
106 return false;
107
108 return matchesPath(test);
109 }
110
matchesHost(const KURL & test) const111 bool URLPatternMatcher::matchesHost(const KURL& test) const
112 {
113 const String& host = test.host();
114 if (equalIgnoringCase(host, m_host))
115 return true;
116
117 if (!m_matchSubdomains)
118 return false;
119
120 // If we're matching subdomains, and we have no host, that means the pattern
121 // was <scheme>://*/<whatever>, so we match anything.
122 if (!m_host.length())
123 return true;
124
125 // Check if the domain is a subdomain of our host.
126 if (!host.endsWith(m_host, false))
127 return false;
128
129 ASSERT(host.length() > m_host.length());
130
131 // Check that the character before the suffix is a period.
132 return host[host.length() - m_host.length() - 1] == '.';
133 }
134
135 struct MatchTester {
136 const String m_pattern;
137 unsigned m_patternIndex;
138
139 const String m_test;
140 unsigned m_testIndex;
141
MatchTesterblink::MatchTester142 MatchTester(const String& pattern, const String& test)
143 : m_pattern(pattern)
144 , m_patternIndex(0)
145 , m_test(test)
146 , m_testIndex(0)
147 {
148 }
149
testStringFinishedblink::MatchTester150 bool testStringFinished() const { return m_testIndex >= m_test.length(); }
patternStringFinishedblink::MatchTester151 bool patternStringFinished() const { return m_patternIndex >= m_pattern.length(); }
152
eatWildcardblink::MatchTester153 void eatWildcard()
154 {
155 while (!patternStringFinished()) {
156 if (m_pattern[m_patternIndex] != '*')
157 return;
158 m_patternIndex++;
159 }
160 }
161
eatSameCharsblink::MatchTester162 void eatSameChars()
163 {
164 while (!patternStringFinished() && !testStringFinished()) {
165 if (m_pattern[m_patternIndex] == '*')
166 return;
167 if (m_pattern[m_patternIndex] != m_test[m_testIndex])
168 return;
169 m_patternIndex++;
170 m_testIndex++;
171 }
172 }
173
testblink::MatchTester174 bool test()
175 {
176 // Eat all the matching chars.
177 eatSameChars();
178
179 // If the string is finished, then the pattern must be empty too, or contains
180 // only wildcards.
181 if (testStringFinished()) {
182 eatWildcard();
183 if (patternStringFinished())
184 return true;
185 return false;
186 }
187
188 // Pattern is empty but not string, this is not a match.
189 if (patternStringFinished())
190 return false;
191
192 // If we don't encounter a *, then we're hosed.
193 if (m_pattern[m_patternIndex] != '*')
194 return false;
195
196 while (!testStringFinished()) {
197 MatchTester nextMatch(*this);
198 nextMatch.m_patternIndex++;
199 if (nextMatch.test())
200 return true;
201 m_testIndex++;
202 }
203
204 // We reached the end of the string. Let's see if the pattern contains only wildcards.
205 eatWildcard();
206 return patternStringFinished();
207 }
208 };
209
matchesPath(const KURL & test) const210 bool URLPatternMatcher::matchesPath(const KURL& test) const
211 {
212 MatchTester match(m_path, test.path());
213 return match.test();
214 }
215
216 } // namespace blink
217