1 /* 2 * Copyright (C) 2008, 2009 Torch Mobile Inc. All rights reserved. (http://www.torchmobile.com/) 3 * Copyright (C) 2004-2007 Apple Inc. All rights reserved. 4 * 5 * This library is free software; you can redistribute it and/or 6 * modify it under the terms of the GNU Library General Public 7 * License as published by the Free Software Foundation; either 8 * version 2 of the License, or (at your option) any later version. 9 * 10 * This library is distributed in the hope that it will be useful, 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13 * Library General Public License for more details. 14 * 15 * You should have received a copy of the GNU Library General Public License 16 * along with this library; see the file COPYING.LIB. If not, write to 17 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 18 * Boston, MA 02110-1301, USA. 19 * 20 */ 21 22 #include "config.h" 23 24 #if ENABLE(WML) 25 #include "WMLPageState.h" 26 27 #include "BackForwardController.h" 28 #include "BackForwardList.h" 29 #include "Document.h" 30 #include "Frame.h" 31 #include "HistoryItem.h" 32 #include "KURL.h" 33 #include "Page.h" 34 #include <wtf/text/CString.h> 35 36 namespace WebCore { 37 38 WMLPageState::WMLPageState(Page* page) 39 : m_page(page) 40 , m_hasAccessControlData(false) 41 { 42 } 43 44 WMLPageState::~WMLPageState() 45 { 46 m_variables.clear(); 47 } 48 49 #ifndef NDEBUG 50 // Debugging helper for use within gdb 51 void WMLPageState::dump() 52 { 53 WMLVariableMap::iterator it = m_variables.begin(); 54 WMLVariableMap::iterator end = m_variables.end(); 55 56 fprintf(stderr, "Dumping WMLPageState (this=%p) associated with Page (page=%p)...\n", this, m_page); 57 for (; it != end; ++it) 58 fprintf(stderr, "\t-> name: '%s'\tvalue: '%s'\n", (*it).first.latin1().data(), (*it).second.latin1().data()); 59 } 60 #endif 61 62 void WMLPageState::reset() 63 { 64 // Remove all the variables 65 m_variables.clear(); 66 67 // Clear the navigation history state 68 if (m_page) 69 m_page->backForward()->client()->clearWMLPageHistory(); 70 } 71 72 static inline String normalizedHostName(const String& passedHost) 73 { 74 if (passedHost.contains("127.0.0.1")) { 75 String host = passedHost; 76 return host.replace("127.0.0.1", "localhost"); 77 } 78 79 return passedHost; 80 } 81 82 static inline String hostFromURL(const KURL& url) 83 { 84 // Default to "localhost" 85 String host = normalizedHostName(url.host()); 86 return host.isEmpty() ? "localhost" : host; 87 } 88 89 static KURL urlForHistoryItem(Frame* frame, HistoryItem* item) 90 { 91 // For LayoutTests we need to find the corresponding WML frame in the test document 92 // to be able to test access-control correctly. Remember that WML is never supposed 93 // to be embedded anywhere, so the purpose is to simulate a standalone WML document. 94 if (frame->document()->isWMLDocument()) 95 return item->url(); 96 97 const HistoryItemVector& childItems = item->children(); 98 HistoryItemVector::const_iterator it = childItems.begin(); 99 const HistoryItemVector::const_iterator end = childItems.end(); 100 101 for (; it != end; ++it) { 102 const RefPtr<HistoryItem> childItem = *it; 103 Frame* childFrame = frame->tree()->child(childItem->target()); 104 if (!childFrame) 105 continue; 106 107 if (Document* childDocument = childFrame->document()) { 108 if (childDocument->isWMLDocument()) 109 return childItem->url(); 110 } 111 } 112 113 return item->url(); 114 } 115 116 static bool tryAccessHistoryURLs(Page* page, KURL& previousURL, KURL& currentURL) 117 { 118 if (!page) 119 return false; 120 121 Frame* frame = page->mainFrame(); 122 if (!frame || !frame->document()) 123 return false; 124 125 HistoryItem* previousItem = page->backForward()->backItem(); 126 if (!previousItem) 127 return false; 128 129 HistoryItem* currentItem = page->backForward()->currentItem(); 130 if (!currentItem) 131 return false; 132 133 previousURL = urlForHistoryItem(frame, previousItem); 134 currentURL = urlForHistoryItem(frame, currentItem); 135 136 return true; 137 } 138 139 bool WMLPageState::processAccessControlData(const String& domain, const String& path) 140 { 141 if (m_hasAccessControlData) 142 return false; 143 144 m_hasAccessControlData = true; 145 146 KURL previousURL, currentURL; 147 if (!tryAccessHistoryURLs(m_page, previousURL, currentURL)) 148 return true; 149 150 // Spec: The path attribute defaults to the value "/" 151 m_accessPath = path.isEmpty() ? "/" : path; 152 153 // Spec: The domain attribute defaults to the current decks domain. 154 String previousHost = hostFromURL(previousURL); 155 m_accessDomain = domain.isEmpty() ? previousHost : normalizedHostName(domain); 156 157 // Spec: To simplify the development of applications that may not know the absolute path to the 158 // current deck, the path attribute accepts relative URIs. The user agent converts the relative 159 // path to an absolute path and then performs prefix matching against the PATH attribute. 160 Document* document = m_page->mainFrame() ? m_page->mainFrame()->document() : 0; 161 if (document && previousHost == m_accessDomain && !m_accessPath.startsWith("/")) { 162 String currentPath = currentURL.path(); 163 164 size_t index = currentPath.reverseFind('/'); 165 if (index != WTF::notFound) 166 m_accessPath = document->completeURL(currentPath.left(index + 1) + m_accessPath).path(); 167 } 168 169 return true; 170 } 171 172 void WMLPageState::resetAccessControlData() 173 { 174 m_hasAccessControlData = false; 175 m_accessDomain = String(); 176 m_accessPath = String(); 177 } 178 179 bool WMLPageState::canAccessDeck() const 180 { 181 if (!m_hasAccessControlData) 182 return true; 183 184 KURL previousURL, currentURL; 185 if (!tryAccessHistoryURLs(m_page, previousURL, currentURL)) 186 return true; 187 188 if (equalIgnoringFragmentIdentifier(previousURL, currentURL)) 189 return true; 190 191 return hostIsAllowedToAccess(hostFromURL(previousURL)) && pathIsAllowedToAccess(previousURL.path()); 192 } 193 194 bool WMLPageState::hostIsAllowedToAccess(const String& host) const 195 { 196 // Spec: The access domain is suffix-matched against the domain name portion of the referring URI 197 Vector<String> subdomainsAllowed; 198 if (m_accessDomain.contains('.')) 199 m_accessDomain.split('.', subdomainsAllowed); 200 else 201 subdomainsAllowed.append(m_accessDomain); 202 203 Vector<String> subdomainsCheck; 204 if (host.contains('.')) 205 host.split('.', subdomainsCheck); 206 else 207 subdomainsCheck.append(host); 208 209 Vector<String>::iterator itAllowed = subdomainsAllowed.end() - 1; 210 Vector<String>::iterator beginAllowed = subdomainsAllowed.begin(); 211 212 Vector<String>::iterator itCheck = subdomainsCheck.end() - 1; 213 Vector<String>::iterator beginCheck = subdomainsCheck.begin(); 214 215 bool hostOk = true; 216 for (; itAllowed >= beginAllowed && itCheck >= beginCheck; ) { 217 if (*itAllowed != *itCheck) { 218 hostOk = false; 219 break; 220 } 221 222 --itAllowed; 223 --itCheck; 224 } 225 226 return hostOk; 227 } 228 229 bool WMLPageState::pathIsAllowedToAccess(const String& path) const 230 { 231 // Spec: The access path is prefix matched against the path portion of the referring URI 232 Vector<String> subpathsAllowed; 233 if (m_accessPath.contains('/')) 234 m_accessPath.split('/', subpathsAllowed); 235 else 236 subpathsAllowed.append(m_accessPath); 237 238 Vector<String> subpathsCheck; 239 if (path.contains('/')) 240 path.split('/', subpathsCheck); 241 else 242 subpathsCheck.append(path); 243 244 Vector<String>::iterator itAllowed = subpathsAllowed.begin(); 245 Vector<String>::iterator endAllowed = subpathsAllowed.end(); 246 247 Vector<String>::iterator itCheck = subpathsCheck.begin(); 248 Vector<String>::iterator endCheck = subpathsCheck.end(); 249 250 bool pathOk = true; 251 for (; itAllowed != endAllowed && itCheck != endCheck; ) { 252 if (*itAllowed != *itCheck) { 253 pathOk = false; 254 break; 255 } 256 257 ++itAllowed; 258 ++itCheck; 259 } 260 261 return pathOk; 262 } 263 264 } 265 266 #endif 267