tdf#130857 qt weld: Implement QtInstanceWidget::strip_mnemonic
[LibreOffice.git] / extensions / source / update / check / download.cxx
blob17056cc53820d926e84f6ce2cdb61e8932729749
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <sal/config.h>
22 #include <string_view>
24 #include <curl/curl.h>
26 #include <systools/curlinit.hxx>
28 #include <o3tl/string_view.hxx>
29 #include <osl/diagnose.h>
30 #include <osl/file.h>
31 #include <com/sun/star/beans/PropertyValue.hpp>
32 #include <com/sun/star/configuration/theDefaultProvider.hpp>
33 #include <com/sun/star/container/XNameAccess.hpp>
34 #include <com/sun/star/lang/XMultiServiceFactory.hpp>
36 #include "download.hxx"
38 namespace beans = css::beans ;
39 namespace container = css::container ;
40 namespace lang = css::lang ;
41 namespace uno = css::uno ;
43 namespace {
45 struct OutData
47 rtl::Reference< DownloadInteractionHandler >Handler;
48 OUString File;
49 OUString DestinationDir;
50 oslFileHandle FileHandle;
51 sal_uInt64 Offset;
52 osl::Condition& StopCondition;
53 CURL *curl;
55 explicit OutData(osl::Condition& rCondition) : FileHandle(nullptr), Offset(0), StopCondition(rCondition), curl(nullptr) {};
60 static void openFile( OutData& out )
62 char * effective_url;
63 curl_easy_getinfo(out.curl, CURLINFO_EFFECTIVE_URL, &effective_url);
65 curl_off_t nDownloadSize;
66 curl_easy_getinfo(out.curl, CURLINFO_CONTENT_LENGTH_DOWNLOAD_T, &nDownloadSize);
68 OString aURL(effective_url);
70 // ensure no trailing '/'
71 sal_Int32 nLen = aURL.getLength();
72 while( (nLen > 0) && ('/' == aURL[nLen-1]) )
73 aURL = aURL.copy(0, --nLen);
75 // extract file name last '/'
76 sal_Int32 nIndex = aURL.lastIndexOf('/');
77 if( nIndex > 0 )
79 out.File = out.DestinationDir
80 + OStringToOUString(aURL.subView(nIndex), RTL_TEXTENCODING_UTF8);
82 oslFileError rc;
84 // Give the user an overwrite warning if the target file exists
85 const sal_Int32 openFlags = osl_File_OpenFlag_Write | osl_File_OpenFlag_Create;
88 rc = osl_openFile(out.File.pData, &out.FileHandle, openFlags);
90 if( osl_File_E_EXIST == rc && ! out.Handler->downloadTargetExists(out.File) )
92 out.StopCondition.set();
93 break;
96 } while( osl_File_E_EXIST == rc );
98 if( osl_File_E_None == rc )
99 out.Handler->downloadStarted(out.File, static_cast<sal_Int64>(nDownloadSize));
104 static OString
105 getStringValue(const uno::Reference< container::XNameAccess >& xNameAccess, const OUString& aName)
107 OSL_ASSERT(xNameAccess->hasByName(aName));
108 uno::Any aValue = xNameAccess->getByName(aName);
110 return OUStringToOString(aValue.get<OUString>(), RTL_TEXTENCODING_UTF8);
114 static sal_Int32
115 getInt32Value(const uno::Reference< container::XNameAccess >& xNameAccess,
116 const OUString& aName)
118 OSL_ASSERT(xNameAccess->hasByName(aName));
119 uno::Any aValue = xNameAccess->getByName(aName);
121 sal_Int32 n = -1;
122 aValue >>= n;
123 return n;
127 static size_t
128 write_function( void *ptr, size_t size, size_t nmemb, void *stream )
130 OutData *out = static_cast < OutData * > (stream);
132 if( nullptr == out->FileHandle )
133 openFile(*out);
135 sal_uInt64 nBytesWritten = 0;
137 if( nullptr != out->FileHandle )
138 osl_writeFile(out->FileHandle, ptr, size * nmemb, &nBytesWritten);
140 return static_cast<size_t>(nBytesWritten);
144 static int
145 progress_callback( void *clientp, curl_off_t dltotal, curl_off_t dlnow, SAL_UNUSED_PARAMETER curl_off_t, SAL_UNUSED_PARAMETER curl_off_t)
147 OutData *out = static_cast < OutData * > (clientp);
149 assert(out);
151 if (out && !out->StopCondition.check())
153 float fPercent = 0;
154 if ( dltotal + out->Offset )
155 fPercent = (dlnow + out->Offset) * 100 / (dltotal + out->Offset);
156 if( fPercent < 0 )
157 fPercent = 0;
159 // Do not report progress for redirection replies
160 long nCode;
161 curl_easy_getinfo(out->curl, CURLINFO_RESPONSE_CODE, &nCode);
162 if( (nCode != 302) && (nCode != 303) && (dltotal > 0) )
163 out->Handler->downloadProgressAt(static_cast<sal_Int8>(fPercent));
165 return 0;
168 // If stop condition is set, return non 0 value to abort
169 return -1;
173 void
174 Download::getProxyForURL(std::u16string_view rURL, OString& rHost, sal_Int32& rPort) const
176 uno::Reference< lang::XMultiServiceFactory > xConfigProvider(
177 css::configuration::theDefaultProvider::get( m_xContext ) );
179 beans::PropertyValue aProperty;
180 aProperty.Name = "nodepath";
181 aProperty.Value <<= u"org.openoffice.Inet/Settings"_ustr;
183 uno::Sequence< uno::Any > aArgumentList{ uno::Any(aProperty) };
185 uno::Reference< container::XNameAccess > xNameAccess(
186 xConfigProvider->createInstanceWithArguments(
187 u"com.sun.star.configuration.ConfigurationAccess"_ustr, aArgumentList ),
188 uno::UNO_QUERY_THROW );
190 OSL_ASSERT(xNameAccess->hasByName(u"ooInetProxyType"_ustr));
191 uno::Any aValue = xNameAccess->getByName(u"ooInetProxyType"_ustr);
193 sal_Int32 nProxyType = aValue.get< sal_Int32 >();
194 if( 0 != nProxyType ) // type 0 means "direct connection to the internet
196 if( o3tl::starts_with(rURL, u"http:") )
198 rHost = getStringValue(xNameAccess, u"ooInetHTTPProxyName"_ustr);
199 rPort = getInt32Value(xNameAccess, u"ooInetHTTPProxyPort"_ustr);
201 else if( o3tl::starts_with(rURL, u"https:") )
203 rHost = getStringValue(xNameAccess, u"ooInetHTTPSProxyName"_ustr);
204 rPort = getInt32Value(xNameAccess, u"ooInetHTTPSProxyPort"_ustr);
210 static bool curl_run(std::u16string_view rURL, OutData& out, const OString& aProxyHost, sal_Int32 nProxyPort)
212 /* Need to investigate further whether it is necessary to call
213 * curl_global_init or not - leave it for now (as the ftp UCB content
214 * provider does as well).
217 CURL * pCURL = curl_easy_init();
218 bool ret = false;
220 if( nullptr != pCURL )
222 ::InitCurl_easy(pCURL);
224 out.curl = pCURL;
226 OString aURL(OUStringToOString(rURL, RTL_TEXTENCODING_UTF8));
227 (void)curl_easy_setopt(pCURL, CURLOPT_URL, aURL.getStr());
229 // abort on http errors
230 (void)curl_easy_setopt(pCURL, CURLOPT_FAILONERROR, 1);
232 // enable redirection
233 (void)curl_easy_setopt(pCURL, CURLOPT_FOLLOWLOCATION, 1);
234 // only allow redirect to https://
235 #if (LIBCURL_VERSION_MAJOR > 7) || (LIBCURL_VERSION_MAJOR == 7 && LIBCURL_VERSION_MINOR >= 85)
236 (void)curl_easy_setopt(pCURL, CURLOPT_REDIR_PROTOCOLS_STR, "https");
237 #else
238 (void)curl_easy_setopt(pCURL, CURLOPT_REDIR_PROTOCOLS, CURLPROTO_HTTPS);
239 #endif
241 // write function
242 (void)curl_easy_setopt(pCURL, CURLOPT_WRITEDATA, &out);
243 (void)curl_easy_setopt(pCURL, CURLOPT_WRITEFUNCTION, &write_function);
245 // progress handler - Condition::check unfortunately is not defined const
246 (void)curl_easy_setopt(pCURL, CURLOPT_NOPROGRESS, 0);
247 (void)curl_easy_setopt(pCURL, CURLOPT_XFERINFOFUNCTION, &progress_callback);
248 (void)curl_easy_setopt(pCURL, CURLOPT_PROGRESSDATA, &out);
250 // proxy
251 (void)curl_easy_setopt(pCURL, CURLOPT_PROXY, aProxyHost.getStr());
252 (void)curl_easy_setopt(pCURL, CURLOPT_PROXYTYPE, CURLPROXY_HTTP);
253 if( -1 != nProxyPort )
254 (void)curl_easy_setopt(pCURL, CURLOPT_PROXYPORT, nProxyPort);
256 if( out.Offset > 0 )
258 // curl_off_t offset = nOffset; libcurl seems to be compiled with large
259 // file support (and we not) ..
260 sal_Int64 offset = static_cast<sal_Int64>(out.Offset);
261 (void)curl_easy_setopt(pCURL, CURLOPT_RESUME_FROM_LARGE, offset);
264 CURLcode cc = curl_easy_perform(pCURL);
266 // treat zero byte downloads as errors
267 if( nullptr == out.FileHandle )
268 openFile(out);
270 if( CURLE_OK == cc )
272 out.Handler->downloadFinished(out.File);
273 ret = true;
276 if ( CURLE_PARTIAL_FILE == cc )
278 // this sometimes happens, when a user throws away his user data, but has already
279 // completed the download of an update.
280 curl_off_t nDownloadSize;
281 curl_easy_getinfo( pCURL, CURLINFO_CONTENT_LENGTH_DOWNLOAD_T, &nDownloadSize );
282 if ( -1 == nDownloadSize )
284 out.Handler->downloadFinished(out.File);
285 ret = true;
289 // Avoid target file being removed
290 else if( (CURLE_ABORTED_BY_CALLBACK == cc) || out.StopCondition.check() )
291 ret = true;
293 // Only report errors when not stopped
294 else
296 OString aMessage("Unknown error"_ostr);
298 const char * error_message = curl_easy_strerror(cc);
299 if( nullptr != error_message )
300 aMessage = error_message;
302 if ( CURLE_HTTP_RETURNED_ERROR == cc )
304 long nError;
305 curl_easy_getinfo( pCURL, CURLINFO_RESPONSE_CODE, &nError );
307 if ( 403 == nError )
308 aMessage += " 403: Access denied!";
309 else if ( 404 == nError )
310 aMessage += " 404: File not found!";
311 else if ( 416 == nError )
313 // we got this error probably, because we already downloaded the file
314 out.Handler->downloadFinished(out.File);
315 ret = true;
317 else
319 aMessage += ":error code = " + OString::number( nError ) + " !";
322 if ( !ret )
323 out.Handler->downloadStalled( OStringToOUString(aMessage, RTL_TEXTENCODING_UTF8) );
326 curl_easy_cleanup(pCURL);
329 return ret;
333 bool
334 Download::start(const OUString& rURL, const OUString& rFile, const OUString& rDestinationDir)
336 OSL_ASSERT( m_aHandler.is() );
338 OutData out(m_aCondition);
339 OUString aFile( rFile );
341 // when rFile is empty, there is no remembered file name. If there is already a file with the
342 // same name ask the user if she wants to resume a download or restart the download
343 if ( aFile.isEmpty() )
345 // GetFileName()
346 OUString aURL( rURL );
347 // ensure no trailing '/'
348 sal_Int32 nLen = aURL.getLength();
349 while( (nLen > 0) && ('/' == aURL[ nLen-1 ]) )
350 aURL = aURL.copy( 0, --nLen );
352 // extract file name last '/'
353 sal_Int32 nIndex = aURL.lastIndexOf('/');
354 aFile = rDestinationDir + aURL.subView( nIndex );
356 // check for existing file
357 oslFileError rc = osl_openFile( aFile.pData, &out.FileHandle, osl_File_OpenFlag_Write | osl_File_OpenFlag_Create );
358 osl_closeFile(out.FileHandle);
359 out.FileHandle = nullptr;
361 if( osl_File_E_EXIST == rc )
363 if ( m_aHandler->checkDownloadDestination( aURL.copy( nIndex+1 ) ) )
365 osl_removeFile( aFile.pData );
366 aFile.clear();
368 else
369 m_aHandler->downloadStarted( aFile, 0 );
371 else
373 osl_removeFile( aFile.pData );
374 aFile.clear();
378 out.File = aFile;
379 out.DestinationDir = rDestinationDir;
380 out.Handler = m_aHandler;
382 if( !aFile.isEmpty() )
384 oslFileError rc = osl_openFile(aFile.pData, &out.FileHandle, osl_File_OpenFlag_Write);
386 if( osl_File_E_None == rc )
388 // Set file pointer to the end of the file on resume
389 if( osl_File_E_None == osl_setFilePos(out.FileHandle, osl_Pos_End, 0) )
391 osl_getFilePos(out.FileHandle, &out.Offset);
394 else if( osl_File_E_NOENT == rc ) // file has been deleted meanwhile ..
395 out.File.clear();
398 OString aProxyHost;
399 sal_Int32 nProxyPort = -1;
400 getProxyForURL(rURL, aProxyHost, nProxyPort);
402 bool ret = curl_run(rURL, out, aProxyHost, nProxyPort);
404 if( nullptr != out.FileHandle )
406 osl_syncFile(out.FileHandle);
407 osl_closeFile(out.FileHandle);
409 // #i90930# Don't remove already downloaded bits, when curl_run reports an error
410 // because later calls might be successful
411 // if( ! ret )
412 // osl_removeFile(out.File.pData);
415 m_aCondition.reset();
416 return ret;
420 void
421 Download::stop()
423 m_aCondition.set();
426 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */