blob: ee46a81eb107fea17390052a8765fa5b01c91269 [file] [log] [blame]
/**************************************************************
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*
*************************************************************/
// MARKER(update_precomp.py): autogen include statement, do not remove
#include "precompiled_ucb.hxx"
#include "SerfRequestProcessor.hxx"
#include "SerfRequestProcessorImpl.hxx"
#include "SerfRequestProcessorImplFac.hxx"
#include "SerfCallbacks.hxx"
#include "SerfSession.hxx"
#include <apr_strings.h>
namespace http_dav_ucp
{
SerfRequestProcessor::SerfRequestProcessor( SerfSession& rSerfSession,
const rtl::OUString & inPath,
const bool bUseChunkedEncoding )
: mrSerfSession( rSerfSession )
, mPathStr( 0 )
, mbUseChunkedEncoding( bUseChunkedEncoding )
, mDestPathStr( 0 )
, mContentType( 0 )
, mReferer( 0 )
, mpProcImpl( 0 )
, mbProcessingDone( false )
, mpDAVException()
, mnHTTPStatusCode( SC_NONE )
, mHTTPStatusCodeText()
, mRedirectLocation()
, mnSuccessfulCredentialAttempts( 0 )
, mbInputOfCredentialsAborted( false )
, mbSetupSerfRequestCalled( false )
, mbAcceptSerfResponseCalled( false )
, mbHandleSerfResponseCalled( false )
{
mPathStr = apr_pstrdup( mrSerfSession.getAprPool(),
rtl::OUStringToOString( inPath, RTL_TEXTENCODING_UTF8 ).getStr() );
}
SerfRequestProcessor::~SerfRequestProcessor()
{
delete mpProcImpl;
delete mpDAVException;
}
void SerfRequestProcessor::prepareProcessor()
{
delete mpDAVException;
mpDAVException = 0;
mnHTTPStatusCode = SC_NONE;
mHTTPStatusCodeText = rtl::OUString();
mRedirectLocation = rtl::OUString();
mnSuccessfulCredentialAttempts = 0;
mbInputOfCredentialsAborted = false;
mbSetupSerfRequestCalled = false;
mbAcceptSerfResponseCalled = false;
mbHandleSerfResponseCalled = false;
}
// PROPFIND - allprop & named
bool SerfRequestProcessor::processPropFind( const Depth inDepth,
const std::vector< ::rtl::OUString > & inPropNames,
std::vector< DAVResource > & ioResources,
apr_status_t& outSerfStatus )
{
mpProcImpl = createPropFindReqProcImpl( mPathStr,
mrSerfSession.getRequestEnvironment().m_aRequestHeaders,
inDepth,
inPropNames,
ioResources );
outSerfStatus = runProcessor();
return outSerfStatus == APR_SUCCESS;
}
// PROPFIND - property names
bool SerfRequestProcessor::processPropFind( const Depth inDepth,
std::vector< DAVResourceInfo > & ioResInfo,
apr_status_t& outSerfStatus )
{
mpProcImpl = createPropFindReqProcImpl( mPathStr,
mrSerfSession.getRequestEnvironment().m_aRequestHeaders,
inDepth,
ioResInfo );
outSerfStatus = runProcessor();
return outSerfStatus == APR_SUCCESS;
}
// PROPPATCH
bool SerfRequestProcessor::processPropPatch( const std::vector< ProppatchValue > & inProperties,
apr_status_t& outSerfStatus )
{
mpProcImpl = createPropPatchReqProcImpl( mPathStr,
mrSerfSession.getRequestEnvironment().m_aRequestHeaders,
inProperties );
outSerfStatus = runProcessor();
return outSerfStatus == APR_SUCCESS;
}
// GET
bool SerfRequestProcessor::processGet( const com::sun::star::uno::Reference< SerfInputStream >& xioInStrm,
apr_status_t& outSerfStatus )
{
mpProcImpl = createGetReqProcImpl( mPathStr,
mrSerfSession.getRequestEnvironment().m_aRequestHeaders,
xioInStrm );
outSerfStatus = runProcessor();
return outSerfStatus == APR_SUCCESS;
}
// GET inclusive header fields
bool SerfRequestProcessor::processGet( const com::sun::star::uno::Reference< SerfInputStream >& xioInStrm,
const std::vector< ::rtl::OUString > & inHeaderNames,
DAVResource & ioResource,
apr_status_t& outSerfStatus )
{
mpProcImpl = createGetReqProcImpl( mPathStr,
mrSerfSession.getRequestEnvironment().m_aRequestHeaders,
xioInStrm,
inHeaderNames,
ioResource );
outSerfStatus = runProcessor();
return outSerfStatus == APR_SUCCESS;
}
// GET
bool SerfRequestProcessor::processGet( const com::sun::star::uno::Reference< com::sun::star::io::XOutputStream >& xioOutStrm,
apr_status_t& outSerfStatus )
{
mpProcImpl = createGetReqProcImpl( mPathStr,
mrSerfSession.getRequestEnvironment().m_aRequestHeaders,
xioOutStrm );
outSerfStatus = runProcessor();
return outSerfStatus == APR_SUCCESS;
}
// GET inclusive header fields
bool SerfRequestProcessor::processGet( const com::sun::star::uno::Reference< com::sun::star::io::XOutputStream >& xioOutStrm,
const std::vector< ::rtl::OUString > & inHeaderNames,
DAVResource & ioResource,
apr_status_t& outSerfStatus )
{
mpProcImpl = createGetReqProcImpl( mPathStr,
mrSerfSession.getRequestEnvironment().m_aRequestHeaders,
xioOutStrm,
inHeaderNames,
ioResource );
outSerfStatus = runProcessor();
return outSerfStatus == APR_SUCCESS;
}
// HEAD
bool SerfRequestProcessor::processHead( const std::vector< ::rtl::OUString > & inHeaderNames,
DAVResource & ioResource,
apr_status_t& outSerfStatus )
{
mpProcImpl = createHeadReqProcImpl( mPathStr,
mrSerfSession.getRequestEnvironment().m_aRequestHeaders,
inHeaderNames,
ioResource );
outSerfStatus = runProcessor();
return outSerfStatus == APR_SUCCESS;
}
// PUT
bool SerfRequestProcessor::processPut( const char* inData,
apr_size_t inDataLen,
apr_status_t& outSerfStatus )
{
mpProcImpl = createPutReqProcImpl( mPathStr,
mrSerfSession.getRequestEnvironment().m_aRequestHeaders,
inData,
inDataLen );
outSerfStatus = runProcessor();
return outSerfStatus == APR_SUCCESS;
}
// POST
bool SerfRequestProcessor::processPost( const char* inData,
apr_size_t inDataLen,
const rtl::OUString & inContentType,
const rtl::OUString & inReferer,
const com::sun::star::uno::Reference< SerfInputStream >& xioInStrm,
apr_status_t& outSerfStatus )
{
mContentType = apr_pstrdup( mrSerfSession.getAprPool(),
rtl::OUStringToOString( inContentType, RTL_TEXTENCODING_UTF8 ).getStr() );
mReferer = apr_pstrdup( mrSerfSession.getAprPool(),
rtl::OUStringToOString( inReferer, RTL_TEXTENCODING_UTF8 ).getStr() );
mpProcImpl = createPostReqProcImpl( mPathStr,
mrSerfSession.getRequestEnvironment().m_aRequestHeaders,
inData,
inDataLen,
mContentType,
mReferer,
xioInStrm );
outSerfStatus = runProcessor();
return outSerfStatus == APR_SUCCESS;
}
// POST
bool SerfRequestProcessor::processPost( const char* inData,
apr_size_t inDataLen,
const rtl::OUString & inContentType,
const rtl::OUString & inReferer,
const com::sun::star::uno::Reference< com::sun::star::io::XOutputStream >& xioOutStrm,
apr_status_t& outSerfStatus )
{
mContentType = apr_pstrdup( mrSerfSession.getAprPool(),
rtl::OUStringToOString( inContentType, RTL_TEXTENCODING_UTF8 ).getStr() );
mReferer = apr_pstrdup( mrSerfSession.getAprPool(),
rtl::OUStringToOString( inReferer, RTL_TEXTENCODING_UTF8 ).getStr() );
mpProcImpl = createPostReqProcImpl( mPathStr,
mrSerfSession.getRequestEnvironment().m_aRequestHeaders,
inData,
inDataLen,
mContentType,
mReferer,
xioOutStrm );
outSerfStatus = runProcessor();
return outSerfStatus == APR_SUCCESS;
}
// DELETE
bool SerfRequestProcessor::processDelete( apr_status_t& outSerfStatus )
{
mpProcImpl = createDeleteReqProcImpl( mPathStr,
mrSerfSession.getRequestEnvironment().m_aRequestHeaders );
outSerfStatus = runProcessor();
return outSerfStatus == APR_SUCCESS;
}
// MKCOL
bool SerfRequestProcessor::processMkCol( apr_status_t& outSerfStatus )
{
mpProcImpl = createMkColReqProcImpl( mPathStr,
mrSerfSession.getRequestEnvironment().m_aRequestHeaders );
outSerfStatus = runProcessor();
return outSerfStatus == APR_SUCCESS;
}
// COPY
bool SerfRequestProcessor::processCopy( const rtl::OUString & inDestinationPath,
const bool inOverwrite,
apr_status_t& outSerfStatus )
{
mDestPathStr = apr_pstrdup( mrSerfSession.getAprPool(),
rtl::OUStringToOString( inDestinationPath, RTL_TEXTENCODING_UTF8 ).getStr() );
mpProcImpl = createCopyReqProcImpl( mPathStr,
mrSerfSession.getRequestEnvironment().m_aRequestHeaders,
mDestPathStr,
inOverwrite );
outSerfStatus = runProcessor();
return outSerfStatus == APR_SUCCESS;
}
// MOVE
bool SerfRequestProcessor::processMove( const rtl::OUString & inDestinationPath,
const bool inOverwrite,
apr_status_t& outSerfStatus )
{
mDestPathStr = apr_pstrdup( mrSerfSession.getAprPool(),
rtl::OUStringToOString( inDestinationPath, RTL_TEXTENCODING_UTF8 ).getStr() );
mpProcImpl = createMoveReqProcImpl( mPathStr,
mrSerfSession.getRequestEnvironment().m_aRequestHeaders,
mDestPathStr,
inOverwrite );
outSerfStatus = runProcessor();
return outSerfStatus == APR_SUCCESS;
}
apr_status_t SerfRequestProcessor::runProcessor()
{
prepareProcessor();
// activate chunked encoding, if requested
if ( mbUseChunkedEncoding )
{
mpProcImpl->activateChunkedEncoding();
}
// create serf request
serf_connection_request_create( mrSerfSession.getSerfConnection(),
Serf_SetupRequest,
this );
// perform serf request
mbProcessingDone = false;
apr_status_t status = APR_SUCCESS;
serf_context_t* pSerfContext = mrSerfSession.getSerfContext();
apr_pool_t* pAprPool = mrSerfSession.getAprPool();
while ( true )
{
status = serf_context_run( pSerfContext,
SERF_DURATION_FOREVER,
pAprPool );
if ( APR_STATUS_IS_TIMEUP( status ) )
{
continue;
}
if ( status != APR_SUCCESS )
{
break;
}
if ( mbProcessingDone )
{
break;
}
}
postprocessProcessor( status );
return status;
}
void SerfRequestProcessor::postprocessProcessor( const apr_status_t inStatus )
{
if ( inStatus == APR_SUCCESS )
{
return;
}
switch ( inStatus )
{
case APR_EGENERAL:
case SERF_ERROR_AUTHN_FAILED:
// general error; <mnHTTPStatusCode> provides more information
{
switch ( mnHTTPStatusCode )
{
case SC_NONE:
if ( !mbSetupSerfRequestCalled )
{
mpDAVException = new DAVException( DAVException::DAV_HTTP_LOOKUP,
SerfUri::makeConnectionEndPointString( mrSerfSession.getHostName(),
mrSerfSession.getPort() ) );
}
else if ( mbInputOfCredentialsAborted )
{
mpDAVException = new DAVException( DAVException::DAV_HTTP_NOAUTH,
SerfUri::makeConnectionEndPointString( mrSerfSession.getHostName(),
mrSerfSession.getPort() ) );
}
else
{
mpDAVException = new DAVException( DAVException::DAV_HTTP_ERROR,
mHTTPStatusCodeText,
mnHTTPStatusCode );
}
break;
case SC_MOVED_PERMANENTLY:
case SC_MOVED_TEMPORARILY:
case SC_SEE_OTHER:
case SC_TEMPORARY_REDIRECT:
mpDAVException = new DAVException( DAVException::DAV_HTTP_REDIRECT,
mRedirectLocation );
break;
default:
mpDAVException = new DAVException( DAVException::DAV_HTTP_ERROR,
mHTTPStatusCodeText,
mnHTTPStatusCode );
break;
}
}
break;
default:
mpDAVException = new DAVException( DAVException::DAV_HTTP_ERROR );
break;
}
}
apr_status_t SerfRequestProcessor::provideSerfCredentials( char ** outUsername,
char ** outPassword,
serf_request_t * inRequest,
int inCode,
const char *inAuthProtocol,
const char *inRealm,
apr_pool_t *inAprPool )
{
// as each successful provided credentials are tried twice - see below - the
// number of real attempts is half of the value of <mnSuccessfulCredentialAttempts>
if ( (mnSuccessfulCredentialAttempts / 2) >= 5 ||
mbInputOfCredentialsAborted )
{
mbInputOfCredentialsAborted = true;
return SERF_ERROR_AUTHN_FAILED;
}
// because serf keeps credentials only for a connection in case of digest authentication
// we give each successful provided credentials a second try in order to workaround the
// situation that the connection for which the credentials have been provided has been closed
// before the provided credentials could be applied for the request.
apr_status_t status = mrSerfSession.provideSerfCredentials( (mnSuccessfulCredentialAttempts % 2) == 1,
outUsername,
outPassword,
inRequest,
inCode,
inAuthProtocol,
inRealm,
inAprPool );
if ( status != APR_SUCCESS )
{
mbInputOfCredentialsAborted = true;
}
else
{
++mnSuccessfulCredentialAttempts;
}
return status;
}
apr_status_t SerfRequestProcessor::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*/ )
{
mbSetupSerfRequestCalled = true;
*outSerfRequestBucket = mpProcImpl->createSerfRequestBucket( inSerfRequest );
// apply callbacks for accepting response and handling response
*outSerfResponseAcceptor = Serf_AcceptResponse;
*outSerfResponseAcceptorBaton = this;
*outSerfResponseHandler = Serf_HandleResponse;
*outSerfResponseHandlerBaton = this;
return APR_SUCCESS;
}
serf_bucket_t* SerfRequestProcessor::acceptSerfResponse( serf_request_t * inSerfRequest,
serf_bucket_t * inSerfStreamBucket,
apr_pool_t * inAprPool )
{
mbAcceptSerfResponseCalled = true;
return mrSerfSession.acceptSerfResponse( inSerfRequest,
inSerfStreamBucket,
inAprPool );
}
apr_status_t SerfRequestProcessor::handleSerfResponse( serf_request_t * inSerfRequest,
serf_bucket_t * inSerfResponseBucket,
apr_pool_t * inAprPool )
{
mbHandleSerfResponseCalled = true;
// some general response handling and error handling
{
if ( !inSerfResponseBucket )
{
/* A NULL response can come back if the request failed completely */
mbProcessingDone = true;
return APR_EGENERAL;
}
serf_status_line sl;
apr_status_t status = serf_bucket_response_status( inSerfResponseBucket, &sl );
if ( status )
{
mbProcessingDone = false; // allow another try in order to get a response
return status;
}
// TODO - check, if response status code handling is correct
mnHTTPStatusCode = ( sl.version != 0 && sl.code >= 0 )
? static_cast< sal_uInt16 >( sl.code )
: SC_NONE;
if ( sl.reason )
{
mHTTPStatusCodeText = ::rtl::OUString::createFromAscii( sl.reason );
}
if ( ( sl.version == 0 || sl.code < 0 ) ||
mnHTTPStatusCode >= 300 )
{
if ( mnHTTPStatusCode == 301 ||
mnHTTPStatusCode == 302 ||
mnHTTPStatusCode == 303 ||
mnHTTPStatusCode == 307 )
{
// new location for certain redirections
serf_bucket_t *headers = serf_bucket_response_get_headers( inSerfResponseBucket );
const char* location = serf_bucket_headers_get( headers, "Location" );
if ( location )
{
mRedirectLocation = rtl::OUString::createFromAscii( location );
}
mbProcessingDone = true;
return APR_EGENERAL;
}
else if ( mrSerfSession.isHeadRequestInProgress() &&
( mnHTTPStatusCode == 401 || mnHTTPStatusCode == 407 ) )
{
// keep going as authentication is not required on HEAD request.
// the response already contains header fields.
}
else
{
mbProcessingDone = true;
return APR_EGENERAL;
}
}
}
// request specific processing of the response bucket
apr_status_t status = APR_SUCCESS;
mbProcessingDone = mpProcImpl->processSerfResponseBucket( inSerfRequest,
inSerfResponseBucket,
inAprPool,
status );
return status;
}
} // namespace http_dav_ucp