2019-09-15 14:16:32 +02:00
|
|
|
import { StorageBase } from 'storage/storage-base';
|
2019-09-18 07:08:23 +02:00
|
|
|
import { noop } from 'util/fn';
|
2016-03-27 13:54:35 +02:00
|
|
|
|
2017-01-31 07:50:28 +01:00
|
|
|
const OneDriveClientId = {
|
2016-03-27 14:06:27 +02:00
|
|
|
Production: '000000004818ED3A',
|
|
|
|
Local: '0000000044183D18'
|
|
|
|
};
|
2016-03-13 17:08:25 +01:00
|
|
|
|
2019-09-18 21:26:43 +02:00
|
|
|
class StorageOneDrive extends StorageBase {
|
|
|
|
name = 'onedrive';
|
|
|
|
enabled = true;
|
|
|
|
uipos = 40;
|
2020-03-29 15:01:11 +02:00
|
|
|
iconSvg = 'onedrive';
|
2016-03-13 17:08:25 +01:00
|
|
|
|
2019-09-18 21:26:43 +02:00
|
|
|
_baseUrl = 'https://graph.microsoft.com/v1.0/me';
|
2016-03-27 13:54:35 +02:00
|
|
|
|
2019-08-18 10:17:09 +02:00
|
|
|
getPathForName(fileName) {
|
2016-03-27 19:17:09 +02:00
|
|
|
return '/drive/root:/' + fileName + '.kdbx';
|
2019-09-18 21:26:43 +02:00
|
|
|
}
|
2016-03-27 19:17:09 +02:00
|
|
|
|
2019-08-18 10:17:09 +02:00
|
|
|
load(path, opts, callback) {
|
2016-07-17 13:30:38 +02:00
|
|
|
this._oauthAuthorize(err => {
|
2016-03-27 13:54:35 +02:00
|
|
|
if (err) {
|
|
|
|
return callback && callback(err);
|
|
|
|
}
|
2016-08-14 23:22:25 +02:00
|
|
|
this.logger.debug('Load', path);
|
2017-01-31 07:50:28 +01:00
|
|
|
const ts = this.logger.ts();
|
|
|
|
const url = this._baseUrl + path;
|
2016-08-14 23:22:25 +02:00
|
|
|
this._xhr({
|
2019-08-18 10:17:09 +02:00
|
|
|
url,
|
2016-03-27 13:54:35 +02:00
|
|
|
responseType: 'json',
|
2019-08-16 23:05:39 +02:00
|
|
|
success: response => {
|
2017-11-26 21:43:36 +01:00
|
|
|
const downloadUrl = response['@microsoft.graph.downloadUrl'];
|
2017-01-31 07:50:28 +01:00
|
|
|
let rev = response.eTag;
|
2016-03-27 13:54:35 +02:00
|
|
|
if (!downloadUrl || !response.eTag) {
|
2019-08-18 08:05:38 +02:00
|
|
|
this.logger.debug(
|
|
|
|
'Load error',
|
|
|
|
path,
|
|
|
|
'no download url',
|
|
|
|
response,
|
|
|
|
this.logger.ts(ts)
|
|
|
|
);
|
2016-03-27 13:54:35 +02:00
|
|
|
return callback && callback('no download url');
|
|
|
|
}
|
2016-08-14 23:22:25 +02:00
|
|
|
this._xhr({
|
2016-03-27 13:54:35 +02:00
|
|
|
url: downloadUrl,
|
|
|
|
responseType: 'arraybuffer',
|
2018-03-13 19:17:16 +01:00
|
|
|
skipAuth: true,
|
2016-08-14 23:22:25 +02:00
|
|
|
success: (response, xhr) => {
|
2016-03-27 13:54:35 +02:00
|
|
|
rev = xhr.getResponseHeader('ETag') || rev;
|
2016-08-14 23:22:25 +02:00
|
|
|
this.logger.debug('Loaded', path, rev, this.logger.ts(ts));
|
2019-08-18 10:17:09 +02:00
|
|
|
return callback && callback(null, response, { rev });
|
2016-03-27 13:54:35 +02:00
|
|
|
},
|
2019-08-16 23:05:39 +02:00
|
|
|
error: err => {
|
2016-08-14 23:22:25 +02:00
|
|
|
this.logger.error('Load error', path, err, this.logger.ts(ts));
|
2016-03-27 13:54:35 +02:00
|
|
|
return callback && callback(err);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
},
|
2019-08-16 23:05:39 +02:00
|
|
|
error: err => {
|
2016-08-14 23:22:25 +02:00
|
|
|
this.logger.error('Load error', path, err, this.logger.ts(ts));
|
2016-03-27 13:54:35 +02:00
|
|
|
return callback && callback(err);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
});
|
2019-09-18 21:26:43 +02:00
|
|
|
}
|
2016-03-13 17:08:25 +01:00
|
|
|
|
2019-08-18 10:17:09 +02:00
|
|
|
stat(path, opts, callback) {
|
2016-07-17 13:30:38 +02:00
|
|
|
this._oauthAuthorize(err => {
|
2016-03-27 13:54:35 +02:00
|
|
|
if (err) {
|
|
|
|
return callback && callback(err);
|
|
|
|
}
|
2016-08-14 23:22:25 +02:00
|
|
|
this.logger.debug('Stat', path);
|
2017-01-31 07:50:28 +01:00
|
|
|
const ts = this.logger.ts();
|
|
|
|
const url = this._baseUrl + path;
|
2016-08-14 23:22:25 +02:00
|
|
|
this._xhr({
|
2019-08-18 10:17:09 +02:00
|
|
|
url,
|
2016-03-27 13:54:35 +02:00
|
|
|
responseType: 'json',
|
2019-08-16 23:05:39 +02:00
|
|
|
success: response => {
|
2017-01-31 07:50:28 +01:00
|
|
|
const rev = response.eTag;
|
2016-03-27 13:54:35 +02:00
|
|
|
if (!rev) {
|
2016-08-14 23:22:25 +02:00
|
|
|
this.logger.error('Stat error', path, 'no eTag', this.logger.ts(ts));
|
2016-03-27 13:54:35 +02:00
|
|
|
return callback && callback('no eTag');
|
|
|
|
}
|
2016-08-14 23:22:25 +02:00
|
|
|
this.logger.debug('Stated', path, rev, this.logger.ts(ts));
|
2019-08-18 10:17:09 +02:00
|
|
|
return callback && callback(null, { rev });
|
2016-03-27 13:54:35 +02:00
|
|
|
},
|
2016-08-14 23:22:25 +02:00
|
|
|
error: (err, xhr) => {
|
2016-03-27 19:17:09 +02:00
|
|
|
if (xhr.status === 404) {
|
2016-08-14 23:22:25 +02:00
|
|
|
this.logger.debug('Stated not found', path, this.logger.ts(ts));
|
2016-03-27 19:17:09 +02:00
|
|
|
return callback && callback({ notFound: true });
|
|
|
|
}
|
2016-08-14 23:22:25 +02:00
|
|
|
this.logger.error('Stat error', path, err, this.logger.ts(ts));
|
2016-03-27 13:54:35 +02:00
|
|
|
return callback && callback(err);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
});
|
2019-09-18 21:26:43 +02:00
|
|
|
}
|
2016-03-27 13:54:35 +02:00
|
|
|
|
2019-08-18 10:17:09 +02:00
|
|
|
save(path, opts, data, callback, rev) {
|
2016-07-17 13:30:38 +02:00
|
|
|
this._oauthAuthorize(err => {
|
2016-03-27 13:54:35 +02:00
|
|
|
if (err) {
|
|
|
|
return callback && callback(err);
|
|
|
|
}
|
2016-08-14 23:22:25 +02:00
|
|
|
this.logger.debug('Save', path, rev);
|
2017-01-31 07:50:28 +01:00
|
|
|
const ts = this.logger.ts();
|
|
|
|
const url = this._baseUrl + path + ':/content';
|
2016-08-14 23:22:25 +02:00
|
|
|
this._xhr({
|
2019-08-18 10:17:09 +02:00
|
|
|
url,
|
2016-03-27 13:54:35 +02:00
|
|
|
method: 'PUT',
|
|
|
|
responseType: 'json',
|
2016-03-27 19:17:09 +02:00
|
|
|
headers: rev ? { 'If-Match': rev } : null,
|
2020-03-17 19:45:38 +01:00
|
|
|
data,
|
2016-03-27 19:17:09 +02:00
|
|
|
statuses: [200, 201, 412],
|
2016-08-14 23:22:25 +02:00
|
|
|
success: (response, xhr) => {
|
2016-03-27 13:54:35 +02:00
|
|
|
rev = response.eTag;
|
|
|
|
if (!rev) {
|
2016-08-14 23:22:25 +02:00
|
|
|
this.logger.error('Save error', path, 'no eTag', this.logger.ts(ts));
|
2016-03-27 13:54:35 +02:00
|
|
|
return callback && callback('no eTag');
|
|
|
|
}
|
|
|
|
if (xhr.status === 412) {
|
2016-08-14 23:22:25 +02:00
|
|
|
this.logger.debug('Save conflict', path, rev, this.logger.ts(ts));
|
2019-08-18 10:17:09 +02:00
|
|
|
return callback && callback({ revConflict: true }, { rev });
|
2016-03-27 13:54:35 +02:00
|
|
|
}
|
2016-08-14 23:22:25 +02:00
|
|
|
this.logger.debug('Saved', path, rev, this.logger.ts(ts));
|
2019-08-18 10:17:09 +02:00
|
|
|
return callback && callback(null, { rev });
|
2016-03-27 13:54:35 +02:00
|
|
|
},
|
2019-08-16 23:05:39 +02:00
|
|
|
error: err => {
|
2016-08-14 23:22:25 +02:00
|
|
|
this.logger.error('Save error', path, err, this.logger.ts(ts));
|
2016-03-27 13:54:35 +02:00
|
|
|
return callback && callback(err);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
});
|
2019-09-18 21:26:43 +02:00
|
|
|
}
|
2016-03-27 13:54:35 +02:00
|
|
|
|
2019-08-18 10:17:09 +02:00
|
|
|
list(dir, callback) {
|
2016-07-17 13:30:38 +02:00
|
|
|
this._oauthAuthorize(err => {
|
2019-08-16 23:05:39 +02:00
|
|
|
if (err) {
|
|
|
|
return callback && callback(err);
|
|
|
|
}
|
2016-08-14 23:22:25 +02:00
|
|
|
this.logger.debug('List');
|
2017-01-31 07:50:28 +01:00
|
|
|
const ts = this.logger.ts();
|
2018-03-13 19:11:18 +01:00
|
|
|
const url = this._baseUrl + (dir ? `${dir}:/children` : '/drive/root/children');
|
2016-08-14 23:22:25 +02:00
|
|
|
this._xhr({
|
2019-08-18 10:17:09 +02:00
|
|
|
url,
|
2016-03-27 13:54:35 +02:00
|
|
|
responseType: 'json',
|
2019-08-16 23:05:39 +02:00
|
|
|
success: response => {
|
2016-03-27 13:54:35 +02:00
|
|
|
if (!response || !response.value) {
|
2016-08-14 23:22:25 +02:00
|
|
|
this.logger.error('List error', this.logger.ts(ts), response);
|
2016-03-27 13:54:35 +02:00
|
|
|
return callback && callback('list error');
|
|
|
|
}
|
2016-08-14 23:22:25 +02:00
|
|
|
this.logger.debug('Listed', this.logger.ts(ts));
|
2017-01-31 07:50:28 +01:00
|
|
|
const fileList = response.value
|
2017-11-26 21:43:36 +01:00
|
|
|
.filter(f => f.name)
|
2016-07-17 13:30:38 +02:00
|
|
|
.map(f => ({
|
|
|
|
name: f.name,
|
|
|
|
path: f.parentReference.path + '/' + f.name,
|
2017-11-26 21:43:36 +01:00
|
|
|
rev: f.eTag,
|
|
|
|
dir: !!f.folder
|
2016-07-17 13:30:38 +02:00
|
|
|
}));
|
2016-03-27 13:54:35 +02:00
|
|
|
return callback && callback(null, fileList);
|
|
|
|
},
|
2019-08-16 23:05:39 +02:00
|
|
|
error: err => {
|
2016-08-14 23:22:25 +02:00
|
|
|
this.logger.error('List error', this.logger.ts(ts), err);
|
2016-03-27 13:54:35 +02:00
|
|
|
return callback && callback(err);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
});
|
2019-09-18 21:26:43 +02:00
|
|
|
}
|
2016-03-27 13:54:35 +02:00
|
|
|
|
2019-08-18 10:17:09 +02:00
|
|
|
remove(path, callback) {
|
2016-08-14 23:22:25 +02:00
|
|
|
this.logger.debug('Remove', path);
|
2017-01-31 07:50:28 +01:00
|
|
|
const ts = this.logger.ts();
|
|
|
|
const url = this._baseUrl + path;
|
2016-08-14 23:22:25 +02:00
|
|
|
this._xhr({
|
2019-08-18 10:17:09 +02:00
|
|
|
url,
|
2016-03-27 19:17:09 +02:00
|
|
|
method: 'DELETE',
|
|
|
|
responseType: 'json',
|
|
|
|
statuses: [200, 204],
|
2016-08-14 23:22:25 +02:00
|
|
|
success: () => {
|
|
|
|
this.logger.debug('Removed', path, this.logger.ts(ts));
|
2016-03-27 19:17:09 +02:00
|
|
|
return callback && callback();
|
|
|
|
},
|
2019-08-16 23:05:39 +02:00
|
|
|
error: err => {
|
2016-08-14 23:22:25 +02:00
|
|
|
this.logger.error('Remove error', path, err, this.logger.ts(ts));
|
2016-03-27 19:17:09 +02:00
|
|
|
return callback && callback(err);
|
|
|
|
}
|
|
|
|
});
|
2019-09-18 21:26:43 +02:00
|
|
|
}
|
2016-03-27 19:17:09 +02:00
|
|
|
|
2019-08-18 10:17:09 +02:00
|
|
|
mkdir(path, callback) {
|
2016-08-21 17:43:59 +02:00
|
|
|
this._oauthAuthorize(err => {
|
2019-08-16 23:05:39 +02:00
|
|
|
if (err) {
|
|
|
|
return callback && callback(err);
|
|
|
|
}
|
2016-08-21 17:43:59 +02:00
|
|
|
this.logger.debug('Make dir', path);
|
2017-01-31 07:50:28 +01:00
|
|
|
const ts = this.logger.ts();
|
|
|
|
const url = this._baseUrl + '/drive/root/children';
|
|
|
|
const data = JSON.stringify({ name: path.replace('/drive/root:/', ''), folder: {} });
|
2016-08-21 17:43:59 +02:00
|
|
|
this._xhr({
|
2019-08-18 10:17:09 +02:00
|
|
|
url,
|
2016-08-21 17:43:59 +02:00
|
|
|
method: 'POST',
|
|
|
|
responseType: 'json',
|
|
|
|
statuses: [200, 204],
|
2020-03-17 19:45:38 +01:00
|
|
|
data,
|
|
|
|
dataType: 'application/json',
|
2016-08-21 17:43:59 +02:00
|
|
|
success: () => {
|
|
|
|
this.logger.debug('Made dir', path, this.logger.ts(ts));
|
|
|
|
return callback && callback();
|
|
|
|
},
|
2019-08-16 23:05:39 +02:00
|
|
|
error: err => {
|
2016-08-21 17:43:59 +02:00
|
|
|
this.logger.error('Make dir error', path, err, this.logger.ts(ts));
|
|
|
|
return callback && callback(err);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
});
|
2019-09-18 21:26:43 +02:00
|
|
|
}
|
2016-08-21 17:43:59 +02:00
|
|
|
|
2019-08-18 10:17:09 +02:00
|
|
|
setEnabled(enabled) {
|
2016-06-04 17:08:50 +02:00
|
|
|
if (!enabled) {
|
2019-08-16 23:05:39 +02:00
|
|
|
const url = 'https://login.microsoftonline.com/common/oauth2/v2.0/logout?post_logout_redirect_uri={url}'.replace(
|
|
|
|
'{url}',
|
|
|
|
this._getOauthRedirectUrl()
|
|
|
|
);
|
2016-06-04 17:08:50 +02:00
|
|
|
this._oauthRevokeToken(url);
|
|
|
|
}
|
2019-09-18 21:26:43 +02:00
|
|
|
super.setEnabled(enabled);
|
|
|
|
}
|
2016-06-04 17:08:50 +02:00
|
|
|
|
2019-08-18 10:17:09 +02:00
|
|
|
_getClientId() {
|
2019-09-17 19:50:42 +02:00
|
|
|
let clientId = this.appSettings.onedriveClientId;
|
2016-03-27 14:06:27 +02:00
|
|
|
if (!clientId) {
|
2019-08-18 08:05:38 +02:00
|
|
|
clientId =
|
|
|
|
location.origin.indexOf('localhost') >= 0
|
|
|
|
? OneDriveClientId.Local
|
|
|
|
: OneDriveClientId.Production;
|
2016-03-27 14:06:27 +02:00
|
|
|
}
|
|
|
|
return clientId;
|
2019-09-18 21:26:43 +02:00
|
|
|
}
|
2016-03-27 14:06:27 +02:00
|
|
|
|
2019-08-18 10:17:09 +02:00
|
|
|
_getOAuthConfig() {
|
2017-01-31 07:50:28 +01:00
|
|
|
const clientId = this._getClientId();
|
2016-03-27 15:18:05 +02:00
|
|
|
return {
|
2017-11-26 21:43:36 +01:00
|
|
|
url: 'https://login.microsoftonline.com/common/oauth2/v2.0/authorize',
|
|
|
|
scope: 'files.readwrite',
|
2019-08-18 10:17:09 +02:00
|
|
|
clientId,
|
2016-03-27 14:57:22 +02:00
|
|
|
width: 600,
|
|
|
|
height: 500
|
2016-03-27 15:18:05 +02:00
|
|
|
};
|
2019-09-18 21:26:43 +02:00
|
|
|
}
|
2017-12-25 18:58:23 +01:00
|
|
|
|
|
|
|
_popupOpened(popupWindow) {
|
|
|
|
if (popupWindow.webContents) {
|
2019-08-16 23:05:39 +02:00
|
|
|
popupWindow.webContents.on('did-finish-load', e => {
|
2017-12-25 18:58:23 +01:00
|
|
|
const webContents = e.sender.webContents;
|
|
|
|
const url = webContents.getURL();
|
2019-08-18 08:05:38 +02:00
|
|
|
if (
|
|
|
|
url &&
|
|
|
|
url.startsWith('https://login.microsoftonline.com/common/oauth2/v2.0/authorize')
|
|
|
|
) {
|
2017-12-25 18:58:23 +01:00
|
|
|
// click the login button mentioned in #821
|
2019-08-16 23:05:39 +02:00
|
|
|
const script = `const selector = '[role="button"][aria-describedby="tileError loginHeader"]';
|
2017-12-25 18:58:23 +01:00
|
|
|
if (document.querySelectorAll(selector).length === 1) document.querySelector(selector).click()`;
|
2019-09-18 07:08:23 +02:00
|
|
|
webContents.executeJavaScript(script).catch(noop);
|
2017-12-25 18:58:23 +01:00
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
2016-03-13 17:08:25 +01:00
|
|
|
}
|
2019-09-18 21:26:43 +02:00
|
|
|
}
|
2016-03-13 17:08:25 +01:00
|
|
|
|
2019-09-15 14:16:32 +02:00
|
|
|
export { StorageOneDrive };
|