diff --git a/.gitignore b/.gitignore index b923ebb..026e8fd 100644 --- a/.gitignore +++ b/.gitignore @@ -47,3 +47,9 @@ /libreoffice-help-7.6.2.1.tar.xz.asc /libreoffice-translations-7.6.2.1.tar.xz /libreoffice-translations-7.6.2.1.tar.xz.asc +/libreoffice-7.6.3.1.tar.xz +/libreoffice-7.6.3.1.tar.xz.asc +/libreoffice-help-7.6.3.1.tar.xz +/libreoffice-help-7.6.3.1.tar.xz.asc +/libreoffice-translations-7.6.3.1.tar.xz +/libreoffice-translations-7.6.3.1.tar.xz.asc diff --git a/717d27a.patch b/717d27a.patch deleted file mode 100644 index 6a2b117..0000000 --- a/717d27a.patch +++ /dev/null @@ -1,1569 +0,0 @@ -From 717d27ae7bc42016aa3110dcbaf7c4c223bfe6c8 Mon Sep 17 00:00:00 2001 -From: Caolán McNamara -Date: Mon, 09 Oct 2023 13:46:38 +0100 -Subject: [PATCH] upgrade to libcmis 0.6.0 - -Change-Id: Ia22d2efca14b1f55f45a4ecb9c487591c3117e17 -Reviewed-on: https://gerrit.libreoffice.org/c/core/+/157704 -Tested-by: Caolán McNamara -Reviewed-by: Caolán McNamara -(cherry picked from commit 4e1a5d693e408db170ccf4c84b49017056f289fa) ---- - -diff --git a/configure.ac b/configure.ac -index 90cf027..5942829 100644 ---- a/configure.ac -+++ b/configure.ac -@@ -7537,7 +7537,7 @@ - dnl =================================================================== - dnl Check for system libcmis - dnl =================================================================== --libo_CHECK_SYSTEM_MODULE([libcmis],[LIBCMIS],[libcmis-0.5 >= 0.5.2],enabled) -+libo_CHECK_SYSTEM_MODULE([libcmis],[LIBCMIS],[libcmis-0.6 >= 0.6.0],enabled) - - dnl =================================================================== - dnl C++11 -@@ -7563,28 +7563,12 @@ - fi - save_CXXFLAGS=$CXXFLAGS - CXXFLAGS="$CXXFLAGS $flag -Werror" -- if test "$SYSTEM_LIBCMIS" = TRUE; then -- CXXFLAGS="$CXXFLAGS -DSYSTEM_LIBCMIS $LIBCMIS_CFLAGS" -- fi - AC_LANG_PUSH([C++]) - AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ - #include - #include - #include - -- #if defined SYSTEM_LIBCMIS -- // See ucb/source/ucp/cmis/auth_provider.hxx: -- #if !defined __clang__ -- #pragma GCC diagnostic push -- #pragma GCC diagnostic ignored "-Wdeprecated" -- #pragma GCC diagnostic ignored "-Wunused-but-set-parameter" -- #endif -- #include -- #if !defined __clang__ -- #pragma GCC diagnostic pop -- #endif -- #endif -- - void f(std::vector & v, std::function fn) { - std::sort(v.begin(), v.end(), fn); - } -diff --git a/download.lst b/download.lst -index 823d535..ec214cb 100644 ---- a/download.lst -+++ b/download.lst -@@ -60,8 +60,8 @@ - # three static lines - # so that git cherry-pick - # will not run into conflicts --LIBCMIS_SHA256SUM := d7b18d9602190e10d437f8a964a32e983afd57e2db316a07d87477a79f5000a2 --LIBCMIS_TARBALL := libcmis-0.5.2.tar.xz -+LIBCMIS_SHA256SUM := 56df575f78dacc21b4cec7cec73d671fd235f7c2010a8bb7940ef1413dc899fd -+LIBCMIS_TARBALL := libcmis-0.6.0.tar.xz - # three static lines - # so that git cherry-pick - # will not run into conflicts -diff --git a/external/libcmis/0001-rename-class-GetObject-to-avoid-name-clash-on-Window.patch b/external/libcmis/0001-rename-class-GetObject-to-avoid-name-clash-on-Window.patch -deleted file mode 100644 -index f82c82f..0000000 ---- a/external/libcmis/0001-rename-class-GetObject-to-avoid-name-clash-on-Window.patch -+++ /dev/null -@@ -1,69 +0,0 @@ --From 219e6d6586c8280dfd9c4851cee0d14d68b6ad65 Mon Sep 17 00:00:00 2001 --From: David Tardon --Date: Fri, 28 Dec 2018 15:26:28 +0100 --Subject: [PATCH] rename class GetObject to avoid name clash on Windows -- ----- -- src/libcmis/ws-objectservice.cxx | 2 +- -- src/libcmis/ws-requests.cxx | 2 +- -- src/libcmis/ws-requests.hxx | 7 +++---- -- 3 files changed, 5 insertions(+), 6 deletions(-) -- --diff --git a/src/libcmis/ws-objectservice.cxx b/src/libcmis/ws-objectservice.cxx --index 9e40085..d57f3cc 100644 ----- a/src/libcmis/ws-objectservice.cxx --+++ b/src/libcmis/ws-objectservice.cxx --@@ -71,7 +71,7 @@ libcmis::ObjectPtr ObjectService::getObject( string repoId, string id ) -- { -- libcmis::ObjectPtr object; -- --- class GetObject request( repoId, id ); --+ GetObjectRequest request( repoId, id ); -- vector< SoapResponsePtr > responses = m_session->soapRequest( m_url, request ); -- if ( responses.size( ) == 1 ) -- { --diff --git a/src/libcmis/ws-requests.cxx b/src/libcmis/ws-requests.cxx --index f8bc245..408d053 100644 ----- a/src/libcmis/ws-requests.cxx --+++ b/src/libcmis/ws-requests.cxx --@@ -269,7 +269,7 @@ SoapResponsePtr GetTypeChildrenResponse::create( xmlNodePtr node, RelatedMultipa -- return SoapResponsePtr( response ); -- } -- ---void GetObject::toXml( xmlTextWriterPtr writer ) --+void GetObjectRequest::toXml( xmlTextWriterPtr writer ) -- { -- xmlTextWriterStartElement( writer, BAD_CAST( "cmism:getObject" ) ); -- xmlTextWriterWriteAttribute( writer, BAD_CAST( "xmlns:cmis" ), BAD_CAST( NS_CMIS_URL ) ); --diff --git a/src/libcmis/ws-requests.hxx b/src/libcmis/ws-requests.hxx --index 2c4ae92..534d9a4 100644 ----- a/src/libcmis/ws-requests.hxx --+++ b/src/libcmis/ws-requests.hxx --@@ -203,21 +203,20 @@ class GetTypeChildrenResponse : public SoapResponse -- std::vector< libcmis::ObjectTypePtr > getChildren( ) { return m_children; } -- }; -- ---#undef GetObject ---class GetObject : public SoapRequest --+class GetObjectRequest : public SoapRequest -- { -- private: -- std::string m_repositoryId; -- std::string m_id; -- -- public: --- GetObject( std::string repoId, std::string id ) : --+ GetObjectRequest( std::string repoId, std::string id ) : -- m_repositoryId( repoId ), -- m_id( id ) -- { -- } -- --- ~GetObject( ) { } --+ ~GetObjectRequest( ) { } -- -- void toXml( xmlTextWriterPtr writer ); -- }; ---- --2.19.2 -- -diff --git a/external/libcmis/UnpackedTarball_libcmis.mk b/external/libcmis/UnpackedTarball_libcmis.mk -index f48201d..1c014d9 100644 ---- a/external/libcmis/UnpackedTarball_libcmis.mk -+++ b/external/libcmis/UnpackedTarball_libcmis.mk -@@ -13,13 +13,4 @@ - - $(eval $(call gb_UnpackedTarball_set_patchlevel,libcmis,1)) - --$(eval $(call gb_UnpackedTarball_add_patches,libcmis, \ -- external/libcmis/libcmis-libxml2_compatibility.patch \ -- external/libcmis/0001-rename-class-GetObject-to-avoid-name-clash-on-Window.patch \ -- external/libcmis/libcmis_onedrive.patch \ -- external/libcmis/libcmis_oauth_pw_as_refreshtoken.patch.1 \ -- external/libcmis/libcmis_gdrive.patch.1 \ -- external/libcmis/libcmis-boost-string.patch \ --)) -- - # vim: set noet sw=4 ts=4: -diff --git a/external/libcmis/libcmis-boost-string.patch b/external/libcmis/libcmis-boost-string.patch -deleted file mode 100644 -index 247d38a..0000000 ---- a/external/libcmis/libcmis-boost-string.patch -+++ /dev/null -@@ -1,11 +0,0 @@ ----- a/src/libcmis/oauth2-handler.cxx --+++ b/src/libcmis/oauth2-handler.cxx --@@ -26,6 +26,8 @@ -- * instead of those above. -- */ -- --+#include --+ -- #include "oauth2-handler.hxx" -- -- #include -diff --git a/external/libcmis/libcmis-libxml2_compatibility.patch b/external/libcmis/libcmis-libxml2_compatibility.patch -deleted file mode 100644 -index 240b85b..0000000 ---- a/external/libcmis/libcmis-libxml2_compatibility.patch -+++ /dev/null -@@ -1,14 +0,0 @@ --# -*- Mode: Diff -*- ----- a/src/libcmis/oauth2-providers.cxx --+++ b/src/libcmis/oauth2-providers.cxx --@@ -34,6 +34,10 @@ -- -- using namespace std; -- --+#if LIBXML_VERSION < 20621 --+#define HTML_PARSE_RECOVER 0 --+#endif --+ -- namespace { -- -- // Encodes the given data according to the application/x-www-form-urlencoded format, see -diff --git a/external/libcmis/libcmis_gdrive.patch.1 b/external/libcmis/libcmis_gdrive.patch.1 -deleted file mode 100644 -index 24ff65d..0000000 ---- a/external/libcmis/libcmis_gdrive.patch.1 -+++ /dev/null -@@ -1,702 +0,0 @@ --diff -ur libcmis.org/src/libcmis/gdrive-document.cxx libcmis/src/libcmis/gdrive-document.cxx ----- libcmis.org/src/libcmis/gdrive-document.cxx 2021-07-27 19:11:02.679247008 +0200 --+++ libcmis/src/libcmis/gdrive-document.cxx 2021-07-27 19:11:18.873246420 +0200 --@@ -145,23 +145,17 @@ -- { -- if ( !os.get( ) ) -- throw libcmis::Exception( "Missing stream" ); --- if ( !isImmutable( ) ) --- throw libcmis::Exception( string ( "Document " + getId( )+ --- " is not editable" ) ); --- string putUrl = getUploadUrl( ) + getId( ); --- putUrl += "?uploadType=media"; --- --- // If it's a Google document, convert it --- if ( isGoogleDoc( ) ) --- putUrl += "&convert=true"; --+ --+ string putUrl = GDRIVE_UPLOAD_LINK + getId( ) + "?uploadType=media"; -- -- // Upload stream -- boost::shared_ptr< istream> is ( new istream ( os->rdbuf( ) ) ); -- vector headers; -- headers.push_back( string( "Content-Type: " ) + contentType ); --+ string res; -- try -- { --- getSession()->httpPutRequest( putUrl, *is, headers ); --+ res = getSession()->httpPatchRequest( putUrl, *is, headers )->getStream()->str(); -- } -- catch ( const CurlException& e ) -- { --@@ -181,35 +175,10 @@ -- { -- if ( !os.get( ) ) -- throw libcmis::Exception( "Missing stream" ); --- --- if ( !isImmutable( ) ) --- throw libcmis::Exception( string ( "Document " + getId( )+ --- " is not editable" ) ); --- string metaUrl = getUrl( ); --- --- // If it's a Google document, convert it --- if ( isGoogleDoc( ) ) --- metaUrl += "?convert=true"; --- --- // Update file name meta information --- if ( !fileName.empty( ) && fileName != getContentFilename( ) ) --- { --- Json metaJson; --- Json fileJson( fileName.c_str( ) ); --- metaJson.add("title", fileJson ); --- --- std::istringstream is( metaJson.toString( ) ); --- vector headers; --- headers.push_back( "Content-Type: application/json" ); --- try --- { --- getSession()->httpPutRequest( metaUrl, is, headers ); --- } --- catch ( const CurlException& e ) --- { --- throw e.getCmisException( ); --- } --- } --+ --+ // TODO: when would the filename need an update? --+ if (!fileName.empty() && fileName != getContentFilename()) --+ std::cout << "filename change is not implemented in setContentStream" << std::endl; -- -- // Upload stream -- uploadStream( os, contentType ); --@@ -251,7 +220,7 @@ -- vector< libcmis::DocumentPtr > GDriveDocument::getAllVersions( ) -- { -- vector< libcmis::DocumentPtr > revisions; --- string versionUrl = getUrl( ) + "/revisions"; --+ string versionUrl = GDRIVE_METADATA_LINK + getId( ) + "/revisions"; -- // Run the http request to get the properties definition -- string res; -- try --@@ -263,7 +232,7 @@ -- throw e.getCmisException( ); -- } -- Json jsonRes = Json::parse( res ); --- Json::JsonVector objs = jsonRes["items"].getList( ); --+ Json::JsonVector objs = jsonRes["revisions"].getList( ); -- -- string parentId = getStringProperty( "cmis:parentId" ); -- --diff -ur libcmis.org/src/libcmis/gdrive-folder.cxx libcmis/src/libcmis/gdrive-folder.cxx ----- libcmis.org/src/libcmis/gdrive-folder.cxx 2021-07-27 19:11:02.678247008 +0200 --+++ libcmis/src/libcmis/gdrive-folder.cxx 2021-07-27 19:11:18.874246420 +0200 --@@ -62,8 +62,8 @@ -- // Instead of sending multiple queries for children, -- // we send a single query to search for objects where parents -- // include the folderID. --- string query = getSession( )->getBindingUrl( ) + --- "/files?q=\"" + getId( ) + "\"+in+parents+and+trashed+=+false"; --+ string query = GDRIVE_METADATA_LINK + "?q=\"" + getId( ) + "\"+in+parents+and+trashed+=+false" + --+ "&fields=files(kind,id,name,parents,mimeType,createdTime,modifiedTime,thumbnailLink,size)"; -- -- string res; -- try --@@ -76,7 +76,7 @@ -- } -- -- Json jsonRes = Json::parse( res ); --- Json::JsonVector objs = jsonRes["items"].getList( ); --+ Json::JsonVector objs = jsonRes["files"].getList( ); -- -- // Create children objects from Json objects -- for(unsigned int i = 0; i < objs.size(); i++) --@@ -95,7 +95,7 @@ -- string GDriveFolder::uploadProperties( Json properties ) -- { -- // URL for uploading meta data --- string metaUrl = getSession()->getBindingUrl() + "/files/"; --+ string metaUrl = GDRIVE_METADATA_LINK + "?fields=kind,id,name,parents,mimeType,createdTime,modifiedTime"; -- -- // add parents to the properties -- properties.add( "parents", GdriveUtils::createJsonFromParentId( getId( ) ) ); --@@ -147,9 +147,15 @@ -- -- Json propsJson = GdriveUtils::toGdriveJson( properties ); -- --- // Add filename to properties --- Json jsonFilename( fileName.c_str( ) ); --- propsJson.add( "title", jsonFilename ); --+ if(!fileName.empty()) { --+ // use provided filename --+ Json jsonFilename( fileName.c_str( ) ); --+ --+ propsJson.add( "name", jsonFilename ); --+ } --+ if(!contentType.empty()) { --+ propsJson.add( "mimeType", Json(contentType.c_str())); --+ } -- -- // Upload meta-datas -- string res = uploadProperties( propsJson); --@@ -171,12 +177,9 @@ -- libcmis::UnfileObjects::Type /*unfile*/, -- bool /*continueOnError*/ ) -- { --- // Object remove doesn't work with folder --- // Using trash instead -- try -- { --- istringstream is( "" ); --- getSession( )->httpPostRequest( getUrl( ) + "/trash", is, "" ); --+ getSession( )->httpDeleteRequest( GDRIVE_METADATA_LINK + getId( ) ); -- } -- catch ( const CurlException& e ) -- { --diff -ur libcmis.org/src/libcmis/gdrive-object.cxx libcmis/src/libcmis/gdrive-object.cxx ----- libcmis.org/src/libcmis/gdrive-object.cxx 2021-07-27 19:11:02.675247009 +0200 --+++ libcmis/src/libcmis/gdrive-object.cxx 2021-07-27 19:11:18.874246420 +0200 --@@ -89,8 +89,8 @@ -- property.reset( new GDriveProperty( it->first, it->second ) ); -- m_properties[ property->getPropertyType( )->getId()] = property; -- --- // we map "title" to both "cmis:name" and "cmis:getContentStreamFileName" --- if ( it->first == "title" ) --+ // we map "name" to both "cmis:name" and "cmis:getContentStreamFileName" --+ if ( it->first == "name" ) -- { -- property.reset( new GDriveProperty( "cmis:name", it->second) ); -- m_properties[ property->getPropertyType( )->getId()] = property; --@@ -142,16 +142,13 @@ -- { -- if ( m_renditions.empty( ) ) -- { --- string downloadUrl = getStringProperty( "downloadUrl" ); --- if ( !downloadUrl.empty( ) ) --- { --- string mimeType = getStringProperty( "cmis:contentStreamMimeType" ); --- if ( !mimeType.empty( ) ) --- { --- RenditionPtr rendition( --- new Rendition( mimeType, mimeType, mimeType, downloadUrl )); --- m_renditions.push_back( rendition ); --- } --+ string downloadUrl = GDRIVE_METADATA_LINK + getId( ) + "?alt=media"; --+ string mimeType = getStringProperty( "cmis:contentStreamMimeType" ); --+ if ( !mimeType.empty( ) ) --+ { --+ RenditionPtr rendition( --+ new Rendition( mimeType, mimeType, mimeType, downloadUrl )); --+ m_renditions.push_back( rendition ); -- } -- -- vector< string > exportLinks = getMultiStringProperty( "exportLinks" ); --@@ -192,7 +189,7 @@ -- { -- vector< string > headers; -- headers.push_back( "Content-Type: application/json" ); --- response = getSession( )->httpPutRequest( getUrl( ), is, headers ); --+ response = getSession( )->httpPatchRequest( getUrl( ), is, headers ); -- } -- catch ( const CurlException& e ) -- { --@@ -228,7 +225,7 @@ -- { -- try -- { --- getSession( )->httpDeleteRequest( getUrl( ) ); --+ getSession( )->httpDeleteRequest( GDRIVE_METADATA_LINK + getId( ) ); -- } -- catch ( const CurlException& e ) -- { --@@ -239,8 +236,8 @@ -- void GDriveObject::move( FolderPtr /*source*/, FolderPtr destination ) -- { -- Json parentsJson; --- Json parentsValue = GdriveUtils::createJsonFromParentId( destination->getId( ) ); --- parentsJson.add( "parents", parentsValue ); --+ parentsJson.add( "addParents", Json(destination->getId( ).c_str()) ); --+ parentsJson.add( "removeParents", Json(getStringProperty( "cmis:parentId" ).c_str()) ); -- -- istringstream is( parentsJson.toString( ) ); -- libcmis::HttpResponsePtr response; --@@ -248,7 +245,7 @@ -- { -- vector< string > headers; -- headers.push_back( "Content-Type: application/json" ); --- response = getSession( )->httpPutRequest( getUrl( ), is, headers ); --+ response = getSession( )->httpPatchRequest( getUrl( ), is, headers ); -- } -- catch ( const CurlException& e ) -- { --@@ -262,12 +259,10 @@ -- -- string GDriveObject::getUrl( ) -- { --- return getSession( )->getBindingUrl( ) + "/files/" + getId( ); ---} --- ---string GDriveObject::getUploadUrl( ) ---{ --- return GDRIVE_UPLOAD_LINKS; --+ // thumbnailLink causes some operations to fail with internal server error, --+ // see https://issuetracker.google.com/issues/36760667 --+ return GDRIVE_METADATA_LINK + getId( ) + --+ "?fields=kind,id,name,parents,mimeType,createdTime,modifiedTime,size"; -- } -- -- vector< string> GDriveObject::getMultiStringProperty( const string& propertyName ) --diff -ur libcmis.org/src/libcmis/gdrive-repository.cxx libcmis/src/libcmis/gdrive-repository.cxx ----- libcmis.org/src/libcmis/gdrive-repository.cxx 2021-07-27 19:11:02.676247009 +0200 --+++ libcmis/src/libcmis/gdrive-repository.cxx 2021-07-27 19:11:18.874246420 +0200 --@@ -35,7 +35,7 @@ -- m_name = "Google Drive"; -- m_description = "Google Drive repository"; -- m_productName = "Google Drive"; --- m_productVersion = "v2"; --+ m_productVersion = "v3"; -- m_rootId = "root"; -- -- m_capabilities[ ACL ] = "discover"; --diff -ur libcmis.org/src/libcmis/gdrive-session.cxx libcmis/src/libcmis/gdrive-session.cxx ----- libcmis.org/src/libcmis/gdrive-session.cxx 2021-07-27 19:11:02.675247009 +0200 --+++ libcmis/src/libcmis/gdrive-session.cxx 2021-07-27 19:11:18.874246420 +0200 --@@ -124,9 +124,13 @@ -- -- libcmis::ObjectPtr GDriveSession::getObject( string objectId ) -- { --+ if(objectId == "root") { --+ return getRootFolder(); --+ } -- // Run the http request to get the properties definition -- string res; --- string objectLink = m_bindingUrl + "/files/" + objectId; --+ string objectLink = GDRIVE_METADATA_LINK + objectId + --+ "?fields=kind,id,name,parents,mimeType,createdTime,modifiedTime,thumbnailLink,size"; -- try -- { -- res = httpGetRequest( objectLink )->getStream()->str(); --@@ -188,9 +192,10 @@ -- { -- // Normal child case -- // Ask for the ID of the child if there is any --- string childIdUrl = m_bindingUrl + "/files/" + objectId + --- "/children/?q=title+=+'" + segment + --- "'&fields=items:id"; --+ // somewhat flawed as names are not necessarily unique in GDrive... --+ string query = libcmis::escape("'" + objectId + "' in parents and trashed = false and name='" + segment + "'"); --+ --+ string childIdUrl = m_bindingUrl + "/files/?q=" + query + "&fields=files(id)"; -- -- string res; -- try --@@ -204,7 +209,7 @@ -- Json jsonRes = Json::parse( res ); -- -- // Did we get an id? --- Json::JsonVector items = jsonRes["items"].getList(); --+ Json::JsonVector items = jsonRes["files"].getList(); -- if ( items.empty( ) ) -- throw libcmis::Exception( "Object not found: " + path, "objectNotFound" ); -- --@@ -219,6 +224,27 @@ -- return getObject( objectId ); -- } -- --+libcmis::FolderPtr GDriveSession::getRootFolder() --+{ --+ // permissions/scope with just drive.file don't allow to get it with the "root" alias/by its actual object-ID --+ Json propsJson; --+ --+ // GDrive folder is a file with a different mime type. --+ string mimeType = GDRIVE_FOLDER_MIME_TYPE; --+ --+ // Add mimetype to the propsJson --+ Json jsonMimeType( mimeType.c_str( ) ); --+ propsJson.add( "mimeType", jsonMimeType ); --+ propsJson.add( "id", "root" ); --+ --+ // Upload meta-datas --+ propsJson.add("cmis:name", "VirtualRoot"); --+ --+ libcmis::FolderPtr folderPtr( new GDriveFolder( this, propsJson ) ); --+ --+ return folderPtr; --+} --+ -- libcmis::ObjectTypePtr GDriveSession::getType( string id ) -- { -- libcmis::ObjectTypePtr type( new GdriveObjectType( id ) ); --diff -ur libcmis.org/src/libcmis/gdrive-session.hxx libcmis/src/libcmis/gdrive-session.hxx ----- libcmis.org/src/libcmis/gdrive-session.hxx 2021-07-27 19:11:02.675247009 +0200 --+++ libcmis/src/libcmis/gdrive-session.hxx 2021-07-27 19:11:18.875246420 +0200 --@@ -57,6 +57,8 @@ -- -- virtual std::vector< libcmis::ObjectTypePtr > getBaseTypes( ); -- --+ virtual libcmis::FolderPtr getRootFolder(); --+ -- virtual std::string getRefreshToken(); -- -- private: --diff -ur libcmis.org/src/libcmis/gdrive-utils.cxx libcmis/src/libcmis/gdrive-utils.cxx ----- libcmis.org/src/libcmis/gdrive-utils.cxx 2021-07-27 19:11:02.677247008 +0200 --+++ libcmis/src/libcmis/gdrive-utils.cxx 2021-07-27 19:11:18.875246420 +0200 --@@ -44,17 +44,17 @@ -- convertedKey = "cmis:createdBy"; -- else if ( key == "description" ) -- convertedKey = "cmis:description"; --- else if ( key == "createdDate" ) --+ else if ( key == "createdTime" ) -- convertedKey = "cmis:creationDate"; -- else if ( key == "lastModifyingUserName" ) -- convertedKey = "cmis:lastModifiedBy"; --- else if ( key == "modifiedDate" ) --+ else if ( key == "modifiedTime" ) -- convertedKey = "cmis:lastModificationDate"; --- else if ( key == "title" ) --+ else if ( key == "name" ) -- convertedKey = "cmis:contentStreamFileName"; -- else if ( key == "mimeType" ) -- convertedKey = "cmis:contentStreamMimeType"; --- else if ( key == "fileSize" ) --+ else if ( key == "size" ) -- convertedKey = "cmis:contentStreamLength"; -- else if ( key == "editable" ) -- convertedKey = "cmis:isImmutable"; --@@ -72,21 +72,21 @@ -- else if ( key == "cmis:createdBy" ) -- convertedKey = "ownerNames"; -- else if ( key == "cmis:creationDate" ) --- convertedKey = "createdDate"; --+ convertedKey = "createdTime"; -- else if ( key == "cmis:description" ) -- convertedKey = "description"; -- else if ( key == "cmis:lastModifiedBy" ) -- convertedKey = "lastModifyingUserName"; -- else if ( key == "cmis:lastModificationDate" ) --- convertedKey = "modifiedDate"; --+ convertedKey = "modifiedTime"; -- else if ( key == "cmis:contentStreamFileName" ) --- convertedKey = "title"; --+ convertedKey = "name"; -- else if ( key == "cmis:name" ) --- convertedKey = "title"; --+ convertedKey = "name"; -- else if ( key == "cmis:contentStreamMimeType" ) -- convertedKey = "mimeType"; -- else if ( key == "cmis:contentStreamLength" ) --- convertedKey = "fileSize"; --+ convertedKey = "size"; -- else if ( key == "cmis:isImmutable" ) -- convertedKey = "editable"; -- else if ( key == "cmis:parentId" ) --@@ -124,9 +124,9 @@ -- bool GdriveUtils::checkUpdatable( const string& key ) -- { -- // taken from https://developers.google.com/drive/v2/reference/files --- bool updatable = ( key == "title" || --+ bool updatable = ( key == "name" || -- key == "description" || --- key == "modifiedDate" || --+ key == "modifiedTime" || -- key == "lastViewedByMeDate" ); -- return updatable; -- } --@@ -143,18 +143,11 @@ -- -- Json GdriveUtils::createJsonFromParentId( const string& parentId ) -- { --- Json parentValue( parentId.c_str( ) ); --- -- // parents is a Json array -- Json firstParent; --- firstParent.add( "id", parentValue ); --- --- Json::JsonVector parents; --- parents.insert( parents.begin( ), firstParent ); --+ firstParent.add( Json( parentId.c_str() ) ); -- --- Json parentsValue( parents ); --- --- return parentsValue; --+ return firstParent; -- } -- -- vector< string > GdriveUtils::parseGdriveProperty( string key, Json json ) --diff -ur libcmis.org/src/libcmis/gdrive-utils.hxx libcmis/src/libcmis/gdrive-utils.hxx ----- libcmis.org/src/libcmis/gdrive-utils.hxx 2021-07-27 19:11:02.677247008 +0200 --+++ libcmis/src/libcmis/gdrive-utils.hxx 2021-07-27 19:11:18.875246420 +0200 --@@ -35,7 +35,8 @@ -- #include "json-utils.hxx" -- -- static const std::string GDRIVE_FOLDER_MIME_TYPE = "application/vnd.google-apps.folder" ; ---static const std::string GDRIVE_UPLOAD_LINKS = "https://www.googleapis.com/upload/drive/v2/files/"; --+static const std::string GDRIVE_UPLOAD_LINK = "https://www.googleapis.com/upload/drive/v3/files/"; --+static const std::string GDRIVE_METADATA_LINK = "https://www.googleapis.com/drive/v3/files/"; -- -- class GdriveUtils -- { --diff -ur libcmis.org/src/libcmis/oauth2-handler.cxx libcmis/src/libcmis/oauth2-handler.cxx ----- libcmis.org/src/libcmis/oauth2-handler.cxx 2021-07-27 19:11:02.676247009 +0200 --+++ libcmis/src/libcmis/oauth2-handler.cxx 2021-07-27 19:11:18.875246420 +0200 --@@ -92,8 +92,11 @@ -- "code=" + authCode + -- "&client_id=" + m_data->getClientId() + -- "&redirect_uri=" + m_data->getRedirectUri() + --- "&scope=" + libcmis::escape( m_data->getScope() ) + -- "&grant_type=authorization_code" ; --+ if(boost::starts_with(m_data->getTokenUrl(), "https://oauth2.googleapis.com/")) --+ post += "&client_secret=" + m_data->getClientSecret(); --+ else --+ post += "&scope=" + libcmis::escape( m_data->getScope() ); -- -- istringstream is( post ); -- --@@ -104,7 +107,7 @@ -- resp = m_session->httpPostRequest ( m_data->getTokenUrl(), is, -- "application/x-www-form-urlencoded" ); -- } --- catch ( const CurlException& ) --+ catch ( const CurlException& e) -- { -- throw libcmis::Exception( -- "Couldn't get tokens from the authorization code "); --@@ -122,6 +125,8 @@ -- "refresh_token=" + m_refresh + -- "&client_id=" + m_data->getClientId() + -- "&grant_type=refresh_token" ; --+ if(boost::starts_with(m_data->getTokenUrl(), "https://oauth2.googleapis.com/")) --+ post += "&client_secret=" + m_data->getClientSecret(); -- -- istringstream is( post ); -- libcmis::HttpResponsePtr resp; --@@ -130,7 +135,7 @@ -- resp = m_session->httpPostRequest( m_data->getTokenUrl( ), is, -- "application/x-www-form-urlencoded" ); -- } --- catch (const CurlException& ) --+ catch (const CurlException& e ) -- { -- throw libcmis::Exception( "Couldn't refresh token "); -- } --diff -ur libcmis.org/src/libcmis/oauth2-providers.cxx libcmis/src/libcmis/oauth2-providers.cxx ----- libcmis.org/src/libcmis/oauth2-providers.cxx 2021-07-27 19:11:02.679247008 +0200 --+++ libcmis/src/libcmis/oauth2-providers.cxx 2021-07-27 19:11:18.886246420 +0200 --@@ -80,172 +80,8 @@ -- -- } -- ---string OAuth2Providers::OAuth2Gdrive( HttpSession* session, const string& authUrl, --- const string& username, const string& password ) ---{ --- /* This member function implements 'Google OAuth 2.0' --- * --- * The interaction is carried out by libcmis, with no web browser involved. --- * --- * Normal sequence (without 2FA) is: --- * 1) a get to activate login page --- * receive first login page, html format --- * 2) subsequent post to sent email --- * receive html page for password input --- * 3) subsequent post to send password --- * receive html page for application consent --- * 4) subsequent post to send a consent for the application --- * receive a single-use authorization code --- * this code is returned as a string --- * --- * Sequence with 2FA is: --- * 1) a get to activate login page --- * receive first login page, html format --- * 2) subsequent post to sent email --- * receive html page for password input --- * 3) subsequent post to send password --- * receive html page for pin input --- * 3b) subsequent post to send pin number --- * receive html page for application consent --- * 4) subsequent post to send a consent for the application --- * receive a single-use authorization code --- * this code is returned as a string --- */ --- --- static const string CONTENT_TYPE( "application/x-www-form-urlencoded" ); --- // STEP 1: get login page --- string res; --- try --- { --- // send the first get, receive the html login page --- res = session->httpGetRequest( authUrl )->getStream( )->str( ); --- } --- catch ( const CurlException& ) --- { --- return string( ); --- } --- --- // STEP 2: send email --- --- string loginEmailPost, loginEmailLink; --- if ( !parseResponse( res.c_str( ), loginEmailPost, loginEmailLink ) ) --- return string( ); --- --- loginEmailPost += "Email="; --- loginEmailPost += escapeForm( username ); --- --- istringstream loginEmailIs( loginEmailPost ); --- string loginEmailRes; --- try --- { --- // send a post with user email, receive the html page for password input --- loginEmailRes = session->httpPostRequest ( loginEmailLink, loginEmailIs, CONTENT_TYPE ) --- ->getStream( )->str( ); --- } --- catch ( const CurlException& ) --- { --- return string( ); --- } --- --- // STEP 3: password page --- --- string loginPasswdPost, loginPasswdLink; --- if ( !parseResponse( loginEmailRes.c_str( ), loginPasswdPost, loginPasswdLink ) ) --- return string( ); --- --- loginPasswdPost += "Passwd="; --- loginPasswdPost += escapeForm( password ); --- --- istringstream loginPasswdIs( loginPasswdPost ); --- string loginPasswdRes; --- try --- { --- // send a post with user password, receive the application consent page --- loginPasswdRes = session->httpPostRequest ( loginPasswdLink, loginPasswdIs, CONTENT_TYPE ) --- ->getStream( )->str( ); --- } --- catch ( const CurlException& ) --- { --- return string( ); --- } --- --- string approvalPost, approvalLink; --- if ( !parseResponse( loginPasswdRes. c_str( ), approvalPost, approvalLink) ) --- return string( ); --- --- // when 2FA is enabled, link doesn't start with 'http' --- if ( approvalLink.compare(0, 4, "http") != 0 ) --- { --- // STEP 3b: 2 Factor Authentication, pin code request --- --- string loginChallengePost( approvalPost ); --- string loginChallengeLink( approvalLink ); --- --- libcmis::OAuth2AuthCodeProvider fallbackProvider = libcmis::SessionFactory::getOAuth2AuthCodeProvider( ); --- unique_ptr< char, void (*)( void * ) > pin{ fallbackProvider( "", "", "" ), free }; --- --- if( !pin ) --- { --- // unset OAuth2AuthCode Provider to avoid showing pin request again in the HttpSession::oauth2Authenticate --- libcmis::SessionFactory::setOAuth2AuthCodeProvider( NULL ); --- return string( ); --- } --- --- loginChallengeLink = "https://accounts.google.com" + loginChallengeLink; --- loginChallengePost += string( PIN_INPUT_NAME ) + "="; --- loginChallengePost += string( pin.get() ); --- --- istringstream loginChallengeIs( loginChallengePost ); --- string loginChallengeRes; --- try --- { --- // send a post with pin, receive the application consent page --- loginChallengeRes = session->httpPostRequest ( loginChallengeLink, loginChallengeIs, CONTENT_TYPE ) --- ->getStream( )->str( ); --- } --- catch ( const CurlException& ) --- { --- return string( ); --- } --- --- approvalPost = string(); --- approvalLink = string(); --- --- if ( !parseResponse( loginChallengeRes. c_str( ), approvalPost, approvalLink) ) --- return string( ); --- } --- else if( approvalLink.compare( "https://accounts.google.com/ServiceLoginAuth" ) == 0 ) --- { --- // wrong password, --- // unset OAuth2AuthCode Provider to avoid showing pin request again in the HttpSession::oauth2Authenticate --- libcmis::SessionFactory::setOAuth2AuthCodeProvider( NULL ); --- return string( ); --- } --- --- // STEP 4: allow libcmis to access google drive --- approvalPost += "submit_access=true"; --- --- istringstream approvalIs( approvalPost ); --- string approvalRes; --- try --- { --- // send a post with application consent --- approvalRes = session->httpPostRequest ( approvalLink, approvalIs, --- CONTENT_TYPE) ->getStream( )->str( ); --- } --- catch ( const CurlException& e ) --- { --- throw e.getCmisException( ); --- } --- --- // Take the authentication code from the text bar --- string code = parseCode( approvalRes.c_str( ) ); --- --- return code; ---} --- ---string OAuth2Providers::OAuth2Onedrive( HttpSession* /*session*/, const string& /*authUrl*/, --- const string& /*username*/, const string& /*password*/ ) --+string OAuth2Providers::OAuth2Dummy( HttpSession* /*session*/, const string& /*authUrl*/, --+ const string& /*username*/, const string& /*password*/ ) -- { -- return string( ); -- } --@@ -314,12 +150,8 @@ -- // For Alfresco in the cloud, only match the hostname as there can be several -- // binding URLs created with it. -- return OAuth2Alfresco; --- else if ( boost::starts_with( url, "https://www.googleapis.com/drive/v2" ) ) --- return OAuth2Gdrive; --- else if ( boost::starts_with( url, "https://graph.microsoft.com/v1.0" ) ) --- return OAuth2Onedrive; -- --- return OAuth2Gdrive; --+ return OAuth2Dummy; -- } -- -- int OAuth2Providers::parseResponse ( const char* response, string& post, string& link ) --diff -ur libcmis.org/src/libcmis/oauth2-providers.hxx libcmis/src/libcmis/oauth2-providers.hxx ----- libcmis.org/src/libcmis/oauth2-providers.hxx 2021-07-27 19:11:02.678247008 +0200 --+++ libcmis/src/libcmis/oauth2-providers.hxx 2021-07-27 19:11:18.886246420 +0200 --@@ -39,12 +39,8 @@ -- class OAuth2Providers -- { -- public : --- static std::string OAuth2Gdrive( HttpSession* session, const std::string& authUrl, --+ static std::string OAuth2Dummy( HttpSession* session, const std::string& authUrl, -- const std::string& username, const std::string& password ); --- --- static std::string OAuth2Onedrive( HttpSession* session, const std::string& authUrl, --- const std::string& username, const std::string& password ); --- -- static std::string OAuth2Alfresco( HttpSession* session, const std::string& authUrl, -- const std::string& username, const std::string& password ); -- --diff -ur libcmis.org/src/libcmis/session-factory.cxx libcmis/src/libcmis/session-factory.cxx ----- libcmis.org/src/libcmis/session-factory.cxx 2021-07-27 19:11:02.679247008 +0200 --+++ libcmis/src/libcmis/session-factory.cxx 2021-07-27 19:11:18.886246420 +0200 --@@ -66,7 +66,7 @@ -- if ( !bindingUrl.empty( ) ) -- { -- // Try the special cases based on the binding URL --- if ( bindingUrl == "https://www.googleapis.com/drive/v2" ) --+ if ( bindingUrl == "https://www.googleapis.com/drive/v3" ) -- { -- session = new GDriveSession( bindingUrl, username, password, -- oauth2, verbose ); -diff --git a/external/libcmis/libcmis_oauth_pw_as_refreshtoken.patch.1 b/external/libcmis/libcmis_oauth_pw_as_refreshtoken.patch.1 -deleted file mode 100644 -index a8cb065..0000000 ---- a/external/libcmis/libcmis_oauth_pw_as_refreshtoken.patch.1 -+++ /dev/null -@@ -1,185 +0,0 @@ --diff -ur libcmis.org/inc/libcmis/session.hxx libcmis/inc/libcmis/session.hxx ----- libcmis.org/inc/libcmis/session.hxx 2021-07-27 19:09:42.580249917 +0200 --+++ libcmis/inc/libcmis/session.hxx 2021-07-27 19:10:02.368249199 +0200 --@@ -95,6 +95,8 @@ -- certificate exception feature available on common web browser. -- */ -- virtual void setNoSSLCertificateCheck( bool noCheck ) = 0; --+ --+ virtual std::string getRefreshToken() { return ""; }; -- }; -- } -- --diff -ur libcmis.org/src/libcmis/gdrive-session.cxx libcmis/src/libcmis/gdrive-session.cxx ----- libcmis.org/src/libcmis/gdrive-session.cxx 2021-07-27 19:09:42.581249917 +0200 --+++ libcmis/src/libcmis/gdrive-session.cxx 2021-07-27 19:10:02.369249198 +0200 --@@ -70,6 +70,46 @@ -- { -- } -- --+ --+void GDriveSession::setOAuth2Data( libcmis::OAuth2DataPtr oauth2 ) --+{ --+ m_oauth2Handler = new OAuth2Handler( this, oauth2 ); --+ m_oauth2Handler->setOAuth2Parser( OAuth2Providers::getOAuth2Parser( getBindingUrl( ) ) ); --+ --+ oauth2Authenticate( ); --+} --+ --+void GDriveSession::oauth2Authenticate() --+{ --+ // treat the supplied password as refresh token --+ if (!m_password.empty()) --+ { --+ try --+ { --+ m_inOAuth2Authentication = true; --+ --+ m_oauth2Handler->setRefreshToken(m_password); --+ // Try to get new access tokens using the stored refreshtoken --+ m_oauth2Handler->refresh(); --+ m_inOAuth2Authentication = false; --+ } --+ catch (const CurlException &e) --+ { --+ m_inOAuth2Authentication = false; --+ // refresh token expired or invalid, trigger initial auth (that in turn will hit the fallback with copy'n'paste method) --+ BaseSession::oauth2Authenticate(); --+ } --+ } --+ else --+ { --+ BaseSession::oauth2Authenticate(); --+ } --+} --+ --+string GDriveSession::getRefreshToken() { --+ return HttpSession::getRefreshToken(); --+} --+ -- libcmis::RepositoryPtr GDriveSession::getRepository( ) -- { -- // Return a dummy repository since GDrive doesn't have that notion --diff -ur libcmis.org/src/libcmis/gdrive-session.hxx libcmis/src/libcmis/gdrive-session.hxx ----- libcmis.org/src/libcmis/gdrive-session.hxx 2021-07-27 19:09:42.583249917 +0200 --+++ libcmis/src/libcmis/gdrive-session.hxx 2021-07-27 19:10:02.369249198 +0200 --@@ -57,8 +57,14 @@ -- -- virtual std::vector< libcmis::ObjectTypePtr > getBaseTypes( ); -- --+ virtual std::string getRefreshToken(); --+ -- private: -- GDriveSession( ); --+ --+ virtual void setOAuth2Data( libcmis::OAuth2DataPtr oauth2 ); --+ --+ void oauth2Authenticate( ); -- }; -- -- #endif /* _GDRIVE_SESSION_HXX_ */ --diff -ur libcmis.org/src/libcmis/http-session.hxx libcmis/src/libcmis/http-session.hxx ----- libcmis.org/src/libcmis/http-session.hxx 2021-07-27 19:09:42.582249917 +0200 --+++ libcmis/src/libcmis/http-session.hxx 2021-07-27 19:10:02.369249198 +0200 --@@ -148,7 +148,7 @@ -- -- void setNoSSLCertificateCheck( bool noCheck ); -- --- std::string getRefreshToken( ); --+ virtual std::string getRefreshToken( ); -- -- protected: -- HttpSession( ); --diff -ur libcmis.org/src/libcmis/oauth2-handler.cxx libcmis/src/libcmis/oauth2-handler.cxx ----- libcmis.org/src/libcmis/oauth2-handler.cxx 2021-07-27 19:09:42.582249917 +0200 --+++ libcmis/src/libcmis/oauth2-handler.cxx 2021-07-27 19:10:02.369249198 +0200 --@@ -158,6 +158,11 @@ -- return m_refresh; -- } -- --+void OAuth2Handler::setRefreshToken( string refreshToken ) --+{ --+ m_refresh = refreshToken; --+} --+ -- string OAuth2Handler::getHttpHeader( ) -- { -- string header; --diff -ur libcmis.org/src/libcmis/oauth2-handler.hxx libcmis/src/libcmis/oauth2-handler.hxx ----- libcmis.org/src/libcmis/oauth2-handler.hxx 2021-07-27 19:09:42.582249917 +0200 --+++ libcmis/src/libcmis/oauth2-handler.hxx 2021-07-27 19:10:02.370249198 +0200 --@@ -61,6 +61,7 @@ -- -- std::string getAccessToken( ) ; -- std::string getRefreshToken( ) ; --+ void setRefreshToken( std::string refreshToken ) ; -- -- // adding HTTP auth header -- std::string getHttpHeader( ) ; --diff -ur libcmis.org/src/libcmis/onedrive-session.cxx libcmis/src/libcmis/onedrive-session.cxx ----- libcmis.org/src/libcmis/onedrive-session.cxx 2021-07-27 19:09:42.583249917 +0200 --+++ libcmis/src/libcmis/onedrive-session.cxx 2021-07-27 19:10:02.370249198 +0200 --@@ -68,6 +68,45 @@ -- { -- } -- --+void OneDriveSession::setOAuth2Data( libcmis::OAuth2DataPtr oauth2 ) --+{ --+ m_oauth2Handler = new OAuth2Handler( this, oauth2 ); --+ m_oauth2Handler->setOAuth2Parser( OAuth2Providers::getOAuth2Parser( getBindingUrl( ) ) ); --+ --+ oauth2Authenticate( ); --+} --+ --+void OneDriveSession::oauth2Authenticate() --+{ --+ // treat the supplied password as refresh token --+ if (!m_password.empty()) --+ { --+ try --+ { --+ m_inOAuth2Authentication = true; --+ --+ m_oauth2Handler->setRefreshToken(m_password); --+ // Try to get new access tokens using the stored refreshtoken --+ m_oauth2Handler->refresh(); --+ m_inOAuth2Authentication = false; --+ } --+ catch (const CurlException &e) --+ { --+ m_inOAuth2Authentication = false; --+ // refresh token expired or invalid, trigger initial auth (that in turn will hit the fallback with copy'n'paste method) --+ BaseSession::oauth2Authenticate(); --+ } --+ } --+ else --+ { --+ BaseSession::oauth2Authenticate(); --+ } --+} --+ --+string OneDriveSession::getRefreshToken() { --+ return HttpSession::getRefreshToken(); --+} --+ -- libcmis::RepositoryPtr OneDriveSession::getRepository( ) -- { -- // Return a dummy repository since OneDrive doesn't have that notion --diff -ur libcmis.org/src/libcmis/onedrive-session.hxx libcmis/src/libcmis/onedrive-session.hxx ----- libcmis.org/src/libcmis/onedrive-session.hxx 2021-07-27 19:09:42.583249917 +0200 --+++ libcmis/src/libcmis/onedrive-session.hxx 2021-07-27 19:10:02.370249198 +0200 --@@ -62,8 +62,14 @@ -- -- bool isAPathMatch( Json objectJson, std::string path ); -- --+ virtual std::string getRefreshToken(); --+ -- private: -- OneDriveSession( ); --+ --+ virtual void setOAuth2Data( libcmis::OAuth2DataPtr oauth2 ); --+ --+ void oauth2Authenticate( ); -- }; -- -- #endif /* _ONEDRIVE_SESSION_HXX_ */ -diff --git a/external/libcmis/libcmis_onedrive.patch b/external/libcmis/libcmis_onedrive.patch -deleted file mode 100644 -index 60d7e7b..0000000 ---- a/external/libcmis/libcmis_onedrive.patch -+++ /dev/null -@@ -1,445 +0,0 @@ --diff --git a/src/libcmis/http-session.cxx b/src/libcmis/http-session.cxx --index 2638482..227667e 100644 ----- a/src/libcmis/http-session.cxx --+++ b/src/libcmis/http-session.cxx --@@ -293,6 +293,94 @@ libcmis::HttpResponsePtr HttpSession::httpGetRequest( string url ) -- return response; -- } -- --+libcmis::HttpResponsePtr HttpSession::httpPatchRequest( string url, istream& is, vector< string > headers ) --+{ --+ checkOAuth2( url ); --+ --+ // Duplicate istream in case we need to retry --+ string isStr( static_cast< stringstream const&>( stringstream( ) << is.rdbuf( ) ).str( ) ); --+ --+ istringstream isOriginal( isStr ), isBackup( isStr ); --+ --+ // Reset the handle for the request --+ curl_easy_reset( m_curlHandle ); --+ initProtocols( ); --+ --+ libcmis::HttpResponsePtr response( new libcmis::HttpResponse( ) ); --+ --+ curl_easy_setopt( m_curlHandle, CURLOPT_WRITEFUNCTION, lcl_bufferData ); --+ curl_easy_setopt( m_curlHandle, CURLOPT_WRITEDATA, response->getData( ).get( ) ); --+ --+ curl_easy_setopt( m_curlHandle, CURLOPT_HEADERFUNCTION, &lcl_getHeaders ); --+ curl_easy_setopt( m_curlHandle, CURLOPT_WRITEHEADER, response.get() ); --+ --+ curl_easy_setopt( m_curlHandle, CURLOPT_MAXREDIRS, 20); --+ --+ // Get the stream length --+ is.seekg( 0, ios::end ); --+ long size = is.tellg( ); --+ is.seekg( 0, ios::beg ); --+ curl_easy_setopt( m_curlHandle, CURLOPT_INFILESIZE, size ); --+ curl_easy_setopt( m_curlHandle, CURLOPT_READDATA, &isOriginal ); --+ curl_easy_setopt( m_curlHandle, CURLOPT_READFUNCTION, lcl_readStream ); --+ curl_easy_setopt( m_curlHandle, CURLOPT_UPLOAD, 1 ); --+ curl_easy_setopt( m_curlHandle, CURLOPT_CUSTOMREQUEST, "PATCH" ); --+ curl_easy_setopt( m_curlHandle, CURLOPT_IOCTLFUNCTION, lcl_ioctlStream ); --+ curl_easy_setopt( m_curlHandle, CURLOPT_IOCTLDATA, &isOriginal ); --+ --+ // If we know for sure that 100-Continue won't be accepted, --+ // don't even try with it to save one HTTP request. --+ if ( m_no100Continue ) --+ headers.push_back( "Expect:" ); --+ try --+ { --+ httpRunRequest( url, headers ); --+ response->getData( )->finish(); --+ } --+ catch ( const CurlException& ) --+ { --+ long status = getHttpStatus( ); --+ /** If we had a HTTP 417 response, this is likely to be due to some --+ HTTP 1.0 proxy / server not accepting the "Expect: 100-continue" --+ header. Try to disable this header and try again. --+ */ --+ if ( status == 417 && !m_no100Continue) --+ { --+ // Remember that we don't want 100-Continue for the future requests --+ m_no100Continue = true; --+ response = httpPutRequest( url, isBackup, headers ); --+ } --+ --+ // If the access token is expired, we get 401 error, --+ // Need to use the refresh token to get a new one. --+ if ( status == 401 && !getRefreshToken( ).empty( ) && !m_refreshedToken ) --+ { --+ --+ // Refresh the token --+ oauth2Refresh(); --+ --+ // Resend the query --+ try --+ { --+ // Avoid infinite recursive call --+ m_refreshedToken = true; --+ response = httpPutRequest( url, isBackup, headers ); --+ m_refreshedToken = false; --+ } --+ catch (const CurlException&) --+ { --+ m_refreshedToken = false; --+ throw; --+ } --+ } --+ // Has tried but failed --+ if ( ( status != 417 || m_no100Continue ) && --+ ( status != 401 || getRefreshToken( ).empty( ) || m_refreshedToken ) ) throw; --+ } --+ m_refreshedToken = false; --+ return response; --+} --+ -- libcmis::HttpResponsePtr HttpSession::httpPutRequest( string url, istream& is, vector< string > headers ) -- { -- checkOAuth2( url ); --diff --git a/src/libcmis/http-session.hxx b/src/libcmis/http-session.hxx --index 851d52d..29de64d 100644 ----- a/src/libcmis/http-session.hxx --+++ b/src/libcmis/http-session.hxx --@@ -132,6 +132,9 @@ class HttpSession -- virtual void setOAuth2Data( libcmis::OAuth2DataPtr oauth2 ); -- -- libcmis::HttpResponsePtr httpGetRequest( std::string url ); --+ libcmis::HttpResponsePtr httpPatchRequest( std::string url, --+ std::istream& is, --+ std::vector< std::string > headers ); -- libcmis::HttpResponsePtr httpPutRequest( std::string url, -- std::istream& is, -- std::vector< std::string > headers ); --diff --git a/src/libcmis/oauth2-handler.cxx b/src/libcmis/oauth2-handler.cxx --index a3320e3..842769f 100644 ----- a/src/libcmis/oauth2-handler.cxx --+++ b/src/libcmis/oauth2-handler.cxx --@@ -91,8 +91,8 @@ void OAuth2Handler::fetchTokens( string authCode ) -- string post = -- "code=" + authCode + -- "&client_id=" + m_data->getClientId() + --- "&client_secret=" + m_data->getClientSecret() + -- "&redirect_uri=" + m_data->getRedirectUri() + --+ "&scope=" + libcmis::escape( m_data->getScope() ) + -- "&grant_type=authorization_code" ; -- -- istringstream is( post ); --@@ -121,7 +121,6 @@ void OAuth2Handler::refresh( ) -- string post = -- "refresh_token=" + m_refresh + -- "&client_id=" + m_data->getClientId() + --- "&client_secret=" + m_data->getClientSecret() + -- "&grant_type=refresh_token" ; -- -- istringstream is( post ); --diff --git a/src/libcmis/oauth2-providers.cxx b/src/libcmis/oauth2-providers.cxx --index 8cf9652..654021f 100644 ----- a/src/libcmis/oauth2-providers.cxx --+++ b/src/libcmis/oauth2-providers.cxx --@@ -312,7 +312,7 @@ OAuth2Parser OAuth2Providers::getOAuth2Parser( const std::string& url ) -- return OAuth2Alfresco; -- else if ( boost::starts_with( url, "https://www.googleapis.com/drive/v2" ) ) -- return OAuth2Gdrive; --- else if ( boost::starts_with( url, "https://apis.live.net/v5.0" ) ) --+ else if ( boost::starts_with( url, "https://graph.microsoft.com/v1.0" ) ) -- return OAuth2Onedrive; -- -- return OAuth2Gdrive; --diff --git a/src/libcmis/onedrive-document.cxx b/src/libcmis/onedrive-document.cxx --index f753b42..863a92f 100644 ----- a/src/libcmis/onedrive-document.cxx --+++ b/src/libcmis/onedrive-document.cxx --@@ -73,7 +73,7 @@ boost::shared_ptr< istream > OneDriveDocument::getContentStream( string /*stream -- boost::shared_ptr< istream > stream; -- string streamUrl = getStringProperty( "source" ); -- if ( streamUrl.empty( ) ) --- throw libcmis::Exception( "can not found stream url" ); --+ throw libcmis::Exception( "could not find stream url" ); -- -- try -- { --@@ -89,15 +89,15 @@ boost::shared_ptr< istream > OneDriveDocument::getContentStream( string /*stream -- void OneDriveDocument::setContentStream( boost::shared_ptr< ostream > os, -- string /*contentType*/, -- string fileName, --- bool /*overwrite*/ ) --+ bool bReplaceExisting ) -- { -- if ( !os.get( ) ) -- throw libcmis::Exception( "Missing stream" ); --- --+ -- string metaUrl = getUrl( ); -- -- // Update file name meta information --- if ( !fileName.empty( ) && fileName != getContentFilename( ) ) --+ if ( bReplaceExisting && !fileName.empty( ) && fileName != getContentFilename( ) ) -- { -- Json metaJson; -- Json fileJson( fileName.c_str( ) ); --@@ -108,7 +108,7 @@ void OneDriveDocument::setContentStream( boost::shared_ptr< ostream > os, -- headers.push_back( "Content-Type: application/json" ); -- try -- { --- getSession()->httpPutRequest( metaUrl, is, headers ); --+ getSession()->httpPatchRequest( metaUrl, is, headers ); -- } -- catch ( const CurlException& e ) -- { --@@ -117,9 +117,9 @@ void OneDriveDocument::setContentStream( boost::shared_ptr< ostream > os, -- } -- -- fileName = libcmis::escape( getStringProperty( "cmis:name" ) ); --- string putUrl = getSession( )->getBindingUrl( ) + "/" + --- getStringProperty( "cmis:parentId" ) + "/files/" + --- fileName + "?overwrite=true"; --+ string putUrl = getSession( )->getBindingUrl( ) + "/me/drive/items/" + --+ getStringProperty( "cmis:parentId" ) + ":/" + --+ fileName + ":/content"; -- -- // Upload stream -- boost::shared_ptr< istream> is ( new istream ( os->rdbuf( ) ) ); --@@ -142,6 +142,7 @@ void OneDriveDocument::setContentStream( boost::shared_ptr< ostream > os, -- libcmis::DocumentPtr OneDriveDocument::checkOut( ) -- { -- // OneDrive doesn't have CheckOut, so just return the same document here --+ // TODO: no longer true - onedrive now has checkout/checkin -- libcmis::ObjectPtr obj = getSession( )->getObject( getId( ) ); -- libcmis::DocumentPtr checkout = -- boost::dynamic_pointer_cast< libcmis::Document > ( obj ); --diff --git a/src/libcmis/onedrive-folder.cxx b/src/libcmis/onedrive-folder.cxx --index a9ae694..c1980c8 100644 ----- a/src/libcmis/onedrive-folder.cxx --+++ b/src/libcmis/onedrive-folder.cxx --@@ -57,7 +57,9 @@ OneDriveFolder::~OneDriveFolder( ) -- vector< libcmis::ObjectPtr > OneDriveFolder::getChildren( ) -- { -- vector< libcmis::ObjectPtr > children; --- string query = getSession( )->getBindingUrl( ) + "/" + getId( ) + "/files"; --+ // TODO: limited to 200 items by default - to get more one would have to --+ // follow @odata.nextLink or change pagination size --+ string query = getSession( )->getBindingUrl( ) + "/me/drive/items/" + getId( ) + "/children"; -- -- string res; -- try --@@ -70,7 +72,7 @@ vector< libcmis::ObjectPtr > OneDriveFolder::getChildren( ) -- } -- -- Json jsonRes = Json::parse( res ); --- Json::JsonVector objs = jsonRes["data"].getList( ); --+ Json::JsonVector objs = jsonRes["value"].getList( ); -- -- // Create children objects from Json objects -- for(unsigned int i = 0; i < objs.size(); i++) --@@ -85,8 +87,7 @@ libcmis::FolderPtr OneDriveFolder::createFolder( -- const PropertyPtrMap& properties ) -- { -- Json propsJson = OneDriveUtils::toOneDriveJson( properties ); --- --- string uploadUrl = getSession( )->getBindingUrl( ) + "/" + getId( ); --+ string uploadUrl = getSession( )->getBindingUrl( ) + "/me/drive/items/" + getId( ) + "/children"; -- -- std::istringstream is( propsJson.toString( ) ); -- string response; --@@ -126,9 +127,10 @@ libcmis::DocumentPtr OneDriveFolder::createDocument( -- } -- } -- --+ // TODO: limited to 4MB, larger uploads need dedicated UploadSession -- fileName = libcmis::escape( fileName ); --- string newDocUrl = getSession( )->getBindingUrl( ) + "/" + --- getId( ) + "/files/" + fileName; --+ string newDocUrl = getSession( )->getBindingUrl( ) + "/me/drive/items/" + --+ getId( ) + ":/" + fileName + ":/content"; -- boost::shared_ptr< istream> is ( new istream ( os->rdbuf( ) ) ); -- vector< string > headers; -- string res; --diff --git a/src/libcmis/onedrive-object.cxx b/src/libcmis/onedrive-object.cxx --index 976a97b..8deb591 100644 ----- a/src/libcmis/onedrive-object.cxx --+++ b/src/libcmis/onedrive-object.cxx --@@ -65,7 +65,7 @@ void OneDriveObject::initializeFromJson ( Json json, string /*id*/, string /*nam -- Json::JsonObject objs = json.getObjects( ); -- Json::JsonObject::iterator it; -- PropertyPtr property; --- bool isFolder = json["type"].toString( ) == "folder"; --+ bool isFolder = json["folder"].toString( ) != ""; -- for ( it = objs.begin( ); it != objs.end( ); ++it) -- { -- property.reset( new OneDriveProperty( it->first, it->second ) ); --@@ -74,7 +74,12 @@ void OneDriveObject::initializeFromJson ( Json json, string /*id*/, string /*nam -- { -- property.reset( new OneDriveProperty( "cmis:contentStreamFileName", it->second ) ); -- m_properties[ property->getPropertyType( )->getId()] = property; --- } --+ } else if ( it->first == "parentReference" ) { --+ if (it->second["id"].toString() != "") { --+ property.reset( new OneDriveProperty( "cmis:parentId", it->second["id"] ) ); --+ m_properties[ property->getPropertyType( )->getId()] = property; --+ } --+ } -- } -- -- m_refreshTimestamp = time( NULL ); --@@ -122,7 +127,7 @@ void OneDriveObject::remove( bool /*allVersions*/ ) -- -- string OneDriveObject::getUrl( ) -- { --- return getSession( )->getBindingUrl( ) + "/" + getId( ); --+ return getSession( )->getBindingUrl( ) + "/me/drive/items/" + getId( ); -- } -- -- string OneDriveObject::getUploadUrl( ) --@@ -152,7 +157,7 @@ libcmis::ObjectPtr OneDriveObject::updateProperties( -- { -- vector< string > headers; -- headers.push_back( "Content-Type: application/json" ); --- response = getSession( )->httpPutRequest( getUrl( ), is, headers ); --+ response = getSession( )->httpPatchRequest( getUrl( ), is, headers ); -- } -- catch ( const CurlException& e ) -- { --diff --git a/src/libcmis/onedrive-repository.cxx b/src/libcmis/onedrive-repository.cxx --index 3eaac9c..b01f5c2 100644 ----- a/src/libcmis/onedrive-repository.cxx --+++ b/src/libcmis/onedrive-repository.cxx --@@ -35,7 +35,7 @@ OneDriveRepository::OneDriveRepository( ) : -- m_description = "One Drive repository"; -- m_productName = "One Drive"; -- m_productVersion = "v5"; --- m_rootId = "me/skydrive"; --+ m_rootId = "/me/drive/root"; -- -- m_capabilities[ ACL ] = "discover"; -- m_capabilities[ AllVersionsSearchable ] = "true"; --diff --git a/src/libcmis/onedrive-session.cxx b/src/libcmis/onedrive-session.cxx --index c6f4270..a603278 100644 ----- a/src/libcmis/onedrive-session.cxx --+++ b/src/libcmis/onedrive-session.cxx --@@ -79,7 +79,9 @@ libcmis::ObjectPtr OneDriveSession::getObject( string objectId ) -- { -- // Run the http request to get the properties definition -- string res; --- string objectLink = m_bindingUrl + "/" + objectId; --+ string objectLink = m_bindingUrl + "/me/drive/items/" + objectId; --+ if (objectId == getRootId()) --+ objectLink = m_bindingUrl + objectId; -- try -- { -- res = httpGetRequest( objectLink )->getStream()->str(); --@@ -95,12 +97,11 @@ libcmis::ObjectPtr OneDriveSession::getObject( string objectId ) -- libcmis::ObjectPtr OneDriveSession::getObjectFromJson( Json& jsonRes ) -- { -- libcmis::ObjectPtr object; --- string kind = jsonRes["type"].toString( ); --- if ( kind == "folder" || kind == "album" ) --+ if ( jsonRes["folder"].toString() != "" ) -- { -- object.reset( new OneDriveFolder( this, jsonRes ) ); -- } --- else if ( kind == "file" ) --+ else if ( jsonRes["file"].toString() != "" ) -- { -- object.reset( new OneDriveDocument( this, jsonRes ) ); -- } --@@ -113,44 +114,18 @@ libcmis::ObjectPtr OneDriveSession::getObjectFromJson( Json& jsonRes ) -- -- libcmis::ObjectPtr OneDriveSession::getObjectByPath( string path ) -- { --- string id; --- if ( path == "/" ) --- { --- id = "me/skydrive"; --- } --- else --+ string res; --+ string objectQuery = m_bindingUrl + "/me/drive/root:" + libcmis::escape( path ); --+ try -- { --- path = "/SkyDrive" + path; --- size_t pos = path.rfind("/"); --- string name = libcmis::escape( path.substr( pos + 1, path.size( ) ) ); --- string res; --- string objectQuery = m_bindingUrl + "/me/skydrive/search?q=" + name; --- try --- { --- res = httpGetRequest( objectQuery )->getStream( )->str( ); --- } --- catch ( const CurlException& e ) --- { --- throw e.getCmisException( ); --- } --- Json jsonRes = Json::parse( res ); --- Json::JsonVector objs = jsonRes["data"].getList( ); --- --- // Searching for a match in the path to the object --- for ( unsigned int i = 0; i < objs.size( ); i++ ) --- { --- if ( isAPathMatch( objs[i], path ) ) --- { --- id = objs[i]["id"].toString( ); --- break; --- } --- } --+ res = httpGetRequest( objectQuery )->getStream( )->str( ); -- } --- if ( id.empty( ) ) --+ catch ( const CurlException& e ) -- { --- throw libcmis::Exception( "No file could be found" ); --+ throw libcmis::Exception( "No file could be found for path " + path + ": " + e.what() ); -- } --- return getObject( id ); --+ Json jsonRes = Json::parse( res ); --+ return getObjectFromJson( jsonRes ); -- } -- -- bool OneDriveSession::isAPathMatch( Json objectJson, string path ) --diff --git a/src/libcmis/onedrive-utils.cxx b/src/libcmis/onedrive-utils.cxx --index dc6ec5d..17ed324 100644 ----- a/src/libcmis/onedrive-utils.cxx --+++ b/src/libcmis/onedrive-utils.cxx --@@ -44,16 +44,16 @@ string OneDriveUtils::toCmisKey( const string& key ) -- convertedKey = "cmis:createdBy"; -- else if ( key == "description" ) -- convertedKey = "cmis:description"; --- else if ( key == "created_time" ) --+ else if ( key == "createdDateTime" ) -- convertedKey = "cmis:creationDate"; --- else if ( key == "updated_time" ) --+ else if ( key == "lastModifiedDateTime" ) -- convertedKey = "cmis:lastModificationDate"; -- else if ( key == "name" ) -- convertedKey = "cmis:name"; -- else if ( key == "size" ) -- convertedKey = "cmis:contentStreamLength"; --- else if ( key == "parent_id" ) --- convertedKey = "cmis:parentId"; --+ else if ( key == "@microsoft.graph.downloadUrl" ) --+ convertedKey = "source"; -- else convertedKey = key; -- return convertedKey; -- } --@@ -75,8 +75,6 @@ string OneDriveUtils::toOneDriveKey( const string& key ) -- convertedKey = "name"; -- else if ( key == "cmis:contentStreamLength" ) -- convertedKey = "file_size"; --- else if ( key == "cmis:parentId" ) --- convertedKey = "parent_id"; -- else convertedKey = key; -- return convertedKey; -- } --diff --git a/src/libcmis/session-factory.cxx b/src/libcmis/session-factory.cxx --index ba55cd9..e740afb 100644 ----- a/src/libcmis/session-factory.cxx --+++ b/src/libcmis/session-factory.cxx --@@ -71,7 +71,7 @@ namespace libcmis -- session = new GDriveSession( bindingUrl, username, password, -- oauth2, verbose ); -- } --- else if ( bindingUrl == "https://apis.live.net/v5.0" ) --+ else if ( bindingUrl == "https://graph.microsoft.com/v1.0" ) -- { -- session = new OneDriveSession( bindingUrl, username, password, -- oauth2, verbose); -diff --git a/ucb/source/ucp/cmis/auth_provider.hxx b/ucb/source/ucp/cmis/auth_provider.hxx -index 1768c12..af1a420 100644 ---- a/ucb/source/ucp/cmis/auth_provider.hxx -+++ b/ucb/source/ucp/cmis/auth_provider.hxx -@@ -8,15 +8,7 @@ - */ - #pragma once - --#if defined __GNUC__ && !defined __clang__ --#pragma GCC diagnostic push --#pragma GCC diagnostic ignored "-Wdeprecated" --#pragma GCC diagnostic ignored "-Wunused-but-set-parameter" --#endif - #include --#if defined __GNUC__ && !defined __clang__ --#pragma GCC diagnostic pop --#endif - - #include - #include diff --git a/libreoffice.spec b/libreoffice.spec index 2b6597e..fa6dfd4 100644 --- a/libreoffice.spec +++ b/libreoffice.spec @@ -1,5 +1,5 @@ # download path contains version without the last (fourth) digit -%global libo_version 7.6.2 +%global libo_version 7.6.3 # Should contain .alphaX / .betaX, if this is pre-release (actually # pre-RC) version. The pre-release string is part of tarball file names, # so we need a way to define it easily at one place. @@ -280,7 +280,7 @@ Patch3: 0001-default-to-sifr-for-gnome-light-mode.patch # backported Patch4: 0001-Only-pass-I.-arguments-to-g-ir-scanner-by-using-pkg-.patch Patch5: limit-tests-giving-dubious-results-to-x86_64.patch -Patch6: 717d27a.patch +Patch6: pdfdoc.patch # not upstreamed # fix FTB in ppc64le from sharkcz # https://lists.freedesktop.org/archives/libreoffice/2023-August/090870.html diff --git a/pdfdoc.patch b/pdfdoc.patch new file mode 100644 index 0000000..43c814f --- /dev/null +++ b/pdfdoc.patch @@ -0,0 +1,22 @@ +--- libreoffice-7.6.3.1/sw/qa/core/text/itrform2.cxx 2023-11-02 16:37:04.000000000 -0500 ++++ core/sw/qa/core/text/itrform2.cxx 2023-11-07 10:31:14.018505043 -0600 +@@ -124,6 +124,10 @@ + + // Then make sure all the expected text is there on page 2: + std::unique_ptr pPdfDocument = parsePDFExport(); ++ if (!pPdfDocument) ++ { ++ return; ++ } + std::unique_ptr pPage2 = pPdfDocument->openPage(1); + int nTextCount = 0; + for (int i = 0; i < pPage2->getObjectCount(); ++i) +@@ -143,7 +147,7 @@ + + CPPUNIT_TEST_FIXTURE(Test, testSplitFlyAnchorLeftMargin) + { +- // Given a document with a floating table, anchor para is followed by an other para with a left ++ // Given a document with a floating table, anchor para is followed by another para with a left + // margin: + createSwDoc("floattable-anchor-left-margin.docx"); + diff --git a/sources b/sources index 9a74a16..6a445fe 100644 --- a/sources +++ b/sources @@ -1,12 +1,12 @@ +SHA512 (libreoffice-7.6.3.1.tar.xz) = 3ce56e96889be565438e4646a662711899bfa1b65a3c80e478a283481594dbd4428bee4193c3034f2422f297c4e3bc45e7af87d4d54172b0d5a2dcb143189a0d +SHA512 (libreoffice-7.6.3.1.tar.xz.asc) = 900dec2eaa586abfde851f968bd38444654b9ed33b4258ddc681b999e2755ca39df8dbc7350fafb11be0828811fc221fbf2cfefb0a207216506141d105655cd7 +SHA512 (libreoffice-help-7.6.3.1.tar.xz) = a364c458b2152fd0ebbfd607d11eb290dbbd9e4c6cb06f11a90d8563638c72365351404eac84c8d08626446cb4e11a6bd23876d94bca77c205b70c6564507c6d +SHA512 (libreoffice-help-7.6.3.1.tar.xz.asc) = b659db1ef3ea2d3b26132230271923b4fe9b707fe066932d02e80c004ea8d3b51472dd0ce7a346af712d691eab122f3650e7f1950180d6dd0ed0e8ed28b03da3 +SHA512 (libreoffice-translations-7.6.3.1.tar.xz) = 5b309b3b6f69628cd2af21a00834281f1deb037b14d8acbc9e8e7933eea56c9488692f5ec283a5086d9ab1487bdb663185e268fd2aa8e310a4db717c7d367c57 +SHA512 (libreoffice-translations-7.6.3.1.tar.xz.asc) = 95722443ba2985a0b58bc29b18a4fa9227d8c33c2e21341444a148833ce4737c127385a8e05f124a0d3bfbbf160ba8863094c9980a8dc904004f743c8604280e SHA512 (17410483b5b5f267aa18b7e00b65e6e0-hsqldb_1_8_0.zip) = a231eba4a1baca11766ef292ab45e302081115477fe23018652882923308856835cf8c9ecba61a5cf22543474ccef3136965d794a90c9e4e9e6dcc21f9af6e1a SHA512 (185d60944ea767075d27247c3162b3bc-unowinreg.dll) = 854b8ae29b57b40ba6bb6ff66e723a0e8dad053fcc2849f0ad763cd8a31352f4aeba9636fd4e3f0f2a0cd985a6f49b4261b9ace68d6be821ed42cfa7a73eb13c SHA512 (a7983f859eafb2677d7ff386a023bc40-xsltml_2.1.2.zip) = 2d3835f7ac356805025cafedcad97faa48d0f5da386e6ac7b7451030059df8e2fdb0861ade07a576ebf9fb5b88a973585ab0437944b06aac9289d6898ba8586a SHA512 (f543e6e2d7275557a839a164941c0a86e5f2c3f2a0042bfc434c88c6dde9e140-opens___.ttf) = 6a6d131dad5191614950a49323ae6d9385afe331983c1c85fde82ce6ee816051d95dde9ef90658b8f0a8a0a21754e72ff724bf41f6b96c046b7b4c2660f7095b SHA512 (libreoffice-multiliblauncher.sh) = db532afdf5000bc66f9e02c7d0ab586468466f63f8f0bdb204832581e8277c5c59f688fa096548d642411cb8c46e8de4a744676b4e624c075262cfd6945138cd SHA512 (dtoa-20180411.tgz) = 722aa814c33a34bfffe6c0201b0035cc3b65854a0ba9ae2f51620a89d68019353e2c306651b35bca337186b22b2e9865ef3c5e3df8e9328006f882e4577f8c85 -SHA512 (libreoffice-7.6.2.1.tar.xz) = 4b209c444437c067bbc08c5b7d0273fa32675a19afc15751a403e1ee024795fe87aecf64de193203ee4f84f5b7ae8d547cde4ed9aced4efd3d6743af65ac6896 -SHA512 (libreoffice-7.6.2.1.tar.xz.asc) = 0278e6e92971ce9d68f21056f0539c38a6a3def6269daf9263d3ed54bddd4761dea0cc2646b3dfff237a92d2bdb958339aee641651d67aa27d179f94ad26c480 -SHA512 (libreoffice-help-7.6.2.1.tar.xz) = 8fda9424a5ff5fbe0317e08d91504b75e3075d13d3f9b65aa21cdbc53578b1dda40b14168becc577b972eefb09a8255a5ce00322277714274c8adf6975f08a61 -SHA512 (libreoffice-help-7.6.2.1.tar.xz.asc) = 46a4546cbee7a7c66df3ebeda99674e5369aaae169c3ab8691d71a9ef5d59189df88084832991afd9d3928584ad83de8f060f452a1d78b2ef229e537eff5cd1d -SHA512 (libreoffice-translations-7.6.2.1.tar.xz) = 4cecbc09d648ee751ca7bbcfa61b8ec174ac71aae7255c6359af5f6cbff019cc4ad5b6b4ca757329907175b13ce57e5afaa70355a9bfc48e86f6ccf8d0b7e80d -SHA512 (libreoffice-translations-7.6.2.1.tar.xz.asc) = 936f34adf6648526db69efb968e66095430e8f5f7fbff241a64f44844dfb4eab2571e2813c39da52103054f72ebf08662b0f1c5d02a0876180dc138090ef6dfe