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