@@ -35,7 +35,7 @@ def checkFileInfo(fileid, acctok):
3535 acctok ['usertype' ] = utils .UserType (acctok ['usertype' ])
3636 statInfo = st .statx (acctok ['endpoint' ], acctok ['filename' ], acctok ['userid' ])
3737
38- # populate metadata for this file
38+ # ** Populate general metadata for this file **
3939 fmd = {}
4040 fmd ['BaseFileName' ] = fmd ['BreadcrumbDocName' ] = os .path .basename (acctok ['filename' ])
4141 fmd ['FileExtension' ] = os .path .splitext (acctok ['filename' ])[1 ]
@@ -72,9 +72,6 @@ def checkFileInfo(fileid, acctok):
7272 if acctok ['viewmode' ] != utils .ViewMode .VIEW_ONLY and srv .config .get ('general' , 'downloadurl' , fallback = None ):
7373 fmd ['DownloadUrl' ] = fmd ['FileUrl' ] = '%s?access_token=%s' % \
7474 (srv .config .get ('general' , 'downloadurl' ), flask .request .args ['access_token' ])
75- if srv .config .get ('apps' , 'businessflow' , fallback = 'True' ).upper () == 'TRUE' :
76- # according to Microsoft, this must be enabled for all users
77- fmd ['LicenseCheckForEditIsEnabled' ] = True
7875 fmd ['BreadcrumbBrandName' ] = srv .config .get ('general' , 'brandingname' , fallback = None )
7976 fmd ['BreadcrumbBrandUrl' ] = srv .config .get ('general' , 'brandingurl' , fallback = None )
8077 fmd ['OwnerId' ] = statInfo ['ownerid' ]
@@ -83,6 +80,9 @@ def checkFileInfo(fileid, acctok):
8380 fmd ['LastModifiedTime' ] = str (datetime .fromtimestamp (int (statInfo ['mtime' ]))) + '.000'
8481 # note that in ownCloud 10 the version is generated as: `'V' + etag + checksum`. Here etag is `version:checksum`.
8582 fmd ['Version' ] = f"v{ statInfo ['etag' ]} "
83+ fmd ['AccessTokenExpiry' ] = acctok ['exp' ] * 1000
84+ fmd ['ServerTime' ] = int (time .time ()) * 1000
85+
8686 fmd ['SupportsExtendedLockLength' ] = fmd ['SupportsGetLock' ] = fmd ['SupportsCoauth' ] = True
8787 fmd ['SupportsUpdate' ] = fmd ['UserCanWrite' ] = fmd ['SupportsLocks' ] = \
8888 fmd ['SupportsDeleteFile' ] = acctok ['viewmode' ] in (utils .ViewMode .READ_WRITE , utils .ViewMode .PREVIEW )
@@ -96,47 +96,55 @@ def checkFileInfo(fileid, acctok):
9696 acctok ['usertype' ] != utils .UserType .REGULAR
9797 fmd ['SupportsRename' ] = fmd ['UserCanRename' ] = enablerename and \
9898 acctok ['viewmode' ] in (utils .ViewMode .READ_WRITE , utils .ViewMode .PREVIEW )
99- fmd ['AccessTokenExpiry' ] = acctok ['exp' ] * 1000
100- fmd ['FileGeoLocationCode' ] = fmd ['RealTimeChannelEndpointUrl' ] = \
101- fmd ['OfficeCollaborationServiceEndpointUrl' ] = '' # to please the wopi validator
102- fmd ['SharingStatus' ] = 'Shared'
103- fmd ['ServerTime' ] = int (time .time ()) * 1000
104- try :
105- fmd ['SequenceNumber' ] = int (statInfo ['etag' ][:statInfo ['etag' ].find (':' )])
106- except (ValueError , AttributeError ):
107- log .warning ('msg="Failed to extract SequenceNumber from etag" ' +
108- f'token="{ flask .request .args ["access_token" ][- 20 :]} " etag="{ statInfo ["etag" ]} "' )
10999 fmd ['SupportsUserInfo' ] = True
110100 uinfo = statInfo ['xattrs' ].get (utils .USERINFOKEY + '.' + acctok ['wopiuser' ].split ('!' )[0 ])
111101 if uinfo :
112102 fmd ['UserInfo' ] = uinfo
113103
114- # populate app-specific metadata
104+ # ** Populate app-specific metadata **
105+ if srv .config .get ('apps' , 'businessflow' , fallback = 'True' ).upper () == 'TRUE' :
106+ # according to Microsoft, this must be enabled for all users
107+ fmd ['LicenseCheckForEditIsEnabled' ] = True
115108 if srv .config .get ('apps' , 'earlyfeatures' , fallback = 'False' ).upper () == 'TRUE' :
116109 fmd ['AllowEarlyFeatures' ] = True
117110 if srv .config .has_option ('apps' , 'compliancedomain' ):
118111 fmd ['ComplianceDomainPrefix' ] = srv .config .get ('apps' , 'compliancedomain' )
112+
113+ # 28/11/2025: the following properties are CSPP+ related and necessary only when running the wopivalidator,
114+ # with 'SequenceNumber' even breaking normal CSPP Office apps:
115+ # learn.microsoft.com/en-us/microsoft-365/cloud-storage-partner-program/rest/files/checkfileinfo/checkfileinfo-csppp
116+ if '.wopitest' in fmd ['BaseFileName' ]:
117+ fmd ['SharingStatus' ] = 'Shared'
118+ fmd ['FileGeoLocationCode' ] = srv .config .get ('apps' , 'compliancedomain' , fallback = 'euc' )
119+ fmd ['RealTimeChannelEndpointUrl' ] = fmd ['OfficeCollaborationServiceEndpointUrl' ] = ''
120+ try :
121+ fmd ['SequenceNumber' ] = int (statInfo ['etag' ][:statInfo ['etag' ].find (':' )])
122+ except (ValueError , AttributeError ):
123+ log .warning ('msg="Failed to extract SequenceNumber from etag" ' +
124+ f'token="{ flask .request .args ["access_token" ][- 20 :]} " etag="{ statInfo ["etag" ]} "' )
125+
119126 # the following is to enable the 'Edit in Word/Excel/PowerPoint' (desktop) action (probably broken)
120127 try :
121128 fmd ['ClientUrl' ] = srv .config .get ('general' , 'webdavurl' ) + '/' + acctok ['filename' ]
122129 except configparser .NoOptionError :
123130 # if no WebDAV URL is provided, ignore this setting
124131 pass
132+
125133 # extensions for Collabora Online
126134 if 'Collabora' in acctok ['appname' ]:
127135 fmd ['EnableOwnerTermination' ] = True
128136 fmd ['DisableExport' ] = fmd ['DisableCopy' ] = fmd ['DisablePrint' ] = acctok ['viewmode' ] == utils .ViewMode .VIEW_ONLY
129137 if srv .config .get ('apps' , 'codedisableexport' , fallback = 'False' ).upper () == 'TRUE' :
130138 fmd ['UserCanNotWriteRelative' ] = fmd ['DisableExport' ] = True
131139
140+ # ** Response and log **
132141 res = flask .Response (json .dumps (fmd ), mimetype = 'application/json' )
133-
134- # redact sensitive metadata for the logs
135142 if srv .config .get ('general' , 'loglevel' ) != 'Debug' :
136143 fmd ['HostViewUrl' ] = fmd ['HostEditUrl' ] = fmd ['DownloadUrl' ] = fmd ['FileUrl' ] = \
137144 fmd ['BreadcrumbBrandUrl' ] = fmd ['FileSharingUrl' ] = '_redacted_'
138145 log .info (f"msg=\" File metadata response\" token=\" { flask .request .args ['access_token' ][- 20 :]} \" metadata=\" { fmd } \" " )
139146 return res
147+
140148 except IOError as e :
141149 log .info ('msg="Requested file not found" filename="%s" token="%s" details="%s"' %
142150 (acctok ['filename' ], flask .request .args ['access_token' ][- 20 :], e ))
0 commit comments