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 // MARKER(update_precomp.py): autogen include statement, do not remove 23 #include "precompiled_webdav.hxx" 24 25 #include <string.h> 26 #include <rtl/uri.hxx> 27 #include <rtl/ustring.hxx> 28 #include <rtl/ustrbuf.hxx> 29 #include "CurlUri.hxx" 30 #include "DAVException.hxx" 31 32 #include <curl/curl.h> 33 34 #include "../inc/urihelper.hxx" 35 36 using namespace http_dav_ucp; 37 38 # if defined __SUNPRO_CC 39 # pragma enable_warn 40 #endif 41 42 // ------------------------------------------------------------------- 43 // Constructor 44 // ------------------------------------------------------------------- 45 46 namespace { 47 48 inline bool matchIgnoreAsciiCase(rtl::OString const & rStr1, 49 sal_Char const * pStr2, 50 sal_Int32 nStr2Len) SAL_THROW(()) 51 { 52 return 53 rtl_str_shortenedCompareIgnoreAsciiCase_WithLength( 54 rStr1.getStr(), rStr1.getLength(), pStr2, nStr2Len, nStr2Len) 55 == 0; 56 } 57 58 inline rtl::OUString getURLStringPart( const CURLU *curlUrl, CURLUPart part, unsigned int flags ) 59 { 60 char *value = NULL; 61 CURLUcode rc = curl_url_get( const_cast<CURLU*>( curlUrl ), part, &value, flags ); 62 if ( rc == CURLUE_OK ) 63 { 64 rtl::OUString str = rtl::OStringToOUString( value, RTL_TEXTENCODING_UTF8 ); 65 curl_free( value ); 66 return str; 67 } 68 return rtl::OUString(); 69 } 70 71 } 72 73 CurlUri::CurlUri( const CURLU * inUri ) 74 throw ( DAVException ) 75 : mURI() 76 , mScheme() 77 , mUserName() 78 , mPassword() 79 , mHostName() 80 , mPort() 81 , mPath() 82 { 83 if ( inUri == 0 ) 84 throw DAVException( DAVException::DAV_INVALID_ARG ); 85 mCurlUri = curl_url_dup( const_cast<CURLU *>(inUri) ); 86 if ( mCurlUri == NULL ) 87 throw DAVException( DAVException::DAV_HTTP_ERROR, 88 rtl::OUString::createFromAscii( "Out of memory" ), 89 SC_INSUFFICIENT_STORAGE ); 90 91 char * uri; 92 CURLUcode rc = curl_url_get( mCurlUri, CURLUPART_URL, &uri, 0 ); 93 if ( rc != CURLUE_OK ) 94 { 95 curl_url_cleanup( mCurlUri ); 96 throw DAVException( DAVException::DAV_INVALID_ARG ); 97 } 98 curl_free( uri ); 99 100 init( mCurlUri ); 101 102 calculateURI(); 103 } 104 105 CurlUri::CurlUri( const rtl::OUString & inUri ) 106 throw ( DAVException ) 107 : mCurlUri( 0 ) 108 , mURI() 109 , mScheme() 110 , mUserName() 111 , mPassword() 112 , mHostName() 113 , mPort( 0 ) 114 , mPath() 115 { 116 if ( inUri.getLength() <= 0 ) 117 throw DAVException( DAVException::DAV_INVALID_ARG ); 118 mCurlUri = curl_url(); 119 if ( mCurlUri == NULL ) 120 throw DAVException( DAVException::DAV_HTTP_ERROR, 121 rtl::OUString::createFromAscii( "Out of memory" ), 122 SC_INSUFFICIENT_STORAGE ); 123 124 // #i77023# 125 rtl::OUString aEscapedUri( ucb_impl::urihelper::encodeURI( inUri ) ); 126 127 rtl::OString theInputUri( 128 aEscapedUri.getStr(), aEscapedUri.getLength(), RTL_TEXTENCODING_UTF8 ); 129 130 if ( curl_url_set( mCurlUri, CURLUPART_URL, theInputUri.getStr(), 0 ) != CURLUE_OK ) 131 { 132 // I kid you not: 133 // Sometimes, we are just given a URL's path part, 134 // and CREATING THE URL ABSOLUTELY MUST SUCCEED, even though the resulting URL 135 // of "/path/to/file.txt" will be the terrible "://:0/path/to/file.txt" !!! 136 // (Such input usually comes from the callers of GetPathBaseName() and the like.) 137 if ( !theInputUri.isEmpty() && theInputUri[0] == '/' && 138 curl_url_set( mCurlUri, CURLUPART_PATH, theInputUri.getStr(), 0 ) != CURLUE_OK ) 139 { 140 throw DAVException( DAVException::DAV_INVALID_ARG ); 141 } 142 } 143 144 rtl::OUString portString = getURLStringPart( mCurlUri, CURLUPART_PORT, 0 ); 145 if ( portString.isEmpty() ) 146 { 147 rtl::OUString defaultPortW = getURLStringPart( mCurlUri, CURLUPART_PORT, CURLU_DEFAULT_PORT ); 148 rtl::OString defaultPortA = OUStringToOString( defaultPortW, RTL_TEXTENCODING_UTF8 ); 149 if ( !defaultPortA.isEmpty() ) 150 curl_url_set( mCurlUri, CURLUPART_PORT, defaultPortA.getStr(), 0 ); 151 } 152 rtl::OUString path = getURLStringPart( mCurlUri, CURLUPART_PATH, 0 ); 153 if ( path.isEmpty() ) 154 curl_url_set( mCurlUri, CURLUPART_PATH, "/", 0); 155 156 init( mCurlUri ); 157 158 calculateURI(); 159 } 160 161 CurlUri::CurlUri( const CurlUri &curlUri ) 162 throw ( DAVException ) 163 : mURI() 164 , mScheme() 165 , mUserName() 166 , mPassword() 167 , mHostName() 168 , mPort() 169 , mPath() 170 { 171 mCurlUri = curl_url_dup( curlUri.mCurlUri ); 172 if ( mCurlUri == NULL ) 173 throw DAVException( DAVException::DAV_HTTP_ERROR, 174 rtl::OUString::createFromAscii( "Out of memory" ), 175 SC_INSUFFICIENT_STORAGE ); 176 177 char * uri; 178 CURLUcode rc = curl_url_get( mCurlUri, CURLUPART_URL, &uri, 0 ); 179 if ( rc != CURLUE_OK ) 180 { 181 curl_url_cleanup( mCurlUri ); 182 throw DAVException( DAVException::DAV_INVALID_ARG ); 183 } 184 curl_free( uri ); 185 186 init( mCurlUri ); 187 188 calculateURI(); 189 } 190 191 void CurlUri::init( const CURLU * pUri ) 192 { 193 mScheme = getURLStringPart( pUri, CURLUPART_SCHEME, 0 ); 194 mUserName = getURLStringPart( pUri, CURLUPART_USER, 0 ); 195 mPassword = getURLStringPart( pUri, CURLUPART_PASSWORD, 0 ); 196 mHostName = getURLStringPart( pUri, CURLUPART_HOST, 0 ); 197 rtl::OUString portString = getURLStringPart( pUri, CURLUPART_PORT, 0); 198 mPort = 0; 199 if ( !portString.isEmpty() ) 200 mPort = portString.toInt32(); 201 mPath = getURLStringPart( pUri, CURLUPART_PATH, 0 ); 202 203 rtl::OUString query = getURLStringPart( pUri, CURLUPART_QUERY, 0 ); 204 if ( !query.isEmpty() ) 205 { 206 mPath += rtl::OUString::createFromAscii( "?" ); 207 mPath += query; 208 } 209 210 rtl::OUString fragment = getURLStringPart( pUri, CURLUPART_FRAGMENT, 0 ); 211 if ( !fragment.isEmpty() ) 212 { 213 mPath += rtl::OUString::createFromAscii( "#" ); 214 mPath += fragment; 215 } 216 } 217 218 CurlUri::~CurlUri( ) 219 { 220 if ( mCurlUri ) 221 curl_url_cleanup( mCurlUri ); 222 } 223 224 void CurlUri::calculateURI () 225 { 226 rtl::OUStringBuffer aBuf( mScheme ); 227 aBuf.appendAscii( "://" ); 228 if ( mUserName.getLength() > 0 ) 229 { 230 aBuf.append( mUserName ); 231 if ( mPassword.getLength() > 0 ) 232 { 233 aBuf.appendAscii( ":" ); 234 aBuf.append( mPassword ); 235 } 236 aBuf.appendAscii( "@" ); 237 } 238 // Is host a numeric IPv6 address? 239 if ( ( mHostName.indexOf( ':' ) != -1 ) && 240 ( mHostName[ 0 ] != sal_Unicode( '[' ) ) ) 241 { 242 aBuf.appendAscii( "[" ); 243 aBuf.append( mHostName ); 244 aBuf.appendAscii( "]" ); 245 } 246 else 247 { 248 aBuf.append( mHostName ); 249 } 250 251 // append port, but only, if not default port. 252 bool bAppendPort = true; 253 switch ( mPort ) 254 { 255 case DEFAULT_HTTP_PORT: 256 bAppendPort = !mScheme.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM( "http" ) ); 257 break; 258 259 case DEFAULT_HTTPS_PORT: 260 bAppendPort = !mScheme.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM( "https" ) ); 261 break; 262 } 263 if ( bAppendPort ) 264 { 265 aBuf.appendAscii( ":" ); 266 aBuf.append( rtl::OUString::valueOf( mPort ) ); 267 } 268 aBuf.append( mPath ); 269 270 mURI = aBuf.makeStringAndClear(); 271 } 272 273 ::rtl::OUString CurlUri::GetPathBaseName () const 274 { 275 sal_Int32 nPos = mPath.lastIndexOf ('/'); 276 sal_Int32 nTrail = 0; 277 if (nPos == mPath.getLength () - 1) 278 { 279 // Trailing slash found. Skip. 280 nTrail = 1; 281 nPos = mPath.lastIndexOf ('/', nPos); 282 } 283 if (nPos != -1) 284 { 285 rtl::OUString aTemp( 286 mPath.copy (nPos + 1, mPath.getLength () - nPos - 1 - nTrail) ); 287 288 // query, fragment present? 289 nPos = aTemp.indexOf( '?' ); 290 if ( nPos == -1 ) 291 nPos = aTemp.indexOf( '#' ); 292 293 if ( nPos != -1 ) 294 aTemp = aTemp.copy( 0, nPos ); 295 296 return aTemp; 297 } 298 else 299 return rtl::OUString::createFromAscii ("/"); 300 } 301 302 bool CurlUri::operator== ( const CurlUri & rOther ) const 303 { 304 return ( mURI == rOther.mURI ); 305 } 306 307 ::rtl::OUString CurlUri::GetPathBaseNameUnescaped () const 308 { 309 return unescape( GetPathBaseName() ); 310 } 311 312 void CurlUri::AppendPath (const rtl::OUString& rPath) 313 { 314 if (mPath.lastIndexOf ('/') != mPath.getLength () - 1) 315 mPath += rtl::OUString::createFromAscii ("/"); 316 317 mPath += rPath; 318 calculateURI (); 319 }; 320 321 // static 322 rtl::OUString CurlUri::escapeSegment( const rtl::OUString& segment ) 323 { 324 return rtl::Uri::encode( segment, 325 rtl_UriCharClassPchar, 326 rtl_UriEncodeIgnoreEscapes, 327 RTL_TEXTENCODING_UTF8 ); 328 } 329 330 // static 331 rtl::OUString CurlUri::unescape( const rtl::OUString& segment ) 332 { 333 return rtl::Uri::decode( segment, 334 rtl_UriDecodeWithCharset, 335 RTL_TEXTENCODING_UTF8 ); 336 } 337 338 // static 339 rtl::OUString CurlUri::makeConnectionEndPointString( 340 const rtl::OUString & rHostName, int nPort ) 341 { 342 rtl::OUStringBuffer aBuf; 343 344 // Is host a numeric IPv6 address? 345 if ( ( rHostName.indexOf( ':' ) != -1 ) && 346 ( rHostName[ 0 ] != sal_Unicode( '[' ) ) ) 347 { 348 aBuf.appendAscii( "[" ); 349 aBuf.append( rHostName ); 350 aBuf.appendAscii( "]" ); 351 } 352 else 353 { 354 aBuf.append( rHostName ); 355 } 356 357 if ( ( nPort != DEFAULT_HTTP_PORT ) && ( nPort != DEFAULT_HTTPS_PORT ) ) 358 { 359 aBuf.appendAscii( ":" ); 360 aBuf.append( rtl::OUString::valueOf( sal_Int32( nPort ) ) ); 361 } 362 return aBuf.makeStringAndClear(); 363 } 364 365