1 /**************************************************************
2 *
3 * Licensed to the Apache Software Foundation (ASF) under one
4 * or more contributor license agreements. See the NOTICE file
5 * distributed with this work for additional information
6 * regarding copyright ownership. The ASF licenses this file
7 * to you under the Apache License, Version 2.0 (the
8 * "License"); you may not use this file except in compliance
9 * with the License. You may obtain a copy of the License at
10 *
11 * http://www.apache.org/licenses/LICENSE-2.0
12 *
13 * Unless required by applicable law or agreed to in writing,
14 * software distributed under the License is distributed on an
15 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16 * KIND, either express or implied. See the License for the
17 * specific language governing permissions and limitations
18 * under the License.
19 *
20 *************************************************************/
21
22
23
24 // MARKER(update_precomp.py): autogen include statement, do not remove
25 #include "precompiled_webdav.hxx"
26
27 #include "CurlRequest.hxx"
28
29 #include <string.h>
30
31 using namespace ::com::sun::star;
32 using namespace http_dav_ucp;
33
CurlRequest(CURL * curl)34 CurlRequest::CurlRequest( CURL *curl )
35 : curl( curl )
36 , curlUrl( NULL )
37 , requestHeaders( NULL )
38 , requestBody( NULL )
39 , requestBodyOffset( 0 )
40 , requestBodySize( 0 )
41 , useChunkedEncoding( false )
42 , provideCredentialsCallback( NULL )
43 , provideCredentialsUserdata( NULL )
44 , statusCode( 0 )
45 , responseBodyInputStream( new CurlInputStream() )
46 {
47 curl_easy_setopt( curl, CURLOPT_HEADERFUNCTION, Curl_HeaderReceived );
48 curl_easy_setopt( curl, CURLOPT_HEADERDATA, this );
49 curl_easy_setopt( curl, CURLOPT_WRITEFUNCTION, Curl_MoreBodyReceived );
50 curl_easy_setopt( curl, CURLOPT_WRITEDATA, this );
51 }
52
~CurlRequest()53 CurlRequest::~CurlRequest()
54 {
55 if ( curlUrl != NULL )
56 curl_url_cleanup( curlUrl );
57 curl_easy_setopt( curl, CURLOPT_CURLU, NULL );
58
59 curl_easy_setopt( curl, CURLOPT_HTTPHEADER, NULL );
60 if ( requestHeaders != NULL )
61 curl_slist_free_all( requestHeaders );
62 curl_easy_setopt( curl, CURLOPT_READFUNCTION, NULL );
63 curl_easy_setopt( curl, CURLOPT_READDATA, NULL );
64 curl_easy_setopt( curl, CURLOPT_INFILESIZE, -1 );
65 curl_easy_setopt( curl, CURLOPT_SEEKFUNCTION, NULL );
66 curl_easy_setopt( curl, CURLOPT_SEEKDATA, NULL );
67 curl_easy_setopt( curl, CURLOPT_HEADERFUNCTION, NULL );
68 curl_easy_setopt( curl, CURLOPT_HEADERDATA, NULL );
69 curl_easy_setopt( curl, CURLOPT_CUSTOMREQUEST, NULL );
70 curl_easy_setopt( curl, CURLOPT_WRITEFUNCTION, NULL );
71 curl_easy_setopt( curl, CURLOPT_WRITEDATA, NULL );
72 }
73
addHeader(const rtl::OString & name,const rtl::OString & value)74 void CurlRequest::addHeader( const rtl::OString &name, const rtl::OString &value) throw (DAVException)
75 {
76 rtl::OString line = name + ": " + value;
77 struct curl_slist *appended = curl_slist_append( requestHeaders, line.getStr() );
78 if ( appended != NULL )
79 {
80 requestHeaders = appended;
81 curl_easy_setopt( curl, CURLOPT_HTTPHEADER, requestHeaders );
82 }
83 else
84 throw DAVException( DAVException::DAV_SESSION_CREATE, rtl::OUString::createFromAscii( "Out of memory" ) );
85 }
86
setRequestBody(const char * body,size_t size)87 void CurlRequest::setRequestBody( const char *body, size_t size )
88 {
89 requestBody = body;
90 requestBodyOffset = 0;
91 requestBodySize = size;
92
93 curl_easy_setopt( curl, CURLOPT_READFUNCTION, Curl_SendMoreBody );
94 curl_easy_setopt( curl, CURLOPT_READDATA, this );
95 curl_easy_setopt( curl, CURLOPT_SEEKFUNCTION, Curl_SeekCallback );
96 curl_easy_setopt( curl, CURLOPT_SEEKDATA, this );
97 if ( useChunkedEncoding )
98 addHeader( "Transfer-Encoding", "chunked" );
99 else
100 curl_easy_setopt( curl, CURLOPT_INFILESIZE_LARGE, (curl_off_t)size );
101 }
102
Curl_SeekCallback(void * userdata,curl_off_t offset,int origin)103 int CurlRequest::Curl_SeekCallback( void *userdata, curl_off_t offset, int origin )
104 {
105 CurlRequest *request = static_cast< CurlRequest* >( userdata );
106 if ( origin == SEEK_SET )
107 request->requestBodyOffset = (size_t) offset;
108 else if ( origin == SEEK_CUR )
109 request->requestBodyOffset += (size_t) offset;
110 else if ( origin == SEEK_END )
111 request->requestBodyOffset += (size_t) ((curl_off_t)request->requestBodySize + offset);
112 else
113 return CURL_SEEKFUNC_CANTSEEK;
114 if ( request->requestBodyOffset > request->requestBodySize )
115 request->requestBodyOffset = request->requestBodySize;
116 return CURL_SEEKFUNC_OK;
117 }
118
Curl_SendMoreBody(char * buffer,size_t size,size_t nitems,void * userdata)119 size_t CurlRequest::Curl_SendMoreBody( char *buffer, size_t size, size_t nitems, void *userdata )
120 {
121 CurlRequest *request = static_cast< CurlRequest* >( userdata );
122 OSL_TRACE("Curl_SendMoreBody");
123 return request->curlSendMoreBody( buffer, nitems );
124 }
125
curlSendMoreBody(char * buffer,size_t len)126 size_t CurlRequest::curlSendMoreBody( char *buffer, size_t len )
127 {
128 size_t bytesToSend = requestBodySize - requestBodyOffset;
129 if ( bytesToSend > len )
130 bytesToSend = len;
131 memcpy( buffer, requestBody, bytesToSend );
132 requestBodyOffset += bytesToSend;
133 return bytesToSend;
134 }
135
setProvideCredentialsCallback(bool (* callback)(long statusCode,void * userdata)throw (DAVException),void * userdata)136 void CurlRequest::setProvideCredentialsCallback( bool (*callback)(long statusCode, void *userdata) throw (DAVException), void *userdata )
137 {
138 provideCredentialsCallback = callback;
139 provideCredentialsUserdata = userdata;
140 }
141
setURI(CurlUri uri,rtl::OUString path)142 void CurlRequest::setURI( CurlUri uri, rtl::OUString path ) throw (DAVException)
143 {
144 if ( curlUrl != NULL )
145 {
146 curl_url_cleanup( curlUrl );
147 curlUrl = NULL;
148 }
149
150 curlUrl = curl_url();
151 if ( curlUrl == NULL )
152 throw DAVException( DAVException::DAV_SESSION_CREATE, rtl::OUString::createFromAscii( "Out of memory" ) );
153
154 if ( CurlUri( path ).GetHost().isEmpty() )
155 {
156 // "path" is really a path, not a URI
157 curl_url_set( curlUrl, CURLUPART_URL, rtl::OUStringToOString( uri.GetURI(), RTL_TEXTENCODING_UTF8 ).getStr(), 0 );
158 curl_url_set( curlUrl, CURLUPART_PATH, rtl::OUStringToOString( path, RTL_TEXTENCODING_UTF8 ).getStr(), 0 );
159 }
160 else
161 {
162 // The "path" is a full URI
163 curl_url_set( curlUrl, CURLUPART_URL, rtl::OUStringToOString( path, RTL_TEXTENCODING_UTF8 ).getStr(), 0 );
164 }
165 curl_easy_setopt( curl, CURLOPT_CURLU, curlUrl );
166 }
167
copy(CurlUri uri,rtl::OUString path)168 CURLcode CurlRequest::copy( CurlUri uri, rtl::OUString path ) throw(DAVException)
169 {
170 setURI( uri, path );
171 curl_easy_setopt( curl, CURLOPT_HTTPGET, 1L );
172 curl_easy_setopt( curl, CURLOPT_CUSTOMREQUEST, "COPY" );
173 return perform();
174 }
175
delete_(CurlUri uri,rtl::OUString path)176 CURLcode CurlRequest::delete_( CurlUri uri, rtl::OUString path ) throw (DAVException)
177 {
178 setURI( uri, path );
179 curl_easy_setopt( curl, CURLOPT_HTTPGET, 1L );
180 curl_easy_setopt( curl, CURLOPT_CUSTOMREQUEST, "DELETE" );
181 return perform();
182 }
183
get(CurlUri uri,rtl::OUString path)184 CURLcode CurlRequest::get( CurlUri uri, rtl::OUString path ) throw(DAVException)
185 {
186 setURI( uri, path );
187 curl_easy_setopt( curl, CURLOPT_HTTPGET, 1L );
188 curl_easy_setopt( curl, CURLOPT_CUSTOMREQUEST, "GET" );
189 return perform();
190 }
191
head(CurlUri uri,rtl::OUString path)192 CURLcode CurlRequest::head( CurlUri uri, rtl::OUString path ) throw (DAVException)
193 {
194 setURI( uri, path );
195 curl_easy_setopt( curl, CURLOPT_NOBODY, 1L );
196 curl_easy_setopt( curl, CURLOPT_CUSTOMREQUEST, "HEAD" );
197 return perform();
198 }
199
lock(CurlUri uri,rtl::OUString path)200 CURLcode CurlRequest::lock( CurlUri uri, rtl::OUString path ) throw (DAVException)
201 {
202 setURI( uri, path );
203 curl_easy_setopt( curl, CURLOPT_UPLOAD, 1L );
204 curl_easy_setopt( curl, CURLOPT_CUSTOMREQUEST, "LOCK" );
205 return perform();
206 }
207
mkcol(CurlUri uri,rtl::OUString path)208 CURLcode CurlRequest::mkcol( CurlUri uri, rtl::OUString path ) throw (DAVException)
209 {
210 setURI( uri, path );
211 curl_easy_setopt( curl, CURLOPT_HTTPGET, 1L );
212 curl_easy_setopt( curl, CURLOPT_CUSTOMREQUEST, "MKCOL" );
213 return perform();
214 }
215
move(CurlUri uri,rtl::OUString path)216 CURLcode CurlRequest::move( CurlUri uri, rtl::OUString path ) throw (DAVException)
217 {
218 setURI( uri, path );
219 curl_easy_setopt( curl, CURLOPT_HTTPGET, 1L );
220 curl_easy_setopt( curl, CURLOPT_CUSTOMREQUEST, "MOVE" );
221 return perform();
222 }
223
post(CurlUri uri,rtl::OUString path)224 CURLcode CurlRequest::post( CurlUri uri, rtl::OUString path ) throw (DAVException)
225 {
226 setURI( uri, path );
227 curl_easy_setopt( curl, CURLOPT_UPLOAD, 1L );
228 curl_easy_setopt( curl, CURLOPT_CUSTOMREQUEST, "POST" );
229 return perform();
230 }
231
propfind(CurlUri uri,rtl::OUString path)232 CURLcode CurlRequest::propfind( CurlUri uri, rtl::OUString path ) throw (DAVException)
233 {
234 setURI( uri, path );
235 curl_easy_setopt( curl, CURLOPT_UPLOAD, 1L );
236 curl_easy_setopt( curl, CURLOPT_CUSTOMREQUEST, "PROPFIND" );
237 return perform();
238 }
239
proppatch(CurlUri uri,rtl::OUString path)240 CURLcode CurlRequest::proppatch( CurlUri uri, rtl::OUString path ) throw (DAVException)
241 {
242 setURI( uri, path );
243 curl_easy_setopt( curl, CURLOPT_UPLOAD, 1L );
244 curl_easy_setopt( curl, CURLOPT_CUSTOMREQUEST, "PROPPATCH" );
245 return perform();
246 }
247
put(CurlUri uri,rtl::OUString path)248 CURLcode CurlRequest::put( CurlUri uri, rtl::OUString path ) throw (DAVException)
249 {
250 setURI( uri, path );
251 curl_easy_setopt( curl, CURLOPT_UPLOAD, 1L );
252 curl_easy_setopt( curl, CURLOPT_CUSTOMREQUEST, "PUT" );
253 return perform();
254 }
255
unlock(CurlUri uri,rtl::OUString path)256 CURLcode CurlRequest::unlock( CurlUri uri, rtl::OUString path ) throw (DAVException)
257 {
258 setURI( uri, path );
259 curl_easy_setopt( curl, CURLOPT_HTTPGET, 1L );
260 curl_easy_setopt( curl, CURLOPT_CUSTOMREQUEST, "UNLOCK" );
261 return perform();
262 }
263
perform()264 CURLcode CurlRequest::perform() throw (DAVException)
265 {
266 CURLcode rc = curl_easy_perform( curl );
267 long statusCode = 0;
268 curl_easy_getinfo( curl, CURLINFO_RESPONSE_CODE, &statusCode );
269 if ( ( statusCode == 401 || statusCode == 407 ) && provideCredentialsCallback != NULL )
270 {
271 bool haveCredentials = provideCredentialsCallback( statusCode, provideCredentialsUserdata );
272 if ( haveCredentials )
273 {
274 // rewind body:
275 requestBodyOffset = 0;
276 // retry with credentials:
277 rc = curl_easy_perform( curl );
278 // If this was to authenticate with the proxy, we may need to authenticate
279 // with the destination host too:
280 if ( statusCode == 407 )
281 {
282 curl_easy_getinfo( curl, CURLINFO_RESPONSE_CODE, &statusCode );
283 if ( statusCode == 401 && provideCredentialsCallback != NULL )
284 {
285 haveCredentials = provideCredentialsCallback( statusCode, provideCredentialsUserdata );
286 if ( haveCredentials )
287 {
288 // rewind body:
289 requestBodyOffset = 0;
290 // retry with credentials:
291 rc = curl_easy_perform( curl );
292 }
293 }
294 }
295 }
296 }
297 return rc;
298 }
299
Curl_HeaderReceived(char * buffer,size_t size,size_t nitems,void * userdata)300 size_t CurlRequest::Curl_HeaderReceived( char *buffer, size_t size, size_t nitems, void *userdata )
301 {
302 CurlRequest *request = static_cast< CurlRequest* >( userdata );
303 OSL_TRACE("Curl_HeaderReceived");
304 request->curlHeaderReceived( buffer, nitems );
305 return nitems;
306 }
307
curlHeaderReceived(const char * buffer,size_t size)308 void CurlRequest::curlHeaderReceived( const char *buffer, size_t size )
309 {
310 rtl::OString lineCrLf( buffer, size );
311 sal_Int32 cr = lineCrLf.indexOf( "\r" );
312 if ( cr < 0 )
313 return;
314
315 rtl::OString line = lineCrLf.copy( 0, cr );
316 if ( line.indexOf( "HTTP/" ) == 0 )
317 {
318 // Status line
319 // Throw away any response headers from a prior response:
320 responseHeaders.clear();
321 sal_Int32 idxFirstSpace = line.indexOf( ' ' );
322 if ( idxFirstSpace > 0 )
323 {
324 int idxSecondSpace = line.indexOf( ' ', idxFirstSpace + 1 );
325 if ( idxSecondSpace > 0 )
326 {
327 reasonPhrase = line.copy( idxSecondSpace + 1 );
328 statusCode = line.copy( idxFirstSpace + 1, idxSecondSpace - idxFirstSpace - 1 ).toInt32();
329 }
330 else
331 {
332 reasonPhrase = "";
333 statusCode = line.copy( idxFirstSpace + 1 ).toInt32();
334 }
335 }
336 }
337 else
338 {
339 // Header line
340 if ( line.getLength() == 0 )
341 {
342 // End of HTTP header
343 // Discard any intermediate body from 100 Trying or 401 Authentication required:
344 responseBodyInputStream = new CurlInputStream();
345 return;
346 }
347 sal_Int32 colon = line.indexOf( ':' );
348 if ( colon < 0 )
349 {
350 OSL_TRACE("Non-empty HTTP line without a ':'. Folded header deprecated by RFC 7230?");
351 return;
352 }
353 Header header;
354 header.name = line.copy( 0, colon ).toAsciiLowerCase();
355 header.value = line.copy( colon + 1 ).trim();
356 responseHeaders.push_back(header);
357 }
358 }
359
findResponseHeader(const rtl::OString & name)360 const CurlRequest::Header *CurlRequest::findResponseHeader( const rtl::OString &name )
361 {
362 std::vector< CurlRequest::Header >::const_iterator it( responseHeaders.begin() );
363 const std::vector< CurlRequest::Header >::const_iterator end( responseHeaders.end() );
364 for ( ; it != end; it++ )
365 {
366 if ( name.equalsIgnoreAsciiCase( it->name ) )
367 return &(*it);
368 }
369 return NULL;
370 }
371
saveResponseBodyTo(const uno::Reference<io::XOutputStream> & xOutStream)372 void CurlRequest::saveResponseBodyTo( const uno::Reference< io::XOutputStream > & xOutStream)
373 {
374 xOutputStream = xOutStream;
375 }
376
Curl_MoreBodyReceived(char * buffer,size_t size,size_t nitems,void * userdata)377 size_t CurlRequest::Curl_MoreBodyReceived( char *buffer, size_t size, size_t nitems, void *userdata )
378 {
379 CurlRequest *request = static_cast< CurlRequest* >( userdata );
380 request->curlMoreBodyReceived( buffer, nitems );
381 return nitems;
382 }
383
curlMoreBodyReceived(const char * buffer,size_t size)384 void CurlRequest::curlMoreBodyReceived( const char *buffer, size_t size )
385 {
386 if ( xOutputStream.is() )
387 {
388 const uno::Sequence< sal_Int8 > aDataSeq( (sal_Int8 *)buffer, size );
389 xOutputStream->writeBytes( aDataSeq );
390 }
391 else if ( responseBodyInputStream.is() )
392 responseBodyInputStream->AddToStream( buffer, size );
393 }
394