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