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 //to examine returned http code
34 #include "DAVException.hxx"
35 
36 namespace http_dav_ucp
37 {
38 
39 SerfRequestProcessor::SerfRequestProcessor( SerfSession& rSerfSession,
40                                             const rtl::OUString & inPath,
41                                             const bool bUseChunkedEncoding )
42     : mrSerfSession( rSerfSession )
43     , mPathStr( 0 )
44     , mbUseChunkedEncoding( bUseChunkedEncoding )
45     , mDestPathStr( 0 )
46     , mContentType( 0 )
47     , mReferer( 0 )
48     , mpProcImpl( 0 )
49     , mbProcessingDone( false )
50     , mpDAVException()
51     , mnHTTPStatusCode( SC_NONE )
52     , mHTTPStatusCodeText()
53     , mRedirectLocation()
54     , mnSuccessfulCredentialAttempts( 0 )
55     , mbInputOfCredentialsAborted( false )
56     , mbSetupSerfRequestCalled( false )
57     , mbAcceptSerfResponseCalled( false )
58     , mbHandleSerfResponseCalled( false )
59 {
60     mPathStr = apr_pstrdup( mrSerfSession.getAprPool(),
61                             rtl::OUStringToOString( inPath, RTL_TEXTENCODING_UTF8 ).getStr() );
62 }
63 
64 SerfRequestProcessor::~SerfRequestProcessor()
65 {
66     delete mpProcImpl;
67     delete mpDAVException;
68 }
69 
70 void SerfRequestProcessor::prepareProcessor()
71 {
72     delete mpDAVException;
73     mpDAVException = 0;
74     mnHTTPStatusCode = SC_NONE;
75     mHTTPStatusCodeText = rtl::OUString();
76     mRedirectLocation = rtl::OUString();
77 
78     mnSuccessfulCredentialAttempts = 0;
79     mbInputOfCredentialsAborted = false;
80     mbSetupSerfRequestCalled = false;
81     mbAcceptSerfResponseCalled = false;
82     mbHandleSerfResponseCalled = false;
83 }
84 
85 // PROPFIND - allprop & named
86 bool SerfRequestProcessor::processPropFind( const Depth inDepth,
87                                             const std::vector< ::rtl::OUString > & inPropNames,
88                                             std::vector< DAVResource > & ioResources,
89                                             apr_status_t& outSerfStatus )
90 {
91     mpProcImpl = createPropFindReqProcImpl( mPathStr,
92                                             mrSerfSession.getRequestEnvironment().m_aRequestHeaders,
93                                             inDepth,
94                                             inPropNames,
95                                             ioResources );
96     outSerfStatus = runProcessor();
97 
98     return outSerfStatus == APR_SUCCESS;
99 }
100 
101 // PROPFIND - property names
102 bool SerfRequestProcessor::processPropFind( const Depth inDepth,
103                                             std::vector< DAVResourceInfo > & ioResInfo,
104                                             apr_status_t& outSerfStatus )
105 {
106     mpProcImpl = createPropFindReqProcImpl( mPathStr,
107                                             mrSerfSession.getRequestEnvironment().m_aRequestHeaders,
108                                             inDepth,
109                                             ioResInfo );
110     outSerfStatus = runProcessor();
111 
112     return outSerfStatus == APR_SUCCESS;
113 }
114 
115 // PROPPATCH
116 bool SerfRequestProcessor::processPropPatch( const std::vector< ProppatchValue > & inProperties,
117                                              const com::sun::star::ucb::Lock  inLock,
118                                              apr_status_t& outSerfStatus )
119 {
120     char * inLockToken = static_cast<char*>(0);
121     if(inLock.LockTokens.getLength() > 0)
122     {
123         inLockToken = apr_psprintf( mrSerfSession.getAprPool(), "(<%s>)",
124                                rtl::OUStringToOString(inLock.LockTokens[0], RTL_TEXTENCODING_UTF8 ).getStr() );
125     }
126     mpProcImpl = createPropPatchReqProcImpl( mPathStr,
127                                              mrSerfSession.getRequestEnvironment().m_aRequestHeaders,
128                                              inProperties,
129                                              inLockToken );
130     outSerfStatus = runProcessor();
131 
132     return outSerfStatus == APR_SUCCESS;
133 }
134 
135 // GET
136 bool SerfRequestProcessor::processGet( const com::sun::star::uno::Reference< SerfInputStream >& xioInStrm,
137                                        apr_status_t& outSerfStatus )
138 {
139     mpProcImpl = createGetReqProcImpl( mPathStr,
140                                        mrSerfSession.getRequestEnvironment().m_aRequestHeaders,
141                                        xioInStrm );
142     outSerfStatus = runProcessor();
143 
144     return outSerfStatus == APR_SUCCESS;
145 }
146 
147 // GET inclusive header fields
148 bool SerfRequestProcessor::processGet( const com::sun::star::uno::Reference< SerfInputStream >& xioInStrm,
149                                        const std::vector< ::rtl::OUString > & inHeaderNames,
150                                        DAVResource & ioResource,
151                                        apr_status_t& outSerfStatus )
152 {
153     mpProcImpl = createGetReqProcImpl( mPathStr,
154                                        mrSerfSession.getRequestEnvironment().m_aRequestHeaders,
155                                        xioInStrm,
156                                        inHeaderNames,
157                                        ioResource );
158     outSerfStatus = runProcessor();
159 
160     return outSerfStatus == APR_SUCCESS;
161 }
162 
163 // GET
164 bool SerfRequestProcessor::processGet( const com::sun::star::uno::Reference< com::sun::star::io::XOutputStream >& xioOutStrm,
165                                        apr_status_t& outSerfStatus )
166 {
167     mpProcImpl = createGetReqProcImpl( mPathStr,
168                                        mrSerfSession.getRequestEnvironment().m_aRequestHeaders,
169                                        xioOutStrm );
170     outSerfStatus = runProcessor();
171 
172     return outSerfStatus == APR_SUCCESS;
173 }
174 
175 // GET inclusive header fields
176 bool SerfRequestProcessor::processGet( const com::sun::star::uno::Reference< com::sun::star::io::XOutputStream >& xioOutStrm,
177                                        const std::vector< ::rtl::OUString > & inHeaderNames,
178                                        DAVResource & ioResource,
179                                        apr_status_t& outSerfStatus )
180 {
181     mpProcImpl = createGetReqProcImpl( mPathStr,
182                                        mrSerfSession.getRequestEnvironment().m_aRequestHeaders,
183                                        xioOutStrm,
184                                        inHeaderNames,
185                                        ioResource );
186     outSerfStatus = runProcessor();
187 
188     return outSerfStatus == APR_SUCCESS;
189 }
190 
191 // HEAD
192 bool SerfRequestProcessor::processHead( const std::vector< ::rtl::OUString > & inHeaderNames,
193                                         DAVResource & ioResource,
194                                         apr_status_t& outSerfStatus )
195 {
196     mpProcImpl = createHeadReqProcImpl( mPathStr,
197                                         mrSerfSession.getRequestEnvironment().m_aRequestHeaders,
198                                         inHeaderNames,
199                                         ioResource );
200     outSerfStatus = runProcessor();
201 
202     return outSerfStatus == APR_SUCCESS;
203 }
204 
205 // PUT
206 bool SerfRequestProcessor::processPut( const char* inData,
207                                        apr_size_t inDataLen,
208                                        const com::sun::star::ucb::Lock inLock,
209                                        apr_status_t& outSerfStatus )
210 {
211     char * inLockToken = static_cast<char*>(0);
212     if(inLock.LockTokens.getLength() > 0)
213     {
214         inLockToken = apr_psprintf( mrSerfSession.getAprPool(), "(<%s>)",
215                                rtl::OUStringToOString(inLock.LockTokens[0], RTL_TEXTENCODING_UTF8 ).getStr() );
216     }
217     mpProcImpl = createPutReqProcImpl( mPathStr,
218                                        mrSerfSession.getRequestEnvironment().m_aRequestHeaders,
219                                        inData,
220                                        inLockToken,
221                                        inDataLen );
222     outSerfStatus = runProcessor();
223 
224     return outSerfStatus == APR_SUCCESS;
225 }
226 
227 // POST
228 bool SerfRequestProcessor::processPost( const char* inData,
229                                         apr_size_t inDataLen,
230                                         const rtl::OUString & inContentType,
231                                         const rtl::OUString & inReferer,
232                                         const com::sun::star::ucb::Lock inLock,
233                                         const com::sun::star::uno::Reference< SerfInputStream >& xioInStrm,
234                                         apr_status_t& outSerfStatus )
235 {
236     mContentType = apr_pstrdup( mrSerfSession.getAprPool(),
237                                 rtl::OUStringToOString( inContentType, RTL_TEXTENCODING_UTF8 ).getStr() );
238     mReferer = apr_pstrdup( mrSerfSession.getAprPool(),
239                                 rtl::OUStringToOString( inReferer, RTL_TEXTENCODING_UTF8 ).getStr() );
240     char * inLockToken = static_cast<char*>(0);
241     if(inLock.LockTokens.getLength() > 0)
242     {
243         inLockToken = apr_psprintf( mrSerfSession.getAprPool(), "(<%s>)",
244                                rtl::OUStringToOString(inLock.LockTokens[0], RTL_TEXTENCODING_UTF8 ).getStr() );
245     }
246     mpProcImpl = createPostReqProcImpl( mPathStr,
247                                         mrSerfSession.getRequestEnvironment().m_aRequestHeaders,
248                                         inData,
249                                         inDataLen,
250                                         inLockToken,
251                                         mContentType,
252                                         mReferer,
253                                         xioInStrm );
254     outSerfStatus = runProcessor();
255 
256     return outSerfStatus == APR_SUCCESS;
257 }
258 
259 // POST
260 bool SerfRequestProcessor::processPost( const char* inData,
261                                         apr_size_t inDataLen,
262                                         const rtl::OUString & inContentType,
263                                         const rtl::OUString & inReferer,
264                                         const com::sun::star::ucb::Lock inLock,
265                                         const com::sun::star::uno::Reference< com::sun::star::io::XOutputStream >& xioOutStrm,
266                                         apr_status_t& outSerfStatus )
267 {
268     mContentType = apr_pstrdup( mrSerfSession.getAprPool(),
269                                 rtl::OUStringToOString( inContentType, RTL_TEXTENCODING_UTF8 ).getStr() );
270     mReferer = apr_pstrdup( mrSerfSession.getAprPool(),
271                             rtl::OUStringToOString( inReferer, RTL_TEXTENCODING_UTF8 ).getStr() );
272     char * inLockToken = static_cast<char*>(0);
273     if(inLock.LockTokens.getLength() > 0)
274     {
275         inLockToken = apr_psprintf( mrSerfSession.getAprPool(), "(<%s>)",
276                                rtl::OUStringToOString(inLock.LockTokens[0], RTL_TEXTENCODING_UTF8 ).getStr() );
277     }
278     mpProcImpl = createPostReqProcImpl( mPathStr,
279                                         mrSerfSession.getRequestEnvironment().m_aRequestHeaders,
280                                         inData,
281                                         inDataLen,
282                                         inLockToken,
283                                         mContentType,
284                                         mReferer,
285                                         xioOutStrm );
286     outSerfStatus = runProcessor();
287 
288     return outSerfStatus == APR_SUCCESS;
289 }
290 
291 // DELETE
292 bool SerfRequestProcessor::processDelete( const com::sun::star::ucb::Lock inLock,
293                                           apr_status_t& outSerfStatus )
294 {
295     char * inLockToken = static_cast<char*>(0);
296     if(inLock.LockTokens.getLength() > 0)
297     {
298         inLockToken = apr_psprintf( mrSerfSession.getAprPool(), "(<%s>)",
299                                rtl::OUStringToOString(inLock.LockTokens[0], RTL_TEXTENCODING_UTF8 ).getStr() );
300     }
301     mpProcImpl = createDeleteReqProcImpl( mPathStr,
302                                           mrSerfSession.getRequestEnvironment().m_aRequestHeaders,
303                                           inLockToken );
304     outSerfStatus = runProcessor();
305 
306     return outSerfStatus == APR_SUCCESS;
307 }
308 
309 // MKCOL
310 bool SerfRequestProcessor::processMkCol( const com::sun::star::ucb::Lock inLock,
311                                          apr_status_t& outSerfStatus )
312 {
313     char * inLockToken = static_cast<char*>(0);
314     if(inLock.LockTokens.getLength() > 0)
315     {
316         inLockToken = apr_psprintf( mrSerfSession.getAprPool(), "(<%s>)",
317                                rtl::OUStringToOString(inLock.LockTokens[0], RTL_TEXTENCODING_UTF8 ).getStr() );
318     }
319     mpProcImpl = createMkColReqProcImpl( mPathStr,
320                                          mrSerfSession.getRequestEnvironment().m_aRequestHeaders,
321                                          inLockToken );
322     outSerfStatus = runProcessor();
323 
324     return outSerfStatus == APR_SUCCESS;
325 }
326 
327 // COPY
328 bool SerfRequestProcessor::processCopy( const rtl::OUString & inDestinationPath,
329                                         const bool inOverwrite,
330                                         const com::sun::star::ucb::Lock inLock,
331                                         apr_status_t& outSerfStatus )
332 {
333     mDestPathStr = apr_pstrdup( mrSerfSession.getAprPool(),
334                                 rtl::OUStringToOString( inDestinationPath, RTL_TEXTENCODING_UTF8 ).getStr() );
335     char * inLockToken = static_cast<char*>(0);
336     if(inLock.LockTokens.getLength() > 0)
337     {
338         inLockToken = apr_psprintf( mrSerfSession.getAprPool(), "(<%s>)",
339                                rtl::OUStringToOString(inLock.LockTokens[0], RTL_TEXTENCODING_UTF8 ).getStr() );
340     }
341     mpProcImpl = createCopyReqProcImpl( mPathStr,
342                                         mrSerfSession.getRequestEnvironment().m_aRequestHeaders,
343                                         mDestPathStr,
344                                         inOverwrite,
345                                         inLockToken );
346     outSerfStatus = runProcessor();
347 
348     return outSerfStatus == APR_SUCCESS;
349 }
350 
351 // MOVE
352 bool SerfRequestProcessor::processMove( const rtl::OUString & inDestinationPath,
353                                         const bool inOverwrite,
354                                         const com::sun::star::ucb::Lock  inLock,
355                                         apr_status_t& outSerfStatus )
356 {
357     mDestPathStr = apr_pstrdup( mrSerfSession.getAprPool(),
358                                 rtl::OUStringToOString( inDestinationPath, RTL_TEXTENCODING_UTF8 ).getStr() );
359     char * inLockToken = static_cast<char*>(0);
360     if(inLock.LockTokens.getLength() > 0)
361     {
362         inLockToken = apr_psprintf( mrSerfSession.getAprPool(), "(<%s>)",
363                                rtl::OUStringToOString(inLock.LockTokens[0], RTL_TEXTENCODING_UTF8 ).getStr() );
364     }
365     mpProcImpl = createMoveReqProcImpl( mPathStr,
366                                         mrSerfSession.getRequestEnvironment().m_aRequestHeaders,
367                                         mDestPathStr,
368                                         inOverwrite,
369                                         inLockToken );
370     outSerfStatus = runProcessor();
371 
372     return outSerfStatus == APR_SUCCESS;
373 }
374 
375 //LOCK creating a new lock
376 bool SerfRequestProcessor::processLock( const rtl::OUString & inDestinationPath,
377                                         const com::sun::star::ucb::Lock& inLock,
378                                         DAVPropertyValue & outLock,
379                                         apr_status_t& outSerfStatus )
380 {
381     mDestPathStr = apr_pstrdup( mrSerfSession.getAprPool(),
382                                 rtl::OUStringToOString( inDestinationPath, RTL_TEXTENCODING_UTF8 ).getStr() );
383     char * Timeout;
384     if(inLock.Timeout == -1)
385         Timeout = apr_psprintf( mrSerfSession.getAprPool(), "Infinite" );
386     else
387         Timeout = apr_psprintf( mrSerfSession.getAprPool(), "Second-%ld", inLock.Timeout );
388 
389     mpProcImpl = createLockReqProcImpl( mPathStr,
390                                         mrSerfSession.getRequestEnvironment().m_aRequestHeaders,
391                                         inLock,
392                                         Timeout,
393                                         outLock);
394     outSerfStatus = runProcessor();
395 
396     return outSerfStatus == APR_SUCCESS;
397 }
398 
399 //LOCK refresh an existing lock
400 bool SerfRequestProcessor::processLockRefresh( const rtl::OUString & inDestinationPath,
401                                         const com::sun::star::ucb::Lock& inLock,
402                                         DAVPropertyValue & outLock,
403                                         apr_status_t& outSerfStatus )
404 {
405     mDestPathStr = apr_pstrdup( mrSerfSession.getAprPool(),
406                                 rtl::OUStringToOString( inDestinationPath, RTL_TEXTENCODING_UTF8 ).getStr() );
407     char * Timeout;
408     if(inLock.Timeout == -1)
409         Timeout = apr_psprintf( mrSerfSession.getAprPool(), "Infinite" );
410     else
411         Timeout = apr_psprintf( mrSerfSession.getAprPool(), "Second-%ld", inLock.Timeout );
412 
413     char * inLockToken = apr_psprintf( mrSerfSession.getAprPool(), "(<%s>)",
414                                  rtl::OUStringToOString(inLock.LockTokens[0], RTL_TEXTENCODING_UTF8 ).getStr() );
415 
416     mpProcImpl = createLockRefreshProcImpl( mPathStr,
417                                             mrSerfSession.getRequestEnvironment().m_aRequestHeaders,
418                                             inLock,
419                                             inLockToken,
420                                             Timeout,
421                                             outLock);
422     outSerfStatus = runProcessor();
423 
424     return outSerfStatus == APR_SUCCESS;
425 }
426 
427 //ULOCK unlock an existing lock
428 bool SerfRequestProcessor::processUnlock( const rtl::OUString & inDestinationPath,
429                                         const com::sun::star::ucb::Lock& inLock,
430                                         apr_status_t& outSerfStatus )
431 {
432     mDestPathStr = apr_pstrdup( mrSerfSession.getAprPool(),
433                                 rtl::OUStringToOString( inDestinationPath, RTL_TEXTENCODING_UTF8 ).getStr() );
434 
435     char * aToken = apr_psprintf( mrSerfSession.getAprPool(), "<%s>",
436                                  rtl::OUStringToOString(inLock.LockTokens[0], RTL_TEXTENCODING_UTF8 ).getStr() );
437 
438     mpProcImpl = createUnlockProcImpl( mPathStr,
439                                        mrSerfSession.getRequestEnvironment().m_aRequestHeaders,
440                                        inLock,
441                                        aToken );
442 
443     outSerfStatus = runProcessor();
444 
445     return outSerfStatus == APR_SUCCESS;
446 }
447 
448 apr_status_t SerfRequestProcessor::runProcessor()
449 {
450     prepareProcessor();
451 
452     // activate chunked encoding, if requested
453     if ( mbUseChunkedEncoding )
454     {
455         mpProcImpl->activateChunkedEncoding();
456     }
457 
458     // create serf request
459     OSL_ASSERT(mrSerfSession.getSerfConnection() != NULL);
460     serf_connection_request_create( mrSerfSession.getSerfConnection(),
461                                     Serf_SetupRequest,
462                                     this );
463 
464     // perform serf request
465     mbProcessingDone = false;
466     apr_status_t status = APR_SUCCESS;
467     serf_context_t* pSerfContext = mrSerfSession.getSerfContext();
468     apr_pool_t* pAprPool = mrSerfSession.getAprPool();
469     while ( true )
470     {
471         status = serf_context_run( pSerfContext,
472                                    SERF_DURATION_FOREVER,
473                                    pAprPool );
474         if ( APR_STATUS_IS_TIMEUP( status ) )
475         {
476             continue;
477         }
478         if ( status != APR_SUCCESS )
479         {
480 #if OSL_DEBUG_LEVEL > 0
481             char buff[512];
482             OSL_TRACE("SerfRequestProcessor::runProcessor, status != APR_SUCCESS: %d (%s)",status, apr_strerror(status, buff, 512));
483 #endif
484             break;
485         }
486         if ( mbProcessingDone )
487         {
488             break;
489         }
490     }
491 
492     postprocessProcessor( status );
493 
494     return status;
495 }
496 
497 void SerfRequestProcessor::postprocessProcessor( const apr_status_t inStatus )
498 {
499     if ( inStatus == APR_SUCCESS )
500     {
501         return;
502     }
503 
504     OSL_TRACE("SerfRequestProcessor::postprocessProcessor:%d",__LINE__);
505     switch ( inStatus )
506     {
507     case APR_EGENERAL:
508     case SERF_ERROR_AUTHN_FAILED:
509         // general error; <mnHTTPStatusCode> provides more information
510         {
511             switch ( mnHTTPStatusCode )
512             {
513             case SC_NONE:
514                 if ( !mbSetupSerfRequestCalled )
515                 {
516                     mpDAVException = new DAVException( DAVException::DAV_HTTP_LOOKUP,
517                                                        SerfUri::makeConnectionEndPointString( mrSerfSession.getHostName(),
518                                                                                               mrSerfSession.getPort() ) );
519                 }
520                 else if ( mbInputOfCredentialsAborted )
521                 {
522                     mpDAVException = new DAVException( DAVException::DAV_HTTP_NOAUTH,
523                                                        SerfUri::makeConnectionEndPointString( mrSerfSession.getHostName(),
524                                                                                               mrSerfSession.getPort() ) );
525                 }
526                 else
527                 {
528                     mpDAVException = new DAVException( DAVException::DAV_HTTP_ERROR,
529                                                        mHTTPStatusCodeText,
530                                                        mnHTTPStatusCode );
531                 }
532                 break;
533             case SC_MOVED_PERMANENTLY:
534             case SC_MOVED_TEMPORARILY:
535             case SC_SEE_OTHER:
536             case SC_TEMPORARY_REDIRECT:
537                 mpDAVException = new DAVException( DAVException::DAV_HTTP_REDIRECT,
538                                                    mRedirectLocation );
539                 break;
540             case SC_LOCKED:
541                 mpDAVException = new DAVException( DAVException::DAV_LOCKED,
542                                                    mHTTPStatusCodeText,
543                                                    mnHTTPStatusCode );
544                 break;
545             default:
546                 mpDAVException = new DAVException( DAVException::DAV_HTTP_ERROR,
547                                                    mHTTPStatusCodeText,
548                                                    mnHTTPStatusCode );
549                 break;
550             }
551         }
552         break;
553 
554     default:
555         mpDAVException = new DAVException( DAVException::DAV_HTTP_ERROR );
556         break;
557     }
558 }
559 
560 apr_status_t SerfRequestProcessor::provideSerfCredentials( char ** outUsername,
561                                                            char ** outPassword,
562                                                            serf_request_t * inRequest,
563                                                            int inCode,
564                                                            const char *inAuthProtocol,
565                                                            const char *inRealm,
566                                                            apr_pool_t *inAprPool )
567 {
568     // as each successful provided credentials are tried twice - see below - the
569     // number of real attempts is half of the value of <mnSuccessfulCredentialAttempts>
570     if ( (mnSuccessfulCredentialAttempts / 2) >= 5 ||
571          mbInputOfCredentialsAborted )
572     {
573         mbInputOfCredentialsAborted = true;
574         return SERF_ERROR_AUTHN_FAILED;
575     }
576 
577     // because serf keeps credentials only for a connection in case of digest authentication
578     // we give each successful provided credentials a second try in order to workaround the
579     // situation that the connection for which the credentials have been provided has been closed
580     // before the provided credentials could be applied for the request.
581     apr_status_t status = mrSerfSession.provideSerfCredentials( (mnSuccessfulCredentialAttempts % 2) == 1,
582                                                                 outUsername,
583                                                                 outPassword,
584                                                                 inRequest,
585                                                                 inCode,
586                                                                 inAuthProtocol,
587                                                                 inRealm,
588                                                                 inAprPool );
589     if ( status != APR_SUCCESS )
590     {
591         mbInputOfCredentialsAborted = true;
592     }
593     else
594     {
595         ++mnSuccessfulCredentialAttempts;
596     }
597 
598     return status;
599 }
600 
601 apr_status_t SerfRequestProcessor::setupSerfRequest( serf_request_t * inSerfRequest,
602                                    serf_bucket_t ** outSerfRequestBucket,
603                                    serf_response_acceptor_t * outSerfResponseAcceptor,
604                                    void ** outSerfResponseAcceptorBaton,
605                                    serf_response_handler_t * outSerfResponseHandler,
606                                    void ** outSerfResponseHandlerBaton,
607                                    apr_pool_t * /*inAprPool*/ )
608 {
609     mbSetupSerfRequestCalled = true;
610     *outSerfRequestBucket = mpProcImpl->createSerfRequestBucket( inSerfRequest );
611 
612     // apply callbacks for accepting response and handling response
613     *outSerfResponseAcceptor = Serf_AcceptResponse;
614     *outSerfResponseAcceptorBaton = this;
615     *outSerfResponseHandler = Serf_HandleResponse;
616     *outSerfResponseHandlerBaton = this;
617 
618     return APR_SUCCESS;
619 }
620 
621 serf_bucket_t* SerfRequestProcessor::acceptSerfResponse( serf_request_t * inSerfRequest,
622                                                          serf_bucket_t * inSerfStreamBucket,
623                                                          apr_pool_t * inAprPool )
624 {
625     mbAcceptSerfResponseCalled = true;
626     return mrSerfSession.acceptSerfResponse( inSerfRequest,
627                                              inSerfStreamBucket,
628                                              inAprPool );
629 }
630 
631 apr_status_t SerfRequestProcessor::handleSerfResponse( serf_request_t * inSerfRequest,
632                                                        serf_bucket_t * inSerfResponseBucket,
633                                                        apr_pool_t * inAprPool )
634 {
635     mbHandleSerfResponseCalled = true;
636 
637     // some general response handling and error handling
638     {
639         if ( !inSerfResponseBucket )
640         {
641             /* A NULL response can come back if the request failed completely */
642             mbProcessingDone = true;
643             return APR_EGENERAL;
644         }
645 
646         serf_status_line sl;
647         apr_status_t status = serf_bucket_response_status( inSerfResponseBucket, &sl );
648         if ( status )
649         {
650             mbProcessingDone = false; // allow another try in order to get a response
651             return status;
652         }
653         serf_bucket_t *headers = serf_bucket_response_get_headers( inSerfResponseBucket );
654 
655         // check header according:
656         // http://tools.ietf.org/html/rfc7231#section-7.4.2
657         // need to do this so we can adjust the protocol accordingly
658         // serf_bucket_headers_get is case independent
659         const char* server = serf_bucket_headers_get( headers, "server" );
660         if( server )
661         {
662             //update the server type on session
663             mrSerfSession.setServerHeaderField( ::rtl::OUString::createFromAscii( server ) );
664         }
665         //the following extension is MS IIS specific,
666         //see https://msdn.microsoft.com/en-us/library/cc250064.aspx
667         //site last checked on 2015-03-02
668         //TODO i126305 need to be added when serf is updated to a version supporting Windows authentication
669         //const char* msDavExtErr = serf_bucket_headers_get( headers, "X-MSDAVEXT_ERROR" );
670 
671         // TODO - check, if response status code handling is correct
672         mnHTTPStatusCode = ( sl.version != 0 && sl.code >= 0 )
673                            ? static_cast< sal_uInt16 >( sl.code )
674                            : SC_NONE;
675         if ( sl.reason )
676         {
677             mHTTPStatusCodeText = ::rtl::OUString::createFromAscii( sl.reason );
678         }
679         if ( ( sl.version == 0 || sl.code < 0 ) ||
680              mnHTTPStatusCode >= 300 )
681         {
682             if ( mnHTTPStatusCode == 301 ||
683                  mnHTTPStatusCode == 302 ||
684                  mnHTTPStatusCode == 303 ||
685                  mnHTTPStatusCode == 307 )
686             {
687                 // new location for certain redirections
688                 const char* location = serf_bucket_headers_get( headers, "Location" );
689                 if ( location )
690                 {
691                     mRedirectLocation = rtl::OUString::createFromAscii( location );
692                 }
693                 mbProcessingDone = true;
694                 return APR_EGENERAL;
695             }
696             else if ( mrSerfSession.isHeadRequestInProgress() &&
697                       ( mnHTTPStatusCode == 401 || mnHTTPStatusCode == 407 ) )
698             {
699                 // keep going as authentication is not required on HEAD request.
700                 // the response already contains header fields.
701             }
702             else
703             {
704                 mbProcessingDone = true;
705                 return APR_EGENERAL;
706             }
707         }
708     }
709 
710     // request specific processing of the response bucket
711     apr_status_t status = APR_SUCCESS;
712     mbProcessingDone = mpProcImpl->processSerfResponseBucket( inSerfRequest,
713                                                               inSerfResponseBucket,
714                                                               inAprPool,
715                                                               status );
716 
717     return status;
718 }
719 
720 } // namespace http_dav_ucp
721 
722