1/* 2 * Copyright (C) 2010, 2011 Apple Inc. All rights reserved. 3 * Copyright (C) 2010 Brent Fulgham <bfulgham@webkit.org> 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 14 * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' 15 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, 16 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 17 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS 18 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 19 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 20 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 21 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 22 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 23 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 24 * THE POSSIBILITY OF SUCH DAMAGE. 25 */ 26 27#include "config.h" 28#include "Download.h" 29 30#include "DataReference.h" 31#include "DownloadSoupErrors.h" 32#include <WebCore/NotImplemented.h> 33#include <WebCore/ResourceHandleInternal.h> 34#include <gio/gio.h> 35#include <wtf/gobject/GOwnPtr.h> 36#include <wtf/gobject/GRefPtr.h> 37#include <wtf/text/CString.h> 38 39#if PLATFORM(GTK) 40#include <glib/gi18n-lib.h> 41#endif 42 43using namespace WebCore; 44 45namespace WebKit { 46 47class DownloadClient : public ResourceHandleClient { 48 WTF_MAKE_NONCOPYABLE(DownloadClient); 49public: 50 DownloadClient(Download* download) 51 : m_download(download) 52 , m_handleResponseLaterID(0) 53 { 54 } 55 56 ~DownloadClient() 57 { 58 if (m_handleResponseLaterID) 59 g_source_remove(m_handleResponseLaterID); 60 } 61 62 void downloadFailed(const ResourceError& error) 63 { 64 m_download->didFail(error, CoreIPC::DataReference()); 65 } 66 67 void didReceiveResponse(ResourceHandle*, const ResourceResponse& response) 68 { 69 m_response = adoptGRef(response.toSoupMessage()); 70 m_download->didReceiveResponse(response); 71 72 if (response.httpStatusCode() >= 400) { 73 downloadFailed(platformDownloadNetworkError(response.httpStatusCode(), response.url().string(), response.httpStatusText())); 74 return; 75 } 76 77 String suggestedFilename = response.suggestedFilename(); 78 if (suggestedFilename.isEmpty()) { 79 KURL url = response.url(); 80 url.setQuery(String()); 81 url.removeFragmentIdentifier(); 82 suggestedFilename = decodeURLEscapeSequences(url.lastPathComponent()); 83 } 84 85 bool overwrite; 86 String destinationURI = m_download->decideDestinationWithSuggestedFilename(suggestedFilename, overwrite); 87 if (destinationURI.isEmpty()) { 88#if PLATFORM(GTK) 89 GOwnPtr<char> buffer(g_strdup_printf(_("Cannot determine destination URI for download with suggested filename %s"), suggestedFilename.utf8().data())); 90 String errorMessage = String::fromUTF8(buffer.get()); 91#else 92 String errorMessage = makeString("Cannot determine destination URI for download with suggested filename ", suggestedFilename); 93#endif 94 downloadFailed(platformDownloadDestinationError(response, errorMessage)); 95 return; 96 } 97 98 GRefPtr<GFile> file = adoptGRef(g_file_new_for_uri(destinationURI.utf8().data())); 99 GOwnPtr<GError> error; 100 m_outputStream = adoptGRef(g_file_replace(file.get(), 0, TRUE, G_FILE_CREATE_NONE, 0, &error.outPtr())); 101 if (!m_outputStream) { 102 downloadFailed(platformDownloadDestinationError(response, error->message)); 103 return; 104 } 105 106 GRefPtr<GFileInfo> info = adoptGRef(g_file_info_new()); 107 g_file_info_set_attribute_string(info.get(), "metadata::download-uri", response.url().string().utf8().data()); 108 g_file_set_attributes_async(file.get(), info.get(), G_FILE_QUERY_INFO_NONE, G_PRIORITY_DEFAULT, 0, 0, 0); 109 110 m_download->didCreateDestination(destinationURI); 111 } 112 113 void didReceiveData(ResourceHandle*, const char* data, int length, int /*encodedDataLength*/) 114 { 115 if (m_handleResponseLaterID) { 116 g_source_remove(m_handleResponseLaterID); 117 handleResponse(); 118 } 119 120 gsize bytesWritten; 121 GOwnPtr<GError> error; 122 g_output_stream_write_all(G_OUTPUT_STREAM(m_outputStream.get()), data, length, &bytesWritten, 0, &error.outPtr()); 123 if (error) { 124 downloadFailed(platformDownloadDestinationError(ResourceResponse(m_response.get()), error->message)); 125 return; 126 } 127 m_download->didReceiveData(bytesWritten); 128 } 129 130 void didFinishLoading(ResourceHandle*, double) 131 { 132 m_outputStream = 0; 133 m_download->didFinish(); 134 } 135 136 void didFail(ResourceHandle*, const ResourceError& error) 137 { 138 downloadFailed(platformDownloadNetworkError(error.errorCode(), error.failingURL(), error.localizedDescription())); 139 } 140 141 void wasBlocked(ResourceHandle*) 142 { 143 notImplemented(); 144 } 145 146 void cannotShowURL(ResourceHandle*) 147 { 148 notImplemented(); 149 } 150 151 void handleResponse() 152 { 153 m_handleResponseLaterID = 0; 154 didReceiveResponse(0, m_delayedResponse); 155 } 156 157 static gboolean handleResponseLaterCallback(DownloadClient* downloadClient) 158 { 159 downloadClient->handleResponse(); 160 return FALSE; 161 } 162 163 void handleResponseLater(const ResourceResponse& response) 164 { 165 ASSERT(!m_response); 166 ASSERT(!m_handleResponseLaterID); 167 168 m_delayedResponse = response; 169 170 // Call didReceiveResponse in an idle to make sure the download is added 171 // to the DownloadManager downloads map. 172 m_handleResponseLaterID = g_idle_add_full(G_PRIORITY_DEFAULT, reinterpret_cast<GSourceFunc>(handleResponseLaterCallback), this, 0); 173 } 174 175 Download* m_download; 176 GRefPtr<GFileOutputStream> m_outputStream; 177 GRefPtr<SoupMessage> m_response; 178 ResourceResponse m_delayedResponse; 179 unsigned m_handleResponseLaterID; 180}; 181 182void Download::start() 183{ 184 ASSERT(!m_downloadClient); 185 ASSERT(!m_resourceHandle); 186 m_downloadClient = adoptPtr(new DownloadClient(this)); 187 m_resourceHandle = ResourceHandle::create(0, m_request, m_downloadClient.get(), false, false); 188 didStart(); 189} 190 191void Download::startWithHandle(ResourceHandle* resourceHandle, const ResourceResponse& response) 192{ 193 ASSERT(!m_downloadClient); 194 ASSERT(!m_resourceHandle); 195 m_downloadClient = adoptPtr(new DownloadClient(this)); 196 resourceHandle->setClient(m_downloadClient.get()); 197 m_resourceHandle = resourceHandle; 198 didStart(); 199 static_cast<DownloadClient*>(m_downloadClient.get())->handleResponseLater(response); 200} 201 202void Download::cancel() 203{ 204 if (!m_resourceHandle) 205 return; 206 m_resourceHandle->cancel(); 207 didCancel(CoreIPC::DataReference()); 208 m_resourceHandle = 0; 209} 210 211void Download::platformInvalidate() 212{ 213 if (m_resourceHandle) { 214 m_resourceHandle->setClient(0); 215 m_resourceHandle->cancel(); 216 m_resourceHandle = 0; 217 } 218 m_downloadClient.release(); 219} 220 221void Download::didDecideDestination(const String& /*destination*/, bool /*allowOverwrite*/) 222{ 223 notImplemented(); 224} 225 226void Download::platformDidFinish() 227{ 228 m_resourceHandle = 0; 229} 230 231void Download::receivedCredential(const AuthenticationChallenge&, const Credential&) 232{ 233 notImplemented(); 234} 235 236void Download::receivedRequestToContinueWithoutCredential(const AuthenticationChallenge&) 237{ 238 notImplemented(); 239} 240 241void Download::receivedCancellation(const AuthenticationChallenge&) 242{ 243 notImplemented(); 244} 245 246void Download::continueWithoutCredential(const AuthenticationChallenge &) 247{ 248 notImplemented(); 249} 250 251void Download::useCredential(const AuthenticationChallenge&, const Credential&) 252{ 253 notImplemented(); 254} 255 256void Download::cancelAuthenticationChallenge(const AuthenticationChallenge&) 257{ 258 notImplemented(); 259} 260 261} // namespace WebKit 262