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 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 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 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 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 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 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 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 136 void CurlRequest::setProvideCredentialsCallback( bool (*callback)(long statusCode, void *userdata) throw (DAVException), void *userdata ) 137 { 138 provideCredentialsCallback = callback; 139 provideCredentialsUserdata = userdata; 140 } 141 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 372 void CurlRequest::saveResponseBodyTo( const uno::Reference< io::XOutputStream > & xOutStream) 373 { 374 xOutputStream = xOutStream; 375 } 376 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 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