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_ucb.hxx"
24 
25 #include "SerfRequestProcessor.hxx"
26 #include "SerfRequestProcessorImpl.hxx"
27 #include "SerfRequestProcessorImplFac.hxx"
28 #include "SerfCallbacks.hxx"
29 #include "SerfSession.hxx"
30 
31 #include <apr_strings.h>
32 
33 namespace http_dav_ucp
34 {
35 
36 SerfRequestProcessor::SerfRequestProcessor( SerfSession& rSerfSession,
37                                             const rtl::OUString & inPath,
38                                             const bool bUseChunkedEncoding )
39     : mrSerfSession( rSerfSession )
40     , mPathStr( 0 )
41     , mbUseChunkedEncoding( bUseChunkedEncoding )
42     , mDestPathStr( 0 )
43     , mContentType( 0 )
44     , mReferer( 0 )
45     , mpProcImpl( 0 )
46     , mbProcessingDone( false )
47     , mpDAVException()
48     , mnHTTPStatusCode( SC_NONE )
49     , mHTTPStatusCodeText()
50     , mRedirectLocation()
51     , mnSuccessfulCredentialAttempts( 0 )
52     , mbInputOfCredentialsAborted( false )
53     , mbSetupSerfRequestCalled( false )
54     , mbAcceptSerfResponseCalled( false )
55     , mbHandleSerfResponseCalled( false )
56 {
57     mPathStr = apr_pstrdup( mrSerfSession.getAprPool(),
58                             rtl::OUStringToOString( inPath, RTL_TEXTENCODING_UTF8 ).getStr() );
59 }
60 
61 SerfRequestProcessor::~SerfRequestProcessor()
62 {
63     delete mpProcImpl;
64     delete mpDAVException;
65 }
66 
67 void SerfRequestProcessor::prepareProcessor()
68 {
69     delete mpDAVException;
70     mpDAVException = 0;
71     mnHTTPStatusCode = SC_NONE;
72     mHTTPStatusCodeText = rtl::OUString();
73     mRedirectLocation = rtl::OUString();
74 
75     mnSuccessfulCredentialAttempts = 0;
76     mbInputOfCredentialsAborted = false;
77     mbSetupSerfRequestCalled = false;
78     mbAcceptSerfResponseCalled = false;
79     mbHandleSerfResponseCalled = false;
80 }
81 
82 // PROPFIND - allprop & named
83 bool SerfRequestProcessor::processPropFind( const Depth inDepth,
84                                             const std::vector< ::rtl::OUString > & inPropNames,
85                                             std::vector< DAVResource > & ioResources,
86                                             apr_status_t& outSerfStatus )
87 {
88     mpProcImpl = createPropFindReqProcImpl( mPathStr,
89                                             mrSerfSession.getRequestEnvironment().m_aRequestHeaders,
90                                             inDepth,
91                                             inPropNames,
92                                             ioResources );
93     outSerfStatus = runProcessor();
94 
95     return outSerfStatus == APR_SUCCESS;
96 }
97 
98 // PROPFIND - property names
99 bool SerfRequestProcessor::processPropFind( const Depth inDepth,
100                                             std::vector< DAVResourceInfo > & ioResInfo,
101                                             apr_status_t& outSerfStatus )
102 {
103     mpProcImpl = createPropFindReqProcImpl( mPathStr,
104                                             mrSerfSession.getRequestEnvironment().m_aRequestHeaders,
105                                             inDepth,
106                                             ioResInfo );
107     outSerfStatus = runProcessor();
108 
109     return outSerfStatus == APR_SUCCESS;
110 }
111 
112 // PROPPATCH
113 bool SerfRequestProcessor::processPropPatch( const std::vector< ProppatchValue > & inProperties,
114                                              apr_status_t& outSerfStatus )
115 {
116     mpProcImpl = createPropPatchReqProcImpl( mPathStr,
117                                              mrSerfSession.getRequestEnvironment().m_aRequestHeaders,
118                                              inProperties );
119     outSerfStatus = runProcessor();
120 
121     return outSerfStatus == APR_SUCCESS;
122 }
123 
124 // GET
125 bool SerfRequestProcessor::processGet( const com::sun::star::uno::Reference< SerfInputStream >& xioInStrm,
126                                        apr_status_t& outSerfStatus )
127 {
128     mpProcImpl = createGetReqProcImpl( mPathStr,
129                                        mrSerfSession.getRequestEnvironment().m_aRequestHeaders,
130                                        xioInStrm );
131     outSerfStatus = runProcessor();
132 
133     return outSerfStatus == APR_SUCCESS;
134 }
135 
136 // GET inclusive header fields
137 bool SerfRequestProcessor::processGet( const com::sun::star::uno::Reference< SerfInputStream >& xioInStrm,
138                                        const std::vector< ::rtl::OUString > & inHeaderNames,
139                                        DAVResource & ioResource,
140                                        apr_status_t& outSerfStatus )
141 {
142     mpProcImpl = createGetReqProcImpl( mPathStr,
143                                        mrSerfSession.getRequestEnvironment().m_aRequestHeaders,
144                                        xioInStrm,
145                                        inHeaderNames,
146                                        ioResource );
147     outSerfStatus = runProcessor();
148 
149     return outSerfStatus == APR_SUCCESS;
150 }
151 
152 // GET
153 bool SerfRequestProcessor::processGet( const com::sun::star::uno::Reference< com::sun::star::io::XOutputStream >& xioOutStrm,
154                                        apr_status_t& outSerfStatus )
155 {
156     mpProcImpl = createGetReqProcImpl( mPathStr,
157                                        mrSerfSession.getRequestEnvironment().m_aRequestHeaders,
158                                        xioOutStrm );
159     outSerfStatus = runProcessor();
160 
161     return outSerfStatus == APR_SUCCESS;
162 }
163 
164 // GET inclusive header fields
165 bool SerfRequestProcessor::processGet( const com::sun::star::uno::Reference< com::sun::star::io::XOutputStream >& xioOutStrm,
166                                        const std::vector< ::rtl::OUString > & inHeaderNames,
167                                        DAVResource & ioResource,
168                                        apr_status_t& outSerfStatus )
169 {
170     mpProcImpl = createGetReqProcImpl( mPathStr,
171                                        mrSerfSession.getRequestEnvironment().m_aRequestHeaders,
172                                        xioOutStrm,
173                                        inHeaderNames,
174                                        ioResource );
175     outSerfStatus = runProcessor();
176 
177     return outSerfStatus == APR_SUCCESS;
178 }
179 
180 // HEAD
181 bool SerfRequestProcessor::processHead( const std::vector< ::rtl::OUString > & inHeaderNames,
182                                         DAVResource & ioResource,
183                                         apr_status_t& outSerfStatus )
184 {
185     mpProcImpl = createHeadReqProcImpl( mPathStr,
186                                         mrSerfSession.getRequestEnvironment().m_aRequestHeaders,
187                                         inHeaderNames,
188                                         ioResource );
189     outSerfStatus = runProcessor();
190 
191     return outSerfStatus == APR_SUCCESS;
192 }
193 
194 // PUT
195 bool SerfRequestProcessor::processPut( const char* inData,
196                                        apr_size_t inDataLen,
197                                        apr_status_t& outSerfStatus )
198 {
199     mpProcImpl = createPutReqProcImpl( mPathStr,
200                                        mrSerfSession.getRequestEnvironment().m_aRequestHeaders,
201                                        inData,
202                                        inDataLen );
203     outSerfStatus = runProcessor();
204 
205     return outSerfStatus == APR_SUCCESS;
206 }
207 
208 // POST
209 bool SerfRequestProcessor::processPost( const char* inData,
210                                         apr_size_t inDataLen,
211                                         const rtl::OUString & inContentType,
212                                         const rtl::OUString & inReferer,
213                                         const com::sun::star::uno::Reference< SerfInputStream >& xioInStrm,
214                                         apr_status_t& outSerfStatus )
215 {
216     mContentType = apr_pstrdup( mrSerfSession.getAprPool(),
217                                 rtl::OUStringToOString( inContentType, RTL_TEXTENCODING_UTF8 ).getStr() );
218     mReferer = apr_pstrdup( mrSerfSession.getAprPool(),
219                                 rtl::OUStringToOString( inReferer, RTL_TEXTENCODING_UTF8 ).getStr() );
220     mpProcImpl = createPostReqProcImpl( mPathStr,
221                                         mrSerfSession.getRequestEnvironment().m_aRequestHeaders,
222                                         inData,
223                                         inDataLen,
224                                         mContentType,
225                                         mReferer,
226                                         xioInStrm );
227     outSerfStatus = runProcessor();
228 
229     return outSerfStatus == APR_SUCCESS;
230 }
231 
232 // POST
233 bool SerfRequestProcessor::processPost( const char* inData,
234                                         apr_size_t inDataLen,
235                                         const rtl::OUString & inContentType,
236                                         const rtl::OUString & inReferer,
237                                         const com::sun::star::uno::Reference< com::sun::star::io::XOutputStream >& xioOutStrm,
238                                         apr_status_t& outSerfStatus )
239 {
240     mContentType = apr_pstrdup( mrSerfSession.getAprPool(),
241                                 rtl::OUStringToOString( inContentType, RTL_TEXTENCODING_UTF8 ).getStr() );
242     mReferer = apr_pstrdup( mrSerfSession.getAprPool(),
243                             rtl::OUStringToOString( inReferer, RTL_TEXTENCODING_UTF8 ).getStr() );
244     mpProcImpl = createPostReqProcImpl( mPathStr,
245                                         mrSerfSession.getRequestEnvironment().m_aRequestHeaders,
246                                         inData,
247                                         inDataLen,
248                                         mContentType,
249                                         mReferer,
250                                         xioOutStrm );
251     outSerfStatus = runProcessor();
252 
253     return outSerfStatus == APR_SUCCESS;
254 }
255 
256 // DELETE
257 bool SerfRequestProcessor::processDelete( apr_status_t& outSerfStatus )
258 {
259     mpProcImpl = createDeleteReqProcImpl( mPathStr,
260                                           mrSerfSession.getRequestEnvironment().m_aRequestHeaders );
261     outSerfStatus = runProcessor();
262 
263     return outSerfStatus == APR_SUCCESS;
264 }
265 
266 // MKCOL
267 bool SerfRequestProcessor::processMkCol( apr_status_t& outSerfStatus )
268 {
269     mpProcImpl = createMkColReqProcImpl( mPathStr,
270                                          mrSerfSession.getRequestEnvironment().m_aRequestHeaders );
271     outSerfStatus = runProcessor();
272 
273     return outSerfStatus == APR_SUCCESS;
274 }
275 
276 // COPY
277 bool SerfRequestProcessor::processCopy( const rtl::OUString & inDestinationPath,
278                                         const bool inOverwrite,
279                                         apr_status_t& outSerfStatus )
280 {
281     mDestPathStr = apr_pstrdup( mrSerfSession.getAprPool(),
282                                 rtl::OUStringToOString( inDestinationPath, RTL_TEXTENCODING_UTF8 ).getStr() );
283     mpProcImpl = createCopyReqProcImpl( mPathStr,
284                                         mrSerfSession.getRequestEnvironment().m_aRequestHeaders,
285                                         mDestPathStr,
286                                         inOverwrite );
287     outSerfStatus = runProcessor();
288 
289     return outSerfStatus == APR_SUCCESS;
290 }
291 
292 // MOVE
293 bool SerfRequestProcessor::processMove( const rtl::OUString & inDestinationPath,
294                                         const bool inOverwrite,
295                                         apr_status_t& outSerfStatus )
296 {
297     mDestPathStr = apr_pstrdup( mrSerfSession.getAprPool(),
298                                 rtl::OUStringToOString( inDestinationPath, RTL_TEXTENCODING_UTF8 ).getStr() );
299     mpProcImpl = createMoveReqProcImpl( mPathStr,
300                                         mrSerfSession.getRequestEnvironment().m_aRequestHeaders,
301                                         mDestPathStr,
302                                         inOverwrite );
303     outSerfStatus = runProcessor();
304 
305     return outSerfStatus == APR_SUCCESS;
306 }
307 
308 apr_status_t SerfRequestProcessor::runProcessor()
309 {
310     prepareProcessor();
311 
312     // activate chunked encoding, if requested
313     if ( mbUseChunkedEncoding )
314     {
315         mpProcImpl->activateChunkedEncoding();
316     }
317 
318     // create serf request
319     serf_connection_request_create( mrSerfSession.getSerfConnection(),
320                                     Serf_SetupRequest,
321                                     this );
322 
323     // perform serf request
324     mbProcessingDone = false;
325     apr_status_t status = APR_SUCCESS;
326     serf_context_t* pSerfContext = mrSerfSession.getSerfContext();
327     apr_pool_t* pAprPool = mrSerfSession.getAprPool();
328     while ( true )
329     {
330         status = serf_context_run( pSerfContext,
331                                    SERF_DURATION_FOREVER,
332                                    pAprPool );
333         if ( APR_STATUS_IS_TIMEUP( status ) )
334         {
335             continue;
336         }
337         if ( status != APR_SUCCESS )
338         {
339             break;
340         }
341         if ( mbProcessingDone )
342         {
343             break;
344         }
345     }
346 
347     postprocessProcessor( status );
348 
349     return status;
350 }
351 
352 void SerfRequestProcessor::postprocessProcessor( const apr_status_t inStatus )
353 {
354     if ( inStatus == APR_SUCCESS )
355     {
356         return;
357     }
358 
359     switch ( inStatus )
360     {
361     case APR_EGENERAL:
362     case SERF_ERROR_AUTHN_FAILED:
363         // general error; <mnHTTPStatusCode> provides more information
364         {
365             switch ( mnHTTPStatusCode )
366             {
367             case SC_NONE:
368                 if ( !mbSetupSerfRequestCalled )
369                 {
370                     mpDAVException = new DAVException( DAVException::DAV_HTTP_LOOKUP,
371                                                        SerfUri::makeConnectionEndPointString( mrSerfSession.getHostName(),
372                                                                                               mrSerfSession.getPort() ) );
373                 }
374                 else if ( mbInputOfCredentialsAborted )
375                 {
376                     mpDAVException = new DAVException( DAVException::DAV_HTTP_NOAUTH,
377                                                        SerfUri::makeConnectionEndPointString( mrSerfSession.getHostName(),
378                                                                                               mrSerfSession.getPort() ) );
379                 }
380                 else
381                 {
382                     mpDAVException = new DAVException( DAVException::DAV_HTTP_ERROR,
383                                                        mHTTPStatusCodeText,
384                                                        mnHTTPStatusCode );
385                 }
386                 break;
387             case SC_MOVED_PERMANENTLY:
388             case SC_MOVED_TEMPORARILY:
389             case SC_SEE_OTHER:
390             case SC_TEMPORARY_REDIRECT:
391                 mpDAVException = new DAVException( DAVException::DAV_HTTP_REDIRECT,
392                                                    mRedirectLocation );
393                 break;
394             default:
395                 mpDAVException = new DAVException( DAVException::DAV_HTTP_ERROR,
396                                                    mHTTPStatusCodeText,
397                                                    mnHTTPStatusCode );
398                 break;
399             }
400         }
401         break;
402 
403     default:
404         mpDAVException = new DAVException( DAVException::DAV_HTTP_ERROR );
405         break;
406     }
407 
408 }
409 
410 apr_status_t SerfRequestProcessor::provideSerfCredentials( char ** outUsername,
411                                                            char ** outPassword,
412                                                            serf_request_t * inRequest,
413                                                            int inCode,
414                                                            const char *inAuthProtocol,
415                                                            const char *inRealm,
416                                                            apr_pool_t *inAprPool )
417 {
418     // as each successful provided credentials are tried twice - see below - the
419     // number of real attempts is half of the value of <mnSuccessfulCredentialAttempts>
420     if ( (mnSuccessfulCredentialAttempts / 2) >= 5 ||
421          mbInputOfCredentialsAborted )
422     {
423         mbInputOfCredentialsAborted = true;
424         return SERF_ERROR_AUTHN_FAILED;
425     }
426 
427     // because serf keeps credentials only for a connection in case of digest authentication
428     // we give each successful provided credentials a second try in order to workaround the
429     // situation that the connection for which the credentials have been provided has been closed
430     // before the provided credentials could be applied for the request.
431     apr_status_t status = mrSerfSession.provideSerfCredentials( (mnSuccessfulCredentialAttempts % 2) == 1,
432                                                                 outUsername,
433                                                                 outPassword,
434                                                                 inRequest,
435                                                                 inCode,
436                                                                 inAuthProtocol,
437                                                                 inRealm,
438                                                                 inAprPool );
439     if ( status != APR_SUCCESS )
440     {
441         mbInputOfCredentialsAborted = true;
442     }
443     else
444     {
445         ++mnSuccessfulCredentialAttempts;
446     }
447 
448     return status;
449 }
450 
451 apr_status_t SerfRequestProcessor::setupSerfRequest( serf_request_t * inSerfRequest,
452                                    serf_bucket_t ** outSerfRequestBucket,
453                                    serf_response_acceptor_t * outSerfResponseAcceptor,
454                                    void ** outSerfResponseAcceptorBaton,
455                                    serf_response_handler_t * outSerfResponseHandler,
456                                    void ** outSerfResponseHandlerBaton,
457                                    apr_pool_t * /*inAprPool*/ )
458 {
459     mbSetupSerfRequestCalled = true;
460     *outSerfRequestBucket = mpProcImpl->createSerfRequestBucket( inSerfRequest );
461 
462     // apply callbacks for accepting response and handling response
463     *outSerfResponseAcceptor = Serf_AcceptResponse;
464     *outSerfResponseAcceptorBaton = this;
465     *outSerfResponseHandler = Serf_HandleResponse;
466     *outSerfResponseHandlerBaton = this;
467 
468     return APR_SUCCESS;
469 }
470 
471 serf_bucket_t* SerfRequestProcessor::acceptSerfResponse( serf_request_t * inSerfRequest,
472                                                          serf_bucket_t * inSerfStreamBucket,
473                                                          apr_pool_t * inAprPool )
474 {
475     mbAcceptSerfResponseCalled = true;
476     return mrSerfSession.acceptSerfResponse( inSerfRequest,
477                                              inSerfStreamBucket,
478                                              inAprPool );
479 }
480 
481 apr_status_t SerfRequestProcessor::handleSerfResponse( serf_request_t * inSerfRequest,
482                                                        serf_bucket_t * inSerfResponseBucket,
483                                                        apr_pool_t * inAprPool )
484 {
485     mbHandleSerfResponseCalled = true;
486 
487     // some general response handling and error handling
488     {
489         if ( !inSerfResponseBucket )
490         {
491             /* A NULL response can come back if the request failed completely */
492             mbProcessingDone = true;
493             return APR_EGENERAL;
494         }
495 
496         serf_status_line sl;
497         apr_status_t status = serf_bucket_response_status( inSerfResponseBucket, &sl );
498         if ( status )
499         {
500             mbProcessingDone = false; // allow another try in order to get a response
501             return status;
502         }
503         // TODO - check, if response status code handling is correct
504         mnHTTPStatusCode = ( sl.version != 0 && sl.code >= 0 )
505                            ? static_cast< sal_uInt16 >( sl.code )
506                            : SC_NONE;
507         if ( sl.reason )
508         {
509             mHTTPStatusCodeText = ::rtl::OUString::createFromAscii( sl.reason );
510         }
511         if ( ( sl.version == 0 || sl.code < 0 ) ||
512              mnHTTPStatusCode >= 300 )
513         {
514             if ( mnHTTPStatusCode == 301 ||
515                  mnHTTPStatusCode == 302 ||
516                  mnHTTPStatusCode == 303 ||
517                  mnHTTPStatusCode == 307 )
518             {
519                 // new location for certain redirections
520                 serf_bucket_t *headers = serf_bucket_response_get_headers( inSerfResponseBucket );
521                 const char* location = serf_bucket_headers_get( headers, "Location" );
522                 if ( location )
523                 {
524                     mRedirectLocation = rtl::OUString::createFromAscii( location );
525                 }
526                 mbProcessingDone = true;
527                 return APR_EGENERAL;
528             }
529             else if ( mrSerfSession.isHeadRequestInProgress() &&
530                       ( mnHTTPStatusCode == 401 || mnHTTPStatusCode == 407 ) )
531             {
532                 // keep going as authentication is not required on HEAD request.
533                 // the response already contains header fields.
534             }
535             else
536             {
537                 mbProcessingDone = true;
538                 return APR_EGENERAL;
539             }
540         }
541     }
542 
543     // request specific processing of the response bucket
544     apr_status_t status = APR_SUCCESS;
545     mbProcessingDone = mpProcImpl->processSerfResponseBucket( inSerfRequest,
546                                                               inSerfResponseBucket,
547                                                               inAprPool,
548                                                               status );
549 
550     return status;
551 }
552 
553 } // namespace http_dav_ucp
554 
555