LibreOffice Module ucb (master)  1
SerfRequestProcessor.cxx
Go to the documentation of this file.
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3  * This file is part of the LibreOffice project.
4  *
5  * This Source Code Form is subject to the terms of the Mozilla Public
6  * License, v. 2.0. If a copy of the MPL was not distributed with this
7  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8  *
9  * This file incorporates work covered by the following license notice:
10  *
11  * Licensed to the Apache Software Foundation (ASF) under one or more
12  * contributor license agreements. See the NOTICE file distributed
13  * with this work for additional information regarding copyright
14  * ownership. The ASF licenses this file to you under the Apache
15  * License, Version 2.0 (the "License"); you may not use this file
16  * except in compliance with the License. You may obtain a copy of
17  * the License at http://www.apache.org/licenses/LICENSE-2.0 .
18  */
19 
20 #include "SerfRequestProcessor.hxx"
21 
22 #include "AprEnv.hxx"
23 #include "SerfCallbacks.hxx"
24 #include "SerfSession.hxx"
27 #include "SerfGetReqProcImpl.hxx"
28 #include "SerfHeadReqProcImpl.hxx"
29 #include "SerfPutReqProcImpl.hxx"
30 #include "SerfPostReqProcImpl.hxx"
32 #include "SerfMkColReqProcImpl.hxx"
33 #include "SerfCopyReqProcImpl.hxx"
34 #include "SerfMoveReqProcImpl.hxx"
35 #include "SerfLockReqProcImpl.hxx"
37 
38 #include <apr_strings.h>
39 
40 namespace http_dav_ucp
41 {
42 
44  const OUString & inPath,
45  const bool bUseChunkedEncoding )
46  : mrSerfSession( rSerfSession )
47  , mPathStr( nullptr )
48  , mbUseChunkedEncoding( bUseChunkedEncoding )
49  , mDestPathStr( nullptr )
50  , mContentType( nullptr )
51  , mReferer( nullptr )
52  , mpProcImpl( nullptr )
53  , mbProcessingDone( false )
54  , mpDAVException()
55  , mnHTTPStatusCode( SC_NONE )
56  , mHTTPStatusCodeText()
57  , mRedirectLocation()
58  , mnSuccessfulCredentialAttempts( 0 )
59  , mbInputOfCredentialsAborted( false )
60  , mbSetupSerfRequestCalled( false )
61  , mbAcceptSerfResponseCalled( false )
62  , mbHandleSerfResponseCalled( false )
63 {
64  mPathStr = apr_pstrdup( SerfSession::getAprPool(),
65  OUStringToOString( inPath, RTL_TEXTENCODING_UTF8 ).getStr() );
66 }
67 
69 {
70  delete mpProcImpl;
71  delete mpDAVException;
72 }
73 
75 {
76  delete mpDAVException;
77  mpDAVException = nullptr;
79  mHTTPStatusCodeText.clear();
80  mRedirectLocation.clear();
81 
87 }
88 
89 // PROPFIND - allprop & named
91  const std::vector< OUString > & inPropNames,
92  std::vector< DAVResource > & ioResources,
93  apr_status_t& outSerfStatus )
94 {
97  inDepth,
98  inPropNames,
99  ioResources );
100  outSerfStatus = runProcessor();
101 
102  return outSerfStatus == APR_SUCCESS;
103 }
104 
105 // PROPFIND - property names
107  std::vector< DAVResourceInfo > & ioResInfo,
108  apr_status_t& outSerfStatus )
109 {
112  inDepth,
113  ioResInfo );
114  outSerfStatus = runProcessor();
115 
116  return outSerfStatus == APR_SUCCESS;
117 }
118 
119 // PROPPATCH
120 bool SerfRequestProcessor::processPropPatch( const std::vector< ProppatchValue > & inProperties,
121  apr_status_t& outSerfStatus )
122 {
125  inProperties );
126  outSerfStatus = runProcessor();
127 
128  return outSerfStatus == APR_SUCCESS;
129 }
130 
131 // GET
133  apr_status_t& outSerfStatus )
134 {
137  xioInStrm );
138  outSerfStatus = runProcessor();
139 
140  return outSerfStatus == APR_SUCCESS;
141 }
142 
143 // GET inclusive header fields
145  const std::vector< OUString > & inHeaderNames,
146  DAVResource & ioResource,
147  apr_status_t& outSerfStatus )
148 {
151  xioInStrm,
152  inHeaderNames,
153  ioResource );
154  outSerfStatus = runProcessor();
155 
156  return outSerfStatus == APR_SUCCESS;
157 }
158 
159 // GET
160 bool SerfRequestProcessor::processGet( const css::uno::Reference< css::io::XOutputStream >& xioOutStrm,
161  apr_status_t& outSerfStatus )
162 {
165  xioOutStrm );
166  outSerfStatus = runProcessor();
167 
168  return outSerfStatus == APR_SUCCESS;
169 }
170 
171 // GET inclusive header fields
172 bool SerfRequestProcessor::processGet( const css::uno::Reference< css::io::XOutputStream >& xioOutStrm,
173  const std::vector< OUString > & inHeaderNames,
174  DAVResource & ioResource,
175  apr_status_t& outSerfStatus )
176 {
179  xioOutStrm,
180  inHeaderNames,
181  ioResource );
182  outSerfStatus = runProcessor();
183 
184  return outSerfStatus == APR_SUCCESS;
185 }
186 
187 // HEAD
188 bool SerfRequestProcessor::processHead( const std::vector< OUString > & inHeaderNames,
189  DAVResource & ioResource,
190  apr_status_t& outSerfStatus )
191 {
194  inHeaderNames,
195  ioResource );
196  outSerfStatus = runProcessor();
197 
198  return outSerfStatus == APR_SUCCESS;
199 }
200 
201 // PUT
202 bool SerfRequestProcessor::processPut( const char* inData,
203  apr_size_t inDataLen,
204  apr_status_t& outSerfStatus )
205 {
206  // get the lock from lock store
207  const OUString sToken(
208  apr_environment::AprEnv::getAprEnv()->getSerfLockStore()->getLockToken(
209  OUString::createFromAscii(mPathStr)) );
210 
213  inData,
214  inDataLen,
215  sToken );
216  outSerfStatus = runProcessor();
217 
218  return outSerfStatus == APR_SUCCESS;
219 }
220 
221 // POST
222 bool SerfRequestProcessor::processPost( const char* inData,
223  apr_size_t inDataLen,
224  const OUString & inContentType,
225  const OUString & inReferer,
226  const rtl::Reference< SerfInputStream >& xioInStrm,
227  apr_status_t& outSerfStatus )
228 {
229  mContentType = apr_pstrdup( SerfSession::getAprPool(),
230  OUStringToOString( inContentType, RTL_TEXTENCODING_UTF8 ).getStr() );
231  mReferer = apr_pstrdup( SerfSession::getAprPool(),
232  OUStringToOString( inReferer, RTL_TEXTENCODING_UTF8 ).getStr() );
235  inData,
236  inDataLen,
237  mContentType,
238  mReferer,
239  xioInStrm );
240  outSerfStatus = runProcessor();
241 
242  return outSerfStatus == APR_SUCCESS;
243 }
244 
245 // POST
246 bool SerfRequestProcessor::processPost( const char* inData,
247  apr_size_t inDataLen,
248  const OUString & inContentType,
249  const OUString & inReferer,
250  const css::uno::Reference< css::io::XOutputStream >& xioOutStrm,
251  apr_status_t& outSerfStatus )
252 {
253  mContentType = apr_pstrdup( SerfSession::getAprPool(),
254  OUStringToOString( inContentType, RTL_TEXTENCODING_UTF8 ).getStr() );
255  mReferer = apr_pstrdup( SerfSession::getAprPool(),
256  OUStringToOString( inReferer, RTL_TEXTENCODING_UTF8 ).getStr() );
259  inData,
260  inDataLen,
261  mContentType,
262  mReferer,
263  xioOutStrm );
264  outSerfStatus = runProcessor();
265 
266  return outSerfStatus == APR_SUCCESS;
267 }
268 
269 // DELETE
270 bool SerfRequestProcessor::processDelete( apr_status_t& outSerfStatus )
271 {
274  outSerfStatus = runProcessor();
275 
276  return outSerfStatus == APR_SUCCESS;
277 }
278 
279 // MKCOL
280 bool SerfRequestProcessor::processMkCol( apr_status_t& outSerfStatus )
281 {
284  outSerfStatus = runProcessor();
285 
286  return outSerfStatus == APR_SUCCESS;
287 }
288 
289 // COPY
290 bool SerfRequestProcessor::processCopy( const OUString & inDestinationPath,
291  const bool inOverwrite,
292  apr_status_t& outSerfStatus )
293 {
294  mDestPathStr = apr_pstrdup( SerfSession::getAprPool(),
295  OUStringToOString( inDestinationPath, RTL_TEXTENCODING_UTF8 ).getStr() );
298  mDestPathStr,
299  inOverwrite );
300  outSerfStatus = runProcessor();
301 
302  return outSerfStatus == APR_SUCCESS;
303 }
304 
305 // MOVE
306 bool SerfRequestProcessor::processMove( const OUString & inDestinationPath,
307  const bool inOverwrite,
308  apr_status_t& outSerfStatus )
309 {
310  mDestPathStr = apr_pstrdup( SerfSession::getAprPool(),
311  OUStringToOString( inDestinationPath, RTL_TEXTENCODING_UTF8 ).getStr() );
314  mDestPathStr,
315  inOverwrite );
316  outSerfStatus = runProcessor();
317 
318  return outSerfStatus == APR_SUCCESS;
319 }
320 
321 
322 bool SerfRequestProcessor::processLock( const css::ucb::Lock & rLock, sal_Int32 *plastChanceToSendRefreshRequest )
323 {
327  rLock,
328  plastChanceToSendRefreshRequest );
329 
330  return runProcessor() == APR_SUCCESS;
331 }
332 
334 {
335  // get the lock from lock store
336  const OUString sToken(
337  apr_environment::AprEnv::getAprEnv()->getSerfLockStore()->getLockToken(
338  OUString::createFromAscii(mPathStr)) );
339  if ( sToken.isEmpty() )
341 
344  sToken );
345 
346  return runProcessor() == APR_SUCCESS;
347 }
348 
350 {
352 
353  // activate chunked encoding, if requested
354  if ( mbUseChunkedEncoding )
355  {
357  }
358 
359  // create serf request
360  serf_connection_request_create( mrSerfSession.getSerfConnection(),
362  this );
363 
364  // perform serf request
365  mbProcessingDone = false;
366  apr_status_t status = APR_SUCCESS;
367  serf_context_t* pSerfContext = mrSerfSession.getSerfContext();
368  apr_pool_t* pAprPool = SerfSession::getAprPool();
369  while ( true )
370  {
371  status = serf_context_run( pSerfContext,
372  SERF_DURATION_FOREVER,
373  pAprPool );
374  if ( APR_STATUS_IS_TIMEUP( status ) )
375  {
376  continue;
377  }
378  if ( status != APR_SUCCESS )
379  {
380  break;
381  }
382  if ( mbProcessingDone )
383  {
384  break;
385  }
386  }
387 
388  postprocessProcessor( status );
389 
390  return status;
391 }
392 
393 void SerfRequestProcessor::postprocessProcessor( const apr_status_t inStatus )
394 {
395  if ( inStatus == APR_SUCCESS )
396  {
397  return;
398  }
399 
400  switch ( inStatus )
401  {
402  case APR_EGENERAL:
403  case SERF_ERROR_AUTHN_FAILED:
404  // general error; <mnHTTPStatusCode> provides more information
405  {
406  switch ( mnHTTPStatusCode )
407  {
408  case SC_NONE:
410  {
413  mrSerfSession.getPort() ) );
414  }
415  else if ( mbInputOfCredentialsAborted )
416  {
419  mrSerfSession.getPort() ) );
420  }
421  else
422  {
426  }
427  break;
430  case SC_SEE_OTHER:
434  break;
435  default:
439  break;
440  }
441  }
442  break;
443 
444  default:
446  break;
447  }
448 
449 }
450 
451 apr_status_t SerfRequestProcessor::provideSerfCredentials( char ** outUsername,
452  char ** outPassword,
453  serf_request_t * inRequest,
454  int inCode,
455  const char *inAuthProtocol,
456  const char *inRealm,
457  apr_pool_t *inAprPool )
458 {
459  // as each successful provided credentials are tried twice - see below - the
460  // number of real attempts is half of the value of <mnSuccessfulCredentialAttempts>
461  if ( (mnSuccessfulCredentialAttempts / 2) >= 5 ||
463  {
465  return SERF_ERROR_AUTHN_FAILED;
466  }
467 
468  // because serf keeps credentials only for a connection in case of digest authentication
469  // we give each successful provided credentials a second try in order to workaround the
470  // situation that the connection for which the credentials have been provided has been closed
471  // before the provided credentials could be applied for the request.
472  apr_status_t status = mrSerfSession.provideSerfCredentials( (mnSuccessfulCredentialAttempts % 2) == 1,
473  outUsername,
474  outPassword,
475  inRequest,
476  inCode,
477  inAuthProtocol,
478  inRealm,
479  inAprPool );
480  if ( status != APR_SUCCESS )
481  {
483  }
484  else
485  {
487  }
488 
489  return status;
490 }
491 
492 apr_status_t SerfRequestProcessor::setupSerfRequest( serf_request_t * inSerfRequest,
493  serf_bucket_t ** outSerfRequestBucket,
494  serf_response_acceptor_t * outSerfResponseAcceptor,
495  void ** outSerfResponseAcceptorBaton,
496  serf_response_handler_t * outSerfResponseHandler,
497  void ** outSerfResponseHandlerBaton,
498  apr_pool_t * /*inAprPool*/ )
499 {
501  *outSerfRequestBucket = mpProcImpl->createSerfRequestBucket( inSerfRequest );
502 
503  // apply callbacks for accepting response and handling response
504  *outSerfResponseAcceptor = Serf_AcceptResponse;
505  *outSerfResponseAcceptorBaton = this;
506  *outSerfResponseHandler = Serf_HandleResponse;
507  *outSerfResponseHandlerBaton = this;
508 
509  return APR_SUCCESS;
510 }
511 
512 serf_bucket_t* SerfRequestProcessor::acceptSerfResponse( serf_request_t * inSerfRequest,
513  serf_bucket_t * inSerfStreamBucket,
514  apr_pool_t * inAprPool )
515 {
517  return mrSerfSession.acceptSerfResponse( inSerfRequest,
518  inSerfStreamBucket,
519  inAprPool );
520 }
521 
522 apr_status_t SerfRequestProcessor::handleSerfResponse( serf_request_t * inSerfRequest,
523  serf_bucket_t * inSerfResponseBucket,
524  apr_pool_t * inAprPool )
525 {
527 
528  // some general response handling and error handling
529  {
530  if ( !inSerfResponseBucket )
531  {
532  /* A NULL response can come back if the request failed completely */
533  mbProcessingDone = true;
534  return APR_EGENERAL;
535  }
536 
537  serf_status_line sl;
538  apr_status_t status = serf_bucket_response_status( inSerfResponseBucket, &sl );
539  if ( status )
540  {
541  mbProcessingDone = false; // allow another try in order to get a response
542  return status;
543  }
544  // TODO - check, if response status code handling is correct
545  mnHTTPStatusCode = ( sl.version != 0 && sl.code >= 0 )
546  ? static_cast< sal_uInt16 >( sl.code )
547  : SC_NONE;
548  if ( sl.reason )
549  {
550  mHTTPStatusCodeText = OUString::createFromAscii( sl.reason );
551  }
552  if ( ( sl.version == 0 || sl.code < 0 ) ||
553  mnHTTPStatusCode >= 300 )
554  {
555  if ( mnHTTPStatusCode == 301 ||
556  mnHTTPStatusCode == 302 ||
557  mnHTTPStatusCode == 303 ||
558  mnHTTPStatusCode == 307 )
559  {
560  // new location for certain redirections
561  serf_bucket_t *headers = serf_bucket_response_get_headers( inSerfResponseBucket );
562  const char* location = serf_bucket_headers_get( headers, "Location" );
563  if ( location )
564  {
565  mRedirectLocation = OUString::createFromAscii( location );
566  }
567  mbProcessingDone = true;
568  return APR_EGENERAL;
569  }
571  ( mnHTTPStatusCode == 401 || mnHTTPStatusCode == 407 ) )
572  {
573  // keep going as authentication is not required on HEAD request.
574  // the response already contains header fields.
575  }
576  else
577  {
578  mbProcessingDone = true;
579  return APR_EGENERAL;
580  }
581  }
582  }
583 
584  // request specific processing of the response bucket
585  apr_status_t status = APR_SUCCESS;
587  inSerfResponseBucket,
588  inAprPool,
589  status );
590 
591  return status;
592 }
593 
594 } // namespace http_dav_ucp
595 
596 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
const sal_uInt16 SC_MOVED_TEMPORARILY
bool processMove(const OUString &inDestinationPath, const bool inOverwrite, apr_status_t &outSerfStatus)
serf_context_t * getSerfContext()
bool processCopy(const OUString &inDestinationPath, const bool inOverwrite, apr_status_t &outSerfStatus)
virtual serf_bucket_t * createSerfRequestBucket(serf_request_t *inSerfRequest)=0
const OUString & getHostName() const
bool processHead(const std::vector< OUString > &inHeaderNames, DAVResource &ioResource, apr_status_t &outSerfStatus)
const sal_uInt16 SC_NONE
bool processMkCol(apr_status_t &outSerfStatus)
apr_status_t Serf_HandleResponse(serf_request_t *request, serf_bucket_t *response, void *handler_baton, apr_pool_t *pool)
apr_status_t Serf_SetupRequest(serf_request_t *request, void *setup_baton, serf_bucket_t **req_bkt, serf_response_acceptor_t *acceptor, void **acceptor_baton, serf_response_handler_t *handler, void **handler_baton, apr_pool_t *pool)
bool processDelete(apr_status_t &outSerfStatus)
serf_bucket_t * acceptSerfResponse(serf_request_t *inSerfRequest, serf_bucket_t *inSerfStreamBucket, apr_pool_t *inAprPool)
OUString makeConnectionEndPointString() const
Definition: SerfUri.hxx:96
apr_status_t provideSerfCredentials(bool bGiveProvidedCredentialsASecondTry, char **outUsername, char **outPassword, serf_request_t *inRequest, int inCode, const char *inAuthProtocol, const char *inRealm, apr_pool_t *inAprPool)
serf_bucket_t * Serf_AcceptResponse(serf_request_t *request, serf_bucket_t *stream, void *acceptor_baton, apr_pool_t *pool)
SerfRequestProcessor(SerfSession &rSerfSession, const OUString &inPath, const bool bUseChunkedEncoding)
void postprocessProcessor(const apr_status_t inStatus)
static AprEnv * getAprEnv()
Definition: AprEnv.cxx:45
serf_connection_t * getSerfConnection()
bool processSerfResponseBucket(serf_request_t *inSerfRequest, serf_bucket_t *inSerfResponseBucket, apr_pool_t *inAprPool, apr_status_t &outStatus)
apr_status_t provideSerfCredentials(char **outUsername, char **outPassword, serf_request_t *inRequest, int inCode, const char *inAuthProtocol, const char *inRealm, apr_pool_t *inAprPool)
bool processGet(const rtl::Reference< SerfInputStream > &xioInStrm, apr_status_t &outSerfStatus)
apr_status_t setupSerfRequest(serf_request_t *inSerfRequest, serf_bucket_t **outSerfRequestBucket, serf_response_acceptor_t *outSerfResponseAcceptor, void **outSerfResponseAcceptorBaton, serf_response_handler_t *outSerfResponseHandler, void **outSerfResponseHandlerBaton, apr_pool_t *inAprPool)
const sal_uInt16 SC_TEMPORARY_REDIRECT
bool processPropPatch(const std::vector< ProppatchValue > &inProperties, apr_status_t &outSerfStatus)
bool processLock(const css::ucb::Lock &rLock, sal_Int32 *plastChanceToSendRefreshRequest=nullptr)
serf_bucket_t * acceptSerfResponse(serf_request_t *inSerfRequest, serf_bucket_t *inSerfStreamBucket, apr_pool_t *inAprPool)
apr_status_t handleSerfResponse(serf_request_t *inSerfRequest, serf_bucket_t *inSerfResponseBucket, apr_pool_t *inAprPool)
OString OUStringToOString(const OUString &str, ConnectionSettings const *settings)
static apr_pool_t * getAprPool()
bool processPost(const char *inData, apr_size_t inDataLen, const OUString &inContentType, const OUString &inReferer, const rtl::Reference< SerfInputStream > &xioInStrm, apr_status_t &outSerfStatus)
const DAVRequestEnvironment & getRequestEnvironment() const
const sal_uInt16 SC_SEE_OTHER
const sal_uInt16 SC_MOVED_PERMANENTLY
bool processPut(const char *inData, apr_size_t inDataLen, apr_status_t &outSerfStatus)
bool processPropFind(const Depth inDepth, const std::vector< OUString > &inPropNames, std::vector< DAVResource > &ioResources, apr_status_t &outSerfStatus)