Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions _locales/en/messages.json
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,12 @@
"Error040": {
"message": "E040: Could not search for your file name in your Google Drive"
},
"Error041": {
"message": "E041: Remote bookmarks file size differs from the content that was actually downloaded from the server. This might be a temporary network issue. If this error persists please contact the server administrator."
},
"Error042": {
"message": "E042: Remote bookmarks file size could not be retrieved. It is impossible to verify that the bookmarks file was downloaded in full. If this error persists please contact the server administrator."
},
"LabelWebdavurl": {
"message": "WebDAV URL"
},
Expand Down
16 changes: 16 additions & 0 deletions src/errors/Error.ts
Original file line number Diff line number Diff line change
Expand Up @@ -341,4 +341,20 @@ export class GoogleDriveSearchError extends FloccusError {
this.code = 40
Object.setPrototypeOf(this, GoogleDriveSearchError.prototype)
}
}

export class FileSizeMismatch extends FloccusError {
constructor() {
super('E041: Remote bookmarks file size differs from the content that was actually downloaded from the server. This might be a temporary network issue. If this error persists please contact the server administrator.')
this.code = 41
Object.setPrototypeOf(this, FileSizeMismatch.prototype)
}
}

export class FileSizeUnknown extends FloccusError {
constructor() {
super('E042: Remote bookmarks file size could not be retrieved. It is impossible to verify that the bookmarks file was downloaded in full. If this error persists please contact the server administrator.')
this.code = 42
Object.setPrototypeOf(this, FileSizeUnknown.prototype)
}
}
103 changes: 101 additions & 2 deletions src/lib/adapters/WebDav.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import {
HttpError, CancelledSyncError,
LockFileError, MissingPermissionsError,
NetworkError, RedirectError, ResourceLockedError,
SlashError
SlashError, FileSizeMismatch, FileSizeUnknown
} from '../../errors/Error'
import { CapacitorHttp as Http } from '@capacitor/core'
import { Capacitor } from '@capacitor/core'
Expand Down Expand Up @@ -211,6 +211,25 @@ export default class WebDavAdapter extends CachingAdapter {

if (response.status === 200) {
let xmlDocText = response.data
if (Capacitor.getPlatform() === 'web') {
let fileSize = null
try {
fileSize = await this.getFileSize(fullUrl)
} catch (e) {
console.warn(e)
Logger.log('Error getting file size: ' + e.message)
}

if (fileSize === null) {
throw new FileSizeUnknown()
}

const byteLength = new TextEncoder().encode(xmlDocText).length
if (fileSize !== byteLength) {
Logger.log('File size mismatch: ' + fileSize + ' != ' + byteLength)
throw new FileSizeMismatch()
}
}

if (this.server.passphrase) {
try {
Expand Down Expand Up @@ -429,6 +448,86 @@ export default class WebDavAdapter extends CachingAdapter {
}
}

async getFileSize(url) {
if (Capacitor.getPlatform() === 'web') {
return this.getFileSizeWeb(url)
} else {
return this.getFileSizeNative(url)
}
}

async getFileSizeWeb(url): Promise<number|null> {
const authString = Base64.encode(
this.server.username + ':' + this.server.password
)
let res
try {
res = await fetch(url,{
method: 'PROPFIND',
headers: {
Authorization: 'Basic ' + authString
},
cache: 'no-store',
credentials: 'omit',
signal: this.abortSignal,
...(!this.server.allowRedirects && {redirect: 'manual'})
})
} catch (e) {
Logger.log('Error Caught')
Logger.log(e)
if (this.abortSignal.aborted) throw new CancelledSyncError()
throw new NetworkError()
}
if (res.status === 0 && !this.server.allowRedirects) {
throw new RedirectError()
}
if (res.status === 401 || res.status === 403) {
throw new AuthenticationError()
}
if (res.status >= 300 && res.status !== 404) {
throw new HttpError(res.status, 'PROPFIND')
}

const xml = await res.text()
const match = xml.match(/<.*?:?getcontentlength>(.*?)</)
return match ? parseInt(match[1]) : null
}

async getFileSizeNative(url): Promise<number|null> {
let res
const authString = Base64.encode(
this.server.username + ':' + this.server.password
)

try {
res = await Http.request({
url: url,
method: 'PROPFIND',
headers: {
Authorization: 'Basic ' + authString,
Pragma: 'no-cache',
'Cache-Control': 'no-cache'
},
responseType: 'text'
})
} catch (e) {
Logger.log('Error Caught')
Logger.log(e)
throw new NetworkError()
}

if (res.status === 401 || res.status === 403) {
throw new AuthenticationError()
}
if (res.status >= 300 && res.status !== 404) {
throw new HttpError(res.status, 'PROPFIND')
}

const xml = res.data
const match = xml.match(/<.*?:?getcontentlength>(.*?)</)
return match ? parseInt(match[1]) : null
}

async downloadFileWeb(url) {
const authString = Base64.encode(
this.server.username + ':' + this.server.password
Expand Down Expand Up @@ -461,7 +560,7 @@ export default class WebDavAdapter extends CachingAdapter {
throw new HttpError(res.status, 'GET')
}

return { status: res.status, data: await res.text(), headers: res.headers }
return { status: res.status, data: await res.text(), headers: Object.fromEntries(res.headers.entries()) }
}

async downloadFileNative(fullURL) {
Expand Down
Loading