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