xref: /trunk/main/ucb/source/ucp/webdav/CurlUri.cxx (revision 91144cd0085a7583d2099b982122deb2184ab956)
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