/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* * This file is part of the LibreOffice project. * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
// TODO FIXME maybe we should compile with BOOST_DATE_TIME_POSIX_TIME_STD_CONFIG // to actually get nanosecond precision in boostTime? // use this way rather than total_nanos to avoid overflows with 32-bit long const tools::Long ticks = boostTime.time_of_day().fractional_seconds();
tools::Long nanoSeconds = ticks * ( 1000000000 / boost::posix_time::time_duration::ticks_per_second());
libcmis::Session* Content::getSession( const uno::Reference< ucb::XCommandEnvironment >& xEnv )
{ // Set the proxy if needed. We are doing that all times as the proxy data shouldn't be cached.
ucbhelper::InternetProxyDecider aProxyDecider( m_xContext );
INetURLObject aBindingUrl( m_aURL.getBindingUrl( ) ); const OUString sProxy = aProxyDecider.getProxy(
INetURLObject::GetScheme( aBindingUrl.GetProtocol( ) ), aBindingUrl.GetHost(), aBindingUrl.GetPort() );
libcmis::SessionFactory::setProxySettings( OUSTR_TO_STDSTR( sProxy ), std::string(), std::string(), std::string() );
// Look for a cached session, key is binding url + repo id
OUString sSessionId = m_aURL.getBindingUrl( ) + m_aURL.getRepositoryId( ); if ( nullptr == m_pSession )
m_pSession = m_pProvider->getSession( sSessionId, m_aURL.getUsername( ) );
// Get the auth credentials
AuthProvider aAuthProvider(xEnv, m_xIdentifier->getContentIdentifier(), m_aURL.getBindingUrl());
AuthProvider::setXEnv( xEnv );
auto rUsername = OUSTR_TO_STDSTR( m_aURL.getUsername( ) ); auto rPassword = OUSTR_TO_STDSTR( m_aURL.getPassword( ) );
bool bSkipInitialPWAuth = false; if (m_aURL.getBindingUrl() == ONEDRIVE_BASE_URL
|| m_aURL.getBindingUrl() == GDRIVE_BASE_URL)
{ // skip the initial username and pw-auth prompt, the only supported method is the // auth-code-fallback one (login with your browser, copy code into the dialog) // TODO: if LO were to listen on localhost for the request, it would be much nicer // user experience
bSkipInitialPWAuth = true;
rPassword = aAuthProvider.getRefreshToken(rUsername);
}
bool bIsDone = false;
while ( !bIsDone )
{ if (bSkipInitialPWAuth || aAuthProvider.authenticationQuery(rUsername, rPassword))
{ // Initiate a CMIS session and register it as we found nothing
libcmis::OAuth2DataPtr oauth2Data; if ( m_aURL.getBindingUrl( ) == GDRIVE_BASE_URL )
{ // reset the skip, so user gets a chance to cancel
bSkipInitialPWAuth = false;
libcmis::SessionFactory::setOAuth2AuthCodeProvider(AuthProvider::copyWebAuthCodeFallback);
oauth2Data = boost::make_shared<libcmis::OAuth2Data>(
GDRIVE_AUTH_URL, GDRIVE_TOKEN_URL,
GDRIVE_SCOPE, GDRIVE_REDIRECT_URI,
GDRIVE_CLIENT_ID, GDRIVE_CLIENT_SECRET );
} if ( m_aURL.getBindingUrl().startsWith( ALFRESCO_CLOUD_BASE_URL ) )
oauth2Data = boost::make_shared<libcmis::OAuth2Data>(
ALFRESCO_CLOUD_AUTH_URL, ALFRESCO_CLOUD_TOKEN_URL,
ALFRESCO_CLOUD_SCOPE, ALFRESCO_CLOUD_REDIRECT_URI,
ALFRESCO_CLOUD_CLIENT_ID, ALFRESCO_CLOUD_CLIENT_SECRET ); if ( m_aURL.getBindingUrl( ) == ONEDRIVE_BASE_URL )
{ // reset the skip, so user gets a chance to cancel
bSkipInitialPWAuth = false;
libcmis::SessionFactory::setOAuth2AuthCodeProvider(AuthProvider::copyWebAuthCodeFallback);
oauth2Data = boost::make_shared<libcmis::OAuth2Data>(
ONEDRIVE_AUTH_URL, ONEDRIVE_TOKEN_URL,
ONEDRIVE_SCOPE, ONEDRIVE_REDIRECT_URI,
ONEDRIVE_CLIENT_ID, ONEDRIVE_CLIENT_SECRET );
} try
{
m_pSession = libcmis::SessionFactory::createSession(
OUSTR_TO_STDSTR( m_aURL.getBindingUrl( ) ),
rUsername, rPassword, OUSTR_TO_STDSTR( m_aURL.getRepositoryId( ) ), false, std::move(oauth2Data) );
if ( m_pSession == nullptr )
{ // Fail: session was not created
ucbhelper::cancelCommandExecution(
ucb::IOErrorCode_INVALID_DEVICE,
generateErrorArguments(m_aURL),
xEnv);
} elseif ( m_pSession->getRepository() == nullptr )
{ // Fail: no repository or repository is invalid
ucbhelper::cancelCommandExecution(
ucb::IOErrorCode_INVALID_DEVICE,
generateErrorArguments(m_aURL),
xEnv,
u"error accessing a repository"_ustr);
} else
{
m_pProvider->registerSession(sSessionId, m_aURL.getUsername( ), m_pSession); if (m_aURL.getBindingUrl() == ONEDRIVE_BASE_URL
|| m_aURL.getBindingUrl() == GDRIVE_BASE_URL)
{
aAuthProvider.storeRefreshToken(rUsername, rPassword,
m_pSession->getRefreshToken());
}
}
libcmis::ObjectTypePtr const & Content::getObjectType( const uno::Reference< ucb::XCommandEnvironment >& xEnv )
{ if ( nullptr == m_pObjectType.get( ) && m_bTransient )
{ const std::string typeId = m_bIsFolder ? "cmis:folder" : "cmis:document"; // The type to create needs to be fetched from the possible children types // defined in the parent folder. Then, we'll pick up the first one we find matching // cmis:folder or cmis:document (depending what we need to create). // The easy case will work in most cases, but not on some servers (like Lotus Live)
libcmis::Folder* pParent = nullptr; bool bTypeRestricted = false; try
{
pParent = dynamic_cast< libcmis::Folder* >( getObject( xEnv ).get( ) );
} catch ( const libcmis::Exception& )
{
}
if ( pParent )
{
std::map< std::string, libcmis::PropertyPtr >& aProperties = pParent->getProperties( );
std::map< std::string, libcmis::PropertyPtr >::iterator it = aProperties.find( "cmis:allowedChildObjectTypeIds" ); if ( it != aProperties.end( ) )
{
libcmis::PropertyPtr pProperty = it->second; if ( pProperty )
{
std::vector< std::string > typesIds = pProperty->getStrings( ); for ( constauto& rType : typesIds )
{
bTypeRestricted = true;
libcmis::ObjectTypePtr type = getSession( xEnv )->getType( rType );
// FIXME Improve performances by adding getBaseTypeId( ) method to libcmis if ( type->getBaseType( )->getId( ) == typeId )
{
m_pObjectType = std::move(type); break;
}
}
}
}
}
if ( !feedSink( rOpenCommand.Sink, xEnv ) )
{ // Note: rOpenCommand.Sink may contain an XStream // implementation. Support for this type of // sink is optional...
SAL_INFO( "ucb.ucp.cmis", "Failed to copy data to sink" );
// Get the URL and send it back as a result
URL aCmisUrl( m_sURL );
std::vector< std::string > aPaths = pDoc->getPaths( ); if ( !aPaths.empty() )
{
aCmisUrl.setObjectPath(STD_TO_OUSTR(aPaths.front()));
} else
{ // We may have unfiled document depending on the server, those // won't have any path, use their ID instead
aCmisUrl.setObjectId(STD_TO_OUSTR(pDoc->getId()));
} return aCmisUrl.asString( );
}
OUString Content::checkOut( const uno::Reference< ucb::XCommandEnvironment > & xEnv )
{
OUString aRet; try
{ // Checkout the document if possible
libcmis::DocumentPtr pDoc = boost::dynamic_pointer_cast< libcmis::Document >( getObject( xEnv ) ); if ( pDoc.get( ) == nullptr )
{
ucbhelper::cancelCommandExecution(
ucb::IOErrorCode_GENERAL,
uno::Sequence< uno::Any >( 0 ),
xEnv,
u"Checkout only supported by documents"_ustr );
}
libcmis::DocumentPtr pPwc = pDoc->checkOut( );
// Compute the URL of the Private Working Copy (PWC)
URL aCmisUrl( m_sURL );
std::vector< std::string > aPaths = pPwc->getPaths( ); if ( !aPaths.empty() )
{
aCmisUrl.setObjectPath(STD_TO_OUSTR(aPaths.front()));
} else
{ // We may have unfiled PWC depending on the server, those // won't have any path, use their ID instead auto sId = pPwc->getId( );
aCmisUrl.setObjectId( STD_TO_OUSTR( sId ) );
}
aRet = aCmisUrl.asString( );
} catch ( const libcmis::Exception& e )
{
SAL_INFO( "ucb.ucp.cmis", "Unexpected libcmis exception: " << e.what( ) );
ucbhelper::cancelCommandExecution(
ucb::IOErrorCode_GENERAL,
uno::Sequence< uno::Any >( 0 ),
xEnv,
o3tl::runtimeToOUString(e.what()));
} return aRet;
}
// For transient content, the URL is the one of the parent if ( !m_bTransient ) return;
OUString sNewPath;
// Try to get the object from the server if there is any
libcmis::FolderPtr pFolder; try
{
pFolder = boost::dynamic_pointer_cast< libcmis::Folder >( getObject( xEnv ) );
} catch ( const libcmis::Exception& )
{
}
if ( nullptr != object.get( ) )
{ // Are the base type matching? if ( object->getBaseType( ) != m_pObjectType->getBaseType( )->getId() )
{
ucbhelper::cancelCommandExecution( uno::Any
( uno::RuntimeException( u"Can't change a folder into a document and vice-versa."_ustr,
getXWeak() ) ),
xEnv );
}
// Update the existing object if it's a document
libcmis::Document* document = dynamic_cast< libcmis::Document* >( object.get( ) ); if ( nullptr != document )
{
boost::shared_ptr< std::ostream > pOut( new std::ostringstream ( std::ios_base::binary | std::ios_base::in | std::ios_base::out ) );
uno::Reference < io::XOutputStream > xOutput = new StdOutputStream( pOut );
copyData( xInputStream, xOutput ); try
{
document->setContentStream( std::move(pOut), OUSTR_TO_STDSTR( rMimeType ), std::string( ), bReplaceExisting );
} catch ( const libcmis::Exception& )
{
ucbhelper::cancelCommandExecution( uno::Any
( uno::RuntimeException( u"Error when setting document content"_ustr,
getXWeak() ) ),
xEnv );
}
}
} else
{ // We need to create a brand new object... either folder or document bool bIsFolder = getObjectType( xEnv )->getBaseType( )->getId( ) == "cmis:folder";
setCmisProperty( "cmis:objectTypeId", getObjectType( xEnv )->getId( ), xEnv );
sal_Int32 nCount = rValues.getLength();
uno::Sequence< uno::Any > aRet( nCount ); auto aRetRange = asNonConstRange(aRet); bool bChanged = false; const beans::PropertyValue* pValues = rValues.getConstArray(); for ( sal_Int32 n = 0; n < nCount; ++n )
{ const beans::PropertyValue& rValue = pValues[ n ]; if ( rValue.Name == "ContentType" ||
rValue.Name == "MediaType" ||
rValue.Name == "IsDocument" ||
rValue.Name == "IsFolder" ||
rValue.Name == "Size" ||
rValue.Name == "CreatableContentsInfo" )
{
lang::IllegalAccessException e ( u"Property is read-only!"_ustr,
getXWeak() );
aRetRange[ n ] <<= e;
} elseif ( rValue.Name == "Title" )
{
OUString aNewTitle; if (!( rValue.Value >>= aNewTitle ))
{
aRetRange[ n ] <<= beans::IllegalTypeException
( u"Property value has wrong type!"_ustr,
getXWeak() ); continue;
}
if ( aNewTitle.isEmpty() )
{
aRetRange[ n ] <<= lang::IllegalArgumentException
( u"Empty title not allowed!"_ustr,
getXWeak(), -1 ); continue;
}
setCmisProperty( "cmis:name", OUSTR_TO_STDSTR( aNewTitle ), xEnv );
bChanged = true;
} else
{
SAL_INFO( "ucb.ucp.cmis", "Couldn't set property: " << rValue.Name );
lang::IllegalAccessException e ( u"Property is read-only!"_ustr,
getXWeak() );
aRetRange[ n ] <<= e;
}
}
try
{
--> --------------------
--> maximum size reached
--> --------------------
Messung V0.5
¤ Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.0.33Bemerkung:
(vorverarbeitet)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.