diff --git a/CliClient/app/command-sync.js b/CliClient/app/command-sync.js index 88a3cb6f7..5e2d52f4b 100644 --- a/CliClient/app/command-sync.js +++ b/CliClient/app/command-sync.js @@ -11,6 +11,7 @@ const md5 = require('md5'); const locker = require('proper-lockfile'); const fs = require('fs-extra'); const osTmpdir = require('os-tmpdir'); +const SyncTargetRegistry = require('lib/SyncTargetRegistry'); class Command extends BaseCommand { @@ -61,14 +62,28 @@ class Command extends BaseCommand { }); } - async doAuth(syncTargetId) { + async doAuth() { const syncTarget = reg.syncTarget(this.syncTargetId_); - this.oneDriveApiUtils_ = new OneDriveApiNodeUtils(syncTarget.api()); - const auth = await this.oneDriveApiUtils_.oauthDance({ - log: (...s) => { return this.stdout(...s); } - }); - this.oneDriveApiUtils_ = null; - return auth; + const syncTargetMd = SyncTargetRegistry.idToMetadata(this.syncTargetId_); + + if (this.syncTargetId_ === 3 || this.syncTargetId_ === 4) { // OneDrive + this.oneDriveApiUtils_ = new OneDriveApiNodeUtils(syncTarget.api()); + const auth = await this.oneDriveApiUtils_.oauthDance({ + log: (...s) => { return this.stdout(...s); } + }); + this.oneDriveApiUtils_ = null; + + Setting.setValue('sync.' + this.syncTargetId_ + '.auth', auth ? JSON.stringify(auth) : null); + if (!auth) { + this.stdout(_('Authentication was not completed (did not receive an authentication token).')); + return false; + } + + return true; + } + + this.stdout(_('Not authentified with %s. Please provide any missing credentials.', syncTarget.label())); + return false; } cancelAuth() { @@ -120,12 +135,8 @@ class Command extends BaseCommand { app().gui().showConsole(); app().gui().maximizeConsole(); - const auth = await this.doAuth(this.syncTargetId_); - Setting.setValue('sync.' + this.syncTargetId_ + '.auth', auth ? JSON.stringify(auth) : null); - if (!auth) { - this.stdout(_('Authentication was not completed (did not receive an authentication token).')); - return cleanUp(); - } + const authDone = await this.doAuth(); + if (!authDone) return cleanUp(); } const sync = await syncTarget.synchronizer(); diff --git a/CliClient/package-lock.json b/CliClient/package-lock.json index 88372305d..cf0a31e2d 100644 --- a/CliClient/package-lock.json +++ b/CliClient/package-lock.json @@ -85,6 +85,11 @@ "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", "dev": true }, + "base-64": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/base-64/-/base-64-0.1.0.tgz", + "integrity": "sha1-eAqZyE59YAJgNhURxId2E78k9rs=" + }, "bcrypt-pbkdf": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.1.tgz", @@ -1050,6 +1055,11 @@ "strict-uri-encode": "1.1.0" } }, + "querystringify": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-1.0.0.tgz", + "integrity": "sha1-YoYkIRLFtxL6ZU5SZlK/ahP/Bcs=" + }, "readable-stream": { "version": "2.3.3", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.3.tgz", @@ -1104,6 +1114,11 @@ "uuid": "3.1.0" } }, + "requires-port": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", + "integrity": "sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8=" + }, "retry": { "version": "0.10.1", "resolved": "https://registry.npmjs.org/retry/-/retry-0.10.1.tgz", @@ -2100,6 +2115,15 @@ "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.1.tgz", "integrity": "sha1-+nG63UQ3r0wUiEHjs7Fl+enlkLc=" }, + "url-parse": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.2.0.tgz", + "integrity": "sha512-DT1XbYAfmQP65M/mE6OALxmXzZ/z1+e5zk2TcSKe/KiYbNGZxgtttzC0mR/sjopbpOXcbniq7eIKmocJnUWlEw==", + "requires": { + "querystringify": "1.0.0", + "requires-port": "1.0.0" + } + }, "url-to-options": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/url-to-options/-/url-to-options-1.0.1.tgz", diff --git a/CliClient/package.json b/CliClient/package.json index ed0a43f0e..e1a5fea85 100644 --- a/CliClient/package.json +++ b/CliClient/package.json @@ -28,6 +28,7 @@ }, "dependencies": { "app-module-path": "^2.2.0", + "base-64": "^0.1.0", "compare-version": "^0.1.2", "follow-redirects": "^1.2.4", "form-data": "^2.1.4", @@ -57,6 +58,7 @@ "strip-ansi": "^4.0.0", "tcp-port-used": "^0.1.2", "tkwidgets": "^0.5.21", + "url-parse": "^1.2.0", "uuid": "^3.0.1", "word-wrap": "^1.2.3", "xml2js": "^0.4.19", diff --git a/ElectronClient/app/package-lock.json b/ElectronClient/app/package-lock.json index ebcbcbd1a..580967b48 100644 --- a/ElectronClient/app/package-lock.json +++ b/ElectronClient/app/package-lock.json @@ -10,9 +10,17 @@ "integrity": "sha512-+rr4OgeTNrLuJAf09o3USdttEYiXvZshWMkhD6wR9v1ieXH0JM1Q2yT41/cJuJcqiPpSXlM/g3aR+Y5MWQdr0Q==", "dev": true, "requires": { + "7zip-bin-linux": "1.3.1", "7zip-bin-win": "2.1.1" }, "dependencies": { + "7zip-bin-linux": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/7zip-bin-linux/-/7zip-bin-linux-1.3.1.tgz", + "integrity": "sha512-Wv1uEEeHbTiS1+ycpwUxYNuIcyohU6Y6vEqY3NquBkeqy0YhVdsNUGsj0XKSRciHR6LoJSEUuqYUexmws3zH7Q==", + "dev": true, + "optional": true + }, "7zip-bin-win": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/7zip-bin-win/-/7zip-bin-win-2.1.1.tgz", @@ -566,6 +574,11 @@ "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", "dev": true }, + "base-64": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/base-64/-/base-64-0.1.0.tgz", + "integrity": "sha1-eAqZyE59YAJgNhURxId2E78k9rs=" + }, "base64-js": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.2.0.tgz", @@ -3526,6 +3539,11 @@ "strict-uri-encode": "1.1.0" } }, + "querystringify": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-1.0.0.tgz", + "integrity": "sha1-YoYkIRLFtxL6ZU5SZlK/ahP/Bcs=" + }, "rabin-bindings": { "version": "1.7.3", "resolved": "https://registry.npmjs.org/rabin-bindings/-/rabin-bindings-1.7.3.tgz", @@ -3862,6 +3880,11 @@ "integrity": "sha1-l/cXtp1IeE9fUmpsWqj/3aBVpNE=", "dev": true }, + "requires-port": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", + "integrity": "sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8=" + }, "rimraf": { "version": "2.6.2", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.2.tgz", @@ -5194,6 +5217,15 @@ } } }, + "url-parse": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.2.0.tgz", + "integrity": "sha512-DT1XbYAfmQP65M/mE6OALxmXzZ/z1+e5zk2TcSKe/KiYbNGZxgtttzC0mR/sjopbpOXcbniq7eIKmocJnUWlEw==", + "requires": { + "querystringify": "1.0.0", + "requires-port": "1.0.0" + } + }, "url-parse-lax": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/url-parse-lax/-/url-parse-lax-1.0.0.tgz", diff --git a/ElectronClient/app/package.json b/ElectronClient/app/package.json index 315e4b521..6a2db8887 100644 --- a/ElectronClient/app/package.json +++ b/ElectronClient/app/package.json @@ -51,6 +51,7 @@ }, "dependencies": { "app-module-path": "^2.2.0", + "base-64": "^0.1.0", "electron-context-menu": "^0.9.1", "electron-log": "^2.2.11", "electron-updater": "^2.16.1", @@ -84,6 +85,7 @@ "string-padding": "^1.0.2", "string-to-stream": "^1.1.0", "tcp-port-used": "^0.1.2", + "url-parse": "^1.2.0", "uuid": "^3.1.0", "xml2js": "^0.4.19" } diff --git a/ReactNativeClient/lib/JoplinError.js b/ReactNativeClient/lib/JoplinError.js index e4e227382..1287f5f6a 100644 --- a/ReactNativeClient/lib/JoplinError.js +++ b/ReactNativeClient/lib/JoplinError.js @@ -2,11 +2,7 @@ class JoplinError extends Error { constructor(message, code = null) { super(message); - this.code_ = code; - } - - get code() { - return this.code_; + this.code = code; } } diff --git a/ReactNativeClient/lib/SyncTargetNextcloud.js b/ReactNativeClient/lib/SyncTargetNextcloud.js index 1bd8aa854..42606183d 100644 --- a/ReactNativeClient/lib/SyncTargetNextcloud.js +++ b/ReactNativeClient/lib/SyncTargetNextcloud.js @@ -4,7 +4,7 @@ const Setting = require('lib/models/Setting.js'); const { FileApi } = require('lib/file-api.js'); const { Synchronizer } = require('lib/synchronizer.js'); const WebDavApi = require('lib/WebDavApi'); -const { FileApiDriverWebDav } = new require('lib/file-api-driver-webdav'); +const { FileApiDriverWebDav } = require('lib/file-api-driver-webdav'); class SyncTargetNextcloud extends BaseSyncTarget { diff --git a/ReactNativeClient/lib/SyncTargetRegistry.js b/ReactNativeClient/lib/SyncTargetRegistry.js index 9a054d8fc..a9c090e0b 100644 --- a/ReactNativeClient/lib/SyncTargetRegistry.js +++ b/ReactNativeClient/lib/SyncTargetRegistry.js @@ -23,6 +23,14 @@ class SyncTargetRegistry { throw new Error('Name not found: ' + name); } + static idToMetadata(id) { + for (let n in this.reg_) { + if (!this.reg_.hasOwnProperty(n)) continue; + if (this.reg_[n].id === id) return this.reg_[n]; + } + throw new Error('ID not found: ' + id); + } + static idAndLabelPlainObject() { let output = {}; for (let n in this.reg_) { diff --git a/ReactNativeClient/lib/WebDavApi.js b/ReactNativeClient/lib/WebDavApi.js index e6d8dc0e7..cde391f7d 100644 --- a/ReactNativeClient/lib/WebDavApi.js +++ b/ReactNativeClient/lib/WebDavApi.js @@ -2,8 +2,9 @@ const { Logger } = require('lib/logger.js'); const { shim } = require('lib/shim.js'); const parseXmlString = require('xml2js').parseString; const JoplinError = require('lib/JoplinError'); -const urlParser = require("url"); +const URL = require('url-parse'); const { rtrimSlashes, ltrimSlashes } = require('lib/path-utils.js'); +const base64 = require('base-64'); // Note that the d: namespace (the DAV namespace) is specific to Nextcloud. The RFC for example uses "D:" however // we make all the tags and attributes lowercase so we handle both the Nextcloud style and RFC. Hopefully other @@ -16,8 +17,6 @@ class WebDavApi { constructor(options) { this.logger_ = new Logger(); this.options_ = options; - - } setLogger(l) { @@ -30,7 +29,7 @@ class WebDavApi { authToken() { if (!this.options_.username() || !this.options_.password()) return null; - return (new Buffer(this.options_.username() + ':' + this.options_.password())).toString('base64'); + return base64.encode(this.options_.username() + ':' + this.options_.password()); } baseUrl() { @@ -38,8 +37,8 @@ class WebDavApi { } relativeBaseUrl() { - const url = urlParser.parse(this.baseUrl(), true); - return url.path; + const url = new URL(this.baseUrl()); + return url.pathname + url.query; } async xmlToJson(xml) { diff --git a/ReactNativeClient/lib/components/global-style.js b/ReactNativeClient/lib/components/global-style.js index 874cb78bc..c407c99b5 100644 --- a/ReactNativeClient/lib/components/global-style.js +++ b/ReactNativeClient/lib/components/global-style.js @@ -13,7 +13,7 @@ const globalStyle = { fontSizeSmaller: 14, dividerColor: "#dddddd", selectedColor: '#e5e5e5', - disabledOpacity: 0.3, + disabledOpacity: 0.2, raisedBackgroundColor: "#0080EF", raisedColor: "#003363", diff --git a/ReactNativeClient/lib/components/screens/config.js b/ReactNativeClient/lib/components/screens/config.js index b685b8f5f..52db82ff7 100644 --- a/ReactNativeClient/lib/components/screens/config.js +++ b/ReactNativeClient/lib/components/screens/config.js @@ -1,5 +1,5 @@ const React = require('react'); const Component = React.Component; -const { TouchableOpacity, Linking, View, Switch, Slider, StyleSheet, Text, Button, ScrollView } = require('react-native'); +const { TouchableOpacity, Linking, View, Switch, Slider, StyleSheet, Text, Button, ScrollView, TextInput } = require('react-native'); const { connect } = require('react-redux'); const { ScreenHeader } = require('lib/components/screen-header.js'); const { _, setLocale } = require('lib/locale.js'); @@ -17,6 +17,23 @@ class ConfigScreenComponent extends BaseScreenComponent { constructor() { super(); this.styles_ = {}; + + this.state = { + settings: {}, + settingsChanged: false, + }; + + this.saveButton_press = () => { + for (let n in this.state.settings) { + if (!this.state.settings.hasOwnProperty(n)) continue; + Setting.setValue(n, this.state.settings[n]); + } + this.setState({settingsChanged:false}); + }; + } + + componentWillMount() { + this.setState({ settings: this.props.settings }); } styles() { @@ -83,7 +100,14 @@ class ConfigScreenComponent extends BaseScreenComponent { let output = null; const updateSettingValue = (key, value) => { - Setting.setValue(key, value); + const settings = Object.assign({}, this.state.settings); + settings[key] = value; + this.setState({ + settings: settings, + settingsChanged: true, + }); + + console.info(settings['sync.5.path']); } const md = Setting.settingMetadata(key); @@ -135,23 +159,33 @@ class ConfigScreenComponent extends BaseScreenComponent { updateSettingValue(key, value)} /> ); + } else if (md.type == Setting.TYPE_STRING) { + return ( + + {md.label()} + updateSettingValue(key, value)} /> + + ); } else { - //throw new Error('Unsupported setting type: ' + setting.type); + //throw new Error('Unsupported setting type: ' + md.type); } return output; } render() { - const settings = this.props.settings; + const settings = this.state.settings; const keys = Setting.keys(true, 'mobile'); let settingComps = []; for (let i = 0; i < keys.length; i++) { const key = keys[i]; - if (key == 'sync.target' && !settings.showAdvancedOptions) continue; + //if (key == 'sync.target' && !settings.showAdvancedOptions) continue; if (!Setting.isPublic(key)) continue; + const md = Setting.settingMetadata(key); + if (md.show && !md.show(settings)) continue; + const comp = this.settingToComponent(key, settings[key]); if (!comp) continue; settingComps.push(comp); @@ -173,11 +207,14 @@ class ConfigScreenComponent extends BaseScreenComponent { ); - //style={this.styles().body} - return ( - + { settingComps } diff --git a/ReactNativeClient/lib/models/Setting.js b/ReactNativeClient/lib/models/Setting.js index e20e43637..d3066b588 100644 --- a/ReactNativeClient/lib/models/Setting.js +++ b/ReactNativeClient/lib/models/Setting.js @@ -81,8 +81,16 @@ class Setting extends BaseModel { 'sync.target': { value: SyncTargetRegistry.nameToId('onedrive'), type: Setting.TYPE_INT, isEnum: true, public: true, label: () => _('Synchronisation target'), description: () => _('The target to synchonise to. If synchronising with the file system, set `sync.2.path` to specify the target directory.'), options: () => { return SyncTargetRegistry.idAndLabelPlainObject(); }}, - 'sync.2.path': { value: '', type: Setting.TYPE_STRING, show: (settings) => { return settings['sync.target'] == SyncTargetRegistry.nameToId('filesystem') }, public: true, label: () => _('Directory to synchronise with (absolute path)'), description: () => _('The path to synchronise with when file system synchronisation is enabled. See `sync.target`.') }, - 'sync.5.path': { value: '', type: Setting.TYPE_STRING, show: (settings) => { return settings['sync.target'] == SyncTargetRegistry.nameToId('nextcloud') }, public: true, label: () => _('Nexcloud directory URL to synchronise with') }, + + 'sync.2.path': { value: '', type: Setting.TYPE_STRING, show: (settings) => { + try { + return settings['sync.target'] == SyncTargetRegistry.nameToId('filesystem') + } catch (error) { + return false; + } + }, public: true, label: () => _('Directory to synchronise with (absolute path)'), description: () => _('The path to synchronise with when file system synchronisation is enabled. See `sync.target`.') }, + + 'sync.5.path': { value: '', type: Setting.TYPE_STRING, show: (settings) => { return settings['sync.target'] == SyncTargetRegistry.nameToId('nextcloud') }, public: true, label: () => _('Nexcloud WebDAV URL') }, 'sync.5.username': { value: '', type: Setting.TYPE_STRING, show: (settings) => { return settings['sync.target'] == SyncTargetRegistry.nameToId('nextcloud') }, public: true, label: () => _('Nexcloud username') }, 'sync.5.password': { value: '', type: Setting.TYPE_STRING, show: (settings) => { return settings['sync.target'] == SyncTargetRegistry.nameToId('nextcloud') }, public: true, label: () => _('Nexcloud password') }, 'sync.3.auth': { value: '', type: Setting.TYPE_STRING, public: false }, diff --git a/ReactNativeClient/lib/shim-init-react.js b/ReactNativeClient/lib/shim-init-react.js index 695c8221d..65ea0657b 100644 --- a/ReactNativeClient/lib/shim-init-react.js +++ b/ReactNativeClient/lib/shim-init-react.js @@ -28,7 +28,19 @@ function shimInit() { shim.fetch = async function(url, options = null) { return shim.fetchWithRetry(() => { - return shim.nativeFetch_(url, options) + // The native fetch() throws an uncatable error if calling it with an invalid URL + // such as '//.resource' so detect if the URL is valid beforehand and throw + // a catchable error. + + if (typeof url !== 'string') { + console.info('NOT A STRING: ', url); + throw new Error('shim.fetch: URL is not a string'); + } + + const lcUrl = url.toLowerCase(); + if (lcUrl.indexOf('http:') !== 0 && lcUrl.indexOf('https:') !== 0) throw new Error('shim.fetch: Invalid URL: ' + lcUrl); + + return shim.nativeFetch_(url, options); }, options); } diff --git a/ReactNativeClient/package-lock.json b/ReactNativeClient/package-lock.json index 2cb6712ad..76efb49c8 100644 --- a/ReactNativeClient/package-lock.json +++ b/ReactNativeClient/package-lock.json @@ -1143,6 +1143,15 @@ } } }, + "buffer": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.0.8.tgz", + "integrity": "sha512-xXvjQhVNz50v2nPeoOsNqWCLGfiv4ji/gXZM28jnVwdLJxH4mFyqgqCKfaK9zf1KUbG6zTkjLOy7ou+jSMarGA==", + "requires": { + "base64-js": "1.2.1", + "ieee754": "1.1.8" + } + }, "builtin-modules": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-1.1.1.tgz", @@ -1672,6 +1681,11 @@ "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=" }, + "emitter-component": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/emitter-component/-/emitter-component-1.1.1.tgz", + "integrity": "sha1-Bl4tvtaVm/RwZ57avq95gdEAOrY=" + }, "encoding": { "version": "0.1.12", "resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.12.tgz", @@ -1794,6 +1808,11 @@ "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-1.1.1.tgz", "integrity": "sha1-qG5e5r2qFgVEddp5fM3fDFVphJE=" }, + "events": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/events/-/events-1.1.1.tgz", + "integrity": "sha1-nr23Y1rQmccNzEwqH1AEKI6L2SQ=" + }, "exec-sh": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/exec-sh/-/exec-sh-0.2.1.tgz", @@ -2376,6 +2395,11 @@ "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.19.tgz", "integrity": "sha512-oTZqweIP51xaGPI4uPa56/Pri/480R+mo7SeU+YETByQNhDG55ycFyNLIgta9vXhILrxXDmF7ZGhqZIcuN0gJQ==" }, + "ieee754": { + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.8.tgz", + "integrity": "sha1-vjPUCsEO8ZJnAfbwii2G+/0a0+Q=" + }, "image-size": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/image-size/-/image-size-0.6.1.tgz", @@ -4418,6 +4442,11 @@ "strict-uri-encode": "1.1.0" } }, + "querystringify": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-1.0.0.tgz", + "integrity": "sha1-YoYkIRLFtxL6ZU5SZlK/ahP/Bcs=" + }, "random-bytes": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/random-bytes/-/random-bytes-1.0.0.tgz", @@ -5249,6 +5278,11 @@ "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-1.0.1.tgz", "integrity": "sha1-l/cXtp1IeE9fUmpsWqj/3aBVpNE=" }, + "requires-port": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", + "integrity": "sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8=" + }, "resolve": { "version": "1.5.0", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.5.0.tgz", @@ -5632,6 +5666,14 @@ "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.4.0.tgz", "integrity": "sha512-zhSCtt8v2NDrRlPQpCNtw/heZLtfUDqxBM1udqikb/Hbk52LK4nQSwr10u77iopCW5LsyHpuXS0GnEc48mLeew==" }, + "stream": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/stream/-/stream-0.0.2.tgz", + "integrity": "sha1-f1Nj8Ff2WSxVlfALyAon9c7B8O8=", + "requires": { + "emitter-component": "1.1.1" + } + }, "stream-buffers": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/stream-buffers/-/stream-buffers-2.2.0.tgz", @@ -5808,6 +5850,11 @@ "resolved": "https://registry.npmjs.org/time-stamp/-/time-stamp-1.1.0.tgz", "integrity": "sha1-dkpaEa9QVhkhsTPztE5hhofg9cM=" }, + "timers": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/timers/-/timers-0.1.1.tgz", + "integrity": "sha1-hqxceMHuQZaU81pY3k/UGDz7nB4=" + }, "tmp": { "version": "0.0.33", "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", @@ -5954,6 +6001,15 @@ "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=" }, + "url-parse": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.2.0.tgz", + "integrity": "sha512-DT1XbYAfmQP65M/mE6OALxmXzZ/z1+e5zk2TcSKe/KiYbNGZxgtttzC0mR/sjopbpOXcbniq7eIKmocJnUWlEw==", + "requires": { + "querystringify": "1.0.0", + "requires-port": "1.0.0" + } + }, "utf8": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/utf8/-/utf8-2.1.2.tgz", @@ -6194,6 +6250,22 @@ "integrity": "sha1-TYuPHszTQZqjYgYb7O9RXh5VljU=", "dev": true }, + "xml2js": { + "version": "0.4.19", + "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.4.19.tgz", + "integrity": "sha512-esZnJZJOiJR9wWKMyuvSE1y6Dq5LCuJanqhxslH2bxM6duahNZ+HMpCLhBQGZkbX6xRf8x1Y2eJlgt2q3qo49Q==", + "requires": { + "sax": "1.1.6", + "xmlbuilder": "9.0.4" + }, + "dependencies": { + "xmlbuilder": { + "version": "9.0.4", + "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-9.0.4.tgz", + "integrity": "sha1-UZy0ymhtAFqEINNJbz8MruzKWA8=" + } + } + }, "xmlbuilder": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-4.0.0.tgz", diff --git a/ReactNativeClient/package.json b/ReactNativeClient/package.json index d72d79bce..5eb84ad23 100644 --- a/ReactNativeClient/package.json +++ b/ReactNativeClient/package.json @@ -9,6 +9,9 @@ "test": "jest" }, "dependencies": { + "base-64": "^0.1.0", + "buffer": "^5.0.8", + "events": "^1.1.1", "form-data": "^2.1.4", "html-entities": "^1.2.1", "markdown-it": "^8.4.0", @@ -38,8 +41,12 @@ "react-navigation": "^1.0.0-beta.21", "react-redux": "4.4.8", "redux": "3.6.0", + "stream": "0.0.2", + "timers": "^0.1.1", + "url-parse": "^1.2.0", "uuid": "^3.0.1", - "word-wrap": "^1.2.3" + "word-wrap": "^1.2.3", + "xml2js": "^0.4.19" }, "devDependencies": { "babel-jest": "19.0.0", diff --git a/ReactNativeClient/root.js b/ReactNativeClient/root.js index ee7fbd550..81ca7b60d 100644 --- a/ReactNativeClient/root.js +++ b/ReactNativeClient/root.js @@ -51,8 +51,10 @@ const SyncTargetRegistry = require('lib/SyncTargetRegistry.js'); const SyncTargetOneDrive = require('lib/SyncTargetOneDrive.js'); const SyncTargetFilesystem = require('lib/SyncTargetFilesystem.js'); const SyncTargetOneDriveDev = require('lib/SyncTargetOneDriveDev.js'); +const SyncTargetNextcloud = require('lib/SyncTargetNextcloud.js'); SyncTargetRegistry.addClass(SyncTargetOneDrive); SyncTargetRegistry.addClass(SyncTargetOneDriveDev); +SyncTargetRegistry.addClass(SyncTargetNextcloud); // Disabled because not fully working //SyncTargetRegistry.addClass(SyncTargetFilesystem);