diff --git a/manifest.konnector b/manifest.konnector index d396f94..8269df2 100644 --- a/manifest.konnector +++ b/manifest.konnector @@ -1,13 +1,13 @@ { "version": "1.0.0", - "name": "Connector template", + "name": "Nextcloud", "type": "konnector", "language": "node", "icon": "icon.svg", - "slug": "template", - "source": "git@github.com:konnectors/template.git", + "slug": "nextcloud", + "source": "git@github.com:konnectors/nextcloud.git", "editor": "Cozy", - "vendor_link": "Link to the target website", + "vendor_link": "https://nextcloud.cozycloud.cc", "categories": ["others"], "fields": { "login": { @@ -15,11 +15,15 @@ }, "password": { "type": "password" + }, + "url": { + "type": "text", + "label": "Your nextcloud full url (ex. \"https://nextcloud.cozycloud.cc\")" } }, "folders": [{"defaultDir": "$administrative/$konnector/$account"}], "data_types": [ - "bill" + "contact" ], "screenshots": [], "permissions": { @@ -43,15 +47,9 @@ "langs": ["fr", "en"], "locales": { "fr": { - "short_description": "Récupère la liste de livres", - "long_description": "Récupère la liste de livre sur le site exemple", + "short_description": "Récupère la liste de contacts nextcloud", + "long_description": "Récupère la liste contact nextcloud sur le site.", "permissions": { - "bank operations": { - "description": "Utilisé pour relier les factures à des operations bancaires" - }, - "bills": { - "description": "Utilisé pour sauver les données des factures" - }, "files": { "description": "Utilisé pour sauvegarder les factures" }, @@ -61,15 +59,9 @@ } }, "en": { - "short_description": "Fetch a list of books", - "long_description": "Fetch a list of books from the example website", + "short_description": "Fetch a list of nextcloud contacts", + "long_description": "Fetch a list of nextcloud contacts on the website", "permissions": { - "bank operations": { - "description": "Required to link bank operations to bills" - }, - "bills": { - "description": "Required to save the bills data" - }, "files": { "description": "Required to save the bills" }, @@ -79,6 +71,6 @@ } } }, - "banksTransactionRegExp": "\\bbooks\\b", + "banksTransactionRegExp": "\\bnextcloud\\b", "manifest_version": "2" } diff --git a/package.json b/package.json index 490ccbf..7be73c9 100644 --- a/package.json +++ b/package.json @@ -1,10 +1,10 @@ { - "name": "cozy-konnector-template", + "name": "nextcloud", "version": "1.0.0", "description": "", "repository": { "type": "git", - "url": "git@github.com:konnectors/cozy-konnector-template.git" + "url": "git@github.com:konnectors/nextcloud.git" }, "keywords": [], "author": "Cozy Cloud", @@ -36,7 +36,10 @@ "travisDeployKey": "./bin/generate_travis_deploy_key" }, "dependencies": { - "cozy-konnector-libs": "4.56.4" + "cozy-konnector-libs": "4.56.4", + "cozy-vcard": "^0.2.18", + "webdav": "^4", + "xml2js": "^0.5.0" }, "devDependencies": { "cozy-jobs-cli": "1.20.2", diff --git a/src/index.js b/src/index.js index 1cff2a8..ebfa100 100644 --- a/src/index.js +++ b/src/index.js @@ -1,124 +1,73 @@ -const { - BaseKonnector, - requestFactory, - scrape, - log, - utils -} = require('cozy-konnector-libs') +/* eslint-disable no-unused-vars */ +const { BaseKonnector, requestFactory, log } = require('cozy-konnector-libs') +const { NextcloudClient } = require('./nextcloudClient') +const VCardParser = require('cozy-vcard') + const request = requestFactory({ - // The debug mode shows all the details about HTTP requests and responses. Very useful for - // debugging but very verbose. This is why it is commented out by default // debug: true, - // Activates [cheerio](https://cheerio.js.org/) parsing on each page - cheerio: true, - // If cheerio is activated do not forget to deactivate json parsing (which is activated by - // default in cozy-konnector-libs - json: false, - // This allows request-promise to keep cookies between requests + cheerio: false, + json: true, jar: true }) -const VENDOR = 'template' -const baseUrl = 'http://books.toscrape.com' +const cozyVCardParser = { + parse: data => { + const parser = new VCardParser(data) + return parser.contacts + } +} module.exports = new BaseKonnector(start) -// The start function is run by the BaseKonnector instance only when it got all the account -// information (fields). When you run this connector yourself in "standalone" mode or "dev" mode, -// the account information come from ./konnector-dev-config.json file -// cozyParameters are static parameters, independents from the account. Most often, it can be a -// secret api key. async function start(fields, cozyParameters) { log('info', 'Authenticating ...') if (cozyParameters) log('debug', 'Found COZY_PARAMETERS') - await authenticate.bind(this)(fields.login, fields.password) + const userCookies = await authenticate.bind(this)(fields) log('info', 'Successfully logged in') - // The BaseKonnector instance expects a Promise as return of the function - log('info', 'Fetching the list of documents') - const $ = await request(`${baseUrl}/index.html`) - // cheerio (https://cheerio.js.org/) uses the same api as jQuery (http://jquery.com/) - log('info', 'Parsing list of documents') - const documents = await parseDocuments($) - - // Here we use the saveBills function even if what we fetch are not bills, - // but this is the most common case in connectors - log('info', 'Saving data to Cozy') - await this.saveBills(documents, fields, { - // This is a bank identifier which will be used to link bills to bank operations. These - // identifiers should be at least a word found in the title of a bank operation related to this - // bill. It is not case sensitive. - identifiers: ['books'] - }) + const userContacts = await getUserContacts.bind(this)(fields, userCookies) } -// This shows authentication using the [signin function](https://github.com/konnectors/libs/blob/master/packages/cozy-konnector-libs/docs/api.md#module_signin) -// even if this in another domain here, but it works as an example -function authenticate(username, password) { - return this.signin({ - url: `http://quotes.toscrape.com/login`, - formSelector: 'form', - formData: { username, password }, - // The validate function will check if the login request was a success. Every website has a - // different way to respond: HTTP status code, error message in HTML ($), HTTP redirection - // (fullResponse.request.uri.href)... - validate: (statusCode, $, fullResponse) => { - log( - 'debug', - fullResponse.request.uri.href, - 'not used here but should be useful for other connectors' - ) - // The login in toscrape.com always works except when no password is set - if ($(`a[href='/logout']`).length === 1) { - return true - } else { - // cozy-konnector-libs has its own logging function which format these logs with colors in - // standalone and dev mode and as JSON in production mode - log('error', $('.error').text()) - return false - } - } - }) -} - -// The goal of this function is to parse a HTML page wrapped by a cheerio instance -// and return an array of JS objects which will be saved to the cozy by saveBills -// (https://github.com/konnectors/libs/blob/master/packages/cozy-konnector-libs/docs/api.md#savebills) -function parseDocuments($) { - // You can find documentation about the scrape function here: - // https://github.com/konnectors/libs/blob/master/packages/cozy-konnector-libs/docs/api.md#scrape - const docs = scrape( - $, - { - title: { - sel: 'h3 a', - attr: 'title' - }, - amount: { - sel: '.price_color', - parse: normalizePrice - }, - fileurl: { - sel: 'img', - attr: 'src', - parse: src => `${baseUrl}/${src}` - } +async function authenticate(fields) { + const loginPage = await request(`${fields.url}/login`) + const requestToken = loginPage.match(/data-requesttoken="(.*)">/)[1] + const postLoginPage = await request(`${fields.url}/login`, { + method: 'POST', + form: { + user: fields.login, + password: fields.password, + timezone: 'Europe/Paris', + timezone_offset: 2, + requesttoken: requestToken }, - 'article' - ) - return docs.map(doc => ({ - ...doc, - // The saveBills function needs a date field - // even if it is a little artificial here (these are not real bills) - date: new Date(), - currency: 'EUR', - filename: `${utils.formatDate(new Date())}_${VENDOR}_${doc.amount.toFixed( - 2 - )}EUR${doc.vendorRef ? '_' + doc.vendorRef : ''}.jpg`, - vendor: VENDOR - })) + followRedirect: true, + resolveWithFullResponse: true + }) + if (postLoginPage.request.uri.href !== `${fields.url}/apps/dashboard/`) { + log('warn', 'something went wrong with login') + throw new Error('LOGIN_FAILED') + } + const cookies = postLoginPage.request.headers.cookie.split('; ') + let userNumber + for (const cookie of cookies) { + if (cookie.startsWith('nc_username')) { + userNumber = cookie.split('=')[1] + } + } + return userNumber } -// Convert a price string to a float -function normalizePrice(price) { - return parseFloat(price.replace('£', '').trim()) +async function getUserContacts(fields, userNumber) { + log('info', 'getUserContacts starts') + const ncClient = new NextcloudClient({ fields, userNumber }) + const client = ncClient.createClient() + const contactsVCards = await ncClient.getUserContacts(client) + let parsedContacts = [] + for (const contactVCard of contactsVCards) { + const contact = cozyVCardParser.parse(contactVCard) + if (contact[0].n.match(';;;')) { + contact[0].n = contact[0].n.replace(/;;;/g, '').replace(';', ' ').trim() + } + parsedContacts.push(contact[0]) + } + return parsedContacts } diff --git a/src/nextcloudClient.js b/src/nextcloudClient.js new file mode 100644 index 0000000..5811423 --- /dev/null +++ b/src/nextcloudClient.js @@ -0,0 +1,61 @@ +const { createClient } = require('webdav') +const { log } = require('cozy-konnector-libs') +const xml2js = require('xml2js') + +class NextcloudClient { + constructor({ fields, userNumber }) { + this.fields = fields + this.userNumber = userNumber + } + createClient() { + const client = createClient( + `${this.fields.url}/remote.php/dav/addressbooks/users/${this.userNumber}/default_shared_by_admin/`, + { username: this.fields.login, password: this.fields.password } + ) + return client + } + + async getUserContacts(client) { + log('info', 'getUserContacts starts') + const resultContacts = await this.makeCustomRequest(client, '/') + const contactsHref = [] + xml2js.parseString(resultContacts.data, function (err, result) { + if (err) { + log('warn', err) + return + } + const nodes = result['d:multistatus']['d:response'] + for (const node of nodes) { + contactsHref.push(node['d:href'][0]) + } + }) + // Here we shifting the first element because it's just de base URL + contactsHref.shift() + const fullContactsVCARD = [] + for (const contactHref of contactsHref) { + const contactCode = contactHref.split('admin')[1] + const fullContact = await this.makeCustomRequest(client, contactCode) + fullContactsVCARD.push(fullContact) + } + return fullContactsVCARD + } + + async makeCustomRequest(client, path) { + log('info', 'makeCustomRequest starts') + if (path === '/') { + const result = await client.customRequest('/', { + method: 'PROPFIND', + headers: { + 'Content-Type': 'text/xml' + } + }) + return result + } + const result = await client.getFileContents(path, { + format: 'text' + }) + return result + } +} + +module.exports = { NextcloudClient } diff --git a/src/zombie.js b/src/zombie.js deleted file mode 100644 index 19ac544..0000000 --- a/src/zombie.js +++ /dev/null @@ -1,115 +0,0 @@ -const { - BaseKonnector, - scrape, - saveBills, - log, - utils, - errors -} = require('cozy-konnector-libs') - -const Browser = require('cozy-konnector-libs/dist/libs/CozyBrowser') -const browser = new Browser() -const cheerio = require('cheerio') - -const VENDOR = 'template' -const baseUrl = 'http://books.toscrape.com' - -module.exports = new BaseKonnector(start) - -// The start function is run by the BaseKonnector instance only when it got all the account -// information (fields). When you run this connector yourself in "standalone" mode or "dev" mode, -// the account information come from ./konnector-dev-config.json file -// cozyParameters are static parameters, independents from the account. Most often, it can be a -// secret api key. -async function start(fields, cozyParameters) { - log('info', 'Authenticating ...') - if (cozyParameters) log('debug', 'Found COZY_PARAMETERS') - await authenticate(fields.login, fields.password) - log('info', 'Successfully logged in') - - log('info', 'Fetching the list of documents') - await browser.visit(`${baseUrl}/index.html`) - - const $ = cheerio.load(browser.html()) - log('info', 'Parsing list of documents') - const documents = await parseDocuments($) - - // Here we use the saveBills function even if what we fetch are not bills, - // but this is the most common case in connectors - log('info', 'Saving data to Cozy') - await saveBills(documents, fields, { - // This is a bank identifier which will be used to link bills to bank operations. These - // identifiers should be at least a word found in the title of a bank operation related to this - // bill. It is not case sensitive. - identifiers: ['books'], - sourceAccount: this.accountId, - sourceAccountIdentifier: fields.login - }) -} - -// This shows authentication using the [signin function](https://github.com/konnectors/libs/blob/master/packages/cozy-konnector-libs/docs/api.md#module_signin) -// even if this in another domain here, but it works as an example -async function authenticate(username, password) { - await browser.visit('http://quotes.toscrape.com/login') - await browser.fill('#username', username) - await browser.fill('#password', password) - await browser.pressButton(`[type=submit]`) - - if ( - !browser.redirected || - browser.location._url !== 'http://quotes.toscrape.com/' - ) { - log('error', browser.query('.error').innerHTML) - throw new Error(errors.LOGIN_FAILED) - } -} - -// The goal of this function is to parse a HTML page wrapped by a cheerio instance -// and return an array of JS objects which will be saved to the cozy by saveBills -// (https://github.com/konnectors/libs/blob/master/packages/cozy-konnector-libs/docs/api.md#savebills) -function parseDocuments($) { - // You can find documentation about the scrape function here: - // https://github.com/konnectors/libs/blob/master/packages/cozy-konnector-libs/docs/api.md#scrape - const docs = scrape( - $, - { - title: { - sel: 'h3 a', - attr: 'title' - }, - amount: { - sel: '.price_color', - parse: normalizePrice - }, - fileurl: { - sel: 'img', - attr: 'src', - parse: src => `${baseUrl}/${src}` - } - }, - 'article' - ) - return docs.map(doc => ({ - ...doc, - // The saveBills function needs a date field - // even if it is a little artificial here (these are not real bills) - date: new Date(), - currency: 'EUR', - filename: `${utils.formatDate(new Date())}_${VENDOR}_${doc.amount.toFixed( - 2 - )}EUR${doc.vendorRef ? '_' + doc.vendorRef : ''}.jpg`, - vendor: VENDOR, - metadata: { - // It can be interesting to add the date of import. This is not mandatory but may be - // useful for debugging or data migration - importDate: new Date(), - // Document version, useful for migration after change of document structure - version: 1 - } - })) -} - -// Convert a price string to a float -function normalizePrice(price) { - return parseFloat(price.replace('£', '').trim()) -} diff --git a/yarn.lock b/yarn.lock index 1f7b4c8..b75665d 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1754,6 +1754,14 @@ aws4@^1.8.0: resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.11.0.tgz#d61f46d83b2519250e2784daf5b09479a8b41c59" integrity sha512-xh1Rl34h6Fi1DC2WWKfxUTVqRsNnr6LsKz2+hfwDxQJWmrx8+c7ylaqBMcHfl1U1r2dsifOvKX3LQuLNZ+XSvA== +axios@^0.27.2: + version "0.27.2" + resolved "https://registry.yarnpkg.com/axios/-/axios-0.27.2.tgz#207658cc8621606e586c85db4b41a750e756d972" + integrity sha512-t+yRIyySRTp/wua5xEr+z1q60QmLq8ABsS5O9Me1AsE5dfKqgnCFzwiCZZ/cGNd1lq4/7akDWMxdhVlucjmnOQ== + dependencies: + follow-redirects "^1.14.9" + form-data "^4.0.0" + babel-plugin-dynamic-import-node@^2.3.3: version "2.3.3" resolved "https://registry.yarnpkg.com/babel-plugin-dynamic-import-node/-/babel-plugin-dynamic-import-node-2.3.3.tgz#84fda19c976ec5c6defef57f9427b3def66e17a3" @@ -1817,6 +1825,11 @@ balanced-match@^1.0.0: resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee" integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw== +base-64@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/base-64/-/base-64-1.0.0.tgz#09d0f2084e32a3fd08c2475b973788eee6ae8f4a" + integrity sha512-kwDPIFCGx0NZHog36dj+tHiwP4QMzsZ3AgMViUBKI0+V5n4U0ufTCUMhnQ04diaRI8EX/QcPfql7zlhZ7j4zgg== + basic-auth@~2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/basic-auth/-/basic-auth-2.0.1.tgz#b998279bf47ce38344b4f3cf916d4679bbf51e3a" @@ -1887,6 +1900,13 @@ brace-expansion@^1.1.7: balanced-match "^1.0.0" concat-map "0.0.1" +brace-expansion@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-2.0.1.tgz#1edc459e0f0c548486ecf9fc99f2221364b9a0ae" + integrity sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA== + dependencies: + balanced-match "^1.0.0" + braces@^3.0.2: version "3.0.2" resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.2.tgz#3454e1a462ee8d599e236df336cd9ea4f8afe107" @@ -1924,6 +1944,11 @@ buffer-from@^1.0.0: resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.2.tgz#2b146a6fd72e80b4f55d255f35ed59a3a9a41bd5" integrity sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ== +byte-length@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/byte-length/-/byte-length-1.0.2.tgz#ba5a5909240b0121c079b7f7b15248d6f08223cc" + integrity sha512-ovBpjmsgd/teRmgcPh23d4gJvxDoXtAzEL9xTfMU8Yc2kqCDb7L9jAG0XHl1nzuGl+h3ebCIF1i62UFyA9V/2Q== + bytes@3.1.2: version "3.1.2" resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.1.2.tgz#8b0beeb98605adf1b128fa4386403c009e0221a5" @@ -2099,7 +2124,7 @@ colors@1.0.x: resolved "https://registry.yarnpkg.com/colors/-/colors-1.0.3.tgz#0433f44d809680fdeb60ed260f1b0c262e82a40b" integrity sha512-pFGrxThWcWQ2MsAz6RtgeWe4NK2kUE1WfsrvvlctdII745EW9I0yflqhe7++M5LEc7bV2c/9/5zc8sFcpL0Drw== -combined-stream@^1.0.6, combined-stream@~1.0.6: +combined-stream@^1.0.6, combined-stream@^1.0.8, combined-stream@~1.0.6: version "1.0.8" resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f" integrity sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg== @@ -2426,6 +2451,14 @@ cozy-stack-client@^33.4.0: mime "^2.4.0" qs "^6.7.0" +cozy-vcard@^0.2.18: + version "0.2.18" + resolved "https://registry.yarnpkg.com/cozy-vcard/-/cozy-vcard-0.2.18.tgz#9f4147da175fafca86475ddc7e212b989bdc0542" + integrity sha512-RyhCbfj/74lxvHXG2GrLutHLjNGj+qb5+BgSsu5ptMcFT+G2Ku9BrK3P7kNNPB6hFPGYmmUY/J05dGLh5+lIXg== + dependencies: + quoted-printable "0.2.1" + utf8 "2.0.0" + cross-fetch@^3.0.6: version "3.1.5" resolved "https://registry.yarnpkg.com/cross-fetch/-/cross-fetch-3.1.5.tgz#e1389f44d9e7ba767907f7af8454787952ab534f" @@ -3145,6 +3178,13 @@ fast-levenshtein@^2.0.6: resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917" integrity sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw== +fast-xml-parser@^3.19.0: + version "3.21.1" + resolved "https://registry.yarnpkg.com/fast-xml-parser/-/fast-xml-parser-3.21.1.tgz#152a1d51d445380f7046b304672dd55d15c9e736" + integrity sha512-FTFVjYoBOZTJekiUsawGsSYV9QL0A+zDYCRj7y34IO6Jg+2IMYEtQa+bbictpdpV8dHxXywqU7C0gRDEOFtBFg== + dependencies: + strnum "^1.0.4" + fastest-levenshtein@^1.0.12: version "1.0.14" resolved "https://registry.yarnpkg.com/fastest-levenshtein/-/fastest-levenshtein-1.0.14.tgz#9054384e4b7a78c88d01a4432dc18871af0ac859" @@ -3237,11 +3277,25 @@ flatted@^3.1.0: resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.2.6.tgz#022e9218c637f9f3fc9c35ab9c9193f05add60b2" integrity sha512-0sQoMh9s0BYsm+12Huy/rkKxVu4R1+r96YX5cG44rHV0pQ6iC3Q+mkoMFaGWObMFYQxCVT+ssG1ksneA2MI9KQ== +follow-redirects@^1.14.9: + version "1.15.2" + resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.2.tgz#b460864144ba63f2681096f274c4e57026da2c13" + integrity sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA== + forever-agent@~0.6.1: version "0.6.1" resolved "https://registry.yarnpkg.com/forever-agent/-/forever-agent-0.6.1.tgz#fbc71f0c41adeb37f96c577ad1ed42d8fdacca91" integrity sha512-j0KLYPhm6zeac4lz3oJ3o65qvgQCcPubiyotZrXqEaG4hNagNYO8qdlUrX5vwqv9ohqeT/Z3j6+yW067yWWdUw== +form-data@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/form-data/-/form-data-4.0.0.tgz#93919daeaf361ee529584b9b31664dc12c9fa452" + integrity sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww== + dependencies: + asynckit "^0.4.0" + combined-stream "^1.0.8" + mime-types "^2.1.12" + form-data@~2.3.2: version "2.3.3" resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.3.3.tgz#dcce52c05f644f298c6a7ab936bd724ceffbf3a6" @@ -3483,6 +3537,11 @@ has@^1.0.3: dependencies: function-bind "^1.1.1" +he@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/he/-/he-1.2.0.tgz#84ae65fa7eafb165fddb61566ae14baf05664f0f" + integrity sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw== + highlight.js@^10.7.1: version "10.7.3" resolved "https://registry.yarnpkg.com/highlight.js/-/highlight.js-10.7.3.tgz#697272e3991356e40c3cac566a74eef681756531" @@ -3495,6 +3554,11 @@ hoist-non-react-statics@^3.3.0, hoist-non-react-statics@^3.3.2: dependencies: react-is "^16.7.0" +hot-patcher@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/hot-patcher/-/hot-patcher-1.0.0.tgz#7124d2dc4ca71bcb58b1551603cd13e4fc3fcecd" + integrity sha512-3H8VH0PreeNsKMZw16nTHbUp4YoHCnPlawpsPXGJUR4qENDynl79b6Xk9CIFvLcH1qungBsCuzKcWyzoPPalTw== + htmlparser2@^8.0.1: version "8.0.1" resolved "https://registry.yarnpkg.com/htmlparser2/-/htmlparser2-8.0.1.tgz#abaa985474fcefe269bc761a779b544d7196d010" @@ -3969,6 +4033,11 @@ kind-of@^6.0.2: resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-6.0.3.tgz#07c05034a6c349fa06e24fa35aa76db4580ce4dd" integrity sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw== +layerr@^0.1.2: + version "0.1.2" + resolved "https://registry.yarnpkg.com/layerr/-/layerr-0.1.2.tgz#16c8e7fb042d3595ab15492bdad088f31d7afd15" + integrity sha512-ob5kTd9H3S4GOG2nVXyQhOu9O8nBgP555XxWPkJI0tR0JeRilfyTp8WtPdIJHLXBmHMSdEq5+KMxiYABeScsIQ== + levn@^0.4.1: version "0.4.1" resolved "https://registry.yarnpkg.com/levn/-/levn-0.4.1.tgz#ae4562c007473b932a6200d403268dd2fffc6ade" @@ -4059,7 +4128,7 @@ lru-cache@^6.0.0: dependencies: yallist "^4.0.0" -md5@^2.2.1: +md5@^2.2.1, md5@^2.3.0: version "2.3.0" resolved "https://registry.yarnpkg.com/md5/-/md5-2.3.0.tgz#c3da9a6aae3a30b46b7b0c349b87b110dc3bda4f" integrity sha512-T1GITYmFaKuO91vxyoQMFETst+O71VUPEU3ze5GNzDm0OWdP8v1ziTaAEPUr/3kLsY3Sftgz242A1SetQiDL7g== @@ -4150,6 +4219,13 @@ minimatch@^3.0.4, minimatch@^3.1.1, minimatch@^3.1.2: dependencies: brace-expansion "^1.1.7" +minimatch@^5.1.0: + version "5.1.6" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-5.1.6.tgz#1cfcb8cf5522ea69952cd2af95ae09477f122a96" + integrity sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g== + dependencies: + brace-expansion "^2.0.1" + minimist@^1.1.0, minimist@^1.2.6: version "1.2.6" resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.6.tgz#8637a5b759ea0d6e98702cfb3a9283323c93af44" @@ -4237,6 +4313,11 @@ neo-async@^2.6.2: resolved "https://registry.yarnpkg.com/neo-async/-/neo-async-2.6.2.tgz#b4aafb93e3aeb2d8174ca53cf163ab7d7308305f" integrity sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw== +nested-property@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/nested-property/-/nested-property-4.0.0.tgz#a67b5a31991e701e03cdbaa6453bc5b1011bb88d" + integrity sha512-yFehXNWRs4cM0+dz7QxCd06hTbWbSkV0ISsqBfkntU6TOY4Qm3Q88fRRLOddkGh2Qq6dZvnKVAahfhjcUvLnyA== + nice-try@^1.0.4: version "1.0.5" resolved "https://registry.yarnpkg.com/nice-try/-/nice-try-1.0.5.tgz#a3378a7696ce7d223e88fc9b764bd7ef1089e366" @@ -4567,6 +4648,11 @@ path-parse@^1.0.7: resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735" integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw== +path-posix@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/path-posix/-/path-posix-1.0.0.tgz#06b26113f56beab042545a23bfa88003ccac260f" + integrity sha512-1gJ0WpNIiYcQydgg3Ed8KzvIqTsDpNwq+cjBCssvBtuTWjEqY1AW+i+OepiEMqDCzyro9B2sLAe4RBPajMYFiA== + path-to-regexp@0.1.7: version "0.1.7" resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-0.1.7.tgz#df604178005f522f15eb4490e7247a1bfaa67f8c" @@ -4865,6 +4951,13 @@ queue-microtask@^1.2.2: resolved "https://registry.yarnpkg.com/queue-microtask/-/queue-microtask-1.2.3.tgz#4929228bbc724dfac43e0efb058caf7b6cfb6243" integrity sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A== +quoted-printable@0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/quoted-printable/-/quoted-printable-0.2.1.tgz#448649cc22ea66a1b614d0eb93343c47602004b5" + integrity sha512-Pnirhi0yrZcNgZdpP7AGYWSoSgnA1zCgTtYycqf139+ozAG5f52lU4MnDOnqC10X7rtQOs/pAb297fjSgIioHQ== + dependencies: + utf8 "~2.0.0" + randombytes@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/randombytes/-/randombytes-2.1.0.tgz#df6f84372f0270dc65cdf6291349ab7a473d4f2a" @@ -5168,7 +5261,7 @@ safe-buffer@5.2.1, safe-buffer@^5.0.1, safe-buffer@^5.1.0, safe-buffer@^5.1.2, s resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== -sax@~1.2.4: +sax@>=0.6.0, sax@~1.2.4: version "1.2.4" resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.4.tgz#2816234e2378bddc4e5354fab5caa895df7100d9" integrity sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw== @@ -5479,6 +5572,11 @@ strip-json-comments@3.1.1, strip-json-comments@^3.1.0, strip-json-comments@^3.1. resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006" integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig== +strnum@^1.0.4: + version "1.0.5" + resolved "https://registry.yarnpkg.com/strnum/-/strnum-1.0.5.tgz#5c4e829fe15ad4ff0d20c3db5ac97b73c9b072db" + integrity sha512-J8bbNyKKXl5qYcR36TIO8W3mVGVHrmmxsd5PAItGkmyzwJvybiw2IVq5nqd0i4LSNSkB/sx9VHllbfFdr9k1JA== + strtok3@^6.2.4: version "6.3.0" resolved "https://registry.yarnpkg.com/strtok3/-/strtok3-6.3.0.tgz#358b80ffe6d5d5620e19a073aa78ce947a90f9a0" @@ -5750,7 +5848,12 @@ uri-js@^4.2.2: dependencies: punycode "^2.1.0" -url-parse@^1.4.7: +url-join@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/url-join/-/url-join-4.0.1.tgz#b642e21a2646808ffa178c4c5fda39844e12cde7" + integrity sha512-jk1+QP6ZJqyOiuEI9AEWQfju/nB2Pw466kbA0LEZljHwKeMgd9WrAEgEGxjPDD2+TNbbb37rTyhEfrCXfuKXnA== + +url-parse@^1.4.7, url-parse@^1.5.10: version "1.5.10" resolved "https://registry.yarnpkg.com/url-parse/-/url-parse-1.5.10.tgz#9d3c2f736c1d75dd3bd2be507dcc111f1e2ea9c1" integrity sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ== @@ -5768,6 +5871,11 @@ utf8-byte-length@^1.0.4: resolved "https://registry.yarnpkg.com/utf8-byte-length/-/utf8-byte-length-1.0.4.tgz#f45f150c4c66eee968186505ab93fcbb8ad6bf61" integrity sha512-4+wkEYLBbWxqTahEsWrhxepcoVOJ+1z5PGIjPZxRkytcdSUaNjIjBM7Xn8E+pdSuV7SzvWovBFA54FO0JSoqhA== +utf8@2.0.0, utf8@~2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/utf8/-/utf8-2.0.0.tgz#79ce59eced874809cab9a71fc7102c7d45d4118d" + integrity sha512-jWXHr+bQ8RsWazLzVY3V7XACPTbBHYSg/VoDVok+DBQk5ULm0AuBCNb9tGmjq2H+znnkBFwjhzzCbn9G3xlYcA== + util-deprecate@^1.0.1, util-deprecate@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" @@ -5864,6 +5972,25 @@ watchpack@^2.3.1: glob-to-regexp "^0.4.1" graceful-fs "^4.1.2" +webdav@^4: + version "4.11.2" + resolved "https://registry.yarnpkg.com/webdav/-/webdav-4.11.2.tgz#49cde29e33538bc9f3d3a0a4da0f3d16215f5794" + integrity sha512-Ht9TPD5EB7gYW0YmhRcE5NW0/dn/HQfyLSPQY1Rw1coQ5MQTUooAQ9Bpqt4EU7QLw0b95tX4cU59R+SIojs9KQ== + dependencies: + axios "^0.27.2" + base-64 "^1.0.0" + byte-length "^1.0.2" + fast-xml-parser "^3.19.0" + he "^1.2.0" + hot-patcher "^1.0.0" + layerr "^0.1.2" + md5 "^2.3.0" + minimatch "^5.1.0" + nested-property "^4.0.0" + path-posix "^1.0.0" + url-join "^4.0.1" + url-parse "^1.5.10" + webidl-conversions@^3.0.0: version "3.0.1" resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-3.0.1.tgz#24534275e2a7bc6be7bc86611cc16ae0a5654871" @@ -6004,6 +6131,19 @@ wrappy@1: resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" integrity sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ== +xml2js@^0.5.0: + version "0.5.0" + resolved "https://registry.yarnpkg.com/xml2js/-/xml2js-0.5.0.tgz#d9440631fbb2ed800203fad106f2724f62c493b7" + integrity sha512-drPFnkQJik/O+uPKpqSgr22mpuFHqKdbS835iAQrUC73L2F5WkboIRd63ai/2Yg6I1jzifPFKH2NTK+cfglkIA== + dependencies: + sax ">=0.6.0" + xmlbuilder "~11.0.0" + +xmlbuilder@~11.0.0: + version "11.0.1" + resolved "https://registry.yarnpkg.com/xmlbuilder/-/xmlbuilder-11.0.1.tgz#be9bae1c8a046e76b31127726347d0ad7002beb3" + integrity sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA== + y18n@^5.0.5: version "5.0.8" resolved "https://registry.yarnpkg.com/y18n/-/y18n-5.0.8.tgz#7f4934d0f7ca8c56f95314939ddcd2dd91ce1d55"