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 "BackForwardList.h" 28 #include "CString.h" 29 #include "Document.h" 30 #include "Frame.h" 31 #include "HistoryItem.h" 32 #include "KURL.h" 33 #include "Page.h" 34 35 namespace WebCore { 36 37 WMLPageState::WMLPageState(Page* page) 38 : m_page(page) 39 , m_hasAccessControlData(false) 40 { 41 } 42 43 WMLPageState::~WMLPageState() 44 { 45 m_variables.clear(); 46 } 47 48 #ifndef NDEBUG 49 // Debugging helper for use within gdb 50 void WMLPageState::dump() 51 { 52 WMLVariableMap::iterator it = m_variables.begin(); 53 WMLVariableMap::iterator end = m_variables.end(); 54 55 fprintf(stderr, "Dumping WMLPageState (this=%p) associated with Page (page=%p)...\n", this, m_page); 56 for (; it != end; ++it) 57 fprintf(stderr, "\t-> name: '%s'\tvalue: '%s'\n", (*it).first.latin1().data(), (*it).second.latin1().data()); 58 } 59 #endif 60 61 void WMLPageState::reset() 62 { 63 // Remove all the variables 64 m_variables.clear(); 65 66 // Clear the navigation history state 67 if (BackForwardList* list = m_page ? m_page->backForwardList() : 0) 68 list->clearWMLPageHistory(); 69 } 70 71 static inline String normalizedHostName(const String& passedHost) 72 { 73 if (passedHost.contains("127.0.0.1")) { 74 String host = passedHost; 75 return host.replace("127.0.0.1", "localhost"); 76 } 77 78 return passedHost; 79 } 80 81 static inline String hostFromURL(const KURL& url) 82 { 83 // Default to "localhost" 84 String host = normalizedHostName(url.host()); 85 return host.isEmpty() ? "localhost" : host; 86 } 87 88 static KURL urlForHistoryItem(Frame* frame, HistoryItem* item) 89 { 90 // For LayoutTests we need to find the corresponding WML frame in the test document 91 // to be able to test access-control correctly. Remember that WML is never supposed 92 // to be embedded anywhere, so the purpose is to simulate a standalone WML document. 93 if (frame->document()->isWMLDocument()) 94 return item->url(); 95 96 const HistoryItemVector& childItems = item->children(); 97 HistoryItemVector::const_iterator it = childItems.begin(); 98 const HistoryItemVector::const_iterator end = childItems.end(); 99 100 for (; it != end; ++it) { 101 const RefPtr<HistoryItem> childItem = *it; 102 Frame* childFrame = frame->tree()->child(childItem->target()); 103 if (!childFrame) 104 continue; 105 106 if (Document* childDocument = childFrame->document()) { 107 if (childDocument->isWMLDocument()) 108 return childItem->url(); 109 } 110 } 111 112 return item->url(); 113 } 114 115 static bool tryAccessHistoryURLs(Page* page, KURL& previousURL, KURL& currentURL) 116 { 117 if (!page) 118 return false; 119 120 Frame* frame = page->mainFrame(); 121 if (!frame || !frame->document()) 122 return false; 123 124 BackForwardList* list = page->backForwardList(); 125 if (!list) 126 return false; 127 128 HistoryItem* previousItem = list->backItem(); 129 if (!previousItem) 130 return false; 131 132 HistoryItem* currentItem = list->currentItem(); 133 if (!currentItem) 134 return false; 135 136 previousURL = urlForHistoryItem(frame, previousItem); 137 currentURL = urlForHistoryItem(frame, currentItem); 138 139 return true; 140 } 141 142 bool WMLPageState::processAccessControlData(const String& domain, const String& path) 143 { 144 if (m_hasAccessControlData) 145 return false; 146 147 m_hasAccessControlData = true; 148 149 KURL previousURL, currentURL; 150 if (!tryAccessHistoryURLs(m_page, previousURL, currentURL)) 151 return true; 152 153 // Spec: The path attribute defaults to the value "/" 154 m_accessPath = path.isEmpty() ? "/" : path; 155 156 // Spec: The domain attribute defaults to the current decks domain. 157 String previousHost = hostFromURL(previousURL); 158 m_accessDomain = domain.isEmpty() ? previousHost : normalizedHostName(domain); 159 160 // Spec: To simplify the development of applications that may not know the absolute path to the 161 // current deck, the path attribute accepts relative URIs. The user agent converts the relative 162 // path to an absolute path and then performs prefix matching against the PATH attribute. 163 Document* document = m_page->mainFrame() ? m_page->mainFrame()->document() : 0; 164 if (document && previousHost == m_accessDomain && !m_accessPath.startsWith("/")) { 165 String currentPath = currentURL.path(); 166 167 size_t index = currentPath.reverseFind('/'); 168 if (index != WTF::notFound) 169 m_accessPath = document->completeURL(currentPath.left(index + 1) + m_accessPath).path(); 170 } 171 172 return true; 173 } 174 175 void WMLPageState::resetAccessControlData() 176 { 177 m_hasAccessControlData = false; 178 m_accessDomain = String(); 179 m_accessPath = String(); 180 } 181 182 bool WMLPageState::canAccessDeck() const 183 { 184 if (!m_hasAccessControlData) 185 return true; 186 187 KURL previousURL, currentURL; 188 if (!tryAccessHistoryURLs(m_page, previousURL, currentURL)) 189 return true; 190 191 if (equalIgnoringFragmentIdentifier(previousURL, currentURL)) 192 return true; 193 194 return hostIsAllowedToAccess(hostFromURL(previousURL)) && pathIsAllowedToAccess(previousURL.path()); 195 } 196 197 bool WMLPageState::hostIsAllowedToAccess(const String& host) const 198 { 199 // Spec: The access domain is suffix-matched against the domain name portion of the referring URI 200 Vector<String> subdomainsAllowed; 201 if (m_accessDomain.contains('.')) 202 m_accessDomain.split('.', subdomainsAllowed); 203 else 204 subdomainsAllowed.append(m_accessDomain); 205 206 Vector<String> subdomainsCheck; 207 if (host.contains('.')) 208 host.split('.', subdomainsCheck); 209 else 210 subdomainsCheck.append(host); 211 212 Vector<String>::iterator itAllowed = subdomainsAllowed.end() - 1; 213 Vector<String>::iterator beginAllowed = subdomainsAllowed.begin(); 214 215 Vector<String>::iterator itCheck = subdomainsCheck.end() - 1; 216 Vector<String>::iterator beginCheck = subdomainsCheck.begin(); 217 218 bool hostOk = true; 219 for (; itAllowed >= beginAllowed && itCheck >= beginCheck; ) { 220 if (*itAllowed != *itCheck) { 221 hostOk = false; 222 break; 223 } 224 225 --itAllowed; 226 --itCheck; 227 } 228 229 return hostOk; 230 } 231 232 bool WMLPageState::pathIsAllowedToAccess(const String& path) const 233 { 234 // Spec: The access path is prefix matched against the path portion of the referring URI 235 Vector<String> subpathsAllowed; 236 if (m_accessPath.contains('/')) 237 m_accessPath.split('/', subpathsAllowed); 238 else 239 subpathsAllowed.append(m_accessPath); 240 241 Vector<String> subpathsCheck; 242 if (path.contains('/')) 243 path.split('/', subpathsCheck); 244 else 245 subpathsCheck.append(path); 246 247 Vector<String>::iterator itAllowed = subpathsAllowed.begin(); 248 Vector<String>::iterator endAllowed = subpathsAllowed.end(); 249 250 Vector<String>::iterator itCheck = subpathsCheck.begin(); 251 Vector<String>::iterator endCheck = subpathsCheck.end(); 252 253 bool pathOk = true; 254 for (; itAllowed != endAllowed && itCheck != endCheck; ) { 255 if (*itAllowed != *itCheck) { 256 pathOk = false; 257 break; 258 } 259 260 ++itAllowed; 261 ++itCheck; 262 } 263 264 return pathOk; 265 } 266 267 } 268 269 #endif 270