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