Update ooo320-m1
[ooovba.git] / extensions / source / update / check / download.cxx
blob3cf77ac18d0a9c75ca23a9b5eb49c6ec0761abe8
1 /*************************************************************************
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * Copyright 2008 by Sun Microsystems, Inc.
7 * OpenOffice.org - a multi-platform office productivity suite
9 * $RCSfile: download.cxx,v $
10 * $Revision: 1.7.88.1 $
12 * This file is part of OpenOffice.org.
14 * OpenOffice.org is free software: you can redistribute it and/or modify
15 * it under the terms of the GNU Lesser General Public License version 3
16 * only, as published by the Free Software Foundation.
18 * OpenOffice.org is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 * GNU Lesser General Public License version 3 for more details
22 * (a copy is included in the LICENSE file that accompanied this code).
24 * You should have received a copy of the GNU Lesser General Public License
25 * version 3 along with OpenOffice.org. If not, see
26 * <http://www.openoffice.org/license.html>
27 * for a copy of the LGPLv3 License.
29 ************************************************************************/
31 // MARKER(update_precomp.py): autogen include statement, do not remove
32 #include "precompiled_extensions.hxx"
34 #if defined WNT
35 #ifdef _MSC_VER
36 #pragma warning(push, 1) /* disable warnings within system headers */
37 #endif
38 #include <curl/curl.h>
39 #ifdef _MSC_VER
40 #pragma warning(pop)
41 #endif
42 #else
43 #include <curl/curl.h>
44 #endif
45 #include <com/sun/star/beans/PropertyValue.hpp>
46 #include <com/sun/star/container/XNameAccess.hpp>
47 #include <com/sun/star/lang/XMultiServiceFactory.hpp>
49 #include "download.hxx"
51 namespace beans = com::sun::star::beans ;
52 namespace container = com::sun::star::container ;
53 namespace lang = com::sun::star::lang ;
54 namespace uno = com::sun::star::uno ;
56 #define UNISTRING(s) rtl::OUString(RTL_CONSTASCII_USTRINGPARAM(s))
59 struct OutData
61 rtl::Reference< DownloadInteractionHandler >Handler;
62 rtl::OUString File;
63 rtl::OUString DestinationDir;
64 oslFileHandle FileHandle;
65 sal_uInt64 Offset;
66 osl::Condition& StopCondition;
67 CURL *curl;
69 OutData(osl::Condition& rCondition) : FileHandle(NULL), Offset(0), StopCondition(rCondition), curl(NULL) {};
72 //------------------------------------------------------------------------------
74 static void openFile( OutData& out )
76 char * effective_url;
77 curl_easy_getinfo(out.curl, CURLINFO_EFFECTIVE_URL, &effective_url);
79 double fDownloadSize;
80 curl_easy_getinfo(out.curl, CURLINFO_CONTENT_LENGTH_DOWNLOAD, &fDownloadSize);
82 rtl::OString aURL(effective_url);
84 // ensure no trailing '/'
85 sal_Int32 nLen = aURL.getLength();
86 while( (nLen > 0) && ('/' == aURL[nLen-1]) )
87 aURL = aURL.copy(0, --nLen);
89 // extract file name last '/'
90 sal_Int32 nIndex = aURL.lastIndexOf('/');
91 if( nIndex > 0 )
93 out.File = out.DestinationDir + rtl::OStringToOUString(aURL.copy(nIndex), RTL_TEXTENCODING_UTF8);
95 oslFileError rc;
97 // Give the user an overwrite warning if the target file exists
98 const sal_Int32 openFlags = osl_File_OpenFlag_Write | osl_File_OpenFlag_Create;
101 rc = osl_openFile(out.File.pData, &out.FileHandle, openFlags);
103 if( osl_File_E_EXIST == rc && ! out.Handler->downloadTargetExists(out.File) )
105 out.StopCondition.set();
106 break;
109 } while( osl_File_E_EXIST == rc );
111 if( osl_File_E_None == rc )
112 out.Handler->downloadStarted(out.File, (sal_Int64) fDownloadSize);
116 //------------------------------------------------------------------------------
118 static inline rtl::OString
119 getStringValue(const uno::Reference< container::XNameAccess >& xNameAccess, const rtl::OUString& aName)
121 rtl::OString aRet;
123 OSL_ASSERT(xNameAccess->hasByName(aName));
124 uno::Any aValue = xNameAccess->getByName(aName);
126 return rtl::OUStringToOString(aValue.get<rtl::OUString>(), RTL_TEXTENCODING_UTF8);
129 //------------------------------------------------------------------------------
131 static inline sal_Int32
132 getInt32Value(const uno::Reference< container::XNameAccess >& xNameAccess,
133 const rtl::OUString& aName, sal_Int32 nDefault=-1)
135 OSL_ASSERT(xNameAccess->hasByName(aName));
136 uno::Any aValue = xNameAccess->getByName(aName);
138 sal_Int32 n=nDefault;
139 aValue >>= n;
140 return n;
143 //------------------------------------------------------------------------------
145 static size_t
146 write_function( void *ptr, size_t size, size_t nmemb, void *stream )
148 OutData *out = reinterpret_cast < OutData * > (stream);
150 if( NULL == out->FileHandle )
151 openFile(*out);
153 sal_uInt64 nBytesWritten = 0;
155 if( NULL != out->FileHandle )
156 osl_writeFile(out->FileHandle, ptr, size * nmemb, &nBytesWritten);
158 return (size_t) nBytesWritten;
161 //------------------------------------------------------------------------------
163 static int
164 progress_callback( void *clientp, double dltotal, double dlnow, double ultotal, double ulnow )
166 (void) ultotal;
167 (void) ulnow;
169 OutData *out = reinterpret_cast < OutData * > (clientp);
171 OSL_ASSERT( out );
173 if( ! out->StopCondition.check() )
175 double fPercent = (dlnow + out->Offset) * 100 / (dltotal + out->Offset);
176 if( fPercent < 0 )
177 fPercent = 0;
179 // Do not report progress for redirection replies
180 long nCode;
181 curl_easy_getinfo(out->curl, CURLINFO_RESPONSE_CODE, &nCode);
182 if( (nCode != 302) && (nCode != 303) && (dltotal > 0) )
183 out->Handler->downloadProgressAt((sal_Int8)fPercent);
185 return 0;
188 // If stop condition is set, return non 0 value to abort
189 return -1;
192 //------------------------------------------------------------------------------
194 void
195 Download::getProxyForURL(const rtl::OUString& rURL, rtl::OString& rHost, sal_Int32& rPort) const
197 if( !m_xContext.is() )
198 throw uno::RuntimeException(
199 UNISTRING( "Download: empty component context" ),
200 uno::Reference< uno::XInterface >() );
202 uno::Reference< lang::XMultiComponentFactory > xServiceManager(m_xContext->getServiceManager());
204 if( !xServiceManager.is() )
205 throw uno::RuntimeException(
206 UNISTRING( "Download: unable to obtain service manager from component context" ),
207 uno::Reference< uno::XInterface >() );
209 uno::Reference< lang::XMultiServiceFactory > xConfigProvider(
210 xServiceManager->createInstanceWithContext( UNISTRING( "com.sun.star.configuration.ConfigurationProvider" ), m_xContext ),
211 uno::UNO_QUERY_THROW);
213 beans::PropertyValue aProperty;
214 aProperty.Name = UNISTRING( "nodepath" );
215 aProperty.Value = uno::makeAny( UNISTRING("org.openoffice.Inet/Settings") );
217 uno::Sequence< uno::Any > aArgumentList( 1 );
218 aArgumentList[0] = uno::makeAny( aProperty );
220 uno::Reference< container::XNameAccess > xNameAccess(
221 xConfigProvider->createInstanceWithArguments(
222 UNISTRING("com.sun.star.configuration.ConfigurationAccess"), aArgumentList ),
223 uno::UNO_QUERY_THROW );
225 OSL_ASSERT(xNameAccess->hasByName(UNISTRING("ooInetProxyType")));
226 uno::Any aValue = xNameAccess->getByName(UNISTRING("ooInetProxyType"));
228 sal_Int32 nProxyType = aValue.get< sal_Int32 >();
229 if( 0 != nProxyType ) // type 0 means "direct connection to the internet
231 if( rURL.matchAsciiL(RTL_CONSTASCII_STRINGPARAM("http:")) )
233 rHost = getStringValue(xNameAccess, UNISTRING("ooInetHTTPProxyName"));
234 rPort = getInt32Value(xNameAccess, UNISTRING("ooInetHTTPProxyPort"));
236 else if( rURL.matchAsciiL(RTL_CONSTASCII_STRINGPARAM("https:")) )
238 rHost = getStringValue(xNameAccess, UNISTRING("ooInetHTTPSProxyName"));
239 rPort = getInt32Value(xNameAccess, UNISTRING("ooInetHTTPSProxyPort"));
241 else if( rURL.matchAsciiL(RTL_CONSTASCII_STRINGPARAM("ftp:")) )
243 rHost = getStringValue(xNameAccess, UNISTRING("ooInetFTPProxyName"));
244 rPort = getInt32Value(xNameAccess, UNISTRING("ooInetFTPProxyPort"));
249 //------------------------------------------------------------------------------
251 bool curl_run(const rtl::OUString& rURL, OutData& out, const rtl::OString& aProxyHost, sal_Int32 nProxyPort)
253 /* Need to investigate further whether it is necessary to call
254 * curl_global_init or not - leave it for now (as the ftp UCB content
255 * provider does as well).
258 CURL * pCURL = curl_easy_init();
259 bool ret = false;
261 if( NULL != pCURL )
263 out.curl = pCURL;
265 rtl::OString aURL(rtl::OUStringToOString(rURL, RTL_TEXTENCODING_UTF8));
266 curl_easy_setopt(pCURL, CURLOPT_URL, aURL.getStr());
268 // enable redirection
269 curl_easy_setopt(pCURL, CURLOPT_FOLLOWLOCATION, 1);
271 // write function
272 curl_easy_setopt(pCURL, CURLOPT_WRITEDATA, &out);
273 curl_easy_setopt(pCURL, CURLOPT_WRITEFUNCTION, &write_function);
275 // progress handler - Condition::check unfortunatly is not defined const
276 curl_easy_setopt(pCURL, CURLOPT_NOPROGRESS, 0);
277 curl_easy_setopt(pCURL, CURLOPT_PROGRESSFUNCTION, &progress_callback);
278 curl_easy_setopt(pCURL, CURLOPT_PROGRESSDATA, &out);
280 // proxy
281 curl_easy_setopt(pCURL, CURLOPT_PROXY, aProxyHost.getStr());
282 curl_easy_setopt(pCURL, CURLOPT_PROXYTYPE, CURLPROXY_HTTP);
283 if( -1 != nProxyPort )
284 curl_easy_setopt(pCURL, CURLOPT_PROXYPORT, nProxyPort);
286 if( out.Offset > 0 )
288 // curl_off_t offset = nOffset; libcurl seems to be compiled with large
289 // file support (and we not) ..
290 sal_Int64 offset = (sal_Int64) out.Offset;
291 curl_easy_setopt(pCURL, CURLOPT_RESUME_FROM_LARGE, offset);
294 CURLcode cc = curl_easy_perform(pCURL);
296 // treat zero byte downloads as errors
297 if( NULL == out.FileHandle )
298 openFile(out);
300 if( CURLE_OK == cc )
302 out.Handler->downloadFinished(out.File);
303 ret = true;
306 // Avoid target file being removed
307 else if( (CURLE_ABORTED_BY_CALLBACK == cc) || out.StopCondition.check() )
308 ret = true;
310 // Only report errors when not stopped
311 else
313 rtl::OString aMessage(RTL_CONSTASCII_STRINGPARAM("Unknown error"));
315 const char * error_message = curl_easy_strerror(cc);
316 if( NULL != error_message )
317 aMessage = error_message;
319 out.Handler->downloadStalled( rtl::OStringToOUString(aMessage, RTL_TEXTENCODING_UTF8) );
322 curl_easy_cleanup(pCURL);
325 return ret;
328 //------------------------------------------------------------------------------
330 bool
331 Download::start(const rtl::OUString& rURL, const rtl::OUString& rFile, const rtl::OUString& rDestinationDir)
333 OSL_ASSERT( m_aHandler.is() );
335 OutData out(m_aCondition);
337 out.File = rFile;
338 out.DestinationDir = rDestinationDir;
339 out.Handler = m_aHandler;
341 if( rFile.getLength() > 0 )
343 oslFileError rc = osl_openFile(rFile.pData, &out.FileHandle, osl_File_OpenFlag_Write);
345 if( osl_File_E_None == rc )
347 // Set file pointer to the end of the file on resume
348 if( osl_File_E_None == osl_setFilePos(out.FileHandle, osl_Pos_End, 0) )
350 osl_getFilePos(out.FileHandle, &out.Offset);
353 else if( osl_File_E_NOENT == rc ) // file has been deleted meanwhile ..
354 out.File = rtl::OUString();
357 rtl::OString aProxyHost;
358 sal_Int32 nProxyPort = -1;
359 getProxyForURL(rURL, aProxyHost, nProxyPort);
361 bool ret = curl_run(rURL, out, aProxyHost, nProxyPort);
363 if( NULL != out.FileHandle )
365 osl_syncFile(out.FileHandle);
366 osl_closeFile(out.FileHandle);
368 // #i90930# Don't remove already downloaded bits, when curl_run reports an error
369 // because later calls might be successful
370 // if( ! ret )
371 // osl_removeFile(out.File.pData);
374 m_aCondition.reset();
375 return ret;
378 //------------------------------------------------------------------------------
380 void
381 Download::stop()
383 m_aCondition.set();