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
matchIgnoreAsciiCase(rtl::OString const & rStr1,sal_Char const * pStr2,sal_Int32 nStr2Len)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
getURLStringPart(const CURLU * curlUrl,CURLUPart part,unsigned int flags)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
CurlUri(const CURLU * inUri)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
CurlUri(const rtl::OUString & inUri)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
CurlUri(const CurlUri & curlUri)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
init(const CURLU * pUri)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
~CurlUri()218 CurlUri::~CurlUri( )
219 {
220 if ( mCurlUri )
221 curl_url_cleanup( mCurlUri );
222 }
223
calculateURI()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
GetPathBaseName() const273 ::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
operator ==(const CurlUri & rOther) const302 bool CurlUri::operator== ( const CurlUri & rOther ) const
303 {
304 return ( mURI == rOther.mURI );
305 }
306
GetPathBaseNameUnescaped() const307 ::rtl::OUString CurlUri::GetPathBaseNameUnescaped () const
308 {
309 return unescape( GetPathBaseName() );
310 }
311
AppendPath(const rtl::OUString & rPath)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
escapeSegment(const rtl::OUString & segment)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
unescape(const rtl::OUString & segment)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
makeConnectionEndPointString(const rtl::OUString & rHostName,int nPort)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