1 /*************************************************************************
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
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"
36 #pragma warning(push, 1) /* disable warnings within system headers */
38 #include <curl/curl.h>
43 #include <curl/curl.h>
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))
61 rtl::Reference
< DownloadInteractionHandler
>Handler
;
63 rtl::OUString DestinationDir
;
64 oslFileHandle FileHandle
;
66 osl::Condition
& StopCondition
;
69 OutData(osl::Condition
& rCondition
) : FileHandle(NULL
), Offset(0), StopCondition(rCondition
), curl(NULL
) {};
72 //------------------------------------------------------------------------------
74 static void openFile( OutData
& out
)
77 curl_easy_getinfo(out
.curl
, CURLINFO_EFFECTIVE_URL
, &effective_url
);
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('/');
93 out
.File
= out
.DestinationDir
+ rtl::OStringToOUString(aURL
.copy(nIndex
), RTL_TEXTENCODING_UTF8
);
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();
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
)
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
;
143 //------------------------------------------------------------------------------
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
)
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 //------------------------------------------------------------------------------
164 progress_callback( void *clientp
, double dltotal
, double dlnow
, double ultotal
, double ulnow
)
169 OutData
*out
= reinterpret_cast < OutData
* > (clientp
);
173 if( ! out
->StopCondition
.check() )
175 double fPercent
= (dlnow
+ out
->Offset
) * 100 / (dltotal
+ out
->Offset
);
179 // Do not report progress for redirection replies
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
);
188 // If stop condition is set, return non 0 value to abort
192 //------------------------------------------------------------------------------
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();
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);
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
);
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
);
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
)
302 out
.Handler
->downloadFinished(out
.File
);
306 // Avoid target file being removed
307 else if( (CURLE_ABORTED_BY_CALLBACK
== cc
) || out
.StopCondition
.check() )
310 // Only report errors when not stopped
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
);
328 //------------------------------------------------------------------------------
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
);
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
371 // osl_removeFile(out.File.pData);
374 m_aCondition
.reset();
378 //------------------------------------------------------------------------------