diff --git a/.eslintrc.js b/.eslintrc.js index 6c8cbc1ce..5e2334f0a 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -35,7 +35,9 @@ module.exports = { // vendors/bootstrap/bootstrap.native.js 'BSN': "readonly", // Mailvelope - 'mailvelope': "readonly" + 'mailvelope': "readonly", + // Punycode + 'IDN': "readonly" }, // http://eslint.org/docs/rules/ rules: { diff --git a/CHANGELOG.md b/CHANGELOG.md index 26981f208..08c855b4c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,78 @@ -## 2.35.2 – 2024-02-26 +## 2.35.4 – 2024-03-16 + +### Added +- \SnappyMail\IDN::toAscii() + +### Changed +- OpenPGP.js to v5.11.1 +- punycode.js lowercase domain names +- Nextcloud changed stored password handling +- application.ini `login_lowercase` removed and now configurable per domain JSON `lowerLogin` +- Update Portuguese by @ner00 + +### Fixed +- Raise JS TypeEroor "toLowerCase" after update + [#1491](https://github.com/the-djmaze/snappymail/issues/1491) +- Call to undefined function shell_exec + [#1496](https://github.com/the-djmaze/snappymail/issues/1496) +- Download attachments as ZIP doesn't work for PGP encrypted mail + [#1499](https://github.com/the-djmaze/snappymail/issues/1499) +- Importing or downloading a PGP public key attachment from a PGP encrypted message doesn't work + [#1500](https://github.com/the-djmaze/snappymail/issues/1500) +- VCard PHP Notice: Undefined index: ENCODING + + +## 2.35.3 – 2024-03-12 + +### Added +- GnuPG can be disabled +- Missing strings for localization inside identity popup (Cryptography > S/MIME) + [#1458](https://github.com/the-djmaze/snappymail/issues/1458) +- Automatically verify PGP and S/MIME signed messages +- TNEFDecoder for + [#1012](https://github.com/the-djmaze/snappymail/discussions/1012) +- RTF to HTML converter for + [#1012](https://github.com/the-djmaze/snappymail/discussions/1012) +- Polyfill for PHP ctype + [#1250](https://github.com/the-djmaze/snappymail/issues/1250) + +### Changed +- `new Error()` to `Error()` +- Reduce KnockoutJS footprint by removing unused code +- CSS reposition rainloopErrorTip location +- Improved error handling on PGP and S/MIME decrypt +- Improved OpenPGP.js import keys +- Use Identity S/MIME key and certificate from server instead of POST +- application.ini `[webmail]language_admin` to `[admin_panel]language` +- application.ini `[security]admin_panel_host` to `[admin_panel]host` +- application.ini `[security]admin_panel_key` to `[admin_panel]key` +- Drop deprecated Domain::SetConfig() +- Internationalized domain names are now handled as punycode +- Cacher->Get() can now return NULL +- Update French by @hguilbert +- Update Polish by @tinola +- Update Portuguese by @ner00 + +### Fixed +- Handling of Internationalized Domain Names in several areas +- Decrypt error message +- Stalwart ManageSieve Error 352 when getting Filters + [#1455](https://github.com/the-djmaze/snappymail/issues/1455) +- Nextcloud V25+ theme slightly broken + [#1463](https://github.com/the-djmaze/snappymail/issues/1463) +- PGP decryption fails with "Not armored text" + [#1462](https://github.com/the-djmaze/snappymail/issues/1462) +- AUTH_BASIC falling through as AUTH_BEARER; change AUTH_BEARER to a different value + [#1461](https://github.com/the-djmaze/snappymail/issues/1461) +- SetPassword expects \SnappyMail\SensitiveString +- Crash on importing corrupt OpenPGP keys +- Crash on old browsers instead of showing error +- Ignore popups on logoutReload() +- Custom SASLMechanisms fail in IMAP when the connection is secure + [#1484](https://github.com/the-djmaze/snappymail/pull/1484) + + +## 2.35.2 – 2024-02-27 ### Added - GnuPG error handling diff --git a/README.md b/README.md index 1c68bed22..e1f64010e 100644 --- a/README.md +++ b/README.md @@ -140,26 +140,26 @@ RainLoop 1.17 vs SnappyMail |js/* |RainLoop |Snappy | |--------------- |--------: |--------: | -|admin.js |2.170.153 | 82.445 | -|app.js |4.207.787 | 429.565 | -|boot.js | 868.735 | 4.142 | -|libs.js | 658.812 | 192.786 | -|sieve.js | 0 | 84.707 | +|admin.js |2.170.153 | 82.845 | +|app.js |4.207.787 | 430.201 | +|boot.js | 868.735 | 4.147 | +|libs.js | 658.812 | 201.911 | +|sieve.js | 0 | 84.703 | |polyfills.js | 334.608 | 0 | |serviceworker.js | 0 | 285 | -|TOTAL |8.240.095 | 793.930 | +|TOTAL |8.240.095 | 804.092 | |js/min/* |RainLoop |Snappy |RL gzip |SM gzip |RL brotli |SM brotli | |--------------- |--------: |--------: |------: |------: |--------: |--------: | -|admin.min.js | 256.831 | 40.573 | 73.606 | 13.585 | 60.877 | 12.188 | -|app.min.js | 515.367 | 195.013 |139.456 | 66.135 |110.485 | 56.623 | -|boot.min.js | 84.659 | 2.084 | 26.998 | 1.202 | 23.643 | 1.003 | -|libs.min.js | 584.772 | 92.746 |180.901 | 34.452 |155.182 | 30.890 | +|admin.min.js | 256.831 | 40.733 | 73.606 | 13.649 | 60.877 | 12.228 | +|app.min.js | 515.367 | 195.260 |139.456 | 66.275 |110.485 | 56.682 | +|boot.min.js | 84.659 | 2.087 | 26.998 | 1.204 | 23.643 | 1.002 | +|libs.min.js | 584.772 | 92.482 |180.901 | 34.729 |155.182 | 31.017 | |sieve.min.js | 0 | 41.164 | 0 | 10.365 | 0 | 9.359 | |polyfills.min.js | 32.837 | 0 | 11.406 | 0 | 10.175 | 0 | -|TOTAL user |1.217.635 | 289.843 |358.761 |101.789 |299.485 | 88.516 | -|TOTAL user+sieve |1.217.635 | 331.007 |358.761 |112.154 |299.485 | 97.875 | -|TOTAL admin | 959.099 | 135.403 |292.911 | 49.239 |249.877 | 44.081 | +|TOTAL user |1.217.635 | 289.829 |358.761 |102.208 |299.485 | 88.701 | +|TOTAL user+sieve |1.217.635 | 330.993 |358.761 |112.573 |299.485 | 98.060 | +|TOTAL admin | 959.099 | 135.302 |292.911 | 49.582 |249.877 | 44.247 | For a user it is around 69% smaller and faster than traditional RainLoop. @@ -188,12 +188,12 @@ For a user it is around 69% smaller and faster than traditional RainLoop. |css/* |RainLoop |Snappy |RL gzip |SM gzip |SM brotli | |------------ |-------: |------: |------: |------: |--------: | -|app.css | 340.331 | 84.472 | 46.946 | 17.622 | 15.112 | -|app.min.css | 274.947 | 67.857 | 39.647 | 15.537 | 13.549 | +|app.css | 340.331 | 84.531 | 46.946 | 17.627 | 15.111 | +|app.min.css | 274.947 | 67.906 | 39.647 | 15.544 | 13.567 | |boot.css | | 1.326 | | 664 | 545 | |boot.min.css | | 1.071 | | 590 | 474 | -|admin.css | | 30.576 | | 7.013 | 6.096 | -|admin.min.css | | 24.692 | | 6.336 | 5.579 | +|admin.css | | 30.573 | | 7.011 | 6.101 | +|admin.min.css | | 24.689 | | 6.334 | 5.575 | ### PGP RainLoop uses the old OpenPGP.js v2 @@ -207,7 +207,7 @@ See https://github.com/the-djmaze/openpgpjs for development |OpenPGP |RainLoop |Snappy |RL gzip |SM gzip |RL brotli |SM brotli | |--------------- |--------: |--------: |------: |-------: |--------: |--------: | -|openpgp.min.js | 330.742 | 540.792 |102.388 | 167.971 | 84.241 | 138.010 | +|openpgp.min.js | 330.742 | 546.309 |102.388 | 169.249 | 84.241 | 138.751 | |openpgp.worker | 1.499 | | 824 | | 695 | | diff --git a/dev/App/Abstract.js b/dev/App/Abstract.js index 113c7dbc7..1e545bbf9 100644 --- a/dev/App/Abstract.js +++ b/dev/App/Abstract.js @@ -3,6 +3,8 @@ import ko from 'ko'; import { logoutLink } from 'Common/Links'; import { i18nToNodes, initOnStartOrLangChange } from 'Common/Translator'; +import { arePopupsVisible } from 'Knoin/Knoin'; + import { LanguageStore } from 'Stores/Language'; import { initThemes } from 'Stores/Theme'; @@ -18,6 +20,7 @@ export class AbstractApp { } logoutReload(url) { + arePopupsVisible(false); url = url || logoutLink(); if (location.href !== url) { setTimeout(() => location.href = url, 100); diff --git a/dev/App/Admin.js b/dev/App/Admin.js index fb970bffc..91440875c 100644 --- a/dev/App/Admin.js +++ b/dev/App/Admin.js @@ -1,6 +1,6 @@ import 'External/ko'; -import { Settings, SettingsGet } from 'Common/Globals'; +import { SettingsGet, SettingsAdmin } from 'Common/Globals'; import { initThemes } from 'Stores/Theme'; import Remote from 'Remote/Admin/Fetch'; @@ -11,6 +11,8 @@ import { LoginAdminScreen } from 'Screen/Admin/Login'; import { startScreens } from 'Knoin/Knoin'; import { AbstractApp } from 'App/Abstract'; +import { AskPopupView } from 'View/Popup/Ask'; + export class AdminApp extends AbstractApp { constructor() { super(Remote); @@ -23,7 +25,8 @@ export class AdminApp extends AbstractApp { } start() { - if (!Settings.app('adminAllowed')) { +// if (!Settings.app('adminAllowed')) { + if (!SettingsAdmin('allowed')) { rl.route.root(); setTimeout(() => location.href = '/', 1); } else if (SettingsGet('Auth')) { @@ -34,3 +37,16 @@ export class AdminApp extends AbstractApp { } } } + +AskPopupView.credentials = function(sAskDesc, btnText) { + return new Promise(resolve => { + this.showModal([ + sAskDesc, + view => resolve({username:view.username(), password:view.passphrase()}), + () => resolve(null), + true, + 3, + btnText + ]); + }); +}; diff --git a/dev/App/User.js b/dev/App/User.js index 21d7b588b..5e8d94e3b 100644 --- a/dev/App/User.js +++ b/dev/App/User.js @@ -86,6 +86,8 @@ export class AppUser extends AbstractApp { this.folderList = FolderUserStore.folderList; this.messageList = MessagelistUserStore; + + this.ask = AskPopupView; } /** @@ -248,3 +250,46 @@ export class AppUser extends AbstractApp { showScreenPopup(ComposePopupView, params); } } + +AskPopupView.password = function(sAskDesc, btnText) { + return new Promise(resolve => { + this.showModal([ + sAskDesc, + view => resolve({password:view.passphrase(), remember:view.remember()}), + () => resolve(null), + true, + 5, + btnText + ]); + }); +}; + +AskPopupView.cryptkey = () => new Promise(resolve => { + const fn = () => AskPopupView.showModal([ + i18n('CRYPTO/ASK_CRYPTKEY_PASS'), + view => { + let pass = view.passphrase(); + if (pass) { + Remote.post('ResealCryptKey', null, { + passphrase: pass + }).then(response => { + resolve(response?.Result); + }).catch(e => { + if (111 === e.code) { + fn(); + } else { + console.error(e); + resolve(null); + } + }); + } else { + resolve(null); + } + }, + () => resolve(null), + true, + 1, + i18n('CRYPTO/DECRYPT') + ]); + fn(); +}); diff --git a/dev/Common/Enums.js b/dev/Common/Enums.js index 028d2a700..cb3df4817 100644 --- a/dev/Common/Enums.js +++ b/dev/Common/Enums.js @@ -50,6 +50,7 @@ Notifications = { ConnectionError: 104, DomainNotAllowed: 109, AccountNotAllowed: 110, + CryptKeyError: 111, ContactsSyncError: 140, @@ -95,7 +96,6 @@ Notifications = { JsonParse: 952, // JsonTimeout: 953, - UnknownNotification: 998, UnknownError: 999, // Admin diff --git a/dev/Common/File.js b/dev/Common/File.js index 532f9ffb2..841554056 100644 --- a/dev/Common/File.js +++ b/dev/Common/File.js @@ -138,7 +138,7 @@ export const FileInfo = { getContentType: fileName => { fileName = lowerCase(fileName); if ('winmail.dat' === fileName) { - return app + 'ms-tnef'; + return app + 'vnd.ms-tnef'; } let ext = fileName.split('.').pop(); if (/^(txt|text|def|list|in|ini|log|sql|cfg|conf)$/.test(ext)) diff --git a/dev/Common/Globals.js b/dev/Common/Globals.js index 4d86df9dc..105660c31 100644 --- a/dev/Common/Globals.js +++ b/dev/Common/Globals.js @@ -15,6 +15,7 @@ export const Settings = rl.settings, SettingsGet = Settings.get, + SettingsAdmin = name => (SettingsGet('Admin') || {})[name], SettingsCapa = name => name && !!(SettingsGet('Capa') || {})[name], dropdowns = [], diff --git a/dev/Common/HtmlEditor.js b/dev/Common/HtmlEditor.js index 0bbfdbdea..06555b478 100644 --- a/dev/Common/HtmlEditor.js +++ b/dev/Common/HtmlEditor.js @@ -26,10 +26,8 @@ export class HtmlEditor { if (element) { onReady = onReady ? [onReady] : []; this.onReady = fn => onReady.push(fn); - // TODO: make 'which' user configurable const which = SettingsUserStore.editorWysiwyg(), wysiwyg = WYSIWYGS.find(item => which == item.name) || WYSIWYGS.find(item => 'Squire' == item.name); -// const wysiwyg = WYSIWYGS.find(item => 'Squire' == item.name); wysiwyg.construct(this, element, editor => setTimeout(()=>{ this.editor = editor; editor.on('blur', () => this.blurTrigger()); diff --git a/dev/Common/Links.js b/dev/Common/Links.js index 525169078..f8847b95f 100644 --- a/dev/Common/Links.js +++ b/dev/Common/Links.js @@ -1,13 +1,13 @@ import { pInt } from 'Common/Utils'; -import { doc, Settings } from 'Common/Globals'; +import { doc, Settings, SettingsAdmin } from 'Common/Globals'; const BASE = doc.location.pathname.replace(/\/+$/,'') + '/', HASH_PREFIX = '#/', - adminPath = () => rl.adminArea() && !Settings.app('adminHost'), + adminPath = () => rl.adminArea() && !SettingsAdmin('host'), - prefix = () => BASE + '?' + (adminPath() ? Settings.app('adminPath') : ''); + prefix = () => BASE + '?' + (adminPath() ? SettingsAdmin('path') : ''); export const SUB_QUERY_PREFIX = '&q[]=', diff --git a/dev/Common/Translator.js b/dev/Common/Translator.js index 0b609a99d..4d1111a64 100644 --- a/dev/Common/Translator.js +++ b/dev/Common/Translator.js @@ -22,7 +22,7 @@ const getNotificationMessage = code => { let key = getKeyByValue(Notifications, code); - return key ? I18N_DATA.NOTIFICATIONS[i18nKey(key).replace('_NOTIFICATION', '_ERROR')] : ''; + return key ? I18N_DATA.NOTIFICATIONS[key] : ''; }, fromNow = date => relativeTime(Math.round((date.getTime() - Date.now()) / 1000)); @@ -211,7 +211,7 @@ export const script.remove(); resolve(); }; - script.onerror = () => reject(new Error('Language '+language+' failed')); + script.onerror = () => reject(Error('Language '+language+' failed')); script.src = langLink(language, admin); // script.async = true; doc.head.append(script); diff --git a/dev/DAV/JCard.js b/dev/DAV/JCard.js index 56c247e67..1dad7d8c8 100644 --- a/dev/DAV/JCard.js +++ b/dev/DAV/JCard.js @@ -15,7 +15,7 @@ export class JCard { if (input) { // read from jCard if (typeof input !== 'object') { - throw new Error('error reading vcard') + throw Error('error reading vcard') } this.parseFromJCard(input) } @@ -87,7 +87,7 @@ export class JCard { arg = new VCardProperty(String(arg), value, params, type); } if (!(arg instanceof VCardProperty)) { - throw new Error('invalid argument of VCard.set(), expects string arguments or a VCardProperty'); + throw Error('invalid argument of VCard.set(), expects string arguments or a VCardProperty'); } let field = arg.getField(); this.props.set(field, [arg]); @@ -101,7 +101,7 @@ export class JCard { arg = new VCardProperty(String(arg), value, params, type); } if (!(arg instanceof VCardProperty)) { - throw new Error('invalid argument of VCard.add(), expects string arguments or a VCardProperty'); + throw Error('invalid argument of VCard.add(), expects string arguments or a VCardProperty'); } // VCardProperty arguments let field = arg.getField(); @@ -125,14 +125,14 @@ export class JCard { else if (arg instanceof VCardProperty) { let propArray = this.props.get(arg.getField()); if (!(propArray === null || propArray === void 0 ? void 0 : propArray.includes(arg))) - throw new Error("Attempted to remove VCardProperty VCard does not have: ".concat(arg)); + throw Error("Attempted to remove VCardProperty VCard does not have: ".concat(arg)); propArray.splice(propArray.indexOf(arg), 1); if (propArray.length === 0) this.props.delete(arg.getField()); } // incorrect arguments else - throw new Error('invalid argument of VCard.remove(), expects ' + + throw Error('invalid argument of VCard.remove(), expects ' + 'string and optional param filter or a VCardProperty'); } @@ -202,7 +202,7 @@ export class JCard { parseFullName(options) { let n = this.getOne('n'); if (n === undefined) { - throw new Error('\'fn\' VCardProperty not present in card, cannot parse full name'); + throw Error('\'fn\' VCardProperty not present in card, cannot parse full name'); } let fnString = ''; // Position in n -> position in fn diff --git a/dev/DAV/VCardProperty.js b/dev/DAV/VCardProperty.js index 59c290e67..30fd8813e 100644 --- a/dev/DAV/VCardProperty.js +++ b/dev/DAV/VCardProperty.js @@ -55,7 +55,7 @@ export class VCardProperty { } // invalid property else { - throw new Error('invalid Property constructor'); + throw Error('invalid Property constructor'); } } diff --git a/dev/External/ko.js b/dev/External/ko.js index 53425caa9..0d693148d 100644 --- a/dev/External/ko.js +++ b/dev/External/ko.js @@ -66,8 +66,7 @@ Object.assign(ko.bindingHandlers, { }, update: (element, fValueAccessor) => { let value = ko.unwrap(fValueAccessor()); - value = isFunction(value) ? value() : value; - errorTip(element, value); + errorTip(element, isFunction(value) ? value() : value); } }, @@ -107,7 +106,7 @@ Object.assign(ko.bindingHandlers, { const command = fValueAccessor(); if (!command || !command.canExecute) { - throw new Error('Value should be a command'); + throw Error('Value should be a command'); } ko.bindingHandlers['FORM'==element.nodeName ? 'submit' : 'click'].init( diff --git a/dev/Knoin/AbstractModel.js b/dev/Knoin/AbstractModel.js index 724cf3dce..0fb0b2d90 100644 --- a/dev/Knoin/AbstractModel.js +++ b/dev/Knoin/AbstractModel.js @@ -27,7 +27,7 @@ export class AbstractModel { constructor() { /* if (new.target === AbstractModel) { - throw new Error("Can't instantiate AbstractModel!"); + throw Error("Can't instantiate AbstractModel!"); } */ Object.defineProperty(this, 'disposables', {value: []}); diff --git a/dev/Mime/Utils.js b/dev/Mime/Utils.js index 1092298f3..014660cf3 100644 --- a/dev/Mime/Utils.js +++ b/dev/Mime/Utils.js @@ -80,6 +80,12 @@ export function MimeToMessage(data, message) detached: true }); } + } else if ('application/pkcs7-mime' === type.value /*&& 'signed-data' === type.params['smime-type']=*/) { + message.smimeSigned({ + micAlg: type.micalg, + bodyPart: part, + detached: false + }); } }); diff --git a/dev/Model/AbstractCollection.js b/dev/Model/AbstractCollection.js index 35c70413c..e4a9d0910 100644 --- a/dev/Model/AbstractCollection.js +++ b/dev/Model/AbstractCollection.js @@ -5,7 +5,7 @@ export class AbstractCollectionModel extends Array constructor() { /* if (new.target === AbstractCollectionModel) { - throw new Error("Can't instantiate AbstractCollectionModel!"); + throw Error("Can't instantiate AbstractCollectionModel!"); } */ super(); diff --git a/dev/Model/Account.js b/dev/Model/Account.js index d5bc71296..2df0ea725 100644 --- a/dev/Model/Account.js +++ b/dev/Model/Account.js @@ -28,6 +28,10 @@ export class AccountModel extends AbstractModel { && setTimeout(()=>this.fetchUnread(), (Math.ceil(Math.random() * 10)) * 3000); } + label() { + return this.name || IDN.toUnicode(this.email); + } + /** * Get INBOX unread messages */ diff --git a/dev/Model/Attachment.js b/dev/Model/Attachment.js index 834f2264b..93fbc42f4 100644 --- a/dev/Model/Attachment.js +++ b/dev/Model/Attachment.js @@ -126,7 +126,10 @@ export class AttachmentModel extends AbstractModel { } get download() { - return b64EncodeJSONSafe({ + return b64EncodeJSONSafe(this.url ? { + fileName: this.fileName, + data: this.url.replace(/^.+,/, '') + } : { folder: this.folder, uid: this.uid, mimeIndex: this.mimeIndex, diff --git a/dev/Model/FolderCollection.js b/dev/Model/FolderCollection.js index 0e15fee68..df2cdcdb9 100644 --- a/dev/Model/FolderCollection.js +++ b/dev/Model/FolderCollection.js @@ -3,9 +3,8 @@ import { AbstractCollectionModel } from 'Model/AbstractCollection'; import { UNUSED_OPTION_VALUE } from 'Common/Consts'; import { isArray, getKeyByValue, forEachObjectEntry, b64EncodeJSONSafe } from 'Common/Utils'; import { ClientSideKeyNameExpandedFolders, FolderType, FolderMetadataKeys } from 'Common/EnumsUser'; -import { clearCache, getFolderFromCacheList, setFolder, setFolderInboxName, removeFolderFromCacheList } from 'Common/Cache'; +import { clearCache, getFolderFromCacheList, setFolder, setFolderInboxName } from 'Common/Cache'; import { Settings, SettingsGet, fireEvent } from 'Common/Globals'; -import { Notifications } from 'Common/Enums'; import * as Local from 'Storage/Client'; @@ -15,7 +14,7 @@ import { MessagelistUserStore } from 'Stores/User/Messagelist'; import { SettingsUserStore } from 'Stores/User/Settings'; import { sortFolders } from 'Common/Folders'; -import { i18n, translateTrigger, getNotification } from 'Common/Translator'; +import { i18n, translateTrigger } from 'Common/Translator'; import { AbstractModel } from 'Knoin/AbstractModel'; @@ -485,41 +484,6 @@ export class FolderModel extends AbstractModel { showScreenPopup(FolderPopupView, [this]); } - rename(nameToEdit, parentName) { - nameToEdit = nameToEdit.trim(); - const folder = this, - parentFolder = getFolderFromCacheList(parentName), - oldFullname = folder.fullName, - newFullname = (parentFolder ? (parentName + parentFolder.delimiter) : '') + nameToEdit; - if (nameToEdit && newFullname != oldFullname) { - Remote.abort('Folders').post('FolderRename', FolderUserStore.foldersRenaming, { - oldName: oldFullname, - newName: newFullname, - subscribe: folder.isSubscribed() ? 1 : 0 - }) - .then(() => { - folder.fullName = newFullname; - folder.name(nameToEdit); - if (folder.subFolders.length || folder.parentName != parentName) { - Remote.setTrigger(FolderUserStore.foldersLoading, true); -// clearTimeout(Remote.foldersTimeout); -// Remote.foldersTimeout = setTimeout(loadFolders, 500); - setTimeout(loadFolders, 500); - // TODO: rename all subfolders with folder.delimiter to prevent reload? - } else { - removeFolderFromCacheList(folder.fullName); - setFolder(folder); - sortFolders(parentFolder ? parentFolder.subFolders : FolderUserStore.folderList); - } - }) - .catch(error => { - FolderUserStore.error( - getNotification(error.code, '', Notifications.CantRenameFolder) - + '.\n' + error.message); - }); - } - } - /** * For url safe '/#/mailbox/...' path */ diff --git a/dev/Model/Identity.js b/dev/Model/Identity.js index ac46a15fe..89beda875 100644 --- a/dev/Model/Identity.js +++ b/dev/Model/Identity.js @@ -32,9 +32,9 @@ export class IdentityModel extends AbstractModel { }); addComputablesTo(this, { - smimeKeyEncrypted: () => this.smimeKey().includes('-----BEGIN ENCRYPTED PRIVATE KEY-----') -// smimeKeyValid: () => this.smimeKeyEncrypted() | this.smimeKey().includes('-----BEGIN PRIVATE KEY-----') -// smimeCertificateValid: () => this.smimeKey().includes('-----BEGIN CERTIFICATE-----') + smimeKeyEncrypted: () => this.smimeKey().includes('-----BEGIN ENCRYPTED PRIVATE KEY-----'), + smimeKeyValid: () => /^-----BEGIN (ENCRYPTED )?PRIVATE KEY-----/.test(this.smimeKey()), + smimeCertificateValid: () => /^-----BEGIN CERTIFICATE-----/.test(this.smimeCertificate()) }); } diff --git a/dev/Model/Message.js b/dev/Model/Message.js index 0767fc072..ae2f3687e 100644 --- a/dev/Model/Message.js +++ b/dev/Model/Message.js @@ -120,12 +120,10 @@ export class MessageModel extends AbstractModel { encrypted: false, pgpSigned: null, - pgpVerified: null, pgpEncrypted: null, pgpDecrypted: false, smimeSigned: null, - smimeVerified: null, smimeEncrypted: null, smimeDecrypted: false, @@ -199,10 +197,9 @@ export class MessageModel extends AbstractModel { } }); - this.smimeSigned.subscribe(value => { - value?.body && MimeToMessage(value.body, this); - value?.body && this.smimeVerified(value.verified); - }); + this.smimeSigned.subscribe(value => + value?.body && MimeToMessage(value.body, this) + ); } get requestHash() { @@ -423,6 +420,11 @@ export class MessageModel extends AbstractModel { return this.viewBody(false); } + swapColors() { + const cl = this.body?.classList; + cl && cl.toggle('swapColors'); + } + /** * @param {boolean=} print = false */ diff --git a/dev/Remote/AbstractFetch.js b/dev/Remote/AbstractFetch.js index c0f4b4f59..0c2aad30e 100644 --- a/dev/Remote/AbstractFetch.js +++ b/dev/Remote/AbstractFetch.js @@ -19,7 +19,6 @@ checkResponseError = data => { Notifications.DomainNotAllowed, Notifications.AccountNotAllowed, Notifications.MailServerError, - Notifications.UnknownNotification, Notifications.UnknownError ].includes(err) ) { @@ -133,7 +132,7 @@ export class AbstractFetchRemote fetchJSON(sAction, getURL(sGetAdd), sGetAdd ? null : (params || {}), undefined === iTimeout ? 30000 : pInt(iTimeout), - data => { + async data => { let iError = 0; if (data) { /* @@ -149,6 +148,10 @@ export class AbstractFetchRemote } } + if (111 === iError && rl.app.ask && await rl.app.ask.cryptkey()) { + return this.request(sAction, fCallback, params, iTimeout, sGetAdd); + } + fCallback && fCallback( iError, data, @@ -186,12 +189,16 @@ export class AbstractFetchRemote post(action, fTrigger, params, timeOut) { this.setTrigger(fTrigger, true); return fetchJSON(action, getURL(), params || {}, pInt(timeOut, 30000), - data => { + async data => { abort(action, 0, 1); if (!data) { return Promise.reject(new FetchError(Notifications.JsonParse)); } + + if (111 === data?.ErrorCode && rl.app.ask && await rl.app.ask.cryptkey()) { + return this.post(action, fTrigger, params, timeOut); + } /* let isCached = false, type = ''; if (data?.epoch) { diff --git a/dev/Settings/Admin/Config.js b/dev/Settings/Admin/Config.js index 4c415adc1..9aa954e1f 100644 --- a/dev/Settings/Admin/Config.js +++ b/dev/Settings/Admin/Config.js @@ -2,6 +2,9 @@ import ko from 'ko'; import Remote from 'Remote/Admin/Fetch'; import { forEachObjectEntry } from 'Common/Utils'; +import { SettingsAdmin } from 'Common/Globals'; +import { LanguageStore } from 'Stores/Language'; +import { ThemeStore } from 'Stores/Theme'; export class AdminSettingsConfig /*extends AbstractViewSettings*/ { @@ -53,6 +56,11 @@ export class AdminSettingsConfig /*extends AbstractViewSettings*/ { items: [] }; forEachObjectEntry(items, (skey, item) => { + if ('language' === skey) { + item[2] = ('webmail' === key) ? LanguageStore.languages : SettingsAdmin('languages'); + } else if ('theme' === skey) { + item[2] = ThemeStore.themes; + } 'admin_password' === skey || section.items.push({ key: `config[${key}][${skey}]`, diff --git a/dev/Settings/Admin/General.js b/dev/Settings/Admin/General.js index 611b244ba..143d30464 100644 --- a/dev/Settings/Admin/General.js +++ b/dev/Settings/Admin/General.js @@ -1,13 +1,9 @@ import ko from 'ko'; -import { - isArray -} from 'Common/Utils'; - import { addObservablesTo, addSubscribablesTo, addComputablesTo } from 'External/ko'; import { SaveSettingStatus } from 'Common/Enums'; -import { Settings, SettingsGet, SettingsCapa } from 'Common/Globals'; +import { SettingsAdmin, SettingsGet, SettingsCapa } from 'Common/Globals'; import { translatorReload, convertLangName } from 'Common/Translator'; import { AbstractViewSettings } from 'Knoin/AbstractViews'; @@ -24,11 +20,7 @@ export class AdminSettingsGeneral extends AbstractViewSettings { super(); this.language = LanguageStore.language; - this.languages = LanguageStore.languages; - - const aLanguagesAdmin = Settings.app('languagesAdmin'); - this.languagesAdmin = ko.observableArray(isArray(aLanguagesAdmin) ? aLanguagesAdmin : []); - this.languageAdmin = ko.observable(SettingsGet('languageAdmin')); + this.languageAdmin = ko.observable(SettingsAdmin('language')); this.theme = ThemeStore.theme; this.themes = ThemeStore.themes; @@ -107,14 +99,18 @@ export class AdminSettingsGeneral extends AbstractViewSettings { } selectLanguage() { - showScreenPopup(LanguagesPopupView, [this.language, this.languages(), LanguageStore.userLanguage()]); + showScreenPopup(LanguagesPopupView, [ + this.language, + LanguageStore.languages, + LanguageStore.userLanguage() + ]); } selectLanguageAdmin() { showScreenPopup(LanguagesPopupView, [ this.languageAdmin, - this.languagesAdmin(), - SettingsGet('languageUsers') + SettingsAdmin('languages'), + SettingsAdmin('clientLanguage') ]); } } diff --git a/dev/Settings/Admin/Security.js b/dev/Settings/Admin/Security.js index e3797b1a5..862fe4a0f 100644 --- a/dev/Settings/Admin/Security.js +++ b/dev/Settings/Admin/Security.js @@ -10,7 +10,7 @@ export class AdminSettingsSecurity extends AbstractViewSettings { constructor() { super(); - this.addSettings(['useLocalProxyForExternalImages']); + this.addSettings(['useLocalProxyForExternalImages', 'autoVerifySignatures']); this.weakPassword = rl.app.weakPassword; @@ -28,6 +28,7 @@ export class AdminSettingsSecurity extends AbstractViewSettings { viewQRCode: '', + capaGnuPG: SettingsCapa('GnuPG'), capaOpenPGP: SettingsCapa('OpenPGP') }); @@ -65,7 +66,8 @@ export class AdminSettingsSecurity extends AbstractViewSettings { adminPasswordNew2: reset, - capaOpenPGP: value => Remote.saveSetting('CapaOpenPGP', value) + capaGnuPG: value => Remote.saveSetting('capaGnuPG', value), + capaOpenPGP: value => Remote.saveSetting('capaOpenPGP', value) }); this.adminTOTP(SettingsGet('adminTOTP')); @@ -75,6 +77,16 @@ export class AdminSettingsSecurity extends AbstractViewSettings { }); } + generateTOTP() { + let CHARS = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ234567', + length = 16, + secret = ''; + while (0 < length--) { + secret += CHARS[Math.floor(Math.random() * 32)]; + } + this.adminTOTP(secret); + } + saveAdminUserCommand() { if (!this.adminLogin().trim()) { this.adminLoginError(true); diff --git a/dev/Settings/User/Security.js b/dev/Settings/User/Security.js index f73cfa882..bbb8e9dd5 100644 --- a/dev/Settings/User/Security.js +++ b/dev/Settings/User/Security.js @@ -63,11 +63,7 @@ export class UserSettingsSecurity extends AbstractViewSettings { importToOpenPGP() { OpenPGPUserStore.isSupported() && Remote.request('GetPGPKeys', - (iError, oData) => { - if (!iError && oData.Result) { - oData.Result.forEach(key => OpenPGPUserStore.importKey(key)); - } - } + (iError, oData) => !iError && oData.Result && OpenPGPUserStore.importKeys(oData.Result) ); } diff --git a/dev/Sieve/Model/Abstract.js b/dev/Sieve/Model/Abstract.js index 84c534ee0..3130fd175 100644 --- a/dev/Sieve/Model/Abstract.js +++ b/dev/Sieve/Model/Abstract.js @@ -22,7 +22,7 @@ export class AbstractModel { constructor() { /* if (new.target === AbstractModel) { - throw new Error("Can't instantiate AbstractModel!"); + throw Error("Can't instantiate AbstractModel!"); } */ Object.defineProperty(this, 'disposables', {value: []}); diff --git a/dev/Stores/Admin/Domain.js b/dev/Stores/Admin/Domain.js index abd599491..ba09daeab 100644 --- a/dev/Stores/Admin/Domain.js +++ b/dev/Stores/Admin/Domain.js @@ -13,6 +13,7 @@ DomainAdminStore.fetch = () => { if (!iError) { DomainAdminStore( data.Result.map(item => { + item.name = IDN.toUnicode(item.name); item.disabled = ko.observable(item.disabled); item.askDelete = ko.observable(false); return item; diff --git a/dev/Stores/Language.js b/dev/Stores/Language.js index df27841e7..aeeb716e3 100644 --- a/dev/Stores/Language.js +++ b/dev/Stores/Language.js @@ -12,7 +12,7 @@ export const LanguageStore = { const aLanguages = Settings.app('languages'); this.languages(isArray(aLanguages) ? aLanguages : []); this.language(SettingsGet('language')); - this.userLanguage(SettingsGet('userLanguage')); + this.userLanguage(SettingsGet('clientLanguage')); this.hourCycle(SettingsGet('hourCycle')); } } diff --git a/dev/Stores/User/GnuPG.js b/dev/Stores/User/GnuPG.js index e390162d5..174686d9d 100644 --- a/dev/Stores/User/GnuPG.js +++ b/dev/Stores/User/GnuPG.js @@ -17,7 +17,7 @@ const keys.find(key => // key[sign ? 'can_sign' : 'can_decrypt'] (key.can_sign || key.can_decrypt) - && (key.emails.includes(query) || key.subkeys.find(key => query == key.keyid || query == key.fingerprint)) + && (key.for(query) || key.subkeys.find(key => query == key.keyid || query == key.fingerprint)) ); export const GnuPGUserStore = new class { @@ -46,6 +46,7 @@ export const GnuPGUserStore = new class { key.fingerprint = key.subkeys[0].fingerprint; key.uids.forEach(uid => uid.email && aEmails.push(uid.email)); key.emails = aEmails; + key.for = email => aEmails.includes(IDN.toASCII(email)); key.askDelete = ko.observable(false); key.openForDeletion = ko.observable(null).askDeleteHelper(); key.remove = () => { @@ -69,7 +70,7 @@ export const GnuPGUserStore = new class { } }; if (isPrivate) { - key.password = async (btnTxt = 'CRYPTO/SIGN') => { + key.password = async btnTxt => { const pass = await Passphrases.ask(key, 'GnuPG key
' + key.id + ' ' + key.emails[0], btnTxt @@ -97,6 +98,7 @@ export const GnuPGUserStore = new class { } } catch (e) { Passphrases.delete(key); + alert(e.message); } } return key.armor; @@ -148,7 +150,7 @@ export const GnuPGUserStore = new class { const count = recipients.length, length = count ? recipients.filter(email => // (key.can_verify || key.can_encrypt) && - this.publicKeys.find(key => key.emails.includes(email)) + this.publicKeys.find(key => key.for(email)) ).length : 0; return length && length === count; } @@ -156,7 +158,7 @@ export const GnuPGUserStore = new class { getPublicKeyFingerprints(recipients) { const fingerprints = []; recipients.forEach(email => { - fingerprints.push(this.publicKeys.find(key => key.emails.includes(email)).fingerprint); + fingerprints.push(this.publicKeys.find(key => key.for(email)).fingerprint); }); return fingerprints; } @@ -204,7 +206,7 @@ export const GnuPGUserStore = new class { } async verify(message) { - let data = message.pgpSigned(); // { bodyPartId: "1", sigPartId: "2", micAlg: "pgp-sha256" } + let data = message.pgpSigned(); // { partId: "1", sigPartId: "2", micAlg: "pgp-sha256" } if (data) { data = { ...data }; // clone // const sender = message.from[0].email; @@ -227,7 +229,7 @@ export const GnuPGUserStore = new class { } async sign(privateKey) { - return await privateKey.password(); + return await privateKey.password('CRYPTO/SIGN'); } }; diff --git a/dev/Stores/User/OpenPGP.js b/dev/Stores/User/OpenPGP.js index 1a637f311..b923c3de0 100644 --- a/dev/Stores/User/OpenPGP.js +++ b/dev/Stores/User/OpenPGP.js @@ -16,7 +16,7 @@ import { Passphrases } from 'Storage/Passphrases'; const findOpenPGPKey = (keys, query/*, sign*/) => keys.find(key => - key.emails.includes(query) || query == key.id || query == key.fingerprint + key.for(query) || query == key.id || query == key.fingerprint ), decryptKey = async (privateKey, btnTxt = 'SIGN') => { @@ -59,9 +59,11 @@ const let keys = [], key, armoredKeys = JSON.parse(storage.getItem(itemname)), i = arrayLength(armoredKeys); - while (i--) { + while (i--) try { key = await openpgp.readKey({armoredKey:armoredKeys[i]}); key.err || keys.push(new OpenPgpKeyModel(armoredKeys[i], key)); + } catch (e) { + console.error(e); } return keys; }, @@ -77,15 +79,11 @@ const class OpenPgpKeyModel { constructor(armor, key) { this.key = key; - const aEmails = []; - if (key.users) { - key.users.forEach(user => user.userID.email && aEmails.push(user.userID.email)); - } this.id = key.getKeyID().toHex().toUpperCase(); this.fingerprint = key.getFingerprint(); this.can_encrypt = !!key.getEncryptionKey(); this.can_sign = !!key.getSigningKey(); - this.emails = aEmails; + this.emails = key.users.map(user => IDN.toASCII(user.userID.email)).filter(email => email); this.armor = armor; this.askDelete = ko.observable(false); this.openForDeletion = ko.observable(null).askDeleteHelper(); @@ -93,6 +91,18 @@ class OpenPgpKeyModel { // key.getPrimaryUser() } +/* + get id() { return this.key.getKeyID().toHex().toUpperCase(); } + get fingerprint() { return this.key.getFingerprint(); } + get can_encrypt() { return !!this.key.getEncryptionKey(); } + get can_sign() { return !!this.key.getSigningKey(); } + get emails() { return this.key.users.map(user => IDN.toASCII(user.userID.email)).filter(email => email); } + get armor() { return this.key.armor(); } +*/ + for(email) { + return this.emails.includes(IDN.toASCII(email)); + } + view() { showScreenPopup(OpenPgpKeyPopupView, [this]); } @@ -142,18 +152,29 @@ export const OpenPGPUserStore = new class { } importKey(armoredKey) { - window.openpgp && openpgp.readKey({armoredKey:armoredKey}).then(key => { - if (!key.err) { - key = new OpenPgpKeyModel(armoredKey, key); - const isPrivate = key.key.isPrivate(), - keys = isPrivate ? this.privateKeys : this.publicKeys; - if (!keys.find(entry => entry.fingerprint == key.fingerprint)) { - keys.push(key); - keys(sort(keys)); - storeOpenPgpKeys(keys, isPrivate ? privateKeysItem : publicKeysItem); + this.importKeys([armoredKey]); + } + + async importKeys(keys) { + if (window.openpgp) { + const privateKeys = this.privateKeys(), + publicKeys = this.publicKeys(); + for (const armoredKey of keys) try { + let key = await openpgp.readKey({armoredKey:armoredKey}); + if (!key.err) { + key = new OpenPgpKeyModel(armoredKey, key); + const keys = key.key.isPrivate() ? privateKeys : publicKeys; + keys.find(entry => entry.fingerprint == key.fingerprint) + || keys.push(key); } + } catch (e) { + console.error(e, armoredKey); } - }); + this.privateKeys(sort(privateKeys)); + this.publicKeys(sort(publicKeys)); + storeOpenPgpKeys(privateKeys, privateKeysItem); + storeOpenPgpKeys(publicKeys, publicKeysItem); + } } /** @@ -180,7 +201,7 @@ export const OpenPGPUserStore = new class { hasPublicKeyForEmails(recipients) { const count = recipients.length, length = count ? recipients.filter(email => - this.publicKeys().find(key => key.emails.includes(email)) + this.publicKeys().find(key => key.for(email)) ).length : 0; return length && length === count; } @@ -229,8 +250,8 @@ export const OpenPGPUserStore = new class { * https://docs.openpgpjs.org/#sign-and-verify-cleartext-messages */ async verify(message) { - const data = message.pgpSigned(), // { bodyPartId: "1", sigPartId: "2", micAlg: "pgp-sha256" } - publicKey = this.publicKeys().find(key => key.emails.includes(message.from[0].email)); + const data = message.pgpSigned(), // { partId: "1", sigPartId: "2", micAlg: "pgp-sha256" } + publicKey = this.publicKeys().find(key => key.for(message.from[0].email)); if (data && publicKey) { data.folder = message.folder; data.uid = message.uid; @@ -289,7 +310,7 @@ export const OpenPGPUserStore = new class { */ async encrypt(text, recipients, signPrivateKey) { const count = recipients.length; - recipients = recipients.map(email => this.publicKeys().find(key => key.emails.includes(email))).filter(key => key); + recipients = recipients.map(email => this.publicKeys().find(key => key.for(email))).filter(key => key); if (count === recipients.length) { if (signPrivateKey) { signPrivateKey = await decryptKey(signPrivateKey); diff --git a/dev/Stores/User/Pgp.js b/dev/Stores/User/Pgp.js index 7eb788464..8b56a83ca 100644 --- a/dev/Stores/User/Pgp.js +++ b/dev/Stores/User/Pgp.js @@ -108,19 +108,14 @@ export const } async verify(message) { - const signed = message.pgpSigned(); + const signed = message.pgpSigned(), + sender = message.from[0].email; if (signed) { - const sender = message.from[0].email, - gnupg = GnuPGUserStore.hasPublicKeyForEmails([sender]), - openpgp = OpenPGPUserStore.hasPublicKeyForEmails([sender]); - // Detached signature use GnuPG first, else we must download whole message - if (gnupg && signed.sigPartId) { - return GnuPGUserStore.verify(message); - } - if (openpgp) { + // OpenPGP only when inline, else we must download the whole message + if (!signed.sigPartId && OpenPGPUserStore.hasPublicKeyForEmails([sender])) { return OpenPGPUserStore.verify(message); } - if (gnupg) { + if (GnuPGUserStore.hasPublicKeyForEmails([sender])) { return GnuPGUserStore.verify(message); } // Mailvelope can't @@ -133,12 +128,12 @@ export const let result = {}; recipients.forEach(email => { OpenPGPUserStore.publicKeys().forEach(key => { - if (key.emails.includes(email)) { + if (key.for(email)) { result[email] = key.armor; } }); GnuPGUserStore.publicKeys.map(async key => { - if (!result[email] && key.emails.includes(email)) { + if (!result[email] && key.for(email)) { result[email] = await key.fetch(); } }); diff --git a/dev/Styles/User/MessageView.less b/dev/Styles/User/MessageView.less index 082cc6ac9..dd7d970c2 100644 --- a/dev/Styles/User/MessageView.less +++ b/dev/Styles/User/MessageView.less @@ -331,6 +331,11 @@ padding: 10px; position: relative; + &.swapColors { + background-color: var(--main-color, currentcolor); + color: var(--main-bg-color); + } + * { box-sizing: unset; /* unicode-bidi: plaintext;*/ diff --git a/dev/Styles/_End.less b/dev/Styles/_End.less index 70cad8162..5ac36e0b7 100644 --- a/dev/Styles/_End.less +++ b/dev/Styles/_End.less @@ -9,7 +9,7 @@ color: red; content: attr(data-rainloopErrorTip); font-size: 90%; - left: 100%; + left: 0; opacity: 1; padding: 0.25em; position : absolute; diff --git a/dev/Styles/rtl.less b/dev/Styles/rtl.less index 3f4be8920..86e27bf3e 100644 --- a/dev/Styles/rtl.less +++ b/dev/Styles/rtl.less @@ -123,4 +123,8 @@ html[dir="rtl"] { border-right: 6px solid #eee; } } + + [data-rainloopErrorTip]::before { + left: 100%; + } } diff --git a/dev/View/Popup/Ask.js b/dev/View/Popup/Ask.js index f7704faf8..8d7c7bca0 100644 --- a/dev/View/Popup/Ask.js +++ b/dev/View/Popup/Ask.js @@ -85,29 +85,3 @@ export class AskPopupView extends AbstractViewPopup { }); } } - -AskPopupView.password = function(sAskDesc, btnText) { - return new Promise(resolve => { - this.showModal([ - sAskDesc, - view => resolve({password:view.passphrase(), remember:view.remember()}), - () => resolve(null), - true, - 5, - btnText - ]); - }); -} - -AskPopupView.credentials = function(sAskDesc, btnText) { - return new Promise(resolve => { - this.showModal([ - sAskDesc, - view => resolve({username:view.username(), password:view.passphrase(), remember:view.remember()}), - () => resolve(null), - true, - 3, - btnText - ]); - }); -} diff --git a/dev/View/Popup/Compose.js b/dev/View/Popup/Compose.js index 2fd0e23bd..cb0010873 100644 --- a/dev/View/Popup/Compose.js +++ b/dev/View/Popup/Compose.js @@ -1377,7 +1377,7 @@ export class ComposePopupView extends AbstractViewPopup { key && options.push(['OpenPGP', key]); key = GnuPGUserStore.getPrivateKeyFor(email, 1); key && options.push(['GnuPG', key]); - identity.smimeKey() && identity.smimeCertificate() && identity.email() === email + identity.smimeKeyValid() && identity.smimeCertificateValid() && identity.email() === email && options.push(['S/MIME']); console.dir({signOptions: options}); this.signOptions(options); @@ -1561,12 +1561,13 @@ export class ComposePopupView extends AbstractViewPopup { } if ('S/MIME' == signOptions[i][0]) { // TODO: sign in PHP fails - params.signCertificate = identity.smimeCertificate(); - params.signPrivateKey = identity.smimeKey(); + params.sign = 'S/MIME'; +// params.signCertificate = identity.smimeCertificate(); +// params.signPrivateKey = identity.smimeKey(); if (identity.smimeKeyEncrypted()) { const pass = await Passphrases.ask(identity, i18n('SMIME/PRIVATE_KEY_OF', {EMAIL: identity.email()}), - 'CRYPTO/DECRYPT' + 'CRYPTO/SIGN' ); if (null != pass) { params.signPassphrase = pass.password; diff --git a/dev/View/Popup/Domain.js b/dev/View/Popup/Domain.js index 0bdc82c80..9a64d7716 100644 --- a/dev/View/Popup/Domain.js +++ b/dev/View/Popup/Domain.js @@ -26,6 +26,7 @@ const imapType: 0, imapTimeout: 300, imapShortLogin: false, + imapLowerLogin: true, // SSL imapSslVerify_peer: false, imapSslAllow_self_signed: false, @@ -50,6 +51,7 @@ const smtpType: 0, smtpTimeout: 60, smtpShortLogin: false, + smtpLowerLogin: true, smtpUseAuth: true, smtpSetSender: false, smtpAuthPlainLine: false, @@ -69,6 +71,7 @@ const secure: pInt(oDomain.imapType()), timeout: oDomain.imapTimeout, shortLogin: !!oDomain.imapShortLogin(), + lowerLogin: !!oDomain.imapLowerLogin(), ssl: { verify_peer: !!oDomain.imapSslVerify_peer(), verify_peer_name: !!oDomain.imapSslVerify_peer(), @@ -92,6 +95,7 @@ const secure: pInt(oDomain.smtpType()), timeout: oDomain.smtpTimeout, shortLogin: !!oDomain.smtpShortLogin(), + lowerLogin: !!oDomain.smtpLowerLogin(), ssl: { verify_peer: !!oDomain.smtpSslVerify_peer(), verify_peer_name: !!oDomain.smtpSslVerify_peer(), @@ -109,6 +113,7 @@ const secure: pInt(oDomain.sieveType()), timeout: oDomain.sieveTimeout, shortLogin: !!oDomain.imapShortLogin(), + lowerLogin: !!oDomain.imapLowerLogin(), ssl: { verify_peer: !!oDomain.imapSslVerify_peer(), verify_peer_name: !!oDomain.imapSslVerify_peer(), @@ -365,6 +370,8 @@ export class DomainPopupView extends AbstractViewPopup { this[key]?.(value); } }); + this.name(IDN.toUnicode(this.name())); + this.aliasName(IDN.toUnicode(this.aliasName())); this.imapCapabilities(this.imapCapabilities.concat(this.imapDisabled_capabilities()).unique()); this.enableSmartPorts(true); } diff --git a/dev/View/Popup/Folder.js b/dev/View/Popup/Folder.js index 221d4172c..4dcdb0442 100644 --- a/dev/View/Popup/Folder.js +++ b/dev/View/Popup/Folder.js @@ -3,8 +3,12 @@ import { addObservablesTo, koComputable } from 'External/ko'; import Remote from 'Remote/User/Fetch'; import { FolderUserStore } from 'Stores/User/Folder'; +import { getFolderFromCacheList, setFolder, removeFolderFromCacheList } from 'Common/Cache'; +import { Notifications } from 'Common/Enums'; +import { FolderMetadataKeys } from 'Common/EnumsUser'; +import { folderListOptionsBuilder, sortFolders } from 'Common/Folders'; +import { initOnStartOrLangChange, i18n, getNotification } from 'Common/Translator'; import { defaultOptionsAfterRender } from 'Common/Utils'; -import { folderListOptionsBuilder } from 'Common/Folders'; export class FolderPopupView extends AbstractViewPopup { constructor() { @@ -28,6 +32,24 @@ export class FolderPopupView extends AbstractViewPopup { ) ); + this.displaySpecSetting = FolderUserStore.displaySpecSetting; + + this.showKolab = FolderUserStore.allowKolab(); + this.kolabTypeOptions = ko.observableArray(); + let i18nFilter = key => i18n('SETTINGS_FOLDERS/TYPE_' + key); + initOnStartOrLangChange(()=>{ + this.kolabTypeOptions([ + { id: '', name: '' }, + { id: 'event', name: i18nFilter('CALENDAR') }, + { id: 'contact', name: i18nFilter('CONTACTS') }, + { id: 'task', name: i18nFilter('TASKS') }, + { id: 'note', name: i18nFilter('NOTES') }, + { id: 'file', name: i18nFilter('FILES') }, + { id: 'journal', name: i18nFilter('JOURNAL') }, + { id: 'configuration', name: i18nFilter('CONFIGURATION') } + ]); + }); + this.defaultOptionsAfterRender = defaultOptionsAfterRender; } @@ -35,9 +57,81 @@ export class FolderPopupView extends AbstractViewPopup { this.editing(false); } - submitForm(form) { - this.folder().rename(this.name(), this.parentFolder()); - console.dir({form}); + submitForm(/*form*/) { + const + folder = this.folder(), + nameToEdit = this.name().trim(), + newParentName = this.parentFolder(), + oldParent = getFolderFromCacheList(folder.parentName), + newParent = getFolderFromCacheList(newParentName), + folderList = FolderUserStore.folderList, + newFolderList = newParent ? newParent.subFolders : folderList, + delimiter = (newParent || folder).delimiter, + oldFullname = folder.fullName, + newFullname = (newParent ? newParentName + delimiter : '') + nameToEdit; + if (nameToEdit && newFullname != oldFullname) { + Remote.abort('Folders').post('FolderRename', FolderUserStore.foldersRenaming, { + oldName: oldFullname, + newName: newFullname, + // toggleFolderSubscription / FolderSubscribe + subscribe: folder.isSubscribed() ? 1 : 0, + // toggleFolderCheckable / FolderCheckable + checkable: folder.checkable() ? 1 : 0, + // toggleFolderKolabType / FolderSetMetadata + kolab: { + // TODO: append '.default' ? + type: FolderMetadataKeys.KolabFolderType, + value: folder.kolabType() + } + }) + .then(() => { + const + renameFolder = (folder, parent) => { + removeFolderFromCacheList(folder.fullName); + folder.parentName = (parent ? parent.fullName : ''); + folder.fullName = (parent ? parent.fullName + delimiter : '') + folder.name(); + folder.delimiter = delimiter; + folder.deep = (parent ? parent.deep : -1) + 1; + setFolder(folder); + }, + renameChildren = folder => { + folder.subFolders.forEach(child => { + renameFolder(child, folder); + renameChildren(child); + }) + }; + folder.name(nameToEdit); + renameFolder(folder, newParent); + if (folder.subFolders.length || newParent != oldParent) { + // Rename all subfolders to prevent reload + renameChildren(folder); + } + (oldParent ? oldParent.subFolders : folderList).remove(folder); + newFolderList.push(folder); + sortFolders(newFolderList); + }) + .catch(error => { + console.error(error); + FolderUserStore.error( + getNotification(error.code, '', Notifications.CantRenameFolder) + '.\n' + error.message + ); + }); + } else { + Remote.request('FolderSettings', null, { + folder: folder.fullName, + // toggleFolderSubscription / FolderSubscribe + subscribe: folder.isSubscribed() ? 1 : 0, + // toggleFolderCheckable / FolderCheckable + checkable: folder.checkable() ? 1 : 0, + // toggleFolderKolabType / FolderSetMetadata + kolab: { + // TODO: append '.default' ? + type: FolderMetadataKeys.KolabFolderType, + value: folder.kolabType() + } + }); + } + this.close(); } diff --git a/dev/View/Popup/OpenPgpGenerate.js b/dev/View/Popup/OpenPgpGenerate.js index cf75ecb39..a2ec51515 100644 --- a/dev/View/Popup/OpenPgpGenerate.js +++ b/dev/View/Popup/OpenPgpGenerate.js @@ -43,7 +43,7 @@ export class OpenPgpGeneratePopupView extends AbstractViewPopup { const type = this.keyType().toLowerCase(), userId = { name: this.name(), - email: this.email() + email: IDN.toASCII(this.email()) }, cfg = { type: type, diff --git a/dev/View/User/Login.js b/dev/View/User/Login.js index 044813049..78f9b7bb0 100644 --- a/dev/View/User/Login.js +++ b/dev/View/User/Login.js @@ -120,7 +120,7 @@ export class LoginUserView extends AbstractViewLogin { iError = Notifications.AuthError; } this.submitError(getNotification(iError, oData?.ErrorMessage, - Notifications.UnknownNotification)); + Notifications.UnknownError)); this.submitErrorAdditional(oData?.ErrorMessageAdditional); } else { rl.setData(oData.Result); @@ -138,25 +138,20 @@ export class LoginUserView extends AbstractViewLogin { onBuild(dom) { super.onBuild(dom); - const signMe = (SettingsGet('signMe') || '').toLowerCase(); - + let signMe = SettingsGet('signMe'); switch (signMe) { - case 'defaultoff': - case 'defaulton': - this.signMeType( - 'defaulton' === signMe ? SignMeOn : SignMeOff - ); - + case SignMeOff: + case SignMeOn: switch (Local.get(ClientSideKeyNameLastSignMe)) { case '-1-': - this.signMeType(SignMeOn); + signMe = SignMeOn; break; case '-0-': - this.signMeType(SignMeOff); + signMe = SignMeOff; break; // no default } - + this.signMeType(signMe); break; default: this.signMeType(SignMeUnused); diff --git a/dev/View/User/MailBox/MessageView.js b/dev/View/User/MailBox/MessageView.js index 70c113131..bb31475fc 100644 --- a/dev/View/User/MailBox/MessageView.js +++ b/dev/View/User/MailBox/MessageView.js @@ -146,7 +146,7 @@ export class MailMessageView extends AbstractViewRight { downloadAsZipAllowed: () => this.attachmentsActions.includes('zip') && (currentMessage()?.attachments || []) - .filter(item => item?.download /*&& !item?.isLinked()*/ && item?.checked()) + .filter(item => item?.checked() && item?.download /*&& !item?.isLinked()*/) .length, tagsAllowed: () => FolderUserStore.currentFolder()?.tagsAllowed(), @@ -570,29 +570,31 @@ export class MailMessageView extends AbstractViewRight { } pgpDecrypt() { - const oMessage = currentMessage(); + const oMessage = currentMessage(), + data = oMessage.pgpEncrypted(); + delete data.error; PgpUserStore.decrypt(oMessage).then(result => { - if (result) { - oMessage.pgpDecrypted(true); - if (result.data) { - MimeToMessage(result.data, oMessage); - oMessage.html() ? oMessage.viewHtml() : oMessage.viewPlain(); - if (result.signatures?.length) { - oMessage.pgpSigned(true); - oMessage.pgpVerified({ - signatures: result.signatures, - success: !!result.signatures.length - }); - } - } - } else { + if (!result) { // TODO: translate - throw 'Decryption failed, canceled or not possible'; + throw Error('Decryption failed, canceled or not possible'); + } + oMessage.pgpDecrypted(true); + if (result.data) { + MimeToMessage(result.data, oMessage); + oMessage.html() ? oMessage.viewHtml() : oMessage.viewPlain(); + if (result.signatures?.length) { + oMessage.pgpSigned({ + signatures: result.signatures, + success: !!result.signatures.length + }); + } } }) .catch(e => { - console.error(e) - alert(e.message); + data.error = e.message; + }) + .finally(() => { + oMessage.pgpEncrypted(data); }); } @@ -600,7 +602,7 @@ export class MailMessageView extends AbstractViewRight { const oMessage = currentMessage()/*, ctrl = event.target.closest('.openpgp-control')*/; PgpUserStore.verify(oMessage).then(result => { if (result) { - oMessage.pgpVerified(result); + oMessage.pgpSigned(result); } else { alert('Verification failed or no valid public key found'); } @@ -627,16 +629,17 @@ export class MailMessageView extends AbstractViewRight { async smimeDecrypt() { const message = currentMessage(); - let pass, data = message.smimeEncrypted(); // { partId: "1" } const addresses = message.from.concat(message.to, message.cc, message.bcc).map(item => item.email), - identity = IdentityUserStore.find(item => addresses.includes(item.email())); + identity = IdentityUserStore.find(item => addresses.includes(item.email())), + data = message.smimeEncrypted(); // { partId: "1" } if (data && identity) { - data = { ...data }; // clone - data.folder = message.folder; - data.uid = message.uid; -// data.bodyPart = data.bodyPart?.raw; - data.certificate = identity.smimeCertificate(); - data.privateKey = identity.smimeKey(); + delete data.error; + let pass, params = { ...data }; // clone + params.folder = message.folder; + params.uid = message.uid; +// params.bodyPart = params.bodyPart?.raw; + params.certificate = identity.smimeCertificate(); + params.privateKey = identity.smimeKey(); if (identity.smimeKeyEncrypted()) { pass = await Passphrases.ask(identity, i18n('SMIME/PRIVATE_KEY_OF', {EMAIL: identity.email()}), @@ -645,36 +648,44 @@ export class MailMessageView extends AbstractViewRight { if (!pass) { return; } - data.passphrase = pass?.password; + params.passphrase = pass?.password; } - Remote.post('SMimeDecryptMessage', null, data).then(response => { - if (response?.Result) { + Remote.post('SMimeDecryptMessage', null, params).then(response => { + if (response?.Result?.data) { message.smimeDecrypted(true); - MimeToMessage(response.Result, message); + MimeToMessage(response.Result.data, message); message.html() ? message.viewHtml() : message.viewPlain(); pass && pass.remember && Passphrases.set(identity, pass.password); + if ('signed' in response.Result) { + message.smimeSigned(response.Result.signed); + } } + }).catch(e => { + data.error = e.message + }) + .finally(() => { + message.smimeEncrypted(data); }); } } smimeVerify(/*self, event*/) { - const message = currentMessage(); - let data = message.smimeSigned(); // { partId: "1", micAlg: "pgp-sha256" } + const message = currentMessage(), + data = message.smimeSigned(); // { partId: "1", micAlg: "pgp-sha256" } if (data) { - data = { ...data }; // clone - data.folder = message.folder; - data.uid = message.uid; - data.bodyPart = data.bodyPart?.raw; - data.sigPart = data.sigPart?.bodyRaw; - Remote.post('SMimeVerifyMessage', null, data).then(response => { + const params = { ...data }; // clone + params.folder = message.folder; + params.uid = message.uid; + params.bodyPart = data.bodyPart?.raw; + params.sigPart = data.sigPart?.bodyRaw; + Remote.post('SMimeVerifyMessage', null, params).then(response => { if (response?.Result) { if (response.Result.body) { MimeToMessage(response.Result.body, message); message.html() ? message.viewHtml() : message.viewPlain(); - response.Result.body = null; } - message.smimeVerified(response.Result.success); + data.success = response.Result.success; + message.smimeSigned(data); } }); } diff --git a/dev/View/User/SystemDropDown.js b/dev/View/User/SystemDropDown.js index 102d5197c..1b10b4fcd 100644 --- a/dev/View/User/SystemDropDown.js +++ b/dev/View/User/SystemDropDown.js @@ -87,9 +87,8 @@ export class SystemDropDownUserView extends AbstractViewRight { } accountName() { - let email = AccountUserStore.email(), - account = AccountUserStore.find(account => account.email == email); - return account?.name || email; + const email = AccountUserStore.email(); + return AccountUserStore.find(account => account.email == email)?.label() || IDN.toUnicode(email); } settingsClick() { diff --git a/dev/boot.js b/dev/boot.js index 1ca127e04..eed775739 100644 --- a/dev/boot.js +++ b/dev/boot.js @@ -3,7 +3,7 @@ const qUri = path => doc.location.pathname.replace(/\/+$/,'') + '/?/' + path, eId = id => doc.getElementById('rl-'+id), - admin = '1' == eId('app')?.dataset?.admin, + admin = '1' == eId('app').dataset.admin, toggle = div => { eId('loading').hidden = true; @@ -69,7 +69,7 @@ window.rl = { headers: {} }, init); let asJSON = 1, - XToken = RL_APP_DATA.System?.token, + XToken = (RL_APP_DATA.System || {}).token, object = {}; if (postData) { init.method = 'POST'; diff --git a/examples/crypto/3140898548F06E97-private.asc b/examples/crypto/3140898548F06E97-private.asc new file mode 100644 index 000000000..be99cdc8b --- /dev/null +++ b/examples/crypto/3140898548F06E97-private.asc @@ -0,0 +1,106 @@ +-----BEGIN PGP PRIVATE KEY BLOCK----- + +lQdGBGNyBMoBEAC+x1PNa+DHQA+CJ0pznECiL11cmTzfkas6ahPNTYqadnA0dab6 +w81tQ6Q/QH92WRbQnqfGqtI+LoyHsga4Abspqj5rKTprC06f/Q5TCF4KS7gRmVUW +8KhTtlIWv9aA+0aTNcjT/OPr8W5l7fb/T/dPHo9Pp3929XjVtVlNhANPRT6h7/M7 +PzrOVT0mwhQnbZYZndD+PpU4MgH/TuKwxh0cI+IrpGZKCNR5ZYeLe3C9R8qojzrz +KxWFaj3Tev6qDDbqIAOp8r+fSC5TZPVtuvcqEZDtr9FLDq2oEgpWY+qKV7HiVo8h +lV4on0syIGOCd+SUW9ZS7qcb8qOPynvVzur4DTamrOdOqNd3ZO9hHh241JdGOdzw +eb9ASnvbtM52DW8LrfzGrtTc6HkzFHiqluwAS9sz99bxdlmT9zcmVDxYoQrbU3ML +ImZjwZZZedca/dDyNLLjehrViC9hI8rK2js6ndBUy9bvvxLtzpJA3K/urAlMsvGU +G1CRpSjys4XQnEJftxH+RssCi8N3mc3dV4YRDLKAHu+DU7d3ytUW82bkIZuYz0t3 +umt45h2dOdgxqam6I8u5t3Ry72vtkaQciaitTBBa2yWxaoi1ib2rCwofVddW65QK +kCDsjV8GeS+t5UMiB28/OOiWSlCWEwdXVew0IVXSZao4/R3r2h+mVl8pVQARAQAB +/gcDArr2sIgJEqz269dc2L7650YxaMd6U+DTHOZLXjEOXI8vzgR+Z4dkKVC/2a4s +qdjSLKDV/DkhTbvJun7DqRlwqwuNp/U3/+K31iF9B25xqSWdDy8+iX0hrar2Rt4E +U+pAkfu+hrC+t4c2f/20tIUSocNKXZ5D56iORqPMbnwDu6nuwdYmnVMHisrf+aKe +/F2WmzivQ2ZLSlFTVU6fQoqMDPZxPHttD7Pm4BVuEvF8KPiOiwtChQvvO09DYCK/ +EDRJWn7dDP1CoPVge5LntVN8xbtTG2Fi0DrYrLqF1mH3RoBn61KUo1wUOz+zh5F6 +0jHIjyscm+SIYPVTsnEVEBsTTsMhPj5vMEKD5qqbrD6cBSsJ25awt7FzovEI+5EK +nl7xZAXvC2NxS+TNFe4hpsfD/Q/UYyOfV5cqQ+6O9m7/Z38xDvgE/oeX6eHmMvJk +GfFAj51I0SNZq71QPqQALG1M5d5adw5WAPWHAgke2xB50dPm7prUIPUXF5zm2+8c +KE6oOq9gQyRVL1nXVBXyDpc3a4vXzz6VNvQlBNLXc7wrO+/HvUc2y9qnWm+IqAvd +JRZvvX4Es/85KUuhPH083XLFfkuSP3lVPaCEppdvDwVom7iggVU0aw8uhJ66qz8S +w6oBl9B/k3bD9VNEGtlo3bXfv4fyFDBwX8eorhcdLv8TnbalVecOHY4BIZHkWaCZ +8PqT+GjNhEEgQDxREY0bBrF+KYE+GtJe+2bwyFTQaioszeaaRmN2WMHZaLn+04nH +Iu8hpABejerOPPY2QGPGhhggQFS45MjOd/2aBGiAOG1Tyia8lxVReb0IGat0Ifm+ +sNZBI26MfYuscsapfOp0obIgGL6CKL30IByiYTAFr24167d87wMt0J709S4o+G90 +vdjsQZv5oHCL2t1F2n4Ib6P0ZicRn7bmO2pfwbQOCxNBMnLfK170QmUBGbBwJTse +9zaPOIPoBp1yQz6KC6wzyVLdACHPntEdaZA88SJJv3uwcK/WsiM+uRE38FJ3q4AX +WNBx1C+HvV3Ybocr6ebhDEj1H0GRRWs5M/I7309tc9waB2O+R8MsMUaCnFLDippo +igBg8ZNIw4rPuLvGRQqUb+VAtKpr2MX4r00heHFSB9joaBoyHEEZCLiAxzk83Jvc +fY8KeJGA07T50q1BseISTZuXdxLNM5r6aZcfk3/zuURs7oqKBSxH8VGfGQxocB1i +4rEAodCKjxVVy1L2YRj0kZMgcUgrbQKLzEcsEmkoehiiVfbc4MZRXLM+yvVRRAhi +PABALLqI0oxhbqya1rGo15kvHKzoJqcsudLHtjkqXlrfxgFtoeEuHPCq+0Nmsgnj +iuxW6WLSEVH4DPIci6nnMnQhS0357i3a8LAKteIX9K/0A2RgjMY7FGgYXnGj8JHM +C0nkYQ95lTb5tF0ZzTtQIf4+gG7gK5kG7PJCzbBQr9a8ll6CjpqbZmMwwUPEwCvv +W9U/Ds+Nf0AaLVoJdNNw/GLru4Ms90SjgITQ49IXlaX0ict5Vpavyu9I23DIlLo5 +r+3JkgCFkYjB6PrLmjzMN/REJ/I6iG0iXUm9tGMJ5azgo2bJmn+VxI1jdvySNxSI +AWsAuZvLpL6qXKQKVYJLKoZACgPq9fyncvPePIUAhVJdAv/zjoJ31zrskEwbrazR +Mmf/3Rm7GGV3hSPqkYRfwTg2/Ef6c9VumevRoWdjxOIOaITueOxb4evzsBGqZKLr +BW05fILbmeCwL4EtjP/ykC8r+ndbqG04KBYGM9rWkznn0NTvbRQi0020GURlbW8g +PGRlbW9Ac25hcHB5bWFpbC5ldT6JAjUEEAEIACkFAmNyBMsGCwkHCAMCCRAxQImF +SPBulwQVCAoCAxYCAQIZAQIbAwIeAQAAQGsP/3iQz6K9Q0bDg0PNSBzP/6v9j5GG +A5k5piiudlIcZcfSDgGmFwVsknJnGjaPXp62/rnFIUm0oHEgkP36eyK15Cycd+qn +KWSlaeWMAcv70tKJDQjweQyyM9896lCXKkdM7SZNvBLfjh9YQdBIayb9QAbk+4Mf +hIQ+miybS4PUeoWkx2RR5cFEiOOMU2+HwgNbcbtzFRRuMpho7mI2E8lTuWWokKAx +ENeNMeuzcRW0FoYzrURvxBtbe0P6fr/673RVs8mdfxumUIyCI0OgaouII0EGqs6R +W+MeMVMmZ5fpFPXcwYzNQJ401VpjL1MQyAIRqTaIX7EERzp6tAtgdqDEJzHYwRtC +n/aAmtCnauNw9rmoakDueLIRZ9FaU4OhYHv6g30FEMA+hrTxLNYs696fbXoB2/4u +KkOtY8pC6D5nvihBkcU1UEd5w4O8HM1V4Vn3ZXAZBY5Y66v/xSrPorDFc4gSXmOq +NgZE6/YMFVmB95mb0WNaKT1Xu8ZFZILAMZseNYd1YrxYg/OdkOHcoO+4h9GFu+Rq +5C/1BbAqrMssf+st+rRolhJ4TcgtdbCZzm5BO2ljnWCqEa3dciTU4jdPrgWi3Vpf +OcX42cmK45H18XbN7r+WbCsEDGmOMK4daGF+417hivREWbsrrEPqxp9FvCci6y8N +iq40mUCT+eVfFHGRnQdGBGNyBMoBEADWxp5f4mhqm6GkUpJJ12fKHsUFb3QNr5Kb +UkDXXd+vqD7mMl/28mAd15BzAIeQD0kawN3/cEUbc+X1tqSZGbVlUGNy0B6NHsdd +OvqBQQ41bBFTlchk7cORit1KYdSyg0QKTzJ90QrMe7/xL+LoM8QrrJjta4tEqZ8p +MQFeqGotnPW4kEQCKA2L8z9AQNeErwTUTL0GZAz8sY3ozCRvzKOMq+MCOAZNc0da +xk6JCtNrySSCefiLsFWDzk1CzbdwjNCzENPmUf6bRISULHuXr3EFNWz5XB0UHn3O +QezmZ+WFGTG63Kd5rsHHO6mZI4Fh+B9H3weF1WkYA+RNbAmhTTjTHbsuZ+tQnAOU +SmArQKLFysVg3ieFQSLrl7o1CceNG63nscR9nmtrcte5l5UJRMqnGP/nMkl0DvMo +0DLb2RykytwFzvZBOW2q4Wn/rgsDSeKoB/NCTHWRL/hYKkk3Q3iys2cJ8XK2datP +NPWfB4pl+M8ik2QGU7y3NZVN+KOacX7/+oNACU0Azhw2+1thFfpOtfYsm5/zVHyS +MtEWb3d3BUmXb++JGDWkeRMLxFjmLNYcVvFljeqfRmLjid76FAKw6dPv4f7ipDLa +z+I+qvzJEIdWtek+E2tOoWdL7+T+mmlkw2S2+eutWxsA1RZvuKLaDGkFxm6rT7z6 +4KdtDqcq0QARAQAB/gcDAizfuR2QYFd36+tujgnFtbXOqjPLHgZ9D+4PlTV9tfJy +srqR7/9951k2wkMMJ33NrntZ5iVsRgEBaBf7ZAABBrl+LFOARYsO/wxe+ilf4fNq +49e4hvE57CiV4YAcGyDu2/EOBmIPQcIbUUUJLylD2M8fAeMrO9L3IX6qF+Z5rHdV +7A+45ZNT743Yy6iQzFllNkBGOquu8PUUPQHTomgZl8gX+UETskb3JSihQiwyZZmG +RqaWwMikgKSYYtPBluyXQMBD48PRKxtMQOI3S+fFFP2IIKXar1q4RHZsBgwYrRlf +PfKLnICZLedYSBgL1NiymLlKsmQpt7PfDRQfyMeRzP0bYv2295sczKX0oyxfRGdF +fmtj4P83XiTfJvW3xbit3vTNVWzQQtXdYc5GPMc+CXeH/lzaefbuWRlxv4JLawnK +gGTmKHa9DaAZOQ9Cq8wbzitnobTeK8JDPRrFrZ+pksDxk2XJCrddR5kmYdXYGygQ +brcLeENVM2sJ9T38qMFJz0Oj7SaYH3l/ErYJiyQgPIpHETqefSj7ZZ/o0fnZP400 +2cjGoHIVDH/D/kW0clfR/WtNavAzT84tUPBgoXVjMwInZngD39XhQKMeWAEpoOAX +yPdu0NisYo1jC+ILMpcnHuAAWhdaKNXoTbPR3PFiKDbdRIEN1+9J3SKA+nY9UeOn +klEnPCZ7/1LWRjgidTsDwHHyDuEmYe0luRl/YAvzyiiQLWJKUerAod0EW3lxTwQ+ +ukMBMqzHjF/rpn/l5A84K4Z3YnAare0C97ldjUWjxYA2UKBYq6dB4R7Jif/6cHcx +ReqCyPqW9S4fbZG1BgwdhEigbo+DU0DmcITX9nW7r6U4ZwMGKsFPU+7ZIZmwfxPy +aRqqzKr9nw0/DbH2xLUN+tjs4QGpzXIUYxgShPt9SvfO0I79nW/6nVsKzQ3Vq2Id +SI8vLNH/lD0+C5bsmCwwCAtkkbHkeUUtyj/yve2bsCtzRFA5XlfibnpT9kPe6D4G +qdBI9Y9Xme3NUYW2Jw1kLbUfHhwHD/KoiRWb6znuGtkcWb7jx0YXNtoObTmJpoiq +uruTMk7tZRj8BqILOm1Hob6h24uyZBro4v5nQukii67Cuf7I99X0P4Dpfxc7Mmnk +ebZoRsMWHCgASir1eG4KJk0SekU4/aYJiZhK0hat0G5l4d4oi1s4ajzMAAbH97FB +fC220wsmZ3YbVDN8tAkukCuSW0kSYdYfXN1iHpqb9W6rodNyqMNqrSQSmZICqDZM +Mg+JI7YwcPsMwMr94iOaCy7W+jsAb6Eon7CMztsAMNKdDbCJxKF0fwxB8QfD4mVg +E4vT44zWnWCvB2XSHhMz0iJJRVlMFqbTdhMS8L26BLBzJfAX4OCUNJdpm6T1gVUR +X56v36FMQH+Iz96whc5psLVsjLiizZGLIJy2FBRTtFtGkdNJOyPaWLGUO2bDeWI7 +Rd9CDiWZD6koVtZT2JB3mk7N4ASQ0KttqaMDBRoIqJlpdjqSKtJxSjPHaNBDjert +jCDE6OwE401/Mnh9AeT5+vr6OVwmk/wbZoC+X9jXzfuqMGqvo6ElLPbSKJpIWtnE +0aqhfjBL+nDIwaqNZAaHZd/sFmVkDh1MJLKgWdCEWWHdReXWKIlSRcNkXMGZ93NQ +kUB1sqp1qmfK56SS90iJAHGAOKbac15dTcW1J/YJz9ZXr4xgEZAuiFCUFMWAEVOo +sCBs1rP2pUOqsQLiLjNleQ2Grz52aPNuCIajQwdjl8K6Ly8EXHcp9qznB6AP5+59 +YdwhmUiJAh8EGAEIABMFAmNyBMwJEDFAiYVI8G6XAhsMAADVEg/8D502NBHE0cnT +FuBJIbz1/lVc272ka0MMaknCHc8OV451HnIJ0foqNOqd1hEtUP1dk+8krgR+Izdo +322WlR7t7LAPHmh7As1iBNHB43+oas0nXcrnnAr7kSGB3a0AVxP62f4ogUIgZkEc +3gRz7QvzUvpVVQSd1SONfnjprEu5M22gMKZT93CkzsIYMumuCB7nlRJ6Fjy9zWem +TOCTDpnfQHgpWBu4Zo6EUKw4SxMhPsT6ngfdAAQtp9Eyxg6Mv0Oj4zYAhu7OMmGV +MWmouHTAluXEa/BjH+W2wa4dk2wZXBwZi/y1ZYVM4hFJcNk74XymsJbZAyX4aIAP +JeSzEyrYltJjBjMv/4DSF5ppznzpl7G4OrojqfngHxABwd5zl87lZ2Pde0XFZnK0 +Ltw7K+pMYswqJlrH1/bKy1oYtrBFlvF2MdLfHJzClr3IbBB1qEaUxz1JMm1AXz+c +y90ESbjZTvf6a0PQmxoPMyBqXryyt0ZEcoccTz02X1VWfCGgpqf4oVdxhVwUq6oV +LV8pPd+oaAzEcUrQlvVMgSCJGpqzibYRNVAVWUwI3v23YzXip8v9hLabSY7B1PRw +eojXyE9btmOUxD4AoJ949b2zklg/faPqBgbHbUeoN6yvWysFcROAdnGA/wHLv/zI +YXYPCv6njzAv1+fTgemAZYyomQ1qhVg= +=a9os +-----END PGP PRIVATE KEY BLOCK----- diff --git a/examples/crypto/3140898548F06E97-public.asc b/examples/crypto/3140898548F06E97-public.asc new file mode 100644 index 000000000..d6407e86a --- /dev/null +++ b/examples/crypto/3140898548F06E97-public.asc @@ -0,0 +1,50 @@ +-----BEGIN PGP PUBLIC KEY BLOCK----- + +mQINBGNyBMoBEAC+x1PNa+DHQA+CJ0pznECiL11cmTzfkas6ahPNTYqadnA0dab6 +w81tQ6Q/QH92WRbQnqfGqtI+LoyHsga4Abspqj5rKTprC06f/Q5TCF4KS7gRmVUW +8KhTtlIWv9aA+0aTNcjT/OPr8W5l7fb/T/dPHo9Pp3929XjVtVlNhANPRT6h7/M7 +PzrOVT0mwhQnbZYZndD+PpU4MgH/TuKwxh0cI+IrpGZKCNR5ZYeLe3C9R8qojzrz +KxWFaj3Tev6qDDbqIAOp8r+fSC5TZPVtuvcqEZDtr9FLDq2oEgpWY+qKV7HiVo8h +lV4on0syIGOCd+SUW9ZS7qcb8qOPynvVzur4DTamrOdOqNd3ZO9hHh241JdGOdzw +eb9ASnvbtM52DW8LrfzGrtTc6HkzFHiqluwAS9sz99bxdlmT9zcmVDxYoQrbU3ML +ImZjwZZZedca/dDyNLLjehrViC9hI8rK2js6ndBUy9bvvxLtzpJA3K/urAlMsvGU +G1CRpSjys4XQnEJftxH+RssCi8N3mc3dV4YRDLKAHu+DU7d3ytUW82bkIZuYz0t3 +umt45h2dOdgxqam6I8u5t3Ry72vtkaQciaitTBBa2yWxaoi1ib2rCwofVddW65QK +kCDsjV8GeS+t5UMiB28/OOiWSlCWEwdXVew0IVXSZao4/R3r2h+mVl8pVQARAQAB +tBlEZW1vIDxkZW1vQHNuYXBweW1haWwuZXU+iQI1BBABCAApBQJjcgTLBgsJBwgD +AgkQMUCJhUjwbpcEFQgKAgMWAgECGQECGwMCHgEAAEBrD/94kM+ivUNGw4NDzUgc +z/+r/Y+RhgOZOaYornZSHGXH0g4BphcFbJJyZxo2j16etv65xSFJtKBxIJD9+nsi +teQsnHfqpylkpWnljAHL+9LSiQ0I8HkMsjPfPepQlypHTO0mTbwS344fWEHQSGsm +/UAG5PuDH4SEPposm0uD1HqFpMdkUeXBRIjjjFNvh8IDW3G7cxUUbjKYaO5iNhPJ +U7llqJCgMRDXjTHrs3EVtBaGM61Eb8QbW3tD+n6/+u90VbPJnX8bplCMgiNDoGqL +iCNBBqrOkVvjHjFTJmeX6RT13MGMzUCeNNVaYy9TEMgCEak2iF+xBEc6erQLYHag +xCcx2MEbQp/2gJrQp2rjcPa5qGpA7niyEWfRWlODoWB7+oN9BRDAPoa08SzWLOve +n216Adv+LipDrWPKQug+Z74oQZHFNVBHecODvBzNVeFZ92VwGQWOWOur/8Uqz6Kw +xXOIEl5jqjYGROv2DBVZgfeZm9FjWik9V7vGRWSCwDGbHjWHdWK8WIPznZDh3KDv +uIfRhbvkauQv9QWwKqzLLH/rLfq0aJYSeE3ILXWwmc5uQTtpY51gqhGt3XIk1OI3 +T64Fot1aXznF+NnJiuOR9fF2ze6/lmwrBAxpjjCuHWhhfuNe4Yr0RFm7K6xD6saf +RbwnIusvDYquNJlAk/nlXxRxkbkCDQRjcgTKARAA1saeX+JoapuhpFKSSddnyh7F +BW90Da+Sm1JA113fr6g+5jJf9vJgHdeQcwCHkA9JGsDd/3BFG3Pl9bakmRm1ZVBj +ctAejR7HXTr6gUEONWwRU5XIZO3DkYrdSmHUsoNECk8yfdEKzHu/8S/i6DPEK6yY +7WuLRKmfKTEBXqhqLZz1uJBEAigNi/M/QEDXhK8E1Ey9BmQM/LGN6Mwkb8yjjKvj +AjgGTXNHWsZOiQrTa8kkgnn4i7BVg85NQs23cIzQsxDT5lH+m0SElCx7l69xBTVs ++VwdFB59zkHs5mflhRkxutynea7BxzupmSOBYfgfR98HhdVpGAPkTWwJoU040x27 +LmfrUJwDlEpgK0CixcrFYN4nhUEi65e6NQnHjRut57HEfZ5ra3LXuZeVCUTKpxj/ +5zJJdA7zKNAy29kcpMrcBc72QTltquFp/64LA0niqAfzQkx1kS/4WCpJN0N4srNn +CfFytnWrTzT1nweKZfjPIpNkBlO8tzWVTfijmnF+//qDQAlNAM4cNvtbYRX6TrX2 +LJuf81R8kjLRFm93dwVJl2/viRg1pHkTC8RY5izWHFbxZY3qn0Zi44ne+hQCsOnT +7+H+4qQy2s/iPqr8yRCHVrXpPhNrTqFnS+/k/pppZMNktvnrrVsbANUWb7ii2gxp +BcZuq0+8+uCnbQ6nKtEAEQEAAYkCHwQYAQgAEwUCY3IEzAkQMUCJhUjwbpcCGwwA +ANUSD/wPnTY0EcTRydMW4EkhvPX+VVzbvaRrQwxqScIdzw5XjnUecgnR+io06p3W +ES1Q/V2T7ySuBH4jN2jfbZaVHu3ssA8eaHsCzWIE0cHjf6hqzSddyuecCvuRIYHd +rQBXE/rZ/iiBQiBmQRzeBHPtC/NS+lVVBJ3VI41+eOmsS7kzbaAwplP3cKTOwhgy +6a4IHueVEnoWPL3NZ6ZM4JMOmd9AeClYG7hmjoRQrDhLEyE+xPqeB90ABC2n0TLG +Doy/Q6PjNgCG7s4yYZUxaai4dMCW5cRr8GMf5bbBrh2TbBlcHBmL/LVlhUziEUlw +2TvhfKawltkDJfhogA8l5LMTKtiW0mMGMy//gNIXmmnOfOmXsbg6uiOp+eAfEAHB +3nOXzuVnY917RcVmcrQu3Dsr6kxizComWsfX9srLWhi2sEWW8XYx0t8cnMKWvchs +EHWoRpTHPUkybUBfP5zL3QRJuNlO9/prQ9CbGg8zIGpevLK3RkRyhxxPPTZfVVZ8 +IaCmp/ihV3GFXBSrqhUtXyk936hoDMRxStCW9UyBIIkamrOJthE1UBVZTAje/bdj +NeKny/2EtptJjsHU9HB6iNfIT1u2Y5TEPgCgn3j1vbOSWD99o+oGBsdtR6g3rK9b +KwVxE4B2cYD/Acu//Mhhdg8K/qePMC/X59OB6YBljKiZDWqFWA== +=0ljX +-----END PGP PUBLIC KEY BLOCK----- diff --git a/examples/crypto/5F3A5CDC09AD8AE3-private.asc b/examples/crypto/5F3A5CDC09AD8AE3-private.asc new file mode 100644 index 000000000..ecdf91192 --- /dev/null +++ b/examples/crypto/5F3A5CDC09AD8AE3-private.asc @@ -0,0 +1,16 @@ +-----BEGIN PGP PRIVATE KEY BLOCK----- + +lIYEYfQDqBYJKwYBBAHaRw8BAQdAy+llquGb/U4M0kD2xyJoQ2pwlDN02C7XD906 +7I8zdB3+BwMC7y2sGFhuzTHldCm0gfdOH3H2PkmhDfM/OSTYGVEu1mKInC3xmp2F +HmC2wUd+yM+hzMrQMgHn61iT9CHDeunUhQqG8PoXfAUAL7syGiXQ0rQZZGVtbyA8 +ZGVtb0BzbmFwcHltYWlsLmV1PoiMBBAWCgAdBQJh9AOoBAsJBwgDFQgKBBYAAgEC +GQECGwMCHgEAIQkQXzpc3AmtiuMWIQQsIj8g6irbTLaPgdlfOlzcCa2K4/nPAP4u +UrWr39wv+YKsNcLwHwOpljyu59iHOXA3halUbVCeJwD/dY6JXCwMDgG+BmurPcJh +S/S8Q6fjlN9hUi/za3acYASciwRh9AOoEgorBgEEAZdVAQUBAQdAvXl+RCkqtUqN +VQ3Fj3bFTZjZOeNlI3ibK2eN6EjlnwcDAQgH/gcDAjAzqrbZEDg05XxnZqG/vHCg +1bJ/OofU+0oYZy6DHhAVDa5IhNLkC7kDeA8gXtQnNVuOd5kSIWvLcVRMZZval8iI +Xfrt037sAAciOIft1uSIeAQYFggACQUCYfQDqAIbDAAhCRBfOlzcCa2K4xYhBCwi +PyDqKttMto+B2V86XNwJrYrjolQBAMqdz8VkgMYjM7tinwUUTe4JjZoCsuhHPN6S +pQLd/UzKAQDgRlbAdrl042/nJcdrBrQz3+wVzkaF0ehvihBf4/tfDw== +=5A1c +-----END PGP PRIVATE KEY BLOCK----- diff --git a/examples/crypto/5F3A5CDC09AD8AE3-public.asc b/examples/crypto/5F3A5CDC09AD8AE3-public.asc new file mode 100644 index 000000000..6490530e2 --- /dev/null +++ b/examples/crypto/5F3A5CDC09AD8AE3-public.asc @@ -0,0 +1,13 @@ +-----BEGIN PGP PUBLIC KEY BLOCK----- + +mDMEYfQDqBYJKwYBBAHaRw8BAQdAy+llquGb/U4M0kD2xyJoQ2pwlDN02C7XD906 +7I8zdB20GWRlbW8gPGRlbW9Ac25hcHB5bWFpbC5ldT6IjAQQFgoAHQUCYfQDqAQL +CQcIAxUICgQWAAIBAhkBAhsDAh4BACEJEF86XNwJrYrjFiEELCI/IOoq20y2j4HZ +Xzpc3AmtiuP5zwD+LlK1q9/cL/mCrDXC8B8DqZY8rufYhzlwN4WpVG1QnicA/3WO +iVwsDA4BvgZrqz3CYUv0vEOn45TfYVIv82t2nGAEuDgEYfQDqBIKKwYBBAGXVQEF +AQEHQL15fkQpKrVKjVUNxY92xU2Y2TnjZSN4mytnjehI5Z8HAwEIB4h4BBgWCAAJ +BQJh9AOoAhsMACEJEF86XNwJrYrjFiEELCI/IOoq20y2j4HZXzpc3AmtiuOiVAEA +yp3PxWSAxiMzu2KfBRRN7gmNmgKy6Ec83pKlAt39TMoBAOBGVsB2uXTjb+clx2sG +tDPf7BXORoXR6G+KEF/j+18P +=fymh +-----END PGP PUBLIC KEY BLOCK----- \ No newline at end of file diff --git a/examples/crypto/A0B428E12402D875-private.asc b/examples/crypto/A0B428E12402D875-private.asc new file mode 100644 index 000000000..900820afe --- /dev/null +++ b/examples/crypto/A0B428E12402D875-private.asc @@ -0,0 +1,18 @@ +-----BEGIN PGP PRIVATE KEY BLOCK----- + +xYYEZfCT4RYJKwYBBAHaRw8BAQdABPuO2YQhhYx3HV4l8NGfC7TAUICCXYPB +B2YAgTIDR1j+CQMIGrTjdLMkuHvgN0hXM6160YQj51KfsJrY2uWtwTtjiJa7 +CgyvXj8krDEGrtdTWL0aGGZN+LpMbieyVPstLjycdxEMx0VwT5VOaMZX68As +vs0vU25hcHB5TWFpbCBEZW1vMyA8ZGVtbzNAeG4tLWR1OGguc25hcHB5bWFp +bC5ldT7CjAQQFgoAPgUCZfCT4QQLCQcICRCgtCjhJALYdQMVCAoEFgACAQIZ +AQIbAwIeARYhBA2rP+d/WzbDEuhm0aC0KOEkAth1AAA7fwEA2spNcx/tdZz8 +yBxsTbaOzWXrPa2RnU24QWI3nKiH65IA/j2UV2O6QwoB9PVoLLhqdsP0w7HH +msAcOY47Kt/rkqIHx4sEZfCT4RIKKwYBBAGXVQEFAQEHQB7lSl1px2GgNclu +DDFneXLe+ZjQAY6OvK23ti0zaCsFAwEIB/4JAwiaLdYHtWW/YOCOgzSJiwKU +4zmfRHIkkbCVyJO19Fpc4DwLx8dMKyPBQbLT0X7JI6SnR9qYPm35pJj2Cbrl +GNk3bImoPkcLVrwkrbUo+33gwngEGBYIACoFAmXwk+EJEKC0KOEkAth1AhsM +FiEEDas/539bNsMS6GbRoLQo4SQC2HUAAD+5APwO89z/dHpWj2GWRoLIbGsM +0zJcy7rldxp7FD+joiMOTQD/TnMrX8vwJCG/RMKzEVY02EnTScACrCzWIoLl +SxgrqQo= +=SBKt +-----END PGP PRIVATE KEY BLOCK----- \ No newline at end of file diff --git a/examples/crypto/A0B428E12402D875-public.asc b/examples/crypto/A0B428E12402D875-public.asc new file mode 100644 index 000000000..80173e6d6 --- /dev/null +++ b/examples/crypto/A0B428E12402D875-public.asc @@ -0,0 +1,14 @@ +-----BEGIN PGP PUBLIC KEY BLOCK----- + +xjMEZfCT4RYJKwYBBAHaRw8BAQdABPuO2YQhhYx3HV4l8NGfC7TAUICCXYPB +B2YAgTIDR1jNL1NuYXBweU1haWwgRGVtbzMgPGRlbW8zQHhuLS1kdThoLnNu +YXBweW1haWwuZXU+wowEEBYKAD4FAmXwk+EECwkHCAkQoLQo4SQC2HUDFQgK +BBYAAgECGQECGwMCHgEWIQQNqz/nf1s2wxLoZtGgtCjhJALYdQAAO38BANrK +TXMf7XWc/MgcbE22js1l6z2tkZ1NuEFiN5yoh+uSAP49lFdjukMKAfT1aCy4 +anbD9MOxx5rAHDmOOyrf65KiB844BGXwk+ESCisGAQQBl1UBBQEBB0Ae5Upd +acdhoDXJbgwxZ3ly3vmY0AGOjrytt7YtM2grBQMBCAfCeAQYFggAKgUCZfCT +4QkQoLQo4SQC2HUCGwwWIQQNqz/nf1s2wxLoZtGgtCjhJALYdQAAP7kA/A7z +3P90elaPYZZGgshsawzTMlzLuuV3GnsUP6OiIw5NAP9Ocytfy/AkIb9EwrMR +VjTYSdNJwAKsLNYiguVLGCupCg== +=FuIG +-----END PGP PUBLIC KEY BLOCK----- diff --git a/examples/crypto/smime.crt b/examples/crypto/smime.crt new file mode 100644 index 000000000..f85d172b7 --- /dev/null +++ b/examples/crypto/smime.crt @@ -0,0 +1,34 @@ +-----BEGIN CERTIFICATE----- +MIIF3jCCA8agAwIBAgIBADANBgkqhkiG9w0BAQsFADCBhTELMAkGA1UEBhMCTkwx +EDAOBgNVBAgMB1V0cmVjaHQxEDAOBgNVBAcMB1V0cmVjaHQxEzARBgNVBAoMClNu +YXBweU1haWwxFjAUBgNVBAMMDXNuYXBweW1haWwuZXUxJTAjBgkqhkiG9w0BCQEW +FnNlY3VyaXR5QHNuYXBweW1haWwuZXUwHhcNMjQwMjE5MjI1OTExWhcNMjcwNTE5 +MjI1OTExWjA9MRgwFgYDVQQDDA9TbmFwcHlNYWlsIERlbW8xITAfBgkqhkiG9w0B +CQEWEmRlbW9Ac25hcHB5bWFpbC5ldTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCC +AgoCggIBAM0jsuMlM2DCEXQ2gxMNHui3ZRjmyR/kvEq+36YBnowq3fGKysn1XZ7O +0V24olaKUcV4YE4BjORAShzv7yH2TvTkCLgutbpFTisooLab/urcjNcwwLeuTc+E +vZ1YArTb8CUdJpoQ6b/NJsiF0srQYGwG0p/P/kw7Is5YZ179LhGeTd4KJ6jLM2Si +4mo3FQPFNL0VEeVsvQtyUFr+3D/eHvgS1CRvb+Z0Zdfw8ssSL2ihLnDBaLxOiPTG +u7shAmPK55n9hZ8N4phezLx11y/pEYcZHeQSIjTIvIHtuyVYMKubNWgHz42mzkIi +AGj5tX/+33e/yRNGpC83q2gbm19AjzhUiyAF3b2CswKZyrDSEX0+Rq2Q//pGXsJ2 +lPJg/JwqVNyQyu4LTphIC/jJPp7K7L/HBS0seakRj+OMGYhnHDEXzolGM3L6j7E1 +Fj9MAiEmzmwaB11HGx7aB1thCX4mMsYWbzFnZDbK12pFGQ8lmcA4MSkBICSxNAJO +5SSKmnFafvZH2sOuEzefWseLjpCwxzWdPG4yDD889dBiGF7XBH3H3FIt3SOXyCDz +4iG3uWgU0XqNaMd8sMaVY4jFXPTMMvATFgUbqBjaS9kATrcdvxlFTqxqdWemLMwa +YS4D0C709ckD3JrBVBgZXBz6EMwJzqv6Et5l6y+SPfIQuzNcuGi3AgMBAAGjgZ8w +gZwwDwYDVR0TAQH/BAUwAwEB/zALBgNVHQ8EBAMCBeAwHQYDVR0lBBYwFAYIKwYB +BQUHAwIGCCsGAQUFBwMEMB0GA1UdEQQWMBSBEmRlbW9Ac25hcHB5bWFpbC5ldTAd +BgNVHQ4EFgQU2UETBx2y4K72pJsm05QdQIzuZXgwHwYDVR0jBBgwFoAUjOzkbdLt +2/b/OqOz3jYT2GtjCDUwDQYJKoZIhvcNAQELBQADggIBAK9b4TiBzKCGb8d4LFfH +PIBGbqB77NhAcOtP+V4rxV60pqLpqn2hk6c6T78yMedFQw6/idEPG1v1XgoCQxnV +s2PmKjVYG6WDqTEPZKgFbw2VpkJEwn7UivL9GZ0VeKHxVSIjSuhkhUKXHC/h7JCu +Lm3+EOwmwlk2kZeC7ADVoCzLYj/F0eeDjb+LR+gSyYdDBYbCjZGIbFZpb88pwNKo +lIYOXwh1TpEZOIfDXvpnp4UBEJ6IX4rhhzCBsxGNH+JbRnP1+pvENDE8B9Ax7MHU +qxFxnO2vw3HUsU2WdiX0NuF4xiXwIZm+JsMQETdTAHQ6EhLGFzU5PukrwRiHsBEw +T5f/bmBegerGRr0NkY46bih77IBoR0QU5GIlNAp3ZgIW8x9JKWrhrXdoTEGt42XY +iA9ugxQHD9RplA2zirgXwWhsUAsSRt9ocEsrZKOnxX/449X/UyQxAbO3FS7kzSCd +2OGsAM2dvpj7bRxcmFbB6eGvEHC/mZ02IKmEqKDUWYTcmHZnFMnTbcFnVFD+cMV4 +B032HeRqxgxjV9fZlDRwsINOfO6laPXVWaYBIZ2+h/MEzA4SlDN4MpikZWgGpbJi +09bN5c6Jra0ltGZKO/KJZsPG8PlZ30yRZytzLM6QuuL6KzTfcnMaOJts7rxn/BTt +r7HREQ/4hof+B0bTZCma/l0n +-----END CERTIFICATE----- diff --git a/examples/crypto/smime.key b/examples/crypto/smime.key new file mode 100644 index 000000000..2413be484 --- /dev/null +++ b/examples/crypto/smime.key @@ -0,0 +1,54 @@ +-----BEGIN ENCRYPTED PRIVATE KEY----- +MIIJnDBOBgkqhkiG9w0BBQ0wQTApBgkqhkiG9w0BBQwwHAQImwuT42kTeZYCAggA +MAwGCCqGSIb3DQIJBQAwFAYIKoZIhvcNAwcECAkQTF6SBnEGBIIJSJ5CyPT/qR4E +Aba2sN0JzyHPb5AB4EaE8cJNZkLayE24AVaA6GAiO+d4UR8gr7wDhmWIYaigf9XM +DOqg4AIqsvBW5zwK+3Fiv6jZj37zWiSoKx2M9bq9DvSXloliJNkm1ZV07bwx8KNC +FYAE06YqJr9cIhzu3f6ijH+eGUat/G11WukGIHASTraRRhzmV51cbUNEpFgJsqFt +WOTkyUEJolpJquigLoIA2GTTps1HlsLlSWU41Y6EMYsyRCwxE8myn/XDkTAVr9OZ +psJOrGnnvbyoD2rdGxfBHMMxFREyapCa8xsLMYHDyDcqZbZk9Qe3UQA2EQg5hVWd +6Wx2yRA5O54MteP7Z/4jOgE4XOOWILX3S905yAQBR942WEN7LG3pcNDPrYo8F+W4 +JM7/VnyUW7AP2f37r7rJUuZKYfL47ZiU7EoisO7sWcWHjPJTe4IPi42db/djmgLH +ufE07XJnNfwBRsJRQhNGsCeh0/pYkpCGOGhbqjyduIbmoQLjMw3K+sTNRNUgcyE0 +jMkEOEvSIE5HZq7wbH7nRMRpZC2q4fsSYZpu6726UOQ2mP96JRXp7o8WZfxQGOQl +J6G8oFfZoJsujCwyRYON0L2H9CF90mhFkt9QY8k/hoTonrmFsse8Jh4Pxl+PzBd4 +Mc9MMgfy8A0Yu54R2gGizd/gbo2ZP+/rKOOTe02Q5JQerrX8YB2U2htTbQMP+Evh +Z9+ElcqMxyI+1OC1yXDq6weDCuugB87F9UCRHy33wGz/1u5h/mMssFMb8lNe6x9c +S9R/J6ylWvMseUmtxES2fqT6/CymJUnnCItB7Jb0GqR9XpZZwHlZAKg82hnvRFDD +S55CvnPMOEkm7dnA/njnuYPakm7/dbe2YXHBAx/FzepKPEY2xabPo3MX2lALPpiO +oDF8GkSejlzOsO1hnJTRY76y7Mi3N3DwfzdawBbLABXygGAGcPeYeq7cOWvXy9In +K8fckONLzjsVmKDSpUmoLv/OhvFoTfC7Rq9VWzza28VTGSrSRLN9c1t3ykISxOhv +0TQUXhJAv2He4nCGclxBi9sOLJFpuOpOenD+mMrhUYdqgNTeZG6y7VvjeXX2y3x+ +sDde0WBVZ+rwYw+Z8kFQ9sNhyhmgar/g7y1uZoon7J7nQhkRDXK7e598XQxJBZgo +3IPMTIMFW6lwQCS9LkQL+d7ZYdR1NTkfHkWIqPJV13JbFVPvGIFfLX6MDSlvq90R +u06X4FovZZgYRhGIAt/MWnOvrYlm/hvmrDI4MPyYW5zJcZ2zzVzLmckR6fZrGHoE +uW7lKAFxsEZgw8IdVFrxNSerc2S+XycWEDRSFvwuWNFcSFNeOYHEPHgA8+o8boeH +nVjEtGPL3WqbVqq/dStlo7Xq62S7p7HBoQ3clGASq1pfU7la7f6sWZws1CyMD3lD +a8hUVMOJs3FmlC3iRpLnfY7iCrb9RIiW8jvpCwi0HOgiin0C8f1dC8v/dN5fLOvv +T1FZ/B531ClmLO2FhIb3tM2HSeWFNNQALjlY47ODFFvr28cIMDcBonjxl7Yezckw +W4lb2Ts7ZtQwrv0VcdvcVyt+8dZmvfCHoomWl9f7V1zwpeiB6j+zt9phh4fQaa3C +uEnsNcU9+Qaorf1e5qBWGV8l99gOvPXuuEP9pARyCKR2QQSltplWYDgYbYENdxdU +MxhL9a/zimyWbQTZyIyaka5gxwXRQjag7/9veScvDEA9bs5LAfvZ1bzNdZFvBTRY +drHYWnsbhAqsIT+TjD+boWmTRVBlvvhea549qX2igMD8zqytqICQyLvPyTpm+E51 +ba/qs4+ljbOPjbin2tji+uHGpYYEm4WsxV28KbyH1pUoMZZPEDj/abbtw5vxlPgp +4+OaTTt8fmxlIZrf6gOXLqDZ9K/x5or7up2wP/tAQ4FqxFDw0CvIIePHgD5QE8dL +LqxKeCn/1cCgXOiYh7H3Qe9NniU5m7Ot6Whv3WFIPl6TRS3CR15xCZBqtKYq9fdg +Y6i37spEBuDkQAa8BXwit3QCvHqRn/j+hnMLGmzyVvlLMvbUFXa2DY39uso1uz9v +VI/IT8Nnq4qKGafD1ubkL2RcNYk2d0Kr2q3qtobl5QksmRpLuFGw0z3rzwjPFGRv +jT1YT+ATKuoS0rG+LYTLRXxuJFeUniYFXDurnRWnT7z2pO6bRs5/ZSkZLaTyF+aH +9MOEIEJcbRz0vrFdqHNCyVHKcRap7HJE+KnWj1Qq9j22dndU5RoYKzP5MTybUvzf +888ZlSgDaauRgLhCBXUJ1QgB0Ox0AzYAeSrIhxS2U05FGemETuKJbpd/WI2leGRK +LnZkOTJAUYOJ+kUo4XF4ClT5iXTa0ii05PhifsE2ayp6JVDUYZ32dF4mR8yoRzbt +pv2iI+kRuz/tV81/xd8R4XKD/Jd0QAk2wPFli/FL9cC2qtSvTxwKFuxzzfLFGVYp +NeW3qlMSnKEHD7J/m/Nf41pIhws9G2npnyNd1Ir6zLgC1/ONkpyD1i4gGJfMcWda +Xw+64j42hQ+5lRMB2aWRwllI7FWOmgBxfGyqImg9ImzIbZ7GlJ335ZBo4hE8k+CJ +RpP7jlfW+oF24bTApihgvla93qqmiiwA0gTnibiJMKLFcG4EInQ8GM3yvA6oQy+2 +4K82/tXlKBJ+PpaBse6g40hF4M+5Ceo8UM6CEy5CYsHRbWi1OpQE4qoAwJHcRvcJ +aLsKANDAfp/Sd0zoFPWe2k65bW1G35LMeC8UbzYuk0GDIuSNWTuLuQ5tWsP4Xu34 +lwun/ikdreo2AxiaBXebJugO6VbCrnBmidYyg9Qn6kQd7ZceVU0cwMbsbJVHM0bX +IhKKQIW8OTE4z6nNMjuusjAgRvO1LzKU9nteCRp2xzRFeZvPTD+K5v1N1FngMUy/ +13zGR82WGk9VmuTNaR12FAFP6DILbaIxPJJxo9QkYHkQSoNNZU9lqiYerWH7EQDX +4qX1WPP7OtE2PX75ouCSphsNVVr7tBmR6ACayFEGZKNKCkeL4qjJW4qOdVBEW4uU +ae62FiKwbl1VF7CXrtH5QibCmGG5vxG/zzxwngZIlXMwwUjh9yRnQo8Q9yXtSB1y +c0y853GguCJ9xidDPRBJpdFZfEndlvcRjW/XPIHsB/tLAHX5NsboJkmjt9orB+CK +/ZYVVu8GG0wUcrvQizogAg== +-----END ENCRYPTED PRIVATE KEY----- diff --git a/examples/eml/tnef.eml b/examples/eml/tnef.eml new file mode 100644 index 000000000..7709e9ada --- /dev/null +++ b/examples/eml/tnef.eml @@ -0,0 +1,1745 @@ +From: Alice +To: Bob +Subject: TNEF test +MIME-Version: 1.0 +Content-Type: multipart/mixed; boundary="=-2UbcDuksJBAwUNBNW7UD" + +--=-2UbcDuksJBAwUNBNW7UD +Content-Type: text/plain; charset=us-ascii; format=flowed + +Test message body with example TNEF part from +https://github.com/Yeraze/ytnef + +https://github.com/the-djmaze/snappymail/discussions/1012 + +--=-2UbcDuksJBAwUNBNW7UD +Content-Type: application/vnd.ms-tnef +Content-Disposition: attachment; filename=winmail.dat +Content-Transfer-Encoding: base64 + +eJ8+IjMPAQaQCAAEAAAAAAABAAEAAQeQBgAIAAAA5AQAAAAAAADoAAEIgAcAGAAAAElQTS5NaWNy +b3NvZnQgTWFpbC5Ob3RlADEIAQ2ABAACAAAAAgACAAEGgAMADgAAANMHBgARAAoAFwAAAAIAFAEB +A5AGAPAEAAAnAAAACwACAAEAAAALACMAAAAAAAMAJgAAAAAACwApAAAAAAADADYAAAAAAB4AcAAB +AAAABQAAAHRlc3QAAAAAAgFxAAEAAAAWAAAAAcM05HC0gONzLGp+T/KnwUnJ7LwETAAAAgEdDAEA +AAAaAAAAU01UUDpKR0VSTEFOREBTWU5BQ09SLkNPTQAAAAsAAQ4AAAAAQAAGDgDiNFbkNMMBAgEK +DgEAAAAYAAAAAAAAAM/yM912IydOt4pJEsJHoOTCgAAAAwAUDgAAAAALAB8OAQAAAAIBCRABAAAA +2QAAANUAAAC7AQAATFpGdYDK2Y4DAAoAcmNwZzEyNRYyAPgLYG4OEDAzM08B9wKkA+MCAGNoCsBz +sGV0MCAHEwKAfQqA2QjIIDsJYg4wNQm7AoCdEfIqCbAJ8ASQYXQFsRpSDeBoEvERUDUuNIAwLjEx +LjIyD0BJAoBcdgiQd2sLgGTaNAxgYwBQCwNjAEELtaAgQ2FzZBixZhjl+wqiCoRkGJ8X4AFAGacZ +1fMb6gPQNDgaTxuZHa8cDyMaHyAsb2JqFOB0cPhoXCcMASEPIh4L4yK/CyPKEeEAKKAAAAADAN4/ +n04AAAMACVkDAAAAAwBAZQAAAAALABOACCAGAAAAAADAAAAAAAAARgAAAAADhQAAAAAAAAMAFYAI +IAYAAAAAAMAAAAAAAABGAAAAABCFAAAAAAAACwAYgAggBgAAAAAAwAAAAAAAAEYAAAAAFIUAAAAA +AAADABuACCAGAAAAAADAAAAAAAAARgAAAABShQAA45ABAAMAIoAIIAYAAAAAAMAAAAAAAABGAAAA +AAGFAAAAAAAAHgBEgAggBgAAAAAAwAAAAAAAAEYAAAAAVIUAAAEAAAAFAAAAMTAuMAAAAAALAEWA +CCAGAAAAAADAAAAAAAAARgAAAAAGhQAAAAAAAAsASYAIIAYAAAAAAMAAAAAAAABGAAAAAA6FAAAA +AAAAAwBMgAggBgAAAAAAwAAAAAAAAEYAAAAAGIUAAAAAAAALAGGACCAGAAAAAADAAAAAAAAARgAA +AACChQAAAAAAAAIB+A8BAAAAEAAAAM/yM912IydOt4pJEsJHoOQCAfoPAQAAABAAAADP8jPddiMn +TreKSRLCR6DkAgH7DwEAAACWAAAAAAAAADihuxAF5RAaobsIACsqVsIAAG1zcHN0LmRsbAAAAAAA +TklUQfm/uAEAqgA32W4AAABDOlxEb2N1bWVudHMgYW5kIFNldHRpbmdzXGpnZXJsYW5kXExvY2Fs +IFNldHRpbmdzXEFwcGxpY2F0aW9uIERhdGFcTWljcm9zb2Z0XE91dGxvb2tcT3V0bG9vay5wc3QA +AAADAP4PBQAAAAMADTT9NwIAAgEUNAEAAAAQAAAATklUQfm/uAEAqgA32W4AAAIBfwABAAAAMQAA +ADAwMDAwMDAwQ0ZGMjMzREQ3NjIzMjc0RUI3OEE0OTEyQzI0N0EwRTQ2NDUzMjcwMAAAAAADAAYQ +ONf/MwMABxBaAAAAAwAQEAAAAAADABEQAAAAAB4ACBABAAAAWwAAAENBU0RBU0RGQVNERkFTRENB +U0RBU0RGQVNERkFTRENBU0RBU0RGQVNERkFTRENBU0RBU0RGQVNERkFTRENBU0RBU0RGQVNERkFT +RENBU0RBU0RGQVNERkFTRAAASAICApAGAA4AAAABAF0AAAAgACAAAAAAAJ4AAhOAAwAOAAAA0wcE +AAcACgAjACYAAQA5AQIPgAYAeQsAAP/Y/+AAEEpGSUYAAQIAAGQAZAAA/+wAEUR1Y2t5AAEABAAA +ADwAAP/uACZBZG9iZQBkwAAAAAEDABUEAwYKDQAAA4gAAAVMAAAH5wAAC3f/2wCEAAYEBAQFBAYF +BQYJBgUGCQsIBgYICwwKCgsKCgwQDAwMDAwMEAwODxAPDgwTExQUExMcGxsbHB8fHx8fHx8fHx8B +BwcHDQwNGBAQGBoVERUaHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8f +Hx8fHx8fH//CABEIAEgAUwMBEQACEQEDEQH/xADFAAABBQEBAQAAAAAAAAAAAAAGAgMEBQcBAAgB +AQEBAQAAAAAAAAAAAAAAAAEAAgMQAAEEAgEDAwMFAAAAAAAAAAMAAQIEEQUTECESMEAGICIUUDFB +JBURAAECAwUFBQYHAQAAAAAAAAECAwARIRAxQVESYSIyEwQgcZGx0fCBocFCUuFicoIjMxRTEgEA +AAAAAAAAAAAAAAAAAABgEwEAAgIBBAEDBQEBAQAAAAABABEhMUEQUWFxIIGRoTDwscHRQPHh/9oA +DAMBAAIRAxEAAAHUy+bN5jqqE0mu0TY2f435k0cvPCdA43GdJNdTlPFsPLs21LrmDaK6nZlE7L1U +esSxOMdLcSbXPGUh2r0aTRZZiU1nPTkqppohxuS5BtYKDZTjSKdqvbPunLtKGTOuZsV0E+daPjcK +mGoNANrl5exyt4xrB9C5IMo/rNuauDQhrmhHJSGm51mWi/GpZFXxqtiGkZy4Pk37GsnYskEs1S2p +r1QHMWPR6v/aAAgBAQABBQJEG4yZ6ZT/ALrQGaF9p+Sy679N9X4dvheOV44bMGXnhPOToEDSNN2y +thc+UVjXthYuF+91EBpv+C8YhBWnK7TiMSr2CglT2fMuSK2O1rUY7LZ2dgSJXE4354PApDVZldWq +0Xpd8ePesTwl/oQW8fjuqtVc9PQ0uKFrVDIq9OAltDf1+kZYXLNfJAVzUVorXiaplWijYsWzG232 +p0/TclgCgg8vIffW3nMsyS112YbN80WrOyz2j3bK+XWDssPJ9f8AhRJUj5prJmX4g5scrnIZsOov +FZWw1lS/G9qC1thqPj5xm29RqOylOLuOXPCMMI7sv5x0/9oACAECAAEFAvrf9If0n9THvv/aAAgB +AwABBQL629nj37epn33/2gAIAQICBj8CR//aAAgBAwIGPwJH/9oACAEBAQY/AoW2b0KUk+4xXsyU +ZJW2pNfEeUUIJyBHl2OoGCzzB+/8bchnGcboA9tsSUYQjpwecT/HpvnGZz22LoVNT3VtNhSZeBI9 +8BzqauITpu00vrFE2TWrSI0pcClfaflHNAlLiFmptWk4y8oSFy3py7xZvzW8r+tlPEfQRrekEjgb +TcPWJKbE8sYJ6dFfnATLmOTuPDA19O2tonSdyRELThppZWKjUMo4/pl7bY6wOibiylTRP2FMqdxs +DiRrK914E/8AM0IPdDylJkokS7oCtakLF2UTUorVnfCwjAV7GHdKFTcbQ+2NaNZEyMUjvsV06uF2 +qf1D1EOQGy4ErN6ThBlhfCxhbP4WdQ7ytalIKCZDGgmcqxLKAtuikHUDtETYPKQfplBUolSlGc9s +aXCeWvdXPxBic+O62WNjPTgyYcBUraUmgjzhf+tvU082rlk/SoD5wUym4U7gOJii5ZSEJ6l1WAOn +CKf1p4RZW+z4wlPUJO5wKSZETj/KxN4OCbVL/wApN1IS91spNT5TIrfnDrTdEAhbWwGvwjUWwSb5 +GnhAGqo4kemfa//aAAgBAQMBPyGB/wDdpiYPh4l5995kOd6/yJ0zBrN48yjh6VReNmNxdQ9zD9jL +0xa8E9emC8fjwLf5Ryta8PeCOM+YZin1H8XHCzTxX+sqK5DVvq4NyDt65qUfHMx5rNVtZs65hppS +/Vn0/wDIU0Ke2b+UinyouTJoDyzQgfT/AHM2DX2mM68tB+YKsTCVf4TKlQD5xKxL92w8FO57d4tT +bQKorrGEZ5/MPFBXLp5/sYNVPExvnarlg9rZagP6juPP12Keo1Ey2lq0d467X1GBxpxG6tNmarJb +DY7gislZaFOmHDT3mDa/zXv6JgzQ26+/u/M4rxUqLDCG+5ELi7Mex7xvnqG9u0BdR3cDx2l7OKOT +5ldpTZYhwsoc7u9V5mKvfB3u/wC4UY3IBmxnX7x1f/sL+jGZP2ajGerM+jXuZhEcEmI90GgMk56g +ZnhzLX2Ibwj/AGVcaEQzmrB03GzDwv1zKFaZPGQPrUQ98oc92/xM9ehn29wUECP8h5IiN9Pqldeo +ug5r+ISr3VNN8S5iovHWc4UYYQvlcY7y9BlKuwErwo7Q1Sb4YZr3RidrpoFeu0LKrZ3DM4iw8Hv/ +ACJm43NeSCb+iS3Z/bUfgWqmuaTvXMZYeNltdmq1LXtS1KyiWAFG62Vlxy77R62qe8EHvgi1BXYF +7ipe4UKrSOEV541UQ+bySmt58zA1Yv1l/wCT/9oACAECAwE/If1J8a6PwrrcuD0SV1D9BlfFfQ+J +8h+kSHxOjHpf/D//2gAIAQMDAT8h/wCHfQ+VdAROg18Fl9GHwJfwOr/xDpuDH51CWyox/Tfh/9oA +DAMBAAIRAxEAABDOG35hJ6NEvj17pF1n4jw0Mup+UtFeJTgk7XkIj+sSuX0GdBkN/wD/2gAIAQED +AT8QDvZjVyvJxHTU/EK9kvg4cFw3JTla7dt5hyFsXHjD4fzEDC2EzxGrYNthwGC2We6WajMS2i+5 +VpmXUmMLjvernvq/2e4UVR6YtqX7l/Q+CUVb0f4uUgrsHw8SkoRu2X02Eawx1QGni4ACi8sTyzfi +JkPpavLRiLwYEAthyDxAXczII1SDejLElH3ap/pCq5vAttLMMDldKUwI8IqoQMyLnHqDnPUGj6pv +FMacLuj2CKzEDsjuvxH8CiS/Gv7wTpHApcM5udi07PMBC/Bh6o2Pgb5jZh9myAKh5oqYrtdd9dr9 +f3HN5agMjVhnzVaVGq4QNbsXwWnoCUksbgS6Nq7wjEFGhwXwHtVymG4ogrsThW8s3uZAYXDfpdWQ +4BFVbmgzTFbRQp4u+JlRTSPsvcGNY5LIYKJkzPv4Xn9guVqaY6Ljtiii8nMaWEdhvHqOQNrB2iER +dQdIFpDOMr3ywA3UxCpNcZ5lfnYfkOhRWKICBXBrTSxFFGzIubAxiaDDWBv94jeo5TNWnid/zS96 +/SMgxQf3iDwoaB3hs0SgwgC8xt0PI1X7ccxjDRRp7T21EHffU2GHC9mKzDV7u3ORNSpABW1Rdt+Z +d6yFS9Uf1MNFwINntmZZRV8fYyjzWGufvHuSrgNipHwvsIKBWl+bSn7oSphbi7XtTzH7qXIX9MjZ +4RpTLilbG4Sj8aVilgiVfWp3YwYtWP8A5CzWcPTKAVVCv5f1MMMGt27qpxWrlfm+0WN+gUpRw2Cr +p4mWRHMw2vsvvCQms2bkl0nKu8KT5EOQG/OtuklpxQUXeh9xM3Yo0gMs3CoKlDCxkn4hBllwul4i +jady/wAe/EIWFr0OU/yYee8LdSp5HIE0igLAmIDdY2RIXsFFqCZAZLQW1E4iuBoFlBlCaccc7Utw +F8xq1FRXaNhbwNRxEKlh/QLzxF+bcQL+vaKArcPGctxqL4YTx7lrP6I5lO+a4P3+3M//2gAIAQID +AT8Qh8tPjp81az1AhTopKxp1LI06BKJUqt9E8wc9R0G+i0xZhK3Nur0XQcxg/oCOoF3KqYviuDpb +iKVMMQhno9BqDFhnprofD//aAAgBAwMBPxCMPjt8WV1a6LZa8db8RRmWXnmwZj64R62amEG5a56R +LOr6Bo89AEhqVC5p1GpZK6Pjq9GMWY6MLcaai2d6YkfhcqHVm2WK4eh1IvSyDZKj+H//2cjwAhCA +AQANAAAAWkFQUEFffjIuSlBHAJoDAhGABgC4DQAAAQAJAAAD3AYAAAAAIQYAAAAABQAAAAkCAAAA +AAUAAAABAv///wClAAAAQQvGAIgAIAAgAAAAAAAgACAAAAAAACgAAAAgAAAAQAAAAAEAAQAAAAAA +AAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAA////AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA8AAAA/AAAAHwAAAB8AAA +AfAAAAHwAAAB8AAAAfAAAAHwAAAB8AAAAfAAAAHwAAAB8AAAAfAAAAHwAAAB8AAAAfAAAAHwAAAB +8AAAAfAAAAHwAAAB8AAAAfAAAAHwAAAB8AAAAfAAAAHwAAAB8AAAA/AAAAPwAAAH8AAAD/AAAf8h +BgAAQQtGAGYAIAAgAAAAAAAgACAAAAAAACgAAAAgAAAAIAAAAAEAGAAAAAAAAAwAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADAwMDAwMDAwMDAwMDAwMDA +wMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAADAwMD////////////////////////AwMDAwMDAwMDAwMDA +wMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAADAwMD////////////////////////////////////AwMDAwMDAwMDAwMDAwMDAwMDAwMDA +wMDAwMDAwMDAwMDAwMDAwMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADAwMD///////////// +///////////////////////////AwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADAwMD///////////////////////////////////// +///////AwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAADAwMD////////AwMCAgICAgICAgICAgICAgICAgICAgICAgICAgIDAwMDAwMDAwMDA +wMCAgICAgIDAwMDAwMDAwMDAwMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADAwMD////////A +wMCAgICAgICAgICAgICAgICAgICAgICAgICAgIDAwMDAwMDAwMDAwMDAwMCAgICAgIDAwMDAwMDA +wMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADAwMD////////AwMCAgICAgICAgICAgICAgICA +gICAgICAgIDAwMDAwMDAwMDAwMDAwMDAwMCAgICAgIDAwMDAwMDAwMAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAADAwMD////////AwMCAgICAgICAgICAgICAgICAgICAgIDAwMDAwMDAwMDAwMDA +wMDAwMCAgICAgICAgIDAwMDAwMDAwMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADAwMD///// +///AwMCAgICAgICAgICAgICAgICAgICAgICAgIDAwMDAwMDAwMDAwMDAwMDAwMCAgICAgIDAwMDA +wMDAwMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADAwMD////////AwMCAgICAgICAgIAAgICA +gICAgICAgIDAwMDAwMDAwMDAwMDAwMDAwMDAwMCAgICAgIDAwMDAwMDAwMAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAADAwMD////////AwMCAgICAgICAgIAAgICAgICAgICAgICAgIDAwMDAwMDA +wMDAwMDAwMDAwMCAgICAgIDAwMDAwMDAwMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADAwMD/ +///////AwMCAgICAgIAAgIAAAIAAgICAgICAgICAgIDAwMDAwMDAwMDAwMDAwMCAgICAgICAgIDA +wMDAwMDAwMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADAwMD////////AwMCAgICAgIAAAIAA +gICAgICAgICAgICAgIDAwMDAwMDAwMDAwMDAwMCAgICAgICAgID////AwMDAwMAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAADAwMD////////AwMCAgICAgIAAAIAAgICAgIAA//8A//8A//8A//8A +///AwMAA//8A//+AgICAgICAgID////AwMDAwMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADA +wMD///////8AAIAAAIAAAIAAAIAAgIAAAIAAAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP+A +gID////AwMDAwMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADAwMD///////8AAIAAAIAAAIAA +AIAAAIAAAIAAAIAAAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP+AgID////AwMDAwMAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAADAwMD///////+AgIAAAP8AgIAAgIAAAP8A//8A//8AgIAAgICA +gIDAwMDAwMDAwMAA//8AAP8AAP+AgID////AwMDAwMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AADAwMD///////+AgIAAAP8AgIAAgIAAAP8A//8A//8A//8A//8A///AwMD////AwMDAwMDAwMAA +//+AgID////AwMDAwMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADAwMD///////+AgIAAAP8A +gIAAAP8AAP8A//8A//8A//8A//8A///AwMDAwMDAwMAA//8A//8A//+AgID////AwMDAwMAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAADAwMD////////AwMAAAP8AAP8AAP8AAP8AAP8A//8A//8A +///AwMAA///AwMDAwMDAwMAA//8A//+AgID////AwMDAwMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAADAwMD///////////+AgICAgICAgICAgIDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDA +wMDAwMD////////AwMDAwMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADAwMD///////////// +///////////////////////////////////////////////////////AwMDAwMDAwMDAwMDAwMAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADAwMD///////////////////////////////////// +///////////////////////////////AwMDAwMDAwMDAwMCAgIAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAADAwMD///////////////////////////////////////////////////////////// +///AwMD//wCAgACAgACAgACAgIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADAwMD///////// +///////////////////////////////////////////////////////AwMDAwMD//wD//wCAgIAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD///////////////////////////////////// +///////////////////////////////AwMDAwMD//wCAgIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAD///////////////////////////////////////////////////////////// +///////AwMDAwMCAgIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD///////// +///////////////////////////////////////////////////////////AwMDAwMAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDA +wMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMCAgIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAADAAAAAADg9wIFkAYAmAEAABIAAAADACAOlBkAAB4AATABAAAADgAAAHphcHBhX2F2MS5qcGcA +AAACAQI3AQAAAAAAAAAeAAM3AQAAAAUAAAAuanBnAAAAAAMABTcBAAAAHgAHNwEAAAAOAAAAemFw +cGFfYXYxLmpwZwAAAAMACzddAAAAAwAUNwAAAAADAPp/AAAAAEAA+38AQN2jV0WzDEAA/H8AQN2j +V0WzDAMA/X8AAAAACwD+fwAAAAADACEOJckDAAIB+A8BAAAAEAAAAM/yM912IydOt4pJEsJHoOQC +AfoPAQAAABAAAADP8jPddiMnTreKSRLCR6DkAgH7DwEAAACWAAAAAAAAADihuxAF5RAaobsIACsq +VsIAAG1zcHN0LmRsbAAAAAAATklUQfm/uAEAqgA32W4AAABDOlxEb2N1bWVudHMgYW5kIFNldHRp +bmdzXGpnZXJsYW5kXExvY2FsIFNldHRpbmdzXEFwcGxpY2F0aW9uIERhdGFcTWljcm9zb2Z0XE91 +dGxvb2tcT3V0bG9vay5wc3QAAAADAP4PBwAAAFdnAgKQBgAOAAAAAQBzAAAAIAAgAAAAAAC0AAIT +gAMADgAAANMHBgARAAoAFgApAAIAPAECEYAGALgNAAABAAkAAAPcBgAAAAAhBgAAAAAFAAAACQIA +AAAABQAAAAEC////AKUAAABBC8YAiAAgACAAAAAAACAAIAAAAAAAKAAAACAAAABAAAAAAQABAAAA +AAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAD///8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADgAAAH4AAAB+AAAAfg +AAAH4AAAB+AAAAfgAAAH4AAAB+AAAAfgAAAH4AAAB+AAAAfgAAAH4AAAB+AAAAfgAAAH4AAAB+AA +AAfgAAAH4AAAB+AAAAfgAAAH4AAAB+AAAAfgAAAH4AAAB+AAAA/gAAAf4AAAP+AAAH/gAAD/4AAB +/yEGAABBC0YAZgAgACAAAAAAACAAIAAAAAAAKAAAACAAAAAgAAAAAQAYAAAAAAAADAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAFVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV +VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVQAAAAAAAAAAAAAAAAAAAAAA +AIaGhtbn58zMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzM +zMzMzMzMzMzMzMzMzMzMzMzMzFVVVQAAAAAAAAAAAAAAAAAAAAAAAIaGhtbn5/////////////// +/////////////////////////////////////////////////////////////////////////8zM +zFVVVQAAAAAAAAAAAAAAAAAAAAAAAIaGhtbn5/////////////////////////////////////// +/////////////////////////////////////////////////8zMzFVVVQAAAAAAAAAAAAAAAAAA +AAAAAIaGhtbn5/////////////////////////////////////////////////////////////// +/////////////////////////8zMzFVVVQAAAAAAAAAAAAAAAAAAAAAAAIaGhtbn5/////////// +//////////////////////////////////////////////////////////////////////////// +/8zMzFVVVQAAAAAAAAAAAAAAAAAAAAAAAIaGhtbn5/////////////////////////////////// +/////////////////////////////////////////////////////8zMzFVVVQAAAAAAAAAAAAAA +AAAAAAAAAIaGhtbn5////////wwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwM +DAwMDAwMDAwMDAwMDAwMDP///////8zMzFVVVQAAAAAAAAAAAAAAAAAAAAAAAIaGhtbn5/////// +/4aGhszMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzAwMDP// +/////8zMzFVVVQAAAAAAAAAAAAAAAAAAAAAAAIaGhtbn5////////4aGhtbn59bn59bn59bn59bn +59bn59bn59bn59bn59bn59bn59bn59bn59bn59bn58zMzAwMDP///////8zMzE1NTQAAAAAAAAAA +AAAAAAAAAAAAAIaGhtbn5////////4aGhtbn5/////////////////////////////////////// +/////////////9bn58zMzAwMDP///////8zMzFVVVQAAAAAAAAAAAAAAAAAAAAAAAIaGhtbn5/// +/////4aGhtbn5////4aGhoaGhoaGhv///4aGhoaGhoaGhv///4aGhoaGhoaGhv///9bn58zMzAwM +DP///////8zMzFVVVQAAAAAAAAAAAAAAAAAAAAAAAIaGhtbn5////////4aGhtbn5/////////// +/////////////////////////////////////////9bn58zMzAwMDP///////8zMzFVVVQAAAAAA +AAAAAAAAAAAAAAAAAIaGhtbn5////////4aGhtbn5////8zMzAAAgP///////8zMzIAAgIAAgP// +/wCAAACAAACAAP///9bn58zMzAwMDP///////8zMzFVVVQAAAAAAAAAAAAAAAAAAAAAAAIaGhtbn +5////////4aGhtbn5////wAAgAAA/wAAgP////8AAP8AAMzMzP///wCAAAD/AP8AAP///9bn58zM +zAwMDP///////8zMzFVVVQAAAAAAAAAAAAAAAAAAAAAAAIaGhtbn5////////4aGhtbn5////wAA +/wD//wAAgP//////AP8AAP8AAP///////wCAAACAAP///9bn58zMzAwMDP///////8zMzFVVVQAA +AAAAAAAAAAAAAAAAAAAAAIaGhtbn5////////4aGhtbn5////////wAA/wAAgP////8AAP////// +/////////wCAAP///////9bn58zMzAwMDP///////8zMzFVVVQAAAAAAAAAAAAAAAAAAAAAAAIaG +htbn5////////4aGhtbn5////////////////////////////////////////////////////9bn +58zMzAwMDP///////8zMzFVVVQAAAAAAAAAAAAAAAAAAAAAAAIaGhtbn5////////4aGhtbn5/// +/////////////////////////////////////////////////9bn58zMzAwMDP///////8zMzFVV +VQAAAAAAAAAAAAAAAAAAAAAAAIaGhtbn5////////4aGhtbn59bn59bn59bn59bn59bn59bn59bn +59bn59bn59bn59bn59bn59bn59bn58zMzAwMDP///////8zMzFVVVQAAAAAAAAAAAAAAAAAAAAAA +AIaGhtbn5////////4aGhszMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzM +zMzMzMzMzAwMDP///////8zMzFVVVQAAAAAAAAAAAAAAAAAAAAAAAIaGhtbn5////////4aGhjOZ +zJkAAJkAAJkAAJkAAJkAAJkAAJkAAJkAAJkAAIAAAIAAAIAAAIAAAIAAAIAAAAwMDP///9bn58zM +zFVVVQAAAAAAAAAAAAAAAAAAAAAAAIaGhtbn5////////4aGhjPM/5kAAJkAAJkAAJkAAJkAAJkA +AJkAAJkAAJkAAP///4AAAP///4AAAP///4AAAAwMDNbn58zMzJmZmVVVVQAAAAAAAAAAAAAAAAAA +AAAAAIaGhtbn5////////4aGhoaGhoaGhoaGhoaGhoaGhoaGhoaGhoaGhoaGhoaGhoaGhoaGhoaG +hoaGhoaGhoaGhgwMDMzMzJmZmXd3d1VVVQAAAAAAAAAAAAAAAAAAAAAAAIaGhtbn5/////////// +/////////////////////////////////////////////////////////////9bn58zMzJmZmXd3 +d3d3d1VVVQAAAAAAAAAAAAAAAAAAAAAAAIaGhtbn5/////////////////////////////////// +/////////////////////////////////5mZmU1NTU1NTTMzMzMzMzMzMzMzMwAAAAAAAAAAAAAA +AAAAAAAAAIaGhtbn5/////////////////////////////////////////////////////////// +/////////5mZmf///////9bn58zMzFVVVQAAAAAAAAAAAAAAAAAAAAAAAAAAAIaGhtbn5/////// +/////////////////////////////////////////////////////////////5mZmf///9bn58zM +zFVVVQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIaGhtbn5/////////////////////////////// +/////////////////////////////////////5mZmdbn58zMzFVVVQAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAIaGhtbn5/////////////////////////////////////////////////////// +/////////////5mZmczMzFVVVQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIaGhtbn59bn +59bn59bn59bn59bn59bn59bn59bn59bn59bn59bn59bn59bn59bn59bn59bn59bn55mZmVVVVQAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIaGhoaGhoaGhoaGhoaGhoaGhoaGhoaGhoaG +hoaGhoaGhoaGhoaGhoaGhoaGhoaGhoaGhoaGhoaGhpmZmQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAMAAAAAACFfAg+ABgAtTwEAPCFET0NUWVBFIE5FVFNDQVBFLUJvb2ttYXJrLWZpbGUtMT4N +CjwhLS0gVGhpcyBpcyBhbiBhdXRvbWF0aWNhbGx5IGdlbmVyYXRlZCBmaWxlLg0KSXQgd2lsbCBi +ZSByZWFkIGFuZCBvdmVyd3JpdHRlbi4NCkRvIE5vdCBFZGl0ISAtLT4NCjxUSVRMRT5Cb29rbWFy +a3M8L1RJVExFPg0KPEgxPkJvb2ttYXJrczwvSDE+DQo8REw+PHA+DQogICAgPERUPjxIMyBGT0xE +RUQgQUREX0RBVEU9IjEwNDI2NDY4NzQiPkFkb2JlIEdvTGl2ZTwvSDM+DQogICAgPERMPjxwPg0K +ICAgICAgICA8RFQ+PEEgSFJFRj0iaHR0cDovL3d3dy5ydWR5bGVtY2tlLmNvbS9BY3Rpb25zLyIg +QUREX0RBVEU9IjEwNDI2NDY4NzQiIExBU1RfVklTSVQ9IjEwNDgxNzQ2NDIiIExBU1RfTU9ESUZJ +RUQ9IjEwNDI2NDY4NzQiPkdvTGl2ZSBBY3Rpb25zIFJlc291cmNlPC9BPg0KICAgICAgICA8RFQ+ +PEEgSFJFRj0iaHR0cDovL3d3dy5tYXR0LXJpZGxleS5jb20vc29mdHdhcmUvYWN0aW9ucy9hY3Rp +b25zLmh0bWwiIEFERF9EQVRFPSIxMDQyNjQ2ODc0IiBMQVNUX1ZJU0lUPSIxMDQ4MTc0NjQyIiBM +QVNUX01PRElGSUVEPSIxMDQyNjQ2ODc0Ij5Hb0xpdmUgQWN0aW9uczwvQT4NCiAgICAgICAgPERU +PjxBIEhSRUY9Imh0dHA6Ly93d3cuZ29saXZlaHEuY29tLyIgQUREX0RBVEU9IjEwNDI2NDY4NzQi +IExBU1RfVklTSVQ9IjEwNDgxNzQ2NDIiIExBU1RfTU9ESUZJRUQ9IjEwNDI2NDY4NzQiPkdvTGl2 +ZSBIZWFkcXVhcnRlcnMgIE5ld3M8L0E+DQogICAgICAgIDxEVD48QSBIUkVGPSJodHRwOi8vd3d3 +LmdvbGl2ZWhlYXZlbi5jb20vIiBBRERfREFURT0iMTA0MjY0Njg3NCIgTEFTVF9WSVNJVD0iMTA0 +ODE3NDY0MiIgTEFTVF9NT0RJRklFRD0iMTA0MjY0Njg3NCI+R29MaXZlSGVhdmVuICBBIEh1YiBm +b3IgdGhlIEdvTGl2ZSBDb21tdW5pdHk8L0E+DQogICAgICAgIDxEVD48QSBIUkVGPSJodHRwOi8v +MjE2LjI0Ni41MS4yMDIvZm9ydW1zL2Fkb2JlX2dvbGl2ZS9pbmRleC5odG0iIEFERF9EQVRFPSIx +MDQyNjQ2ODc0IiBMQVNUX1ZJU0lUPSIxMDQ4MTc0NjQyIiBMQVNUX01PRElGSUVEPSIxMDQyNjQ2 +ODc0Ij5odHRwLS0yMTYuMjQ2LjUxLjIwMi1mb3J1bXMtYWRvYmVfZ29saXZlLWluZGV4Lmh0bTwv +QT4NCiAgICAgICAgPERUPjxBIEhSRUY9Imh0dHA6Ly93d3cud2JsYWR5LmNvbS9hY3Rpb25zLmh0 +bWwiIEFERF9EQVRFPSIxMDQyNjQ2ODc0IiBMQVNUX1ZJU0lUPSIxMDQ4MTc0NjQyIiBMQVNUX01P +RElGSUVEPSIxMDQyNjQ2ODc0Ij5Nb3JlIEFkb2JlIEdvTGl2ZSBBY3Rpb25zIHBhZ2Ugb25lPC9B +Pg0KICAgICAgICA8RFQ+PEEgSFJFRj0iaHR0cDovL3d3dy5vdXQudG8vYWN0aW9ucy9odG1sL2lu +ZGV4Lmh0bWwiIEFERF9EQVRFPSIxMDQyNjQ2ODc0IiBMQVNUX1ZJU0lUPSIxMDQ4MTc0NjQyIiBM +QVNUX01PRElGSUVEPSIxMDQyNjQ2ODc0Ij5PVVRhY3Rpb25zPC9BPg0KICAgIDwvREw+PHA+DQog +ICAgPERUPjxIMyBGT0xERUQgQUREX0RBVEU9IjEwNDI2NDY4NzQiPkFTUCBTdHVmZjwvSDM+DQog +ICAgPERMPjxwPg0KICAgICAgICA8RFQ+PEEgSFJFRj0iaHR0cDovL3d3dy5hc3BzdHJlZXQuY29t +L2QudGFmIiBBRERfREFURT0iMTA0MjY0Njg3NCIgTEFTVF9WSVNJVD0iMTA0ODE3NDY0MiIgTEFT +VF9NT0RJRklFRD0iMTA0MjY0Njg3NCI+QVNQIC0gQXBwbGljYXRpb24gc2VydmljZSBwcm92aWRl +cnM8L0E+DQogICAgICAgIDxEVD48QSBIUkVGPSJodHRwOi8vd3d3LmNybXhjaGFuZ2UuY29tLyIg +QUREX0RBVEU9IjEwNDI2NDY4NzQiIExBU1RfVklTSVQ9IjEwNDgxNzQ2NDIiIExBU1RfTU9ESUZJ +RUQ9IjEwNDI2NDY4NzQiPkNSTVhjaGFuZ2UgR2F0ZXdheSB0byB0aGUgQ29udGFjdCBDZW50ZXIs +IENSTSBhbmQgQ3VzdG9tZXIgQ2FyZSBDb21tdW5pdHk8L0E+DQogICAgICAgIDxEVD48QSBIUkVG +PSJodHRwOi8vd3d3LnBlb3BsZXNvZnQuY29tL2VuL3VzL3Byb2R1Y3RzL2FwcGxpY2F0aW9ucy9j +cm0vcHJvZHVjdF9jb250ZW50Lmh0bWwiIEFERF9EQVRFPSIxMDQyNjQ2ODc0IiBMQVNUX1ZJU0lU +PSIxMDQ4MTc0NjQyIiBMQVNUX01PRElGSUVEPSIxMDQyNjQ2ODc0Ij5QZW9wbGVTb2Z0ICBDdXN0 +b21lciBSZWxhdGlvbnNoaXAgTWFuYWdlbWVudCAgSG9tZTwvQT4NCiAgICA8L0RMPjxwPg0KICAg +IDxEVD48SDMgRk9MREVEIEFERF9EQVRFPSIxMDQyNjQ2ODc0Ij5EYWlseTwvSDM+DQogICAgPERM +PjxwPg0KICAgICAgICA8RFQ+PEEgSFJFRj0iaHR0cDovL2FudHdycC5nc2ZjLm5hc2EuZ292L2Fw +b2QvYXJjaGl2ZXBpeC5odG1sIiBBRERfREFURT0iMTA0MjY0Njg3NCIgTEFTVF9WSVNJVD0iMTA0 +ODE3NDY0MiIgTEFTVF9NT0RJRklFRD0iMTA0MjY0Njg3NCI+QXN0cm9ub215IFBpY3R1cmUgb2Yg +dGhlIERheSBBcmNoaXZlPC9BPg0KICAgICAgICA8RFQ+PEEgSFJFRj0iaHR0cDovL3d3dy5mYWly +dnVlLmNvbS8/ZmVhdHVyZT1hd2FyZHMyMDAxIiBBRERfREFURT0iMTA0MjY0Njg3NCIgTEFTVF9W +SVNJVD0iMTA0ODE3NDY0MiIgTEFTVF9NT0RJRklFRD0iMTA0MjY0Njg3NCI+QmVzdCBvZiB0aGUg +QmxvZ3M8L0E+DQogICAgICAgIDxEVD48QSBIUkVGPSJodHRwOi8vd3d3LmJyaWxsc2NvbnRlbnQu +Y29tL2luZGV4Lmh0bWwiIEFERF9EQVRFPSIxMDQyNjQ2ODc0IiBMQVNUX1ZJU0lUPSIxMDQ4MTc0 +NjQyIiBMQVNUX01PRElGSUVEPSIxMDQyNjQ2ODc0Ij5CcmlsbCdzIENvbnRlbnQ8L0E+DQogICAg +ICAgIDxEVD48QSBIUkVGPSJodHRwOi8vd3d3LmNhbXdvcmxkLmNvbS8iIEFERF9EQVRFPSIxMDQy +NjQ2ODc0IiBMQVNUX1ZJU0lUPSIxMDQ4MTc0NjQyIiBMQVNUX01PRElGSUVEPSIxMDQyNjQ2ODc0 +Ij5DYW1Xb3JsZCBUaGlua2luZyBPdXRzaWRlIHRoZSBCb3g8L0E+DQogICAgICAgIDxEVD48QSBI +UkVGPSJodHRwOi8vd3d3LmNodWQuY29tLyIgQUREX0RBVEU9IjEwNDI2NDY4NzQiIExBU1RfVklT +SVQ9IjEwNDgxNzQ2NDIiIExBU1RfTU9ESUZJRUQ9IjEwNDI2NDY4NzQiPkNIVUQgLSBDaW5lbWF0 +aWMgSGFwcGVuaW5ncyBVbmRlciBEZXZlbG9wbWVudDwvQT4NCiAgICAgICAgPERUPjxBIEhSRUY9 +Imh0dHA6Ly93d3cuZGF5cG9wLmNvbS8iIEFERF9EQVRFPSIxMDQyNjQ2ODc0IiBMQVNUX1ZJU0lU +PSIxMDQ4MTc0NjQyIiBMQVNUX01PRElGSUVEPSIxMDQyNjQ2ODc0Ij5EYXlwb3AgLSBhIGN1cnJl +bnQgZXZlbnRzLXdlYmxvZy1uZXdzIHNlYXJjaCBlbmdpbmU8L0E+DQogICAgICAgIDxEVD48QSBI +UkVGPSJodHRwOi8vd3d3LmRvdGNvbXNjb29wLmNvbS8iIEFERF9EQVRFPSIxMDQyNjQ2ODc0IiBM +QVNUX1ZJU0lUPSIxMDQ4MTc0NjQyIiBMQVNUX01PRElGSUVEPSIxMDQyNjQ2ODc0Ij5Eb3Rjb20g +U2Nvb3A8L0E+DQogICAgICAgIDxEVD48QSBIUkVGPSJodHRwOi8vd3d3LmRvdGN1bHQuY29tLyIg +QUREX0RBVEU9IjEwNDI2NDY4NzQiIExBU1RfVklTSVQ9IjEwNDgxNzQ2NDIiIExBU1RfTU9ESUZJ +RUQ9IjEwNDI2NDY4NzQiPmRvdENVTFQuY29tPC9BPg0KICAgICAgICA8RFQ+PEEgSFJFRj0iaHR0 +cDovL2RyZXcuY29ycnVwdC5uZXQvIiBBRERfREFURT0iMTA0MjY0Njg3NCIgTEFTVF9WSVNJVD0i +MTA0ODE3NDY0MiIgTEFTVF9NT0RJRklFRD0iMTA0MjY0Njg3NCI+ZHJldyMjXiM8L0E+DQogICAg +ICAgIDxEVD48QSBIUkVGPSJodHRwOi8vd3d3LmZhcmsuY29tLyIgQUREX0RBVEU9IjEwNDI2NDY4 +NzQiIExBU1RfVklTSVQ9IjEwNDgxNzQ2NDIiIExBU1RfTU9ESUZJRUQ9IjEwNDI2NDY4NzQiPkZB +UksgRHJldyBDdXJ0aXMnIEZBUksuY29tPC9BPg0KICAgICAgICA8RFQ+PEEgSFJFRj0iaHR0cDov +L3d3dy5mZWVkbWFnLmNvbS8iIEFERF9EQVRFPSIxMDQyNjQ2ODc0IiBMQVNUX1ZJU0lUPSIxMDQ4 +MTc0NjQyIiBMQVNUX01PRElGSUVEPSIxMDQyNjQ2ODc0Ij5GRUVEIE1hZ2F6aW5lPC9BPg0KICAg +ICAgICA8RFQ+PEEgSFJFRj0iaHR0cDovL3d3dy5lbGVnYW50aGFjay5jb20vaW5kZXguaHRtbCIg +QUREX0RBVEU9IjEwNDI2NDY4NzQiIExBU1RfVklTSVQ9IjEwNDgxNzQ2NDIiIExBU1RfTU9ESUZJ +RUQ9IjEwNDI2NDY4NzQiPklBIEVILiBFbGVnYW50aGFjayBpcyBhIGd1aWRlIHRvIEluZm9ybWF0 +aW9uIEFyY2hpdGVjdHVyZSwgSW50ZXJmYWNlIERlc2lnbiwgVXNhYmlsaXR5LCBVc2VyIENlPC9B +Pg0KICAgICAgICA8RFQ+PEEgSFJFRj0iaHR0cDovL3d3dy5pcm9ubWluZHMuY29tL2luZGV4LnNo +dG1sIiBBRERfREFURT0iMTA0MjY0Njg3NCIgTEFTVF9WSVNJVD0iMTA0ODE3NDY0MiIgTEFTVF9N +T0RJRklFRD0iMTA0MjY0Njg3NCI+SXJvbm1pbmRzPC9BPg0KICAgICAgICA8RFQ+PEEgSFJFRj0i +aHR0cDovL3d3dy5tZW1lcG9vbC5jb20vIiBBRERfREFURT0iMTA0MjY0Njg3NCIgTEFTVF9WSVNJ +VD0iMTA0ODE3NDY0MiIgTEFTVF9NT0RJRklFRD0iMTA0MjY0Njg3NCI+bWVtZXBvb2wuY29tPC9B +Pg0KICAgICAgICA8RFQ+PEEgSFJFRj0iaHR0cDovL3d3dy5tZXRhZmlsdGVyLmNvbS8iIEFERF9E +QVRFPSIxMDQyNjQ2ODc0IiBMQVNUX1ZJU0lUPSIxMDQ4MTc0NjQyIiBMQVNUX01PRElGSUVEPSIx +MDQyNjQ2ODc0Ij5NZXRhZmlsdGVyICBDb21tdW5pdHkgV2VibG9nPC9BPg0KICAgICAgICA8RFQ+ +PEEgSFJFRj0iaHR0cDovL3d3dy5tcmJhcnJldHQuY29tLyIgQUREX0RBVEU9IjEwNDI2NDY4NzQi +IExBU1RfVklTSVQ9IjEwNDgxNzQ2NDIiIExBU1RfTU9ESUZJRUQ9IjEwNDI2NDY4NzQiPm1yYmFy +cmV0dC5jb208L0E+DQogICAgICAgIDxEVD48QSBIUkVGPSJodHRwOi8vd3d3LnBsYXN0aWMuY29t +LyIgQUREX0RBVEU9IjEwNDI2NDY4NzQiIExBU1RfVklTSVQ9IjEwNDgxNzQ2NDIiIExBU1RfTU9E +SUZJRUQ9IjEwNDI2NDY4NzQiPlBsYXN0aWM8L0E+DQogICAgICAgIDxEVD48QSBIUkVGPSJodHRw +Oi8vd3d3LnNhbG9uLmNvbS8iIEFERF9EQVRFPSIxMDQyNjQ2ODc0IiBMQVNUX1ZJU0lUPSIxMDQ4 +MTc0NjQyIiBMQVNUX01PRElGSUVEPSIxMDQyNjQ2ODc0Ij5TYWxvbi5jb208L0E+DQogICAgICAg +IDxEVD48QSBIUkVGPSJodHRwOi8vd3d3LnNoYXJwZXdvcmxkLmNvbS8iIEFERF9EQVRFPSIxMDQy +NjQ2ODc0IiBMQVNUX1ZJU0lUPSIxMDQ4MTc0NjQyIiBMQVNUX01PRElGSUVEPSIxMDQyNjQ2ODc0 +Ij5TSEFSUEVXT1JMRCAgYW1lcmljYSdzIG5vLiAxIHdlYnNpdGU8L0E+DQogICAgICAgIDxEVD48 +QSBIUkVGPSJodHRwOi8vd3d3LnNsYXNoZG90Lm9yZy8iIEFERF9EQVRFPSIxMDQyNjQ2ODc0IiBM +QVNUX1ZJU0lUPSIxMDQ4MTc0NjQyIiBMQVNUX01PRElGSUVEPSIxMDQyNjQ2ODc0Ij5TbGFzaGRv +dCBOZXdzIGZvciBuZXJkcywgc3R1ZmYgdGhhdCBtYXR0ZXJzPC9BPg0KICAgICAgICA8RFQ+PEEg +SFJFRj0iaHR0cDovL3d3dy5zdWNrLmNvbS8iIEFERF9EQVRFPSIxMDQyNjQ2ODc0IiBMQVNUX1ZJ +U0lUPSIxMDQ4MTc0NjQyIiBMQVNUX01PRElGSUVEPSIxMDQyNjQ2ODc0Ij5TdWNrLmNvbSBEYWls +eTwvQT4NCiAgICAgICAgPERUPjxBIEhSRUY9Imh0dHA6Ly93d3cudGFsa2hhcmRvbmxpbmUuY29t +L2luZGV4LnBocCIgQUREX0RBVEU9IjEwNDI2NDY4NzQiIExBU1RfVklTSVQ9IjEwNDgxNzQ2NDIi +IExBU1RfTU9ESUZJRUQ9IjEwNDI2NDY4NzQiPlRhbGsgSGFyZCBPbmxpbmUgRm9ydW1zPC9BPg0K +ICAgICAgICA8RFQ+PEEgSFJFRj0iaHR0cDovL3d3dy50aGVtb3JuaW5nbmV3cy5vcmcvIiBBRERf +REFURT0iMTA0MjY0Njg3NCIgTEFTVF9WSVNJVD0iMTA0ODE3NDY0MiIgTEFTVF9NT0RJRklFRD0i +MTA0MjY0Njg3NCI+VGhlIE1vcm5pbmcgTmV3czwvQT4NCiAgICAgICAgPERUPjxBIEhSRUY9Imh0 +dHA6Ly93d3cudGhlc3RhbmRhcmQuY29tLyIgQUREX0RBVEU9IjEwNDI2NDY4NzQiIExBU1RfVklT +SVQ9IjEwNDgxNzQ2NDIiIExBU1RfTU9ESUZJRUQ9IjEwNDI2NDY4NzQiPlRoZVN0YW5kYXJkLmNv +bSBob21lcGFnZTwvQT4NCiAgICAgICAgPERUPjxBIEhSRUY9Imh0dHA6Ly93d3cud2lyZWQuY29t +LyIgQUREX0RBVEU9IjEwNDI2NDY4NzQiIExBU1RfVklTSVQ9IjEwNDgxNzQ2NDIiIExBU1RfTU9E +SUZJRUQ9IjEwNDI2NDY4NzQiPldpcmVkIE5ld3M8L0E+DQogICAgPC9ETD48cD4NCiAgICA8RFQ+ +PEgzIEZPTERFRCBBRERfREFURT0iMTA0MTI2NTc4MyI+RGVsbDwvSDM+DQogICAgPERMPjxwPg0K +ICAgICAgICA8RFQ+PEEgSFJFRj0iaHR0cDovL3d3dy5kZWxsYXVjdGlvbi5jb20iIEFERF9EQVRF +PSIxMDQxMjY1NzkyIiBMQVNUX1ZJU0lUPSIxMDQ4MTc0NjQyIiBMQVNUX01PRElGSUVEPSIxMDQw +MjU2NzE4Ij5EZWxsIEF1Y3Rpb248L0E+DQogICAgICAgIDxEVD48QSBIUkVGPSJodHRwOi8vd3d3 +LmRlbGwuY29tLyIgQUREX0RBVEU9IjEwNDEyNjU3OTIiIExBU1RfVklTSVQ9IjEwNDgxNzQ2NDIi +IExBU1RfTU9ESUZJRUQ9IjEwNDAyNTY3MTgiPkRlbGw8L0E+DQogICAgICAgIDxEVD48QSBIUkVG +PSJodHRwOi8vd3d3LmRlbGxuZXQuY29tLyIgQUREX0RBVEU9IjEwNDEyNjU3OTIiIExBU1RfVklT +SVQ9IjEwNDgxNzQ2NDIiIExBU1RfTU9ESUZJRUQ9IjEwNDAyNTY3MTgiPkRlbGxuZXQ8L0E+DQog +ICAgICAgIDxEVD48QSBIUkVGPSJodHRwOi8vd3d3LmdpZ2FidXlzLmNvbS8iIEFERF9EQVRFPSIx +MDQxMjY1NzkyIiBMQVNUX1ZJU0lUPSIxMDQ4MTc0NjQyIiBMQVNUX01PRElGSUVEPSIxMDQwMjU2 +NzE4Ij5HaWdhYnV5czwvQT4NCiAgICAgICAgPERUPjxBIEhSRUY9Imh0dHA6Ly9zdXBwb3J0LmRl +bGwuY29tIiBBRERfREFURT0iMTA0MTI2NTc5MiIgTEFTVF9WSVNJVD0iMTA0ODE3NDY0MiIgTEFT +VF9NT0RJRklFRD0iMTA0MDI1NjcxOCI+U3VwcG9ydC5EZWxsLmNvbTwvQT4NCiAgICA8L0RMPjxw +Pg0KICAgIDxEVD48SDMgRk9MREVEIEFERF9EQVRFPSIxMDQyNjQ2ODc0Ij5lWmluZXM8L0gzPg0K +ICAgIDxETD48cD4NCiAgICAgICAgPERUPjxBIEhSRUY9Imh0dHA6Ly9hYW4ub3JnL2diYXNlL0Fh +bi9pbmRleCIgQUREX0RBVEU9IjEwNDI2NDY4NzQiIExBU1RfVklTSVQ9IjEwNDgxNzQ2NDIiIExB +U1RfTU9ESUZJRUQ9IjEwNDI2NDY4NzQiPkFzc29jaWF0aW9uIG9mIEFsdGVybmF0aXZlIE5ld3N3 +ZWVrbGllczwvQT4NCiAgICAgICAgPERUPjxBIEhSRUY9Imh0dHA6Ly9sb3N0YXRzZWEubmV0L0xB +Uy9pbmRleDIuaHRtIiBBRERfREFURT0iMTA0MjY0Njg3NCIgTEFTVF9WSVNJVD0iMTA0ODE3NDY0 +MiIgTEFTVF9NT0RJRklFRD0iMTA0MjY0Njg3NCI+TE9TVCBBVCBTRUEgb25saW5lPC9BPg0KICAg +ICAgICA8RFQ+PEEgSFJFRj0iaHR0cDovL3d3dy5tb3RoZXJqb25lcy5jb20vIiBBRERfREFURT0i +MTA0MjY0Njg3NCIgTEFTVF9WSVNJVD0iMTA0ODE3NDY0MiIgTEFTVF9NT0RJRklFRD0iMTA0MjY0 +Njg3NCI+TW90aGVySm9uZXMuY29tIC0tIE5ld3MgYW5kIFJlc291cmNlcyBmb3IgdGhlIFNrZXB0 +aWNhbCBDaXRpemVuPC9BPg0KICAgICAgICA8RFQ+PEEgSFJFRj0iaHR0cDovL3d3dy5zaGlmdC5j +b20vaW5kZXguYXNwIiBBRERfREFURT0iMTA0MjY0Njg3NCIgTEFTVF9WSVNJVD0iMTA0ODE3NDY0 +MiIgTEFTVF9NT0RJRklFRD0iMTA0MjY0Njg3NCI+U2hpZnQuY29tPC9BPg0KICAgIDwvREw+PHA+ +DQogICAgPERUPjxIMyBGT0xERUQgQUREX0RBVEU9IjEwNDI2NDY4NzQiPkZpbmFuY2VzPC9IMz4N +CiAgICA8REw+PHA+DQogICAgICAgIDxEVD48QSBIUkVGPSJodHRwOi8vcGVyc29uYWwzMDAuZmlk +ZWxpdHkuY29tL2dlbi9tZmxmaWQvNS8zMTYwNjkzMDEuaHRtbCIgQUREX0RBVEU9IjEwNDI2NDY4 +NzQiIExBU1RfVklTSVQ9IjEwNDgxNzQ2NDIiIExBU1RfTU9ESUZJRUQ9IjEwNDI2NDY4NzQiPkZp +ZGVsaXR5IEFzc2V0IE1hbmFnZXIgR3Jvd3RoPC9BPg0KICAgICAgICA8RFQ+PEEgSFJFRj0iaHR0 +cDovL3d3dy5mb29sLmNvbS9pbmRleC5odG0iIEFERF9EQVRFPSIxMDQyNjQ2ODc0IiBMQVNUX1ZJ +U0lUPSIxMDQ4MTc0NjQyIiBMQVNUX01PRElGSUVEPSIxMDQyNjQ2ODc0Ij5Gb29sLmNvbSBGaW5h +bmNlIGFuZCBGb2xseSAtLSBNYWluIFBhZ2U8L0E+DQogICAgICAgIDxEVD48QSBIUkVGPSJodHRw +Oi8vd3d3LmZvb2wuY29tL21vbmV5LzQwMWsvNDAxazAwMy5odG0/cmVmPU1URjQwMWsiIEFERF9E +QVRFPSIxMDQyNjQ2ODc0IiBMQVNUX1ZJU0lUPSIxMDQ4MTc0NjQyIiBMQVNUX01PRElGSUVEPSIx +MDQyNjQ2ODc0Ij5Gb29sLmNvbSBJcyBZb3VyIDQwMShrKSBGb29saXNoIC0tIEhvdyB0byBQaWNr +IGEgV2lubmVyPC9BPg0KICAgICAgICA8RFQ+PEEgSFJFRj0iaHR0cHM6Ly9haXMyLnRpYWEtY3Jl +Zi5vcmcvY2dpLWJpbi9XZWJPYmplY3RzL0lBUGFydGljaXBhbnQiIEFERF9EQVRFPSIxMDQyNjQ2 +ODc0IiBMQVNUX1ZJU0lUPSIxMDQ4MTc0NjQyIiBMQVNUX01PRElGSUVEPSIxMDQyNjQ2ODc0Ij5J +QVBhcnRpY2lwYW50PC9BPg0KICAgICAgICA8RFQ+PEEgSFJFRj0iaHR0cDovL215ZmlkZWxpdHkz +MDAubWVtYmVycy5maWRlbGl0eS5jb20vbXlGaWRlbGl0eS9pbmRleC5qaHRtbCIgQUREX0RBVEU9 +IjEwNDI2NDY4NzQiIExBU1RfVklTSVQ9IjEwNDgxNzQ2NDIiIExBU1RfTU9ESUZJRUQ9IjEwNDI2 +NDY4NzQiPk15IEZpZGVsaXR5PC9BPg0KICAgICAgICA8RFQ+PEEgSFJFRj0iaHR0cDovL215LmZv +b2wuY29tLyIgQUREX0RBVEU9IjEwNDI2NDY4NzQiIExBU1RfVklTSVQ9IjEwNDgxNzQ2NDIiIExB +U1RfTU9ESUZJRUQ9IjEwNDI2NDY4NzQiPlRoZSBNb3RsZXkgRm9vbCAtLSBNeSBGb29sPC9BPg0K +ICAgICAgICA8RFQ+PEEgSFJFRj0iaHR0cDovL3d3dy5mYWJpYW4uY29tLyIgQUREX0RBVEU9IjEw +NDI2NDY4NzQiIExBU1RfVklTSVQ9IjEwNDgxNzQ2NDIiIExBU1RfTU9ESUZJRUQ9IjEwNDI2NDY4 +NzQiPldlbGNvbWUgdG8gTWF2ZXJpY2sgSW52ZXN0aW5nIHdpdGggRG91ZyBGYWJpYW4hPC9BPg0K +ICAgIDwvREw+PHA+DQogICAgPERUPjxIMyBGT0xERUQgQUREX0RBVEU9IjEwNDI2NDY4NzQiPkZv +bnRzPC9IMz4NCiAgICA8REw+PHA+DQogICAgICAgIDxEVD48QSBIUkVGPSJodHRwOi8vd3d3LmRz +ZzQuY29tLzA0L2V4dHJhL2JpdG1hcC9pbmRleC5odG1sIiBBRERfREFURT0iMTA0MjY0Njg3NCIg +TEFTVF9WSVNJVD0iMTA0ODE3NDY0MiIgTEFTVF9NT0RJRklFRD0iMTA0MjY0Njg3NCI+MDQgIGV4 +dHJhICBiaXRtYXA8L0E+DQogICAgICAgIDxEVD48QSBIUkVGPSJodHRwOi8vd3d3LjEwMDFmb250 +cy5jb20vIiBBRERfREFURT0iMTA0MjY0Njg3NCIgTEFTVF9WSVNJVD0iMTA0ODE3NDY0MiIgTEFT +VF9NT0RJRklFRD0iMTA0MjY0Njg3NCI+MTAwMSBGb250cyAuY29tPC9BPg0KICAgICAgICA8RFQ+ +PEEgSFJFRj0iaHR0cDovL3d3dy5ibGFtYm90LmNvbS90aXRsZS5odG1sIiBBRERfREFURT0iMTA0 +MzM1NDA3NiIgTEFTVF9WSVNJVD0iMTA0ODE3NDY0MiIgTEFTVF9NT0RJRklFRD0iMTA0MzM1NDA3 +NiI+QkxBTUJPVCBDb21pYyBGb250cyAmIExldHRlcmluZzwvQT4NCiAgICAgICAgPERUPjxBIEhS +RUY9Imh0dHA6Ly93d3cuY2hhbmsuY29tL2ZvbnRzLnBocCIgQUREX0RBVEU9IjEwNDI2NDY4NzQi +IExBU1RfVklTSVQ9IjEwNDgxNzQ2NDIiIExBU1RfTU9ESUZJRUQ9IjEwNDI2NDY4NzQiPkNIQU5L +IEZPTlRTITwvQT4NCiAgICAgICAgPERUPjxBIEhSRUY9Imh0dHA6Ly93d3cuY2hlYXBza2F0ZWZv +bnRzLmNvbS9mb250cy5odG1sIiBBRERfREFURT0iMTA0MjY0Njg3NCIgTEFTVF9WSVNJVD0iMTA0 +ODE3NDY0MiIgTEFTVF9NT0RJRklFRD0iMTA0MjY0Njg3NCI+Q2hlYXBza2F0ZUZvbnRzIC0gRm9u +dHM8L0E+DQogICAgICAgIDxEVD48QSBIUkVGPSJodHRwOi8vd3d3LndlYm1hc3RlcmJhc2UuY29t +L2FydGljbGUucGhwLzgxMSIgQUREX0RBVEU9IjEwNDI2NDY4NzQiIExBU1RfVklTSVQ9IjEwNDgx +NzQ2NDMiIExBU1RfTU9ESUZJRUQ9IjEwNDI2NDY4NzQiPkRyLiBEZXNpZ24ncyBEZWZpbml0aXZl +IERlc2lnbiBMaW5rcyBMaXN0PC9BPg0KICAgICAgICA8RFQ+PEEgSFJFRj0iaHR0cDovL2Rlc2t0 +b3BwdWIuYWJvdXQuY29tL2NzL3RoZW1lZm9udHMvIiBBRERfREFURT0iMTA0MjY0Njg3NCIgTEFT +VF9WSVNJVD0iMTA0ODE3NDY0MyIgTEFTVF9NT0RJRklFRD0iMTA0MjY0Njg3NCI+Rm9udCBDb2xs +ZWN0aW9ucyBpbiBTcGVjaWFsdHkgVGhlbWVzPC9BPg0KICAgICAgICA8RFQ+PEEgSFJFRj0iaHR0 +cDovL3d3dy5mcmVzaG1lZGlhLm5sLyIgQUREX0RBVEU9IjEwNDI2NDY4NzQiIExBU1RfVklTSVQ9 +IjEwNDgxNzQ2NDMiIExBU1RfTU9ESUZJRUQ9IjEwNDI2NDY4NzQiPmZvbnQtby12aWxsZTwvQT4N +CiAgICAgICAgPERUPjxBIEhSRUY9Imh0dHA6Ly93d3cubW9vcnN0YXRpb24ub3JnL3R5cG9hc2lz +L2Rlc2lnbmVycy9mb250bWFrZXIvZm9udG1ha2VyLmh0bSIgQUREX0RBVEU9IjEwNDI2NDY4NzQi +IExBU1RfVklTSVQ9IjEwNDgxNzQ2NDMiIExBU1RfTU9ESUZJRUQ9IjEwNDI2NDY4NzQiPkZvbnRt +YWtlciBbUEFHRSAxXTwvQT4NCiAgICAgICAgPERUPjxBIEhSRUY9Imh0dHA6Ly93d3cuZm9udG9z +YXVydXMuY29tL2ZvbnRzLmFzcD90YXJnZXQ9bnVsbCIgQUREX0RBVEU9IjEwNDI2NDY4NzQiIExB +U1RfVklTSVQ9IjEwNDgxNzQ2NDMiIExBU1RfTU9ESUZJRUQ9IjEwNDI2NDY4NzQiPkZvbnRvc2F1 +cnVzIFRleHQ8L0E+DQogICAgICAgIDxEVD48QSBIUkVGPSJodHRwOi8vd3d3LmZvbnRzbnRoaW5n +cy5jb20vIiBBRERfREFURT0iMTA0MjY0Njg3NCIgTEFTVF9WSVNJVD0iMTA0ODE3NDY0MyIgTEFT +VF9NT0RJRklFRD0iMTA0MjY0Njg3NCI+Rm9udHMgJiBUaGluZ3MgLSB0aGUgbW9zdCB1bnVzdWFs +IGZvbnRzIG9ubGluZS4uLjwvQT4NCiAgICAgICAgPERUPjxBIEhSRUY9Imh0dHA6Ly93d3cuZnJl +ZS10eXBld3JpdGVyLWZvbnRzLmNvbS9Gb250cy5odG1sIiBBRERfREFURT0iMTA0MjY0Njg3NCIg +TEFTVF9WSVNJVD0iMTA0ODE3NDY0MyIgTEFTVF9NT0RJRklFRD0iMTA0MjY0Njg3NCI+RnJlZSBU +eXBld3JpdGVyIEZvbnRzPC9BPg0KICAgICAgICA8RFQ+PEEgSFJFRj0iaHR0cDovL3d3dy5sYXJh +YmllZm9udHMuY29tLyIgQUREX0RBVEU9IjEwNDI2NDY4NzQiIExBU1RfVklTSVQ9IjEwNDgxNzQ2 +NDMiIExBU1RfTU9ESUZJRUQ9IjEwNDI2NDY4NzQiPkxhcmFiaWUgRm9udHM8L0E+DQogICAgICAg +IDxEVD48QSBIUkVGPSJodHRwOi8vd3d3Lm9yZ2RvdC5jb20vYWxpYXNmb250cy8iIEFERF9EQVRF +PSIxMDQ2MTA0MDM4IiBMQVNUX1ZJU0lUPSIxMDQ4MTc0NjQzIiBMQVNUX01PRElGSUVEPSIxMDQ2 +MTA0MDM5Ij5Pcmdkb3Qgb3BlbiBzb3VyY2UgcGl4ZWwgYW5kIGxlZCBmb250cyBmb3IgZmxhc2g8 +L0E+DQogICAgICAgIDxEVD48QSBIUkVGPSJodHRwOi8vd3d3LnBoYW50b21wb3dlci5kZS9waGFu +dG9tcGhvbnRzLmh0bWwiIEFERF9EQVRFPSIxMDQyNjQ2ODc0IiBMQVNUX1ZJU0lUPSIxMDQ4MTc0 +NjQzIiBMQVNUX01PRElGSUVEPSIxMDQyNjQ2ODc0Ij5waGFudG9tcGhvbnRzPC9BPg0KICAgICAg +ICA8RFQ+PEEgSFJFRj0iaHR0cDovL3d3dy5pYW1jYWwuY29tL2ZvbnRzLyIgQUREX0RBVEU9IjEw +NDI2NDY4NzQiIExBU1RfVklTSVQ9IjEwNDgxNzQ2NDMiIExBU1RfTU9ESUZJRUQ9IjEwNDI2NDY4 +NzQiPlNtYWxsIEZvbnRzIGJ5IENhbCBIZW5kZXJzb248L0E+DQogICAgICAgIDxEVD48QSBIUkVG +PSJodHRwOi8vd3d3LmRvZ2hhdXNlLmNvbS9mb250cy5odG1sIiBBRERfREFURT0iMTA0MjY0Njg3 +NCIgTEFTVF9WSVNJVD0iMTA0ODE3NDY0MyIgTEFTVF9NT0RJRklFRD0iMTA0MjY0Njg3NCI+VGhl +IERvZyBIYXVzZSBGb250czwvQT4NCiAgICAgICAgPERUPjxBIEhSRUY9Imh0dHA6Ly93d3cud3Bk +ZmQuY29tL3dwZHR5cG8zYS5odG0iIEFERF9EQVRFPSIxMDQyNjQ2ODc0IiBMQVNUX1ZJU0lUPSIx +MDQ4MTc0NjQzIiBMQVNUX01PRElGSUVEPSIxMDQyNjQ2ODc0Ij5XZWIgUGFnZSBEZXNpZ24gZm9y +IERlc2lnbmVycyAtIFBpeGVsIEZvbnRzPC9BPg0KICAgICAgICA8RFQ+PEEgSFJFRj0iaHR0cDov +L3d3dy5waXp6YWR1ZGUuZGsvZm9udHMuaHRtbCIgQUREX0RBVEU9IjEwNDI2NDY4NzQiIExBU1Rf +VklTSVQ9IjEwNDgxNzQ2NDMiIExBU1RfTU9ESUZJRUQ9IjEwNDI2NDY4NzQiPnd3dy5waXp6YWR1 +ZGUuZGsgIGZvbnRzLmh0bWw8L0E+DQogICAgPC9ETD48cD4NCiAgICA8RFQ+PEgzIEZPTERFRCBB +RERfREFURT0iMTA0MjY0Njg3NCI+RnVubmllczwvSDM+DQogICAgPERMPjxwPg0KICAgICAgICA8 +RFQ+PEEgSFJFRj0iaHR0cDovL3d3dy5zYXRpcmV3aXJlLmNvbS9pbmRleC5zaHRtbCIgQUREX0RB +VEU9IjEwNDI2NDY4NzQiIExBU1RfVklTSVQ9IjEwNDgxNzQ2NDMiIExBU1RfTU9ESUZJRUQ9IjEw +NDI2NDY4NzQiPlNhdGlyZVdpcmUgIGRvdC5jb20uZWR5PC9BPg0KICAgICAgICA8RFQ+PEEgSFJF +Rj0iaHR0cDovL3NubC5qdC5vcmcvIiBBRERfREFURT0iMTA0MjY0Njg3NCIgTEFTVF9WSVNJVD0i +MTA0ODE3NDY0MyIgTEFTVF9NT0RJRklFRD0iMTA0MjY0Njg3NCI+U05MIE1NIChTYXR1cmRheSBO +aWdodCBMaXZlIE11bHRpbWVkaWEgLSBzbmwuanQub3JnKSBZT1VSIFNPVVJDRSBGT1IgU05MIE1V +TFRJTUVESUEsIHNhdHVyZGF5IG48L0E+DQogICAgICAgIDxEVD48QSBIUkVGPSJodHRwOi8vcmlu +a3dvcmtzLmNvbS9kaWFsZWN0LyIgQUREX0RBVEU9IjEwNDI2NDY4NzQiIExBU1RfVklTSVQ9IjEw +NDgxNzQ2NDMiIExBU1RfTU9ESUZJRUQ9IjEwNDI2NDY4NzQiPlRoZSBEaWFsZWN0aXplcjwvQT4N +CiAgICAgICAgPERUPjxBIEhSRUY9Imh0dHA6Ly9pbmZvbWVkaWEuanQub3JnL2luZGV4LnBodG1s +IiBBRERfREFURT0iMTA0MjY0Njg3NCIgTEFTVF9WSVNJVD0iMTA0ODE3NDY0MyIgTEFTVF9NT0RJ +RklFRD0iMTA0MjY0Njg3NCI+V2VsY29tZSB0byBTTkwgSW5mb01lZGlhPC9BPg0KICAgIDwvREw+ +PHA+DQogICAgPERUPjxIMyBGT0xERUQgQUREX0RBVEU9IjEwNDI2NDY4NzQiPkdyYXRlZnVsIERl +YWQ8L0gzPg0KICAgIDxETD48cD4NCiAgICAgICAgPERUPjxBIEhSRUY9Imh0dHA6Ly9jZmEtd3d3 +LmhhcnZhcmQuZWR1L2NmYS9wcy9zcGVjaWFsL0dhcmNpYS5odG1sIiBBRERfREFURT0iMTA0MjY0 +Njg3NCIgTEFTVF9WSVNJVD0iMTA0ODE3NDY0MyIgTEFTVF9NT0RJRklFRD0iMTA0MjY0Njg3NCI+ +KDQ0NDIpIEdhcmNpYSAoU3Rhcik8L0E+DQogICAgICAgIDxEVD48QSBIUkVGPSJodHRwOi8vY2Zh +LXd3dy5oYXJ2YXJkLmVkdS9jZmEvcHMvc3BlY2lhbC9yb2NrbnJvbGwvMDAwNDQ0Mi5odG1sIiBB +RERfREFURT0iMTA0MjY0Njg3NCIgTEFTVF9WSVNJVD0iMTA0ODE3NDY0MyIgTEFTVF9NT0RJRklF +RD0iMTA0MjY0Njg3NCI+KDQ0NDIpIEdhcmNpYTwvQT4NCiAgICAgICAgPERUPjxBIEhSRUY9Imh0 +dHA6Ly9zbGF0ZS5tc24uY29tLz9pZD0yMDcwMjUxIiBBRERfREFURT0iMTA0MjY0Njg3NCIgTEFT +VF9WSVNJVD0iMTA0ODE3NDY0MyIgTEFTVF9NT0RJRklFRD0iMTA0MjY0Njg3NCI+QSBMb25nLCBT +dGFpZCBUcmlwIC0gSG93IERlYWRoZWFkcyBydWluZWQgdGhlIEdyYXRlZnVsIERlYWQuIEJ5IE1h +cmOgV2VpbmdhcnRlbjwvQT4NCiAgICAgICAgPERUPjxBIEhSRUY9Imh0dHA6Ly93d3cuYWNjZXNz +cGxhY2UuY29tL2dyYXRlZnVsLmh0bSIgQUREX0RBVEU9IjEwNDI2NDY4NzQiIExBU1RfVklTSVQ9 +IjEwNDgxNzQ2NDMiIExBU1RfTU9ESUZJRUQ9IjEwNDI2NDY4NzQiPkFjY2VzcyBQbGFjZSBHcmF0 +ZWZ1bCBEZWFkIC0gTHlyaWNzLCBQaWN0dXJlcywgTVAzLCBTZXRsaXN0cy4uPC9BPg0KICAgICAg +ICA8RFQ+PEEgSFJFRj0iaHR0cDovL3d3dy50aGViZWFyLm9yZy8iIEFERF9EQVRFPSIxMDQyNjQ2 +ODc0IiBMQVNUX1ZJU0lUPSIxMDQ4MTc0NjQzIiBMQVNUX01PRElGSUVEPSIxMDQyNjQ2ODc0Ij5C +RUFSJ1MgQVJUIFBBR0VTPC9BPg0KICAgICAgICA8RFQ+PEEgSFJFRj0iaHR0cDovL3d3dy5uaWku +bmV0L35vYmllMS9kZWFkY2QvYmV0dHlfYm9hcmRfaW5mby5odG0iIEFERF9EQVRFPSIxMDQyNjQ2 +ODc0IiBMQVNUX1ZJU0lUPSIxMDQ4MTc0NjQzIiBMQVNUX01PRElGSUVEPSIxMDQyNjQ2ODc0Ij5C +ZXR0eSBCb2FyZCBpbmZvPC9BPg0KICAgICAgICA8RFQ+PEEgSFJFRj0iaHR0cDovL3d3dy5ibGFp +cmphY2tzb24uY29tLyIgQUREX0RBVEU9IjEwNDI2NDY4NzQiIExBU1RfVklTSVQ9IjEwNDgxNzQ2 +NDMiIExBU1RfTU9ESUZJRUQ9IjEwNDI2NDY4NzQiPkJsYWlyIEphY2tzb24gLSBHYXJjaWE8L0E+ +DQogICAgICAgIDxEVD48QSBIUkVGPSJodHRwOi8vZ3JvdXBzLmdvb2dsZS5jb20vZ3JvdXBzP3E9 +YnVmZmFsbytncm91cDpyZWMubXVzaWMuZ2RlYWQmaGw9ZW4mYXNfZHJyYj1iJmFzX21pbmQ9MTIm +YXNfbWlubT01JmFzX21pbnk9MTk4MSZhc19tYXhkPTE3JmFzX21heG09MTImYXNfbWF4eT0xOTkx +JnJudW09OCZzZWxtPTE5OTBKdWwyMC4xNDI2MTYuMTM3OTIlNDB1dmFhcnBhLlZpcmdpbmlhLkVE +VSIgQUREX0RBVEU9IjEwNDI2NDY4NzQiIExBU1RfVklTSVQ9IjEwNDgxNzQ2NDMiIExBU1RfTU9E +SUZJRUQ9IjEwNDI2NDY4NzQiPkJ1ZmZhbG8gTmV3cyBDb25jZXJ0IFJldmlldyA3LTE3LTkwPC9B +Pg0KICAgICAgICA8RFQ+PEEgSFJFRj0iaHR0cDovL2l2ZXJtY2xlb2QudHJpcG9kLmNvbS9wb3N0 +ZXJmcmFtZXMuaHRtbCIgQUREX0RBVEU9IjEwNDI2NDY4NzQiIExBU1RfVklTSVQ9IjEwNDgxNzQ2 +NDMiIExBU1RfTU9ESUZJRUQ9IjEwNDI2NDY4NzQiPkNvbmNlcnQgUG9zdGVyczwvQT4NCiAgICAg +ICAgPERUPjxBIEhSRUY9Imh0dHA6Ly9kYi5ldHJlZS5vcmcvYnJvd3NlYnlteXNob3dzLnBocD9h +cnRpc3Q9MiIgQUREX0RBVEU9IjEwNDI2NDY4NzQiIExBU1RfVklTSVQ9IjEwNDgxNzQ2NDMiIExB +U1RfTU9ESUZJRUQ9IjEwNDI2NDY4NzQiPmRiLmV0cmVlLm9yZyAtIEdEIGJ5IFllYXI8L0E+DQog +ICAgICAgIDxEVD48QSBIUkVGPSJodHRwOi8vcHViNy5lemJvYXJkLmNvbS9iZGVhZGhvb2siIEFE +RF9EQVRFPSIxMDQ4MDE0NjQ2IiBMQVNUX1ZJU0lUPSIxMDQ4MTc0NjQzIiBMQVNUX01PRElGSUVE +PSIxMDQ4MDE0NjQ3Ij5EZWFkaG9vayBAIHd3dy5lemJvYXJkLmNvbTwvQT4NCiAgICAgICAgPERU +PjxBIEhSRUY9Imh0dHA6Ly93d3cuZGVhZGxlZ3MuY29tLyIgQUREX0RBVEU9IjEwNDI2NDY4NzQi +IExBU1RfVklTSVQ9IjEwNDgxNzQ2NDMiIExBU1RfTU9ESUZJRUQ9IjEwNDI2NDY4NzQiPmRlYWRs +ZWdzLmNvbSBtcDMgYW5kIHJlYWwgYXVkaW8gbGlicmFyeSBvZiBncmF0ZWZ1bCBkZWFkIHF1YWxp +dHkgYXVkaW8uIE1wMyBzaG93cyBhbmQgTXAzIGJvb3RsPC9BPg0KICAgICAgICA8RFQ+PEEgSFJF +Rj0iaHR0cDovL3d3dy5kZWFkc2hvdy5jb20vIiBBRERfREFURT0iMTA0MjY0Njg3NCIgTEFTVF9W +SVNJVD0iMTA0ODE3NDY0MyIgTEFTVF9NT0RJRklFRD0iMTA0MjY0Njg3NCI+RGVhZHNob3cuY29t +PC9BPg0KICAgICAgICA8RFQ+PEEgSFJFRj0iaHR0cDovL3d3dy5nZW9jaXRpZXMuY29tL1N1bnNl +dFN0cmlwL1BhbG1zLzU1MjAvZGVhZDEuaHRtbCIgQUREX0RBVEU9IjEwNDI2NDY4NzQiIExBU1Rf +VklTSVQ9IjEwNDgxNzQ2NDMiIExBU1RfTU9ESUZJRUQ9IjEwNDI2NDY4NzQiPkRpZCBZb3UgS25v +dzwvQT4NCiAgICAgICAgPERUPjxBIEhSRUY9Imh0dHA6Ly9sYXdyb21haW5lLmNvbS9pZDIwLmh0 +bSIgQUREX0RBVEU9IjEwNDI2NDY4NzQiIExBU1RfVklTSVQ9IjEwNDgxNzQ2NDMiIExBU1RfTU9E +SUZJRUQ9IjEwNDI2NDY4NzQiPkRvdWcgSXJ3aW5zIEZpcnN0IEFtZW5kZWQgUGV0aXRpb24gdW5k +ZXIgUHJvYmF0ZSBDb2RlIHNlY3Rpb24gOTg2MDwvQT4NCiAgICAgICAgPERUPjxBIEhSRUY9Imh0 +dHA6Ly93d3cuZG96aW4uY29tLyMiIEFERF9EQVRFPSIxMDQyNjQ2ODc0IiBMQVNUX1ZJU0lUPSIx +MDQ4MTc0NjQzIiBMQVNUX01PRElGSUVEPSIxMDQyNjQ2ODc0Ij5Eb3ppbi5jb208L0E+DQogICAg +ICAgIDxEVD48QSBIUkVGPSJodHRwOi8vd3d3LmV1cm9kZWFkLm5ldC8iIEFERF9EQVRFPSIxMDQy +NjQ2ODc0IiBMQVNUX1ZJU0lUPSIxMDQ4MTc0NjQzIiBMQVNUX01PRElGSUVEPSIxMDQyNjQ2ODc0 +Ij5FdXJvZGVhZC5uZXQgLSBmb3IgR3JhdGVmdWwgRGVhZCBmYW5zIGV2ZXJ5d2hlcmU8L0E+DQog +ICAgICAgIDxEVD48QSBIUkVGPSJodHRwOi8vZXVyb2RlYWQubmV0L3BhZ2VzLyIgQUREX0RBVEU9 +IjEwNDI2NDY4NzQiIExBU1RfVklTSVQ9IjEwNDgxNzQ2NDMiIExBU1RfTU9ESUZJRUQ9IjEwNDI2 +NDY4NzQiPkV1cm9kZWFkLm5ldCBMaW5rcyBEaXJlY3Rvcnk8L0E+DQogICAgICAgIDxEVD48QSBI +UkVGPSJodHRwOi8vd3d3LnBzeWtlZGVsYm9rLnNlL2dhcmNpYXNfZ3VpdGFycy5odG1sIiBBRERf +REFURT0iMTA0MjY0Njg3NCIgTEFTVF9WSVNJVD0iMTA0ODE3NDY0MyIgTEFTVF9NT0RJRklFRD0i +MTA0MjY0Njg3NCI+R2FyY2lhJ3MgR3VpdGFycyBNYWMgVmlkZW8gR2FtZTwvQT4NCiAgICAgICAg +PERUPjxBIEhSRUY9Imh0dHA6Ly93d3cuZ2RjZGNvdmVycy5jb20vIiBBRERfREFURT0iMTA0MjY0 +Njg3NCIgTEFTVF9WSVNJVD0iMTA0ODE3NDY0MyIgTEFTVF9NT0RJRklFRD0iMTA0MjY0Njg3NCI+ +R0QgQ0QtUiBDb3ZlcnM8L0E+DQogICAgICAgIDxEVD48QSBIUkVGPSJodHRwOi8vd3d3Lmdvb2ds +ZS5jb20vc2VhcmNoP3E9Z3JhdGVmdWwrZGVhZCsxJTJGMjAlMkY3OSIgQUREX0RBVEU9IjEwNDI2 +NDY4NzQiIExBU1RfVklTSVQ9IjEwNDgxNzQ2NDMiIExBU1RfTU9ESUZJRUQ9IjEwNDI2NDY4NzQi +Pkdvb2dsZSBTZWFyY2ggZ3JhdGVmdWwgZGVhZCAxLTIwLTc5PC9BPg0KICAgICAgICA8RFQ+PEEg +SFJFRj0iaHR0cDovL2dyb3Vwcy5nb29nbGUuY29tL2dyb3Vwcz9obD1lbiZscj0mc2FmZT1vZmYm +Z3JvdXA9cmVjLm11c2ljLmdkZWFkIiBBRERfREFURT0iMTA0MjY0Njg3NCIgTEFTVF9WSVNJVD0i +MTA0ODE3NDY0MyIgTEFTVF9NT0RJRklFRD0iMTA0MjY0Njg3NCI+R29vZ2xlIFNlYXJjaCByZWMu +bXVzaWMuZ2RlYWQ8L0E+DQogICAgICAgIDxEVD48QSBIUkVGPSJodHRwOi8vZGlyZWN0b3J5Lmdv +b2dsZS5jb20vVG9wL0FydHMvTXVzaWMvQmFuZHNfYW5kX0FydGlzdHMvRy9HcmF0ZWZ1bF9EZWFk +LyIgQUREX0RBVEU9IjEwNDI2NDY4NzQiIExBU1RfVklTSVQ9IjEwNDgxNzQ2NDMiIExBU1RfTU9E +SUZJRUQ9IjEwNDI2NDY4NzQiPkdvb2dsZSBXZWIgRGlyZWN0b3J5IC0gQXJ0cyAgTXVzaWMgIEJh +bmRzoGFuZKBBcnRpc3RzICBHICBHcmF0ZWZ1bKBEZWFkPC9BPg0KICAgICAgICA8RFQ+PEEgSFJF +Rj0iaHR0cDovL3d3dy5ncmFtbXkuY29tL2ZlYXR1cmVzLzAxMTdfbWNuYWxseS5odG1sIiBBRERf +REFURT0iMTA0MjY0Njg3NCIgTEFTVF9WSVNJVD0iMTA0ODE3NDY0MyIgTEFTVF9NT0RJRklFRD0i +MTA0MjY0Njg3NCI+R1JBTU1ZLmNvbSAtIEludGVydmlldyB3aXRoIERlbm5pcyBNY05hbGx5PC9B +Pg0KICAgICAgICA8RFQ+PEEgSFJFRj0iaHR0cDovL3d3dy5ncmF0ZWZ1bGRheS5jb20vIiBBRERf +REFURT0iMTA0MjY0Njg3NCIgTEFTVF9WSVNJVD0iMTA0ODE3NDY0MyIgTEFTVF9NT0RJRklFRD0i +MTA0MjY0Njg3NCI+R3JhdGVmdWwgRGF5PC9BPg0KICAgICAgICA8RFQ+PEEgSFJFRj0iaHR0cDov +L3d3dy53ZWxsLmNvbS91c2VyL2FsbWFuYWMvIiBBRERfREFURT0iMTA0MjY0Njg3NCIgTEFTVF9W +SVNJVD0iMTA0ODE3NDY0MyIgTEFTVF9NT0RJRklFRD0iMTA0MjY0Njg3NCI+R3JhdGVmdWwgRGVh +ZCBBbG1hbmFjPC9BPg0KICAgICAgICA8RFQ+PEEgSFJFRj0iaHR0cDovL3d3dy5iYXJuY2FyZC5j +b20vZ2QuaHRtbCIgQUREX0RBVEU9IjEwNDI2NDY4NzQiIExBU1RfVklTSVQ9IjEwNDgxNzQ2NDMi +IExBU1RfTU9ESUZJRUQ9IjEwNDI2NDY4NzQiPkdSQVRFRlVMIERFQUQgQVQgV0lOVEVSTEFORCwg +TkVXIFlFQVInUyAxOTcyLTczPC9BPg0KICAgICAgICA8RFQ+PEEgSFJFRj0iaHR0cDovL3d3dy5k +ZWFkZGlzYy5jb20vIiBBRERfREFURT0iMTA0MjY0Njg3NCIgTEFTVF9WSVNJVD0iMTA0ODE3NDY0 +MyIgTEFTVF9NT0RJRklFRD0iMTA0MjY0Njg3NCI+R3JhdGVmdWwgRGVhZCBGYW1pbHkgRGlzY29n +cmFwaHk8L0E+DQogICAgICAgIDxEVD48QSBIUkVGPSJodHRwOi8vd3d3LmNzLmNtdS5lZHUvfm1s +ZW9uZS9nZGVhZC9mYXEvZmFxLmh0bWwiIEFERF9EQVRFPSIxMDQyNjQ2ODc0IiBMQVNUX1ZJU0lU +PSIxMDQ4MTc0NjQzIiBMQVNUX01PRElGSUVEPSIxMDQyNjQ2ODc0Ij5HcmF0ZWZ1bCBEZWFkIEZy +ZXF1ZW50bHkgQXNrZWQgUXVlc3Rpb25zPC9BPg0KICAgICAgICA8RFQ+PEEgSFJFRj0iaHR0cDov +L3d3dy5uZXRyZWFjaC5uZXQvfm5ob2plbS9kZWFkbGluay5odG0iIEFERF9EQVRFPSIxMDQyNjQ2 +ODc0IiBMQVNUX1ZJU0lUPSIxMDQ4MTc0NjQzIiBMQVNUX01PRElGSUVEPSIxMDQyNjQ2ODc0Ij5H +cmF0ZWZ1bCBEZWFkIExpbmtzPC9BPg0KICAgICAgICA8RFQ+PEEgSFJFRj0iaHR0cDovL3d3dy5n +ZGxpdmUuY29tLyIgQUREX0RBVEU9IjEwNDI2NDY4NzQiIExBU1RfVklTSVQ9IjEwNDgxNzQ2NDMi +IExBU1RfTU9ESUZJRUQ9IjEwNDI2NDY4NzQiPkdSQVRFRlVMIERFQUQgTElWRSAtIHd3dy5nZGxp +dmUuY29tPC9BPg0KICAgICAgICA8RFQ+PEEgSFJFRj0iaHR0cDovL3d3dzMuY2xlYXJsaWdodC5j +b20vfmFjc2EvaW50cm8uaHRtIiBBRERfREFURT0iMTA0MjY0Njg3NCIgTEFTVF9WSVNJVD0iMTA0 +ODE3NDY0MyIgTEFTVF9NT0RJRklFRD0iMTA0MjY0Njg3NCI+R3JhdGVmdWwgRGVhZCBMeXJpYyBB +bmQgU29uZyBGaW5kZXI8L0E+DQogICAgICAgIDxEVD48QSBIUkVGPSJodHRwOi8vd3d3Lmd1aXRh +ci5jb20vRzJLZmVhdHVyZXMvdmlld2ZlYXR1cmUuYXNwP2ZlYXR1cmVJRD00MiIgQUREX0RBVEU9 +IjEwNDI2NDY4NzQiIExBU1RfVklTSVQ9IjEwNDgxNzQ2NDMiIExBU1RfTU9ESUZJRUQ9IjEwNDI2 +NDY4NzQiPkd1aXRhci5jb20gLSBKZXJyeSBHYXJjaWEgVGhlIEFjb3VzdGljIE1hbidzIERlYWQ8 +L0E+DQogICAgICAgIDxEVD48QSBIUkVGPSJodHRwOi8vd3d3LnN0cmF0ZWd5LWJ1c2luZXNzLmNv +bS9zdHJhdGVneS85NzEwNi9wYWdlMS5odG1sIiBBRERfREFURT0iMTA0MjY0Njg3NCIgTEFTVF9W +SVNJVD0iMTA0ODE3NDY0MyIgTEFTVF9NT0RJRklFRD0iMTA0MjY0Njg3NCI+SG93IHRvICdUcnVj +aycgdGhlIEJyYW5kIExlc3NvbnMgZnJvbSB0aGUgR3JhdGVmdWwgRGVhZCAtIFBhZ2UgMTwvQT4N +CiAgICAgICAgPERUPjxBIEhSRUY9Imh0dHA6Ly9sYXdyb21haW5lLmNvbS9pZDQuaHRtIiBBRERf +REFURT0iMTA0MjY0Njg3NCIgTEFTVF9WSVNJVD0iMTA0ODE3NDY0MyIgTEFTVF9NT0RJRklFRD0i +MTA0MjY0Njg3NCI+SmVycnkgR2FyY2lhcyBHdWl0YXJzPC9BPg0KICAgICAgICA8RFQ+PEEgSFJF +Rj0iaHR0cDovL3d3dy5jb25zZXJ2YXRvcnkuY29tL2VyaWMvIiBBRERfREFURT0iMTA0MjY0Njg3 +NCIgTEFTVF9WSVNJVD0iMTA0ODE3NDY0MyIgTEFTVF9NT0RJRklFRD0iMTA0MjY0Njg3NCI+TG9u +ZSBTdGFyIERlYWQgd2ViIHNwYWNlPC9BPg0KICAgICAgICA8RFQ+PEEgSFJFRj0iaHR0cDovL3d3 +dy5wc2lsby5jb20vZGVhZC9pbmRleC5waHAiIEFERF9EQVRFPSIxMDQ2NDQzMjk1IiBMQVNUX1ZJ +U0lUPSIxMDQ4MTc0NjQzIiBMQVNUX01PRElGSUVEPSIxMDQ2NDQzMjk1Ij5wc2lsby5jb20gLSBE +ZWFkIFRpY2tldHMsIFBhc3NlcywgJiBMYW1pbmF0ZXM8L0E+DQogICAgICAgIDxEVD48QSBIUkVG +PSJodHRwOi8vd3d3LnJvY2toYWxsLmNvbS9ob2YvaW5kdWN0ZWUuYXNwP2lkPTExMyIgQUREX0RB +VEU9IjEwNDI2NDY4NzQiIExBU1RfVklTSVQ9IjEwNDgxNzQ2NDMiIExBU1RfTU9ESUZJRUQ9IjEw +NDI2NDY4NzQiPlJvY2sgYW5kIFJvbGwgSGFsbCBvZiBGYW1lIGFuZCBNdXNldW0gIEhhbGwgb2Yg +RmFtZSAgSW5kdWN0ZWUgRGV0YWlsPC9BPg0KICAgICAgICA8RFQ+PEEgSFJFRj0iaHR0cDovL3d3 +dy5yb2xsaW5nc3RvbmUuY29tL2FydGlzdHMvZGVmYXVsdC5hc3A/YWZsPXNlcyZvaWQ9MzQzIiBB +RERfREFURT0iMTA0MjY0Njg3NCIgTEFTVF9WSVNJVD0iMTA0ODE3NDY0MyIgTEFTVF9NT0RJRklF +RD0iMTA0MjY0Njg3NCI+Um9sbGluZ1N0b25lLmNvbSBBcnRpc3RzIFRoZSBHcmF0ZWZ1bCBEZWFk +PC9BPg0KICAgICAgICA8RFQ+PEEgSFJFRj0iaHR0cDovL2RiLmV0cmVlLm9yZy9zaG5saXN0LnBo +cD9hcnRpc3Q9MiZ5ZWFyPTE5OTUiIEFERF9EQVRFPSIxMDQyNjQ2ODc0IiBMQVNUX1ZJU0lUPSIx +MDQ4MTc0NjQzIiBMQVNUX01PRElGSUVEPSIxMDQyNjQ2ODc0Ij5TSE4gTGlzdCBmb3IgR3JhdGVm +dWwgRGVhZDwvQT4NCiAgICAgICAgPERUPjxBIEhSRUY9Imh0dHA6Ly93d3cuZWZmLm9yZy9wdWIv +UHVibGljYXRpb25zL0pvaG5fUGVycnlfQmFybG93L0hUTUwvYmFybG93c19seXJpY3MuaHRtbCIg +QUREX0RBVEU9IjEwNDI2NDY4NzQiIExBU1RfVklTSVQ9IjEwNDgxNzQ2NDMiIExBU1RfTU9ESUZJ +RUQ9IjEwNDI2NDY4NzQiPlNvbmdzIGZvciB0aGUgRGVhZDwvQT4NCiAgICAgICAgPERUPjxBIEhS +RUY9Imh0dHA6Ly93d3cuc2ZnYXRlLmNvbS9jZ2ktYmluL2FydGljbGUuY2dpP2ZpbGU9L2MvYS8y +MDAxLzExLzA2L0RENjIzNjQuRFRMIiBBRERfREFURT0iMTA0MjY0Njg3NCIgTEFTVF9WSVNJVD0i +MTA0ODE3NDY0MyIgTEFTVF9NT0RJRklFRD0iMTA0MjY0Njg3NCI+U3RyaW5ncyBvZiBnb2xkIC0g +VGhlcmUgd2FzIHNvbWV0aGluZyBzcGVjaWFsIGFib3V0IERvdWcgSXJ3aW4ncyBndWl0YXJzLCBh +bmQgSmVycnkgR2FyY2lhIGtuZXc8L0E+DQogICAgICAgIDxEVD48QSBIUkVGPSJodHRwOi8vd3d3 +Lm1hdGgudWthbnMuZWR1L3RidC92ZW51ZXMvTlkuaHRtbCIgQUREX0RBVEU9IjEwNDI2NDY4NzQi +IExBU1RfVklTSVQ9IjEwNDgxNzQ2NDMiIExBU1RfTU9ESUZJRUQ9IjEwNDI2NDY4NzQiPlRCVCBW +ZW51ZXMgTmV3IFlvcms8L0E+DQogICAgICAgIDxEVD48QSBIUkVGPSJodHRwOi8vYXJ0cy51Y3Nj +LmVkdS9nZGVhZC9hZ2RsLyIgQUREX0RBVEU9IjEwNDI2NDY4NzQiIExBU1RfVklTSVQ9IjEwNDgx +NzQ2NDMiIExBU1RfTU9ESUZJRUQ9IjEwNDI2NDY4NzQiPlRoZSBBbm5vdGF0ZWQgR3JhdGVmdWwg +RGVhZCBMeXJpY3MsIGJ5IERhdmlkIERvZGQ8L0E+DQogICAgICAgIDxEVD48QSBIUkVGPSJodHRw +Oi8vd3d3LmV1cm9kZWFkLm5ldC9jZ2ktYmluL2lrb25ib2FyZC9mb3J1bXMuY2dpP2ZvcnVtPTIi +IEFERF9EQVRFPSIxMDQyNjQ2ODc0IiBMQVNUX1ZJU0lUPSIxMDQ4MTc0NjQzIiBMQVNUX01PRElG +SUVEPSIxMDQyNjQ2ODc0Ij5UaGUgRGVhZEJvYXJkICBEZWFkIEdlbmVyYWwgIEdvdCBzb21ldGhp +bmcgdG8gdGFsayBhYm91dDwvQT4NCiAgICAgICAgPERUPjxBIEhSRUY9Imh0dHA6Ly93d3cubWFt +YmEuZGVtb24uY28udWsvaGVhZHMvaW5kZXguaHRtbCIgQUREX0RBVEU9IjEwNDI2NDY4NzQiIExB +U1RfVklTSVQ9IjEwNDgxNzQ2NDMiIExBU1RfTU9ESUZJRUQ9IjEwNDI2NDY4NzQiPlRoZSBEZWFk +aGVhZHMgTmV3c2xldHRlcnMgMTk3MiAtMTk3NDwvQT4NCiAgICAgICAgPERUPjxBIEhSRUY9Imh0 +dHA6Ly93d3cuYXBibmV3cy5jb20vbWVkaWEvZ2ZpbGVzL2RlYWQvZGVhZGZpbGVzLmh0bWwiIEFE +RF9EQVRFPSIxMDQyNjQ2ODc0IiBMQVNUX1ZJU0lUPSIxMDQ4MTc0NjQzIiBMQVNUX01PRElGSUVE +PSIxMDQyNjQ2ODc0Ij5UaGUgR3JhdGVmdWwgRGVhZCAtIEZCSSBGaWxlczwvQT4NCiAgICAgICAg +PERUPjxBIEhSRUY9Imh0dHA6Ly93d3cuc2V0bGlzdHMubmV0LyIgQUREX0RBVEU9IjEwNDI2NDY4 +NzQiIExBU1RfVklTSVQ9IjEwNDgxNzQ2NDMiIExBU1RfTU9ESUZJRUQ9IjEwNDI2NDY4NzQiPlRo +ZSBTZXRMaXN0IFByb2dyYW08L0E+DQogICAgICAgIDxEVD48QSBIUkVGPSJodHRwOi8vd3d3LnRo +ZXNtb2tpbmdndW4uY29tL2FyY2hpdmUvZ2FyY2lhMS5odG1sIiBBRERfREFURT0iMTA0MjY0Njg3 +NCIgTEFTVF9WSVNJVD0iMTA0ODE3NDY0MyIgTEFTVF9NT0RJRklFRD0iMTA0MjY0Njg3NCI+VGhl +IFNtb2tpbmcgR3VuIEFyY2hpdmUtIEdhcmNpYSdzIEFzc2V0czwvQT4NCiAgICAgICAgPERUPjxB +IEhSRUY9Imh0dHA6Ly93d3cudGhlc3RhbmRhcmQuY29tL2FydGljbGUvZGlzcGxheS8wLDExNTEs +MjI4NTAsMDAuaHRtbCIgQUREX0RBVEU9IjEwNDI2NDY4NzQiIExBU1RfVklTSVQ9IjEwNDgxNzQ2 +NDMiIExBU1RfTU9ESUZJRUQ9IjEwNDI2NDY4NzQiPlRoZVN0YW5kYXJkLmNvbSBJJ20gV2l0aCB0 +aGUgQnJhbmQ8L0E+DQogICAgICAgIDxEVD48QSBIUkVGPSJodHRwOi8vaG9tZS5ueWNhcC5yci5j +b20vY29udGVudC91c19kZWFkLmh0bWwiIEFERF9EQVRFPSIxMDQyNjQ2ODc0IiBMQVNUX1ZJU0lU +PSIxMDQ4MTc0NjQzIiBMQVNUX01PRElGSUVEPSIxMDQyNjQ2ODc0Ij5VbmNsZSBTYW0gYW5kIHRo +ZSBHcmF0ZWZ1bCBEZWFkPC9BPg0KICAgICAgICA8RFQ+PEEgSFJFRj0iaHR0cDovL3d3dy53aXJl +ZC5jb20vbmV3cy9jdWx0dXJlLzAsMTI4NCw0MzE4NCwwMC5odG1sIiBBRERfREFURT0iMTA0MjY0 +Njg3NCIgTEFTVF9WSVNJVD0iMTA0ODE3NDY0MyIgTEFTVF9NT0RJRklFRD0iMTA0MjY0Njg3NCI+ +V2lyZWQuY29tIC0gQXByaWwgMjMsIDIwMDEgLSBEZWFkaGVhZHMgTWF5IE5vdCBCZSBHcmF0ZWZ1 +bDwvQT4NCiAgICAgICAgPERUPjxBIEhSRUY9Imh0dHA6Ly93d3cuZ2RsaXZlLmNvbS9zaG4vIiBB +RERfREFURT0iMTA0MjY0Njg3NCIgTEFTVF9WSVNJVD0iMTA0ODE3NDY0MyIgTEFTVF9NT0RJRklF +RD0iMTA0MjY0Njg3NCI+d3d3LmdkbGl2ZS5jb20gLSAtc2huLTwvQT4NCiAgICA8L0RMPjxwPg0K +ICAgIDxEVD48SDMgRk9MREVEIEFERF9EQVRFPSIxMDQyNjQ2ODc0Ij5JbmRpZSBNdXNpYyBTaXRl +czwvSDM+DQogICAgPERMPjxwPg0KICAgICAgICA8RFQ+PEEgSFJFRj0iaHR0cDovL3d3dy5pbmRp +ZXRhYnMubmV0LyIgQUREX0RBVEU9IjEwNDI2NDY4NzQiIExBU1RfVklTSVQ9IjEwNDgxNzQ2NDMi +IExBU1RfTU9ESUZJRUQ9IjEwNDI2NDY4NzQiPi0taW5kaWV0YWJzLm5ldCAgaSBjYW4ndCBmcmVl +c3R5bGUuIGknbSBhIGxvdyBidWRnZXQgbHVkYWNyaXMuPC9BPg0KICAgICAgICA8RFQ+PEEgSFJF +Rj0iaHR0cDovL3d3dy4zMG11c2ljLmNvbS8iIEFERF9EQVRFPSIxMDQyNjQ2ODc0IiBMQVNUX1ZJ +U0lUPSIxMDQ4MTc0NjQzIiBMQVNUX01PRElGSUVEPSIxMDQyNjQ2ODc0Ij4tMzAtbXVzaWMgLi4u +IDMwbXVzaWMuY29tPC9BPg0KICAgICAgICA8RFQ+PEEgSFJFRj0iaHR0cDovL3d3dy4zd2suY29t +LyIgQUREX0RBVEU9IjEwNDI2NDY4NzQiIExBU1RfVklTSVQ9IjEwNDgxNzQ2NDMiIExBU1RfTU9E +SUZJRUQ9IjEwNDI2NDY4NzQiPjNXSyBVbmRlcmdyb3VuZCBSYWRpbyAtIEludGVybmV0IE9ubHkg +QWx0ZXJuYXRpdmUgUmFkaW8hPC9BPg0KICAgICAgICA8RFQ+PEEgSFJFRj0iaHR0cDovL3d3dy5h +dmVyc2lvbi5jb20vIiBBRERfREFURT0iMTA0MjY0Njg3NCIgTEFTVF9WSVNJVD0iMTA0ODE3NDY0 +MyIgTEFTVF9NT0RJRklFRD0iMTA0MjY0Njg3NCI+QXZlcnNpb24uY29tIC0gUm9jayAgUHVuayAg +SW5kaWUgTXVzaWM8L0E+DQogICAgICAgIDxEVD48QSBIUkVGPSJodHRwOi8vd3d3LmJhcnN1ay5j +b20vd2ViLmNnaSIgQUREX0RBVEU9IjEwNDI2NDY4NzQiIExBU1RfVklTSVQ9IjEwNDgxNzQ2NDMi +IExBU1RfTU9ESUZJRUQ9IjEwNDI2NDY4NzQiPmJhcnN1ayByZWNvcmRzPC9BPg0KICAgICAgICA8 +RFQ+PEEgSFJFRj0iaHR0cDovL3d3dy5jbWouY29tLyIgQUREX0RBVEU9IjEwNDI2NDY4NzQiIExB +U1RfVklTSVQ9IjEwNDgxNzQ2NDMiIExBU1RfTU9ESUZJRUQ9IjEwNDI2NDY4NzQiPkNNSiBOZXcg +TXVzaWMgRmlyc3Q8L0E+DQogICAgICAgIDxEVD48QSBIUkVGPSJodHRwOi8vY3dhcy5oaW5haC5j +b20vbmV3cy8iIEFERF9EQVRFPSIxMDQyNjQ2ODc0IiBMQVNUX1ZJU0lUPSIxMDQ4MTc0NjQzIiBM +QVNUX01PRElGSUVEPSIxMDQyNjQ2ODc0Ij5Db21lcyB3aXRoIGEgU21pbGU8L0E+DQogICAgICAg +IDxEVD48QSBIUkVGPSJodHRwOi8vd3d3LjItNC03LW11c2ljLmNvbS9mcm9udHBhZ2UuYXNwIiBB +RERfREFURT0iMTA0MjY0Njg3NCIgTEFTVF9WSVNJVD0iMTA0ODE3NDY0MyIgTEFTVF9NT0RJRklF +RD0iMTA0MjY0Njg3NCI+Q3J1ZCBNYWdhemluZTwvQT4NCiAgICAgICAgPERUPjxBIEhSRUY9Imh0 +dHA6Ly93d3cuYWRlcXVhY3kubmV0L2luZGV4LnNodG1sIiBBRERfREFURT0iMTA0MjY0Njg3NCIg +TEFTVF9WSVNJVD0iMTA0ODE3NDY0MyIgTEFTVF9NT0RJRklFRD0iMTA0MjY0Njg3NCI+RGVsdXNp +b25zIG9mIEFkZXF1YWN5PC9BPg0KICAgICAgICA8RFQ+PEEgSFJFRj0iaHR0cDovL3d3dy5kdXN0 +ZWRtYWdhemluZS5jb20vIiBBRERfREFURT0iMTA0NTE2NzU4NSIgTEFTVF9WSVNJVD0iMTA0ODE3 +NDY0MyIgTEFTVF9NT0RJRklFRD0iMTA0NTE2NzU4NSI+RHVzdGVkIE1hZ2F6aW5lPC9BPg0KICAg +ICAgICA8RFQ+PEEgSFJFRj0iaHR0cDovL3d3dy5lbW1pZW1hZy5jb20vIiBBRERfREFURT0iMTA0 +MjY0Njg3NCIgTEFTVF9WSVNJVD0iMTA0ODE3NDY0MyIgTEFTVF9NT0RJRklFRD0iMTA0MjY0Njg3 +NCI+RW1taWUgTWFnYXppbmU8L0E+DQogICAgICAgIDxEVD48QSBIUkVGPSJodHRwOi8vd3d3LmVw +aXRvbmljLmNvbS8iIEFERF9EQVRFPSIxMDQyNjQ2ODc0IiBMQVNUX1ZJU0lUPSIxMDQ4MTc0NjQz +IiBMQVNUX01PRElGSUVEPSIxMDQyNjQ2ODc0Ij5FcGl0b25pYy5jb20gSGkgUXVhbGl0eSBGcmVl +IE1QMyBNdXNpYzwvQT4NCiAgICAgICAgPERUPjxBIEhSRUY9Imh0dHA6Ly93d3cuYmFzZW1lbnQt +bGlmZS5jb20vIiBBRERfREFURT0iMTA0MjY0Njg3NCIgTEFTVF9WSVNJVD0iMTA0ODE3NDY0MyIg +TEFTVF9NT0RJRklFRD0iMTA0MjY0Njg3NCI+aHR0cC0td3d3LmJhc2VtZW50LWxpZmUuY29tLTwv +QT4NCiAgICAgICAgPERUPjxBIEhSRUY9Imh0dHA6Ly93d3cuaHlicmlkbWFnYXppbmUuY29tL2lu +ZGV4Lmh0bWwiIEFERF9EQVRFPSIxMDQyNjQ2ODc0IiBMQVNUX1ZJU0lUPSIxMDQ4MTc0NjQzIiBM +QVNUX01PRElGSUVEPSIxMDQyNjQ2ODc0Ij5oeWJyaWRtYWdhemluZS5jb208L0E+DQogICAgICAg +IDxEVD48QSBIUkVGPSJodHRwOi8vd3d3LmlubXVzaWN3ZXRydXN0LmNvbS8iIEFERF9EQVRFPSIx +MDQyNjQ2ODc0IiBMQVNUX1ZJU0lUPSIxMDQ4MTc0NjQzIiBMQVNUX01PRElGSUVEPSIxMDQyNjQ2 +ODc0Ij5JbiBNdXNpYyBXZSBUcnVzdDwvQT4NCiAgICAgICAgPERUPjxBIEhSRUY9Imh0dHA6Ly93 +d3cuaW5kaWV3b3Jrc2hvcC5jb20vbmV3cy8iIEFERF9EQVRFPSIxMDQ1MTcwNTM3IiBMQVNUX1ZJ +U0lUPSIxMDQ4MTc0NjQzIiBMQVNUX01PRElGSUVEPSIxMDQ1MTcwNTM4Ij5pbmRpZXdvcmtzaG9w +LmNvbTwvQT4NCiAgICAgICAgPERUPjxBIEhSRUY9Imh0dHA6Ly93d3cuaW5zb3VuZC5jb20vIiBB +RERfREFURT0iMTA0MjY0Njg3NCIgTEFTVF9WSVNJVD0iMTA0ODE3NDY0MyIgTEFTVF9NT0RJRklF +RD0iMTA0MjY0Njg3NCI+aW5zb3VuZDwvQT4NCiAgICAgICAgPERUPjxBIEhSRUY9Imh0dHA6Ly93 +d3cuanVua21lZGlhLm9yZy9pbmRleC5odG1sIiBBRERfREFURT0iMTA0MjY0Njg3NCIgTEFTVF9W +SVNJVD0iMTA0ODE3NDY0MyIgTEFTVF9NT0RJRklFRD0iMTA0MjY0Njg3NCI+aiB1IG4gayBtIGUg +ZCBpIGEgIG11c2ljIG5ld3MsIHJldmlld3MgJiBpbnRlcnZpZXdzPC9BPg0KICAgICAgICA8RFQ+ +PEEgSFJFRj0iaHR0cDovL2xvc3RhdHNlYS5uZXQvTEFTLyIgQUREX0RBVEU9IjEwNDI2NDY4NzUi +IExBU1RfVklTSVQ9IjEwNDgxNzQ2NDMiIExBU1RfTU9ESUZJRUQ9IjEwNDI2NDY4NzUiPkwgbyBz +IHQgQSB0IFMgZSBhIC4gbyBuIGwgaSBuIGU8L0E+DQogICAgICAgIDxEVD48QSBIUkVGPSJodHRw +Oi8vd3d3LmtuZXJkLmNvbS9+aW5kaWVwb3BsaXZlLyIgQUREX0RBVEU9IjEwNDI2NDY4NzUiIExB +U1RfVklTSVQ9IjEwNDgxNzQ2NDMiIExBU1RfTU9ESUZJRUQ9IjEwNDI2NDY4NzUiPkxpdmUgSW5k +aWUgUm9jayBNdXNpYzwvQT4NCiAgICAgICAgPERUPjxBIEhSRUY9Imh0dHA6Ly9tcDMucGFsdWth +dmlsbGUubmV0L2RlZmF1bHQuYXNwP2JhbmQ9MyIgQUREX0RBVEU9IjEwNDI2NDY4NzUiIExBU1Rf +VklTSVQ9IjEwNDgxNzQ2NDMiIExBU1RfTU9ESUZJRUQ9IjEwNDI2NDY4NzUiPm1wMy5wYWx1a2F2 +aWxsZS5uZXQ8L0E+DQogICAgICAgIDxEVD48QSBIUkVGPSJodHRwOi8vd3d3LnByb2plY3RhdGxh +bnRpc3ppbmUuY29tLyIgQUREX0RBVEU9IjEwNDI2NDY4NzUiIExBU1RfVklTSVQ9IjEwNDgxNzQ2 +NDMiIExBU1RfTU9ESUZJRUQ9IjEwNDI2NDY4NzUiPlAgUiBPIEogRSBDIFQgIEEgVCBMIEEgTiBU +IEkgUyBaIEkgTiBFPC9BPg0KICAgICAgICA8RFQ+PEEgSFJFRj0iaHR0cDovL3d3dy5waXRjaGZv +cmttZWRpYS5jb20vIiBBRERfREFURT0iMTA0MjY0Njg3NSIgTEFTVF9WSVNJVD0iMTA0ODE3NDY0 +MyIgTEFTVF9NT0RJRklFRD0iMTA0MjY0Njg3NSI+UGl0Y2hmb3JrbWVkaWEuY29tPC9BPg0KICAg +ICAgICA8RFQ+PEEgSFJFRj0iaHR0cDovL3d3dy5wb3BtYXR0ZXJzLmNvbS8iIEFERF9EQVRFPSIx +MDQyNjQ2ODc1IiBMQVNUX1ZJU0lUPSIxMDQ4MTc0NjQzIiBMQVNUX01PRElGSUVEPSIxMDQyNjQ2 +ODc1Ij5Qb3BNYXR0ZXJzPC9BPg0KICAgICAgICA8RFQ+PEEgSFJFRj0iaHR0cDovL3d3dy5yb2Nr +ZXQtZnVlbC5jb20vIiBBRERfREFURT0iMTA0NTE3MDA2NSIgTEFTVF9WSVNJVD0iMTA0ODE3NDY0 +MyIgTEFTVF9NT0RJRklFRD0iMTA0NTE3MDA2NSI+Um9ja2V0IEZ1ZWwgT25saW5lIE1hZ2F6aW5l +PC9BPg0KICAgICAgICA8RFQ+PEEgSFJFRj0iaHR0cDovL3d3dy5zb3VuZHRoZXNpcmVucy5jb20v +IiBBRERfREFURT0iMTA0MjY0Njg3NSIgTEFTVF9WSVNJVD0iMTA0ODE3NDY0MyIgTEFTVF9NT0RJ +RklFRD0iMTA0MjY0Njg3NSI+U09VTkRUSEVTSVJFTlMuQ09NIC0gSW5kZXBlbmRlbnQgT25saW5l +IE1hZ2F6aW5lPC9BPg0KICAgICAgICA8RFQ+PEEgSFJFRj0iaHR0cDovL3d3dy5zcGxlbmRpZGV6 +aW5lLmNvbS8iIEFERF9EQVRFPSIxMDQyNjQ2ODc1IiBMQVNUX1ZJU0lUPSIxMDQ4MTc0NjQzIiBM +QVNUX01PRElGSUVEPSIxMDQyNjQ2ODc1Ij5TcGxlbmRpZDwvQT4NCiAgICAgICAgPERUPjxBIEhS +RUY9Imh0dHA6Ly90b25ldmVuZG9yLmNvbS8iIEFERF9EQVRFPSIxMDQyNjQ2ODc1IiBMQVNUX1ZJ +U0lUPSIxMDQ4MTc0NjQzIiBMQVNUX01PRElGSUVEPSIxMDQyNjQ2ODc1Ij50b25ldmVuZG9yLmNv +bSAuIHdlbGNvbWU8L0E+DQogICAgPC9ETD48cD4NCiAgICA8RFQ+PEgzIEZPTERFRCBBRERfREFU +RT0iMTA0MjY0Njg3NSI+SW5mb3JtYXRpb24gQXJjaGl0ZWN0dXJlPC9IMz4NCiAgICA8REw+PHA+ +DQogICAgICAgIDxEVD48QSBIUkVGPSJodHRwOi8vYXJndXMtYWNpYS5jb20vaW5kZXguaHRtbCIg +QUREX0RBVEU9IjEwNDI2NDY4NzUiIExBU1RfVklTSVQ9IjEwNDgxNzQ2NDMiIExBU1RfTU9ESUZJ +RUQ9IjEwNDI2NDY4NzUiPkFyZ3VzIENlbnRlciBmb3IgSW5mb3JtYXRpb24gQXJjaGl0ZWN0dXJl +PC9BPg0KICAgIDwvREw+PHA+DQogICAgPERUPjxIMyBGT0xERUQgQUREX0RBVEU9IjEwNDI2NDY4 +NzUiPkpvYnM8L0gzPg0KICAgIDxETD48cD4NCiAgICAgICAgPERUPjxBIEhSRUY9Imh0dHA6Ly93 +d3cuYnVmZmFsb2pvYmZpbmRlci5jb20vIiBBRERfREFURT0iMTA0MjY0Njg3NSIgTEFTVF9WSVNJ +VD0iMTA0ODE3NDY0MyIgTEFTVF9NT0RJRklFRD0iMTA0MjY0Njg3NSI+QnVmZmFsb0pvYkZpbmRl +ci5jb208L0E+DQogICAgICAgIDxEVD48QSBIUkVGPSJodHRwOi8vd3d3LmdyZWF0am9ibmV0d29y +ay5jb20vIiBBRERfREFURT0iMTA0MjY0Njg3NSIgTEFTVF9WSVNJVD0iMTA0ODE3NDY0MyIgTEFT +VF9NT0RJRklFRD0iMTA0MjY0Njg3NSI+R3JlYXRKb2JOZXR3b3JrLmNvbSAgSm9icyBpbiBCdWZm +YWxvLCBOZXcgWW9yazwvQT4NCiAgICAgICAgPERUPjxBIEhSRUY9Imh0dHA6Ly93d3cuaG90am9i +cy5jb20vIiBBRERfREFURT0iMTA0MjY0Njg3NSIgTEFTVF9WSVNJVD0iMTA0ODE3NDY0MyIgTEFT +VF9NT0RJRklFRD0iMTA0MjY0Njg3NSI+SG90Sm9icy5jb208L0E+DQogICAgICAgIDxEVD48QSBI +UkVGPSJodHRwOi8vd3d3LmlhbWJ1ZmZhbG9uaWFnYXJham9icy5jb20vIiBBRERfREFURT0iMTA0 +MjY0Njg3NSIgTEFTVF9WSVNJVD0iMTA0ODE3NDY0MyIgTEFTVF9NT0RJRklFRD0iMTA0MjY0Njg3 +NSI+aWFtYnVmZmFsb25pYWdhcmFqb2JzLmNvbTwvQT4NCiAgICAgICAgPERUPjxBIEhSRUY9Imh0 +dHA6Ly93d3cud255am9icy5jb20vZGVmYXVsdC5hc3AiIEFERF9EQVRFPSIxMDQyNjQ2ODc1IiBM +QVNUX1ZJU0lUPSIxMDQ4MTc0NjQzIiBMQVNUX01PRElGSUVEPSIxMDQyNjQ2ODc1Ij5XZXN0ZXJu +IE5ldyBZb3JrIEpvYnMgPC9BPg0KICAgIDwvREw+PHA+DQogICAgPERUPjxIMyBGT0xERUQgQURE +X0RBVEU9IjEwNDI2NDY4NzUiPkxhYjwvSDM+DQogICAgPERMPjxwPg0KICAgICAgICA8RFQ+PEEg +SFJFRj0iaHR0cDovL3d3dy5ibGFja2xhYnN0dWRpb3MuY29tL0dhbGxlcnlfZmlsZXMvZ2FsbGVy +eS1hdXR1bW4uaHRtIiBBRERfREFURT0iMTA0MjY0Njg3NSIgTEFTVF9WSVNJVD0iMTA0ODE3NDY0 +MyIgTEFTVF9NT0RJRklFRD0iMTA0MjY0Njg3NSI+QmxhY2sgTGFiIFN0dWRpb3MgLSBHYWxsZXJ5 +IEF1dHVtbjwvQT4NCiAgICAgICAgPERUPjxBIEhSRUY9Imh0dHA6Ly93d3cucHVwc29uY3Vwcy5j +b20vbGlua3MvIiBBRERfREFURT0iMTA0MjY0Njg3NSIgTEFTVF9WSVNJVD0iMTA0ODE3NDY0MyIg +TEFTVF9NT0RJRklFRD0iMTA0MjY0Njg3NSI+RG9nIExpbmtzIC0gRS1jb21tZXJjZTwvQT4NCiAg +ICAgICAgPERUPjxBIEhSRUY9Imh0dHA6Ly93d3cubHVja3lsYWIuY29tLyIgQUREX0RBVEU9IjEw +NDI2NDY4NzUiIExBU1RfVklTSVQ9IjEwNDgxNzQ2NDMiIExBU1RfTU9ESUZJRUQ9IjEwNDI2NDY4 +NzUiPkxVQ0tZIExBQlJBRE9SIEJSRVdJTkcgQ09NUEFOWTwvQT4NCiAgICA8L0RMPjxwPg0KICAg +IDxEVD48SDMgRk9MREVEIEFERF9EQVRFPSIxMDQxMjY1NzgzIj5MaW5rczwvSDM+DQogICAgPERM +PjxwPg0KICAgICAgICA8RFQ+PEEgSFJFRj0iaHR0cDovL3d3dy4zd2suY29tL2NoYXJ0Lmh0bSIg +QUREX0RBVEU9IjEwNDQyODg4NTMiIExBU1RfVklTSVQ9IjEwNDgxNzIzNDMiIExBU1RfTU9ESUZJ +RUQ9IjEwNDQyODg4NTMiPjNXSzwvQT4NCiAgICAgICAgPERUPjxBIEhSRUY9Imh0dHA6Ly9hZG1p +bi5zeW5hY29yLmNvbS8iIEFERF9EQVRFPSIxMDQyNjQ2ODc1IiBMQVNUX1ZJU0lUPSIxMDQ4MTcy +MjQ0IiBMQVNUX01PRElGSUVEPSIxMDQyNjQ2ODc1Ij5BZG1pbjwvQT4NCiAgICAgICAgPERUPjxB +IEhSRUY9Imh0dHA6Ly93d3cuYnVmZmFsb25ld3MuY29tLyIgQUREX0RBVEU9IjEwNDI2NDY4NzUi +IExBU1RfVklTSVQ9IjEwNDgxNzIyNDQiIExBU1RfTU9ESUZJRUQ9IjEwNDI2NDY4NzUiPkJOZXdz +PC9BPg0KICAgICAgICA8RFQ+PEEgSFJFRj0iaHR0cDovL3d3dy53bnltdXNpYy5jb20vYm9hcmQi +IEFERF9EQVRFPSIxMDQyNjQ2ODc1IiBMQVNUX1ZJU0lUPSIxMDQ4MTcyOTUyIiBMQVNUX01PRElG +SUVEPSIxMDQyNjQ2ODc1Ij5Cb2FyZDwvQT4NCiAgICAgICAgPERUPjxBIEhSRUY9Imh0dHA6Ly9k +Yi5ldHJlZS5vcmcvYnJvamF5IiBBRERfREFURT0iMTA0MjY0Njg3NSIgTEFTVF9WSVNJVD0iMTA0 +ODE3MjM0MyIgTEFTVF9NT0RJRklFRD0iMTA0MjY0Njg3NSI+Q0RSPC9BPg0KICAgICAgICA8RFQ+ +PEEgSFJFRj0iaHR0cDovL3d3dy5kZW1vbi5uZXQvZXh0ZXJuYWwvIiBBRERfREFURT0iMTA0MjY0 +Njg3NSIgTEFTVF9WSVNJVD0iMTA0ODE3MjM0MyIgTEFTVF9NT0RJRklFRD0iMTA0MjY0Njg3NSI+ +RGVtb248L0E+DQogICAgICAgIDxEVD48QSBIUkVGPSJodHRwOi8vY2dpMS5lYmF5LmNvbS9hdy1j +Z2kvZWJheUlTQVBJLmRsbD9NeUViYXlJdGVtc0JpZGRpbmdPbiZ1c2VyaWQ9amF5dHJhZGVyNzEm +cGFzcz1oVlRVYkU4UHMxNnJkcXQ1SC5jNDMwMiZmaXJzdD1OJnNlbGxlclNvcnQ9MyZiaWRkZXJT +b3J0PTMmd2F0Y2hTb3J0PTMmZGF5c3NpbmNlPTImcDE9MCZwMj0wJnAzPTAmcDQ9MCZwNT0wIiBB +RERfREFURT0iMTA0MjY0Njg3NSIgTEFTVF9WSVNJVD0iMTA0ODE3MjI0NCIgTEFTVF9NT0RJRklF +RD0iMTA0MjY0Njg3NSI+ZUJheTwvQT4NCiAgICAgICAgPERUPjxBIEhSRUY9Imh0dHA6Ly93d3cu +ZmFyay5jb20vIiBBRERfREFURT0iMTA0MjY0Njg3NSIgTEFTVF9WSVNJVD0iMTA0ODE3MjI0NCIg +TEFTVF9NT0RJRklFRD0iMTA0MjY0Njg3NSI+RmFyazwvQT4NCiAgICAgICAgPERUPjxBIEhSRUY9 +Imh0dHA6Ly93d3cuZ29vZ2xlLmNvbS8iIEFERF9EQVRFPSIxMDQyNjQ2ODc1IiBMQVNUX1ZJU0lU +PSIxMDQ4MTcyMzQzIiBMQVNUX01PRElGSUVEPSIxMDQyNjQ2ODc1Ij5Hb29nbGU8L0E+DQogICAg +ICAgIDxEVD48QSBIUkVGPSJodHRwOi8vcWEuY2hlay5jb20vfmpnZXJsYW5kL3Bob2VuaXgvamF5 +LnBocD9wcm9kX2NvZGU9NDMiIEFERF9EQVRFPSIxMDQ0NjM5ODQ4IiBMQVNUX1ZJU0lUPSIxMDQ4 +MTcyMjQ0IiBMQVNUX01PRElGSUVEPSIxMDQ4MDgzNTU1Ij5qYXk8L0E+DQogICAgICAgIDxEVD48 +QSBIUkVGPSJodHRwOi8vd3d3Lmxpc2hvc3QuY29tL35qYXkvd255bXVzaWMvYm9hcmQvIiBBRERf +REFURT0iMTA0MjY0Njg3NSIgTEFTVF9WSVNJVD0iMTA0ODE3MjM0MyIgTEFTVF9NT0RJRklFRD0i +MTA0MjY0Njg3NSI+TGlzaG9zdCAtIEJvYXJkPC9BPg0KICAgICAgICA8RFQ+PEEgSFJFRj0iaHR0 +cHM6Ly9vbmxpbmViYW5raW5nLm1hbmR0YmFuay5jb20vIiBBRERfREFURT0iMTA0MjY0Njg3NSIg +TEFTVF9WSVNJVD0iMTA0ODE3MjM0MyIgTEFTVF9NT0RJRklFRD0iMTA0MjY0Njg3NSI+TSZUPC9B +Pg0KICAgICAgICA8RFQ+PEEgSFJFRj0iaHR0cDovL3d3dy5taW5kbGVhZGVycy5jb20vIiBBRERf +REFURT0iMTA0NTA2MzkzOSIgTEFTVF9WSVNJVD0iMTA0ODE3MjI0NCIgTEFTVF9NT0RJRklFRD0i +MTA0NTA2MzkzOSI+TWluZExlYWRlcnM8L0E+DQogICAgICAgIDxEVD48QSBIUkVGPSJodHRwOi8v +d3d3Lm1zbmJjLmNvbS9uZXdzL2RlZmF1bHQuYXNwIiBBRERfREFURT0iMTA0MjY0Njg3NSIgTEFT +VF9WSVNJVD0iMTA0ODE3MjI0NCIgTEFTVF9NT0RJRklFRD0iMTA0MjY0Njg3NSI+TVNOPC9BPg0K +ICAgICAgICA8RFQ+PEEgSFJFRj0iaHR0cDovL215LmZvb2wuY29tLyIgQUREX0RBVEU9IjEwNDI2 +NDY4NzUiIExBU1RfVklTSVQ9IjEwNDgxNzIzNDMiIExBU1RfTU9ESUZJRUQ9IjEwNDI2NDY4NzUi +Pk15Rm9vbDwvQT4NCiAgICAgICAgPERUPjxBIEhSRUY9Imh0dHA6Ly9teXViLmJ1ZmZhbG8uZWR1 +LyIgQUREX0RBVEU9IjEwNDI2NDY4NzUiIExBU1RfVklTSVQ9IjEwNDgxNzIzNDMiIExBU1RfTU9E +SUZJRUQ9IjEwNDI2NDY4NzUiPk15VUI8L0E+DQogICAgICAgIDxEVD48QSBIUkVGPSJodHRwOi8v +d3d3Lm55dGltZXMuY29tL3lyL21vL2RheS8iIEFERF9EQVRFPSIxMDQyNjQ2ODc1IiBMQVNUX1ZJ +U0lUPSIxMDQ4MTcyMjQ0IiBMQVNUX01PRElGSUVEPSIxMDQyNjQ2ODc1Ij5OWVQ8L0E+DQogICAg +ICAgIDxEVD48QSBIUkVGPSJqYXZhc2NyaXB0OnBocGZ1bmM9d2luZG93LnByb21wdCgnUEhQIEZ1 +bmN0aW9uIExvb2t1cDonLCcnKSA7IGlmKHBocGZ1bmMgJiYgcGhwZnVuYyAhPSAnJykgeyB3aW5k +b3cubG9jYXRpb24gPSAnaHR0cDovL3d3dy5waHAubmV0L21hbnVhbC1sb29rdXAucGhwP2Z1bmN0 +aW9uPScgK3BocGZ1bmM7IH0gZWxzZSB7IGhpc3RvcnkuZ28oLTEpOyB9IiBBRERfREFURT0iMTA0 +MjY0Njg3NSIgTEFTVF9WSVNJVD0iMTA0ODE3MjI0NCIgTEFTVF9NT0RJRklFRD0iMTA0MjY0Njg3 +NSI+UEhQPC9BPg0KICAgICAgICA8RFQ+PEEgSFJFRj0iaHR0cDovL3FhLmNoZWsuY29tL2NoZWtp +bi5waHA/dV9kPWpheXRlc3RAcWEuY2hlay5jb20mcGFzc3dvcmQ9dGVzdGpheSZib3VuY2V0bz10 +ZW1wbGF0ZTEzLmNoZWsuY29tIiBBRERfREFURT0iMTA0MzMzOTg5OSIgTEFTVF9WSVNJVD0iMTA0 +ODE3MjI0NCIgTEFTVF9NT0RJRklFRD0iMTA0NjQ0ODY4NCI+UG9ydGFsUUE8L0E+DQogICAgICAg +IDxEVD48QSBIUkVGPSJodHRwOi8vcWEuY2hlay5jb20vfmpnZXJsYW5kL3Byb3BsYXQzLyIgQURE +X0RBVEU9IjEwNDI2NDY4NzUiIExBU1RfVklTSVQ9IjEwNDgxNzIyNDMiIExBU1RfTU9ESUZJRUQ9 +IjEwNDI2NDY4NzUiPlBQMzwvQT4NCiAgICAgICAgPERUPjxBIEhSRUY9Imh0dHA6Ly9xYS5jaGVr +LmNvbS9+amdlcmxhbmQvcHJvcGxhdDQvIiBBRERfREFURT0iMTA0MjY0Njg3NSIgTEFTVF9WSVNJ +VD0iMTA0ODE3MjI0NCIgTEFTVF9NT0RJRklFRD0iMTA0MjY0Njg3NSI+UFA0PC9BPg0KICAgICAg +ICA8RFQ+PEEgSFJFRj0iaHR0cDovL2RiLmV0cmVlLm9yZy9zaG93bWFpbnQvY29ycmVjdGlvbnMv +IiBBRERfREFURT0iMTA0MjY0Njg3NSIgTEFTVF9WSVNJVD0iMTA0ODE3MjM0MyIgTEFTVF9NT0RJ +RklFRD0iMTA0MjY0Njg3NSI+U2V0bGlzdCBDb3JyZWN0aW9uczwvQT4NCiAgICAgICAgPERUPjxB +IEhSRUY9Imh0dHA6Ly9zbGFzaGRvdC5vcmcvIiBBRERfREFURT0iMTA0MjY0Njg3NSIgTEFTVF9W +SVNJVD0iMTA0ODE3MjI0NCIgTEFTVF9NT0RJRklFRD0iMTA0MjY0Njg3NSI+U2xhc2g8L0E+DQog +ICAgICAgIDxEVD48QSBIUkVGPSJodHRwOi8vd3d3LnRoZWJsYWNrZG9nLm9yZy8iIEFERF9EQVRF +PSIxMDQyNjQ2ODc1IiBMQVNUX1ZJU0lUPSIxMDQ4MTcyMzQzIiBMQVNUX01PRElGSUVEPSIxMDQy +NjQ2ODc1Ij5UTUQ8L0E+DQogICAgPC9ETD48cD4NCiAgICA8RFQ+PEgzIEZPTERFRCBBRERfREFU +RT0iMTA0MjY0Njg3NSI+TWVkaWE8L0gzPg0KICAgIDxETD48cD4NCiAgICAgICAgPERUPjxBIEhS +RUY9Imh0dHA6Ly93d3cubWljcm9zb2Z0LmNvbS9pc2FwaS9yZWRpci5kbGw/cHJkPVdpbmRvd3Mm +c2JwPU1lZGlhUGxheWVyJmFyPUZhdm9yaXRlJnNiYT1CbG9vbWJlcmcmcHZlcj02LjIiIEFERF9E +QVRFPSIxMDQyNjQ2ODc1IiBMQVNUX1ZJU0lUPSIxMDQ4MTc0NjQzIiBMQVNUX01PRElGSUVEPSIx +MDQyNjQ2ODc1Ij5CbG9vbWJlcmc8L0E+DQogICAgICAgIDxEVD48QSBIUkVGPSJodHRwOi8vd3d3 +Lm1pY3Jvc29mdC5jb20vaXNhcGkvcmVkaXIuZGxsP3ByZD1XaW5kb3dzJnNicD1NZWRpYVBsYXll +ciZhcj1GYXZvcml0ZSZzYmE9Q2FwaXRvbCZwdmVyPTYuMiIgQUREX0RBVEU9IjEwNDI2NDY4NzUi +IExBU1RfVklTSVQ9IjEwNDgxNzQ2NDMiIExBU1RfTU9ESUZJRUQ9IjEwNDI2NDY4NzUiPkNhcGl0 +b2wgUmVjb3JkczwvQT4NCiAgICAgICAgPERUPjxBIEhSRUY9Imh0dHA6Ly93d3cubWljcm9zb2Z0 +LmNvbS9pc2FwaS9yZWRpci5kbGw/cHJkPVdpbmRvd3Mmc2JwPU1lZGlhUGxheWVyJmFyPUZhdm9y +aXRlJnNiYT1DQlMmcHZlcj02LjIiIEFERF9EQVRFPSIxMDQyNjQ2ODc1IiBMQVNUX1ZJU0lUPSIx +MDQ4MTc0NjQzIiBMQVNUX01PRElGSUVEPSIxMDQyNjQ2ODc1Ij5DQlM8L0E+DQogICAgICAgIDxE +VD48QSBIUkVGPSJodHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20vaXNhcGkvcmVkaXIuZGxsP3ByZD1X +aW5kb3dzJnNicD1NZWRpYVBsYXllciZhcj1GYXZvcml0ZSZzYmE9Q05CQyZwdmVyPTYuMiIgQURE +X0RBVEU9IjEwNDI2NDY4NzUiIExBU1RfVklTSVQ9IjEwNDgxNzQ2NDMiIExBU1RfTU9ESUZJRUQ9 +IjEwNDI2NDY4NzUiPkNOQkMgRG93IEpvbmVzIEJ1c2luZXNzIFZpZGVvPC9BPg0KICAgICAgICA8 +RFQ+PEEgSFJFRj0iaHR0cDovL3d3dy5taWNyb3NvZnQuY29tL2lzYXBpL3JlZGlyLmRsbD9wcmQ9 +V2luZG93cyZzYnA9TWVkaWFQbGF5ZXImYXI9RmF2b3JpdGUmc2JhPUNORVQmcHZlcj02LjIiIEFE +RF9EQVRFPSIxMDQyNjQ2ODc1IiBMQVNUX1ZJU0lUPSIxMDQ4MTc0NjQzIiBMQVNUX01PRElGSUVE +PSIxMDQyNjQ2ODc1Ij5DTkVUIFRvZGF5IC0gVGVjaG5vbG9neSBOZXdzPC9BPg0KICAgICAgICA8 +RFQ+PEEgSFJFRj0iaHR0cDovL3d3dy5taWNyb3NvZnQuY29tL2lzYXBpL3JlZGlyLmRsbD9wcmQ9 +V2luZG93cyZzYnA9TWVkaWFQbGF5ZXImYXI9RmF2b3JpdGUmc2JhPUNOTiZwdmVyPTYuMiIgQURE +X0RBVEU9IjEwNDI2NDY4NzUiIExBU1RfVklTSVQ9IjEwNDgxNzQ2NDMiIExBU1RfTU9ESUZJRUQ9 +IjEwNDI2NDY4NzUiPkNOTiBWaWRlb3NlbGVjdDwvQT4NCiAgICAgICAgPERUPjxBIEhSRUY9Imh0 +dHA6Ly93d3cubWljcm9zb2Z0LmNvbS9pc2FwaS9yZWRpci5kbGw/cHJkPVdpbmRvd3Mmc2JwPU1l +ZGlhUGxheWVyJmFyPUZhdm9yaXRlJnNiYT1EaXNuZXkmcHZlcj02LjIiIEFERF9EQVRFPSIxMDQy +NjQ2ODc1IiBMQVNUX1ZJU0lUPSIxMDQ4MTc0NjQzIiBMQVNUX01PRElGSUVEPSIxMDQyNjQ2ODc1 +Ij5EaXNuZXk8L0E+DQogICAgICAgIDxEVD48QSBIUkVGPSJodHRwOi8vd3d3Lm1pY3Jvc29mdC5j +b20vaXNhcGkvcmVkaXIuZGxsP3ByZD1XaW5kb3dzJnNicD1NZWRpYVBsYXllciZhcj1GYXZvcml0 +ZSZzYmE9RVNQTiZwdmVyPTYuMiIgQUREX0RBVEU9IjEwNDI2NDY4NzUiIExBU1RfVklTSVQ9IjEw +NDgxNzQ2NDMiIExBU1RfTU9ESUZJRUQ9IjEwNDI2NDY4NzUiPkVTUE4gU3BvcnRzPC9BPg0KICAg +ICAgICA8RFQ+PEEgSFJFRj0iaHR0cDovL3d3dy5taWNyb3NvZnQuY29tL2lzYXBpL3JlZGlyLmRs +bD9wcmQ9V2luZG93cyZzYnA9TWVkaWFQbGF5ZXImYXI9RmF2b3JpdGUmc2JhPUZveE5ld3MmcHZl +cj02LjIiIEFERF9EQVRFPSIxMDQyNjQ2ODc1IiBMQVNUX1ZJU0lUPSIxMDQ4MTc0NjQzIiBMQVNU +X01PRElGSUVEPSIxMDQyNjQ2ODc1Ij5Gb3ggTmV3czwvQT4NCiAgICAgICAgPERUPjxBIEhSRUY9 +Imh0dHA6Ly93d3cubWljcm9zb2Z0LmNvbS9pc2FwaS9yZWRpci5kbGw/cHJkPVdpbmRvd3Mmc2Jw +PU1lZGlhUGxheWVyJmFyPUZhdm9yaXRlJnNiYT1Gb3hTcG9ydHMmcHZlcj02LjIiIEFERF9EQVRF +PSIxMDQyNjQ2ODc1IiBMQVNUX1ZJU0lUPSIxMDQ4MTc0NjQzIiBMQVNUX01PRElGSUVEPSIxMDQy +NjQ2ODc1Ij5Gb3ggU3BvcnRzPC9BPg0KICAgICAgICA8RFQ+PEEgSFJFRj0iaHR0cDovL3d3dy5t +aWNyb3NvZnQuY29tL2lzYXBpL3JlZGlyLmRsbD9wcmQ9V2luZG93cyZzYnA9TWVkaWFQbGF5ZXIm +YXI9RmF2b3JpdGUmc2JhPUhvbGx5d29vZCZwdmVyPTYuMiIgQUREX0RBVEU9IjEwNDI2NDY4NzUi +IExBU1RfVklTSVQ9IjEwNDgxNzQ2NDMiIExBU1RfTU9ESUZJRUQ9IjEwNDI2NDY4NzUiPkhvbGx5 +d29vZCBPbmxpbmU8L0E+DQogICAgICAgIDxEVD48QSBIUkVGPSJodHRwOi8vd3d3Lm1pY3Jvc29m +dC5jb20vaXNhcGkvcmVkaXIuZGxsP3ByZD13aW5kb3dzJnNicD1tZWRpYXBsYXllciZhcj1NZWRp +YSZzYmE9UmFkaW9HdWlkZSZwdmVyPTYuMiIgQUREX0RBVEU9IjEwNDI2NDY4NzUiIExBU1RfVklT +SVQ9IjEwNDgxNzQ2NDMiIExBU1RfTU9ESUZJRUQ9IjEwNDI2NDY4NzUiPkludGVybmV0IFJhZGlv +IEd1aWRlPC9BPg0KICAgICAgICA8RFQ+PEEgSFJFRj0iaHR0cDovL3d3dy5taWNyb3NvZnQuY29t +L2lzYXBpL3JlZGlyLmRsbD9wcmQ9V2luZG93cyZzYnA9TWVkaWFQbGF5ZXImYXI9RmF2b3JpdGUm +c2JhPU1TTkJDJnB2ZXI9Ni4yIiBBRERfREFURT0iMTA0MjY0Njg3NSIgTEFTVF9WSVNJVD0iMTA0 +ODE3NDY0MyIgTEFTVF9NT0RJRklFRD0iMTA0MjY0Njg3NSI+TVNOQkM8L0E+DQogICAgICAgIDxE +VD48QSBIUkVGPSJodHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20vaXNhcGkvcmVkaXIuZGxsP3ByZD1X +aW5kb3dzJnNicD1NZWRpYVBsYXllciZhcj1GYXZvcml0ZSZzYmE9TXVzaWNWaWRlb3MmcHZlcj02 +LjIiIEFERF9EQVRFPSIxMDQyNjQ2ODc1IiBMQVNUX1ZJU0lUPSIxMDQ4MTc0NjQzIiBMQVNUX01P +RElGSUVEPSIxMDQyNjQ2ODc1Ij5NVVNJQ1ZJREVPUy5DT008L0E+DQogICAgICAgIDxEVD48QSBI +UkVGPSJodHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20vaXNhcGkvcmVkaXIuZGxsP3ByZD1XaW5kb3dz +JnNicD1NZWRpYVBsYXllciZhcj1GYXZvcml0ZSZzYmE9TkJDJnB2ZXI9Ni4yIiBBRERfREFURT0i +MTA0MjY0Njg3NSIgTEFTVF9WSVNJVD0iMTA0ODE3NDY0MyIgTEFTVF9NT0RJRklFRD0iMTA0MjY0 +Njg3NSI+TkJDIFZpZGVvU2Vla2VyPC9BPg0KICAgICAgICA8RFQ+PEEgSFJFRj0iY2FzdDM6Ly8y +NC40Ni42My4xNjA6NDIwMC9qZXJyeSIgQUREX0RBVEU9IjEwNDI2NDY4NzUiIExBU1RfVklTSVQ9 +IjEwNDgxNzQ2NDMiIExBU1RfTU9ESUZJRUQ9IjEwNDI2NDY4NzUiPlN1Z2FybWVncyBMaXZlIFN0 +cmVhbTwvQT4NCiAgICAgICAgPERUPjxBIEhSRUY9Imh0dHA6Ly93d3cubWljcm9zb2Z0LmNvbS9p +c2FwaS9yZWRpci5kbGw/cHJkPVdpbmRvd3Mmc2JwPU1lZGlhUGxheWVyJmFyPUZhdm9yaXRlJnNi +YT1UVkd1aWRlJnB2ZXI9Ni4yIiBBRERfREFURT0iMTA0MjY0Njg3NSIgTEFTVF9WSVNJVD0iMTA0 +ODE3NDY0MyIgTEFTVF9NT0RJRklFRD0iMTA0MjY0Njg3NSI+VFYgR3VpZGUgRW50ZXJ0YWlubWVu +dCBOZXR3b3JrPC9BPg0KICAgICAgICA8RFQ+PEEgSFJFRj0iaHR0cDovL3d3dy5taWNyb3NvZnQu +Y29tL2lzYXBpL3JlZGlyLmRsbD9wcmQ9V2luZG93cyZzYnA9TWVkaWFQbGF5ZXImYXI9RmF2b3Jp +dGUmc2JhPVVuaXZlcnNhbFN0dWRpb3MmcHZlcj02LjIiIEFERF9EQVRFPSIxMDQyNjQ2ODc1IiBM +QVNUX1ZJU0lUPSIxMDQ4MTc0NjQzIiBMQVNUX01PRElGSUVEPSIxMDQyNjQ2ODc1Ij5Vbml2ZXJz +YWwgU3R1ZGlvcyBPbmxpbmU8L0E+DQogICAgICAgIDxEVD48QSBIUkVGPSJodHRwOi8vd3d3Lm1p +Y3Jvc29mdC5jb20vaXNhcGkvcmVkaXIuZGxsP3ByZD1XaW5kb3dzJnNicD1NZWRpYVBsYXllciZh +cj1GYXZvcml0ZSZzYmE9V2FybmVyQnJvcyZwdmVyPTYuMiIgQUREX0RBVEU9IjEwNDI2NDY4NzUi +IExBU1RfVklTSVQ9IjEwNDgxNzQ2NDMiIExBU1RfTU9ESUZJRUQ9IjEwNDI2NDY4NzUiPldhcm5l +ciBCcm9zLiBIaXAgQ2xpcHM8L0E+DQogICAgICAgIDxEVD48QSBIUkVGPSJodHRwOi8vd3d3Lm1p +Y3Jvc29mdC5jb20vaXNhcGkvcmVkaXIuZGxsP3ByZD1udHNlcnZlciZzYnA9bmV0c2hvdyZhcj1O +ZXRTaG93JnNiYT1FdmVudHMmcHZlcj0zLjAiIEFERF9EQVRFPSIxMDQyNjQ2ODc1IiBMQVNUX1ZJ +U0lUPSIxMDQ4MTc0NjQzIiBMQVNUX01PRElGSUVEPSIxMDQyNjQ2ODc1Ij5XaGF0J3MgT24gTm93 +PC9BPg0KICAgICAgICA8RFQ+PEEgSFJFRj0iaHR0cDovL3d3dy5taWNyb3NvZnQuY29tL2lzYXBp +L3JlZGlyLmRsbD9wcmQ9d2luZG93cyZzYnA9bWVkaWFwbGF5ZXImYXI9TWVkaWEmc2JhPUd1aWRl +JnB2ZXI9Ni4yIiBBRERfREFURT0iMTA0MjY0Njg3NSIgTEFTVF9WSVNJVD0iMTA0ODE3NDY0NCIg +TEFTVF9NT0RJRklFRD0iMTA0MjY0Njg3NSI+V2luZG93cyBNZWRpYSBTaG93Y2FzZTwvQT4NCiAg +ICA8L0RMPjxwPg0KICAgIDxEVD48SDMgRk9MREVEIEFERF9EQVRFPSIxMDQyNjQ2ODc1Ij5NdXNp +YzwvSDM+DQogICAgPERMPjxwPg0KICAgICAgICA8RFQ+PEEgSFJFRj0iaHR0cDovL3d3dy5kY24u +Y29tL2luZGV4LnNodG1sIiBBRERfREFURT0iMTA0MjY0Njg3NSIgTEFTVF9WSVNJVD0iMTA0ODE3 +NDY0NCIgTEFTVF9NT0RJRklFRD0iMTA0MjY0Njg3NSI+RGlnaXRhbCBDbHViIE5ldHdvcmsgKERD +TikgLSBUaGUgTGl2ZSBNdXNpYyBTb3VyY2U8L0E+DQogICAgICAgIDxEVD48QSBIUkVGPSJodHRw +Oi8vd3d3LmVwaXRvbmljLmNvbS9pbmRleC5odG1sIiBBRERfREFURT0iMTA0MjY0Njg3NSIgTEFT +VF9WSVNJVD0iMTA0ODE3NDY0NCIgTEFTVF9NT0RJRklFRD0iMTA0MjY0Njg3NSI+RXBpdG9uaWMu +Y29tIEhpIFF1YWxpdHkgRnJlZSBNUDMgTXVzaWM8L0E+DQogICAgICAgIDxEVD48QSBIUkVGPSJo +dHRwOi8vbXAzLnBhbHVrYXZpbGxlLm5ldC9kZWZhdWx0LmFzcD9iYW5kPTMiIEFERF9EQVRFPSIx +MDQyNjQ2ODc1IiBMQVNUX1ZJU0lUPSIxMDQ4MTc0NjQ0IiBMQVNUX01PRElGSUVEPSIxMDQyNjQ2 +ODc1Ij5JbmRpZSBMaXZlIE1QM3MgLSBtcDMucGFsdWthdmlsbGUubmV0PC9BPg0KICAgICAgICA8 +RFQ+PEEgSFJFRj0iaHR0cDovL3d3dy5rbmVyZC5jb20vfmluZGllcG9wbGl2ZS8iIEFERF9EQVRF +PSIxMDQyNjQ2ODc1IiBMQVNUX1ZJU0lUPSIxMDQ4MTc0NjQ0IiBMQVNUX01PRElGSUVEPSIxMDQy +NjQ2ODc1Ij5MaXZlIEluZGllIFJvY2sgTXVzaWM8L0E+DQogICAgICAgIDxEVD48QSBIUkVGPSJo +dHRwOi8vZWFzeWNhc3Qtc2YubGl2ZTM2NS5jb20vbWVtYmVycy9jb250cm9scGFuZWwubGl2ZSIg +QUREX0RBVEU9IjEwNDI2NDY4NzUiIExBU1RfVklTSVQ9IjEwNDgxNzQ2NDQiIExBU1RfTU9ESUZJ +RUQ9IjEwNDI2NDY4NzUiPkxpdmUzNjUuY29tIC0gQnJvYWRjYXN0PC9BPg0KICAgICAgICA8RFQ+ +PEEgSFJFRj0iaHR0cDovL2dpci5tcDMuY29tL2dpcj9nZW5yZV9pZD0wJmdlb19pZD0wMDAwMzA0 +OCIgQUREX0RBVEU9IjEwNDI2NDY4NzUiIExBU1RfVklTSVQ9IjEwNDgxNzQ2NDQiIExBU1RfTU9E +SUZJRUQ9IjEwNDI2NDY4NzUiPk1QMy5jb20gQnVmZmFsbzwvQT4NCiAgICAgICAgPERUPjxBIEhS +RUY9Imh0dHA6Ly93d3cubXVzaWN0b2RheS5jb20vZGVmYXVsdC5hc3AiIEFERF9EQVRFPSIxMDQy +NjQ2ODc1IiBMQVNUX1ZJU0lUPSIxMDQ4MTc0NjQ0IiBMQVNUX01PRElGSUVEPSIxMDQyNjQ2ODc1 +Ij5NdXNpY1RvZGF5PC9BPg0KICAgICAgICA8RFQ+PEEgSFJFRj0iaHR0cDovL3d3dy5jaG9pY2Vv +bmVvbmxpbmUuY29tL350aW0vbXAzL2pheS8iIEFERF9EQVRFPSIxMDQyNjQ2ODc1IiBMQVNUX1ZJ +U0lUPSIxMDQ4MTc0NjQ0IiBMQVNUX01PRElGSUVEPSIxMDQyNjQ2ODc1Ij5UaW0ncyBTZWNyZXQg +TVAzIFN0YXNoPC9BPg0KICAgIDwvREw+PHA+DQogICAgPERUPjxIMyBGT0xERUQgQUREX0RBVEU9 +IjEwNDI2NDY4NzUiPk5pY2UgU2l0ZXM8L0gzPg0KICAgIDxETD48cD4NCiAgICAgICAgPERUPjxB +IEhSRUY9Imh0dHA6Ly93d3cuYXRnLmNvbS8iIEFERF9EQVRFPSIxMDQyNjQ2ODc1IiBMQVNUX1ZJ +U0lUPSIxMDQ4MTc0NjQ0IiBMQVNUX01PRElGSUVEPSIxMDQyNjQ2ODc1Ij5BcnQgVGVjaG5vbG9n +eSBHcm91cDwvQT4NCiAgICAgICAgPERUPjxBIEhSRUY9Imh0dHA6Ly93d3cuYmxhY2tkb2dpbnRl +cmFjdGl2ZS5jb20vaW5kZXguYXNwIiBBRERfREFURT0iMTA0MjY0Njg3NSIgTEFTVF9WSVNJVD0i +MTA0ODE3NDY0NCIgTEFTVF9NT0RJRklFRD0iMTA0MjY0Njg3NSI+QmxhY2sgRG9nIEludGVyYWN0 +aXZlICBBIE5ldyBCcmVlZDwvQT4NCiAgICAgICAgPERUPjxBIEhSRUY9Imh0dHA6Ly93d3cuYm9y +bm1hZy5jb20vbW90aGVyLmh0bWwiIEFERF9EQVRFPSIxMDQyNjQ2ODc1IiBMQVNUX1ZJU0lUPSIx +MDQ4MTc0NjQ0IiBMQVNUX01PRElGSUVEPSIxMDQyNjQ2ODc1Ij5Cb3JuIE1hZ2F6aW5lIERlc2ln +biBhbmQgTGl0ZXJhdHVyZSBDb2xsYWJvcmF0aW9uPC9BPg0KICAgICAgICA8RFQ+PEEgSFJFRj0i +aHR0cDovL3d3dy5icmllbmNlLmNvbS8iIEFERF9EQVRFPSIxMDQyNjQ2ODc1IiBMQVNUX1ZJU0lU +PSIxMDQ4MTc0NjQ0IiBMQVNUX01PRElGSUVEPSIxMDQyNjQ2ODc1Ij5CcmllbmNlLmNvbTwvQT4N +CiAgICAgICAgPERUPjxBIEhSRUY9Imh0dHA6Ly93d3cuZmFjdGl2YS5jb20vIiBBRERfREFURT0i +MTA0MjY0Njg3NSIgTEFTVF9WSVNJVD0iMTA0ODE3NDY0NCIgTEFTVF9NT0RJRklFRD0iMTA0MjY0 +Njg3NSI+RmFjdGl2YSwgYSBEb3cgSm9uZXMgUmV1dGVycyBDb21wYW55PC9BPg0KICAgICAgICA8 +RFQ+PEEgSFJFRj0iaHR0cDovL3d3dy5mYWN0b3JkZXNpZ24uY29tLyIgQUREX0RBVEU9IjEwNDI2 +NDY4NzUiIExBU1RfVklTSVQ9IjEwNDgxNzQ2NDQiIExBU1RfTU9ESUZJRUQ9IjEwNDI2NDY4NzUi +PkZBQ1RPUiBERVNJR048L0E+DQogICAgICAgIDxEVD48QSBIUkVGPSJodHRwOi8vd3d3LmVzY2Fw +ZWhldi5jb20vaW5kZXguYXNwIiBBRERfREFURT0iMTA0MjY0Njg3NSIgTEFTVF9WSVNJVD0iMTA0 +ODE3NDY0NCIgTEFTVF9NT0RJRklFRD0iMTA0MjY0Njg3NSI+Rm9yZCBIRVY8L0E+DQogICAgICAg +IDxEVD48QSBIUkVGPSJodHRwOi8vd3d3LmZ1c2lvbm9uZS5jb20vIiBBRERfREFURT0iMTA0MjY0 +Njg3NSIgTEFTVF9WSVNJVD0iMTA0ODE3NDY0NCIgTEFTVF9NT0RJRklFRD0iMTA0MjY0Njg3NSI+ +ZnVzaW9uT25lPC9BPg0KICAgICAgICA8RFQ+PEEgSFJFRj0iaHR0cDovL3d3dy5paHQuY29tL2Zy +b250cGFnZS5odG1sIiBBRERfREFURT0iMTA0MjY0Njg3NSIgTEFTVF9WSVNJVD0iMTA0ODE3NDY0 +NCIgTEFTVF9NT0RJRklFRD0iMTA0MjY0Njg3NSI+SW50ZXJuYXRpb25hbCBIZXJhbGQgVHJpYnVu +ZTwvQT4NCiAgICAgICAgPERUPjxBIEhSRUY9Imh0dHA6Ly93d3cuaXN5bmRpY2F0ZS5jb20vIiBB +RERfREFURT0iMTA0MjY0Njg3NSIgTEFTVF9WSVNJVD0iMTA0ODE3NDY0NCIgTEFTVF9NT0RJRklF +RD0iMTA0MjY0Njg3NSI+aVN5bmRpY2F0ZSBTeW5kaWNhdGlvbiBzb2x1dGlvbnMgYnVpbHQgb24g +b3VyIEludGVsbGlnZW50IFN5bmRpY2F0aW9uIE5ldHdvcms8L0E+DQogICAgICAgIDxEVD48QSBI +UkVGPSJodHRwOi8vd3d3Lm9kZGNhc3QuY29tL25ld3NpdGUyL3NpdGUvaW5kZXguaHRtbCIgQURE +X0RBVEU9IjEwNDI2NDY4NzUiIExBU1RfVklTSVQ9IjEwNDgxNzQ2NDQiIExBU1RfTU9ESUZJRUQ9 +IjEwNDI2NDY4NzUiPm9kZGNhc3Q8L0E+DQogICAgICAgIDxEVD48QSBIUkVGPSJodHRwOi8vd3d3 +LnNpZWdlbGdhbGUuY29tL2luZGV4X2ZsYXNoLmh0bWwiIEFERF9EQVRFPSIxMDQyNjQ2ODc1IiBM +QVNUX1ZJU0lUPSIxMDQ4MTc0NjQ0IiBMQVNUX01PRElGSUVEPSIxMDQyNjQ2ODc1Ij5zaWVnZWxn +YWxlICBMYXVuY2g8L0E+DQogICAgPC9ETD48cD4NCiAgICA8RFQ+PEgzIEZPTERFRCBBRERfREFU +RT0iMTA0MjY0Njg3NSI+UEhQPC9IMz4NCiAgICA8REw+PHA+DQogICAgICAgIDxEVD48QSBIUkVG +PSJodHRwOi8vY29kZXdhbGtlcnMuY29tLyIgQUREX0RBVEU9IjEwNDc1NzAzOTQiIExBU1RfVklT +SVQ9IjEwNDgxNzQ2NDQiIExBU1RfTU9ESUZJRUQ9IjEwNDc1NzAzOTUiPmNvZGV3YWxrZXJzLmNv +bSAtIG1haW4gcGFnZSAtIFBIUCBIZWxwPC9BPg0KICAgICAgICA8RFQ+PEEgSFJFRj0iaHR0cDov +L3d3dy5lendlYmRlc2lnbi5jb20vcGhwLmh0bWwiIEFERF9EQVRFPSIxMDQyNjQ2ODc1IiBMQVNU +X1ZJU0lUPSIxMDQ4MTc0NjQ0IiBMQVNUX01PRElGSUVEPSIxMDQyNjQ2ODc1Ij5FWndlYmRlc2ln +bi5jb20gLSBQSFAgUmVzb3VyY2VzPC9BPg0KICAgICAgICA8RFQ+PEEgSFJFRj0iaHR0cDovL3d3 +dy5ob3RzY3JpcHRzLmNvbS9QSFAvIiBBRERfREFURT0iMTA0MjY0Njg3NSIgTEFTVF9WSVNJVD0i +MTA0ODE3NDY0NCIgTEFTVF9NT0RJRklFRD0iMTA0MjY0Njg3NSI+SG90U2NyaXB0cy5jb20gUEhQ +PC9BPg0KICAgICAgICA8RFQ+PEEgSFJFRj0iaHR0cDovL3d3dy5waHBpbnNpZGVyLmNvbS8iIEFE +RF9EQVRFPSIxMDQyNjQ2ODc1IiBMQVNUX1ZJU0lUPSIxMDQ4MTc0NjQ0IiBMQVNUX01PRElGSUVE +PSIxMDQyNjQ2ODc1Ij5odHRwLS13d3cucGhwaW5zaWRlci5jb20tPC9BPg0KICAgICAgICA8RFQ+ +PEEgSFJFRj0iaHR0cDovL3d3dy5tYWNyb21lZGlhLmNvbS9zb2Z0d2FyZS9kcmVhbXdlYXZlci9z +cGVjaWFsL2V4dGVuZC9waHAvaW5kZXguaHRtbD9wcm9tb2lkPWZtYTVhIiBBRERfREFURT0iMTA0 +NzU3MzE5NiIgTEFTVF9WSVNJVD0iMTA0ODE3NDY0NCIgTEFTVF9NT0RJRklFRD0iMTA0NzU3MzE5 +NyI+TWFjcm9tZWRpYSAtIFBIUCBhbmQgRHJlYW13ZWF2ZXIgTVg8L0E+DQogICAgICAgIDxEVD48 +QSBIUkVGPSJodHRwOi8vd3d3Lm15c3FsLmNvbS9kb2N1bWVudGF0aW9uL215c3FsL2NvbW1lbnRl +ZC8iIEFERF9EQVRFPSIxMDQyNjQ2ODc1IiBMQVNUX1ZJU0lUPSIxMDQ4MTc0NjQ0IiBMQVNUX01P +RElGSUVEPSIxMDQyNjQ2ODc1Ij5NeVNRTCAgRG9jdW1lbnRhdGlvbiAgTXlTUUwgIENvbW1lbnRl +ZCBNeVNRTCBNYW51YWw8L0E+DQogICAgICAgIDxEVD48QSBIUkVGPSJodHRwOi8vbnlwaHAub3Jn +LyIgQUREX0RBVEU9IjEwNDI2NDY4NzUiIExBU1RfVklTSVQ9IjEwNDgxNzQ2NDQiIExBU1RfTU9E +SUZJRUQ9IjEwNDI2NDY4NzUiPk5ldyBZb3JrIFBIUCAtIExpbnV4IEFwYWNoZSBNeVNRTCBQSFA8 +L0E+DQogICAgICAgIDxEVD48QSBIUkVGPSJodHRwOi8vd3d3LnBocGhpZGVvdXQuY29tL2FydGlj +bGVzLnBocCIgQUREX0RBVEU9IjEwNDI2NDY4NzUiIExBU1RfVklTSVQ9IjEwNDgxNzQ2NDQiIExB +U1RfTU9ESUZJRUQ9IjEwNDI2NDY4NzUiPlBIUCBIaWRlb3V0PC9BPg0KICAgICAgICA8RFQ+PEEg +SFJFRj0iaHR0cDovL3d3dy5waHAubmV0L2luZGV4LnBocCIgQUREX0RBVEU9IjEwNDI2NDY4NzUi +IExBU1RfVklTSVQ9IjEwNDgxNzQ2NDQiIExBU1RfTU9ESUZJRUQ9IjEwNDI2NDY4NzUiPlBIUCBI +eXBlcnRleHQgUHJlcHJvY2Vzc29yPC9BPg0KICAgICAgICA8RFQ+PEEgSFJFRj0iaHR0cDovL3d3 +dy5waHAtbWFnLmRlLyIgQUREX0RBVEU9IjEwNDI2NDY4NzUiIExBU1RfVklTSVQ9IjEwNDgxNzQ2 +NDQiIExBU1RfTU9ESUZJRUQ9IjEwNDI2NDY4NzUiPlBIUCBNYWdhemluIC0gR2VybWFuPC9BPg0K +ICAgICAgICA8RFQ+PEEgSFJFRj0iaHR0cDovL3d3dy5waHAubmV0L21hbnVhbC9lbi9mdW5jdGlv +bi5kYXRlLnBocCIgQUREX0RBVEU9IjEwNDI2NDY4NzUiIExBU1RfVklTSVQ9IjEwNDgxNzQ2NDQi +IExBU1RfTU9ESUZJRUQ9IjEwNDI2NDY4NzUiPlBIUCBNYW51YWwgZGF0ZTwvQT4NCiAgICAgICAg +PERUPjxBIEhSRUY9Imh0dHA6Ly93d3cucGhwd29ybGQuY29tLyIgQUREX0RBVEU9IjEwNDI2NDY4 +NzUiIExBU1RfVklTSVQ9IjEwNDgxNzQ2NDQiIExBU1RfTU9ESUZJRUQ9IjEwNDI2NDY4NzUiPlBI +UCBXT1JMRCAtIEZyZWUgUEhQIEhlbHAgLCBSZXNvdXJjZXMgYW5kIFNjcmlwdHMgQ29uc3VsdGlu +ZyAtIE1ZU1FMLCBPcmFjbGUgZGF0YWJhc2UgUEhQIFN1cHBvPC9BPg0KICAgICAgICA8RFQ+PEEg +SFJFRj0iaHR0cDovL3d3dy5waHBudWtlLm9yZy8iIEFERF9EQVRFPSIxMDQyNjQ2ODc1IiBMQVNU +X1ZJU0lUPSIxMDQ4MTc0NjQ0IiBMQVNUX01PRElGSUVEPSIxMDQyNjQ2ODc1Ij5QSFAtTnVrZTwv +QT4NCiAgICAgICAgPERUPjxBIEhSRUY9Imh0dHA6Ly93d3cucGhwYnVpbGRlci5jb20vIiBBRERf +REFURT0iMTA0MjY0Njg3NSIgTEFTVF9WSVNJVD0iMTA0ODE3NDY0NCIgTEFTVF9NT0RJRklFRD0i +MTA0MjY0Njg3NSI+UEhQQnVpbGRlci5jb20gLSBUaGUgUmVzb3VyY2UgRm9yIFBIUCBEZXZlbG9w +ZXJzPC9BPg0KICAgICAgICA8RFQ+PEEgSFJFRj0iaHR0cDovL3BocC1jb24uY29tLyIgQUREX0RB +VEU9IjEwNDc1NjU4NTIiIExBU1RfVklTSVQ9IjEwNDgxNzQ2NDQiIExBU1RfTU9ESUZJRUQ9IjEw +NDc1NjU4NTIiPlBIUENvbiBFYXN0IDIwMDMgIFVTIENvbmZlcmVuY2UgZm9yIFBIUCBEZXZlbG9w +ZXJzLjwvQT4NCiAgICAgICAgPERUPjxBIEhSRUY9Imh0dHA6Ly9waHBkZXZlbG9wZXIub3JnLyIg +QUREX0RBVEU9IjEwNDc1NzA0MzgiIExBU1RfVklTSVQ9IjEwNDgxNzQ2NDQiIExBU1RfTU9ESUZJ +RUQ9IjEwNDc1NzA0MzkiPlBIUERldmVsb3Blci5vcmc8L0E+DQogICAgICAgIDxEVD48QSBIUkVG +PSJodHRwOi8vd3d3LnBocGtpdGNoZW4uY29tLyIgQUREX0RBVEU9IjEwNDc1NjU4MTEiIExBU1Rf +VklTSVQ9IjEwNDgxNzQ2NDQiIExBU1RfTU9ESUZJRUQ9IjEwNDc1NjU4MTIiPlBIUGtpdGNoZW4g +LSBDb21lIFNlZSBXaGF0J3MgQ29va2luJzwvQT4NCiAgICAgICAgPERUPjxBIEhSRUY9Imh0dHA6 +Ly9waHB3aXphcmQubmV0LyIgQUREX0RBVEU9IjEwNDI2NDY4NzUiIExBU1RfVklTSVQ9IjEwNDgx +NzQ2NDQiIExBU1RfTU9ESUZJRUQ9IjEwNDI2NDY4NzUiPnBocFdpemFyZCAtIEJ1aWxkaW5nIER5 +bmFtaWMgV2Vic2l0ZXMgd2l0aCBQSFA8L0E+DQogICAgICAgIDxEVD48QSBIUkVGPSJodHRwOi8v +cHguc2tsYXIuY29tLyIgQUREX0RBVEU9IjEwNDI2NDY4NzUiIExBU1RfVklTSVQ9IjEwNDgxNzQ2 +NDQiIExBU1RfTU9ESUZJRUQ9IjEwNDI2NDY4NzUiPlBYIFBIUCBDb2RlIEV4Y2hhbmdlPC9BPg0K +ICAgICAgICA8RFQ+PEEgSFJFRj0iaHR0cDovL3d3dy5vbW5pZ3JvdXAuY29tL21haWxtYW4vYXJj +aGl2ZS93ZWJvYmplY3RzLWRldi8yMDAxLUZlYnJ1YXJ5LzAwNjgxOC5odG1sIiBBRERfREFURT0i +MTA0MjY0Njg3NSIgTEFTVF9WSVNJVD0iMTA0ODE3NDY0NCIgTEFTVF9NT0RJRklFRD0iMTA0MjY0 +Njg3NSI+UmFuZG9tIENWUyBub3RlczwvQT4NCiAgICAgICAgPERUPjxBIEhSRUY9Imh0dHA6Ly9w +aHAucmVzb3VyY2VpbmRleC5jb20vIiBBRERfREFURT0iMTA0MjY0Njg3NSIgTEFTVF9WSVNJVD0i +MTA0ODE3NDY0NCIgTEFTVF9NT0RJRklFRD0iMTA0MjY0Njg3NSI+VGhlIFBIUCBSZXNvdXJjZSBJ +bmRleDwvQT4NCiAgICAgICAgPERUPjxBIEhSRUY9Imh0dHA6Ly93d3cuc3RhcnMuY29tL0F1dGhv +cmluZy9MYW5ndWFnZXMvUEhQLyIgQUREX0RBVEU9IjEwNDI2NDY4NzUiIExBU1RfVklTSVQ9IjEw +NDgxNzQ2NDQiIExBU1RfTU9ESUZJRUQ9IjEwNDI2NDY4NzUiPldEVkwgUEhQPC9BPg0KICAgICAg +ICA8RFQ+PEEgSFJFRj0iaHR0cDovL3d3dy5kLnVtbi5lZHUvaXRzcy9zdXBwb3J0L1RyYWluaW5n +L09ubGluZS93ZWJkZXNpZ24vcGhwLmh0bWwiIEFERF9EQVRFPSIxMDQyNjQ2ODc1IiBMQVNUX1ZJ +U0lUPSIxMDQ4MTc0NjQ0IiBMQVNUX01PRElGSUVEPSIxMDQyNjQ2ODc1Ij5XZWIgRGVzaWduIFJl +ZmVyZW5jZXMgUEhQPC9BPg0KICAgICAgICA8RFQ+PEEgSFJFRj0iaHR0cDovL3d3dy53ZWJtYXN0 +ZXJiYXNlLmNvbS9zdWJjYXRzLzUzIiBBRERfREFURT0iMTA0MjY0Njg3NSIgTEFTVF9WSVNJVD0i +MTA0ODE3NDY0NCIgTEFTVF9NT0RJRklFRD0iMTA0MjY0Njg3NSI+V2VibWFzdGVyQmFzZSAtIFBI +UCBhbmQgTXlTUUw8L0E+DQogICAgPC9ETD48cD4NCiAgICA8RFQ+PEgzIEZPTERFRCBBRERfREFU +RT0iMTA0MjY0Njg3NSI+cGhwQkI8L0gzPg0KICAgIDxETD48cD4NCiAgICAgICAgPERUPjxBIEhS +RUY9Imh0dHA6Ly9oYWNrcy5waHBiYi5jb20vIiBBRERfREFURT0iMTA0MjY0Njg3NSIgTEFTVF9W +SVNJVD0iMTA0ODE3NDY0NCIgTEFTVF9NT0RJRklFRD0iMTA0MjY0Njg3NSI+SGFja3MucGhwQkIu +Q29tPC9BPg0KICAgICAgICA8RFQ+PEEgSFJFRj0iaHR0cDovL3BocGJiLnNvdXJjZWZvcmdlLm5l +dC9waHBCQjIvaW5kZXgucGhwIiBBRERfREFURT0iMTA0MjY0Njg3NSIgTEFTVF9WSVNJVD0iMTA0 +ODE3NDY0NCIgTEFTVF9NT0RJRklFRD0iMTA0MjY0Njg3NSI+cGhwQkIgMiAgSW5kZXg8L0E+DQog +ICAgICAgIDxEVD48QSBIUkVGPSJodHRwOi8vcGVyc29uYWwuYmVsbHNvdXRoLm5ldC9hdGwvYS9s +L2FsaWV4YWkvcGhwYmIvIiBBRERfREFURT0iMTA0MjY0Njg3NSIgTEFTVF9WSVNJVD0iMTA0ODE3 +NDY0NCIgTEFTVF9NT0RJRklFRD0iMTA0MjY0Njg3NSI+cGhwQkIyIENWUyBzbmFwc2hvdHM8L0E+ +DQogICAgICAgIDxEVD48QSBIUkVGPSJodHRwOi8vd3d3LnBocGJiMi11c2Vycy5kZS8iIEFERF9E +QVRFPSIxMDQyNjQ2ODc1IiBMQVNUX1ZJU0lUPSIxMDQ4MTc0NjQ0IiBMQVNUX01PRElGSUVEPSIx +MDQyNjQ2ODc1Ij5waHBCQjIuZGU8L0E+DQogICAgICAgIDxEVD48QSBIUkVGPSJodHRwOi8vc291 +cmNlZm9yZ2UubmV0L3Byb2plY3RzL3BocGJiLyIgQUREX0RBVEU9IjEwNDI2NDY4NzUiIExBU1Rf +VklTSVQ9IjEwNDgxNzQ2NDQiIExBU1RfTU9ESUZJRUQ9IjEwNDI2NDY4NzUiPlNvdXJjZUZvcmdl +IFByb2plY3QgSW5mbyAtIHBocEJCPC9BPg0KICAgIDwvREw+PHA+DQogICAgPERUPjxIMyBGT0xE +RUQgQUREX0RBVEU9IjEwNDI2NDY4NzUiPlBocE51a2U8L0gzPg0KICAgIDxETD48cD4NCiAgICAg +ICAgPERUPjxBIEhSRUY9Imh0dHA6Ly9waHBudWtlLm9yZy8iIEFERF9EQVRFPSIxMDQyNjQ2ODc1 +IiBMQVNUX1ZJU0lUPSIxMDQ4MTc0NjQ0IiBMQVNUX01PRElGSUVEPSIxMDQyNjQ2ODc1Ij5QSFAt +TnVrZTwvQT4NCiAgICAgICAgPERUPjxBIEhSRUY9Imh0dHA6Ly93d3cucWNoYy5jb20vIiBBRERf +REFURT0iMTA0MjY0Njg3NSIgTEFTVF9WSVNJVD0iMTA0ODE3NDY0NCIgTEFTVF9NT0RJRklFRD0i +MTA0MjY0Njg3NSI+UXVlYmVjIEhhcmRjb3JlIE5ld3M8L0E+DQogICAgICAgIDxEVD48QSBIUkVG +PSJodHRwOi8vZ3JvdXBzLnlhaG9vLmNvbS9ncm91cC9waHBudWtlL2ZpbGVzLyIgQUREX0RBVEU9 +IjEwNDI2NDY4NzUiIExBU1RfVklTSVQ9IjEwNDgxNzQ2NDQiIExBU1RfTU9ESUZJRUQ9IjEwNDI2 +NDY4NzUiPllhaG9vISBHcm91cHMgIHBocG51a2UgRmlsZXM8L0E+DQogICAgPC9ETD48cD4NCiAg +ICA8RFQ+PEgzIEZPTERFRCBBRERfREFURT0iMTA0MjY0Njg3NSI+UmVmZXJlbmNlczwvSDM+DQog +ICAgPERMPjxwPg0KICAgICAgICA8RFQ+PEEgSFJFRj0iaHR0cDovL2FsbG11c2ljLmNvbS8iIEFE +RF9EQVRFPSIxMDQyNjQ2ODc1IiBMQVNUX1ZJU0lUPSIxMDQ4MTc0NjQ0IiBMQVNUX01PRElGSUVE +PSIxMDQyNjQ2ODc1Ij5BTUcgQWxsIE11c2ljIEd1aWRlPC9BPg0KICAgICAgICA8RFQ+PEEgSFJF +Rj0iaHR0cDovL3d3dy5iYXJ0bGVieS5jb20vIiBBRERfREFURT0iMTA0MjY0Njg3NSIgTEFTVF9W +SVNJVD0iMTA0ODE3NDY0NCIgTEFTVF9NT0RJRklFRD0iMTA0MjY0Njg3NSI+QmFydGxlYnkuY29t +IEdyZWF0IEJvb2tzIE9ubGluZTwvQT4NCiAgICAgICAgPERUPjxBIEhSRUY9Imh0dHA6Ly9meWku +Y25uLmNvbS9meWkvIiBBRERfREFURT0iMTA0MjY0Njg3NSIgTEFTVF9WSVNJVD0iMTA0ODE3NDY0 +NCIgTEFTVF9NT0RJRklFRD0iMTA0MjY0Njg3NSI+Q05OZnlpLmNvbSAtIFN0dWRlbnQgTWFpbnBh +Z2U8L0E+DQogICAgICAgIDxEVD48QSBIUkVGPSJodHRwOi8vZ3d1LmVkdS9+Z3ByaWNlL2hhbmRi +b29rLmh0bSIgQUREX0RBVEU9IjEwNDI2NDY4NzUiIExBU1RfVklTSVQ9IjEwNDgxNzQ2NDQiIExB +U1RfTU9ESUZJRUQ9IjEwNDI2NDY4NzUiPkZhc3QgRmFjdHMgSGFuZGJvb2s8L0E+DQogICAgICAg +IDxEVD48QSBIUkVGPSJodHRwOi8vd3d3LmhhcmNvdXJ0LmNvbS8iIEFERF9EQVRFPSIxMDQyNjQ2 +ODc1IiBMQVNUX1ZJU0lUPSIxMDQ4MTc0NjQ0IiBMQVNUX01PRElGSUVEPSIxMDQyNjQ2ODc1Ij5o +b21ld29yayBoZWxwIGFuZCBlZHVjYXRpb25hbCByZXNvdXJjZXMgYXQgaGFyY291cnQuY29tPC9B +Pg0KICAgICAgICA8RFQ+PEEgSFJFRj0iaHR0cDovL3doYXRpcy50ZWNodGFyZ2V0LmNvbS8iIEFE +RF9EQVRFPSIxMDQyNjQ2ODc1IiBMQVNUX1ZJU0lUPSIxMDQ4MTc0NjQ0IiBMQVNUX01PRElGSUVE +PSIxMDQyNjQ2ODc1Ij53aGF0aXMuY29tPC9BPg0KICAgIDwvREw+PHA+DQogICAgPERUPjxIMyBG +T0xERUQgQUREX0RBVEU9IjEwNDI2NDY4NzUiPlNldGxpc3RzPC9IMz4NCiAgICA8REw+PHA+DQog +ICAgICAgIDxEVD48QSBIUkVGPSJodHRwOi8vd3d3LmFsbG1hbmJyb3RoZXJzYmFuZC5jb20vbXVz +aWMvYWJiYXNlLmh0bWwiIEFERF9EQVRFPSIxMDQyNjQ2ODc1IiBMQVNUX1ZJU0lUPSIxMDQ4MTc0 +NjQ0IiBMQVNUX01PRElGSUVEPSIxMDQyNjQ2ODc1Ij5BbGxtYW4gQnJvdGhlcnMgQmFuZCBTZXRs +aXN0czwvQT4NCiAgICAgICAgPERUPjxBIEhSRUY9Imh0dHA6Ly9iZW5oYXJwZXIubmV0L3NldGxp +c3RzL2xhdW5jaC5waHAiIEFERF9EQVRFPSIxMDQyNjQ2ODc1IiBMQVNUX1ZJU0lUPSIxMDQ4MTc0 +NjQ0IiBMQVNUX01PRElGSUVEPSIxMDQyNjQ2ODc1Ij5CZW4gSGFycGVyIFNldGxpc3RzPC9BPg0K +ICAgICAgICA8RFQ+PEEgSFJFRj0iaHR0cDovL3d3dy5taW5kc2hhcmVkZXNpZ24uY29tL2JsYWNr +Y3Jvd2VzL3NldGxpc3RzLmh0bWwiIEFERF9EQVRFPSIxMDQyNjQ2ODc1IiBMQVNUX1ZJU0lUPSIx +MDQ4MTc0NjQ0IiBMQVNUX01PRElGSUVEPSIxMDQyNjQ2ODc1Ij5CbGFjayBDcm93cyBTZXRsaXN0 +cyAjMTwvQT4NCiAgICAgICAgPERUPjxBIEhSRUY9Imh0dHA6Ly93d3cuYm9hLmNvbS9jZi90YXBl +c2VhcmNoLmNmbSIgQUREX0RBVEU9IjEwNDI2NDY4NzUiIExBU1RfVklTSVQ9IjEwNDgxNzQ2NDQi +IExBU1RfTU9ESUZJRUQ9IjEwNDI2NDY4NzUiPkJsYWNrIENyb3dzIFNldGxpc3RzICMyPC9BPg0K +ICAgICAgICA8RFQ+PEEgSFJFRj0iaHR0cDovL3d3dy5zZ2kubmV0L2JsdWVzdHJhdmVsZXIvU2V0 +bGlzdHMvZnJhbWUuaHRtbCIgQUREX0RBVEU9IjEwNDI2NDY4NzUiIExBU1RfVklTSVQ9IjEwNDgx +NzQ2NDQiIExBU1RfTU9ESUZJRUQ9IjEwNDI2NDY4NzUiPkJsdWVzIFRyYXZlbGVyIFNldGxpc3Rz +PC9BPg0KICAgICAgICA8RFQ+PEEgSFJFRj0iaHR0cDovL3d3dy5ibHVlc3RyYXZlbGVyLm5ldC9z +ZXRsaXN0LyIgQUREX0RBVEU9IjEwNDI2NDY4NzUiIExBU1RfVklTSVQ9IjEwNDgxNzQ2NDQiIExB +U1RfTU9ESUZJRUQ9IjEwNDI2NDY4NzUiPkJsdWVzIFRyYXZlbGVyIFN0YWdlIGFuZCBQcmUtc2hv +dyBTZXRsaXN0czwvQT4NCiAgICAgICAgPERUPjxBIEhSRUY9Imh0dHA6Ly93d3cuZXhwZWN0aW5n +cmFpbi5jb20vZG9rL3NldC90b3VybWFpbi5odG1sIiBBRERfREFURT0iMTA0MjY0Njg3NSIgTEFT +VF9WSVNJVD0iMTA0ODE3NDY0NCIgTEFTVF9NT0RJRklFRD0iMTA0MjY0Njg3NSI+Qm9iIER5bGFu +IFNldGxpc3RzPC9BPg0KICAgICAgICA8RFQ+PEEgSFJFRj0iaHR0cDovL3d3dy5saXZlbWFybGV5 +LmNvbS9wZXJmb3JtLmh0bSIgQUREX0RBVEU9IjEwNDI2NDY4NzUiIExBU1RfVklTSVQ9IjEwNDgx +NzQ2NDQiIExBU1RfTU9ESUZJRUQ9IjEwNDI2NDY4NzUiPkJvYiBNYXJsZXkgUGVyZm9ybWFuY2Vz +PC9BPg0KICAgICAgICA8RFQ+PEEgSFJFRj0iaHR0cDovL3d3dy5icnV1dWNlLmNvbS9zZXRsaXN0 +cy5odG0iIEFERF9EQVRFPSIxMDQyNjQ2ODc1IiBMQVNUX1ZJU0lUPSIxMDQ4MTc0NjQ0IiBMQVNU +X01PRElGSUVEPSIxMDQyNjQ2ODc1Ij5CcnVjZSBIb3Juc2J5IFNldGxpc3RzPC9BPg0KICAgICAg +ICA8RFQ+PEEgSFJFRj0iaHR0cDovL3d3dy5jaGFybGllaHVudGVyLmNvbS9zZXRsaXN0cy9pbmRl +eC5odG0iIEFERF9EQVRFPSIxMDQyNjQ2ODc1IiBMQVNUX1ZJU0lUPSIxMDQ4MTc0NjQ0IiBMQVNU +X01PRElGSUVEPSIxMDQyNjQ2ODc1Ij5DaGFybGllIEh1bnRlciBTZXRsaXN0cyA8L0E+DQogICAg +ICAgIDxEVD48QSBIUkVGPSJodHRwOi8vd3d3LmVudGVyYWN0LmNvbS9+cmZ1c3MvY2Nib290cy5o +dG1sIiBBRERfREFURT0iMTA0MjY0Njg3NSIgTEFTVF9WSVNJVD0iMTA0ODE3NDY0NCIgTEFTVF9N +T0RJRklFRD0iMTA0MjY0Njg3NSI+Q291bnRpbmcgQ3Jvd3MgQm9vdGxlZyBHdWlkZTwvQT4NCiAg +ICAgICAgPERUPjxBIEhSRUY9Imh0dHA6Ly93d3cuZ2VvY2l0aWVzLmNvbS9+c2F0ZWxsaXRlNzgv +IzIwMDAiIEFERF9EQVRFPSIxMDQyNjQ2ODc1IiBMQVNUX1ZJU0lUPSIxMDQ4MTc0NjQ0IiBMQVNU +X01PRElGSUVEPSIxMDQyNjQ2ODc1Ij5Db3VudGluZyBDcm93cyBTZXRsaXN0cyAtIDI8L0E+DQog +ICAgICAgIDxEVD48QSBIUkVGPSJodHRwOi8vd3d3LmRhcmtzdGFyb3JjaGVzdHJhLm5ldC9QYXN0 +X1NldGxpc3QuaHRtIiBBRERfREFURT0iMTA0MjY0Njg3NSIgTEFTVF9WSVNJVD0iMTA0ODE3NDY0 +NCIgTEFTVF9NT0RJRklFRD0iMTA0MjY0Njg3NSI+RGFyayBTdGFyIE9yY2hlc3RyYSBTZXRsaXN0 +PC9BPg0KICAgICAgICA8RFQ+PEEgSFJFRj0iaHR0cDovL3d3dy5uYW5jaWVzLm9yZy9kbWJ0YS8i +IEFERF9EQVRFPSIxMDQyNjQ2ODc1IiBMQVNUX1ZJU0lUPSIxMDQ4MTc0NjQ0IiBMQVNUX01PRElG +SUVEPSIxMDQyNjQ2ODc1Ij5EYXZlIE1hdHRoZXdzIEJhbmQgU2V0bGlzdHM8L0E+DQogICAgICAg +IDxEVD48QSBIUkVGPSJodHRwOi8vbWljaGVsZWouaG9tZS5taW5kc3ByaW5nLmNvbS9kZXJla2Jh +c2UvIiBBRERfREFURT0iMTA0MjY0Njg3NSIgTEFTVF9WSVNJVD0iMTA0ODE3NDY0NCIgTEFTVF9N +T0RJRklFRD0iMTA0MjY0Njg3NSI+RGVyZWsgVHJ1Y2tzIEJhbmQ8L0E+DQogICAgICAgIDxEVD48 +QSBIUkVGPSJodHRwOi8vd3d3LmRpc2NvYmlzY3VpdHMubmV0L3NldGxpc3RzLmh0bWwiIEFERF9E +QVRFPSIxMDQyNjQ2ODc1IiBMQVNUX1ZJU0lUPSIxMDQ4MTc0NjQ0IiBMQVNUX01PRElGSUVEPSIx +MDQyNjQ2ODc1Ij5EaXNjbyBCaXNjdWl0cyBTZXRsaXN0czwvQT4NCiAgICAgICAgPERUPjxBIEhS +RUY9Imh0dHA6Ly93d3cuZ2FsYWN0aWNmdW5rLmNvbS9zZXRsaXN0cy9kZWZhdWx0LmFzcCIgQURE +X0RBVEU9IjEwNDI2NDY4NzUiIExBU1RfVklTSVQ9IjEwNDgxNzQ2NDQiIExBU1RfTU9ESUZJRUQ9 +IjEwNDI2NDY4NzUiPkdhbGFjdGljIFNldGxpc3RzPC9BPg0KICAgICAgICA8RFQ+PEEgSFJFRj0i +aHR0cDovL3d3dy5tdWxlYmFzZS5jb20vIiBBRERfREFURT0iMTA0MjY0Njg3NSIgTEFTVF9WSVNJ +VD0iMTA0ODE3NDY0NCIgTEFTVF9NT0RJRklFRD0iMTA0MjY0Njg3NSI+R292J3QgTXVsZSBTZXRs +aXN0czwvQT4NCiAgICAgICAgPERUPjxBIEhSRUY9Imh0dHA6Ly93d3cuZGVhZGxpc3RzLmNvbS8i +IEFERF9EQVRFPSIxMDQyNjQ2ODc1IiBMQVNUX1ZJU0lUPSIxMDQ4MTc0NjQ0IiBMQVNUX01PRElG +SUVEPSIxMDQyNjQ2ODc1Ij5HcmF0ZWZ1bCBEZWFkTGlzdHM8L0E+DQogICAgICAgIDxEVD48QSBI +UkVGPSJodHRwOi8vd3d3Lm9uZS1wZXJjZW50LmNvbS9ib290b2dyYXBoeS9qYW5lc19hZGRpY3Rp +b24vaW5kZXguc2h0bWwiIEFERF9EQVRFPSIxMDQyNjQ2ODc1IiBMQVNUX1ZJU0lUPSIxMDQ4MTc0 +NjQ0IiBMQVNUX01PRElGSUVEPSIxMDQyNjQ2ODc1Ij5KYW5lJ3MgQWRkaWN0aW9uIC0gQm9vdHM8 +L0E+DQogICAgICAgIDxEVD48QSBIUkVGPSJodHRwOi8vd3d3LmphenptYW5kb2xpbnByb2plY3Qu +Y29tL3NldHMuaHRtbCIgQUREX0RBVEU9IjEwNDI2NDY4NzUiIExBU1RfVklTSVQ9IjEwNDgxNzQ2 +NDQiIExBU1RfTU9ESUZJRUQ9IjEwNDI2NDY4NzUiPkphenogTWFuZG9saW4gUHJvamVjdCBTZXRs +aXN0czwvQT4NCiAgICAgICAgPERUPjxBIEhSRUY9Imh0dHA6Ly93d3cudGhlamVycnlzaXRlLmNv +bS8iIEFERF9EQVRFPSIxMDQyNjQ2ODc1IiBMQVNUX1ZJU0lUPSIxMDQ4MTc0NjQ0IiBMQVNUX01P +RElGSUVEPSIxMDQyNjQ2ODc1Ij5KZXJyeSBHYXJjaWEgU2V0bGlzdHM8L0E+DQogICAgICAgIDxE +VD48QSBIUkVGPSJodHRwOi8vd3d3LmplcnJ5am9zZXBoLmNvbS9TZXRsaXN0cy5odG1sIiBBRERf +REFURT0iMTA0MjY0Njg3NSIgTEFTVF9WSVNJVD0iMTA0ODE3NDY0NCIgTEFTVF9NT0RJRklFRD0i +MTA0MjY0Njg3NSI+SmVycnkgSm9zZXBoICYgdGhlIEphY2ttb3Jtb25zIC0gU2V0bGlzdHM8L0E+ +DQogICAgICAgIDxEVD48QSBIUkVGPSJodHRwOi8vd3d3LmNvYm8ub3JnL2NnaS1sb2NhbC9zZXRs +aXN0cy9kYi5jZ2k/ZGI9ZGVmYXVsdCZ1aWQ9JnZpZXdfc2VhcmNoPTEiIEFERF9EQVRFPSIxMDQy +NjQ2ODc1IiBMQVNUX1ZJU0lUPSIxMDQ4MTc0NjQ0IiBMQVNUX01PRElGSUVEPSIxMDQyNjQ2ODc1 +Ij5KaW1teSBCdWZmZXQgU2V0bGlzdHM8L0E+DQogICAgICAgIDxEVD48QSBIUkVGPSJodHRwOi8v +d3d3Lmx1a2V3LmNvbS9rZHR1LyIgQUREX0RBVEU9IjEwNDI2NDY4NzUiIExBU1RfVklTSVQ9IjEw +NDgxNzQ2NDQiIExBU1RfTU9ESUZJRUQ9IjEwNDI2NDY4NzUiPkthcmwgRGVuc29uJ3MgVGlueSBV +bml2ZXJzZSBTZXRsaXN0czwvQT4NCiAgICAgICAgPERUPjxBIEhSRUY9Imh0dHA6Ly93d3cuYm9v +dGxlZHouY29tL3NldGxpc3RzLmh0bSIgQUREX0RBVEU9IjEwNDI2NDY4NzUiIExBU1RfVklTSVQ9 +IjEwNDgxNzQ2NDQiIExBU1RfTU9ESUZJRUQ9IjEwNDI2NDY4NzUiPkxlZCBaZXBwZWxpbiBTZXRs +aXN0czwvQT4NCiAgICAgICAgPERUPjxBIEhSRUY9Imh0dHA6Ly90aW5wYW4uZm9ydHVuZWNpdHku +Y29tL3NhaW50cy83NTIvbG9zX3NldGxpc3QvIiBBRERfREFURT0iMTA0MjY0Njg3NSIgTEFTVF9W +SVNJVD0iMTA0ODE3NDY0NCIgTEFTVF9NT0RJRklFRD0iMTA0MjY0Njg3NSI+TGVmdG92ZXIgU2Fs +bW9uIFNldGxpc3RzPC9BPg0KICAgICAgICA8RFQ+PEEgSFJFRj0iaHR0cDovL3d3dy5jcnlzdGFs +LW5pZ2h0LmNvbS9+YndpbGxlbi9NTVNldHMuaHRtIiBBRERfREFURT0iMTA0MjY0Njg3NSIgTEFT +VF9WSVNJVD0iMTA0ODE3NDY0NCIgTEFTVF9NT0RJRklFRD0iMTA0MjY0Njg3NSI+TW9kZXN0IE1v +dXNlIFNldGxpc3RzPC9BPg0KICAgICAgICA8RFQ+PEEgSFJFRj0iaHR0cDovL3d3dy5tb2VsaW5r +cy5jb20vc2V0bGlzdHMvaW5kZXguaHRtbCIgQUREX0RBVEU9IjEwNDI2NDY4NzUiIExBU1RfVklT +SVQ9IjEwNDgxNzQ2NDQiIExBU1RfTU9ESUZJRUQ9IjEwNDI2NDY4NzUiPk1vZSBTZXRsaXN0czwv +QT4NCiAgICAgICAgPERUPjxBIEhSRUY9Imh0dHA6Ly9jYW51ZmVlbGl0LmNvbS9Sb2JlcnQvdG9v +c2V0bGlzdHMuaHRtbCIgQUREX0RBVEU9IjEwNDI2NDY4NzUiIExBU1RfVklTSVQ9IjEwNDgxNzQ2 +NDQiIExBU1RfTU9ESUZJRUQ9IjEwNDI2NDY4NzUiPk90aGVyIE9uZXMgU2V0bGlzdHM8L0E+DQog +ICAgICAgIDxEVD48QSBIUkVGPSJodHRwOi8vd3d3LnBoaWx6b25lLmNvbS9waGlsYmFzZS8iIEFE +RF9EQVRFPSIxMDQyNjQ2ODc1IiBMQVNUX1ZJU0lUPSIxMDQ4MTc0NjQ0IiBMQVNUX01PRElGSUVE +PSIxMDQyNjQ2ODc1Ij5QaGlsIExlc2ggU2V0bGlzdHM8L0E+DQogICAgICAgIDxEVD48QSBIUkVG +PSJodHRwOi8vb3Vyd29ybGQuY29tcHVzZXJ2ZS5jb20vaG9tZXBhZ2VzL1BGQXJjaGl2ZXMvdG91 +cmRhdGUuaHRtIzE5NzIiIEFERF9EQVRFPSIxMDQyNjQ2ODc1IiBMQVNUX1ZJU0lUPSIxMDQ4MTc0 +NjQ0IiBMQVNUX01PRElGSUVEPSIxMDQyNjQ2ODc1Ij5QaW5rIEZsb3lkIENvbmNlcnQgQXBwZWFy +YW5jZXM8L0E+DQogICAgICAgIDxEVD48QSBIUkVGPSJodHRwOi8vd3d3LnBmLXJvaW8uZGUvIiBB +RERfREFURT0iMTA0MjY0Njg3NSIgTEFTVF9WSVNJVD0iMTA0ODE3NDY0NCIgTEFTVF9NT0RJRklF +RD0iMTA0MjY0Njg3NSI+UGluayBGbG95ZCBST0lPPC9BPg0KICAgICAgICA8RFQ+PEEgSFJFRj0i +aHR0cDovL21lbWJlcnMuYW9sLmNvbS9SYXRCYXNlL2Zyb250cGFnZS5odG1sIiBBRERfREFURT0i +MTA0MjY0Njg3NSIgTEFTVF9WSVNJVD0iMTA0ODE3NDY0NCIgTEFTVF9NT0RJRklFRD0iMTA0MjY0 +Njg3NSI+UmF0ZG9nIFNldGxpc3RzPC9BPg0KICAgICAgICA8RFQ+PEEgSFJFRj0iaHR0cDovL21l +bWJlcnMuYW9sLmNvbS9qd3RqdGEvcHJpbWFsc2NyZWFtLmh0bWwiIEFERF9EQVRFPSIxMDQyNjQ2 +ODc1IiBMQVNUX1ZJU0lUPSIxMDQ4MTc0NjQ0IiBMQVNUX01PRElGSUVEPSIxMDQyNjQ2ODc1Ij5S +dXN0ZWQgUm9vdCBTZXRsaXN0czwvQT4NCiAgICAgICAgPERUPjxBIEhSRUY9Imh0dHA6Ly93d3cu +dGhlc2FtcGxlcy5vcmcvc2V0bGlzdHMiIEFERF9EQVRFPSIxMDQyNjQ2ODc1IiBMQVNUX1ZJU0lU +PSIxMDQ4MTc0NjQ0IiBMQVNUX01PRElGSUVEPSIxMDQyNjQ2ODc1Ij5TYW1wbGVzIFNldGxpc3Rz +PC9BPg0KICAgICAgICA8RFQ+PEEgSFJFRj0iaHR0cDovL3d3dy5zdHRmLmNvbS9zZWN0b3I5Mi5o +dG1sIiBBRERfREFURT0iMTA0MjY0Njg3NSIgTEFTVF9WSVNJVD0iMTA0ODE3NDY0NCIgTEFTVF9N +T0RJRklFRD0iMTA0MjY0Njg3NSI+U2VjdG9yIDkgU2V0bGlzdHM8L0E+DQogICAgICAgIDxEVD48 +QSBIUkVGPSJodHRwOi8vd3d3LnNldGxpc3QuY29tLyIgQUREX0RBVEU9IjEwNDI2NDY4NzUiIExB +U1RfVklTSVQ9IjEwNDgxNzQ2NDQiIExBU1RfTU9ESUZJRUQ9IjEwNDI2NDY4NzUiPlNldGxpc3Qu +Q29tIEZvciBhbGwgeW91ciBzZXRsaXN0IG5lZWRzITwvQT4NCiAgICAgICAgPERUPjxBIEhSRUY9 +Imh0dHA6Ly9qYW0uYWJvdXQuY29tL211c2ljcGVyZm9ybS9qYW0vY3Mvc2V0bGlzdHMvaW5kZXgu +aHRtIiBBRERfREFURT0iMTA0MjY0Njg3NSIgTEFTVF9WSVNJVD0iMTA0ODE3NDY0NCIgTEFTVF9N +T0RJRklFRD0iMTA0MjY0Njg3NSI+U2V0bGlzdHMgLSBBYm91dC5jb208L0E+DQogICAgICAgIDxE +VD48QSBIUkVGPSJodHRwOi8vZXhwZXJ0LmNjLnB1cmR1ZS5lZHUvfm1vaHJqL2Nkci9zb3VsaXZl +Lmh0bWwiIEFERF9EQVRFPSIxMDQyNjQ2ODc1IiBMQVNUX1ZJU0lUPSIxMDQ4MTc0NjQ0IiBMQVNU +X01PRElGSUVEPSIxMDQyNjQ2ODc1Ij5Tb3VsaXZlIFNldGxpc3RzPC9BPg0KICAgICAgICA8RFQ+ +PEEgSFJFRj0iaHR0cDovL3d3dy5zdHJhbmdlZm9say5jb20vY29udGVudC9zZXRsaXN0L3NldGxp +c3RfZGIucGhwMyIgQUREX0RBVEU9IjEwNDI2NDY4NzUiIExBU1RfVklTSVQ9IjEwNDgxNzQ2NDQi +IExBU1RfTU9ESUZJRUQ9IjEwNDI2NDY4NzUiPlN0cmFuZ2Vmb2xrIFNldGxpc3RzPC9BPg0KICAg +ICAgICA8RFQ+PEEgSFJFRj0iaHR0cDovL3d3dy5mcmllbmRzb2ZjaGVlc2UuY29tL3NldGxpc3Rz +L2luZGV4LmFzcCIgQUREX0RBVEU9IjEwNDI2NDY4NzUiIExBU1RfVklTSVQ9IjEwNDgxNzQ2NDQi +IExBU1RfTU9ESUZJRUQ9IjEwNDI2NDY4NzUiPlN0cmluZyBDaGVlc2UgU2V0bGlzdHM8L0E+DQog +ICAgICAgIDxEVD48QSBIUkVGPSJodHRwOi8vd3d3LnBvcmNocGFydHkuY29tL3NldGxpc3RzLmh0 +bSIgQUREX0RBVEU9IjEwNDI2NDY4NzUiIExBU1RfVklTSVQ9IjEwNDgxNzQ2NDQiIExBU1RfTU9E +SUZJRUQ9IjEwNDI2NDY4NzUiPlRoZSBSZWNpcGUgU2V0bGlzdHM8L0E+DQogICAgICAgIDxEVD48 +QSBIUkVGPSJodHRwOi8vd3d3LnUyc2V0bGlzdHMuY29tLyIgQUREX0RBVEU9IjEwNDI2NDY4NzUi +IExBU1RfVklTSVQ9IjEwNDgxNzQ2NDQiIExBU1RfTU9ESUZJRUQ9IjEwNDI2NDY4NzUiPlUyIFNl +dGxpc3QgQXJjaGl2ZTwvQT4NCiAgICAgICAgPERUPjxBIEhSRUY9Imh0dHA6Ly93d3cuZXZlcnlk +YXljb21wYW5pb24uY29tL2FzcC9kZWZhdWx0LmFzcCIgQUREX0RBVEU9IjEwNDI2NDY4NzUiIExB +U1RfVklTSVQ9IjEwNDgxNzQ2NDQiIExBU1RfTU9ESUZJRUQ9IjEwNDI2NDY4NzUiPldpZGVzcHJl +YWQgUGFuaWMgU2V0bGlzdHM8L0E+DQogICAgPC9ETD48cD4NCiAgICA8RFQ+PEgzIEZPTERFRCBB +RERfREFURT0iMTA0MjY0Njg3NSI+U2hvcHBpbmc8L0gzPg0KICAgIDxETD48cD4NCiAgICAgICAg +PERUPjxBIEhSRUY9Imh0dHA6Ly93d3cuZmlnaHRkaXZ4LmNvbS9jb3Vwb25zLmh0bSIgQUREX0RB +VEU9IjEwNDI2NDY4NzUiIExBU1RfVklTSVQ9IjEwNDgxNzQ2NDQiIExBU1RfTU9ESUZJRUQ9IjEw +NDI2NDY4NzUiPk9ubGluZSBEVkQgRGVhbHMgYW5kIENvdXBvbnM8L0E+DQogICAgICAgIDxEVD48 +QSBIUkVGPSJodHRwOi8vdGVjaGJhcmdhaW5zLmNvbS8iIEFERF9EQVRFPSIxMDQyNjQ2ODc1IiBM +QVNUX1ZJU0lUPSIxMDQ4MTc0NjQ0IiBMQVNUX01PRElGSUVEPSIxMDQyNjQ2ODc1Ij50ZWNoYmFy +Z2FpbnMuY29tPC9BPg0KICAgIDwvREw+PHA+DQogICAgPERUPjxIMyBGT0xERUQgQUREX0RBVEU9 +IjEwNDI2NDY4NzUiPlN0ZWFtaW5nIFR1bmVzPC9IMz4NCiAgICA8REw+PHA+DQogICAgICAgIDxE +VD48QSBIUkVGPSJodHRwOi8vd3d3LjN3ay5jb20vbGlzdHJlYWwuaHRtIiBBRERfREFURT0iMTA0 +MjY0Njg3NSIgTEFTVF9WSVNJVD0iMTA0ODE3NDY0NCIgTEFTVF9NT0RJRklFRD0iMTA0MjY0Njg3 +NSI+M1dLIFJlYWxQbGF5ZXI8L0E+DQogICAgICAgIDxEVD48QSBIUkVGPSJodHRwOi8vd3d3LjN3 +ay5jb20vIiBBRERfREFURT0iMTA0MjY0Njg3NSIgTEFTVF9WSVNJVD0iMTA0ODE3NDY0NCIgTEFT +VF9NT0RJRklFRD0iMTA0MjY0Njg3NSI+M1dLIFVuZGVyZ3JvdW5kIFJhZGlvPC9BPg0KICAgICAg +ICA8RFQ+PEEgSFJFRj0iaHR0cDovL3d3dy5ncmF0ZWZ1bGRheS5jb20vIiBBRERfREFURT0iMTA0 +MjY0Njg3NSIgTEFTVF9WSVNJVD0iMTA0ODE3NDY0NCIgTEFTVF9NT0RJRklFRD0iMTA0MjY0Njg3 +NSI+R3JhdGVmdWwgRGF5PC9BPg0KICAgICAgICA8RFQ+PEEgSFJFRj0iaHR0cDovL3d3dy5zdWdh +cm1lZ3Mub3JnL2dyOGZ1bGRlYWRzbWlsLmh0bWwjZ2QiIEFERF9EQVRFPSIxMDQyNjQ2ODc1IiBM +QVNUX1ZJU0lUPSIxMDQ4MTc0NjQ0IiBMQVNUX01PRElGSUVEPSIxMDQyNjQ2ODc1Ij5HcmF0ZWZ1 +bCBEZWFkIGFuZCBQaHJpZW5kcy0gU3RyZWFtaW5nIFJlYWwgQXVkaW8gRmlsZXM8L0E+DQogICAg +ICAgIDxEVD48QSBIUkVGPSJodHRwOi8vdGhlaGlwLm9yZy9wdWIvYm9vdGxlZ3MvbXAzX3Nob3dz +LyIgQUREX0RBVEU9IjEwNDI2NDY4NzUiIExBU1RfVklTSVQ9IjEwNDgxNzQ2NDQiIExBU1RfTU9E +SUZJRUQ9IjEwNDI2NDY4NzUiPkhpcCBCb290cyAtIE1QMzwvQT4NCiAgICAgICAgPERUPjxBIEhS +RUY9Imh0dHA6Ly93d3cucnlhbmFkYW1zLmluZm8vcGxheWxpc3QuaHRtIiBBRERfREFURT0iMTA0 +NDU0OTAxNyIgTEFTVF9WSVNJVD0iMTA0ODE3NDY0NCIgTEFTVF9NT0RJRklFRD0iMTA0NDU0OTAx +NyI+SG9va2VkIG9uIFNvbmljcyBSYWRpbzwvQT4NCiAgICAgICAgPERUPjxBIEhSRUY9Imh0dHA6 +Ly93d3cubGlzdGVuLmNvbS9saXN0ZW5ob21lLmpzcCIgQUREX0RBVEU9IjEwNDI2NDY4NzUiIExB +U1RfVklTSVQ9IjEwNDgxNzQ2NDQiIExBU1RfTU9ESUZJRUQ9IjEwNDI2NDY4NzUiPkxpc3Rlbjwv +QT4NCiAgICAgICAgPERUPjxBIEhSRUY9Imh0dHA6Ly93d3cubXVzaWNuZXZlcnN0b3BzLmNvbS9p +bmRleC5waHAzP2xzX3Nlc3Npb249MGQxYmNkZTE4N2FhNzkxZGE4MDE5ZmU1MTVjMjdkNjQiIEFE +RF9EQVRFPSIxMDQyNjQ2ODc1IiBMQVNUX1ZJU0lUPSIxMDQ4MTc0NjQ0IiBMQVNUX01PRElGSUVE +PSIxMDQyNjQ2ODc1Ij5tdXNpY25ldmVyc3RvcHMuY29tPC9BPg0KICAgICAgICA8RFQ+PEEgSFJF +Rj0iaHR0cDovL3d3dy5vbnNoYXJlLmNvbS9ncm91cGxpc3QuanNwP2NhdGVnb3J5aWQ9MTYzNzQ0 +IiBBRERfREFURT0iMTA0MjY0Njg3NSIgTEFTVF9WSVNJVD0iMTA0ODE3NDY0NCIgTEFTVF9NT0RJ +RklFRD0iMTA0MjY0Njg3NSI+T25TaGFyZSBHcm91cExpc3Rpbmc8L0E+DQogICAgICAgIDxEVD48 +QSBIUkVGPSJodHRwOi8vd3d3LnRoZWJpZ3JpcG9mZi5jb20vIiBBRERfREFURT0iMTA0MjY0Njg3 +NSIgTEFTVF9WSVNJVD0iMTA0ODE3NDY0NCIgTEFTVF9NT0RJRklFRD0iMTA0MjY0Njg3NSI+cmlw +LW9mZiByYWRpbyBAIHRoZWJpZ3JpcG9mZi5jb208L0E+DQogICAgICAgIDxEVD48QSBIUkVGPSJo +dHRwOi8vc3VnYXJtZWdzLm9yZy8xYTQvP009RCIgQUREX0RBVEU9IjEwNDI2NDY4NzUiIExBU1Rf +VklTSVQ9IjEwNDgxNzQ2NDQiIExBU1RfTU9ESUZJRUQ9IjEwNDI2NDY4NzUiPlN1Z2FybWVncy0g +c3RyZWFtczwvQT4NCiAgICAgICAgPERUPjxBIEhSRUY9Imh0dHA6Ly90aGVqYW16b25lLmNvbS8i +IEFERF9EQVRFPSIxMDQyNjQ2ODc1IiBMQVNUX1ZJU0lUPSIxMDQ4MTc0NjQ0IiBMQVNUX01PRElG +SUVEPSIxMDQyNjQ2ODc1Ij5UaGUgSmFtIFpvbmUhIC0gQ29tZSBpbiBhbmQgZ3Jvb3ZlIHdpdGgg +dGhlIERlYWQsIFBoaXNoLCBQaW5rIEZsb3lkLCBEYXZlIE1hdHRoZXdzIGFuZCBtYW55IG90aDwv +QT4NCiAgICA8L0RMPjxwPg0KICAgIDxEVD48SDMgRk9MREVEIEFERF9EQVRFPSIxMDQyNjQ2ODc1 +Ij5UcmFkaW5nPC9IMz4NCiAgICA8REw+PHA+DQogICAgICAgIDxEVD48QSBIUkVGPSJodHRwOi8v +dHRjLmNvcnJ1cHQubmV0LyIgQUREX0RBVEU9IjEwNDI2NDY4NzUiIExBU1RfVklTSVQ9IjEwNDgx +NzQ2NDQiIExBU1RfTU9ESUZJRUQ9IjEwNDI2NDY4NzUiPiN0YXBlX3RyYWRlX2NlbnRyYWwgLSBE +YWxuZXQncyBDb25jZXJ0IFRyYWRpbmcgQ2hhbm5lbDwvQT4NCiAgICAgICAgPERUPjxBIEhSRUY9 +Imh0dHA6Ly93d3cuZGVzb2xhdGlvbnJvdy5jb20vY292ZXJzL2FydHdvcmsuaHRtbCIgQUREX0RB +VEU9IjEwNDI2NDY4NzUiIExBU1RfVklTSVQ9IjEwNDgxNzQ2NDQiIExBU1RfTU9ESUZJRUQ9IjEw +NDI2NDY4NzUiPkJvYiBEeWxhbiBCb290bGVnIEFydHdvcms8L0E+DQogICAgICAgIDxEVD48QSBI +UkVGPSJodHRwOi8vbWVtYmVycy50cmlwb2QuY29tL35EYXZlTUwvdHJhZGUtYXJ0Lmh0bSIgQURE +X0RBVEU9IjEwNDI2NDY4NzUiIExBU1RfVklTSVQ9IjEwNDgxNzQ2NDQiIExBU1RfTU9ESUZJRUQ9 +IjEwNDI2NDY4NzUiPkNELVIgVHJhZGluZyBCb290bGVnIENvdmVyIEFydCBMaW5rczwvQT4NCiAg +ICAgICAgPERUPjxBIEhSRUY9Imh0dHA6Ly9waG90b3MueWFob28uY29tL2JjL3dpdGVfa25pdGUv +bHN0PyYuZGlyPS9DRCtDT1ZFUitBUlQrLStPUklHSU5BTCYuc3JjPXBoJi5iZWdpbj05OTk5Ji52 +aWV3PXQmLm9yZGVyPSYuZG9uZT1odHRwJTNhLy9waG90b3MueWFob28uY29tL2JjL3dpdGVfa25p +dGUvbHN0JTNmJTI2LmRpcj0vQ0QlMmJDT1ZFUiUyYkFSVCUyYi0lMmJPUklHSU5BTCUyNi5zcmM9 +cGglMjYudmlldz10IiBBRERfREFURT0iMTA0MjY0Njg3NSIgTEFTVF9WSVNJVD0iMTA0ODE3NDY0 +NCIgTEFTVF9NT0RJRklFRD0iMTA0MjY0Njg3NSI+Q0RSIENvdmVycyAtIFlhaG9vISBQaG90b3Mg +LSBUaHVtYm5haWxzIFZpZXc8L0E+DQogICAgICAgIDxEVD48QSBIUkVGPSJodHRwOi8vd3d3LmNk +cmluZm8uY29tLyIgQUREX0RBVEU9IjEwNDI2NDY4NzUiIExBU1RfVklTSVQ9IjEwNDgxNzQ2NDQi +IExBU1RfTU9ESUZJRUQ9IjEwNDI2NDY4NzUiPkNEUi1JbmZvLCBUaGUgUmVjb3JkaW5nIEF1dGhv +cml0eTwvQT4NCiAgICAgICAgPERUPjxBIEhSRUY9Imh0dHA6Ly93d3cuY292ZXJ1bml2ZXJzZS5j +b20vIiBBRERfREFURT0iMTA0MjY0Njg3NSIgTEFTVF9WSVNJVD0iMTA0ODE3NDY0NCIgTEFTVF9N +T0RJRklFRD0iMTA0MjY0Njg3NSI+Q292ZXJVbml2ZXJzZTwvQT4NCiAgICAgICAgPERUPjxBIEhS +RUY9Imh0dHA6Ly93d3cuZXRyZWUub3JnLyIgQUREX0RBVEU9IjEwNDI2NDY4NzUiIExBU1RfVklT +SVQ9IjEwNDgxNzQ2NDQiIExBU1RfTU9ESUZJRUQ9IjEwNDI2NDY4NzUiPkV0cmVlLm9yZzwvQT4N +CiAgICAgICAgPERUPjxBIEhSRUY9Imh0dHA6Ly93d3cuZXRyZWVuZXdzLm9yZy4vaW5kZXgucGhw +IiBBRERfREFURT0iMTA0MjY0Njg3NSIgTEFTVF9WSVNJVD0iMTA0ODE3NDY0NCIgTEFTVF9NT0RJ +RklFRD0iMTA0MjY0Njg3NSI+ZXRyZWVuZXdzLm9yZzwvQT4NCiAgICAgICAgPERUPjxBIEhSRUY9 +ImZ0cDovL3RoZWhpcC5vcmcvcHViLyIgQUREX0RBVEU9IjEwNDI2NDY4NzUiIExBU1RfVklTSVQ9 +IjEwNDgxNzQ2NDQiIExBU1RfTU9ESUZJRUQ9IjEwNDI2NDY4NzUiPkhpcCBGVFA8L0E+DQogICAg +ICAgIDxEVD48QSBIUkVGPSJodHRwOi8vd3d3LnUyLWZsb20uZGUvIiBBRERfREFURT0iMTA0MjY0 +Njg3NSIgTEFTVF9WSVNJVD0iMTA0ODE3NDY0NCIgTEFTVF9NT0RJRklFRD0iMTA0MjY0Njg3NSI+ +aHR0cC0td3d3LnUyLWZsb20uZGUtPC9BPg0KICAgICAgICA8RFQ+PEEgSFJFRj0iaHR0cDovL3d3 +dy5pbmhpZGluZy5jb20vaW5kZXguaHRtIiBBRERfREFURT0iMTA0MjY0Njg3NSIgTEFTVF9WSVNJ +VD0iMTA0ODE3NDY0NCIgTEFTVF9NT0RJRklFRD0iMTA0MjY0Njg3NSI+aSBuIGggaSBkIGkgbiBn +oCAuoCBjIG8gbTwvQT4NCiAgICAgICAgPERUPjxBIEhSRUY9Imh0dHA6Ly93d3cubXVzaWNuZXZl +cnN0b3BzLmNvbS9ldHJlZS8iIEFERF9EQVRFPSIxMDQyNjQ2ODc1IiBMQVNUX1ZJU0lUPSIxMDQ4 +MTc0NjQ0IiBMQVNUX01PRElGSUVEPSIxMDQyNjQ2ODc1Ij5JbmRleCBvZiAtZXRyZWU8L0E+DQog +ICAgICAgIDxEVD48QSBIUkVGPSJodHRwOi8vd3d3LmNkLXJvbWR1cGxpY2F0aW9uLmNvbS9pbmxh +eV90ZW1wbGF0ZS5odG0iIEFERF9EQVRFPSIxMDQyNjQ2ODc1IiBMQVNUX1ZJU0lUPSIxMDQ4MTc0 +NjQ0IiBMQVNUX01PRElGSUVEPSIxMDQyNjQ2ODc1Ij5JbmxheSBDYXJkIFRlbXBsYXRlPC9BPg0K +ICAgICAgICA8RFQ+PEEgSFJFRj0iaHR0cDovL3d3dy5mb3J0dW5lY2l0eS5jby51ay9tYWRjaGVz +dGVyL2Jhc3MvMzc2L2luZGV4Lmh0bWwiIEFERF9EQVRFPSIxMDQyNjQ2ODc1IiBMQVNUX1ZJU0lU +PSIxMDQ4MTc0NjQ0IiBMQVNUX01PRElGSUVEPSIxMDQyNjQ2ODc1Ij5KdWxpYW4gRm93bGVyJ3Mg +Q0QgSmV3ZWwgQ2FzZSBJbnNlcnRzPC9BPg0KICAgICAgICA8RFQ+PEEgSFJFRj0iaHR0cDovL3d3 +dy5uaWNrc3BpY2tzLmNvbS9zaGVsbC5odG0iIEFERF9EQVRFPSIxMDQyNjQ2ODc1IiBMQVNUX1ZJ +U0lUPSIxMDQ4MTc0NjQ0IiBMQVNUX01PRElGSUVEPSIxMDQyNjQ2ODc1Ij5OaWNrc1BpY2tzLmNv +bTwvQT4NCiAgICAgICAgPERUPjxBIEhSRUY9Imh0dHA6Ly93d3cub2x5bXB1cy1tb25zLmNvbS9q +YW5lLyIgQUREX0RBVEU9IjEwNDI2NDY4NzUiIExBU1RfVklTSVQ9IjEwNDgxNzQ2NDQiIExBU1Rf +TU9ESUZJRUQ9IjEwNDI2NDY4NzUiPk5vdGhpbmcncyBTaG9ja2luZzwvQT4NCiAgICAgICAgPERU +PjxBIEhSRUY9Imh0dHA6Ly93d3cucGFwZXJjZGNhc2UuY29tLyIgQUREX0RBVEU9IjEwNDI2NDY4 +NzUiIExBU1RfVklTSVQ9IjEwNDgxNzQ2NDQiIExBU1RfTU9ESUZJRUQ9IjEwNDI2NDY4NzUiPlBh +cGVyIENEIENhc2U8L0E+DQogICAgICAgIDxEVD48QSBIUkVGPSJodHRwOi8vd3d3LnBmLXJvaW8u +ZGUvIiBBRERfREFURT0iMTA0MjY0Njg3NiIgTEFTVF9WSVNJVD0iMTA0ODE3NDY0NCIgTEFTVF9N +T0RJRklFRD0iMTA0MjY0Njg3NiI+UGluayBGbG95ZCBST0lPPC9BPg0KICAgICAgICA8RFQ+PEEg +SFJFRj0iaHR0cDovL3NhdGl2YS5ldHJlZS5vcmcvIiBBRERfREFURT0iMTA0MjY0Njg3NiIgTEFT +VF9WSVNJVD0iMTA0ODE3NDY0NCIgTEFTVF9NT0RJRklFRD0iMTA0MjY0Njg3NiI+c2F0aXZhLmV0 +cmVlLm9yZyBob21lcGFnZTwvQT4NCiAgICAgICAgPERUPjxBIEhSRUY9Imh0dHA6Ly9pY2R3ZWIu +Y2MucHVyZHVlLmVkdS9+c2NyZWVjaC9pbmRleDIuaHRtbCIgQUREX0RBVEU9IjEwNDI2NDY4NzYi +IExBU1RfVklTSVQ9IjEwNDgxNzQ2NDQiIExBU1RfTU9ESUZJRUQ9IjEwNDI2NDY4NzYiPlNjcmVl +Y2gncyBEb21haW48L0E+DQogICAgICAgIDxEVD48QSBIUkVGPSJodHRwOi8vZm9ydW0uc2hpbmJ1 +cm4uY29tL2luZGV4LnBocCIgQUREX0RBVEU9IjEwNDI2NDY4NzYiIExBU1RfVklTSVQ9IjEwNDgx +NzQ2NDQiIExBU1RfTU9ESUZJRUQ9IjEwNDI2NDY4NzYiPlNoaW5idXJuIC0gcG93ZXJlZCBieSBY +TUI8L0E+DQogICAgICAgIDxEVD48QSBIUkVGPSJodHRwOi8vc2hudHVuZXMub3JnLyIgQUREX0RB +VEU9IjEwNDI2NDY4NzYiIExBU1RfVklTSVQ9IjEwNDgxNzQ2NDQiIExBU1RfTU9ESUZJRUQ9IjEw +NDI2NDY4NzYiPnNobnR1bmVzLm9yZzwvQT4NCiAgICAgICAgPERUPjxBIEhSRUY9Imh0dHA6Ly93 +d3cuZ2VvY2l0aWVzLmNvbS9TdW5zZXRTdHJpcC9DbHViLzY3NTIvcmV2ZW5nZS5odG1sIiBBRERf +REFURT0iMTA0MjY0Njg3NiIgTEFTVF9WSVNJVD0iMTA0ODE3NDY0NCIgTEFTVF9NT0RJRklFRD0i +MTA0MjY0Njg3NiI+VGFwZXJzIFJldmVuZ2U8L0E+DQogICAgICAgIDxEVD48QSBIUkVGPSJodHRw +Oi8vd3d3LnN3LWRlc2lnbnMuY29tL3RlbXBsYXRlcy5odG1sIiBBRERfREFURT0iMTA0MjY0Njg3 +NiIgTEFTVF9WSVNJVD0iMTA0ODE3NDY0NCIgTEFTVF9NT0RJRklFRD0iMTA0MjY0Njg3NiI+VGVt +cGxhdGVzIENELVJPTSBMYWJlbHMsIENEIFRyYXkgQ2FyZHMsIENEIEluc2VydHMsIGFuZCBDRCBN +YWlsZXJzPC9BPg0KICAgICAgICA8RFQ+PEEgSFJFRj0iaHR0cDovL3d3dy5tZXRyb25vaXIuZjJz +LmNvbS8iIEFERF9EQVRFPSIxMDQyNjQ2ODc2IiBMQVNUX1ZJU0lUPSIxMDQ4MTc0NjQ0IiBMQVNU +X01PRElGSUVEPSIxMDQyNjQ2ODc2Ij5XZWxjb21lIHRvIE1ldHJvcG9saXMgTm9pciAtIFlvdXIg +aG9tZSBmb3IgaGlnaCBxdWFsaXR5IEhpcDwvQT4NCiAgICA8L0RMPjxwPg0KICAgIDxEVD48SDMg +Rk9MREVEIEFERF9EQVRFPSIxMDQyNjQ2ODc2Ij5XQVAtV01MPC9IMz4NCiAgICA8REw+PHA+DQog +ICAgICAgIDxEVD48QSBIUkVGPSJodHRwOi8vd3d3LmFsbG5ldGRldmljZXMuY29tL2ZhcS8iIEFE +RF9EQVRFPSIxMDQyNjQ2ODc2IiBMQVNUX1ZJU0lUPSIxMDQ4MTc0NjQ0IiBMQVNUX01PRElGSUVE +PSIxMDQyNjQ2ODc2Ij5hbGxOZXREZXZpY2VzIC0tIFRoZSBXaXJlbGVzcyBGQVE8L0E+DQogICAg +ICAgIDxEVD48QSBIUkVGPSJodHRwOi8vd3d3Lm5va2lhLmNvbS93YXAvIiBBRERfREFURT0iMTA0 +MjY0Njg3NiIgTEFTVF9WSVNJVD0iMTA0ODE3NDY0NCIgTEFTVF9NT0RJRklFRD0iMTA0MjY0Njg3 +NiI+Tm9raWEgLSBXQVAgb24gV2ViPC9BPg0KICAgICAgICA8RFQ+PEEgSFJFRj0iaHR0cDovL2Rl +dmVsb3Blci5vcGVud2F2ZS5jb20vIiBBRERfREFURT0iMTA0MjY0Njg3NiIgTEFTVF9WSVNJVD0i +MTA0ODE3NDY0NCIgTEFTVF9NT0RJRklFRD0iMTA0MjY0Njg3NiI+T3BlbndhdmUgRGV2ZWxvcGVy +IFByb2dyYW08L0E+DQogICAgICAgIDxEVD48QSBIUkVGPSJodHRwOi8vd3d3Lm9wZW53YXZlLmNv +bS9pbmRleC5odG1sIiBBRERfREFURT0iMTA0MjY0Njg3NiIgTEFTVF9WSVNJVD0iMTA0ODE3NDY0 +NCIgTEFTVF9NT0RJRklFRD0iMTA0MjY0Njg3NiI+T3BlbndhdmUgU3lzdGVtcyBJbmMuPC9BPg0K +ICAgICAgICA8RFQ+PEEgSFJFRj0iaHR0cDovL3d3dy53aXJlbGVzc2luYW51dHNoZWxsLmNvbS93 +YXAvd21scmVmLnNodG1sIiBBRERfREFURT0iMTA0MjY0Njg3NiIgTEFTVF9WSVNJVD0iMTA0ODE3 +NDY0NCIgTEFTVF9NT0RJRklFRD0iMTA0MjY0Njg3NiI+VGFnIFJlZmVyZW5jZSAtIFdpcmVsZXNz +IGluIGEgbnV0c2hlbGwuY29tPC9BPg0KICAgICAgICA8RFQ+PEEgSFJFRj0iaHR0cDovL3RhZ3Rh +Zy5jb20vc2l0ZS9pbmRleC5waHAzIiBBRERfREFURT0iMTA0MjY0Njg3NiIgTEFTVF9WSVNJVD0i +MTA0ODE3NDY0NCIgTEFTVF9NT0RJRklFRD0iMTA0MjY0Njg3NiI+VGFnVGFnLmNvbSAtKy0gRlJF +RSBXQVAgc2l0ZSBjb25zdHJ1Y3Rpb24gYW5kIGhvc3Rpbmc8L0E+DQogICAgICAgIDxEVD48QSBI +UkVGPSJodHRwOi8vdHRlbXVsYXRvci5jb20vIiBBRERfREFURT0iMTA0MjY0Njg3NiIgTEFTVF9W +SVNJVD0iMTA0ODE3NDY0NCIgTEFTVF9NT0RJRklFRD0iMTA0MjY0Njg3NiI+VFRlbXVsYXRvciAt +IFdBUCBlbXVsYXRvcjwvQT4NCiAgICAgICAgPERUPjxBIEhSRUY9Imh0dHA6Ly9jZWxscGhvbmVz +LmFib3V0LmNvbS9nYWRnZXRzL2NlbGxwaG9uZXMvY3Mvd2FwL2luZGV4Lmh0bT9ybms9cjEmdGVy +bXM9d2FwIiBBRERfREFURT0iMTA0MjY0Njg3NiIgTEFTVF9WSVNJVD0iMTA0ODE3NDY0NCIgTEFT +VF9NT0RJRklFRD0iMTA0MjY0Njg3NiI+V0FQIChXaXJlbGVzcyBhcHBsaWNhdGlvbiBwcm90b2Nv +bCkgLSBBYm91dC5jb208L0E+DQogICAgICAgIDxEVD48QSBIUkVGPSJodHRwOi8vd3d3LmdlbG9u +Lm5ldC8iIEFERF9EQVRFPSIxMDQyNjQ2ODc2IiBMQVNUX1ZJU0lUPSIxMDQ4MTc0NjQ0IiBMQVNU +X01PRElGSUVEPSIxMDQyNjQ2ODc2Ij5XQVAgYnJvd3NlciBAIEdlbG9uLm5ldDwvQT4NCiAgICAg +ICAgPERUPjxBIEhSRUY9Imh0dHA6Ly93d3cud2FwZm9ydW0ub3JnLyIgQUREX0RBVEU9IjEwNDI2 +NDY4NzYiIExBU1RfVklTSVQ9IjEwNDgxNzQ2NDQiIExBU1RfTU9ESUZJRUQ9IjEwNDI2NDY4NzYi +PldBUCBGb3J1bTwvQT4NCiAgICAgICAgPERUPjxBIEhSRUY9Imh0dHA6Ly93d3cuYW55d2hlcmV5 +b3Vnby5jb20vd2FwL0luZGV4LnBvIiBBRERfREFURT0iMTA0MjY0Njg3NiIgTEFTVF9WSVNJVD0i +MTA0ODE3NDY0NCIgTEFTVF9NT0RJRklFRD0iMTA0MjY0Njg3NiI+V0FQIEhvbWUgLSBBbnl3aGVy +ZVlvdUdvLmNvbTwvQT4NCiAgICAgICAgPERUPjxBIEhSRUY9Imh0dHA6Ly93d3cubm5ncm91cC5j +b20vcmVwb3J0cy93YXAvIiBBRERfREFURT0iMTA0MjY0Njg3NiIgTEFTVF9WSVNJVD0iMTA0ODE3 +NDY0NCIgTEFTVF9NT0RJRklFRD0iMTA0MjY0Njg3NiI+V0FQIFVzYWJpbGl0eSBSZXBvcnQgRmll +bGQgU3R1ZHkgRmFsbCAyMDAwPC9BPg0KICAgICAgICA8RFQ+PEEgSFJFRj0iaHR0cDovL3d3dy53 +YXAuY29tLyIgQUREX0RBVEU9IjEwNDI2NDY4NzYiIExBU1RfVklTSVQ9IjEwNDgxNzQ2NDQiIExB +U1RfTU9ESUZJRUQ9IjEwNDI2NDY4NzYiPldBUC5jb20gLSAtIFlvdXIgZ3VpZGUgdG8gdGhlIHdp +cmVsZXNzIEludGVybmV0LCB3YXAgcGhvbmVzLCB3YXAgc2VydmljZXMgYW5kIFBEQXM8L0E+DQog +ICAgICAgIDxEVD48QSBIUkVGPSJodHRwOi8vd3d3LndhcHRpZ2VyLmRlL3dhcHRpZ2VyLyIgQURE +X0RBVEU9IjEwNDI2NDY4NzYiIExBU1RfVklTSVQ9IjEwNDgxNzQ2NDQiIExBU1RfTU9ESUZJRUQ9 +IjEwNDI2NDY4NzYiPldhcFRpZ2VyIC0gV0FQIGVtdWxhdG9yPC9BPg0KICAgICAgICA8RFQ+PEEg +SFJFRj0iaHR0cDovL3d3dy53YXBkcml2ZS5jb20vRE9DUy9pbmRleC5odG1sIiBBRERfREFURT0i +MTA0MjY0Njg3NiIgTEFTVF9WSVNJVD0iMTA0ODE3NDY0NCIgTEFTVF9NT0RJRklFRD0iMTA0MjY0 +Njg3NiI+V2VsY29tZSB0byBXQVBEcml2ZSAgRnJlZSBXQVAgc2l0ZXMgIFdBUCBuZXdzICBXTUwg +dHV0b3JpYWwgIFdBUCBnYXRld2F5czwvQT4NCiAgICAgICAgPERUPjxBIEhSRUY9Imh0dHA6Ly93 +d3cud2lyZWxlc3NkZXZuZXQuY29tLyIgQUREX0RBVEU9IjEwNDI2NDY4NzYiIExBU1RfVklTSVQ9 +IjEwNDgxNzQ2NDUiIExBU1RfTU9ESUZJRUQ9IjEwNDI2NDY4NzYiPldpcmVsZXNzIERldmVsb3Bl +ciBOZXR3b3JrIC0gSG9tZTwvQT4NCiAgICAgICAgPERUPjxBIEhSRUY9Imh0dHA6Ly93d3cud2Fw +cmVkaXJlY3QuY29tLyIgQUREX0RBVEU9IjEwNDI2NDY4NzYiIExBU1RfVklTSVQ9IjEwNDgxNzQ2 +NDUiIExBU1RfTU9ESUZJRUQ9IjEwNDI2NDY4NzYiPldpcmVsZXNzIGluIGEgbnV0c2hlbGwuY29t +IC0gV0FQLCBTTVMsIGlNb2RlLCAzRywgQmx1ZXRvb3RoLCBWb3hNTCwgSjJNRTwvQT4NCiAgICAg +ICAgPERUPjxBIEhSRUY9Imh0dHA6Ly93aXJlbGVzcy5hYm91dC5jb20vaW5kdXN0cnkvd2lyZWxl +c3MvaW5kZXguaHRtP3Juaz1jNCZ0ZXJtcz13YXAiIEFERF9EQVRFPSIxMDQyNjQ2ODc2IiBMQVNU +X1ZJU0lUPSIxMDQ4MTc0NjQ1IiBMQVNUX01PRElGSUVEPSIxMDQyNjQ2ODc2Ij5XaXJlbGVzcyBJ +bmR1c3RyeSAtIEFib3V0LmNvbTwvQT4NCiAgICAgICAgPERUPjxBIEhSRUY9Imh0dHA6Ly93d3cu +d21sc2NyaXB0LmNvbS8iIEFERF9EQVRFPSIxMDQyNjQ2ODc2IiBMQVNUX1ZJU0lUPSIxMDQ4MTc0 +NjQ1IiBMQVNUX01PRElGSUVEPSIxMDQyNjQ2ODc2Ij5XTUxTY3JpcHQuY29tPC9BPg0KICAgICAg +ICA8RFQ+PEEgSFJFRj0iaHR0cDovL21vYmlsZS55YWhvby5jb20vcGhvbmUvdG91cj8ucHY9bWFp +biYucGc9MSYucGg9dHAiIEFERF9EQVRFPSIxMDQyNjQ2ODc2IiBMQVNUX1ZJU0lUPSIxMDQ4MTc0 +NjQ1IiBMQVNUX01PRElGSUVEPSIxMDQyNjQ2ODc2Ij5ZYWhvbyEgTW9iaWxlIC0gVG91cnM8L0E+ +DQogICAgICAgIDxEVD48QSBIUkVGPSJodHRwOi8vbW9iaWxlLnlhaG9vLmNvbS9waG9uZS9kaXIi +IEFERF9EQVRFPSIxMDQyNjQ2ODc2IiBMQVNUX1ZJU0lUPSIxMDQ4MTc0NjQ1IiBMQVNUX01PRElG +SUVEPSIxMDQyNjQ2ODc2Ij5ZYWhvbyEgTW9iaWxlPC9BPg0KICAgIDwvREw+PHA+DQogICAgPERU +PjxIMyBGT0xERUQgQUREX0RBVEU9IjEwNDI2NDY4NzYiPldlYiBEZXNpZ248L0gzPg0KICAgIDxE +TD48cD4NCiAgICAgICAgPERUPjxBIEhSRUY9Imh0dHA6Ly93d3cucGxpbmtvLm5ldC80MDQvIiBB +RERfREFURT0iMTA0MjY0Njg3NiIgTEFTVF9WSVNJVD0iMTA0ODE3NDY0NSIgTEFTVF9NT0RJRklF +RD0iMTA0MjY0Njg3NiI+NDA0IFJlc2VhcmNoIExhYjwvQT4NCiAgICAgICAgPERUPjxBIEhSRUY9 +Imh0dHA6Ly93d3cuYWxpc3RhcGFydC5jb20vbWFpbi5odG1sIiBBRERfREFURT0iMTA0MjY0Njg3 +NiIgTEFTVF9WSVNJVD0iMTA0ODE3NDY0NSIgTEFTVF9NT0RJRklFRD0iMTA0MjY0Njg3NiI+QSBM +aXN0IEFwYXJ0IEZvciBQZW9wbGUgV2hvIE1ha2UgV2Vic2l0ZXM8L0E+DQogICAgICAgIDxEVD48 +QSBIUkVGPSJodHRwOi8vd3d3LmFzc2VtYmxlci5vcmcvIiBBRERfREFURT0iMTA0MjY0Njg3NiIg +TEFTVF9WSVNJVD0iMTA0ODE3NDY0NSIgTEFTVF9NT0RJRklFRD0iMTA0MjY0Njg3NiI+QVNTRU1C +TEVSLk9SRyA9IE1ha2luZyBBcnQgV2l0aCBNYWNoaW5lIENvZGU7PC9BPg0KICAgICAgICA8RFQ+ +PEEgSFJFRj0iaHR0cDovL2JvYmJ5LmNhc3Qub3JnL2JvYmJ5L2h0bWwvZW4vaW5kZXguanNwIiBB +RERfREFURT0iMTA0MjY0Njg3NiIgTEFTVF9WSVNJVD0iMTA0ODE3NDY0NSIgTEFTVF9NT0RJRklF +RD0iMTA0MjY0Njg3NiI+Qm9iYnkgVmFsaWRhdG9yPC9BPg0KICAgICAgICA8RFQ+PEEgSFJFRj0i +aHR0cDovL3d3dy5ib3hlc2FuZGFycm93cy5jb20vIiBBRERfREFURT0iMTA0MjY0Njg3NiIgTEFT +VF9WSVNJVD0iMTA0ODE3NDY0NSIgTEFTVF9NT0RJRklFRD0iMTA0MjY0Njg3NiI+Qm94ZXMgYW5k +IEFycm93cyBCZWNhdXNlIHdlIGNhbjwvQT4NCiAgICAgICAgPERUPjxBIEhSRUY9Imh0dHA6Ly93 +d3cuY2hhbWkuY29tLyIgQUREX0RBVEU9IjEwNDI2NDY4NzYiIExBU1RfVklTSVQ9IjEwNDgxNzQ2 +NDUiIExBU1RfTU9ESUZJRUQ9IjEwNDI2NDY4NzYiPkNoYW1pLmNvbSAtIEhUTUwtS2l0LCBldGMu +PC9BPg0KICAgICAgICA8RFQ+PEEgSFJFRj0iaHR0cDovL2hvbWUuY25ldC5jb20vd2ViYnVpbGRp +bmcvMC03NjkwLmh0bWw/dGFnPXN0LmJsLjM4ODEuZGlyMS43NjkwIiBBRERfREFURT0iMTA0MjY0 +Njg3NiIgTEFTVF9WSVNJVD0iMTA0ODE3NDY0NSIgTEFTVF9NT0RJRklFRD0iMTA0MjY0Njg3NiI+ +Q05FVC5jb20gLSBXZWIgQnVpbGRpbmcgLSBTdHVwaWQgV2ViIFRyaWNrczwvQT4NCiAgICAgICAg +PERUPjxBIEhSRUY9Imh0dHA6Ly93d3cuY29sb3JzY2hlbWVyLmNvbS9vbmxpbmUvIiBBRERfREFU +RT0iMTA0MjY0Njg3NiIgTEFTVF9WSVNJVD0iMTA0ODE3NDY0NSIgTEFTVF9NT0RJRklFRD0iMTA0 +MjY0Njg3NiI+Q29sb3IgU2NoZW1lciAtIE9ubGluZSBDb2xvciBTY2hlbWUgR2VuZXJhdG9yPC9B +Pg0KICAgICAgICA8RFQ+PEEgSFJFRj0iaHR0cDovL2J1aWxkZXIuY25ldC5jb20vd2ViYnVpbGRp +bmcvMC0zODg1LTgtNDUwMDAzMS0xLmh0bWw/dGFnPXN0LmJsLjM4ODUuZGlyMS4zODg1LTgtNDUw +MDAzMSIgQUREX0RBVEU9IjEwNDI2NDY4NzYiIExBU1RfVklTSVQ9IjEwNDgxNzQ2NDUiIExBU1Rf +TU9ESUZJRUQ9IjEwNDI2NDY4NzYiPkNvbnRyYWN0cyBmb3IgZXZlcnkgb2NjYXNpb24gLSBXZWIg +QnVpbGRpbmcgLSBDTkVULmNvbTwvQT4NCiAgICAgICAgPERUPjxBIEhSRUY9Imh0dHA6Ly9tc2Ru +Lm1pY3Jvc29mdC5jb20vd29ya3Nob3AvYXV0aG9yL2Nzcy9yZWZlcmVuY2UvYXR0cmlidXRlcy5h +c3AiIEFERF9EQVRFPSIxMDQyNjQ2ODc2IiBMQVNUX1ZJU0lUPSIxMDQ4MTc0NjQ1IiBMQVNUX01P +RElGSUVEPSIxMDQyNjQ2ODc2Ij5DU1MgQXR0cmlidXRlcyBSZWZlcmVuY2U8L0E+DQogICAgICAg +IDxEVD48QSBIUkVGPSJodHRwOi8vd3d3Mi5pbWFnaXdhcmUuY29tL1J4SFRNTC8iIEFERF9EQVRF +PSIxMDQyNjQ2ODc2IiBMQVNUX1ZJU0lUPSIxMDQ4MTc0NjQ1IiBMQVNUX01PRElGSUVEPSIxMDQy +NjQ2ODc2Ij5Eb2N0b3IgSFRNTCB2NjwvQT4NCiAgICAgICAgPERUPjxBIEhSRUY9Imh0dHA6Ly9l +dm9sdC5vcmcvaW5kZXguaHRtbCIgQUREX0RBVEU9IjEwNDI2NDY4NzYiIExBU1RfVklTSVQ9IjEw +NDgxNzQ2NDUiIExBU1RfTU9ESUZJRUQ9IjEwNDI2NDY4NzYiPmV2b2x0Lm9yZyAgV29ya2VycyBv +ZiB0aGUgV2ViLCBFdm9sdCE8L0E+DQogICAgICAgIDxEVD48QSBIUkVGPSJodHRwOi8vd3d3LmZs +YXNoOTlnb29kLmNvbS9pbmRleF9mbGFzaC5odG1sIiBBRERfREFURT0iMTA0MjY0Njg3NiIgTEFT +VF9WSVNJVD0iMTA0ODE3NDY0NSIgTEFTVF9NT0RJRklFRD0iMTA0MjY0Njg3NiI+Rmxhc2ggOTkl +IEdvb2QuIEZpcnN0IEFpZCBNYW51YWwgRm9yIFVzYWJsZSBGbGFzaCBTaXRlczwvQT4NCiAgICAg +ICAgPERUPjxBIEhSRUY9Imh0dHA6Ly9yOTM3LmNvbS9mcWEuY2ZtIiBBRERfREFURT0iMTA0MjY0 +Njg3NiIgTEFTVF9WSVNJVD0iMTA0ODE3NDY0NSIgTEFTVF9NT0RJRklFRD0iMTA0MjY0Njg3NiI+ +RnJlcXVlbnQgUXVlc3Rpb25zIEFuc3dlcmVkIC0tIHI5MzcuY29tPC9BPg0KICAgICAgICA8RFQ+ +PEEgSFJFRj0iaHR0cDovL3d3dy5nbGlzaC5jb20vY3NzLyIgQUREX0RBVEU9IjEwNDI2NDY4NzYi +IExBU1RfVklTSVQ9IjEwNDgxNzQ2NDUiIExBU1RfTU9ESUZJRUQ9IjEwNDI2NDY4NzYiPmdsaXNo +LmNvbSAgQ1NTIGxheW91dCB0ZWNobmlxdWVzPC9BPg0KICAgICAgICA8RFQ+PEEgSFJFRj0iaHR0 +cDovL2RlZXppbi5jb20vIiBBRERfREFURT0iMTA0MjY0Njg3NiIgTEFTVF9WSVNJVD0iMTA0ODE3 +NDY0NSIgTEFTVF9NT0RJRklFRD0iMTA0MjY0Njg3NiI+R3JhcGhpYyBEZXNpZ24gUmVzb3VyY2Vz +IENlbnRlciAtIEdyYXBoaWMgZGVzaWduIHR1dG9yaWFscywgZ3JhcGhpY3MgdGlwcywgd2ViIGRl +c2lnbiBhcnRpY2xlcyw8L0E+DQogICAgICAgIDxEVD48QSBIUkVGPSJodHRwOi8vd3d3Lmh0bWxo +ZWxwLmNvbS9yZWZlcmVuY2UvY3NzLyIgQUREX0RBVEU9IjEwNDI2NDY4NzYiIExBU1RfVklTSVQ9 +IjEwNDgxNzQ2NDUiIExBU1RfTU9ESUZJRUQ9IjEwNDI2NDY4NzYiPkd1aWRlIHRvIENhc2NhZGlu +ZyBTdHlsZSBTaGVldHM8L0E+DQogICAgICAgIDxEVD48QSBIUkVGPSJodHRwOi8vd3d3Lmd1aXN0 +dWZmLmNvbS8iIEFERF9EQVRFPSIxMDQyNjQ2ODc2IiBMQVNUX1ZJU0lUPSIxMDQ4MTc0NjQ1IiBM +QVNUX01PRElGSUVEPSIxMDQyNjQ2ODc2Ij5HVUlTdHVmZi5jb20gLSBGcmVlIEdyYXBoaWNhbCBV +c2VyIEludGVyZmFjZXM8L0E+DQogICAgICAgIDxEVD48QSBIUkVGPSJodHRwOi8vd3d3LmRldmVs +b3Blci5pcnQub3JnL3NjcmlwdC93aW5kb3cuaHRtIiBBRERfREFURT0iMTA0MjY0Njg3NiIgTEFT +VF9WSVNJVD0iMTA0ODE3NDY0NSIgTEFTVF9NT0RJRklFRD0iMTA0MjY0Njg3NiI+aXJ0Lm9yZyAt +IEphdmFTY3JpcHQgV2luZG93cyBGQVEgS25vd2xlZGdlIEJhc2U8L0E+DQogICAgICAgIDxEVD48 +QSBIUkVGPSJodHRwOi8vd3d3LnhzNGFsbC5ubC9+cHBrL2pzL2luZGV4Lmh0bWwiIEFERF9EQVRF +PSIxMDQyNjQ2ODc2IiBMQVNUX1ZJU0lUPSIxMDQ4MTc0NjQ1IiBMQVNUX01PRElGSUVEPSIxMDQy +NjQ2ODc2Ij5KYXZhU2NyaXB0IFNlY3Rpb24gLSBIb21lcGFnZTwvQT4NCiAgICAgICAgPERUPjxB +IEhSRUY9Imh0dHA6Ly93d3cuampnLm5ldC9pYS8iIEFERF9EQVRFPSIxMDQyNjQ2ODc2IiBMQVNU +X1ZJU0lUPSIxMDQ4MTc0NjQ1IiBMQVNUX01PRElGSUVEPSIxMDQyNjQ2ODc2Ij5qamcubmV0IGlu +Zm9ybWF0aW9uIGFyY2hpdGVjdHVyZSByZXNvdXJjZXM8L0E+DQogICAgICAgIDxEVD48QSBIUkVG +PSJodHRwOi8vd3d3LnNob3Jld2Fsa2VyLmNvbS9wYWdlcy9saWdodGhvdXNlX2luZGV4Lmh0bWwi +IEFERF9EQVRFPSIxMDQyNjQ2ODc2IiBMQVNUX1ZJU0lUPSIxMDQ4MTc0NjQ1IiBMQVNUX01PRElG +SUVEPSIxMDQyNjQ2ODc2Ij5MaWdodGhvdXNlIGxhdGVzdDwvQT4NCiAgICAgICAgPERUPjxBIEhS +RUY9Imh0dHA6Ly93d3cud2Vic2l0ZS1hbmFseXN0LmNvLmlsL2x1Y2Rlc2svbHVjZGVzay5odG1s +IiBBRERfREFURT0iMTA0MjY0Njg3NiIgTEFTVF9WSVNJVD0iMTA0ODE3NDY0NSIgTEFTVF9NT0RJ +RklFRD0iMTA0MjY0Njg3NiI+THVjRGVzayAtIEluZm9ybWF0aW9uIERlc2lnbiAuIFdlYiBVc2Fi +aWxpdHkgLiBVc2VyIEV4cGVyaWVuY2U8L0E+DQogICAgICAgIDxEVD48QSBIUkVGPSJodHRwOi8v +d3d3Lm1lZGlhaW5zcGlyYXRpb24uY29tLyIgQUREX0RBVEU9IjEwNDI2NDY4NzYiIExBU1RfVklT +SVQ9IjEwNDgxNzQ2NDUiIExBU1RfTU9ESUZJRUQ9IjEwNDI2NDY4NzYiPm1lZGlhIGluc3BpcmF0 +aW9uPC9BPg0KICAgICAgICA8RFQ+PEEgSFJFRj0iaHR0cDovL21vcmVjcmF5b25zLmNvbS9pbmRl +eC5odG1sIiBBRERfREFURT0iMTA0MjY0Njg3NiIgTEFTVF9WSVNJVD0iMTA0ODE3NDY0NSIgTEFT +VF9NT0RJRklFRD0iMTA0MjY0Njg3NiI+bW9yZSBjcmF5b25zIGNvbG9ycyBmb3Igd2ViIGRlc2ln +bmVyczwvQT4NCiAgICAgICAgPERUPjxBIEhSRUY9Imh0dHA6Ly93d3cudi0yLm9yZy9pbmRleC5o +dG1sIiBBRERfREFURT0iMTA0MjY0Njg3NiIgTEFTVF9WSVNJVD0iMTA0ODE3NDY0NSIgTEFTVF9N +T0RJRklFRD0iMTA0MjY0Njg3NiI+bXkgZ29kLi4uaXQncyBwaGlsaXBwZSBzdGFyY2suLi4gIHYt +MiBvcmdhbmlzYXRpb248L0E+DQogICAgICAgIDxEVD48QSBIUkVGPSJodHRwOi8vY29tcHV0aW5n +Lm5ldHNjYXBlLmNvbS9jb21wdXRpbmcvZGV2ZWxvcGVyL21haW4udG1wbCIgQUREX0RBVEU9IjEw +NDI2NDY4NzYiIExBU1RfVklTSVQ9IjEwNDgxNzQ2NDUiIExBU1RfTU9ESUZJRUQ9IjEwNDI2NDY4 +NzYiPk5ldHNjYXBlIC0gRGV2ZWxvcGVyPC9BPg0KICAgICAgICA8RFQ+PEEgSFJFRj0iaHR0cDov +L3d3dy5vcmVpbGx5bmV0LmNvbS9qYXZhc2NyaXB0LyIgQUREX0RBVEU9IjEwNDI2NDY4NzYiIExB +U1RfVklTSVQ9IjEwNDgxNzQ2NDUiIExBU1RfTU9ESUZJRUQ9IjEwNDI2NDY4NzYiPk8nUmVpbGx5 +IE5ldHdvcmsgSmF2YXNjcmlwdCBhbmQgQ1NTIERldkNlbnRlcjwvQT4NCiAgICAgICAgPERUPjxB +IEhSRUY9Imh0dHA6Ly93d3cucGRuLXBpeC5jb20vcGl4L2NvbHVtbi8iIEFERF9EQVRFPSIxMDQy +NjQ2ODc2IiBMQVNUX1ZJU0lUPSIxMDQ4MTc0NjQ1IiBMQVNUX01PRElGSUVEPSIxMDQyNjQ2ODc2 +Ij5QRE4ncyBQSVggLSBzZWNvbmQgc2l0ZTwvQT4NCiAgICAgICAgPERUPjxBIEhSRUY9Imh0dHA6 +Ly93d3cucGRuLXBpeC5jb20vcGl4LyIgQUREX0RBVEU9IjEwNDI2NDY4NzYiIExBU1RfVklTSVQ9 +IjEwNDgxNzQ2NDUiIExBU1RfTU9ESUZJRUQ9IjEwNDI2NDY4NzYiPlBETidzIFBJWCAtIFRIRSBN +QUdBWklORSBGT1IgVklTVUFMIENSRUFUSVZFUzwvQT4NCiAgICAgICAgPERUPjxBIEhSRUY9Imh0 +dHA6Ly93d3cucGlyYXRlZC1zaXRlcy5jb20vIiBBRERfREFURT0iMTA0MjY0Njg3NiIgTEFTVF9W +SVNJVD0iMTA0ODE3NDY0NSIgTEFTVF9NT0RJRklFRD0iMTA0MjY0Njg3NiI+UGlyYXRlZCBTaXRl +cyEhIEFhYXJyZ2doaC4uLjwvQT4NCiAgICAgICAgPERUPjxBIEhSRUY9Imh0dHA6Ly93d3cucGly +YXRlZC1zaXRlcy5jb20vIiBBRERfREFURT0iMTA0MjY0Njg3NiIgTEFTVF9WSVNJVD0iMTA0ODE3 +NDY0NSIgTEFTVF9NT0RJRklFRD0iMTA0MjY0Njg3NiI+UGlyYXRlZCBTaXRlcyE8L0E+DQogICAg +ICAgIDxEVD48QSBIUkVGPSJodHRwOi8vd3d3LmRyZWFtd2VhdmVyLWV4dGVuc2lvbnMuY29tLyIg +QUREX0RBVEU9IjEwNDI2NDY4NzYiIExBU1RfVklTSVQ9IjEwNDgxNzQ2NDUiIExBU1RfTU9ESUZJ +RUQ9IjEwNDI2NDY4NzYiPlJhYmkncyBEcmVhbXdlYXZlciBFeHRlbnNpb25zPC9BPg0KICAgICAg +ICA8RFQ+PEEgSFJFRj0iaHR0cDovL3d3dy53ZWJndWlsZC5vcmcvY29udGVzdC8iIEFERF9EQVRF +PSIxMDQyNjQ2ODc2IiBMQVNUX1ZJU0lUPSIxMDQ4MTc0NjQ1IiBMQVNUX01PRElGSUVEPSIxMDQy +NjQ2ODc2Ij5TaWxpY29uIFZhbGxleSBXZWJHdWlsZCBXZWIgU2l0ZSBEZXNpZ24gQ29udGVzdDwv +QT4NCiAgICAgICAgPERUPjxBIEhSRUY9Imh0dHA6Ly93d3cuc2ltcGx5dGhlYmVzdHNjcmlwdHMu +Y29tL2phdmFzY3JpcHRzLyIgQUREX0RBVEU9IjEwNDI2NDY4NzYiIExBU1RfVklTSVQ9IjEwNDgx +NzQ2NDUiIExBU1RfTU9ESUZJRUQ9IjEwNDI2NDY4NzYiPlNpbXBseXRoZUJlc3QgSmF2YXNjcmlw +dHMsIEpBVkEgU2NyaXB0czwvQT4NCiAgICAgICAgPERUPjxBIEhSRUY9Imh0dHA6Ly93d3cuc2l0 +ZXBvaW50LmNvbS8iIEFERF9EQVRFPSIxMDQyNjQ2ODc2IiBMQVNUX1ZJU0lUPSIxMDQ4MTc0NjQ1 +IiBMQVNUX01PRElGSUVEPSIxMDQyNjQ2ODc2Ij5TaXRlUG9pbnQuY29tIC0gSGVscGluZyBCdXNp +bmVzcyBHcm93IE9ubGluZSE8L0E+DQogICAgICAgIDxEVD48QSBIUkVGPSJodHRwOi8vd3d3LnRl +YW1waG90b3Nob3AuY29tL3Bob3Rvc2hvcC90dXRvcmlhbHMvcHNfdHV0X3RlY2huaXF1ZXMucGhw +IyIgQUREX0RBVEU9IjEwNDI2NDY4NzYiIExBU1RfVklTSVQ9IjEwNDgxNzQ2NDUiIExBU1RfTU9E +SUZJRUQ9IjEwNDI2NDY4NzYiPnRlYW1waG90b3Nob3AuY29tPC9BPg0KICAgICAgICA8RFQ+PEEg +SFJFRj0iaHR0cDovL3d3dy53ZWJzdGFuZGFyZHMub3JnLyIgQUREX0RBVEU9IjEwNDI2NDY4NzYi +IExBU1RfVklTSVQ9IjEwNDgxNzQ2NDUiIExBU1RfTU9ESUZJRUQ9IjEwNDI2NDY4NzYiPlRoZSBX +ZWIgU3RhbmRhcmRzIFByb2plY3QgRmlnaHRpbmcgZm9yIFN0YW5kYXJkcyBpbiBvdXIgQnJvd3Nl +cnM8L0E+DQogICAgICAgIDxEVD48QSBIUkVGPSJodHRwOi8vdG9tYWxhay5vcmcvIiBBRERfREFU +RT0iMTA0MjY0Njg3NiIgTEFTVF9WSVNJVD0iMTA0ODE3NDY0NSIgTEFTVF9NT0RJRklFRD0iMTA0 +MjY0Njg3NiI+VG9tYWxhaydzIFJlYWxtICBEYWlseSBMaW5rcyB0byBTdHJhdGVnaWMgV2ViIERl +c2lnbiBOZXdzPC9BPg0KICAgICAgICA8RFQ+PEEgSFJFRj0iaHR0cDovL3d3dy5qdWljeXN0dWRp +by5jb20vdHV0b3JpYWxzLmh0bWwiIEFERF9EQVRFPSIxMDQyNjQ2ODc2IiBMQVNUX1ZJU0lUPSIx +MDQ4MTc0NjQ1IiBMQVNUX01PRElGSUVEPSIxMDQyNjQ2ODc2Ij5UdXRvcmlhbHM8L0E+DQogICAg +ICAgIDxEVD48QSBIUkVGPSJodHRwOi8vd3d3LnVzYWJpbGl0eS5nb3YvIiBBRERfREFURT0iMTA0 +MjY0Njg3NiIgTEFTVF9WSVNJVD0iMTA0ODE3NDY0NSIgTEFTVF9NT0RJRklFRD0iMTA0MjY0Njg3 +NiI+VXNhYmlsaXR5LmdvdiAtIFByb3ZpZGVkIGJ5IHRoZSBOQ0kgTmF0aW9uYWwgQ2FuY2VyIElu +c3RpdHV0ZTwvQT4NCiAgICAgICAgPERUPjxBIEhSRUY9Imh0dHA6Ly91c2FibGV3ZWIuY29tLyIg +QUREX0RBVEU9IjEwNDI2NDY4NzYiIExBU1RfVklTSVQ9IjEwNDgxNzQ2NDUiIExBU1RfTU9ESUZJ +RUQ9IjEwNDI2NDY4NzYiPlVzYWJsZSBXZWI8L0E+DQogICAgICAgIDxEVD48QSBIUkVGPSJodHRw +Oi8vd3d3LnZpc2lib25lLmNvbS9jb2xvcmxhYi8iIEFERF9EQVRFPSIxMDQyNjQ2ODc2IiBMQVNU +X1ZJU0lUPSIxMDQ4MTc0NjQ1IiBMQVNUX01PRElGSUVEPSIxMDQyNjQ2ODc2Ij5WaXNpQm9uZSBX +ZWJtYXN0ZXIncyBDb2xvciBMYWI8L0E+DQogICAgICAgIDxEVD48QSBIUkVGPSJodHRwOi8vdmFs +aWRhdG9yLnczLm9yZy8iIEFERF9EQVRFPSIxMDQyNjQ2ODc2IiBMQVNUX1ZJU0lUPSIxMDQ4MTc0 +NjQ1IiBMQVNUX01PRElGSUVEPSIxMDQyNjQ2ODc2Ij5XM0MgSFRNTCBWYWxpZGF0aW9uIFNlcnZp +Y2U8L0E+DQogICAgICAgIDxEVD48QSBIUkVGPSJodHRwOi8vd3d3Lnczc2Nob29scy5jb20vZGVm +YXVsdC5hc3AiIEFERF9EQVRFPSIxMDQyNjQ2ODc2IiBMQVNUX1ZJU0lUPSIxMDQ4MTc0NjQ1IiBM +QVNUX01PRElGSUVEPSIxMDQyNjQ2ODc2Ij5XM1NjaG9vbHMgT25saW5lIFdlYiBUdXRvcmlhbHM8 +L0E+DQogICAgICAgIDxEVD48QSBIUkVGPSJodHRwOi8vd3d3Lm11bmRpZGVzaWduLmNvbS93ZWJj +dC93ZWJjdC5odG1sIiBBRERfREFURT0iMTA0MjY0Njg3NiIgTEFTVF9WSVNJVD0iMTA0ODE3NDY0 +NSIgTEFTVF9NT0RJRklFRD0iMTA0MjY0Njg3NiI+V2ViIENvbG9yIFRoZW9yeTwvQT4NCiAgICAg +ICAgPERUPjxBIEhSRUY9Imh0dHA6Ly93d3cuZC51bW4uZWR1L2l0c3Mvc3VwcG9ydC9UcmFpbmlu +Zy9PbmxpbmUvd2ViZGVzaWduLyIgQUREX0RBVEU9IjEwNDI2NDY4NzYiIExBU1RfVklTSVQ9IjEw +NDgxNzQ2NDUiIExBU1RfTU9ESUZJRUQ9IjEwNDI2NDY4NzYiPldlYiBEZXNpZ24gUmVmZXJlbmNl +czwvQT4NCiAgICAgICAgPERUPjxBIEhSRUY9Imh0dHA6Ly93d3cuc3RhcnMuY29tLyIgQUREX0RB +VEU9IjEwNDI2NDY4NzYiIExBU1RfVklTSVQ9IjEwNDgxNzQ2NDUiIExBU1RfTU9ESUZJRUQ9IjEw +NDI2NDY4NzYiPldlYiBEZXZlbG9wZXIncyBWaXJ0dWFsIExpYnJhcnkgRW5jeWNsb3BlZGlhIG9m +IFdlYiBEZXNpZ24gVHV0b3JpYWxzLCBBcnRpY2xlcyBhbmQgRGlzY3Vzc2lvbnM8L0E+DQogICAg +ICAgIDxEVD48QSBIUkVGPSJodHRwOi8vd3d3LndwZGZkLmNvbS93cGRob21lLmh0bSIgQUREX0RB +VEU9IjEwNDI2NDY4NzYiIExBU1RfVklTSVQ9IjEwNDgxNzQ2NDUiIExBU1RfTU9ESUZJRUQ9IjEw +NDI2NDY4NzYiPldlYiBQYWdlIERlc2lnbiBmb3IgRGVzaWduZXJzIC0gSG9tZSBQYWdlPC9BPg0K +ICAgICAgICA8RFQ+PEEgSFJFRj0iaHR0cDovL3d3dy53ZWJzaXRlc3RoYXRzdWNrLmNvbS9pbmRl +eC5odG1sIiBBRERfREFURT0iMTA0MjY0Njg3NiIgTEFTVF9WSVNJVD0iMTA0ODE3NDY0NSIgTEFT +VF9NT0RJRklFRD0iMTA0MjY0Njg3NiI+V2ViIFBhZ2VzIFRoYXQgU3VjazwvQT4NCiAgICAgICAg +PERUPjxBIEhSRUY9Imh0dHA6Ly93ZWJzaXRlZ2FyYWdlLm5ldHNjYXBlLmNvbS8iIEFERF9EQVRF +PSIxMDQyNjQ2ODc2IiBMQVNUX1ZJU0lUPSIxMDQ4MTc0NjQ1IiBMQVNUX01PRElGSUVEPSIxMDQy +NjQ2ODc2Ij5XZWIgU2l0ZSBHYXJhZ2U8L0E+DQogICAgICAgIDxEVD48QSBIUkVGPSJodHRwOi8v +d3d3LmthdHN1ZXlkZXNpZ253b3Jrcy5jb20vd21wX2luZGV4Lmh0bSIgQUREX0RBVEU9IjEwNDI2 +NDY4NzYiIExBU1RfVklTSVQ9IjEwNDgxNzQ2NDUiIExBU1RfTU9ESUZJRUQ9IjEwNDI2NDY4NzYi +PldlYiBUaXBlczwvQT4NCiAgICAgICAgPERUPjxBIEhSRUY9Imh0dHA6Ly93d3cud2VibWFzdGVy +YmFzZS5jb20vIiBBRERfREFURT0iMTA0MjY0Njg3NiIgTEFTVF9WSVNJVD0iMTA0ODE3NDY0NSIg +TEFTVF9NT0RJRklFRD0iMTA0MjY0Njg3NiI+V2VibWFzdGVyQmFzZSAtIEhlbHBpbmcgQnVzaW5l +c3MgR3JvdyBPbmxpbmU8L0E+DQogICAgICAgIDxEVD48QSBIUkVGPSJodHRwOi8vaG90d2lyZWQu +bHljb3MuY29tL3dlYm1vbmtleS9yZWZlcmVuY2UvY29sb3JfY29kZXMvIiBBRERfREFURT0iMTA0 +MjY0Njg3NiIgTEFTVF9WSVNJVD0iMTA0ODE3NDY0NSIgTEFTVF9NT0RJRklFRD0iMTA0MjY0Njg3 +NiI+V2VibW9ua2V5ICBSZWZlcmVuY2UgQ29sb3IgQ29kZXM8L0E+DQogICAgICAgIDxEVD48QSBI +UkVGPSJodHRwOi8vaG90d2lyZWQubHljb3MuY29tL3dlYm1vbmtleS9nbG9zc2FyeS8iIEFERF9E +QVRFPSIxMDQyNjQ2ODc2IiBMQVNUX1ZJU0lUPSIxMDQ4MTc0NjQ1IiBMQVNUX01PRElGSUVEPSIx +MDQyNjQ2ODc2Ij5XZWJtb25rZXkgIFJlZmVyZW5jZSBHbG9zc2FyeTwvQT4NCiAgICAgICAgPERU +PjxBIEhSRUY9Imh0dHA6Ly9ob3R3aXJlZC5seWNvcy5jb20vd2VibW9ua2V5L3JlZmVyZW5jZS9o +dG1sX2NoZWF0c2hlZXQvaW5kZXguaHRtbCIgQUREX0RBVEU9IjEwNDI2NDY4NzYiIExBU1RfVklT +SVQ9IjEwNDgxNzQ2NDUiIExBU1RfTU9ESUZJRUQ9IjEwNDI2NDY4NzYiPldlYm1vbmtleSAgUmVm +ZXJlbmNlIEhUTUwgQ2hlYXRzaGVldDwvQT4NCiAgICAgICAgPERUPjxBIEhSRUY9Imh0dHA6Ly9o +b3R3aXJlZC5seWNvcy5jb20vd2VibW9ua2V5L3JlZmVyZW5jZS9zcGVjaWFsX2NoYXJhY3RlcnMv +IiBBRERfREFURT0iMTA0MjY0Njg3NiIgTEFTVF9WSVNJVD0iMTA0ODE3NDY0NSIgTEFTVF9NT0RJ +RklFRD0iMTA0MjY0Njg3NiI+V2VibW9ua2V5ICBSZWZlcmVuY2UgU3BlY2lhbCBDaGFyYWN0ZXJz +PC9BPg0KICAgICAgICA8RFQ+PEEgSFJFRj0iaHR0cDovL2hvdHdpcmVkLmx5Y29zLmNvbS93ZWJt +b25rZXkvIiBBRERfREFURT0iMTA0MjY0Njg3NiIgTEFTVF9WSVNJVD0iMTA0ODE3NDY0NSIgTEFT +VF9NT0RJRklFRD0iMTA0MjY0Njg3NiI+V2VibW9ua2V5PC9BPg0KICAgICAgICA8RFQ+PEEgSFJF +Rj0iaHR0cDovL3d3dy53ZWJyZXZpZXcuY29tLyIgQUREX0RBVEU9IjEwNDI2NDY4NzYiIExBU1Rf +VklTSVQ9IjEwNDgxNzQ2NDUiIExBU1RfTU9ESUZJRUQ9IjEwNDI2NDY4NzYiPldlYlJldmlldy5j +b208L0E+DQogICAgICAgIDxEVD48QSBIUkVGPSJodHRwOi8vd3d3LndlYndvcmQuY29tL2hvdHNp +dGVzLmh0bWwiIEFERF9EQVRFPSIxMDQyNjQ2ODc2IiBMQVNUX1ZJU0lUPSIxMDQ4MTc0NjQ1IiBM +QVNUX01PRElGSUVEPSIxMDQyNjQ2ODc2Ij5XZWJXb3JkLmNvbSBIb3QgV2ViIFNpdGVzPC9BPg0K +ICAgICAgICA8RFQ+PEEgSFJFRj0iaHR0cDovL3d3dy5zaXRlY3JpdGlxdWUubmV0L2luZGV4LnBo +cCIgQUREX0RBVEU9IjEwNDI2NDY4NzYiIExBU1RfVklTSVQ9IjEwNDgxNzQ2NDUiIExBU1RfTU9E +SUZJRUQ9IjEwNDI2NDY4NzYiPldlbGNvbWUgdG8gU2l0ZUNyaXRpcXVlLm5ldCAtIEhlbHBpbmcg +WW91IENyZWF0ZSBCZXR0ZXIgRGVzaWducyBUaHJvdWdoIEZyaWVuZGx5IEFkdmljZSE8L0E+DQog +ICAgICAgIDxEVD48QSBIUkVGPSJodHRwOi8vd3d3Lm5yMXdlYnJlc291cmNlLmNvbS9pbmRleC5o +dG0iIEFERF9EQVRFPSIxMDQyNjQ2ODc2IiBMQVNUX1ZJU0lUPSIxMDQ4MTc0NjQ1IiBMQVNUX01P +RElGSUVEPSIxMDQyNjQ2ODc2Ij5XZWxjb21lIHRvIFdlYiBSZXNvdXJjZXMgLSBXaGVyZSBXZWJU +ZWNobm9sb2d5IG1lZXRzIFNpbXBsaWNpdHk8L0E+DQogICAgICAgIDxEVD48QSBIUkVGPSJodHRw +Oi8vd3d3LnpkbmV0LmNvbS9kZXZoZWFkL2ZpbHRlcnMvMCw5NDI5LDIxMzMyMjQsMDAuaHRtbCIg +QUREX0RBVEU9IjEwNDI2NDY4NzYiIExBU1RfVklTSVQ9IjEwNDgxNzQ2NDUiIExBU1RfTU9ESUZJ +RUQ9IjEwNDI2NDY4NzYiPlpETmV0IGQgZSB2IGUgbCBvIHAgZSByICBVc2FiaWxpdHk8L0E+DQog +ICAgPC9ETD48cD4NCiAgICA8RFQ+PEgzIEZPTERFRCBBRERfREFURT0iMTA0MjY0Njg3NiI+V05Z +TXVzaWM8L0gzPg0KICAgIDxETD48cD4NCiAgICAgICAgPERUPjxBIEhSRUY9Imh0dHA6Ly93d3cu +d255bXVzaWMuY29tLyIgQUREX0RBVEU9IjEwNDI2NDY4NzYiIExBU1RfVklTSVQ9IjEwNDgxNzQ2 +NDUiIExBU1RfTU9ESUZJRUQ9IjEwNDI2NDY4NzYiPkJ1ZmZhbG8gTXVzaWMgT25saW5lIC0gV255 +bXVzaWMuY29tPC9BPg0KICAgICAgICA8RFQ+PEEgSFJFRj0iaHR0cDovL3d3dy53bnltdXNpYy5j +b20vYm9hcmQvY2dpLWJpbi9VbHRpbWF0ZS5jZ2k/YWN0aW9uPWludHJvJkJ5cGFzc0Nvb2tpZT10 +cnVlIiBBRERfREFURT0iMTA0MjY0Njg3NiIgTEFTVF9WSVNJVD0iMTA0ODE3NDY0NSIgTEFTVF9N +T0RJRklFRD0iMTA0MjY0Njg3NiI+QnVmZmFsbyBNdXNpYyBPbmxpbmUgTWVzc2FnZSBCb2FyZDwv +QT4NCiAgICAgICAgPERUPjxBIEhSRUY9Imh0dHA6Ly93d3cud255bXVzaWMuY29tL2JvYXJkL05v +bkNHSS9jcC5odG1sIiBBRERfREFURT0iMTA0MjY0Njg3NiIgTEFTVF9WSVNJVD0iMTA0ODE3NDY0 +NSIgTEFTVF9NT0RJRklFRD0iMTA0MjY0Njg3NiI+VWx0aW1hdGUgQnVsbGV0aW4gQm9hcmQgLSBD +b250cm9sIFBhbmVsIEZyYW1lPC9BPg0KICAgICAgICA8RFQ+PEEgSFJFRj0iaHR0cDovL3d3dy53 +bnltdXNpYy5jb20vbGlzdC5waHAzIiBBRERfREFURT0iMTA0MjY0Njg3NiIgTEFTVF9WSVNJVD0i +MTA0ODE3NDY0NSIgTEFTVF9NT0RJRklFRD0iMTA0MjY0Njg3NiI+V05ZTXVzaWMgLSBUaGUgTGlz +dDwvQT4NCiAgICA8L0RMPjxwPg0KICAgIDxEVD48SDMgRk9MREVEIEFERF9EQVRFPSIxMDQyNjQ2 +ODc2Ij5aaXB6YXBzPC9IMz4NCiAgICA8REw+PHA+DQogICAgICAgIDxEVD48QSBIUkVGPSJodHRw +Oi8vc2VhcmNoLWRlc2MuZWJheS5jb20vc2VhcmNoL3NlYXJjaC5kbGw/R2V0UmVzdWx0JnN0PTIm +U29ydFByb3BlcnR5PU1ldGFFbmRTb3J0JnF1ZXJ5PXppcHphcCZzcmNoZGVzYz15JmZyb209Ujcm +ZWJheXRhZzE9ZWJheXJlZyZodD0xJmNvbWJpbmU9eSIgQUREX0RBVEU9IjEwNDI2NDY4NzYiIExB +U1RfVklTSVQ9IjEwNDgxNzQ2NDUiIExBU1RfTU9ESUZJRUQ9IjEwNDI2NDY4NzYiPkViYXkgLSBJ +dGVtcyBtYXRjaGluZyAoIHppcHphcCApPC9BPg0KICAgICAgICA8RFQ+PEEgSFJFRj0iaHR0cDov +L3d3dy5sYXRlbmN5cHJvamVjdC5jb20vemlwemFwcy96aXB6YXByYWNpbmcuaHRtbCIgQUREX0RB +VEU9IjEwNDI2NDY4NzYiIExBU1RfVklTSVQ9IjEwNDgxNzQ2NDUiIExBU1RfTU9ESUZJRUQ9IjEw +NDI2NDY4NzYiPmxhdGVuY3lwcm9qZWN0LmNvbSAtIHppcCBtb2RzIGFuZCByYWNpbmc8L0E+DQog +ICAgICAgIDxEVD48QSBIUkVGPSJodHRwOi8vd3d3Lm1pY3JvcmNjZW50ZXIuY29tLyIgQUREX0RB +VEU9IjEwNDI2NDY4NzYiIExBU1RfVklTSVQ9IjEwNDgxNzQ2NDUiIExBU1RfTU9ESUZJRUQ9IjEw +NDI2NDY4NzYiPk1pY3JvIFJDIENhcnMgQ2VudGVyPC9BPg0KICAgICAgICA8RFQ+PEEgSFJFRj0i +aHR0cDovL3d3dy5taWNyb3JjY2VudGVyLmNvbS9mb3J1bS9pbmRleC5waHA/cz03YWEwNTYyMTQ2 +NWVhMzQ0NjQ3YmE5MDY4YWExODlkNyZhY3Q9U0YmZj01IiBBRERfREFURT0iMTA0MjY0Njg3NiIg +TEFTVF9WSVNJVD0iMTA0ODE3NDY0NSIgTEFTVF9NT0RJRklFRD0iMTA0MjY0Njg3NiI+TWljcm8g +UkMgQ2FycyBGb3J1bTwvQT4NCiAgICAgICAgPERUPjxBIEhSRUY9Imh0dHA6Ly93d3cucmFkaW9z +aGFjay5jb20vUGFydG5lcnMvWmlwWmFwcy9aaXBaYXBzSG9tZS5hc3AiIEFERF9EQVRFPSIxMDQy +NjQ2ODc2IiBMQVNUX1ZJU0lUPSIxMDQ4MTc0NjQ1IiBMQVNUX01PRElGSUVEPSIxMDQyNjQ2ODc2 +Ij5SYWRpb3NoYWNrLmNvbSAtIFppcFphcHM8L0E+DQogICAgICAgIDxEVD48QSBIUkVGPSJodHRw +Oi8vd3d3LnRpbnlyYy5jb20vZm9ydW1zL2ZvcnVtZGlzcGxheS5waHA/cz0mZm9ydW1pZD03NSIg +QUREX0RBVEU9IjEwNDI2NDY4NzYiIExBU1RfVklTSVQ9IjEwNDgxNzQ2NDUiIExBU1RfTU9ESUZJ +RUQ9IjEwNDI2NDY4NzYiPlRpbnlSQy5jb20gLSBaaXBaYXBzPC9BPg0KICAgICAgICA8RFQ+PEEg +SFJFRj0iaHR0cDovL3d3dy50aW55cmMuY29tLyIgQUREX0RBVEU9IjEwNDI2NDY4NzYiIExBU1Rf +VklTSVQ9IjEwNDgxNzQ2NDUiIExBU1RfTU9ESUZJRUQ9IjEwNDI2NDY4NzYiPlRpbnlSQy5jb208 +L0E+DQogICAgPC9ETD48cD4NCiAgICA8RFQ+PEEgSFJFRj0iaHR0cDovL2RtLmRlc2ttb2QuY29t +LyIgQUREX0RBVEU9IjEwNDI2NDY4NzYiIExBU1RfVklTSVQ9IjEwNDgxNzQ2NDUiIExBU1RfTU9E +SUZJRUQ9IjEwNDI2NDY4NzYiPi5bRGVza01vZCAtIFlvdXIgU291cmNlIGZvciBEZXNrdG9wIE1v +ZGlmaWNhdGlvbl0uPC9BPg0KICAgIDxEVD48QSBIUkVGPSJodHRwOi8vd3d3LmFsdC1waHAtZmFx +Lm9yZy9sb2NhbC83NS9saW5rcy5odG1sIiBBRERfREFURT0iMTA0MjY0Njg3NiIgTEFTVF9WSVNJ +VD0iMTA0ODE3NDY0NSIgTEFTVF9NT0RJRklFRD0iMTA0MjY0Njg3NiI+QUxULVBIUC1GQVEgLSBI +b3cgZG8gSSB0dXJuIG5ld2xpbmVzIC0gcmV0dXJucyBpbnRvIGh0bWwgYnJlYWtzIChicik8L0E+ +DQogICAgPERUPjxBIEhSRUY9Imh0dHA6Ly93d3cuYmFieWNlbnRyZS5jby51ay9yZWZjYXAvNDE4 +OC5odG1sIiBBRERfREFURT0iMTA0MzM0NjA2MCIgTEFTVF9WSVNJVD0iMTA0ODE3NDY0NSIgTEFT +VF9NT0RJRklFRD0iMTA0MzM0NjA2MCI+QmFieUNlbnRyZSAgRGFkcycgc3R1ZmYgRGlldCBmb3Ig +YSBoZWFsdGh5IGZhdGhlci10by1iZTwvQT4NCiAgICA8RFQ+PEEgSFJFRj0iaHR0cDovL3d3dy5i +YWJ5Y2VudHJlLmNvLnVrL3RyeWluZ2ZvcmJhYnkiIEFERF9EQVRFPSIxMDQzMzQ2MTQwIiBMQVNU +X1ZJU0lUPSIxMDQ4MTc0NjQ1IiBMQVNUX01PRElGSUVEPSIxMDQzMzQ2MTQwIj5CYWJ5Q2VudHJl +ICBUcnlpbmcgZm9yIGEgYmFieTwvQT4NCiAgICA8RFQ+PEEgSFJFRj0iaHR0cDovL3N3ei1idXNp +bmVzc3dlZWsuc2FsYXJ5LmNvbS9zYWxhcnl3aXphcmQvbGF5b3V0c2NyaXB0cy9zd3psX2NvbXBy +ZXN1bHQuYXNwP3ppcGNvZGU9MTQyMjMmbmFycm93Y29kZT1JVDAzJmdlbz1CdWZmYWxvJTJDK05Z +KzE0MjIzJmpvYmNvZGU9SVQxMDAwMDE1MyZqb2J0aXRsZT1XZWJtYXN0ZXImaW1hZ2UxLng9NTEm +aW1hZ2UxLnk9MTciIEFERF9EQVRFPSIxMDQyNjQ2ODc2IiBMQVNUX1ZJU0lUPSIxMDQ4MTc0NjQ1 +IiBMQVNUX01PRElGSUVEPSIxMDQyNjQ2ODc2Ij5CdXNpbmVzcyBXZWVrIE9ubGluZSBTYWxhcnkg +V2l6YXJkPC9BPg0KICAgIDxEVD48QSBIUkVGPSJodHRwOi8vd3d3LmNvcHlsZWZ0Lm5ldC9pbmRl +eC5waHRtbD9keW5hbWljPTEmcmVmZXJlcj0lMkZpdGVtLnBodG1sJTNGJTI2cGFnZSUzRHByb2R1 +Y3RfMTA3NV9mcm9udC5waHRtbCIgQUREX0RBVEU9IjEwNDI2NDY4NzYiIExBU1RfVklTSVQ9IjEw +NDgxNzQ2NDUiIExBU1RfTU9ESUZJRUQ9IjEwNDI2NDY4NzYiPkNvcHlsZWZ0Lm5ldCAtIEdlZWsg +Q2hpYyE8L0E+DQogICAgPERUPjxBIEhSRUY9Imh0dHA6Ly93d3cuZG9tYWdnaW9yZS5pdC9pbmRl +eC5odG1sIiBBRERfREFURT0iMTA0MzY5MjQ0MiIgTEFTVF9WSVNJVD0iMTA0ODE3NDY0NSIgTEFT +VF9NT0RJRklFRD0iMTA0MzY5MjQ0MiI+Q292ZXJhbGwgQ0QgQXVkaW8gQ292ZXJzPC9BPg0KICAg +IDxEVD48QSBIUkVGPSJodHRwOi8vd3d3LmNzLnV0YWguZWR1L2RlcHQvb2xkL3RleGluZm8vY3Zz +L2N2c190b2MuaHRtbCIgQUREX0RBVEU9IjEwNDI2NDY4NzYiIExBU1RfVklTSVQ9IjEwNDgxNzQ2 +NDUiIExBU1RfTU9ESUZJRUQ9IjEwNDI2NDY4NzYiPkNWUy0tQ29uY3VycmVudCBWZXJzaW9ucyBT +eXN0ZW0gLSBUYWJsZSBvZiBDb250ZW50czwvQT4NCiAgICA8RFQ+PEEgSFJFRj0iaHR0cDovL3d3 +dy5ic3lzLnRzdWt1YmEuYWMuanAvfmp1bnlhL2R0Ym9vdHMvMDktbG91ZC5odG1sIiBBRERfREFU +RT0iMTA0MjY0Njg3NiIgTEFTVF9WSVNJVD0iMTA0ODE3NDY0NSIgTEFTVF9NT0RJRklFRD0iMTA0 +MjY0Njg3NiI+RHJlYW0gVGhlYXRlciAtIEltcGF4eCA1LjE4LjkzIC0gRHJlYW0gT3V0IExvdWQ8 +L0E+DQogICAgPERUPjxBIEhSRUY9Imh0dHA6Ly93d3cuZXZyc29mdC5jb20vZmFzdHN1Ym1pdC8i +IEFERF9EQVRFPSIxMDQyNjQ2ODc2IiBMQVNUX1ZJU0lUPSIxMDQ4MTc0NjQ1IiBMQVNUX01PRElG +SUVEPSIxMDQyNjQ2ODc2Ij5GYXN0U3VibWl0IC0gU3VibWl0IHlvdXIgd2Vic2l0ZSB0byBzZWFy +Y2ggZW5naW5lcyBmb3IgRlJFRSE8L0E+DQogICAgPERUPjxBIEhSRUY9Imh0dHA6Ly93d3cuc3Rh +bmZvcmQuZWR1L2dyb3VwL3BhbmRlZ3JvdXAvQ29zbS9pbmRleC5odG1sIiBBRERfREFURT0iMTA0 +MjY0Njg3NiIgTEFTVF9WSVNJVD0iMTA0ODE3NDY0NSIgTEFTVF9NT0RJRklFRD0iMTA0MjY0Njg3 +NiI+Rm9sZGluZ0Bob21lPC9BPg0KICAgIDxEVD48QSBIUkVGPSJodHRwOi8vZWNhbGFtZS50cmlw +b2QuY29tL2ZyZWUuaHRtbCIgQUREX0RBVEU9IjEwNDI2NDY4NzYiIExBU1RfVklTSVQ9IjEwNDgx +NzQ2NDUiIExBU1RfTU9ESUZJRUQ9IjEwNDI2NDY4NzYiPkZyZWUgTmV3cyBTZXJ2ZXJzPC9BPg0K +ICAgIDxEVD48QSBIUkVGPSJodHRwOi8vd3d3LmZyZWV3YXJlcGFsbS5jb20vIiBBRERfREFURT0i +MTA0MjY0Njg3NiIgTEFTVF9WSVNJVD0iMTA0ODE3NDY0NSIgTEFTVF9NT0RJRklFRD0iMTA0MjY0 +Njg3NiI+RnJlZXdhcmVQYWxtPC9BPg0KICAgIDxEVD48QSBIUkVGPSJodHRwOi8vd3d3LmJ1ZmZh +bG9uZXQub3JnLyIgQUREX0RBVEU9IjEwNDI2NDY4NzYiIExBU1RfVklTSVQ9IjEwNDgxNzQ2NDUi +IExBU1RfTU9ESUZJRUQ9IjEwNDI2NDY4NzYiPkhpc3Rvcnkgb2YgQnVmZmFsbywgTmV3IFlvcms8 +L0E+DQogICAgPERUPjxBIEhSRUY9Imh0dHA6Ly93d3cudGFjb3NoZWxsLmNvbS9zcGluYWxjcmFj +a2VyL2NnaS1iaW4vc2VhcmNoLnBsP2luZGV4IiBBRERfREFURT0iMTA0MjY0Njg3NiIgTEFTVF9W +SVNJVD0iMTA0ODE3NDY0NSIgTEFTVF9NT0RJRklFRD0iMTA0MjY0Njg3NiI+aW50ZXJuZXQgYmVh +dGxlcyByZWNvcmRpbmcgaW5kZXggbWFpbiBwYWdlPC9BPg0KICAgIDxEVD48QSBIUkVGPSJodHRw +Oi8vZGV2ZWxvcGVyLmlydC5vcmcvc2NyaXB0L2RodG1sLmh0bSIgQUREX0RBVEU9IjEwNDI2NDY4 +NzYiIExBU1RfVklTSVQ9IjEwNDgxNzQ2NDUiIExBU1RfTU9ESUZJRUQ9IjEwNDI2NDY4NzYiPmly +dC5vcmcgLSBEeW5hbWljIEhUTUwgRkFRIEtub3dsZWRnZSBCYXNlPC9BPg0KICAgIDxEVD48QSBI +UkVGPSJodHRwOi8vZGV2ZWxvcGVyLmlydC5vcmcvc2NyaXB0L3RhYmxlLmh0bSIgQUREX0RBVEU9 +IjEwNDI2NDY4NzYiIExBU1RfVklTSVQ9IjEwNDgxNzQ2NDUiIExBU1RfTU9ESUZJRUQ9IjEwNDI2 +NDY4NzYiPmlydC5vcmcgLSBKYXZhU2NyaXB0IFRhYmxlIEZBUSBLbm93bGVkZ2UgQmFzZTwvQT4N +CiAgICA8RFQ+PEEgSFJFRj0iaHR0cDovL3N1cHBvcnQubWljcm9zb2Z0LmNvbS9zdXBwb3J0L2ti +L2FydGljbGVzL1ExMjYvNC80OS5hc3AiIEFERF9EQVRFPSIxMDQyNjQ2ODc2IiBMQVNUX1ZJU0lU +PSIxMDQ4MTc0NjQ1IiBMQVNUX01PRElGSUVEPSIxMDQyNjQ2ODc2Ij5LZXlib2FyZCBTaG9ydGN1 +dHMgZm9yIFdpbmRvd3M8L0E+DQogICAgPERUPjxBIEhSRUY9Imh0dHA6Ly9ob21lcGFnZS5tYWMu +Y29tL2xlcGVyb3VzL1Bob3RvQWxidW0xLmh0bWwiIEFERF9EQVRFPSIxMDQyNjQ2ODc2IiBMQVNU +X1ZJU0lUPSIxMDQ4MTc0NjQ1IiBMQVNUX01PRElGSUVEPSIxMDQyNjQ2ODc2Ij5Lbm93IFlvdXIg +UGxhY2UhIFNodXQgWW91ciBGYWNlITwvQT4NCiAgICA8RFQ+PEEgSFJFRj0iaHR0cDovL3d3dy5n +ZW5lc2lzLXBhdGguY29tL2xhbWJzdHVmZi5odG1sIiBBRERfREFURT0iMTA0MjY0Njg3NiIgTEFT +VF9WSVNJVD0iMTA0ODE3NDY0NSIgTEFTVF9NT0RJRklFRD0iMTA0MjY0Njg3NiI+TGFtYiBzdHVm +ZiE8L0E+DQogICAgPERUPjxBIEhSRUY9Imh0dHA6Ly93d3cuYml6dWJjb21tLmNvbS9MaWJlcnR5 +QmVsbC9NaXNjLi5odG0iIEFERF9EQVRFPSIxMDQyNjQ2ODc2IiBMQVNUX1ZJU0lUPSIxMDQ4MTc0 +NjQ1IiBMQVNUX01PRElGSUVEPSIxMDQyNjQ2ODc2Ij5NaXNjLiBTcGFjZSBMaXN0PC9BPg0KICAg +IDxEVD48QSBIUkVGPSJodHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20vaXNhcGkvcmVkaXIuZGxsP3By +ZD1pZSZwdmVyPTYmYXI9SVN0YXJ0IiBBRERfREFURT0iMTA0MTI2NTc5MiIgTEFTVF9WSVNJVD0i +MTA0ODE3NDY0NSIgTEFTVF9NT0RJRklFRD0iMTA0MTI2NTgxMiI+TVNOLmNvbTwvQT4NCiAgICA8 +RFQ+PEEgSFJFRj0iaHR0cDovL3Zpc2libGVlYXJ0aC5uYXNhLmdvdi8iIEFERF9EQVRFPSIxMDQy +NjQ2ODc2IiBMQVNUX1ZJU0lUPSIxMDQ4MTc0NjQ1IiBMQVNUX01PRElGSUVEPSIxMDQyNjQ2ODc2 +Ij5OQVNBJ3MgVmlzaWJsZSBFYXJ0aDwvQT4NCiAgICA8RFQ+PEEgSFJFRj0iaHR0cDovL3d3dy5k +ZWJyaXMuY29tL2pvdXJuYWwvMTM0IiBBRERfREFURT0iMTA0MjY0Njg3NiIgTEFTVF9WSVNJVD0i +MTA0ODE3NDY0NSIgTEFTVF9NT0RJRklFRD0iMTA0MjY0Njg3NiI+T3B0LU91dDwvQT4NCiAgICA8 +RFQ+PEEgSFJFRj0iaHR0cDovL2RvY3MucG9ydGFsLmRldi8iIEFERF9EQVRFPSIxMDQyNjQ2ODc2 +IiBMQVNUX1ZJU0lUPSIxMDQ4MTc0NjQ1IiBMQVNUX01PRElGSUVEPSIxMDQyNjQ2ODc2Ij5Qb3J0 +YWwgRG9jcyAgVE9DPC9BPg0KICAgIDxEVD48QSBIUkVGPSJodHRwOi8vd3d3Lm1pY3Jvc29mdC5j +b20vaXNhcGkvcmVkaXIuZGxsP3ByZD13aW5kb3dzJnNicD1tZWRpYXBsYXllciZwbGNpZD0mcHZl +cj02LjEmb3M9Jm92ZXI9Jm9sY2lkPSZjbGNpZD0mYXI9TWVkaWEmc2JhPVJhZGlvQmFyJm8xPSZv +Mj0mbzM9IiBBRERfREFURT0iMTA0MTI2NTc5MiIgTEFTVF9WSVNJVD0iMTA0ODE3NDY0NSIgTEFT +VF9NT0RJRklFRD0iMTA0MTI2NTgxMiI+UmFkaW8gU3RhdGlvbiBHdWlkZTwvQT4NCiAgICA8RFQ+ +PEEgSFJFRj0iaHR0cDovL3d3dy5yZXN0b3JhdGlvbmNlbnRyYWwuY29tL2luZGV4Lmh0bWwiIEFE +RF9EQVRFPSIxMDQyNjQ2ODc2IiBMQVNUX1ZJU0lUPSIxMDQ4MTc0NjQ1IiBMQVNUX01PRElGSUVE +PSIxMDQyNjQ2ODc2Ij5SZXN0b3JhdGlvbiBDZW50cmFsIEhvbWUsIG51cnR1cmluZyB0aGUgcGFz +c2lvbiBmb3IgYW50aXF1ZXMgYW5kIG9sZCBob21lcyB3aXRoIGFuIEFydHMgJiBDcmFmdDwvQT4N +CiAgICA8RFQ+PEEgSFJFRj0iaHR0cDovL3d3dy5mYWJ1bG91c2Zvb2RzLmNvbS9ob2xpZGF5cy9o +YWxsb3dlZW4vcHVtcHBsYW5zMi5odG1sIiBBRERfREFURT0iMTA0MjY0Njg3NiIgTEFTVF9WSVNJ +VD0iMTA0ODE3NDY0NSIgTEFTVF9NT0RJRklFRD0iMTA0MjY0Njg3NiI+Um9jayBhbmQgUm9sbCBQ +dW1wa2luIENhcnZpbmcgUGF0dGVybnM8L0E+DQogICAgPERUPjxBIEhSRUY9Imh0dHA6Ly93d3cu +c2ZuMi5jb20vZm9ydW0vaG9tZS5waHAiIEFERF9EQVRFPSIxMDQyNjQ2ODc2IiBMQVNUX1ZJU0lU +PSIxMDQ4MTc0NjQ1IiBMQVNUX01PRElGSUVEPSIxMDQyNjQ2ODc2Ij5TdGVybkZhbk5ldHdvcmsg +SG9tZSBQYWdlPC9BPg0KICAgIDxEVD48QSBIUkVGPSJodHRwOi8vd3d3LnN3aG9pcy5uZXQvIiBB +RERfREFURT0iMTA0MjY0Njg3NiIgTEFTVF9WSVNJVD0iMTA0ODE3NDY0NSIgTEFTVF9NT0RJRklF +RD0iMTA0MjY0Njg3NiI+U1dob2lzLm5ldDwvQT4NCiAgICA8RFQ+PEEgSFJFRj0iaHR0cDovL3Nj +b3V0MTguY3Mud2lzYy5lZHUvbmV0LW5ld3MvOTgtMDYvOTgtMDYtMjIvMDAxMC5odG1sIiBBRERf +REFURT0iMTA0MjY0Njg3NiIgTEFTVF9WSVNJVD0iMTA0ODE3NDY0NSIgTEFTVF9NT0RJRklFRD0i +MTA0MjY0Njg3NiI+VGhlIExhd3llcnMgV2hvIEdldCBJdCBSaWdodCAtIEpheSBHZXJsYW5kPC9B +Pg0KICAgIDxEVD48QSBIUkVGPSJodHRwOi8vd3d3LnRoaW5rZ2Vlay5jb20vIiBBRERfREFURT0i +MTA0MjY0Njg3NiIgTEFTVF9WSVNJVD0iMTA0ODE3NDY0NSIgTEFTVF9NT0RJRklFRD0iMTA0MjY0 +Njg3NiI+VGhpbmtHZWVrICBTdHVmZiBmb3IgU21hcnQgTWFzc2VzPC9BPg0KICAgIDxEVD48QSBI +UkVGPSJodHRwOi8vam9ic2VhcmNoLm5ldHNjYXBlLm1vbnN0ZXIuY29tL2dldGpvYi5hc3A/Sm9i +SUQ9MTMyOTQxNzMmY29sPSZjeT0mYnJkPSZsaWQ9NTQ2JmZuPTYmcT0mQVZTRE09MjAwMSUyRDEx +JTJEMTUrMTYlM0EyNCUzQTAwJTJFMDAwJkNDRD1teSUyRW5ldHNjYXBlJTJFbW9uc3RlciUyRWNv +bSZKU0Q9am9ic2VhcmNoJTJFbmV0c2NhcGUlMkVtb25zdGVyJTJFY29tJkhEPWNvbXBhbnklMkVu +ZXRzY2FwZSUyRW1vbnN0ZXIlMkVjb20mQUQ9aHR0cCUzQSUyIiBBRERfREFURT0iMTA0MjY0Njg3 +NiIgTEFTVF9WSVNJVD0iMTA0ODE3NDY0NSIgTEFTVF9NT0RJRklFRD0iMTA0MjY0Njg3NiI+VVMt +TlktQnVmZmFsby1XZWIgRGV2ZWxvcGVyPC9BPg0KICAgIDxEVD48QSBIUkVGPSJodHRwOi8vd3d3 +LmV2cnNvZnQuY29tL3dlYm1hZy8iIEFERF9EQVRFPSIxMDQyNjQ2ODc2IiBMQVNUX1ZJU0lUPSIx +MDQ4MTc0NjQ1IiBMQVNUX01PRElGSUVEPSIxMDQyNjQ2ODc2Ij5XZWJNYWcgT25saW5lIC0gVGhl +IE5vIzEgUmVzb3VyY2UgZm9yIFdlYiBCdWlsZGVyczwvQT4NCiAgICA8RFQ+PEEgSFJFRj0iaHR0 +cDovL2FtYXppbmdmb3J1bXMuY29tL2ZvcnVtL0lNTk9CT0RZL2ZvcnVtLmh0bWwiIEFERF9EQVRF +PSIxMDQyNjQ2ODc2IiBMQVNUX1ZJU0lUPSIxMDQ4MTc0NjQ1IiBMQVNUX01PRElGSUVEPSIxMDQy +NjQ2ODc2Ij5XZWxjb21lIHRvIHRoZSBIYXBweSBCdWZmYWxvIFNsYW5kZXIgQ29ybmVyISEhPC9B +Pg0KICAgIDxEVD48QSBIUkVGPSJodHRwOi8vd3d3Lnd4cGVyaWVuY2Uud2ViLXVubGltaXRlZC5k +ZS9waHBCQi9pbmRleC5waHAiIEFERF9EQVRFPSIxMDQyNjQ2ODc2IiBMQVNUX1ZJU0lUPSIxMDQ4 +MTc0NjQ1IiBMQVNUX01PRElGSUVEPSIxMDQyNjQ2ODc2Ij5XeHBlcmllbmNlIEZvcnVtcyAtIEZv +cnVtIEluZGV4PC9BPg0KICAgIDxEVD48QSBIUkVGPSJodHRwOi8vd3d3LnJnYWdub24uY29tL21h +c3RlcnMvanMtbGFuZ3VhZ2UuaHRtbCIgQUREX0RBVEU9IjEwNDI2NDY4NzYiIExBU1RfVklTSVQ9 +IjEwNDgxNzQ2NDUiIExBU1RfTU9ESUZJRUQ9IjEwNDI2NDY4NzYiPltSZWFsJ3MgSkFWQSBKQVZB +U0NSSVBUIFBCIGFuZCBXU0ggSG93LXRvXTwvQT4NCjwvREw+PHA+DQpWmgIQgAEADQAAAGJvb2tt +YXJrLmh0bQDNBAIFkAYAmAEAABIAAAADACAOjk8BAB4AATABAAAADQAAAGJvb2ttYXJrLmh0bQAA +AAACAQI3AQAAAAAAAAAeAAM3AQAAAAUAAAAuaHRtAAAAAAMABTcBAAAAHgAHNwEAAAANAAAAYm9v +a21hcmsuaHRtAAAAAAMACzdzAAAAAwAUNwAAAAADAPp/AAAAAEAA+38AQN2jV0WzDEAA/H8AQN2j +V0WzDAMA/X8AAAAACwD+fwAAAAADACEORckDAAIB+A8BAAAAEAAAAM/yM912IydOt4pJEsJHoOQC +AfoPAQAAABAAAADP8jPddiMnTreKSRLCR6DkAgH7DwEAAACWAAAAAAAAADihuxAF5RAaobsIACsq +VsIAAG1zcHN0LmRsbAAAAAAATklUQfm/uAEAqgA32W4AAABDOlxEb2N1bWVudHMgYW5kIFNldHRp +bmdzXGpnZXJsYW5kXExvY2FsIFNldHRpbmdzXEFwcGxpY2F0aW9uIERhdGFcTWljcm9zb2Z0XE91 +dGxvb2tcT3V0bG9vay5wc3QAAAADAP4PBwAAAHpn + +--=-2UbcDuksJBAwUNBNW7UD-- + diff --git a/examples/eml/tnef2.eml b/examples/eml/tnef2.eml new file mode 100644 index 000000000..ecf71d444 --- /dev/null +++ b/examples/eml/tnef2.eml @@ -0,0 +1,101 @@ +From: Alice +To: Bob +Subject: TNEF test 2 +Date: Thu, 10 Feb 2022 12:19:57 +0100 +Message-ID: <001d01d81e70$23211250$696336f0$@example.net> +MIME-Version: 1.0 +Content-Type: multipart/mixed; + boundary="----=_NextPart_000_001E_01D81E78.84E7EB50" +X-Mailer: Microsoft Outlook 16.0 +Content-Language: cs +X-MS-TNEF-Correlator: 0000000045EA84AB25A1494B8CD618FC27E0940E04CA7500 +Authentication-Results: example.net; + dkim=pass header.d=example.net header.s=mail header.b=UH8A3Z8P; + dmarc=pass (policy=reject) header.from=example.net; + spf=pass () smtp.mailfrom=example@example.net + +This is a multipart message in MIME format. + +------=_NextPart_000_001E_01D81E78.84E7EB50 +Content-Type: text/plain; + charset="iso-8859-2" +Content-Transfer-Encoding: quoted-printable + +Dobr=FD den, + +=20 + +pos=EDl=E1m zku=B9ebn=ED e-mail: + +=20 + +P=F8=EDli=B9 =BElu=BBou=E8k=FD k=F9=F2 =FAp=ECl =EF=E1belsk=E9 =F3dy + +=20 + +S pozdravem + +=20 + +Mgr. Milo=B9 Bart=E1k + + +------=_NextPart_000_001E_01D81E78.84E7EB50 +Content-Type: application/ms-tnef; + name="winmail.dat" +Content-Transfer-Encoding: base64 +Content-Disposition: attachment; + filename="winmail.dat" + +eJ8+IjoLAQaQCAAEAAAAAAABAAEAAQeQBgAIAAAA4gQAAAAAAADmAAEIgAcAGAAAAElQTS5NaWNy +b3NvZnQgTWFpbC5Ob3RlADEIAQOQBgCMCgAAJwAAAAsAAgABAAAAAwAmAAAAAAALACkAAAAAAB4A +cAABAAAAHAAAAFprb3Waa2EgZS1tYWlsdSBiZXogcPjtbG9oeQACAXEAAQAAABYAAAAB2B5wHowu +HckFefFLTYHQP0bV2A3UAAALAAEOAAAAAAIBCg4BAAAAGAAAAAAAAABF6oSrJaFJS4zWGPwn4JQO +woAAAAMAFA4BAAAAHgAoDgEAAAAvAAAAMDAwMDAwMDMBYmFydGFrQGVzcC1tYWlsLmN6AWJhcnRh +a0Blc3AtbWFpbC5jegAAHgApDgEAAAAvAAAAMDAwMDAwMDMBYmFydGFrQGVzcC1tYWlsLmN6AWJh +cnRha0Blc3AtbWFpbC5jegAAAwDeP7BvAAADAPE/BQQAAAMAAlkAABYAAwAJWQIAAAALABCACCAG +AAAAAADAAAAAAAAARgAAAAADhQAAAAAAAAMAEoAIIAYAAAAAAMAAAAAAAABGAAAAABCFAAAAAAAA +AwBzgAggBgAAAAAAwAAAAAAAAEYAAAAAAYUAAAAAAAALAKmACCAGAAAAAADAAAAAAAAARgAAAAAG +hQAAAAAAAAsArYAIIAYAAAAAAMAAAAAAAABGAAAAAA6FAAAAAAAAAwCwgAggBgAAAAAAwAAAAAAA +AEYAAAAAGIUAAAAAAAALAMaACCAGAAAAAADAAAAAAAAARgAAAACChQAAAAAAAAMAAIEIIAYAAAAA +AMAAAAAAAABGAAAAAOuFAAAFBAAAAgELggggBgAAAAAAwAAAAAAAAEYBAAAANgAAAEkAbgBUAHIA +YQBuAHMAaQB0AE0AZQBzAHMAYQBnAGUAQwBvAHIAcgBlAGwAYQB0AG8AcgAAAAAAAQAAABAAAAAW +G8UG6IOCQIQ5QUZYFGJYHgAxggggBgAAAAAAwAAAAAAAAEYBAAAAGAAAAEMAbwBtAHAAbwBzAGUA +VAB5AHAAZQAAAAEAAAAIAAAAbmV3TWFpbAALAB8OAQAAAAIB+A8BAAAAEAAAAEXqhKsloUlLjNYY +/CfglA4CAfoPAQAAABAAAABF6oSrJaFJS4zWGPwn4JQOAwD+DwUAAAACAQkQAQAAAFYGAABSBgAA +YREAAExaRnVvWyMSAwAKAHJjcGcxMjWDAtEDYWh0bWwxAzH4YmlkBAADMAEDAfcKgCcCpAPjAgBj +aArAc2XgdDIzOCAHEwKAEIPfAFAEVghVB7ISdTIC4xFYtwYABsMSdTMERhFXMBN8VxKDCO8J9zsZ +YjUJtDliOQojMTkzEnIMYGNnAFALCQFkMzYXoAulNHIgEIIqXA6yAZAOEDlkIDwOsiB4DtAAgDpI +dj0iCHBuOgTwaGJlAMBzLW0N4ANgc2pvAYAtBaBtIAAO0CJdH6VvIC8hOSFgZg3gZXskVSIWdyKv +I78loAWwZA0iFm0lwA6wdHA6L6ovJiUuIQcuIaEvJGRiLwHQMDQvDiAqoG0LIfcox3cs4C53My4B +BbBnL1RSL1JFhEMtDrI0MCI+EoONHpczHjAfUGVhZC59NDE2DvA8B4ABkCBuMmEHgD1HCfAEkGF0 +rwWxBaACMAnwdCXATSEWBCBXKAEgMTUgKG8kgB7gBJAJgCAHgA9QdRxtKS5uHiAw8XN0eYxsZTU/ +MPEhLS0Ko5AvKiBGAiEgRAEQMQuAaXRpAiAEICovpQqjQAISLWYA0GUKpBsBkQMwezpFIQBseTp0 +IkMxgGIHIQXQMhBoLiIagDrnCrBuIUBlLdwxOhWQHjAz8DM+oR4w7jY+wT5xGoB9Oe86/zwD+Txg +bGk8kT0/PkMz4TPw3xWQRGIeMD9bOIFTNoI4/9EKwXAuTSFQTgWwAMC4bCwgQmBHig9QdkeID0DK +AMAtYAuAOjBjbRdCuUAzAJB6JLAxMS4eMAUwS1476EJjIixz7wBxTEAGckK5bSFQQHEZcMcmcCGA +C2BuZ3UfACSwoEVOLVVTP6ZzPeE6LkXiRSDwC3AZAHZawHBydnkxN0n7UHH1NnMtNoBwJLBVgBGg +AiD7B0AhknA+EU0PTh9PL0ExRxjjJaALgGRvdzKQeEdM8T/ER4JDaHBGQWF+dR7gVA9VGFrgVnAA +IC3/AiA8IFa/V89Y31A/UU8/8u8KsFGAM4MGYGM5YhxTQQaLTFMw4DJMwiA3OWajg2GKSsQ3MC44 +NWbSf2ivaTM/pkkiZL9BFWRiOv9rSj+mOBA2zx8xKUA2jzeYMlsGkCBnMpBiIiA5KF0+PB+xPgqj +PG+/JhARgFWAAQFcMgQgdl4RezLBNMF0IhBSgA9AAMB4oSXAMTAyNiIQL3K1Ni9ygjfwWwnwD1Bm +Xd9uX3EPch9zJQtgeQhgBUBXdCpyt3UScHQcZDIQYX91YXXJent5sHZvLow2Mi+fL68fAA5AH0AG +4GR5SDCZYwE9QwXwQmBuayXAQCMwNTYzQ34xduGEZTk1NEYBwHTRb9LkPScn8i13MgApEDyQ7YIw +a4cAKAEnLnAeowAhfwMwYvJ1gR8wiDYXoB5bOb0/MDxJITJQC2AEED1rSu+IGQAAiX8e0zYvcXzg +i1QnR5eMf42DRG88kFwndwVwSRAJ8Cx3jS9xcyBw/XduNRWQftKTYYg2CqKIJ/8KcohHCrGWeInt +AcBvkZNuX46vj7+Xn5KvHnk4HjAmnG5iUoACgIg4J2EBQP+cb5QflS+WP5dPmF+Zb5p/f5uPpXRW +cZGQCYAZUKrgMRBtIHprDHAnOWFsZWILkKrhID4wUzI6/5z/ng+h/6MPpB+lL6Y/p0//qF+pb6Ef +rc+e75//t3+vL3+wP7FPu3+zb7R/tY+2nyB6UJGROKrTAKCrwbsROY960KuiWqCroWU4a5GT/8di +G1CRoBWQkZFzYKrRi1BXuxEBEKsiYnrQc8dhZfuJQZGgM4Owt7+4z7y/vc//vt+/78D/wg/DH8Qv +u9/Lf/+5r7q/1S/M383vzv/ZL9Efn9Iv0z/UTwYAqpF6ZDIA/nYmUNVv1n/ab9t/3I/dn//er9+/ +4M/h39mP5K/XX9hv/+5f5g/nH+gv8l/qT+tf7G//7X8eTPDRg4D66ztR+78e1L40EgA2YD3hhkZM +CTRMw41aFCOJMC9gN0Q7Yj+vY0KEMIgK+5lNCcAuPNC7U1HGE0IKwFxgqzFrHlz/hOBvkj3hiBn1 +KIn989L9WvcJP/9fAG83AZ8CrwO/+2//7s/lD/PIBx8ILwvJ9G/1f//2j/efL+4rQG+RSSEXfxuf +34NADWEK8IOhd243gfIuEQUucH0jAAAAAwAWEAAAAAADAA00/T+lDgMADzT9P6UOAgEUNAEAAAAQ +AAAATklUQfm/uAEAqgA32W4AAAIBfwABAAAAMQAAADAwMDAwMDAwNDVFQTg0QUIyNUExNDk0QjhD +RDYxOEZDMjdFMDk0MEUwNENBNzUwMAAAAAADAAYQuPK1DAMABxBYAAAAAwAQEAAAAAADABEQAAAA +AB4ACBABAAAAWQAAAERPQlL9REVOLFBPU+1M4U1aS1W5RUJO7UUtTUFJTDpQ+O1MSbm+TFW7T1Xo +S/1L+fL6UOxM7+FCRUxTS+nzRFlTUE9aRFJBVkVNTUdSTUlMT7lCQVJU4UsAAAAAvTM= + +------=_NextPart_000_001E_01D81E78.84E7EB50-- + diff --git a/integrations/cloudron/DESCRIPTION.md b/integrations/cloudron/DESCRIPTION.md index 668ac8df4..54ddaca71 100644 --- a/integrations/cloudron/DESCRIPTION.md +++ b/integrations/cloudron/DESCRIPTION.md @@ -1,4 +1,4 @@ -This app packages SnappyMail 2.35.2. +This app packages SnappyMail 2.35.4. SnappyMail is a simple, modern, lightweight & fast web-based email client. diff --git a/integrations/cloudron/Dockerfile b/integrations/cloudron/Dockerfile index 29f540199..15b49d1cb 100644 --- a/integrations/cloudron/Dockerfile +++ b/integrations/cloudron/Dockerfile @@ -4,7 +4,7 @@ RUN mkdir -p /app/code WORKDIR /app/code # If you change the extraction below, be sure to test on scaleway -VERSION=2.35.2 +VERSION=2.35.4 RUN wget https://github.com/the-djmaze/snappymail/releases/download/v${VERSION}/snappymail-${VERSION}.zip -O /tmp/snappymail.zip && \ unzip /tmp/snappymail.zip -d /app/code && \ rm /tmp/snappymail.zip && \ diff --git a/integrations/nextcloud/snappymail/appinfo/info.xml b/integrations/nextcloud/snappymail/appinfo/info.xml index c6e1b1a84..560564654 100644 --- a/integrations/nextcloud/snappymail/appinfo/info.xml +++ b/integrations/nextcloud/snappymail/appinfo/info.xml @@ -3,10 +3,10 @@ snappymail SnappyMail SnappyMail Webmail - 2.35.2 + 2.35.4 agpl SnappyMail, RainLoop Team, Nextgen-Networks, Tab Fitts, Nathan Kinkade, Pierre-Alain Bandinelli - xxtea zip --> - + OCA\SnappyMail\Settings\AdminSettings diff --git a/integrations/nextcloud/snappymail/lib/AppInfo/Application.php b/integrations/nextcloud/snappymail/lib/AppInfo/Application.php index 0b040139c..df40033cf 100644 --- a/integrations/nextcloud/snappymail/lib/AppInfo/Application.php +++ b/integrations/nextcloud/snappymail/lib/AppInfo/Application.php @@ -75,20 +75,24 @@ class Application extends App implements IBootstrap $dispatcher = $context->getAppContainer()->query('OCP\EventDispatcher\IEventDispatcher'); $dispatcher->addListener(PostLoginEvent::class, function (PostLoginEvent $Event) { +/* $config = \OC::$server->getConfig(); // Only store the user's password in the current session if they have // enabled auto-login using Nextcloud username or email address. if ($config->getAppValue('snappymail', 'snappymail-autologin', false) || $config->getAppValue('snappymail', 'snappymail-autologin-with-email', false)) { +*/ $sUID = $Event->getUser()->getUID(); \OC::$server->getSession()['snappymail-nc-uid'] = $sUID; - \OC::$server->getSession()['snappymail-password'] = SnappyMailHelper::encodePassword($Event->getPassword(), $sUID); + \OC::$server->getSession()['snappymail-passphrase'] = SnappyMailHelper::encodePassword($Event->getPassword(), $sUID); +/* } +*/ }); $dispatcher->addListener(BeforeUserLoggedOutEvent::class, function (BeforeUserLoggedOutEvent $Event) { // https://github.com/nextcloud/server/issues/36083#issuecomment-1387370634 -// \OC::$server->getSession()['snappymail-password'] = ''; +// \OC::$server->getSession()['snappymail-passphrase'] = ''; SnappyMailHelper::loadApp(); // \RainLoop\Api::Actions()->Logout(true); \RainLoop\Api::Actions()->DoLogout(); @@ -99,12 +103,12 @@ class Application extends App implements IBootstrap $class = 'OCA\Impersonate\Events\BeginImpersonateEvent'; if (\class_exists($class)) { $dispatcher->addListener($class, function ($Event) { - \OC::$server->getSession()['snappymail-password'] = ''; + \OC::$server->getSession()['snappymail-passphrase'] = ''; SnappyMailHelper::loadApp(); \RainLoop\Api::Actions()->Logout(true); }); $dispatcher->addListener('OCA\Impersonate\Events\EndImpersonateEvent', function ($Event) { - \OC::$server->getSession()['snappymail-password'] = ''; + \OC::$server->getSession()['snappymail-passphrase'] = ''; SnappyMailHelper::loadApp(); \RainLoop\Api::Actions()->Logout(true); }); diff --git a/integrations/nextcloud/snappymail/lib/Controller/FetchController.php b/integrations/nextcloud/snappymail/lib/Controller/FetchController.php index 06dff2f10..0baa2a4df 100644 --- a/integrations/nextcloud/snappymail/lib/Controller/FetchController.php +++ b/integrations/nextcloud/snappymail/lib/Controller/FetchController.php @@ -103,7 +103,7 @@ class FetchController extends Controller { $sPass = $_POST['snappymail-password']; if ('******' !== $sPass) { - $this->config->setUserValue($sUser, 'snappymail', 'snappymail-password', + $this->config->setUserValue($sUser, 'snappymail', 'passphrase', $sPass ? SnappyMailHelper::encodePassword($sPass, \md5($sEmail)) : ''); } } else { diff --git a/integrations/nextcloud/snappymail/lib/Migration/InstallStep.php b/integrations/nextcloud/snappymail/lib/Migration/InstallStep.php index dfb1f1e8d..cca015aa9 100644 --- a/integrations/nextcloud/snappymail/lib/Migration/InstallStep.php +++ b/integrations/nextcloud/snappymail/lib/Migration/InstallStep.php @@ -85,11 +85,15 @@ class InstallStep implements IRepairStep // $oDomain = \RainLoop\Model\Domain::fromIniArray('nextcloud', []); $oDomain = new \RainLoop\Model\Domain('nextcloud'); $iSecurityType = \MailSo\Net\Enumerations\ConnectionSecurityType::NONE; - $oDomain->SetConfig( - 'localhost', 143, $iSecurityType, true, - true, 'localhost', 4190, $iSecurityType, - 'localhost', 25, $iSecurityType, true, true, false, false, - ''); + $oDomain->ImapSettings()->host = 'localhost'; + $oDomain->ImapSettings()->type = $iSecurityType; + $oDomain->ImapSettings()->shortLogin = true; + $oDomain->SieveSettings()->enabled = true; + $oDomain->SieveSettings()->host = 'localhost'; + $oDomain->SieveSettings()->type = $iSecurityType; + $oDomain->SmtpSettings()->host = 'localhost'; + $oDomain->SmtpSettings()->type = $iSecurityType; + $oDomain->SmtpSettings()->shortLogin = true; $oProvider->Save($oDomain); if (!$oConfig->Get('login', 'default_domain', '')) { $oConfig->Set('login', 'default_domain', 'nextcloud'); diff --git a/integrations/nextcloud/snappymail/lib/Settings/PersonalSettings.php b/integrations/nextcloud/snappymail/lib/Settings/PersonalSettings.php index 22211e659..dcfcd1453 100644 --- a/integrations/nextcloud/snappymail/lib/Settings/PersonalSettings.php +++ b/integrations/nextcloud/snappymail/lib/Settings/PersonalSettings.php @@ -24,15 +24,19 @@ class PersonalSettings implements ISettings $sEmail = $aRainLoop[0]; $this->config->setUserValue($uid, 'snappymail', 'snappymail-email', $sEmail); if ($aRainLoop[1]) { - $this->config->setUserValue($uid, 'snappymail', 'snappymail-password', + $this->config->setUserValue($uid, 'snappymail', 'passphrase', \OCA\SnappyMail\Util\SnappyMailHelper::encodePassword($aRainLoop[1], \md5($sEmail)) ); } } } + if ($sPass = $this->config->getUserValue($uid, 'snappymail', 'snappymail-password')) { + $this->config->deleteUserValue($uid, 'snappymail', 'snappymail-password'); + $this->config->setUserValue($uid, 'snappymail', 'passphrase', $sPass); + } $parameters = [ 'snappymail-email' => $sEmail, - 'snappymail-password' => $this->config->getUserValue($uid, 'snappymail', 'snappymail-password') ? '******' : '' + 'snappymail-password' => $this->config->getUserValue($uid, 'snappymail', 'passphrase') ? '******' : '' ]; \OCP\Util::addScript('snappymail', 'snappymail'); return new TemplateResponse('snappymail', 'personal_settings', $parameters, ''); diff --git a/integrations/nextcloud/snappymail/lib/Util/SnappyMailHelper.php b/integrations/nextcloud/snappymail/lib/Util/SnappyMailHelper.php index 721cdf86e..7d6016747 100644 --- a/integrations/nextcloud/snappymail/lib/Util/SnappyMailHelper.php +++ b/integrations/nextcloud/snappymail/lib/Util/SnappyMailHelper.php @@ -4,15 +4,15 @@ namespace OCA\SnappyMail\Util; class SnappyMailResponse extends \OCP\AppFramework\Http\Response { - public function render(): string - { + public function render(): string + { $data = ''; $i = \ob_get_level(); while ($i--) { $data .= \ob_get_clean(); } return $data; - } + } } class SnappyMailHelper @@ -98,15 +98,16 @@ class SnappyMailHelper $oActions->Plugins()->RunHook('login.success', array($oAccount)); $oActions->SetAuthToken($oAccount); - if ($oConfig->Get('login', 'sign_me_auto', \RainLoop\Enumerations\SignMeType::DEFAULT_OFF) === \RainLoop\Enumerations\SignMeType::DEFAULT_ON) { + if ($oConfig->Get('login', 'sign_me_auto', \RainLoop\Enumerations\SignMeType::DefaultOff) === \RainLoop\Enumerations\SignMeType::DefaultOn) { $oActions->SetSignMeToken($oAccount); } } } catch (\Throwable $e) { // Login failure, reset password to prevent more attempts $sUID = \OC::$server->getUserSession()->getUser()->getUID(); - \OC::$server->getSession()['snappymail-password'] = ''; - \OC::$server->getConfig()->setUserValue($sUID, 'snappymail', 'snappymail-password', ''); + \OC::$server->getSession()['snappymail-passphrase'] = ''; + \OC::$server->getConfig()->setUserValue($sUID, 'snappymail', 'passphrase', ''); + \SnappyMail\Log::error('Nextcloud', $e->getMessage()); } } } @@ -132,11 +133,14 @@ class SnappyMailHelper // If the user has set credentials for SnappyMail in their personal settings, // this has the first priority. $sEmail = $config->getUserValue($sUID, 'snappymail', 'snappymail-email'); - $sPassword = $config->getUserValue($sUID, 'snappymail', 'snappymail-password'); + $sPassword = $config->getUserValue($sUID, 'snappymail', 'passphrase') + ?: $config->getUserValue($sUID, 'snappymail', 'snappymail-password'); if ($sEmail && $sPassword) { $sPassword = static::decodePassword($sPassword, \md5($sEmail)); if ($sPassword) { return [$sUID, $sEmail, $sPassword]; + } else { + \SnappyMail\Log::debug('Nextcloud', 'decodePassword failed for getUserValue'); } } @@ -165,16 +169,18 @@ class SnappyMailHelper $sPassword = ''; if ($config->getAppValue('snappymail', 'snappymail-autologin', false)) { $sEmail = $sUID; - $sPassword = $ocSession['snappymail-password']; + $sPassword = $ocSession['snappymail-passphrase']; } else if ($config->getAppValue('snappymail', 'snappymail-autologin-with-email', false)) { $sEmail = $config->getUserValue($sUID, 'settings', 'email'); - $sPassword = $ocSession['snappymail-password']; + $sPassword = $ocSession['snappymail-passphrase']; } else { \SnappyMail\Log::debug('Nextcloud', 'snappymail-autologin is off'); } if ($sPassword) { return [$sUID, $sEmail, static::decodePassword($sPassword, $sUID)]; } + } else { + \SnappyMail\Log::debug('Nextcloud', "snappymail-nc-uid mismatch '{$ocSession['snappymail-nc-uid']}' != '{$sUID}'"); } return [$sUID, '', '']; @@ -205,6 +211,6 @@ class SnappyMailHelper { static::loadApp(); $result = \SnappyMail\Crypt::DecryptUrlSafe($sPassword, $sSalt); - return $result ? new \SnappyMail\SensitiveString($result) : $result; + return $result ? new \SnappyMail\SensitiveString($result) : null; } } diff --git a/integrations/nextcloud/snappymail/templates/admin-local.php b/integrations/nextcloud/snappymail/templates/admin-local.php index e4cfeac4b..91cfd5531 100644 --- a/integrations/nextcloud/snappymail/templates/admin-local.php +++ b/integrations/nextcloud/snappymail/templates/admin-local.php @@ -40,9 +40,9 @@
/g, '') + .replace(/]*>\s*<\/span>/gi, '') + .trim(); + tpl.querySelectorAll('a:empty,span:empty').forEach(el => el.remove()); + return tpl.content; + } + }, + + pasteSanitizer = (event) => { + const frag = event.detail.fragment; + frag.querySelectorAll('a:empty,span:empty').forEach(el => el.remove()); + frag.querySelectorAll(removeElements).forEach(el => el.remove()); + frag.querySelectorAll('*').forEach(el => { + if (!el.matches(allowedElements)) { + el.replaceWith(getFragmentOfChildren(el)); + } else if (el.hasAttributes()) { + [...el.attributes].forEach(attr => { + let name = attr.name.toLowerCase(); + if (!allowedAttributes.includes(name)) { + el.removeAttribute(name); + } + }); + } + }); + }, + + pasteImageHandler = (e, squire) => { + + const items = [...e.detail.clipboardData.items]; + const imageItems = items.filter((item) => /image/.test(item.type)); + if (!imageItems.length) { + return false; + } + let reader = new FileReader(); + reader.onload = (loadEvent) => { + squire.insertImage(loadEvent.target.result); + }; + reader.readAsDataURL(imageItems[0].getAsFile()); + }; + + + class CompactComposer { + constructor(container) { + const + plain = createElement('textarea'), + wysiwyg = createElement('div'), + toolbar = createElement('div'), + squire = new win.Squire2(wysiwyg, SquireDefaultConfig); + + this.container = container; + + plain.className = 'squire-plain'; + wysiwyg.className = 'squire-wysiwyg'; + wysiwyg.dir = 'auto'; + this.mode = ''; // 'plain' | 'wysiwyg' + this.squire = squire; + this.plain = plain; + this.wysiwyg = wysiwyg; + this.toolbar = toolbar; + + toolbar.className = 'squire-toolbar btn-toolbar'; + const actions = this.#makeActions(squire, toolbar); + + this.squire.addEventListener('willPaste', pasteSanitizer); + this.squire.addEventListener('pasteImage', (e) => { + pasteImageHandler(e, squire); + }); + +// squire.addEventListener('focus', () => shortcuts.off()); +// squire.addEventListener('blur', () => shortcuts.on()); + + container.append(toolbar, wysiwyg, plain); + + const fontFamilySelect = getByProp(actions, 'fontFamily').element; + + const fontSizeAction = getByProp(actions, 'fontSize'); + + /** + * @param {string} fontName + * @return {string} + */ + const normalizeFontName = (fontName) => fontName.trim().replace(/(^["']*|["']*$)/g, '').trim().toLowerCase(); + + /** @type {string[]} - lower cased array of available font families*/ + const fontFamiliesLowerCase = Object.values(fontFamilySelect.options).map(option => option.value.toLowerCase()); + + /** + * A theme might have CSS like div.squire-wysiwyg[contenteditable="true"] { + * font-family: 'Times New Roman', Times, serif; } + * so let's find the best match squire.getRoot()'s font + * it will also help to properly handle generic font names like 'sans-serif' + * @type {number} + */ + let defaultFontFamilyIndex = 0; + const squireRootFonts = getComputedStyle(squire.getRoot()).fontFamily.split(',').map(normalizeFontName); + fontFamiliesLowerCase.some((family, index) => { + const matchFound = family.split(',').some(availableFontName => { + const normalizedFontName = normalizeFontName(availableFontName); + return squireRootFonts.some(squireFontName => squireFontName === normalizedFontName); + }); + if (matchFound) { + defaultFontFamilyIndex = index; + } + return matchFound; + }); + + /** + * Instead of comparing whole 'font-family' strings, + * we are going to look for individual font names, because we might be + * editing a Draft started in another email client for example + * + * @type {Object.} + */ + const fontNamesMap = {}; + /** + * @param {string} fontFamily + * @param {number} index + */ + const processFontFamilyString = (fontFamily, index) => { + fontFamily.split(',').forEach(fontName => { + const key = normalizeFontName(fontName); + if (fontNamesMap[key] === undefined) { + fontNamesMap[key] = index; + } + }); + }; + // first deal with the default font family + processFontFamilyString(fontFamiliesLowerCase[defaultFontFamilyIndex], defaultFontFamilyIndex); + // and now with the rest of the font families + fontFamiliesLowerCase.forEach((fontFamily, index) => { + if (index !== defaultFontFamilyIndex) { + processFontFamilyString(fontFamily, index); + } + }); + + // ----- + + let ignoreNextSelectEvent = false; + + squire.addEventListener('pathChange', e => { + + const tokensMap = this.buildTokensMap(e.detail); + + if (tokensMap.has('__selection__')) { + ignoreNextSelectEvent = false; + return; + } + this.indicators.forEach((indicator) => { + indicator.element.classList.toggle('active', indicator.selectors.some(selector => tokensMap.has(selector))); + }); + + let familySelectedIndex = defaultFontFamilyIndex; + const fontFamily = tokensMap.get('__font_family__'); + if (fontFamily) { + familySelectedIndex = -1; // show empty select if we don't know the font + const fontNames = fontFamily.split(','); + for (let i = 0; i < fontNames.length; i++) { + const index = fontNamesMap[normalizeFontName(fontNames[i])]; + if (index !== undefined) { + familySelectedIndex = index; + break; + } + } + } + fontFamilySelect.selectedIndex = familySelectedIndex; + + let sizeSelectedIndex = fontSizeAction.defaultValueIndex; + const fontSize = tokensMap.get('__font_size__'); + if (fontSize) { + // -1 is ok because it will just show a blank + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ +
+ +
+
+ + +
+ + +
+
+
+ + + +
+ + +
+
+
    +
  • +
    + +
    +
    +
    +
    + × +
    + +
    +
  • +
+
diff --git a/plugins/ldap-contacts-suggestions/LdapContactsSuggestions.php b/plugins/ldap-contacts-suggestions/LdapContactsSuggestions.php index 305f3d823..24ccf750f 100644 --- a/plugins/ldap-contacts-suggestions/LdapContactsSuggestions.php +++ b/plugins/ldap-contacts-suggestions/LdapContactsSuggestions.php @@ -1,4 +1,4 @@ -Email()); + $sDomain = \MailSo\Base\Utils::getEmailAddressDomain($oAccount->Email()); $sBaseDn = \strtr($this->sBaseDn, array( '{domain}' => $sDomain, '{domain:dc}' => 'dc='.\strtr($sDomain, array('.' => ',dc=')), '{email}' => $oAccount->Email(), - '{email:user}' => \MailSo\Base\Utils::GetAccountNameFromEmail($oAccount->Email()), + '{email:user}' => \MailSo\Base\Utils::getEmailAddressLocalPart($oAccount->Email()), '{email:domain}' => $sDomain, '{login}' => $oAccount->IncLogin(), '{imap:login}' => $oAccount->IncLogin(), - '{imap:host}' => $oAccount->Domain()->IncHost(), - '{imap:port}' => $oAccount->Domain()->IncPort() + '{imap:host}' => $oAccount->Domain()->ImapSettings()->host, + '{imap:port}' => $oAccount->Domain()->ImapSettings()->port )); $aObjectClasses = empty($this->sObjectClasses) ? array() : \explode(',', $this->sObjectClasses); diff --git a/plugins/ldap-contacts-suggestions/index.php b/plugins/ldap-contacts-suggestions/index.php index c3c1242d4..dbbf2d515 100644 --- a/plugins/ldap-contacts-suggestions/index.php +++ b/plugins/ldap-contacts-suggestions/index.php @@ -4,9 +4,9 @@ class LdapContactsSuggestionsPlugin extends \RainLoop\Plugins\AbstractPlugin { const NAME = 'Contacts suggestions (LDAP)', - VERSION = '2.13', - RELEASE = '2023-10-01', - REQUIRED = '2.23.0', + VERSION = '2.14', + RELEASE = '2024-03-12', + REQUIRED = '2.35.3', CATEGORY = 'Contacts', DESCRIPTION = 'Get contacts suggestions from LDAP.'; diff --git a/plugins/ldap-login-mapping/index.php b/plugins/ldap-login-mapping/index.php index 53c8f5622..c15347cab 100644 --- a/plugins/ldap-login-mapping/index.php +++ b/plugins/ldap-login-mapping/index.php @@ -8,10 +8,10 @@ class LDAPLoginMappingPlugin extends AbstractPlugin { const NAME = 'LDAP login mapping', - VERSION = '2.1', + VERSION = '2.2', AUTHOR = 'RainLoop Team, Ludovic Pouzenc, ZephOne', - RELEASE = '2023-01-19', - REQUIRED = '2.19.2', + RELEASE = '2024-03-12', + REQUIRED = '2.35.3', CATEGORY = 'Login', DESCRIPTION = 'Enable custom mapping using ldap field'; /** @@ -175,7 +175,7 @@ class LDAPLoginMappingPlugin extends AbstractPlugin 'LDAP'); return FALSE; } - $sLogin = \MailSo\Base\Utils::GetAccountNameFromEmail($sEmail); + $sLogin = \MailSo\Base\Utils::getEmailAddressLocalPart($sEmail); $this->oLogger->Write('ldap_connect: trying...', \LOG_INFO, 'LDAP'); diff --git a/plugins/ldap-mail-accounts/LdapMailAccounts.php b/plugins/ldap-mail-accounts/LdapMailAccounts.php index e68e9e179..2f93dbd14 100644 --- a/plugins/ldap-mail-accounts/LdapMailAccounts.php +++ b/plugins/ldap-mail-accounts/LdapMailAccounts.php @@ -69,8 +69,8 @@ class LdapMailAccounts // and removes the domainname if this was configured inside the domain config. $username = $sEmail; $oActions = \RainLoop\Api::Actions(); - $oDomain = $oActions->DomainProvider()->Load(\MailSo\Base\Utils::GetDomainFromEmail($sEmail), true); - if ($oDomain->IncShortLogin()){ + $oDomain = $oActions->DomainProvider()->Load(\MailSo\Base\Utils::getEmailAddressDomain($sEmail), true); + if ($oDomain->ImapSettings()->shortLogin){ $username = @ldap_escape($this->RemoveEventualDomainPart($sEmail), "", LDAP_ESCAPE_FILTER); } @@ -101,7 +101,7 @@ class LdapMailAccounts if (count($mailAddressResults) < 1) { $this->logger->Write("Could not find user $username in LDAP! Overwriting of main mail address not possible.", \LOG_NOTICE, self::LOG_KEY); return false; - } + } foreach($mailAddressResults as $mailAddressResult) { @@ -208,7 +208,7 @@ class LdapMailAccounts { //Try to login the user with the same password as the primary account has //if this fails the user will see the new mail addresses but will be asked for the correct password - $sPass = $oAccount->IncPassword(); + $sPass = new \SnappyMail\SensitiveString($oAccount->IncPassword()); //After creating the accounts here $sUsername is used as username to login to the IMAP server (see Account.php) $oNewAccount = RainLoop\Model\AdditionalAccount::NewInstanceFromCredentials($oActions, $sEmail, $sUsername, $sPass); @@ -415,8 +415,8 @@ class LdapMailAccounts */ public static function RemoveEventualDomainPart(string $sInput) : string { - // Copy of \MailSo\Base\Utils::GetAccountNameFromEmail to make sure that also after eventual future - // updates the input string gets returned when no '@' is found (GetDomainFromEmail already doesn't do this) + // Copy of \MailSo\Base\Utils::getEmailAddressLocalPart to make sure that also after eventual future + // updates the input string gets returned when no '@' is found (getEmailAddressDomain already doesn't do this) $sResult = ''; if (\strlen($sInput)) { diff --git a/plugins/ldap-mail-accounts/index.php b/plugins/ldap-mail-accounts/index.php index e5258e26d..a6063c640 100644 --- a/plugins/ldap-mail-accounts/index.php +++ b/plugins/ldap-mail-accounts/index.php @@ -12,11 +12,11 @@ class LdapMailAccountsPlugin extends AbstractPlugin { const NAME = 'LDAP Mail Accounts', - VERSION = '2.0.0', + VERSION = '2.1.1', AUTHOR = 'cm-schl', URL = 'https://github.com/cm-sch', - RELEASE = '2023-03-14', - REQUIRED = '2.25.4', + RELEASE = '2024-03-16', + REQUIRED = '2.35.3', CATEGORY = 'Accounts', DESCRIPTION = 'Add additional mail accounts the SnappyMail user has access to by a LDAP query. Basing on the work of FWest98 (https://github.com/FWest98).'; @@ -44,10 +44,10 @@ class LdapMailAccountsPlugin extends AbstractPlugin */ public function overwriteMainAccountEmail(&$sEmail, &$sLogin) { -$this->Manager()->Actions()->Logger()->Write("Login DATA: login: $sLogin email: $sEmail", \LOG_WARNING, "LDAP MAIL ACCOUNTS PLUGIN"); + $this->Manager()->Actions()->Logger()->Write("Login DATA: login: $sLogin email: $sEmail", \LOG_WARNING, "LDAP MAIL ACCOUNTS PLUGIN"); - // Set up config - $config = LdapMailAccountsConfig::MakeConfig($this->Config()); + // Set up config + $config = LdapMailAccountsConfig::MakeConfig($this->Config()); if ($config->bool_overwrite_mail_address_main_account) { @@ -55,7 +55,7 @@ $this->Manager()->Actions()->Logger()->Write("Login DATA: login: $sLogin email: $oldapMailAccounts->overwriteEmail($sEmail, $sLogin); } -$this->Manager()->Actions()->Logger()->Write("Login DATA: login: $sLogin email: $sEmail", \LOG_WARNING, "LDAP MAIL ACCOUNTS PLUGIN"); + $this->Manager()->Actions()->Logger()->Write("Login DATA: login: $sLogin email: $sEmail", \LOG_WARNING, "LDAP MAIL ACCOUNTS PLUGIN"); } // Function gets called by RainLoop/Actions/User.php @@ -90,7 +90,7 @@ $this->Manager()->Actions()->Logger()->Write("Login DATA: login: $sLogin email: \nThe value found inside ldap will overwrite the mail address of the SnappyMail main account (the account the user logged in at SnappyMail) \nThe mail address used at login will still be used to login to the servers.") ->SetDefaultValue("mail"), - ]); + ]); return [ \RainLoop\Plugins\Property::NewInstance(LdapMailAccountsConfig::CONFIG_SERVER) @@ -172,7 +172,7 @@ $this->Manager()->Actions()->Logger()->Write("Login DATA: login: $sLogin email: ->SetDefaultValue("displayName"), $groupOverwriteMainAccount - + ]; } } diff --git a/plugins/login-autoconfig/index.php b/plugins/login-autoconfig/index.php index 01bac9249..ba23a9b92 100644 --- a/plugins/login-autoconfig/index.php +++ b/plugins/login-autoconfig/index.php @@ -11,9 +11,9 @@ class LoginAutoconfigPlugin extends \RainLoop\Plugins\AbstractPlugin NAME = 'Login Autoconfig', AUTHOR = 'SnappyMail', URL = 'https://snappymail.eu/', - VERSION = '1.0', - RELEASE = '2024-02-05', - REQUIRED = '2.34.0', + VERSION = '1.1', + RELEASE = '2024-03-12', + REQUIRED = '2.35.3', CATEGORY = 'Login', LICENSE = 'MIT', DESCRIPTION = 'Tries to login using the domain autoconfig'; @@ -27,7 +27,7 @@ class LoginAutoconfigPlugin extends \RainLoop\Plugins\AbstractPlugin { if (\str_contains($sEmail, '@')) { $oProvider = $this->Manager()->Actions()->DomainProvider(); - $sDomain = \MailSo\Base\Utils::GetDomainFromEmail($sEmail); + $sDomain = \MailSo\Base\Utils::getEmailAddressDomain($sEmail); $oDomain = $oProvider->Load($sDomain, false); if (!$oDomain) { $result = \RainLoop\Providers\Domain\Autoconfig::discover($sEmail); @@ -69,7 +69,7 @@ class LoginAutoconfigPlugin extends \RainLoop\Plugins\AbstractPlugin 'whiteList' => '' ]); $oProvider->Save($oDomain); - \SnappyMail\Log::notice("Domain setup for '{$sDomain}' is created and active"); + \SnappyMail\Log::notice('Autoconfig', "Domain setup for '{$sDomain}' is created and active"); } } } diff --git a/plugins/login-oauth2/index.php b/plugins/login-oauth2/index.php index e4e58fe13..6b4d8b1a2 100644 --- a/plugins/login-oauth2/index.php +++ b/plugins/login-oauth2/index.php @@ -4,9 +4,9 @@ class LoginOAuth2Plugin extends \RainLoop\Plugins\AbstractPlugin { const NAME = 'OAuth2', - VERSION = '1.1', - RELEASE = '2022-11-11', - REQUIRED = '2.21.0', + VERSION = '1.2', + RELEASE = '2024-03-12', + REQUIRED = '2.35.3', CATEGORY = 'Login', DESCRIPTION = 'IMAP, Sieve & SMTP login using RFC 7628 OAuth2'; @@ -55,7 +55,7 @@ class LoginOAuth2Plugin extends \RainLoop\Plugins\AbstractPlugin public function clientLogin(\RainLoop\Model\Account $oAccount, \MailSo\Net\NetClient $oClient, \MailSo\Net\ConnectSettings $oSettings) : void { - $sPassword = $oSettings->Password; + $sPassword = $oSettings->passphrase; $iGatLen = \strlen(static::GMAIL_TOKENS_PREFIX); if ($sPassword && static::GMAIL_TOKENS_PREFIX === \substr($sPassword, 0, $iGatLen)) { $aTokens = \json_decode(\substr($sPassword, $iGatLen)); @@ -63,7 +63,7 @@ class LoginOAuth2Plugin extends \RainLoop\Plugins\AbstractPlugin $sRefreshToken = !empty($aTokens[1]) ? $aTokens[1] : ''; } if ($sAccessToken && $sRefreshToken) { - $oSettings->Password = $this->gmailRefreshToken($sAccessToken, $sRefreshToken); + $oSettings->passphrase = $this->gmailRefreshToken($sAccessToken, $sRefreshToken); \array_unshift($oSettings->SASLMechanisms, 'OAUTHBEARER', 'XOAUTH2'); } } @@ -77,8 +77,8 @@ class LoginOAuth2Plugin extends \RainLoop\Plugins\AbstractPlugin * So we need to securely save a cryptkey. * Encrypted using the old/new refresh token is an option: * 1. decrypt cryptkey with the old refresh token - * 2. $oAccount->SetCryptKey('cryptkey') - * 3. encrypt cryptkey with the new refresh token + * 2. encrypt cryptkey with the new refresh token + * = $oAccount->resealCryptKey(new \SnappyMail\SensitiveString('old refresh token')) */ } } diff --git a/plugins/login-override/index.php b/plugins/login-override/index.php index 8a2110551..26f95d01a 100644 --- a/plugins/login-override/index.php +++ b/plugins/login-override/index.php @@ -4,9 +4,9 @@ class LoginOverridePlugin extends \RainLoop\Plugins\AbstractPlugin { const NAME = 'Login Override', - VERSION = '2.2', - RELEASE = '2023-02-08', - REQUIRED = '2.25.3', + VERSION = '2.3', + RELEASE = '2024-03-12', + REQUIRED = '2.35.3', CATEGORY = 'Filters', DESCRIPTION = 'Override IMAP/SMTP login credentials for specific users.'; @@ -50,10 +50,10 @@ class LoginOverridePlugin extends \RainLoop\Plugins\AbstractPlugin $line = \explode(':', $line, 3); if (!empty($line[0]) && $line[0] === $sEmail) { if (!empty($line[1])) { - $oSettings->Login = $line[1]; + $oSettings->username = $line[1]; } if (!empty($line[2])) { - $oSettings->Password = $line[2]; + $oSettings->passphrase = $line[2]; } break; } diff --git a/plugins/proxyauth-login-example/LICENSE b/plugins/login-proxyauth-example/LICENSE similarity index 96% rename from plugins/proxyauth-login-example/LICENSE rename to plugins/login-proxyauth-example/LICENSE index 4a4ca8d81..f709b02e2 100644 --- a/plugins/proxyauth-login-example/LICENSE +++ b/plugins/login-proxyauth-example/LICENSE @@ -1,6 +1,6 @@ The MIT License (MIT) -Copyright (c) 2014 RainLoop Team +Copyright (c) 2016 RainLoop Team Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in diff --git a/plugins/login-proxyauth-example/index.php b/plugins/login-proxyauth-example/index.php new file mode 100644 index 000000000..7b3ebcf4b --- /dev/null +++ b/plugins/login-proxyauth-example/index.php @@ -0,0 +1,33 @@ +addHook('imap.before-login', 'beforeLogin'); + $this->addHook('imap.after-login', 'afterLogin'); + } + + public function beforeLogin(\RainLoop\Model\Account $oAccount, \MailSo\Net\NetClient $oImapClient, \MailSo\Net\ConnectSettings $oSettings) : void + { + if ('example.com' === $oAccount->Domain()->Name()) { + $oSettings->username = 'AdminUsername'; + $oSettings->passphrase = 'AdminPassword'; + } + } + + public function afterLogin(\RainLoop\Model\Account $oAccount, \MailSo\Imap\ImapClient $oImapClient, bool $bSuccess) + { + if ($bSuccess && 'example.com' === $oAccount->Domain()->Name()) { + $oImapClient->SendRequestGetResponse('PROXYAUTH', array($oImapClient->EscapeString($oAccount->IncLogin()))); + } + } +} diff --git a/plugins/nextcloud/index.php b/plugins/nextcloud/index.php index d17aaa0ed..66ca84838 100644 --- a/plugins/nextcloud/index.php +++ b/plugins/nextcloud/index.php @@ -4,11 +4,11 @@ class NextcloudPlugin extends \RainLoop\Plugins\AbstractPlugin { const NAME = 'Nextcloud', - VERSION = '2.31', - RELEASE = '2024-02-18', + VERSION = '2.34', + RELEASE = '2024-03-16', CATEGORY = 'Integrations', DESCRIPTION = 'Integrate with Nextcloud v20+', - REQUIRED = '2.34.0'; + REQUIRED = '2.35.4'; public function Init() : void { @@ -80,7 +80,7 @@ class NextcloudPlugin extends \RainLoop\Plugins\AbstractPlugin ) { $sAccessToken = \OC::$server->getSession()->get('oidc_access_token'); if ($sAccessToken) { - $oSettings->Password = $sAccessToken; + $oSettings->passphrase = $sAccessToken; \array_unshift($oSettings->SASLMechanisms, 'OAUTHBEARER'); } } @@ -170,10 +170,14 @@ class NextcloudPlugin extends \RainLoop\Plugins\AbstractPlugin $oFiles->is_dir($sSaveFolder) || $oFiles->mkdir($sSaveFolder); $data->result = true; foreach ($data->items as $aItem) { - $sSavedFileName = isset($aItem['fileName']) ? $aItem['fileName'] : 'file.dat'; - $sSavedFileHash = !empty($aItem['fileHash']) ? $aItem['fileHash'] : ''; - if (!empty($sSavedFileHash)) { - $fFile = $data->filesProvider->GetFile($data->account, $sSavedFileHash, 'rb'); + $sSavedFileName = empty($aItem['fileName']) ? 'file.dat' : $aItem['fileName']; + if (!empty($aItem['data'])) { + $sSavedFileNameFull = static::SmartFileExists($sSaveFolder.'/'.$sSavedFileName, $oFiles); + if (!$oFiles->file_put_contents($sSavedFileNameFull, $aItem['data'])) { + $data->result = false; + } + } else if (!empty($aItem['fileHash'])) { + $fFile = $data->filesProvider->GetFile($data->account, $aItem['fileHash'], 'rb'); if (\is_resource($fFile)) { $sSavedFileNameFull = static::SmartFileExists($sSaveFolder.'/'.$sSavedFileName, $oFiles); if (!$oFiles->file_put_contents($sSavedFileNameFull, $fFile)) { @@ -186,20 +190,14 @@ class NextcloudPlugin extends \RainLoop\Plugins\AbstractPlugin } } } - - foreach ($data->items as $aItem) { - $sFileHash = (string) (isset($aItem['fileHash']) ? $aItem['fileHash'] : ''); - if (!empty($sFileHash)) { - $data->filesProvider->Clear($data->account, $sFileHash); - } - } } } public function FilterAppData($bAdmin, &$aResult) : void { if (!$bAdmin && \is_array($aResult)) { - $sUID = \OC::$server->getUserSession()->getUser()->getUID(); + $ocUser = \OC::$server->getUserSession()->getUser(); + $sUID = $ocUser->getUID(); $oUrlGen = \OC::$server->getURLGenerator(); $sWebDAV = $oUrlGen->getAbsoluteURL($oUrlGen->linkTo('', 'remote.php') . '/dav'); // $sWebDAV = \OCP\Util::linkToRemote('dav'); @@ -227,6 +225,11 @@ class NextcloudPlugin extends \RainLoop\Plugins\AbstractPlugin if ($sCustomEmail) { $sEmail = $sCustomEmail; } + if (!$sEmail) { + $sEmail = $ocUser->getEMailAddress(); +// ?: $oc->user->getPrimaryEMailAddress(); + } +/* if ($config->getAppValue('snappymail', 'snappymail-autologin-oidc', false)) { if (\OC::$server->getSession()->get('is_oidc')) { $sEmail = "{$sUID}@nextcloud"; @@ -237,6 +240,7 @@ class NextcloudPlugin extends \RainLoop\Plugins\AbstractPlugin } else { \SnappyMail\Log::debug('Nextcloud', 'OIDC is off'); } +*/ $aResult['DevEmail'] = $sEmail ?: ''; } else if (!empty($aResult['ContactsSync'])) { $bSave = false; @@ -248,8 +252,13 @@ class NextcloudPlugin extends \RainLoop\Plugins\AbstractPlugin $aResult['ContactsSync']['User'] = $sUID; $bSave = true; } - if (empty($aResult['ContactsSync']['Password'])) { - $aResult['ContactsSync']['Password'] = ''; + $pass = \OC::$server->getSession()['snappymail-passphrase']; + if ($pass/* && empty($aResult['ContactsSync']['Password'])*/) { + $pass = \SnappyMail\Crypt::DecryptUrlSafe($pass, $sUID); + if ($pass) { + $aResult['ContactsSync']['Password'] = $pass; + $bSave = true; + } } if ($bSave) { $oActions = \RainLoop\Api::Actions(); diff --git a/plugins/override-smtp-credentials/index.php b/plugins/override-smtp-credentials/index.php index 063d10508..e524bef71 100644 --- a/plugins/override-smtp-credentials/index.php +++ b/plugins/override-smtp-credentials/index.php @@ -4,9 +4,9 @@ class OverrideSmtpCredentialsPlugin extends \RainLoop\Plugins\AbstractPlugin { const NAME = 'Override SMTP Credentials', - VERSION = '2.4', - RELEASE = '2023-01-19', - REQUIRED = '2.23.0', + VERSION = '2.5', + RELEASE = '2024-03-12', + REQUIRED = '2.35.3', CATEGORY = 'Filters', DESCRIPTION = 'Override SMTP credentials for specific users.'; @@ -67,8 +67,8 @@ class OverrideSmtpCredentialsPlugin extends \RainLoop\Plugins\AbstractPlugin $sFoundValue = ''; if (\strlen($sWhiteList) && \RainLoop\Plugins\Helper::ValidateWildcardValues($oAccount->Email(), $sWhiteList, $sFoundValue)) { $oSettings->useAuth = (bool) $this->Config()->Get('plugin', 'smtp_auth', true); - $oSettings->Login = \trim($this->Config()->Get('plugin', 'smtp_user', '')); - $oSettings->Password = (string) $this->Config()->Get('plugin', 'smtp_password', ''); + $oSettings->username = \trim($this->Config()->Get('plugin', 'smtp_user', '')); + $oSettings->passphrase = (string) $this->Config()->Get('plugin', 'smtp_password', ''); } } diff --git a/plugins/proxyauth-login-example/index.php b/plugins/proxyauth-login-example/index.php deleted file mode 100644 index e92cde71d..000000000 --- a/plugins/proxyauth-login-example/index.php +++ /dev/null @@ -1,43 +0,0 @@ -addHook('login.success', 'EventLoginPostLoginProvide'); - } - - /** - * @param string $sLogin - * @param string $sPassword - */ - public function isValidAccount($sLogin, $sPassword) - { - return !empty($sLogin) && !empty($sPassword); - } - - /** - * @param \RainLoop\Model\Account $oAccount - */ - public function EventLoginPostLoginProvide(MainAccount $oAccount) - { - // Verify logic - if (!$this->isValidAccount($oAccount->IncLogin(), $oAccount->IncPassword())) { - // throw a Auth Error Exception - throw new \RainLoop\Exceptions\ClientException(\RainLoop\Notifications::AuthError); - } - - $oAccount->SetProxyAuthUser('admin@domain.com'); - $oAccount->SetProxyAuthPassword('secret-admin-password'); - } -} diff --git a/plugins/recaptcha/index.php b/plugins/recaptcha/index.php index 9d76e4a7d..9245bd512 100644 --- a/plugins/recaptcha/index.php +++ b/plugins/recaptcha/index.php @@ -6,9 +6,9 @@ class RecaptchaPlugin extends \RainLoop\Plugins\AbstractPlugin NAME = 'reCaptcha', AUTHOR = 'SnappyMail', URL = 'https://snappymail.eu/', - VERSION = '2.15', - RELEASE = '2023-02-22', - REQUIRED = '2.26.4', + VERSION = '2.16', + RELEASE = '2024-03-12', + REQUIRED = '2.35.3', CATEGORY = 'General', LICENSE = 'MIT', DESCRIPTION = 'A CAPTCHA (v2) is a program that can generate and grade tests that humans can pass but current computer programs cannot. For example, humans can read distorted text as the one shown below, but current computer programs can\'t. More info at https://developers.google.com/recaptcha'; @@ -64,7 +64,7 @@ class RecaptchaPlugin extends \RainLoop\Plugins\AbstractPlugin $oCacher = $this->Manager()->Actions()->Cacher(); $sLimit = $oCacher && $oCacher->IsInited() ? $oCacher->Get($this->getCaptchaCacherKey()) : '0'; - if (\strlen($sLimit) && \is_numeric($sLimit)) { + if (\is_numeric($sLimit)) { $iConfigLimit -= (int) $sLimit; } } @@ -123,7 +123,7 @@ class RecaptchaPlugin extends \RainLoop\Plugins\AbstractPlugin if (false === $aResponse['Result']) { $iLimit = 0; $sLimut = $oCacher->Get($sKey); - if (\strlen($sLimut) && \is_numeric($sLimut)) { + if (\is_numeric($sLimut)) { $iLimit = (int) $sLimut; } diff --git a/plugins/smtp-use-from-adr-account/index.php b/plugins/smtp-use-from-adr-account/index.php index d4b30843b..cea9eab3b 100644 --- a/plugins/smtp-use-from-adr-account/index.php +++ b/plugins/smtp-use-from-adr-account/index.php @@ -7,9 +7,9 @@ class SmtpUseFromAdrAccountPlugin extends \RainLoop\Plugins\AbstractPlugin NAME = 'Use From-Address-Account for smtp', AUTHOR = 'attike', URL = 'https://github.com/attike', - VERSION = '1.0', - RELEASE = '2023-12-06', - REQUIRED = '2.23.0', + VERSION = '1.1', + RELEASE = '2024-03-12', + REQUIRED = '2.35.3', CATEGORY = 'Filters', DESCRIPTION = 'Set smpt-config and -credentials based on selected from-address-account'; @@ -34,8 +34,8 @@ class SmtpUseFromAdrAccountPlugin extends \RainLoop\Plugins\AbstractPlugin if (\strlen($sWhiteList) && \RainLoop\Plugins\Helper::ValidateWildcardValues($sFrom, $sWhiteList, $sFoundValue) && $sFrom != $oAccount->Email()) { \SnappyMail\LOG::info(get_class($this) ,'From address different from account recognized: '. $oAccount->Email().' -> '.$sFrom . '(~ '.$sFoundValue.')'); $oMainAccount; - $oFromAccount; - if ( $oAccount instanceof \RainLoop\Model\MainAccount ) { + $oFromAccount; + if ($oAccount instanceof \RainLoop\Model\MainAccount ) { $oMainAccount=$oAccount; } else { $oMainAccount=$this->Manager()->Actions()->getMainAccountFromToken(); @@ -44,21 +44,21 @@ class SmtpUseFromAdrAccountPlugin extends \RainLoop\Plugins\AbstractPlugin return; } } - $aAccounts=$this->Manager()->Actions()->getAccounts($oMainAccount); - foreach ($aAccounts as &$value) { - $oValue=\RainLoop\Model\AdditionalAccount::NewInstanceFromTokenArray($this->Manager()->Actions(), $value); - if ($oValue->Email()==$sFrom) { - $oFromAccount = $oValue; - break; - } - } - if (is_null($oFromAccount)){ + $aAccounts = $this->Manager()->Actions()->getAccounts($oMainAccount); + foreach ($aAccounts as &$value) { + $oValue=\RainLoop\Model\AdditionalAccount::NewInstanceFromTokenArray($this->Manager()->Actions(), $value); + if ($oValue->Email()==$sFrom) { + $oFromAccount = $oValue; + break; + } + } + if (is_null($oFromAccount)){ \SnappyMail\LOG::info(get_class($this),'No Account found for '. $sFrom); if ($this->Config()->Get('plugin', 'throw_notfound_exception', true)) { throw new \RainLoop\Exceptions\ClientException(\RainLoop\Notifications::AccountDoesNotExist); } - return; - } + return; + } $this->aFromAccount[$oAccount->Email()]=$oFromAccount; } } @@ -71,10 +71,10 @@ class SmtpUseFromAdrAccountPlugin extends \RainLoop\Plugins\AbstractPlugin { if ( isset($this->aFromAccount[$oAccount->Email()]) ) { $oFromAccount = $this->aFromAccount[$oAccount->Email()]; - $oSettings->host = $oFromAccount->Domain()->OutHost(); - $oSettings->port = (int) $oFromAccount->Domain()->OutPort(); - $oSettings->type = $oFromAccount->Domain()-> SmtpSettings()->type; - \SnappyMail\LOG::info(get_class($this),'Smtp config rewrite: '. $oFromAccount->Domain()->OutHost()); + $oSettings->host = $oFromAccount->Domain()->SmtpSettings()->host; + $oSettings->port = (int) $oFromAccount->Domain()->SmtpSettings()->port; + $oSettings->type = $oFromAccount->Domain()->SmtpSettings()->type; + \SnappyMail\LOG::info(get_class($this),'Smtp config rewrite: '. $oSettings->host); } } @@ -88,9 +88,9 @@ class SmtpUseFromAdrAccountPlugin extends \RainLoop\Plugins\AbstractPlugin if ( isset($this->aFromAccount[$oAccount->Email()]) ) { $oFromAccount = $this->aFromAccount[$oAccount->Email()]; unset($this->aFromAccount[$oAccount->Email()]); - $oSettings->Login = $oFromAccount-> OutLogin(); - $oSettings->useAuth = $oFromAccount->Domain()-> SmtpSettings()->useAuth; - $oSettings->Password = $oFromAccount->IncPassword(); + $oSettings->useAuth = $oFromAccount->Domain()->SmtpSettings()->useAuth; + $oSettings->username = $oFromAccount->OutLogin(); + $oSettings->passphrase = $oFromAccount->IncPassword(); \SnappyMail\LOG::info(get_class($this),'user/pwd rewrite: '. $oFromAccount->Email()); } } @@ -106,10 +106,9 @@ class SmtpUseFromAdrAccountPlugin extends \RainLoop\Plugins\AbstractPlugin ->SetDescription('space as delimiter, wildcard supported.') ->SetDefaultValue('user@example.com *@example2.com'), \RainLoop\Plugins\Property::NewInstance('throw_notfound_exception')->SetLabel('Throw Exception, if from-adr is not found as account') - ->SetType(\RainLoop\Enumerations\PluginPropertyType::BOOL) - ->SetDescription('it is not possible to send eMails in this case, regardless of whether the smtp-server would do it') - ->SetDefaultValue(true), - + ->SetType(\RainLoop\Enumerations\PluginPropertyType::BOOL) + ->SetDescription('it is not possible to send eMails in this case, regardless of whether the smtp-server would do it') + ->SetDefaultValue(true) ); } diff --git a/plugins/white-list/index.php b/plugins/white-list/index.php index efad99ea8..2b91b8bcc 100644 --- a/plugins/white-list/index.php +++ b/plugins/white-list/index.php @@ -4,35 +4,31 @@ class WhiteListPlugin extends \RainLoop\Plugins\AbstractPlugin { const NAME = 'Whitelist', - VERSION = '2.1', - RELEASE = '2021-04-21', + VERSION = '2.2', + RELEASE = '2024-03-04', REQUIRED = '2.5.0', CATEGORY = 'Login', DESCRIPTION = 'Simple login whitelist (with wildcard and exceptions functionality).'; public function Init() : void { - $this->addHook('login.credentials', 'FilterLoginCredentials'); + $this->addHook('login.credentials.step-1', 'FilterLoginCredentials'); } /** - * @param string $sEmail - * @param string $sLogin - * @param string $sPassword - * * @throws \RainLoop\Exceptions\ClientException */ - public function FilterLoginCredentials(&$sEmail, &$sLogin, &$sPassword) + public function FilterLoginCredentials(string &$sEmail) { $sWhiteList = \trim($this->Config()->Get('plugin', 'white_list', '')); - if (0 < strlen($sWhiteList) && !\RainLoop\Plugins\Helper::ValidateWildcardValues($sEmail, $sWhiteList)) - { + if (\strlen($sWhiteList) && !\RainLoop\Plugins\Helper::ValidateWildcardValues($sEmail, $sWhiteList)) { $sExceptions = \trim($this->Config()->Get('plugin', 'exceptions', '')); - if (0 === \strlen($sExceptions) || !\RainLoop\Plugins\Helper::ValidateWildcardValues($sEmail, $sExceptions)) - { + if (!\strlen($sExceptions) || \RainLoop\Plugins\Helper::ValidateWildcardValues($sEmail, $sExceptions)) { throw new \RainLoop\Exceptions\ClientException( - $this->Config()->Get('plugin', 'auth_error', true) ? - \RainLoop\Notifications::AuthError : \RainLoop\Notifications::AccountNotAllowed); + $this->Config()->Get('plugin', 'auth_error', false) + ? \RainLoop\Notifications::AuthError + : \RainLoop\Notifications::AccountNotAllowed + ); } } } @@ -46,7 +42,7 @@ class WhiteListPlugin extends \RainLoop\Plugins\AbstractPlugin \RainLoop\Plugins\Property::NewInstance('auth_error')->SetLabel('Auth Error') ->SetType(\RainLoop\Enumerations\PluginPropertyType::BOOL) ->SetDescription('Throw an authentication error instead of an access error.') - ->SetDefaultValue(true), + ->SetDefaultValue(false), \RainLoop\Plugins\Property::NewInstance('white_list')->SetLabel('White List') ->SetType(\RainLoop\Enumerations\PluginPropertyType::STRING_TEXT) ->SetDescription('Emails white list, space as delimiter, wildcard supported.') diff --git a/snappymail/v/0.0.0/app/domains/default.json b/snappymail/v/0.0.0/app/domains/default.json index 8f6a37243..a2034262d 100644 --- a/snappymail/v/0.0.0/app/domains/default.json +++ b/snappymail/v/0.0.0/app/domains/default.json @@ -1,11 +1,11 @@ { - "name": "*", "IMAP": { "host": "localhost", "port": 143, "type": 0, "timeout": 300, "shortLogin": false, + "lowerLogin": true, "sasl": [ "SCRAM-SHA3-512", "SCRAM-SHA-512", @@ -40,6 +40,7 @@ "type": 0, "timeout": 60, "shortLogin": false, + "lowerLogin": true, "sasl": [ "SCRAM-SHA3-512", "SCRAM-SHA-512", @@ -66,6 +67,7 @@ "type": 0, "timeout": 10, "shortLogin": false, + "lowerLogin": true, "sasl": [ "SCRAM-SHA3-512", "SCRAM-SHA-512", diff --git a/snappymail/v/0.0.0/app/domains/disabled b/snappymail/v/0.0.0/app/domains/disabled index 09fd760ff..c4d1d2143 100644 --- a/snappymail/v/0.0.0/app/domains/disabled +++ b/snappymail/v/0.0.0/app/domains/disabled @@ -1 +1,5 @@ -outlook.com,qq.com,yahoo.com,gmail.com,hotmail.com \ No newline at end of file +outlook.com +qq.com +yahoo.com +gmail.com +hotmail.com diff --git a/snappymail/v/0.0.0/app/libraries/MailSo/Base/Http.php b/snappymail/v/0.0.0/app/libraries/MailSo/Base/Http.php index 788932ae0..72009a623 100644 --- a/snappymail/v/0.0.0/app/libraries/MailSo/Base/Http.php +++ b/snappymail/v/0.0.0/app/libraries/MailSo/Base/Http.php @@ -115,11 +115,10 @@ class Http if (!\strlen($sHost)) { $sName = static::GetServer('SERVER_NAME', ''); $iPort = (int) static::GetServer('SERVER_PORT', 80); - $sHost = (\in_array($iPort, array(80, 433))) ? $sName : $sName.':'.$iPort; } - if ($bWithoutWWW && 'www.' === \substr(\strtolower($sHost), 0, 4)) { + if ($bWithoutWWW && \str_starts_with($sHost, 'www.')) { $sHost = \substr($sHost, 4); } diff --git a/snappymail/v/0.0.0/app/libraries/MailSo/Base/Utils.php b/snappymail/v/0.0.0/app/libraries/MailSo/Base/Utils.php index 7a4c5f9b8..551c99cb5 100644 --- a/snappymail/v/0.0.0/app/libraries/MailSo/Base/Utils.php +++ b/snappymail/v/0.0.0/app/libraries/MailSo/Base/Utils.php @@ -165,17 +165,6 @@ abstract class Utils || !\preg_match('/[^\x09\x10\x13\x0A\x0D\x20-\x7E]/', $sValue); } - public static function StrMailDomainToLower(string $sValue) : string - { - $aParts = \explode('@', $sValue); - $iLast = \count($aParts) - 1; - if ($iLast) { - $aParts[$iLast] = \mb_strtolower($aParts[$iLast]); - } - - return \implode('@', $aParts); - } - public static function StripSpaces(string $sValue) : string { return static::Trim( @@ -196,10 +185,10 @@ abstract class Utils return \round($iSize, $iRound).$aSizes[$iIndex]; } - public static function DecodeEncodingValue(string $sEncodedValue, string $sEncodeingType) : string + public static function DecodeEncodingValue(string $sEncodedValue, string $sEncodingType) : string { $sResult = $sEncodedValue; - switch (\strtolower($sEncodeingType)) + switch (\strtolower($sEncodingType)) { case 'q': case 'quoted_printable': @@ -420,21 +409,30 @@ abstract class Utils return \trim($sValue); } + /** + * @deprecated use getEmailAddressLocalPart + */ public static function GetAccountNameFromEmail(string $sEmail) : string { - $sResult = ''; - if (\strlen($sEmail)) { - $iPos = \strrpos($sEmail, '@'); - $sResult = (false === $iPos) ? $sEmail : \substr($sEmail, 0, $iPos); - } - - return $sResult; + return static::getEmailAddressLocalPart($sEmail); + } + public static function getEmailAddressLocalPart(string $sEmail) : string + { + $iPos = \strrpos($sEmail, '@'); + return (false === $iPos) ? $sEmail : \substr($sEmail, 0, $iPos); } + /** + * @deprecated use getEmailAddressDomain + */ public static function GetDomainFromEmail(string $sEmail) : string { - $aParts = \explode('@', $sEmail); - return isset($aParts[1]) ? \array_pop($aParts) : ''; + return static::getEmailAddressDomain($sEmail); + } + public static function getEmailAddressDomain(string $sEmail) : string + { + $iPos = \strrpos($sEmail, '@'); + return (false === $iPos) ? '' : \substr($sEmail, $iPos + 1); } public static function GetClearDomainName(string $sDomain) : string @@ -686,56 +684,42 @@ abstract class Utils return \sha1($sAdditionalSalt . \random_bytes(16)); } - public static function ValidateDomain(string $sDomain, bool $bSimple = false) : bool - { - $aMatch = array(); - if ($bSimple) { - return \preg_match('/.+(\.[a-zA-Z]+)$/', $sDomain, $aMatch) && !empty($aMatch[1]); - } - - return \preg_match('/.+(\.[a-zA-Z]+)$/', $sDomain, $aMatch) && !empty($aMatch[1]) && \in_array($aMatch[1], \explode(' ', - '.academy .actor .agency .audio .bar .beer .bike .blue .boutique .cab .camera .camp .capital .cards .careers .cash .catering .center .cheap .city .cleaning .clinic .clothing .club .coffee .community .company .computer .construction .consulting .contractors .cool .credit .dance .dating .democrat .dental .diamonds .digital .direct .directory .discount .domains .education .email .energy .equipment .estate .events .expert .exposed .fail .farm .fish .fitness .florist .fund .futbol .gallery .gift .glass .graphics .guru .help .holdings .holiday .host .hosting .house .institute .international .kitchen .land .life .lighting .limo .link .management .market .marketing .media .menu .moda .partners .parts .photo .photography .photos .pics .pink .press .productions .pub .red .rentals .repair .report .rest .sexy .shoes .social .solar .solutions .space .support .systems .tattoo .tax .technology .tips .today .tools .town .toys .trade .training .university .uno .vacations .vision .vodka .voyage .watch .webcam .wiki .work .works .wtf .zone .aero .asia .biz .cat .com .coop .edu .gov .info .int .jobs .mil .mobi .museum .name .net .org .pro .tel .travel .xxx .xyz '. - '.ac .ad .ae .af .ag .ai .al .am .an .ao .aq .ar .as .at .au .aw .ax .az .ba .bb .bd .be .bf .bg .bh .bi .bj .bm .bn .bo .br .bs .bt .bv .bw .by .bz .ca .cc .cd .cf .cg .ch .ci .ck .cl .cm .cn .co .cr .cs .cu .cv .cx .cy .cz .dd .de .dj .dk .dm .do .dz .ec .ee .eg .er .es .et .eu .fi .fj .fk .fm .fo .fr .ga .gb .gd .ge .gf .gg .gh .gi .gl .gm .gn .gp .gq .gr .gs .gt .gu .gw .gy .hk .hm .hn .hr .ht .hu .id .ie .il .im .in .io .iq .ir .is .it .je .jm .jo .jp .ke .kg .kh .ki .km .kn .kp .kr .kw .ky .kz .la .lb .lc .li .lk .lr .ls .lt .lu .lv .ly .ma .mc .md .me .mg .mh .mk .ml .mm .mn .mo .mp .mq .mr .ms .mt .mu .mv .mw .mx .my .mz .na .nc .ne .nf .ng .ni .nl .no .np .nr .nu .nz .om .pa .pe .pf .pg .ph .pk .pl .pm .pn .pr .ps .pt .pw .py .qa .re .ro .rs .ru . .rw .sa .sb .sc .sd .se .sg .sh .si .sj .sk .sl .sm .sn .so .sr .st .su .sv .sy .sz .tc .td .tf .tg .th .tj .tk .tl .tm .tn .to .tp .tr .tt .tv .tw .tz .ua .ug .uk .us .uy .uz .va .vc .ve .vg .vi .vn .vu .wf .ws .ye .yt .za .zm .zw' - )); - } - public static function ValidateIP(string $sIp) : bool { return !empty($sIp) && $sIp === \filter_var($sIp, FILTER_VALIDATE_IP); } - public static function IdnToUtf8(string $sStr, bool $bLowerIfAscii = false) : string + /** + * @deprecated + */ + public static function IdnToUtf8(string $sStr) : string { - if (\strlen($sStr) && \preg_match('/(^|\.|@)xn--/i', $sStr)) { - try - { - $sStr = \SnappyMail\IDN::anyToUtf8($sStr); - } - catch (\Throwable $oException) {} + $trace = \debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 2)[1]; + \SnappyMail\Log::warning('MailSo', "Deprecated function IdnToUtf8 called at {$trace['file']}#{$trace['line']}"); + if (\preg_match('/(^|\.|@)xn--/i', $sStr)) { + $sStr = \str_contains($sStr, '@') + ? \SnappyMail\IDN::emailToUtf8($string) + : \idn_to_utf8($string); } - - return $bLowerIfAscii ? static::StrMailDomainToLower($sStr) : $sStr; + return $sStr; } - public static function IdnToAscii(string $sStr, bool $bLowerIfAscii = false) : string + /** + * @deprecated + */ + public static function IdnToAscii(string $sStr, bool $bLowerCase = false) : string { - $sStr = $bLowerIfAscii ? static::StrMailDomainToLower($sStr) : $sStr; - - $sUser = ''; - $sDomain = $sStr; - if (false !== \strpos($sStr, '@')) { - $sUser = static::GetAccountNameFromEmail($sStr); - $sDomain = static::GetDomainFromEmail($sStr); + $trace = \debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 2)[1]; + \SnappyMail\Log::warning('MailSo', "Deprecated function IdnToAscii called at {$trace['file']}#{$trace['line']}"); + $aParts = \explode('@', $sStr); + $sDomain = \array_pop($aParts); + if (\preg_match('/[^\x20-\x7E]/', $sDomain)) { + $sDomain = \idn_to_ascii($string); } - - if (\strlen($sDomain) && \preg_match('/[^\x20-\x7E]/', $sDomain)) { - try - { - $sDomain = \SnappyMail\IDN::anyToAscii($sDomain); - } - catch (\Throwable $oException) {} + if ($bLowerCase) { + $sDomain = \strtolower($sDomain); } - - return ('' === $sUser ? '' : $sUser.'@').$sDomain; + $aParts[] = $sDomain; + return \implode('@', $aParts); } } diff --git a/snappymail/v/0.0.0/app/libraries/MailSo/Cache/CacheClient.php b/snappymail/v/0.0.0/app/libraries/MailSo/Cache/CacheClient.php index 5144c1296..d42b9f09b 100644 --- a/snappymail/v/0.0.0/app/libraries/MailSo/Cache/CacheClient.php +++ b/snappymail/v/0.0.0/app/libraries/MailSo/Cache/CacheClient.php @@ -49,18 +49,20 @@ class CacheClient return '1' === $this->Get($sKey.'/LOCK'); } - public function Get(string $sKey, bool $bClearAfterGet = false) + public function Exists(string $sKey) : bool { - $sValue = ''; + return $this->oDriver && $this->oDriver->Exists($sKey.$this->sCacheIndex); + } + public function Get(string $sKey, bool $bClearAfterGet = false) : ?string + { + $sValue = null; if ($this->oDriver) { $sValue = $this->oDriver->Get($sKey.$this->sCacheIndex); + if ($bClearAfterGet) { + $this->Delete($sKey); + } } - - if ($bClearAfterGet) { - $this->Delete($sKey); - } - return $sValue; } diff --git a/snappymail/v/0.0.0/app/libraries/MailSo/Cache/DriverInterface.php b/snappymail/v/0.0.0/app/libraries/MailSo/Cache/DriverInterface.php index ae7fa478f..06a73ace0 100644 --- a/snappymail/v/0.0.0/app/libraries/MailSo/Cache/DriverInterface.php +++ b/snappymail/v/0.0.0/app/libraries/MailSo/Cache/DriverInterface.php @@ -19,7 +19,9 @@ interface DriverInterface { public function Set(string $sKey, string $sValue) : bool; - public function Get(string $sKey) : string; + public function Exists(string $sKey) : bool; + + public function Get(string $sKey) : ?string; public function Delete(string $sKey) : void; diff --git a/snappymail/v/0.0.0/app/libraries/MailSo/Cache/Drivers/APCU.php b/snappymail/v/0.0.0/app/libraries/MailSo/Cache/Drivers/APCU.php index 090aba857..09162268c 100644 --- a/snappymail/v/0.0.0/app/libraries/MailSo/Cache/Drivers/APCU.php +++ b/snappymail/v/0.0.0/app/libraries/MailSo/Cache/Drivers/APCU.php @@ -32,10 +32,15 @@ class APCU implements \MailSo\Cache\DriverInterface return \apcu_store($this->generateCachedKey($sKey), (string) $sValue); } - public function Get(string $sKey) : string + public function Exists(string $sKey) : bool + { + return \apcu_exists($this->generateCachedKey($sKey)); + } + + public function Get(string $sKey) : ?string { $sValue = \apcu_fetch($this->generateCachedKey($sKey)); - return \is_string($sValue) ? $sValue : ''; + return \is_string($sValue) ? $sValue : null; } public function Delete(string $sKey) : void diff --git a/snappymail/v/0.0.0/app/libraries/MailSo/Cache/Drivers/File.php b/snappymail/v/0.0.0/app/libraries/MailSo/Cache/Drivers/File.php index aa7de2783..a2d27b159 100644 --- a/snappymail/v/0.0.0/app/libraries/MailSo/Cache/Drivers/File.php +++ b/snappymail/v/0.0.0/app/libraries/MailSo/Cache/Drivers/File.php @@ -46,14 +46,20 @@ class File implements \MailSo\Cache\DriverInterface return '' === $sPath ? false : false !== \file_put_contents($sPath, $sValue); } - public function Get(string $sKey) : string + public function Exists(string $sKey) : bool { - $sValue = ''; + $sPath = $this->generateCachedFileName($sKey); + return '' !== $sPath && \file_exists($sPath); + } + + public function Get(string $sKey) : ?string + { + $sValue = null; $sPath = $this->generateCachedFileName($sKey); if ('' !== $sPath && \file_exists($sPath)) { $sValue = \file_get_contents($sPath); } - return \is_string($sValue) ? $sValue : ''; + return \is_string($sValue) ? $sValue : null; } public function Delete(string $sKey) : void diff --git a/snappymail/v/0.0.0/app/libraries/MailSo/Cache/Drivers/Memcache.php b/snappymail/v/0.0.0/app/libraries/MailSo/Cache/Drivers/Memcache.php index b5e2a5252..60ab3a667 100644 --- a/snappymail/v/0.0.0/app/libraries/MailSo/Cache/Drivers/Memcache.php +++ b/snappymail/v/0.0.0/app/libraries/MailSo/Cache/Drivers/Memcache.php @@ -46,10 +46,15 @@ class Memcache implements \MailSo\Cache\DriverInterface return $this->oMem ? $this->oMem->set($this->generateCachedKey($sKey), $sValue, 0, $this->iExpire) : false; } - public function Get(string $sKey) : string + public function Exists(string $sKey) : bool { - $sValue = $this->oMem ? $this->oMem->get($this->generateCachedKey($sKey)) : ''; - return \is_string($sValue) ? $sValue : ''; + return $this->oMem && false !== $this->oMem->get($this->generateCachedKey($sKey)); + } + + public function Get(string $sKey) : ?string + { + $sValue = $this->oMem ? $this->oMem->get($this->generateCachedKey($sKey)) : null; + return \is_string($sValue) ? $sValue : null; } public function Delete(string $sKey) : void diff --git a/snappymail/v/0.0.0/app/libraries/MailSo/Cache/Drivers/Redis.php b/snappymail/v/0.0.0/app/libraries/MailSo/Cache/Drivers/Redis.php index 9271f0cb0..2c41a4209 100644 --- a/snappymail/v/0.0.0/app/libraries/MailSo/Cache/Drivers/Redis.php +++ b/snappymail/v/0.0.0/app/libraries/MailSo/Cache/Drivers/Redis.php @@ -68,10 +68,15 @@ class Redis implements \MailSo\Cache\DriverInterface return $sValue === true || $sValue == 'OK'; } - public function Get(string $sKey) : string + public function Exists(string $sKey) : bool + { + return $this->oRedis && $this->oRedis->exists($this->generateCachedKey($sKey)); + } + + public function Get(string $sKey) : ?string { $sValue = $this->oRedis ? $this->oRedis->get($this->generateCachedKey($sKey)) : ''; - return \is_string($sValue) ? $sValue : ''; + return \is_string($sValue) ? $sValue : null; } public function Delete(string $sKey) : void diff --git a/snappymail/v/0.0.0/app/libraries/MailSo/Imap/BodyStructure.php b/snappymail/v/0.0.0/app/libraries/MailSo/Imap/BodyStructure.php index d619b13cb..8e8c7bf6f 100644 --- a/snappymail/v/0.0.0/app/libraries/MailSo/Imap/BodyStructure.php +++ b/snappymail/v/0.0.0/app/libraries/MailSo/Imap/BodyStructure.php @@ -19,38 +19,40 @@ use MailSo\Mime\Enumerations\ContentType; */ class BodyStructure implements \JsonSerializable { - private string $sContentType; - private array $aContentTypeParams; - private string $sCharset; + private string + $sCharset = '', + $sContentID = '', + $sContentTransferEncoding = '', + $sContentType = '', + $sDescription = '', + $sDisposition = '', + $sFileName = '', + $sLanguage = '', + $sLocation = '', + $sPartID = ''; - private string $sContentID; - - private string $sDescription; - - private string $sMailEncodingName; - - private string $sDisposition; - - private string $sFileName; - - private string $sLanguage = ''; - - private string $sLocation = ''; - - private int $iSize; - - private string $sPartID; + private int $iSize = 0; /** * \MailSo\Imap\BodyStructure[] */ private array $aSubParts; - public function MailEncodingName() : string + public function Charset() : string { - return $this->sMailEncodingName; + return $this->sCharset; + } + + public function ContentTransferEncoding() : string + { + return $this->sContentTransferEncoding; + } + + public function ContentType() : string + { + return $this->sContentType; } public function PartID() : string @@ -88,15 +90,10 @@ class BodyStructure implements \JsonSerializable return ($this->isInline() ? 'inline' : 'part' ) . $sIdx; } - public function ContentType() : string - { - return $this->sContentType; - } - public function EstimatedSize() : int { $fCoefficient = 1; - switch ($this->sMailEncodingName) + switch ($this->sContentTransferEncoding) { case 'base64': $fCoefficient = 0.75; @@ -109,11 +106,6 @@ class BodyStructure implements \JsonSerializable return (int) ($this->iSize * $fCoefficient); } - public function Charset() : string - { - return $this->sCharset; - } - public function SubParts() : array { return $this->aSubParts; @@ -192,7 +184,7 @@ class BodyStructure implements \JsonSerializable { return !empty($this->aContentTypeParams['format']) && 'flowed' === \strtolower(\trim($this->aContentTypeParams['format'])) - && !\in_array($this->sMailEncodingName, array('base64', 'quoted-printable')); + && !\in_array($this->sContentTransferEncoding, array('base64', 'quoted-printable')); } public function GetHtmlAndPlainParts() : array @@ -209,11 +201,12 @@ class BodyStructure implements \JsonSerializable /** * No text found, is it encrypted? * If so, just return that. + * Only when \RainLoop\Api::Config()->Get('security', 'openpgp', true) */ if (!$aParts) { $gEncryptedParts = $this->SearchByContentType('multipart/encrypted'); foreach ($gEncryptedParts as $oPart) { - if ($oPart->isPgpEncrypted() && $oPart->SubParts()[1]->isInline()) { + if ($oPart->isPgpEncrypted()) { return array($oPart->SubParts()[1]); } } @@ -366,7 +359,7 @@ class BodyStructure implements \JsonSerializable $sCharset = ''; // \MailSo\Base\Enumerations\Charset::UTF_8 ? $sContentID = ''; $sDescription = ''; - $sMailEncodingName = ''; + $sContentTransferEncoding = ''; $iSize = 0; $iExtraItemPos = 0; // list index of items which have no well-established position (such as 0, 1, 5, etc). @@ -462,10 +455,10 @@ class BodyStructure implements \JsonSerializable if (!\is_string($aBodyStructure[5])) { return null; } - $sMailEncodingName = $aBodyStructure[5]; + $sContentTransferEncoding = $aBodyStructure[5]; } - $iSize = \is_numeric($aBodyStructure[6]) ? (int) $aBodyStructure[6] : -1; + $iSize = \is_numeric($aBodyStructure[6]) ? (int) $aBodyStructure[6] : 0; if (!\strlen($sPartID) || '.' === $sPartID[\strlen($sPartID) - 1]) { // This is the only sub-part of the message (otherwise, it would be @@ -518,7 +511,7 @@ class BodyStructure implements \JsonSerializable $oStructure->sCharset = $sCharset; $oStructure->sContentID = \trim($sContentID); $oStructure->sDescription = $sDescription; - $oStructure->sMailEncodingName = \strtolower($sMailEncodingName); + $oStructure->sContentTransferEncoding = \strtolower($sContentTransferEncoding); $oStructure->sDisposition = \strtolower($sDisposition); $oStructure->sFileName = \MailSo\Base\Utils::Utf8Clear($sFileName); $oStructure->iSize = $iSize; diff --git a/snappymail/v/0.0.0/app/libraries/MailSo/Imap/Commands/Folders.php b/snappymail/v/0.0.0/app/libraries/MailSo/Imap/Commands/Folders.php index febfc7e82..7e410ff5f 100644 --- a/snappymail/v/0.0.0/app/libraries/MailSo/Imap/Commands/Folders.php +++ b/snappymail/v/0.0.0/app/libraries/MailSo/Imap/Commands/Folders.php @@ -82,7 +82,8 @@ trait Folders $this->SendRequestGetResponse('DELETE', [$this->EscapeFolderName($sFolderName)]); // $this->FolderCheck(); - // Will this workaround solve Dovecot issue #124 ? + // Will this workaround solve Dovecot issue? + // https://github.com/the-djmaze/snappymail/issues/124 try { $this->FolderRename($sFolderName, "{$sFolderName}-dummy"); $this->FolderRename("{$sFolderName}-dummy", $sFolderName); diff --git a/snappymail/v/0.0.0/app/libraries/MailSo/Imap/ImapClient.php b/snappymail/v/0.0.0/app/libraries/MailSo/Imap/ImapClient.php index c3b85a9c9..3a714114a 100644 --- a/snappymail/v/0.0.0/app/libraries/MailSo/Imap/ImapClient.php +++ b/snappymail/v/0.0.0/app/libraries/MailSo/Imap/ImapClient.php @@ -53,7 +53,7 @@ class ImapClient extends \MailSo\Net\NetClient public function Hash() : string { return \md5('ImapClientHash/'. - $this->Settings->Login . '@' . + $this->Settings->username . '@' . $this->Settings->host . ':' . $this->Settings->port ); @@ -111,26 +111,14 @@ class ImapClient extends \MailSo\Net\NetClient return $this; } - if (!empty($oSettings->ProxyAuthUser) && $oSettings->ProxyAuthPassword) { - $sLogin = $oSettings->ProxyAuthUser; - $sPassword = $oSettings->ProxyAuthPassword->getValue(); - $sProxyAuthUser = $oSettings->Login; - } else { - $sLogin = $oSettings->Login; - $sPassword = $oSettings->Password; - $sProxyAuthUser = ''; - } - - $sLogin = \MailSo\Base\Utils::IdnToAscii(\MailSo\Base\Utils::Trim($sLogin)); + $sLogin = $oSettings->username; + $sPassword = $oSettings->passphrase; if (!\strlen($sLogin) || !\strlen($sPassword)) { $this->writeLogException(new \UnexpectedValueException, \LOG_ERR); } $type = ''; - if ($this->Encrypted()) { - \array_unshift($oSettings->SASLMechanisms, 'PLAIN', 'LOGIN'); - } foreach ($oSettings->SASLMechanisms as $sasl_type) { if ($this->hasCapability("AUTH={$sasl_type}") && \SnappyMail\SASL::isSupported($sasl_type)) { $type = $sasl_type; @@ -225,9 +213,6 @@ class ImapClient extends \MailSo\Net\NetClient $this->setCapabilities($oResponse); - if (\strlen($sProxyAuthUser)) { - $this->SendRequestGetResponse('PROXYAUTH', array($this->EscapeString($sProxyAuthUser))); - } /* // TODO: RFC 9051 if ($this->hasCapability('IMAP4rev2')) { diff --git a/snappymail/v/0.0.0/app/libraries/MailSo/Mail/MailClient.php b/snappymail/v/0.0.0/app/libraries/MailSo/Mail/MailClient.php index 1f2c4aa07..0d7df0da4 100644 --- a/snappymail/v/0.0.0/app/libraries/MailSo/Mail/MailClient.php +++ b/snappymail/v/0.0.0/app/libraries/MailSo/Mail/MailClient.php @@ -127,7 +127,6 @@ class MailClient $this->oImapClient->FolderExamine($sFolderName); $oBodyStructure = null; - $oMessage = null; $aFetchItems = array( FetchType::UID, @@ -150,18 +149,6 @@ class MailClient } $aFetchItems[] = $sLine; } -/* - $gSignatureParts = $oBodyStructure->SearchByContentType('multipart/signed'); - foreach ($gSignatureParts as $oPart) { - if ($oPart->isPgpSigned()) { - // An empty section specification refers to the entire message, including the header. - // But Dovecot does not return it with BODY.PEEK[1], so we also use BODY.PEEK[1.MIME]. - $aFetchItems[] = FetchType::BODY_PEEK.'['.$oPart->SubParts()[0]->PartID().'.MIME]'; - $aFetchItems[] = FetchType::BODY_PEEK.'['.$oPart->SubParts()[0]->PartID().']'; - $aFetchItems[] = FetchType::BODY_PEEK.'['.$oPart->SubParts()[1]->PartID().']'; - } - } -*/ } } @@ -170,21 +157,10 @@ class MailClient } $aFetchResponse = $this->oImapClient->Fetch($aFetchItems, $iIndex, $bIndexIsUid); - if (\count($aFetchResponse)) { - $oMessage = Message::fromFetchResponse($sFolderName, $aFetchResponse[0], $oBodyStructure); - // S/MIME opaque signed. Verify it, so we have the raw mime body to show - if ($oMessage->smimeSigned && !$oMessage->smimeSigned['detached']) { - $sBody = $this->oImapClient->FetchMessagePart( - $oMessage->Uid, - $oMessage->smimeSigned['partId'] - ); - $result = (new \SnappyMail\SMime\OpenSSL(''))->verify($sBody, null, true); - $oMessage->smimeSigned['body'] = $result['body']; - $oMessage->smimeSigned['verified'] = true; - } - } - return $oMessage; + return \count($aFetchResponse) + ? Message::fromFetchResponse($sFolderName, $aFetchResponse[0], $oBodyStructure) + : null; } /** @@ -513,11 +489,11 @@ class MailClient $aFetchIterator = $this->oImapClient->FetchIterate($aFetchItems, (string) $oRange, $oRange->UID); // FETCH does not respond in the id order of the SequenceSet, so we prefill $aCollection for the right sort order. $aCollection = \array_fill_keys($oRange->getArrayCopy(), null); - foreach ($aFetchIterator as $oFetchResponseItem) { + foreach ($aFetchIterator as $oFetchResponse) { $id = $oRange->UID - ? $oFetchResponseItem->GetFetchValue(FetchType::UID) - : $oFetchResponseItem->oImapResponse->ResponseList[1]; - $oMessage = Message::fromFetchResponse($oMessageCollection->FolderName, $oFetchResponseItem); + ? $oFetchResponse->GetFetchValue(FetchType::UID) + : $oFetchResponse->oImapResponse->ResponseList[1]; + $oMessage = Message::fromFetchResponse($oMessageCollection->FolderName, $oFetchResponse); if ($oMessage) { if ($aAllThreads) { $iUid = $oMessage->Uid; @@ -958,7 +934,7 @@ class MailClient /** * @throws \InvalidArgumentException */ - public function FolderRename(string $sPrevFolderFullName, string $sNewFolderFullName, bool $bSubscribe = true) : self + public function FolderRename(string $sPrevFolderFullName, string $sNewFolderFullName) : self { if (!\strlen($sPrevFolderFullName) || !\strlen($sNewFolderFullName)) { throw new \ValueError; @@ -974,19 +950,21 @@ class MailClient throw new \MailSo\RuntimeException('New folder name contains delimiter.'); } */ - $oSubscribedFolders = array(); - if ($bSubscribe) { - $oSubscribedFolders = $this->oImapClient->FolderSubscribeList($sPrevFolderFullName, '*'); - foreach ($oSubscribedFolders as /* @var $oFolder \MailSo\Imap\Folder */ $oFolder) { - $this->oImapClient->FolderUnsubscribe($oFolder->FullName); - } - } + + /** + * https://datatracker.ietf.org/doc/html/rfc3501#section-6.3.5 + * Does not mention subscriptions + * https://datatracker.ietf.org/doc/html/rfc9051#section-6.3.6 + * Mentions that a server doesn't automatically manage subscriptions + */ + $oSubscribedFolders = $this->oImapClient->FolderSubscribeList($sPrevFolderFullName, '*'); $this->oImapClient->FolderRename($sPrevFolderFullName, $sNewFolderFullName); foreach ($oSubscribedFolders as /* @var $oFolder \MailSo\Imap\Folder */ $oFolder) { $sFolderFullNameForResubscribe = $oFolder->FullName; if (\str_starts_with($sFolderFullNameForResubscribe, $sPrevFolderFullName)) { + $this->oImapClient->FolderUnsubscribe($sFolderFullNameForResubscribe); $this->oImapClient->FolderSubscribe( $sNewFolderFullName . \substr($sFolderFullNameForResubscribe, \strlen($sPrevFolderFullName)) ); diff --git a/snappymail/v/0.0.0/app/libraries/MailSo/Mail/Message.php b/snappymail/v/0.0.0/app/libraries/MailSo/Mail/Message.php index bb26b9b17..9c234fd0d 100644 --- a/snappymail/v/0.0.0/app/libraries/MailSo/Mail/Message.php +++ b/snappymail/v/0.0.0/app/libraries/MailSo/Mail/Message.php @@ -66,7 +66,7 @@ class Message implements \JsonSerializable private ?array $DraftInfo = null; - private ?array $pgpSigned = null; + public ?array $pgpSigned = null; private ?array $pgpEncrypted = null; public ?array $smimeSigned = null; @@ -112,6 +112,16 @@ class Message implements \JsonSerializable return $this->Attachments; } + public function setPlain(string $value) : void + { + $this->sPlain = $value; + } + + public function setHtml(string $value) : void + { + $this->sHtml = $value; + } + private function setSpamScore($value) : void { $this->SpamScore = \intval(\max(0, \min(100, $value))); @@ -341,7 +351,7 @@ class Message implements \JsonSerializable $oMessage->pgpSigned = [ // /?/Raw/&q[]=/0/Download/&q[]=/... // /?/Raw/&q[]=/0/View/&q[]=/... - 'bodyPartId' => $oPart->SubParts()[0]->PartID(), + 'partId' => $oPart->SubParts()[0]->PartID(), 'sigPartId' => $oPart->SubParts()[1]->PartID(), 'micAlg' => $oHeaders ? (string) $oHeaders->ParameterValue(MimeHeader::CONTENT_TYPE, 'micalg') : '' ]; @@ -357,9 +367,9 @@ class Message implements \JsonSerializable // An empty section specification refers to the entire message, including the header. // But Dovecot does not return it with BODY.PEEK[1], so we also use BODY.PEEK[1.MIME]. $sPgpText = \trim( - \trim($oFetchResponse->GetFetchValue(FetchType::BODY.'['.$oMessage->pgpSigned['bodyPartId'].'.MIME]')) + \trim($oFetchResponse->GetFetchValue(FetchType::BODY.'['.$oMessage->pgpSigned['partId'].'.MIME]')) . "\r\n\r\n" - . \trim($oFetchResponse->GetFetchValue(FetchType::BODY.'['.$oMessage->pgpSigned['bodyPartId'].']')) + . \trim($oFetchResponse->GetFetchValue(FetchType::BODY.'['.$oMessage->pgpSigned['partId'].']')) ); if ($sPgpText) { $oMessage->pgpSigned['body'] = $sPgpText; @@ -387,7 +397,7 @@ class Message implements \JsonSerializable } if (\is_string($sText) && \strlen($sText)) { - $sText = Utils::DecodeEncodingValue($sText, $oPart->MailEncodingName()); + $sText = Utils::DecodeEncodingValue($sText, $oPart->ContentTransferEncoding()); $sText = Utils::ConvertEncoding($sText, Utils::NormalizeCharset($oPart->Charset() ?: $sCharset, true), \MailSo\Base\Enumerations\Charset::UTF_8 @@ -398,7 +408,7 @@ class Message implements \JsonSerializable // Cleartext Signature if (!$oMessage->pgpSigned && \str_contains($sText, '-----BEGIN PGP SIGNED MESSAGE-----')) { $oMessage->pgpSigned = [ - 'bodyPartId' => $oPart->PartID() + 'partId' => $oPart->PartID() ]; } diff --git a/snappymail/v/0.0.0/app/libraries/MailSo/Mime/Email.php b/snappymail/v/0.0.0/app/libraries/MailSo/Mime/Email.php index 66371f9c4..d254459a0 100644 --- a/snappymail/v/0.0.0/app/libraries/MailSo/Mime/Email.php +++ b/snappymail/v/0.0.0/app/libraries/MailSo/Mime/Email.php @@ -17,8 +17,14 @@ namespace MailSo\Mime; */ class Email implements \JsonSerializable { + /** + * display-name https://datatracker.ietf.org/doc/html/rfc2822#section-3.4 + */ private string $sDisplayName; + /** + * addr-spec https://datatracker.ietf.org/doc/html/rfc2822#section-3.4.1 + */ private string $sEmail; private string $sDkimStatus = Enumerations\DkimStatus::NONE; @@ -32,8 +38,7 @@ class Email implements \JsonSerializable throw new \ValueError; } - $this->sEmail = \MailSo\Base\Utils::IdnToAscii( - \MailSo\Base\Utils::Trim($sEmail), true); + $this->sEmail = \SnappyMail\IDN::emailToAscii(\MailSo\Base\Utils::Trim($sEmail)); $this->sDisplayName = \MailSo\Base\Utils::Trim($sDisplayName); } @@ -156,7 +161,7 @@ class Email implements \JsonSerializable public function GetEmail(bool $bIdn = false) : string { - return $bIdn ? \MailSo\Base\Utils::IdnToUtf8($this->sEmail) : $this->sEmail; + return $bIdn ? \SnappyMail\IDN::emailToUtf8($this->sEmail) : $this->sEmail; } public function GetDisplayName() : string @@ -164,14 +169,14 @@ class Email implements \JsonSerializable return $this->sDisplayName; } - public function GetAccountName() : string + public function getLocalPart() : string { - return \MailSo\Base\Utils::GetAccountNameFromEmail($this->GetEmail(false)); + return \MailSo\Base\Utils::getEmailAddressLocalPart($this->sEmail); } public function GetDomain(bool $bIdn = false) : string { - return \MailSo\Base\Utils::GetDomainFromEmail($this->GetEmail($bIdn)); + return \MailSo\Base\Utils::getEmailAddressDomain($this->GetEmail($bIdn)); } public function SetDkimStatus(string $sDkimStatus) diff --git a/snappymail/v/0.0.0/app/libraries/MailSo/Mime/Part.php b/snappymail/v/0.0.0/app/libraries/MailSo/Mime/Part.php index 655012df2..79b8e07ea 100644 --- a/snappymail/v/0.0.0/app/libraries/MailSo/Mime/Part.php +++ b/snappymail/v/0.0.0/app/libraries/MailSo/Mime/Part.php @@ -60,7 +60,7 @@ class Part Enumerations\Header::CONTENT_TYPE, Enumerations\Parameter::FORMAT))); - if ($bResult && \in_array($this->MailEncodingName(), array('base64', 'quoted-printable'))) { + if ($bResult && \in_array($this->ContentTransferEncoding(), array('base64', 'quoted-printable'))) { $bResult = false; } diff --git a/snappymail/v/0.0.0/app/libraries/MailSo/Net/ConnectSettings.php b/snappymail/v/0.0.0/app/libraries/MailSo/Net/ConnectSettings.php index f706b99e3..23bd3484b 100644 --- a/snappymail/v/0.0.0/app/libraries/MailSo/Net/ConnectSettings.php +++ b/snappymail/v/0.0.0/app/libraries/MailSo/Net/ConnectSettings.php @@ -35,9 +35,11 @@ class ConnectSettings implements \JsonSerializable public SSLContext $ssl; // public bool $tls_weak = false; - // Authentication settings use by all child classes + // Authentication settings used by all child classes public bool $useAuth = true; public bool $shortLogin = false; + public bool $lowerLogin = true; + public string $stripLogin = ''; public array $SASLMechanisms = [ // https://github.com/the-djmaze/snappymail/issues/182 'SCRAM-SHA3-512', @@ -48,10 +50,9 @@ class ConnectSettings implements \JsonSerializable 'PLAIN', 'LOGIN' ]; - public string $Login = ''; - private ?SensitiveString $Password = null; - public string $ProxyAuthUser = ''; - public ?SensitiveString $ProxyAuthPassword = null; + + private string $username = ''; + private ?SensitiveString $passphrase = null; public function __construct() { @@ -60,21 +61,53 @@ class ConnectSettings implements \JsonSerializable public function __get(string $name) { - if ('Password' === $name) { - return $this->Password ? $this->Password->getValue() : ''; + $name = \strtolower($name); + if ('passphrase' === $name || 'password' === $name) { + return $this->passphrase ? $this->passphrase->getValue() : ''; + } + if ('username' === $name || 'login' === $name) { + return $this->username; } } - public function __set(string $name, $value) - { - if ('Password' === $name) { - $this->Password = \is_string($value) ? new SensitiveString($value) : $value; + public function __set(string $name, + #[\SensitiveParameter] + $value + ) { + $name = \strtolower($name); + if ('passphrase' === $name || 'password' === $name) { + $this->passphrase = \is_string($value) ? new SensitiveString($value) : $value; + } + if ('username' === $name || 'login' === $name) { + $this->username = $this->fixUsername($value); } } - public static function Host() : string + public function fixUsername(string $value, bool $allowShorten = true) : string { - return \SnappyMail\IDN::toAscii($this->host); + $value = \SnappyMail\IDN::emailToAscii($value); +// $value = \SnappyMail\IDN::emailToAscii(\MailSo\Base\Utils::Trim($value)); + // Strip the domain part + if ($this->shortLogin && $allowShorten) { + $value = \MailSo\Base\Utils::getEmailAddressLocalPart($value); + } + // Convert to lowercase + if ($this->lowerLogin) { + $value = \mb_strtolower($value); + } + // Strip certain characters + if ($this->stripLogin) { + $value = \explode('@', $value); + if (isset($value[1])) { + $domain = \array_pop($value); + } + $value = \str_replace(\str_split($this->stripLogin), '', $value); + if (isset($value[1])) { + $value[] = $domain; + } + $value = \implode('@', $value); + } + return $value; } public static function fromArray(array $aSettings) : self @@ -87,6 +120,12 @@ class ConnectSettings implements \JsonSerializable $object->timeout = $aSettings['timeout']; } $object->shortLogin = !empty($aSettings['shortLogin']); + if (isset($aSettings['lowerLogin'])) { + $object->lowerLogin = !empty($aSettings['lowerLogin']); + } + if (isset($aSettings['stripLogin'])) { + $object->stripLogin = $aSettings['stripLogin']; + } $object->ssl = SSLContext::fromArray($aSettings['ssl'] ?? []); if (!empty($aSettings['sasl']) && \is_array($aSettings['sasl'])) { $object->SASLMechanisms = $aSettings['sasl']; @@ -105,6 +144,8 @@ class ConnectSettings implements \JsonSerializable 'type' => $this->type, 'timeout' => $this->timeout, 'shortLogin' => $this->shortLogin, + 'lowerLogin' => $this->lowerLogin, + 'stripLogin' => $this->stripLogin, 'sasl' => $this->SASLMechanisms, 'ssl' => $this->ssl // 'tls_weak' => $this->tls_weak diff --git a/snappymail/v/0.0.0/app/libraries/MailSo/Sieve/SieveClient.php b/snappymail/v/0.0.0/app/libraries/MailSo/Sieve/SieveClient.php index 669273225..35917fd24 100644 --- a/snappymail/v/0.0.0/app/libraries/MailSo/Sieve/SieveClient.php +++ b/snappymail/v/0.0.0/app/libraries/MailSo/Sieve/SieveClient.php @@ -87,8 +87,8 @@ class SieveClient extends \MailSo\Net\NetClient return $this; } - $sLogin = $oSettings->Login; - $sPassword = $oSettings->Password; + $sLogin = $oSettings->username; + $sPassword = $oSettings->passphrase; $sLoginAuthKey = ''; if (!\strlen($sLogin) || !\strlen($sPassword)) { $this->writeLogException(new \InvalidArgumentException, \LOG_ERR); diff --git a/snappymail/v/0.0.0/app/libraries/MailSo/Smtp/SmtpClient.php b/snappymail/v/0.0.0/app/libraries/MailSo/Smtp/SmtpClient.php index 0e3261cbb..f72de9d6c 100644 --- a/snappymail/v/0.0.0/app/libraries/MailSo/Smtp/SmtpClient.php +++ b/snappymail/v/0.0.0/app/libraries/MailSo/Smtp/SmtpClient.php @@ -15,6 +15,7 @@ namespace MailSo\Smtp; use MailSo\Net\Enumerations\ConnectionSecurityType; +use SnappyMail\IDN; /** * @category MailSo @@ -127,8 +128,8 @@ class SmtpClient extends \MailSo\Net\NetClient return $this; } - $sLogin = \MailSo\Base\Utils::IdnToAscii(\MailSo\Base\Utils::Trim($oSettings->Login)); - $sPassword = $oSettings->Password; + $sLogin = $oSettings->username; + $sPassword = $oSettings->passphrase; $type = ''; foreach ($oSettings->SASLMechanisms as $sasl_type) { @@ -227,9 +228,13 @@ class SmtpClient extends \MailSo\Net\NetClient */ public function MailFrom(string $sFrom, int $iSizeIfSupported = 0, bool $bDsn = false, bool $bRequireTLS = false) : self { - $sFrom = \MailSo\Base\Utils::IdnToAscii(\MailSo\Base\Utils::Trim($sFrom), true); - +// $sFrom = IDN::emailToAscii($sFrom); $sCmd = "FROM:<{$sFrom}>"; + // RFC 6531 + if ($this->hasCapability('SMTPUTF8')) { +// $sFrom = IDN::emailToUtf8($sFrom); +// $sCmd = "FROM:<{$sFrom}> SMTPUTF8"; + } if (0 < $iSizeIfSupported && $this->hasCapability('SIZE')) { $sCmd .= ' SIZE='.$iSizeIfSupported; @@ -243,18 +248,10 @@ class SmtpClient extends \MailSo\Net\NetClient // RFC 6152 if ($this->hasCapability('8BITMIME')) { // $sCmd .= ' BODY=8BITMIME'; - // RFC 6531 - if ($this->hasCapability('SMTPUTF8')) { -// $sCmd .= ' SMTPUTF8'; - } } // RFC 3030 else if ($this->hasCapability('BINARYMIME')) { // $sCmd .= ' BODY=BINARYMIME'; - // RFC 6531 - if ($this->hasCapability('SMTPUTF8')) { -// $sCmd .= ' SMTPUTF8'; - } } // RFC 8689 @@ -281,7 +278,7 @@ class SmtpClient extends \MailSo\Net\NetClient $this->writeLogException(new \MailSo\RuntimeException('No sender reverse path has been supplied'), \LOG_ERR); } - $sTo = \MailSo\Base\Utils::IdnToAscii(\MailSo\Base\Utils::Trim($sTo), true); +// $sTo = IDN::emailToAscii($sTo); $sCmd = 'TO:<'.$sTo.'>'; @@ -413,18 +410,16 @@ class SmtpClient extends \MailSo\Net\NetClient */ public function Vrfy(string $sUser) : self { + $sUser = \MailSo\Base\Utils::Trim($sUser); /* // RFC 6531 if ($this->hasCapability('SMTPUTF8')) { - $this->sendRequestWithCheck('VRFY ' - . \MailSo\Base\Utils::IdnToUtf8(\MailSo\Base\Utils::Trim($sUser)) - . ' SMTPUTF8', + $this->sendRequestWithCheck('VRFY ' . IDN::emailToUtf8($sUser) . ' SMTPUTF8', array(250, 251, 252), ); } else { */ - $this->sendRequestWithCheck( - 'VRFY ' . \MailSo\Base\Utils::IdnToAscii(\MailSo\Base\Utils::Trim($sUser)), + $this->sendRequestWithCheck('VRFY ' . IDN::emailToAscii($sUser), array(250, 251, 252) ); return $this; @@ -442,16 +437,14 @@ class SmtpClient extends \MailSo\Net\NetClient /* public function Expn(string $sUser) : self { + $sUser = \MailSo\Base\Utils::Trim($sUser); // RFC 6531 if ($this->hasCapability('SMTPUTF8')) { - $this->sendRequestWithCheck('EXPN ' - . \MailSo\Base\Utils::IdnToUtf8(\MailSo\Base\Utils::Trim($sUser)) - . ' SMTPUTF8', + $this->sendRequestWithCheck('EXPN ' . IDN::emailToUtf8($sUser) . ' SMTPUTF8', array(250, 251, 252) ); } else { - $this->sendRequestWithCheck('EXPN ' - . \MailSo\Base\Utils::IdnToAscii(\MailSo\Base\Utils::Trim($sUser)), + $this->sendRequestWithCheck('EXPN ' . IDN::emailToAscii($sUser), array(250, 251, 252), ); return $this; diff --git a/snappymail/v/0.0.0/app/libraries/RainLoop/Actions.php b/snappymail/v/0.0.0/app/libraries/RainLoop/Actions.php index 291f95ea2..26f73b49e 100644 --- a/snappymail/v/0.0.0/app/libraries/RainLoop/Actions.php +++ b/snappymail/v/0.0.0/app/libraries/RainLoop/Actions.php @@ -3,6 +3,7 @@ namespace RainLoop; use RainLoop\Enumerations\Capa; +use RainLoop\Enumerations\SignMeType; class Actions { @@ -100,11 +101,6 @@ class Actions */ protected $oLocalSettingsProvider = null; - /** - * @var \RainLoop\Providers\AddressBook - */ - protected $oAddressBookProvider = null; - /** * @var \RainLoop\Config\Application */ @@ -273,13 +269,14 @@ class Actions } if ($oAccount) { + $oDomain = $oAccount->Domain(); $sLine = \str_replace('{imap:login}', $oAccount->IncLogin(), $sLine); - $sLine = \str_replace('{imap:host}', $oAccount->Domain()->IncHost(), $sLine); - $sLine = \str_replace('{imap:port}', $oAccount->Domain()->IncPort(), $sLine); + $sLine = \str_replace('{imap:host}', $oDomain->ImapSettings()->host, $sLine); + $sLine = \str_replace('{imap:port}', $oDomain->ImapSettings()->port, $sLine); $sLine = \str_replace('{smtp:login}', $oAccount->OutLogin(), $sLine); - $sLine = \str_replace('{smtp:host}', $oAccount->Domain()->OutHost(), $sLine); - $sLine = \str_replace('{smtp:port}', $oAccount->Domain()->OutPort(), $sLine); + $sLine = \str_replace('{smtp:host}', $oDomain->SmtpSettings()->host, $sLine); + $sLine = \str_replace('{smtp:port}', $oDomain->SmtpSettings()->port, $sLine); } $aClear['/\{imap:([^}]*)\}/i'] = 'imap'; @@ -329,10 +326,10 @@ class Actions $sEmail = $oAccount->Email(); $sLine = \str_replace('{user:email}', $sEmail, $sLine); - $sLine = \str_replace('{user:login}', \MailSo\Base\Utils::GetAccountNameFromEmail($sEmail), $sLine); - $sLine = \str_replace('{user:domain}', \MailSo\Base\Utils::GetDomainFromEmail($sEmail), $sLine); + $sLine = \str_replace('{user:login}', \MailSo\Base\Utils::getEmailAddressLocalPart($sEmail), $sLine); + $sLine = \str_replace('{user:domain}', \MailSo\Base\Utils::getEmailAddressDomain($sEmail), $sLine); $sLine = \str_replace('{user:domain-clear}', - \MailSo\Base\Utils::GetClearDomainName(\MailSo\Base\Utils::GetDomainFromEmail($sEmail)), + \MailSo\Base\Utils::GetClearDomainName(\MailSo\Base\Utils::getEmailAddressDomain($sEmail)), $sLine); } } @@ -467,31 +464,6 @@ class Actions return $this->oDomainProvider; } - public function AddressBookProvider(?Model\Account $oAccount = null): Providers\AddressBook - { - if (null === $this->oAddressBookProvider) { - $oDriver = null; - try { -// if ($this->oConfig->Get('contacts', 'enable', false)) { - if ($this->GetCapa(Capa::CONTACTS)) { - $oDriver = $this->fabrica('address-book', $oAccount); - } - if ($oAccount && $oDriver) { - $oDriver->SetEmail($this->GetMainEmail($oAccount)); - $oDriver->setDAVClientConfig($this->getContactsSyncData($oAccount)); - } - } catch (\Throwable $e) { - \SnappyMail\LOG::error('AddressBook', $e->getMessage()."\n".$e->getTraceAsString()); - $oDriver = null; -// $oDriver = new Providers\AddressBook\PdoAddressBook(); - } - $this->oAddressBookProvider = new Providers\AddressBook($oDriver); - $this->oAddressBookProvider->SetLogger($this->oLogger); - } - - return $this->oAddressBookProvider; - } - public function Cacher(?Model\Account $oAccount = null, bool $bForceFile = false): \MailSo\Cache\CacheClient { $sKey = ''; @@ -561,33 +533,42 @@ class Actions return $this->oPlugins; } - public function LoggerAuth(): \MailSo\Log\Logger + protected function LoggerAuthHelper(?Model\Account $oAccount, string $sLogin = '', bool $admin = false): void { - if (!$this->oLoggerAuth) { - $this->oLoggerAuth = new \MailSo\Log\Logger(false); - if ($this->oConfig->Get('logs', 'auth_logging', false)) { -// $this->oLoggerAuth->SetLevel(\LOG_WARNING); - - $sAuthLogFileFullPath = (\trim($this->oConfig->Get('logs', 'path', '') ?: \APP_PRIVATE_DATA . 'logs')) - . '/' . $this->compileLogFileName($this->oConfig->Get('logs', 'auth_logging_filename', '')); - $sLogFileDir = \dirname($sAuthLogFileFullPath); - \is_dir($sLogFileDir) || \mkdir($sLogFileDir, 0755, true); - $this->oLoggerAuth->append( - (new \MailSo\Log\Drivers\File($sAuthLogFileFullPath)) - ->DisableTimePrefix() - ->DisableGuidPrefix() - ->DisableTypedPrefix() - ); - } + if ($sLogin) { + $sHost = $admin ? $this->Http()->GetHost(true, true) : \MailSo\Base\Utils::getEmailAddressDomain($sLogin); + $aAdditionalParams = array( + '{imap:login}' => $sLogin, + '{imap:host}' => $sHost, + '{smtp:login}' => $sLogin, + '{smtp:host}' => $sHost, + '{user:email}' => $sLogin, + '{user:login}' => $admin ? $sLogin : \MailSo\Base\Utils::getEmailAddressLocalPart($sLogin), + '{user:domain}' => $sHost, + ); + } else { + $aAdditionalParams = array(); } - return $this->oLoggerAuth; - } - - protected function LoggerAuthHelper(?Model\Account $oAccount = null, array $aAdditionalParams = array(), bool $admin = false): void - { $sLine = $this->oConfig->Get('logs', 'auth_logging_format', ''); if (!empty($sLine)) { - $this->LoggerAuth()->Write($this->compileLogParams($sLine, $oAccount, $aAdditionalParams), \LOG_WARNING); + if (!$this->oLoggerAuth) { + $this->oLoggerAuth = new \MailSo\Log\Logger(false); + if ($this->oConfig->Get('logs', 'auth_logging', false)) { +// $this->oLoggerAuth->SetLevel(\LOG_WARNING); + + $sAuthLogFileFullPath = (\trim($this->oConfig->Get('logs', 'path', '') ?: \APP_PRIVATE_DATA . 'logs')) + . '/' . $this->compileLogFileName($this->oConfig->Get('logs', 'auth_logging_filename', '')); + $sLogFileDir = \dirname($sAuthLogFileFullPath); + \is_dir($sLogFileDir) || \mkdir($sLogFileDir, 0755, true); + $this->oLoggerAuth->append( + (new \MailSo\Log\Drivers\File($sAuthLogFileFullPath)) + ->DisableTimePrefix() + ->DisableGuidPrefix() + ->DisableTypedPrefix() + ); + } + } + $this->oLoggerAuth->Write($this->compileLogParams($sLine, $oAccount, $aAdditionalParams), \LOG_WARNING); } if (($this->oConfig->Get('logs', 'auth_logging', false) || $this->oConfig->Get('logs', 'auth_syslog', false)) && \openlog('snappymail', 0, \LOG_AUTHPRIV)) { @@ -609,109 +590,58 @@ class Actions 'title' => $oConfig->Get('webmail', 'title', 'SnappyMail Webmail'), 'loadingDescription' => $oConfig->Get('webmail', 'loading_description', 'SnappyMail'), 'Plugins' => array(), - 'System' => \array_merge( - array( - 'version' => APP_VERSION, - 'token' => Utils::GetCsrfToken(), - 'languages' => \SnappyMail\L10n::getLanguages(false), - 'webPath' => \RainLoop\Utils::WebPath(), - 'webVersionPath' => \RainLoop\Utils::WebVersionPath() - ), $bAdmin ? array( - 'adminHost' => '' !== $oConfig->Get('security', 'admin_panel_host', ''), - 'adminPath' => $oConfig->Get('security', 'admin_panel_key', '') ?: 'admin', - 'adminAllowed' => (bool)$oConfig->Get('security', 'allow_admin_panel', true) - ) : array() + 'System' => array( + 'version' => APP_VERSION, + 'token' => Utils::GetCsrfToken(), + 'languages' => \SnappyMail\L10n::getLanguages(false), + 'webPath' => \RainLoop\Utils::WebPath(), + 'webVersionPath' => \RainLoop\Utils::WebVersionPath() ), 'allowLanguagesOnLogin' => (bool) $oConfig->Get('login', 'allow_languages_on_login', true) ); - $UserLanguageRaw = $this->detectUserLanguage($bAdmin); - if ($bAdmin) { -// $this->AdminAppData($aResult); - $aResult['Auth'] = $this->IsAdminLoggined(false); - if ($aResult['Auth']) { - $aResult['adminLogin'] = (string)$oConfig->Get('security', 'admin_login', ''); - $aResult['adminTOTP'] = (string)$oConfig->Get('security', 'admin_totp', ''); - $aResult['pluginsEnable'] = (bool)$oConfig->Get('plugins', 'enable', false); - - $aResult['loginDefaultDomain'] = $oConfig->Get('login', 'default_domain', ''); - $aResult['determineUserLanguage'] = (bool)$oConfig->Get('login', 'determine_user_language', true); - $aResult['determineUserDomain'] = (bool)$oConfig->Get('login', 'determine_user_domain', false); - - $aResult['supportedPdoDrivers'] = \RainLoop\Pdo\Base::getAvailableDrivers(); - - $aResult['contactsEnable'] = (bool)$oConfig->Get('contacts', 'enable', false); - $aResult['contactsSync'] = (bool)$oConfig->Get('contacts', 'allow_sync', false); - $aResult['contactsPdoType'] = Providers\AddressBook\PdoAddressBook::validPdoType($oConfig->Get('contacts', 'type', 'sqlite')); - $aResult['contactsPdoDsn'] = (string)$oConfig->Get('contacts', 'pdo_dsn', ''); - $aResult['contactsPdoType'] = (string)$oConfig->Get('contacts', 'type', ''); - $aResult['contactsPdoUser'] = (string)$oConfig->Get('contacts', 'pdo_user', ''); - $aResult['contactsPdoPassword'] = static::APP_DUMMY; - $aResult['contactsMySQLSSLCA'] = (string) $oConfig->Get('contacts', 'mysql_ssl_ca', ''); - $aResult['contactsMySQLSSLVerify'] = !!$oConfig->Get('contacts', 'mysql_ssl_verify', true); - $aResult['contactsMySQLSSLCiphers'] = (string) $oConfig->Get('contacts', 'mysql_ssl_ciphers', ''); - $aResult['contactsSQLiteGlobal'] = !!$oConfig->Get('contacts', 'sqlite_global', \is_file(APP_PRIVATE_DATA . '/AddressBook.sqlite')); - $aResult['contactsSuggestionsLimit'] = (int)$oConfig->Get('contacts', 'suggestions_limit', 20); - - $aResult['faviconUrl'] = $oConfig->Get('webmail', 'favicon_url', ''); - - $aResult['weakPassword'] = \is_file(APP_PRIVATE_DATA.'admin_password.txt'); - - $aResult['System']['languagesAdmin'] = \SnappyMail\L10n::getLanguages(true); - $aResult['languageAdmin'] = $this->ValidateLanguage($oConfig->Get('webmail', 'language_admin', 'en'), '', true); - $aResult['languageUsers'] = $this->ValidateLanguage($UserLanguageRaw, '', true, true); - } else { - $passfile = APP_PRIVATE_DATA.'admin_password.txt'; - $sPassword = $oConfig->Get('security', 'admin_password', ''); - if (!$sPassword) { - $sPassword = \substr(\base64_encode(\random_bytes(16)), 0, 12); - Utils::saveFile($passfile, $sPassword . "\n"); -// \chmod($passfile, 0600); - $oConfig->SetPassword($sPassword); - $oConfig->Save(); - } - } + ActionsAdmin::AdminAppData($this, $aResult); } else { $oAccount = $this->getAccountFromToken(false); if ($oAccount) { - $aResult = \array_merge($aResult, [ - 'Auth' => true, - 'Email' => \MailSo\Base\Utils::IdnToUtf8($oAccount->Email()), - 'accountHash' => $oAccount->Hash(), - 'accountSignMe' => isset($_COOKIE[self::AUTH_SIGN_ME_TOKEN_KEY]), - - 'contactsAllowed' => $this->AddressBookProvider($oAccount)->IsActive(), - - 'allowSpellcheck' => $oConfig->Get('defaults', 'allow_spellcheck', false), - 'ViewHTML' => (bool) $oConfig->Get('defaults', 'view_html', true), - 'ViewImages' => $oConfig->Get('defaults', 'view_images', 'ask'), - 'ViewImagesWhitelist' => '', - 'RemoveColors' => (bool) $oConfig->Get('defaults', 'remove_colors', false), - 'AllowStyles' => false, - 'ListInlineAttachments' => false, - 'CollapseBlockquotes' => $oConfig->Get('defaults', 'collapse_blockquotes', true), - 'MaxBlockquotesLevel' => 0, - 'simpleAttachmentsList' => false, - 'listGrouped' => $oConfig->Get('defaults', 'mail_list_grouped', false), - 'MessagesPerPage' => (int) $oConfig->Get('webmail', 'messages_per_page', 25), - 'messageNewWindow' => false, - 'messageReadAuto' => true, // (bool) $oConfig->Get('webmail', 'message_read_auto', true), - 'MessageReadDelay' => (int) $oConfig->Get('webmail', 'message_read_delay', 5), - 'MsgDefaultAction' => (int) $oConfig->Get('defaults', 'msg_default_action', 1), - 'SoundNotification' => true, - 'NotificationSound' => 'new-mail', - 'DesktopNotifications' => true, - 'Layout' => (int) $oConfig->Get('defaults', 'view_layout', Enumerations\Layout::SIDE_PREVIEW->value), - 'EditorDefaultType' => \str_replace('Forced', '', $oConfig->Get('defaults', 'view_editor_type', '')), - 'editorWysiwyg' => 'Squire', - 'UseCheckboxesInList' => (bool) $oConfig->Get('defaults', 'view_use_checkboxes', true), - 'showNextMessage' => (bool) $oConfig->Get('defaults', 'view_show_next_message', false), - 'AutoLogout' => (int) $oConfig->Get('defaults', 'autologout', 30), - 'AllowDraftAutosave' => (bool) $oConfig->Get('defaults', 'allow_draft_autosave', true), - 'ContactsAutosave' => (bool) $oConfig->Get('defaults', 'contacts_autosave', true), - 'sieveAllowFileintoInbox' => (bool)$oConfig->Get('labs', 'sieve_allow_fileinto_inbox', false) - ]); + $aResult = \array_merge( + $aResult, + [ + 'Auth' => true, + 'accountSignMe' => isset($_COOKIE[self::AUTH_SIGN_ME_TOKEN_KEY]), + 'allowSpellcheck' => $oConfig->Get('defaults', 'allow_spellcheck', false), + 'ViewHTML' => (bool) $oConfig->Get('defaults', 'view_html', true), + 'ViewImages' => $oConfig->Get('defaults', 'view_images', 'ask'), + 'ViewImagesWhitelist' => '', + 'RemoveColors' => (bool) $oConfig->Get('defaults', 'remove_colors', false), + 'AllowStyles' => false, + 'ListInlineAttachments' => false, + 'CollapseBlockquotes' => $oConfig->Get('defaults', 'collapse_blockquotes', true), + 'MaxBlockquotesLevel' => 0, + 'simpleAttachmentsList' => false, + 'listGrouped' => $oConfig->Get('defaults', 'mail_list_grouped', false), + 'MessagesPerPage' => (int) $oConfig->Get('webmail', 'messages_per_page', 25), + 'messageNewWindow' => false, + 'messageReadAuto' => true, // (bool) $oConfig->Get('webmail', 'message_read_auto', true), + 'MessageReadDelay' => (int) $oConfig->Get('webmail', 'message_read_delay', 5), + 'MsgDefaultAction' => (int) $oConfig->Get('defaults', 'msg_default_action', 1), + 'SoundNotification' => true, + 'NotificationSound' => 'new-mail', + 'DesktopNotifications' => true, + 'Layout' => (int) $oConfig->Get('defaults', 'view_layout', Enumerations\Layout::SIDE_PREVIEW->value), + 'EditorDefaultType' => \str_replace('Forced', '', $oConfig->Get('defaults', 'view_editor_type', '')), + 'editorWysiwyg' => 'Squire', + 'UseCheckboxesInList' => (bool) $oConfig->Get('defaults', 'view_use_checkboxes', true), + 'showNextMessage' => (bool) $oConfig->Get('defaults', 'view_show_next_message', false), + 'AutoLogout' => (int) $oConfig->Get('defaults', 'autologout', 30), + 'AllowDraftAutosave' => (bool) $oConfig->Get('defaults', 'allow_draft_autosave', true), + 'ContactsAutosave' => (bool) $oConfig->Get('defaults', 'contacts_autosave', true), + 'sieveAllowFileintoInbox' => (bool)$oConfig->Get('labs', 'sieve_allow_fileinto_inbox', false) + ], + // MainAccount or AdditionalAccount + $this->getAccountData($oAccount) + ); $aAttachmentsActions = array(); if ($this->GetCapa(Capa::ATTACHMENTS_ACTIONS)) { @@ -747,13 +677,10 @@ class Actions $mMailToData = Utils::DecodeKeyValuesQ($sToken); if (!empty($mMailToData['MailTo']) && 'MailTo' === $mMailToData['MailTo'] && !empty($mMailToData['To'])) { - $aResult['mailToEmail'] = \MailSo\Base\Utils::IdnToUtf8($mMailToData['To']); + $aResult['mailToEmail'] = \SnappyMail\IDN::emailToUtf8($mMailToData['To']); } } - // MainAccount or AdditionalAccount - $aResult = \array_merge($aResult, $this->getAccountData($oAccount)); - // MainAccount $oSettings = $this->SettingsProvider()->Load($oAccount); if ($oSettings instanceof Settings) { @@ -833,13 +760,17 @@ class Actions $aResult['DevEmail'] = ''; $aResult['DevPassword'] = ''; } - - $aResult['signMe'] = (string) $oConfig->Get('login', 'sign_me_auto', Enumerations\SignMeType::DEFAULT_OFF->value); + $aResult['signMe'] = [ + SignMeType::DefaultOff => 0, + SignMeType::DefaultOn => 1, + SignMeType::Unused => 2 + ][(string) $oConfig->Get('login', 'sign_me_auto', SignMeType::DefaultOff->value)]; } } if ($aResult['Auth']) { $aResult['useLocalProxyForExternalImages'] = (bool)$oConfig->Get('labs', 'use_local_proxy_for_external_images', false); + $aResult['autoVerifySignatures'] = (bool)$oConfig->Get('security', 'auto_verify_signatures', false); $aResult['allowLanguagesOnSettings'] = (bool) $oConfig->Get('webmail', 'allow_languages_on_settings', true); $aResult['Capa'] = $this->Capa($bAdmin, $oAccount); $value = \ini_get('upload_max_filesize'); @@ -860,7 +791,7 @@ class Actions $aResult['Theme'] = $this->GetTheme($bAdmin); $aResult['language'] = $this->GetLanguage(); - $aResult['userLanguage'] = $this->ValidateLanguage($UserLanguageRaw, '', false, true); + $aResult['clientLanguage'] = $this->ValidateLanguage($this->detectClientLanguage($bAdmin), '', false, true); $aResult['PluginsLink'] = $this->oPlugins->HaveJs($bAdmin) ? 'Plugins/0/' . ($bAdmin ? 'Admin' : 'User') . '/' . $this->etag($this->oPlugins->Hash()) . '/' @@ -888,20 +819,6 @@ class Actions } } - protected function getAdditionalLogParamsByUserLogin(string $sLogin, bool $bAdmin = false): array - { - $sHost = $bAdmin ? $this->Http()->GetHost(true, true) : \MailSo\Base\Utils::GetDomainFromEmail($sLogin); - return array( - '{imap:login}' => $sLogin, - '{imap:host}' => $sHost, - '{smtp:login}' => $sLogin, - '{smtp:host}' => $sHost, - '{user:email}' => $sLogin, - '{user:login}' => $bAdmin ? $sLogin : \MailSo\Base\Utils::GetAccountNameFromEmail($sLogin), - '{user:domain}' => $sHost, - ); - } - public function DoPing(): array { return $this->DefaultResponse('Pong'); @@ -912,19 +829,6 @@ class Actions return $this->DefaultResponse(APP_VERSION === (string)$this->GetActionParam('version', '')); } - public function MainClearFileName(string $sFileName, string $sContentType, string $sMimeIndex, int $iMaxLength = 250): string - { - $sFileName = !\strlen($sFileName) ? \preg_replace('/[^a-zA-Z0-9]/', '.', (empty($sMimeIndex) ? '' : $sMimeIndex . '.') . $sContentType) : $sFileName; - $sClearedFileName = \MailSo\Base\Utils::StripSpaces(\preg_replace('/[\.]+/', '.', $sFileName)); - $sExt = \MailSo\Base\Utils::GetFileExtension($sClearedFileName); - - if (10 < $iMaxLength && $iMaxLength < \strlen($sClearedFileName) - \strlen($sExt)) { - $sClearedFileName = \substr($sClearedFileName, 0, $iMaxLength) . (empty($sExt) ? '' : '.' . $sExt); - } - - return \MailSo\Base\Utils::SecureFileName(\MailSo\Base\Utils::Utf8Clear($sClearedFileName)); - } - public function Upload(?array $aFile, int $iError): array { $oAccount = $this->getAccountFromToken(); @@ -983,16 +887,16 @@ class Actions Capa::ATTACHMENTS_ACTIONS => (bool) $oConfig->Get('capa', 'attachments_actions', false), Capa::CONTACTS => (bool) $oConfig->Get('contacts', 'enable', false), Capa::DANGEROUS_ACTIONS => (bool) $oConfig->Get('capa', 'dangerous_actions', true), - Capa::GNUPG => (bool) $oConfig->Get('security', 'openpgp', false) && \SnappyMail\PGP\GnuPG::isSupported(), + Capa::GNUPG => (bool) $oConfig->Get('security', 'gnupg', true) && \SnappyMail\PGP\GnuPG::isSupported(), Capa::IDENTITIES => (bool) $oConfig->Get('webmail', 'allow_additional_identities', false), - Capa::OPENPGP => (bool) $oConfig->Get('security', 'openpgp', false), + Capa::OPENPGP => (bool) $oConfig->Get('security', 'openpgp', true), Capa::SIEVE => false, Capa::THEMES => (bool) $oConfig->Get('webmail', 'allow_themes', false), Capa::USER_BACKGROUND => (bool) $oConfig->Get('webmail', 'allow_user_background', false), 'Kolab' => false, // See Kolab plugin ); } - $aResult[Capa::SIEVE] = $bAdmin || ($oAccount && $oAccount->Domain()->UseSieve()); + $aResult[Capa::SIEVE] = $bAdmin || ($oAccount && $oAccount->Domain()->SieveSettings()->enabled); return $aResult; } diff --git a/snappymail/v/0.0.0/app/libraries/RainLoop/Actions/Accounts.php b/snappymail/v/0.0.0/app/libraries/RainLoop/Actions/Accounts.php index 1fd3d2003..1eb62f8aa 100644 --- a/snappymail/v/0.0.0/app/libraries/RainLoop/Actions/Accounts.php +++ b/snappymail/v/0.0.0/app/libraries/RainLoop/Actions/Accounts.php @@ -12,6 +12,7 @@ use RainLoop\Notifications; use RainLoop\Providers\Identities; use RainLoop\Providers\Storage\Enumerations\StorageType; use RainLoop\Utils; +use SnappyMail\IDN; trait Accounts { @@ -85,7 +86,7 @@ trait Accounts $sName = \trim($this->GetActionParam('name', '')); $bNew = !empty($this->GetActionParam('new', 1)); - $sEmail = \MailSo\Base\Utils::IdnToAscii($sEmail, true); + $sEmail = IDN::emailToAscii($sEmail); if ($bNew && ($oMainAccount->Email() === $sEmail || isset($aAccounts[$sEmail]))) { throw new ClientException(Notifications::AccountAlreadyExists); } else if (!$bNew && !isset($aAccounts[$sEmail])) { @@ -109,7 +110,7 @@ trait Accounts protected function loadAdditionalAccountImapClient(string $sEmail): \MailSo\Imap\ImapClient { - $sEmail = \MailSo\Base\Utils::IdnToAscii(\trim($sEmail), true); + $sEmail = IDN::emailToAscii($sEmail); if (!\strlen($sEmail)) { throw new ClientException(Notifications::AccountDoesNotExist); } @@ -173,7 +174,7 @@ trait Accounts } $sEmailToDelete = \trim($this->GetActionParam('emailToDelete', '')); - $sEmailToDelete = \MailSo\Base\Utils::IdnToAscii($sEmailToDelete, true); + $sEmailToDelete = IDN::emailToAscii($sEmailToDelete); $aAccounts = $this->GetAccounts($oMainAccount); @@ -198,6 +199,7 @@ trait Accounts { $oConfig = $this->Config(); $aResult = [ +// 'Email' => IDN::emailToUtf8($oAccount->Email()), 'Email' => $oAccount->Email(), 'accountHash' => $oAccount->Hash(), 'mainEmail' => \RainLoop\Api::Actions()->getMainAccountFromToken()->Email(), @@ -330,7 +332,7 @@ trait Accounts return $this->DefaultResponse(array( 'Accounts' => \array_values(\array_map(function($value){ return [ - 'email' => \MailSo\Base\Utils::IdnToUtf8($value['email'] ?? $value[1]), + 'email' => IDN::emailToUtf8($value['email'] ?? $value[1]), 'name' => $value['name'] ?? '' ]; }, diff --git a/snappymail/v/0.0.0/app/libraries/RainLoop/Actions/Admin.php b/snappymail/v/0.0.0/app/libraries/RainLoop/Actions/Admin.php index 7e31d78b1..970703a77 100644 --- a/snappymail/v/0.0.0/app/libraries/RainLoop/Actions/Admin.php +++ b/snappymail/v/0.0.0/app/libraries/RainLoop/Actions/Admin.php @@ -15,7 +15,7 @@ trait Admin { if ($this->Config()->Get('security', 'allow_admin_panel', true)) { $sAdminKey = $this->getAdminAuthKey(); - if ($sAdminKey && '' !== $this->Cacher(null, true)->Get(KeyPathHelper::SessionAdminKey($sAdminKey), '')) { + if ($sAdminKey && $this->Cacher(null, true)->Get(KeyPathHelper::SessionAdminKey($sAdminKey))) { return true; } } diff --git a/snappymail/v/0.0.0/app/libraries/RainLoop/Actions/AdminDomains.php b/snappymail/v/0.0.0/app/libraries/RainLoop/Actions/AdminDomains.php index c917f3bbf..a86653dd4 100644 --- a/snappymail/v/0.0.0/app/libraries/RainLoop/Actions/AdminDomains.php +++ b/snappymail/v/0.0.0/app/libraries/RainLoop/Actions/AdminDomains.php @@ -10,8 +10,13 @@ trait AdminDomains public function DoAdminDomainLoad() : array { $this->IsAdminLoggined(); - - return $this->DefaultResponse($this->DomainProvider()->Load($this->GetActionParam('name', ''), false, false)); + $mResult = false; + $oDomain = $this->DomainProvider()->Load($this->GetActionParam('name', ''), false, false); + if ($oDomain) { + $mResult = $oDomain->jsonSerialize(); + $mResult['name'] = $oDomain->Name(); + } + return $this->DefaultResponse($mResult); } public function DoAdminDomainList() : array @@ -24,8 +29,7 @@ trait AdminDomains public function DoAdminDomainDelete() : array { $this->IsAdminLoggined(); - - return $this->DefaultResponse($this->DomainProvider()->Delete((string) $this->GetActionParam('name', ''))); + return $this->DefaultResponse($this->DomainProvider()->Delete($this->GetActionParam('name', ''))); } public function DoAdminDomainDisable() : array @@ -64,7 +68,7 @@ trait AdminDomains $sLogin = ''; $this->resolveLoginCredentials($sEmail, $oPassword, $sLogin); $oDomain = \str_contains($sEmail, '@') - ? $this->DomainProvider()->Load(\MailSo\Base\Utils::GetDomainFromEmail($sEmail), true) + ? $this->DomainProvider()->Load(\MailSo\Base\Utils::getEmailAddressDomain($sEmail), true) : null; return $this->DefaultResponse(array( 'email' => $sEmail, @@ -77,7 +81,8 @@ trait AdminDomains public function DoAdminDomainAutoconfig() : array { $this->IsAdminLoggined(); - $sDomain = $this->GetActionParam('domain'); +// $sDomain = \SnappyMail\IDN::toAscii($this->GetActionParam('domain')); + $sDomain = \strtolower(\idn_to_ascii($this->GetActionParam('domain'))); $sEmail = "test@{$sDomain}"; return $this->DefaultResponse(array( 'email' => $sEmail, @@ -111,8 +116,8 @@ trait AdminDomains 'connectCapa' => \array_values(\array_diff($oImapClient->Capabilities(), ['STARTTLS'])) ]; if (!empty($aAuth['user'])) { - $oSettings->Login = $aAuth['user']; - $oSettings->Password = $aAuth['pass']; + $oSettings->username = $aAuth['user']; + $oSettings->passphrase = $aAuth['pass']; $oImapClient->Login($oSettings); $mImapResult['authCapa'] = \array_values(\array_unique(\array_map(function($n){ return \str_starts_with($n, 'THREAD=') ? 'THREAD' : $n; @@ -137,7 +142,7 @@ trait AdminDomains $sImapErrorDesc = $oException->getMessage(); } - if ($oDomain->OutUsePhpMail()) { + if ($oDomain->SmtpSettings()->usePhpMail) { $mSmtpResult = \MailSo\Base\Utils::FunctionCallable('mail'); if (!$mSmtpResult) { $sSmtpErrorDesc = 'PHP: mail() function is undefined'; @@ -156,8 +161,8 @@ trait AdminDomains ]; if (!empty($aAuth['user'])) { - $oSettings->Login = $aAuth['user']; - $oSettings->Password = $aAuth['pass']; + $oSettings->username = $aAuth['user']; + $oSettings->passphrase = $aAuth['pass']; $oSmtpClient->Login($oSettings); $mSmtpResult['authCapa'] = $oSmtpClient->Capability(); } @@ -179,7 +184,7 @@ trait AdminDomains } } - if ($oDomain->UseSieve()) { + if ($oDomain->SieveSettings()->enabled) { try { $oSieveClient = new \MailSo\Sieve\SieveClient(); @@ -192,8 +197,8 @@ trait AdminDomains ]; if (!empty($aAuth['user'])) { - $oSettings->Login = $aAuth['user']; - $oSettings->Password = $aAuth['pass']; + $oSettings->username = $aAuth['user']; + $oSettings->passphrase = $aAuth['pass']; $oSieveClient->Login($oSettings); $mSieveResult['authCapa'] = $oSieveClient->Capability(); } diff --git a/snappymail/v/0.0.0/app/libraries/RainLoop/Actions/Attachments.php b/snappymail/v/0.0.0/app/libraries/RainLoop/Actions/Attachments.php index 8647b6a0d..b8f2d2691 100644 --- a/snappymail/v/0.0.0/app/libraries/RainLoop/Actions/Attachments.php +++ b/snappymail/v/0.0.0/app/libraries/RainLoop/Actions/Attachments.php @@ -30,7 +30,7 @@ trait Attachments if (\is_array($aHashes) && \count($aHashes)) { foreach ($aHashes as $sZipHash) { $aResult = $this->getMimeFileByHash($oAccount, $sZipHash); - if (empty($aResult['fileHash'])) { + if (empty($aResult['data']) && empty($aResult['fileHash'])) { $bError = true; break; } @@ -60,10 +60,16 @@ trait Attachments $oZip->open($sZipFileName, \ZIPARCHIVE::CREATE | \ZIPARCHIVE::OVERWRITE); $oZip->setArchiveComment('SnappyMail/'.APP_VERSION); foreach ($aData as $aItem) { - $sFullFileNameHash = $oFilesProvider->GetFileName($oAccount, $aItem['fileHash']); $sFileName = ($mUIDs ? "{$aItem['uid']}/" : ($sFolder ? "{$aItem['uid']}-" : '')) . $aItem['fileName']; - if (!$oZip->addFile($sFullFileNameHash, $sFileName)) { - $bError = true; + if ($aItem['data']) { + if (!$oZip->addFromString($sFileName, $aItem['data'])) { + $bError = true; + } + } else { + $sFullFileNameHash = $oFilesProvider->GetFileName($oAccount, $aItem['fileHash']); + if (!$oZip->addFile($sFullFileNameHash, $sFileName)) { + $bError = true; + } } } @@ -92,20 +98,22 @@ trait Attachments $oZip = new \PharData($sZipFileName . '.zip', 0, null, \Phar::ZIP); $oZip->compressFiles(\Phar::GZ); foreach ($aData as $aItem) { - $oZip->addFile( - $oFilesProvider->GetFileName($oAccount, $aItem['fileHash']), - ($mUIDs ? "{$aItem['uid']}/" : ($sFolder ? "{$aItem['uid']}-" : '')) . $aItem['fileName'] - ); + if ($aItem['data']) { + if (!$oZip->addFromString($sFileName, \MailSo\Base\Utils::UrlSafeBase64Decode($aItem['data']))) { + $bError = true; + } + } else { + $oZip->addFile( + $oFilesProvider->GetFileName($oAccount, $aItem['fileHash']), + ($mUIDs ? "{$aItem['uid']}/" : ($sFolder ? "{$aItem['uid']}-" : '')) . $aItem['fileName'] + ); + } } $oZip->compressFiles(\Phar::GZ); unset($oZip); \rename($sZipFileName . '.zip', $sZipFileName); } - foreach ($aData as $aItem) { - $oFilesProvider->Clear($oAccount, $aItem['fileHash']); - } - if (!$bError) { $mResult = array( 'fileHash' => $this->encodeRawKey(array( @@ -129,46 +137,49 @@ trait Attachments break; } + foreach ($aData as $aItem) { + $aItem['fileHash'] && $oFilesProvider->Clear($oAccount, $aItem['fileHash']); + } + return $this->DefaultResponse($bError ? false : $mResult); } private function getMimeFileByHash(\RainLoop\Model\Account $oAccount, string $sHash) : array { $aValues = $this->decodeRawKey($sHash); - - $sFolder = isset($aValues['folder']) ? (string) $aValues['folder'] : ''; - $iUid = isset($aValues['uid']) ? (int) $aValues['uid'] : 0; - $sMimeIndex = isset($aValues['mimeIndex']) ? (string) $aValues['mimeIndex'] : ''; - - $sContentTypeIn = isset($aValues['mimeType']) ? (string) $aValues['mimeType'] : ''; - $sFileNameIn = isset($aValues['fileName']) ? (string) $aValues['fileName'] : 'file.dat'; - - $oFileProvider = $this->FilesProvider(); - - $sResultHash = ''; - - $mResult = $this->MailClient()->MessageMimeStream(function ($rResource, $sContentType, $sFileName, $sMimeIndex = '') - use ($oAccount, $oFileProvider, $sFileNameIn, $sContentTypeIn, &$sResultHash) { - - unset($sContentType, $sFileName, $sMimeIndex); - - if (\is_resource($rResource)) { - $sHash = \MailSo\Base\Utils::Sha1Rand($sFileNameIn.'~'.$sContentTypeIn); - $rTempResource = $oFileProvider->GetFile($oAccount, $sHash, 'wb+'); - - if (\is_resource($rTempResource)) { - if (-1 < \MailSo\Base\Utils::WriteStream($rResource, $rTempResource)) { - $sResultHash = $sHash; + $aValues['fileName'] = empty($aValues['fileName']) ? 'file.dat' : (string) $aValues['fileName']; + $aValues['fileHash'] = null; + if (isset($aValues['data'])) { + $aValues['data'] = \MailSo\Base\Utils::UrlSafeBase64Decode($aValues['data']); + } else { + $sFolder = isset($aValues['folder']) ? (string) $aValues['folder'] : ''; + $iUid = isset($aValues['uid']) ? (int) $aValues['uid'] : 0; + $sMimeIndex = isset($aValues['mimeIndex']) ? (string) $aValues['mimeIndex'] : ''; + if ($sFolder && $iUid && $sMimeIndex) { + $oFileProvider = $this->FilesProvider(); + $mResult = $this->MailClient()->MessageMimeStream( + function ($rResource, $sContentType, $sFileName, $sMimeIndex = '') + use ($oAccount, $oFileProvider, &$aValues) { + unset($sContentType, $sFileName, $sMimeIndex); + if (\is_resource($rResource)) { + $sContentTypeIn = isset($aValues['mimeType']) ? (string) $aValues['mimeType'] : ''; + $sHash = \MailSo\Base\Utils::Sha1Rand($aValues['fileName'].'~'.$sContentTypeIn); + $rTempResource = $oFileProvider->GetFile($oAccount, $sHash, 'wb+'); + if (\is_resource($rTempResource)) { + if (-1 < \MailSo\Base\Utils::WriteStream($rResource, $rTempResource)) { + $sResultHash = $sHash; + $aValues['fileHash'] = $sHash; + } + \fclose($rTempResource); + } } - - \fclose($rTempResource); - } - } - - }, $sFolder, $iUid, $sMimeIndex); - - $aValues['fileName'] = $sFileNameIn; - $aValues['fileHash'] = $mResult ? $sResultHash : ''; + }, + $sFolder, + $iUid, + $sMimeIndex + ); + } + } return $aValues; } diff --git a/snappymail/v/0.0.0/app/libraries/RainLoop/Actions/Contacts.php b/snappymail/v/0.0.0/app/libraries/RainLoop/Actions/Contacts.php index f8694232a..5d8a4c291 100644 --- a/snappymail/v/0.0.0/app/libraries/RainLoop/Actions/Contacts.php +++ b/snappymail/v/0.0.0/app/libraries/RainLoop/Actions/Contacts.php @@ -2,8 +2,39 @@ namespace RainLoop\Actions; +use RainLoop\Enumerations\Capa; + trait Contacts { + /** + * @var \RainLoop\Providers\AddressBook + */ + protected $oAddressBookProvider = null; + + public function AddressBookProvider(?\RainLoop\Model\Account $oAccount = null): \RainLoop\Providers\AddressBook + { + if (null === $this->oAddressBookProvider) { + $oDriver = null; + try { +// if ($this->oConfig->Get('contacts', 'enable', false)) { + if ($this->GetCapa(Capa::CONTACTS)) { + $oDriver = $this->fabrica('address-book', $oAccount); + } + if ($oAccount && $oDriver) { + $oDriver->SetEmail($this->GetMainEmail($oAccount)); + $oDriver->setDAVClientConfig($this->getContactsSyncData($oAccount)); + } + } catch (\Throwable $e) { + \SnappyMail\LOG::error('AddressBook', $e->getMessage()."\n".$e->getTraceAsString()); + $oDriver = null; +// $oDriver = new \RainLoop\Providers\AddressBook\PdoAddressBook(); + } + $this->oAddressBookProvider = new \RainLoop\Providers\AddressBook($oDriver); + $this->oAddressBookProvider->SetLogger($this->oLogger); + } + + return $this->oAddressBookProvider; + } public function DoSaveContactsSyncData() : array { @@ -172,6 +203,7 @@ trait Contacts if (!isset($aData['Mode'])) { $aData['Mode'] = empty($aData['Enable']) ? 0 : 1; } +// $oAccount = $this->getAccountFromToken(); $oMainAccount = $this->getMainAccountFromToken(); if ($aData['Password']) { $aData['Password'] = \SnappyMail\Crypt::EncryptToJSON($aData['Password'], $oMainAccount->CryptKey()); @@ -187,6 +219,7 @@ trait Contacts protected function getContactsSyncData(\RainLoop\Model\Account $oAccount) : ?array { +// $oAccount = $this->getAccountFromToken(); $sData = $this->StorageProvider()->Get($oAccount, \RainLoop\Providers\Storage\Enumerations\StorageType::CONFIG, 'contacts_sync' diff --git a/snappymail/v/0.0.0/app/libraries/RainLoop/Actions/Folders.php b/snappymail/v/0.0.0/app/libraries/RainLoop/Actions/Folders.php index e7ff3bc57..1c91eec9b 100644 --- a/snappymail/v/0.0.0/app/libraries/RainLoop/Actions/Folders.php +++ b/snappymail/v/0.0.0/app/libraries/RainLoop/Actions/Folders.php @@ -2,7 +2,6 @@ namespace RainLoop\Actions; -use RainLoop\Enumerations\Capa; use RainLoop\Exceptions\ClientException; use RainLoop\Notifications; use MailSo\Imap\Enumerations\FolderType; @@ -146,6 +145,42 @@ trait Folders return $this->TrueResponse(); } + public function DoFolderSettings() : array + { + $this->initMailClientConnection(); + + $sFolderFullName = $this->GetActionParam('folder', ''); + + // DoFolderSubscribe + try + { + $bSubscribe = !empty($this->GetActionParam('subscribe', 0)); + $this->ImapClient()->{$bSubscribe ? 'FolderSubscribe' : 'FolderUnsubscribe'}($sFolderFullName); + } + catch (\Throwable $oException) + { + } + + // DoFolderCheckable + $this->SetFolderCheckable($sFolderFullName, !empty($this->GetActionParam('checkable'))); + + // DoFolderSetMetadata + try + { + $aKolab = $this->GetActionParam('kolab'); + if ($aKolab['type']) { + $this->ImapClient()->FolderSetMetadata($sFolderFullName, [ + $aKolab['type'] => $aKolab['value'] ?: null + ]); + } + } + catch (\Throwable $oException) + { + } + + return $this->TrueResponse(); + } + public function DoFolderSubscribe() : array { $this->initMailClientConnection(); @@ -168,28 +203,35 @@ trait Folders return $this->TrueResponse(); } - public function DoFolderCheckable() : array + protected function SetFolderCheckable(string $sFolderFullName, bool $bCheckable) : bool { $oAccount = $this->getAccountFromToken(); - - $sFolderFullName = $this->GetActionParam('folder', ''); - $oSettingsLocal = $this->SettingsProvider(true)->Load($oAccount); - $aCheckableFolder = \json_decode($oSettingsLocal->GetConf('CheckableFolder', '[]')); - if (!\is_array($aCheckableFolder)) { - $aCheckableFolder = array(); + $aCheckableFolders = \json_decode($oSettingsLocal->GetConf('CheckableFolder', '[]')); + if (!\is_array($aCheckableFolders)) { + $aCheckableFolders = array(); } - if (!empty($this->GetActionParam('checkable', '0'))) { - $aCheckableFolder[] = $sFolderFullName; - } else if (($key = \array_search($sFolderFullName, $aCheckableFolder)) !== false) { - \array_splice($aCheckableFolder, $key, 1); + if ($bCheckable) { + $aCheckableFolders[] = $sFolderFullName; + } else if (($key = \array_search($sFolderFullName, $aCheckableFolders)) !== false) { + \array_splice($aCheckableFolders, $key, 1); } - $oSettingsLocal->SetConf('CheckableFolder', \json_encode(\array_unique($aCheckableFolder))); + $oSettingsLocal->SetConf('CheckableFolder', \json_encode(\array_unique($aCheckableFolders))); - return $this->DefaultResponse($oSettingsLocal->save()); + return $oSettingsLocal->save(); + } + + public function DoFolderCheckable() : array + { + return $this->DefaultResponse( + $this->SetFolderCheckable( + $this->GetActionParam('folder', ''), + !empty($this->GetActionParam('checkable')) + ) + ); } /** @@ -201,11 +243,59 @@ trait Folders try { - $sFullName = $this->MailClient()->FolderRename( - $this->GetActionParam('oldName', ''), - $this->GetActionParam('newName', ''), - !empty($this->GetActionParam('subscribe', 1)) - ); + $sOldName = $this->GetActionParam('oldName', ''); + $sNewName = $this->GetActionParam('newName', ''); + $sDelimiter = $this->ImapClient()->FolderHierarchyDelimiter($sOldName); + + $this->MailClient()->FolderRename($sOldName, $sNewName); + + // DoFolderSubscribe + try + { + $bSubscribe = !empty($this->GetActionParam('subscribe', 0)); + $this->ImapClient()->{$bSubscribe ? 'FolderSubscribe' : 'FolderUnsubscribe'}($sNewName); + } + catch (\Throwable $oException) + { + } + + // DoFolderCheckable + $oAccount = $this->getAccountFromToken(); + $oSettingsLocal = $this->SettingsProvider(true)->Load($oAccount); + $aCheckableFolders = \json_decode($oSettingsLocal->GetConf('CheckableFolder', '[]')); + $aRemoveFolders = []; + if (\is_array($aCheckableFolders)) { + foreach ($aCheckableFolders as $sFolder) { + if (\str_starts_with($sFolder . $sDelimiter, $sOldName . $sDelimiter)) { + $aRemoveFolders[] = $sFolder; + if ($sFolder !== $sOldName) { + $aCheckableFolders[] = $sNewName . $sDelimiter . \substr($sFolder, \strlen($sOldName) + 1); + } + } + } + $aCheckableFolders = \array_diff($aCheckableFolders, $aRemoveFolders); + } else { + $aCheckableFolders = []; + } + if ($this->GetActionParam('checkable')) { + $aCheckableFolders[] = $sNewName; + } + $oSettingsLocal->SetConf('CheckableFolder', \json_encode(\array_unique($aCheckableFolders))); + $oSettingsLocal->save(); + + // DoFolderSetMetadata + try + { + $aKolab = $this->GetActionParam('kolab'); + if ($aKolab['type']) { + $this->ImapClient()->FolderSetMetadata($sNewName, [ + $aKolab['type'] => $aKolab['value'] ?: null + ]); + } + } + catch (\Throwable $oException) + { + } } catch (\Throwable $oException) { diff --git a/snappymail/v/0.0.0/app/libraries/RainLoop/Actions/Localization.php b/snappymail/v/0.0.0/app/libraries/RainLoop/Actions/Localization.php index 88d6ddb2d..3af73d4f7 100644 --- a/snappymail/v/0.0.0/app/libraries/RainLoop/Actions/Localization.php +++ b/snappymail/v/0.0.0/app/libraries/RainLoop/Actions/Localization.php @@ -8,19 +8,19 @@ trait Localization { $oConfig = $this->Config(); if ($bAdmin) { - $sLanguage = $oConfig->Get('webmail', 'language_admin', 'en'); + $sLanguage = $oConfig->Get('admin_panel', 'language', 'en'); } else { $sLanguage = $oConfig->Get('webmail', 'language', 'en'); if ($oAccount = $this->getAccountFromToken(false)) { if ($oConfig->Get('login', 'determine_user_language', true)) { - $sLanguage = $this->ValidateLanguage($this->detectUserLanguage($bAdmin), $sLanguage, false); + $sLanguage = $this->ValidateLanguage($this->detectClientLanguage($bAdmin), $sLanguage, false); } if ($oConfig->Get('webmail', 'allow_languages_on_settings', true) && ($oSettings = $this->SettingsProvider()->Load($oAccount))) { $sLanguage = $oSettings->GetConf('language', $sLanguage); } } else if ($oConfig->Get('login', 'allow_languages_on_login', true) && $oConfig->Get('login', 'determine_user_language', true)) { - $sLanguage = $this->ValidateLanguage($this->detectUserLanguage($bAdmin), $sLanguage, false); + $sLanguage = $this->ValidateLanguage($this->detectClientLanguage($bAdmin), $sLanguage, false); } } $sHookLanguage = $sLanguage = $this->ValidateLanguage($sLanguage, '', $bAdmin) ?: 'en'; @@ -77,13 +77,12 @@ trait Localization return ''; } - $sResult = $this->Config()->Get('webmail', $bAdmin ? 'language_admin' : 'language', 'en'); + $sResult = $this->Config()->Get($bAdmin ? 'admin_panel' : 'webmail', 'language', 'en'); return \in_array($sResult, $aLang) ? $sResult : 'en'; } - public function detectUserLanguage(bool $bAdmin): string + public function detectClientLanguage(bool $bAdmin): string { - $sResult = ''; $aLangs = $aList = array(); $sAcceptLang = \strtolower(\MailSo\Base\Http::GetServer('HTTP_ACCEPT_LANGUAGE', 'en')); @@ -99,12 +98,11 @@ trait Localization foreach (\array_keys($aLangs) as $sLang) { $sLang = $this->ValidateLanguage($sLang, '', $bAdmin, true); if (!empty($sLang)) { - $sResult = $sLang; - break; + return $sLang; } } - return $sResult; + return ''; } public function StaticI18N(string $sKey): string diff --git a/snappymail/v/0.0.0/app/libraries/RainLoop/Actions/Messages.php b/snappymail/v/0.0.0/app/libraries/RainLoop/Actions/Messages.php index f8988aefb..61de585be 100644 --- a/snappymail/v/0.0.0/app/libraries/RainLoop/Actions/Messages.php +++ b/snappymail/v/0.0.0/app/libraries/RainLoop/Actions/Messages.php @@ -2,11 +2,11 @@ namespace RainLoop\Actions; -use RainLoop\Enumerations\Capa; use RainLoop\Exceptions\ClientException; use RainLoop\Model\Account; use RainLoop\Notifications; use MailSo\Imap\SequenceSet; +use MailSo\Imap\Enumerations\FetchType; use MailSo\Imap\Enumerations\MessageFlag; use MailSo\Mime\Part as MimePart; use MailSo\Mime\Enumerations\Header as MimeEnumHeader; @@ -455,6 +455,92 @@ trait Messages try { $oMessage = $this->MailClient()->Message($sFolder, $iUid, true, $this->Cacher($oAccount)); + + $bAutoVerify = $this->Config()->Get('security', 'auto_verify_signatures', false); + + // S/MIME signed. Verify it, so we have the raw mime body to show + if ($oMessage->smimeSigned && ($bAutoVerify || !$oMessage->smimeSigned['detached'])) try { + $bOpaque = !$oMessage->smimeSigned['detached']; + $sBody = $this->ImapClient()->FetchMessagePart( + $oMessage->Uid, + $oMessage->smimeSigned['partId'] + ); + $result = (new \SnappyMail\SMime\OpenSSL(''))->verify($sBody, null, $bOpaque); + if ($result) { + if ($bOpaque) { + $oMessage->smimeSigned['body'] = $result['body']; + } + $oMessage->smimeSigned['success'] = $result['success']; + } + } catch (\Throwable $e) { + $this->logException($e); + } + + if ($bAutoVerify && $oMessage->pgpSigned) try { + $GPG = $this->GnuPG(); + if ($GPG) { + if ($oMessage->pgpSigned['sigPartId']) { + $sPartId = $oMessage->pgpSigned['partId']; + $sSigPartId = $oMessage->pgpSigned['sigPartId']; + $aParts = [ + FetchType::BODY_PEEK.'['.$sPartId.']', + // An empty section specification refers to the entire message, including the header. + // But Dovecot does not return it with BODY.PEEK[1], so we also use BODY.PEEK[1.MIME]. + FetchType::BODY_PEEK.'['.$sPartId.'.MIME]', + FetchType::BODY_PEEK.'['.$sSigPartId.']' + ]; + $oFetchResponse = $this->ImapClient()->Fetch($aParts, $oMessage->Uid, true)[0]; + $sBodyMime = $oFetchResponse->GetFetchValue(FetchType::BODY.'['.$sPartId.'.MIME]'); + $info = $this->GnuPG()->verify( + \preg_replace('/\\r?\\n/su', "\r\n", + $sBodyMime . $oFetchResponse->GetFetchValue(FetchType::BODY.'['.$sPartId.']') + ), + \preg_replace('/[^\x00-\x7F]/', '', + $oFetchResponse->GetFetchValue(FetchType::BODY.'['.$sSigPartId.']') + ) + ); + } else { + // clearsigned text + $info = $this->GnuPG()->verify($oMessage->sPlain, ''); + } + if (!empty($info[0]) && 0 == $info[0]['status']) { + $info = $info[0]; + $oMessage->pgpSigned = [ + 'fingerprint' => $info['fingerprint'], + 'success' => true + ]; + } + } + } catch (\Throwable $e) { + $this->logException($e); + } +/* + if (!$oMessage->sPlain && !$oMessage->sHtml && !$oMessage->pgpEncrypted && !$oMessage->smimeEncrypted) { + $aAttachments = $oMessage->Attachments ?: []; + foreach ($aAttachments as $oAttachment) { +// \in_array($oAttachment->ContentType(), ['application/vnd.ms-tnef', 'application/ms-tnef']) + if ('winmail.dat' === \strtolower($oAttachment->FileName())) { + $sData = $this->ImapClient()->FetchMessagePart( + $oMessage->Uid, + $oAttachment->PartID() + ); + $oTNEF = new \TNEFDecoder\TNEFAttachment; + $oTNEF->decodeTnef($sData); + foreach ($oTNEF->getFiles() as $oFile) { + if (\in_array($oFile->type, ['application/rtf', 'text/rtf'])) { + $rtf = new \SnappyMail\Rtf\Document($oFile->content); + $oMessage->setHtml($rtf->toHTML()); + } else { + // List as attachment? + $oMapiAttachment = new \MailSo\Mail\Attachment($sFolder, $iUid, BodyStructure); + $oMessage->Attachments->append($oMapiAttachment); + } + } + break; + } + } + } +*/ } catch (\Throwable $oException) { @@ -612,7 +698,6 @@ trait Messages ?: \SnappyMail\File\MimeType::fromFilename($sFileName) ?: 'application/octet-stream'; // 'text/plain' -// $sFileName = $self->MainClearFileName($sFileName, $sContentType, $sMimeIndex); $sTempName .= \SnappyMail\File\MimeType::toExtension($sContentType); if ($self->FilesProvider()->PutFile($oAccount, $sTempName, $rResource)) { @@ -631,7 +716,7 @@ trait Messages ?: \SnappyMail\File\MimeType::fromFilename($sTempName) ?: 'application/octet-stream'; // 'text/plain' $aAttachments[$mIndex] = [ -// 'name' => $sFileName, +// 'name' => '', 'tempName' => $sTempName, 'mimeType' => $sContentType // 'size' => $oFilesProvider->FileSize($oAccount, $sTempName) @@ -677,8 +762,7 @@ trait Messages $this->Plugins()->RunHook('filter.smtp-from', array($oAccount, $oMessage, &$sFrom)); $aHiddenRcpt = array(); - if ($bAddHiddenRcpt) - { + if ($bAddHiddenRcpt) { $this->Plugins()->RunHook('filter.smtp-hidden-rcpt', array($oAccount, $oMessage, &$aHiddenRcpt)); } @@ -870,15 +954,16 @@ trait Messages $oMessage->DoesNotAddDefaultXMailer(); } - $sFrom = $this->GetActionParam('from', ''); - $oMessage->SetFrom(\MailSo\Mime\Email::Parse($sFrom)); + $oMessage->SetFrom(\MailSo\Mime\Email::Parse($this->GetActionParam('from', ''))); + $oFrom = $oMessage->GetFrom(); + /* - $oFromIdentity = $this->GetIdentityByID($oAccount, $this->GetActionParam('identityID', '')); - if ($oFromIdentity) + $oIdentity = $this->GetIdentityByID($oAccount, $this->GetActionParam('identityID', '')); + if ($oIdentity) { $oMessage->SetFrom(new \MailSo\Mime\Email( - $oFromIdentity->Email(), $oFromIdentity->Name())); - if ($oAccount->Domain()->OutSetSender()) { + $oIdentity->Email(), $oIdentity->Name())); + if ($oAccount->Domain()->SmtpSettings()->setSender) { $oMessage->SetSender(\MailSo\Mime\Email::Parse($oAccount->Email())); } } @@ -887,14 +972,13 @@ trait Messages $oMessage->SetFrom(\MailSo\Mime\Email::Parse($oAccount->Email())); } */ - $oFrom = $oMessage->GetFrom(); $oMessage->RegenerateMessageId($oFrom ? $oFrom->GetDomain() : ''); $oMessage->SetReplyTo(new \MailSo\Mime\EmailCollection($this->GetActionParam('replyTo', ''))); if (!empty($this->GetActionParam('readReceiptRequest', 0))) { // Read Receipts Reference Main Account Email, Not Identities #147 -// $oMessage->SetReadReceipt(($oFromIdentity ?: $oAccount)->Email()); +// $oMessage->SetReadReceipt(($oIdentity ?: $oAccount)->Email()); $oMessage->SetReadReceipt($oFrom->GetEmail()); } @@ -1109,6 +1193,18 @@ trait Messages } else { $sCertificate = $this->GetActionParam('signCertificate', ''); $sPrivateKey = $this->GetActionParam('signPrivateKey', ''); + if ('S/MIME' === $this->GetActionParam('sign', '')) { + $sID = $this->GetActionParam('identityID', ''); + foreach ($this->GetIdentities($oAccount) as $oIdentity) { + if ($oIdentity && $oIdentity->smimeCertificate && $oIdentity->smimeKey + && ($oIdentity->Id() === $sID || $oIdentity->Email() === $oFrom->GetEmail()) + ) { + $sCertificate = $oIdentity->smimeCertificate; + $sPrivateKey = $oIdentity->smimeKey; + break; + } + } + } if ($sCertificate && $sPrivateKey) { $oBody = $oMessage->GetRootPart(); diff --git a/snappymail/v/0.0.0/app/libraries/RainLoop/Actions/Pgp.php b/snappymail/v/0.0.0/app/libraries/RainLoop/Actions/Pgp.php index 57aba42ea..f70d9a021 100644 --- a/snappymail/v/0.0.0/app/libraries/RainLoop/Actions/Pgp.php +++ b/snappymail/v/0.0.0/app/libraries/RainLoop/Actions/Pgp.php @@ -303,7 +303,7 @@ trait Pgp } else { $sFolderName = $this->GetActionParam('folder', ''); $iUid = (int) $this->GetActionParam('uid', 0); - $sBodyPartId = $this->GetActionParam('bodyPartId', ''); + $sPartId = $this->GetActionParam('partId', ''); $sSigPartId = $this->GetActionParam('sigPartId', ''); // $sMicAlg = $this->GetActionParam('micAlg', ''); @@ -312,10 +312,10 @@ trait Pgp $oImapClient->FolderExamine($sFolderName); $aParts = [ - FetchType::BODY_PEEK.'['.$sBodyPartId.']', + FetchType::BODY_PEEK.'['.$sPartId.']', // An empty section specification refers to the entire message, including the header. // But Dovecot does not return it with BODY.PEEK[1], so we also use BODY.PEEK[1.MIME]. - FetchType::BODY_PEEK.'['.$sBodyPartId.'.MIME]' + FetchType::BODY_PEEK.'['.$sPartId.'.MIME]' ]; if ($sSigPartId) { $aParts[] = FetchType::BODY_PEEK.'['.$sSigPartId.']'; @@ -323,20 +323,20 @@ trait Pgp $oFetchResponse = $oImapClient->Fetch($aParts, $iUid, true)[0]; - $sBodyMime = $oFetchResponse->GetFetchValue(FetchType::BODY.'['.$sBodyPartId.'.MIME]'); + $sBodyMime = $oFetchResponse->GetFetchValue(FetchType::BODY.'['.$sPartId.'.MIME]'); if ($sSigPartId) { $result = [ 'text' => \preg_replace('/\\r?\\n/su', "\r\n", - $sBodyMime . $oFetchResponse->GetFetchValue(FetchType::BODY.'['.$sBodyPartId.']') + $sBodyMime . $oFetchResponse->GetFetchValue(FetchType::BODY.'['.$sPartId.']') ), - 'signature' => preg_replace('/[^\x00-\x7F]/', '', + 'signature' => \preg_replace('/[^\x00-\x7F]/', '', $oFetchResponse->GetFetchValue(FetchType::BODY.'['.$sSigPartId.']') ) ]; } else { // clearsigned text $result = [ - 'text' => $oFetchResponse->GetFetchValue(FetchType::BODY.'['.$sBodyPartId.']'), + 'text' => $oFetchResponse->GetFetchValue(FetchType::BODY.'['.$sPartId.']'), 'signature' => '' ]; $decode = (new \MailSo\Mime\HeaderCollection($sBodyMime))->ValueByName(MimeEnumHeader::CONTENT_TRANSFER_ENCODING); diff --git a/snappymail/v/0.0.0/app/libraries/RainLoop/Actions/Raw.php b/snappymail/v/0.0.0/app/libraries/RainLoop/Actions/Raw.php index 05590dc6b..18d45ea0b 100644 --- a/snappymail/v/0.0.0/app/libraries/RainLoop/Actions/Raw.php +++ b/snappymail/v/0.0.0/app/libraries/RainLoop/Actions/Raw.php @@ -76,6 +76,19 @@ trait Raw return false; } + private function clearFileName(string $sFileName, string $sContentType, string $sMimeIndex, int $iMaxLength = 250): string + { + $sFileName = !\strlen($sFileName) ? \preg_replace('/[^a-zA-Z0-9]/', '.', (empty($sMimeIndex) ? '' : $sMimeIndex . '.') . $sContentType) : $sFileName; + $sClearedFileName = \MailSo\Base\Utils::StripSpaces(\preg_replace('/[\.]+/', '.', $sFileName)); + $sExt = \MailSo\Base\Utils::GetFileExtension($sClearedFileName); + + if (10 < $iMaxLength && $iMaxLength < \strlen($sClearedFileName) - \strlen($sExt)) { + $sClearedFileName = \substr($sClearedFileName, 0, $iMaxLength) . (empty($sExt) ? '' : '.' . $sExt); + } + + return \MailSo\Base\Utils::SecureFileName(\MailSo\Base\Utils::Utf8Clear($sClearedFileName)); + } + /** * Message, Message Attachment or Zip */ @@ -99,6 +112,8 @@ trait Raw $sRangeStart = $aMatch[1]; $sRangeEnd = $aMatch[2]; $bIsRangeRequest = true; + } else { + $this->verifyCacheByKey($sRawKey); } $sMimeIndex = isset($aValues['mimeIndex']) ? (string) $aValues['mimeIndex'] : ''; @@ -106,8 +121,10 @@ trait Raw $sFileNameIn = isset($aValues['fileName']) ? (string) $aValues['fileName'] : ''; if (!empty($aValues['fileHash'])) { - $this->verifyCacheByKey($sRawKey); - + $rResource = $this->FilesProvider()->GetFile($oAccount, (string) $aValues['fileHash']); + if (!\is_resource($rResource)) { + return false; + } // https://github.com/the-djmaze/snappymail/issues/144 if ('.pdf' === \substr($sFileNameIn,-4)) { $sContentTypeOut = 'application/pdf'; // application/octet-stream @@ -116,23 +133,14 @@ trait Raw ?: \SnappyMail\File\MimeType::fromFilename($sFileNameIn) ?: 'application/octet-stream'; } - - $sFileNameOut = $this->MainClearFileName($sFileNameIn, $sContentTypeIn, $sMimeIndex); - - $rResource = $this->FilesProvider()->GetFile($oAccount, (string) $aValues['fileHash']); - if (\is_resource($rResource)) { - \header('Content-Type: '.$sContentTypeOut); - \header('Content-Disposition: attachment; '. - \trim(\MailSo\Base\Utils::EncodeHeaderUtf8AttributeValue('filename', $sFileNameOut))); - - \header('Accept-Ranges: none'); - \header('Content-Transfer-Encoding: binary'); - - \MailSo\Base\Utils::FpassthruWithTimeLimitReset($rResource); - return true; - } - - return false; + $sFileNameOut = $this->clearFileName($sFileNameIn, $sContentTypeIn, $sMimeIndex); + \header('Content-Type: '.$sContentTypeOut); + \header('Content-Disposition: attachment; '. + \trim(\MailSo\Base\Utils::EncodeHeaderUtf8AttributeValue('filename', $sFileNameOut))); + \header('Accept-Ranges: none'); + \header('Content-Transfer-Encoding: binary'); + \MailSo\Base\Utils::FpassthruWithTimeLimitReset($rResource); + return true; } $sFolder = isset($aValues['folder']) ? (string) $aValues['folder'] : ''; @@ -141,8 +149,6 @@ trait Raw return false; } - $this->verifyCacheByKey($sRawKey); - $this->initMailClientConnection(); $self = $this; @@ -166,7 +172,7 @@ trait Raw if ($sFileNameIn) { $sFileName = $sFileNameIn; } - $sFileName = $self->MainClearFileName($sFileName, $sContentType, $sMimeIndex); + $sFileName = $self->clearFileName($sFileName, $sContentType, $sMimeIndex); if ('.pdf' === \substr($sFileName, -4)) { // https://github.com/the-djmaze/snappymail/issues/144 @@ -257,7 +263,8 @@ trait Raw \MailSo\Base\Utils::FpassthruWithTimeLimitReset($rResource); } } - }, $sFolder, $iUid, $sMimeIndex); + }, $sFolder, $iUid, $sMimeIndex + ); } private static function loadImage($resource, bool $bDetectImageOrientation = false, int $iThumbnailBoxSize = 0) : \SnappyMail\Image diff --git a/snappymail/v/0.0.0/app/libraries/RainLoop/Actions/Response.php b/snappymail/v/0.0.0/app/libraries/RainLoop/Actions/Response.php index a61ba47fc..153f70931 100644 --- a/snappymail/v/0.0.0/app/libraries/RainLoop/Actions/Response.php +++ b/snappymail/v/0.0.0/app/libraries/RainLoop/Actions/Response.php @@ -2,7 +2,6 @@ namespace RainLoop\Actions; -use RainLoop\Enumerations\Capa; use RainLoop\Notifications; use RainLoop\Utils; diff --git a/snappymail/v/0.0.0/app/libraries/RainLoop/Actions/SMime.php b/snappymail/v/0.0.0/app/libraries/RainLoop/Actions/SMime.php index 75e35e5d2..a3ded4ab1 100644 --- a/snappymail/v/0.0.0/app/libraries/RainLoop/Actions/SMime.php +++ b/snappymail/v/0.0.0/app/libraries/RainLoop/Actions/SMime.php @@ -104,6 +104,20 @@ trait SMime $SMIME->setCertificate($sCertificate); $SMIME->setPrivateKey($sPrivateKey, $oPassphrase); $result = $SMIME->decrypt($sBody); + if ($result) { + $result = ['data' => $result]; + if (\str_contains($result['data'], 'multipart/signed') + || \preg_match('/smime-type=["\']?signed-data/', $result['data']) + ) { + $signed = $SMIME->verify($result['data'], null, true); + $result['signed'] = [ + 'success' => !empty($signed['success']) + ]; + if (!empty($signed['body'])) { + $result['data'] = $signed['body']; + } + } + } return $this->DefaultResponse($result ?: false); } diff --git a/snappymail/v/0.0.0/app/libraries/RainLoop/Actions/Themes.php b/snappymail/v/0.0.0/app/libraries/RainLoop/Actions/Themes.php index 89a60d884..01779808d 100644 --- a/snappymail/v/0.0.0/app/libraries/RainLoop/Actions/Themes.php +++ b/snappymail/v/0.0.0/app/libraries/RainLoop/Actions/Themes.php @@ -2,6 +2,8 @@ namespace RainLoop\Actions; +use RainLoop\Enumerations\Capa; + trait Themes { public function GetTheme(bool $bAdmin): string @@ -11,7 +13,7 @@ trait Themes $sTheme = $this->Config()->Get('webmail', 'theme', 'Default'); if (!$bAdmin && ($oAccount = $this->getAccountFromToken(false)) - && $this->GetCapa(\RainLoop\Enumerations\Capa::THEMES) + && $this->GetCapa(Capa::THEMES) && ($oSettingsLocal = $this->SettingsProvider(true)->Load($oAccount))) { $sTheme = (string) $oSettingsLocal->GetConf('Theme', $sTheme); } @@ -146,7 +148,7 @@ trait Themes { $oAccount = $this->getAccountFromToken(); - if (!$this->GetCapa(\RainLoop\Enumerations\Capa::USER_BACKGROUND)) { + if (!$this->GetCapa(Capa::USER_BACKGROUND)) { return $this->FalseResponse(); } diff --git a/snappymail/v/0.0.0/app/libraries/RainLoop/Actions/UserAuth.php b/snappymail/v/0.0.0/app/libraries/RainLoop/Actions/UserAuth.php index 89b72d1a8..721ed989c 100644 --- a/snappymail/v/0.0.0/app/libraries/RainLoop/Actions/UserAuth.php +++ b/snappymail/v/0.0.0/app/libraries/RainLoop/Actions/UserAuth.php @@ -2,6 +2,7 @@ namespace RainLoop\Actions; +use RainLoop\Enumerations\Capa; use RainLoop\Notifications; use RainLoop\Utils; use RainLoop\Model\Account; @@ -19,68 +20,64 @@ trait UserAuth private $oAdditionalAuthAccount = false; private $oMainAuthAccount = false; + public function DoResealCryptKey() : array + { + return $this->DefaultResponse( + $this->getMainAccountFromToken()->resealCryptKey( + new \SnappyMail\SensitiveString($this->GetActionParam('passphrase', '')) + ) + ); + } + /** * @throws \RainLoop\Exceptions\ClientException */ public function resolveLoginCredentials(string &$sEmail, \SnappyMail\SensitiveString $oPassword, string &$sLogin): void { - $sEmail = \MailSo\Base\Utils::Trim($sEmail); - if ($this->Config()->Get('login', 'login_lowercase', true)) { - $sEmail = \mb_strtolower($sEmail); - } + $sEmail = \SnappyMail\IDN::emailToAscii(\MailSo\Base\Utils::Trim($sEmail)); $this->Plugins()->RunHook('login.credentials.step-1', array(&$sEmail)); + $oDomain = null; + $oDomainProvider = $this->DomainProvider(); if (!\str_contains($sEmail, '@')) { - $this->logWrite('The email address "' . $sEmail . '" is not complete', \LOG_INFO, 'LOGIN'); - - $bAdded = false; - + $this->logWrite("The email address '{$sEmail}' is incomplete", \LOG_INFO, 'LOGIN'); if ($this->Config()->Get('login', 'determine_user_domain', false)) { - $sUserHost = \trim($this->Http()->GetHost(true, true)); - $this->logWrite('Determined user domain: ' . $sUserHost, \LOG_INFO, 'LOGIN'); +// $sUserHost = \SnappyMail\IDN::toAscii($this->Http()->GetHost(false, true)); + $sUserHost = \strtolower(\idn_to_ascii($this->Http()->GetHost(false, true))); + $this->logWrite("Determined user domain: {$sUserHost}", \LOG_INFO, 'LOGIN'); $aDomainParts = \explode('.', $sUserHost); $iLimit = \min(\count($aDomainParts), 14); - - $oDomainProvider = $this->DomainProvider(); while (0 < $iLimit--) { $sLine = \implode('.', $aDomainParts); - $oDomain = $oDomainProvider->Load($sLine, false); if ($oDomain) { - $bAdded = true; - $this->logWrite('Check "' . $sLine . '": OK (' . $sEmail . ' > ' . $sEmail . '@' . $sLine . ')', - \LOG_INFO, 'LOGIN'); - $sEmail .= '@' . $sLine; + $this->logWrite("Check '{$sLine}': OK", \LOG_INFO, 'LOGIN'); break; } else { - $this->logWrite('Check "' . $sLine . '": NO', \LOG_INFO, 'LOGIN'); + $this->logWrite("Check '{$sLine}': NO", \LOG_INFO, 'LOGIN'); } - \array_shift($aDomainParts); } - if (!$bAdded) { + if (!$oDomain) { $oDomain = $oDomainProvider->Load($sUserHost, true); if ($oDomain) { - $bAdded = true; - $this->logWrite('Check "' . $sUserHost . '" with wildcard: OK (' . $sEmail . ' > ' . $sEmail . '@' . $sUserHost . ')', - \LOG_INFO, 'LOGIN'); - $sEmail .= '@' . $sUserHost; + $this->logWrite("Check '{$sUserHost}' with wildcard: OK", \LOG_INFO, 'LOGIN'); } else { - $this->logWrite('Check "' . $sUserHost . '" with wildcard: NO', \LOG_INFO, 'LOGIN'); + $this->logWrite("Check '{$sUserHost}' with wildcard: NO", \LOG_INFO, 'LOGIN'); } } - if (!$bAdded) { - $this->logWrite('Domain was not found!', \LOG_INFO, 'LOGIN'); + if (!$oDomain) { + $this->logWrite("Domain '{$sUserHost}' was not determined!", \LOG_INFO, 'LOGIN'); } } - if (!$bAdded) { + if (!$oDomain) { $sDefDomain = \trim($this->Config()->Get('login', 'default_domain', '')); if (\strlen($sDefDomain)) { if ('HTTP_HOST' === $sDefDomain || 'SERVER_NAME' === $sDefDomain) { @@ -88,10 +85,8 @@ trait UserAuth } else if ('gethostname' === $sDefDomain) { $sDefDomain = \gethostname(); } - $this->logWrite('Default domain "' . $sDefDomain . '" was used. (' . $sEmail . ' > ' . $sEmail . '@' . $sDefDomain . ')', - \LOG_INFO, 'LOGIN'); - $sEmail .= '@' . $sDefDomain; + $this->logWrite("Default domain '{$sDefDomain}' will be used.", \LOG_INFO, 'LOGIN'); } else { $this->logWrite('Default domain not configured.', \LOG_INFO, 'LOGIN'); } @@ -103,8 +98,11 @@ trait UserAuth $this->logMask($sPassword); $sLogin = $sEmail; - if ($this->Config()->Get('login', 'login_lowercase', true)) { - $sLogin = \mb_strtolower($sLogin); + if (\str_contains($sEmail, '@') + && ($oDomain || ($oDomain = $oDomainProvider->Load(\MailSo\Base\Utils::getEmailAddressDomain($sEmail)))) + ) { + $sEmail = $oDomain->ImapSettings()->fixUsername($sEmail, false); + $sLogin = $oDomain->ImapSettings()->fixUsername($sLogin); } $this->Plugins()->RunHook('login.credentials', array(&$sEmail, &$sLogin, &$sPassword)); @@ -136,7 +134,7 @@ trait UserAuth throw new ClientException(Notifications::AuthError); } } catch (\Throwable $oException) { - $this->LoggerAuthHelper($oAccount, $this->getAdditionalLogParamsByUserLogin($sInputEmail)); + $this->LoggerAuthHelper($oAccount, $sInputEmail); throw $oException; } @@ -164,28 +162,25 @@ trait UserAuth { $this->Http()->ServerNoCache(); $oMainAccount = $this->getMainAccountFromToken(false); - if ($sEmail && $oMainAccount && $this->GetCapa(\RainLoop\Enumerations\Capa::ADDITIONAL_ACCOUNTS)) { + if ($sEmail && $oMainAccount && $this->GetCapa(Capa::ADDITIONAL_ACCOUNTS)) { $oAccount = null; - if ($oMainAccount->Email() === $sEmail) { - $this->SetAdditionalAuthToken($oAccount); - return true; - } - $sEmail = \MailSo\Base\Utils::IdnToAscii($sEmail); - $aAccounts = $this->GetAccounts($oMainAccount); - if (!isset($aAccounts[$sEmail])) { - throw new ClientException(Notifications::AccountDoesNotExist); - } - $oAccount = AdditionalAccount::NewInstanceFromTokenArray( - $this, $aAccounts[$sEmail] - ); - if (!$oAccount) { - throw new ClientException(Notifications::AccountSwitchFailed); - } - - // Test the login - $oImapClient = new \MailSo\Imap\ImapClient; - $this->imapConnect($oAccount, false, $oImapClient); + if ($oMainAccount->Email() !== $sEmail) { + $sEmail = \SnappyMail\IDN::emailToAscii($sEmail); + $aAccounts = $this->GetAccounts($oMainAccount); + if (!isset($aAccounts[$sEmail])) { + throw new ClientException(Notifications::AccountDoesNotExist); + } + $oAccount = AdditionalAccount::NewInstanceFromTokenArray( + $this, $aAccounts[$sEmail] + ); + if (!$oAccount) { + throw new ClientException(Notifications::AccountSwitchFailed); + } + // Test the login + $oImapClient = new \MailSo\Imap\ImapClient; + $this->imapConnect($oAccount, false, $oImapClient); + } $this->SetAdditionalAuthToken($oAccount); return true; } diff --git a/snappymail/v/0.0.0/app/libraries/RainLoop/ActionsAdmin.php b/snappymail/v/0.0.0/app/libraries/RainLoop/ActionsAdmin.php index 757169491..267ff0c11 100644 --- a/snappymail/v/0.0.0/app/libraries/RainLoop/ActionsAdmin.php +++ b/snappymail/v/0.0.0/app/libraries/RainLoop/ActionsAdmin.php @@ -56,7 +56,7 @@ class ActionsAdmin extends Actions return $self->ValidateLanguage($sLanguage, '', false); }); - $this->setConfigFromParams($oConfig, 'languageAdmin', 'webmail', 'language_admin', 'string', function ($sLanguage) use ($self) { + $this->setConfigFromParams($oConfig, 'languageAdmin', 'admin_panel', 'language', 'string', function ($sLanguage) use ($self) { return $self->ValidateLanguage($sLanguage, '', true); }); @@ -65,6 +65,7 @@ class ActionsAdmin extends Actions }); $this->setConfigFromParams($oConfig, 'useLocalProxyForExternalImages', 'labs', 'use_local_proxy_for_external_images', 'bool'); + $this->setConfigFromParams($oConfig, 'autoVerifySignatures', 'security', 'auto_verify_signatures', 'bool'); $this->setConfigFromParams($oConfig, 'allowLanguagesOnSettings', 'webmail', 'allow_languages_on_settings', 'bool'); $this->setConfigFromParams($oConfig, 'allowLanguagesOnLogin', 'login', 'allow_languages_on_login', 'bool'); @@ -91,7 +92,8 @@ class ActionsAdmin extends Actions $this->setConfigFromParams($oConfig, 'CapaAttachmentThumbnails', 'interface', 'show_attachment_thumbnail', 'bool'); $this->setConfigFromParams($oConfig, 'CapaThemes', 'webmail', 'allow_themes', 'bool'); $this->setConfigFromParams($oConfig, 'CapaUserBackground', 'webmail', 'allow_user_background', 'bool'); - $this->setConfigFromParams($oConfig, 'CapaOpenPGP', 'security', 'openpgp', 'bool'); + $this->setConfigFromParams($oConfig, 'capaGnuPG', 'security', 'gnupg', 'bool'); + $this->setConfigFromParams($oConfig, 'capaOpenPGP', 'security', 'openpgp', 'bool'); $this->setConfigFromParams($oConfig, 'determineUserLanguage', 'login', 'determine_user_language', 'bool'); $this->setConfigFromParams($oConfig, 'determineUserDomain', 'login', 'determine_user_domain', 'bool'); @@ -122,7 +124,7 @@ class ActionsAdmin extends Actions !$this->Config()->ValidatePassword($oPassword) || ($totp && !\SnappyMail\TOTP::Verify($totp, $this->GetActionParam('TOTP', '')))) { - $this->LoggerAuthHelper(null, $this->getAdditionalLogParamsByUserLogin($sLogin, true), true); + $this->LoggerAuthHelper(null, $sLogin, true); $this->loginErrorDelay(); throw new ClientException(Notifications::AuthError); } @@ -365,56 +367,6 @@ class ActionsAdmin extends Actions return $this->DefaultResponse($QR->__toString()); } -/* - public function AdminAppData(array &$aResult): void - { - $oConfig = $this->oConfig; - $aResult['Auth'] = $this->IsAdminLoggined(false); - if ($aResult['Auth']) { - $aResult['adminLogin'] = (string)$oConfig->Get('security', 'admin_login', ''); - $aResult['adminTOTP'] = (string)$oConfig->Get('security', 'admin_totp', ''); - $aResult['pluginsEnable'] = (bool)$oConfig->Get('plugins', 'enable', false); - - $aResult['loginDefaultDomain'] = $oConfig->Get('login', 'default_domain', ''); - $aResult['determineUserLanguage'] = (bool)$oConfig->Get('login', 'determine_user_language', true); - $aResult['determineUserDomain'] = (bool)$oConfig->Get('login', 'determine_user_domain', false); - - $aResult['supportedPdoDrivers'] = \RainLoop\Pdo\Base::getAvailableDrivers(); - - $aResult['contactsEnable'] = (bool)$oConfig->Get('contacts', 'enable', false); - $aResult['contactsSync'] = (bool)$oConfig->Get('contacts', 'allow_sync', false); - $aResult['contactsPdoType'] = Providers\AddressBook\PdoAddressBook::validPdoType($oConfig->Get('contacts', 'type', 'sqlite')); - $aResult['contactsPdoDsn'] = (string)$oConfig->Get('contacts', 'pdo_dsn', ''); - $aResult['contactsPdoType'] = (string)$oConfig->Get('contacts', 'type', ''); - $aResult['contactsPdoUser'] = (string)$oConfig->Get('contacts', 'pdo_user', ''); - $aResult['contactsPdoPassword'] = static::APP_DUMMY; - $aResult['contactsMySQLSSLCA'] = (string) $oConfig->Get('contacts', 'mysql_ssl_ca', ''); - $aResult['contactsMySQLSSLVerify'] = !!$oConfig->Get('contacts', 'mysql_ssl_verify', true); - $aResult['contactsMySQLSSLCiphers'] = (string) $oConfig->Get('contacts', 'mysql_ssl_ciphers', ''); - $aResult['contactsSQLiteGlobal'] = !!$oConfig->Get('contacts', 'sqlite_global', \is_file(APP_PRIVATE_DATA . '/AddressBook.sqlite')); - $aResult['contactsSuggestionsLimit'] = (int)$oConfig->Get('contacts', 'suggestions_limit', 20); - - $aResult['faviconUrl'] = $oConfig->Get('webmail', 'favicon_url', ''); - - $aResult['weakPassword'] = \is_file(APP_PRIVATE_DATA.'admin_password.txt'); - - $aResult['System']['languagesAdmin'] = \SnappyMail\L10n::getLanguages(true); - $aResult['languageAdmin'] = $this->ValidateLanguage($oConfig->Get('webmail', 'language_admin', 'en'), '', true); - $aResult['languageUsers'] = $this->ValidateLanguage($this->detectUserLanguage(true), '', true, true); - } else { - $passfile = APP_PRIVATE_DATA.'admin_password.txt'; - $sPassword = $oConfig->Get('security', 'admin_password', ''); - if (!$sPassword) { - $sPassword = \substr(\base64_encode(\random_bytes(16)), 0, 12); - Utils::saveFile($passfile, $sPassword . "\n"); -// \chmod($passfile, 0600); - $oConfig->SetPassword($sPassword); - $oConfig->Save(); - } - } - } -*/ - private function setAdminAuthToken() : string { $sRand = \MailSo\Base\Utils::Sha1Rand(); @@ -463,4 +415,57 @@ class ActionsAdmin extends Actions } } + public static function AdminAppData(Actions $oActions, array &$aResult): void + { + $oConfig = $oActions->Config(); + $aResult['Admin'] = [ + 'host' => '' !== $oConfig->Get('admin_panel', 'host', ''), + 'path' => $oConfig->Get('admin_panel', 'key', '') ?: 'admin', + 'allowed' => (bool)$oConfig->Get('security', 'allow_admin_panel', true) + ]; + + $aResult['Auth'] = $oActions->IsAdminLoggined(false); + if ($aResult['Auth']) { + $aResult['adminLogin'] = (string)$oConfig->Get('security', 'admin_login', ''); + $aResult['adminTOTP'] = (string)$oConfig->Get('security', 'admin_totp', ''); + $aResult['pluginsEnable'] = (bool)$oConfig->Get('plugins', 'enable', false); + + $aResult['loginDefaultDomain'] = $oConfig->Get('login', 'default_domain', ''); + $aResult['determineUserLanguage'] = (bool)$oConfig->Get('login', 'determine_user_language', true); + $aResult['determineUserDomain'] = (bool)$oConfig->Get('login', 'determine_user_domain', false); + + $aResult['supportedPdoDrivers'] = \RainLoop\Pdo\Base::getAvailableDrivers(); + + $aResult['contactsEnable'] = (bool)$oConfig->Get('contacts', 'enable', false); + $aResult['contactsSync'] = (bool)$oConfig->Get('contacts', 'allow_sync', false); + $aResult['contactsPdoType'] = Providers\AddressBook\PdoAddressBook::validPdoType($oConfig->Get('contacts', 'type', 'sqlite')); + $aResult['contactsPdoDsn'] = (string)$oConfig->Get('contacts', 'pdo_dsn', ''); + $aResult['contactsPdoType'] = (string)$oConfig->Get('contacts', 'type', ''); + $aResult['contactsPdoUser'] = (string)$oConfig->Get('contacts', 'pdo_user', ''); + $aResult['contactsPdoPassword'] = static::APP_DUMMY; + $aResult['contactsMySQLSSLCA'] = (string) $oConfig->Get('contacts', 'mysql_ssl_ca', ''); + $aResult['contactsMySQLSSLVerify'] = !!$oConfig->Get('contacts', 'mysql_ssl_verify', true); + $aResult['contactsMySQLSSLCiphers'] = (string) $oConfig->Get('contacts', 'mysql_ssl_ciphers', ''); + $aResult['contactsSQLiteGlobal'] = !!$oConfig->Get('contacts', 'sqlite_global', \is_file(APP_PRIVATE_DATA . '/AddressBook.sqlite')); + $aResult['contactsSuggestionsLimit'] = (int)$oConfig->Get('contacts', 'suggestions_limit', 20); + + $aResult['faviconUrl'] = $oConfig->Get('webmail', 'favicon_url', ''); + + $aResult['weakPassword'] = \is_file(APP_PRIVATE_DATA.'admin_password.txt'); + + $aResult['Admin']['language'] = $oActions->ValidateLanguage($oConfig->Get('admin_panel', 'language', 'en'), '', true); + $aResult['Admin']['languages'] = \SnappyMail\L10n::getLanguages(true); + $aResult['Admin']['clientLanguage'] = $oActions->ValidateLanguage($oActions->detectClientLanguage(true), '', true, true); + } else { + $passfile = APP_PRIVATE_DATA.'admin_password.txt'; + $sPassword = $oConfig->Get('security', 'admin_password', ''); + if (!$sPassword) { + $sPassword = \substr(\base64_encode(\random_bytes(16)), 0, 12); + Utils::saveFile($passfile, $sPassword . "\n"); +// \chmod($passfile, 0600); + $oConfig->SetPassword(new \SnappyMail\SensitiveString($sPassword)); + $oConfig->Save(); + } + } + } } diff --git a/snappymail/v/0.0.0/app/libraries/RainLoop/Api.php b/snappymail/v/0.0.0/app/libraries/RainLoop/Api.php index f1d3de2fc..5098824bb 100644 --- a/snappymail/v/0.0.0/app/libraries/RainLoop/Api.php +++ b/snappymail/v/0.0.0/app/libraries/RainLoop/Api.php @@ -104,7 +104,7 @@ abstract class Api public static function ClearUserData(string $sEmail) : bool { if (\strlen($sEmail)) { - $sEmail = \MailSo\Base\Utils::IdnToAscii($sEmail); + $sEmail = \SnappyMail\IDN::emailToAscii($sEmail); $oStorageProvider = static::Actions()->StorageProvider(); if ($oStorageProvider && $oStorageProvider->IsActive()) { diff --git a/snappymail/v/0.0.0/app/libraries/RainLoop/Config/Application.php b/snappymail/v/0.0.0/app/libraries/RainLoop/Config/Application.php index d88f7441c..86ac02a46 100644 --- a/snappymail/v/0.0.0/app/libraries/RainLoop/Config/Application.php +++ b/snappymail/v/0.0.0/app/libraries/RainLoop/Config/Application.php @@ -95,6 +95,18 @@ class Application extends \RainLoop\Config\AbstractConfig public function Set(string $sSectionKey, string $sParamKey, $mParamValue) : void { // Workarounds for the changed application structure + if ('webmail' === $sSectionKey) { + if ('language_admin' === $sSectionKey) { + $sSectionKey = 'admin_panel'; + $sParamKey = 'language'; + } + } + if ('security' === $sSectionKey) { + if (\str_starts_with($sParamKey, 'admin_panel_')) { + $sSectionKey = 'admin_panel'; + $sParamKey = \str_replace('admin_panel_', '', $sParamKey); + } + } if ('labs' === $sSectionKey) { if (\str_starts_with($sParamKey, 'imap_')) { $sSectionKey = 'imap'; @@ -163,7 +175,6 @@ class Application extends \RainLoop\Config\AbstractConfig 'allow_user_background' => array(false), 'language' => array('en', 'Language used by default'), - 'language_admin' => array('en', 'Admin Panel interface language'), 'allow_languages_on_settings' => array(true, 'Allow language selection on settings screen'), 'allow_additional_accounts' => array(true), @@ -202,14 +213,14 @@ Warning: only enable when server does not do this, else double compression error 'custom_server_signature' => array('SnappyMail'), 'x_xss_protection_header' => array('1; mode=block'), + 'gnupg' => array(true), 'openpgp' => array(true), + 'auto_verify_signatures' => array(false), 'allow_admin_panel' => array(true, 'Access settings'), 'admin_login' => array('admin', 'Login and password for web admin panel'), 'admin_password' => array(''), 'admin_totp' => array(''), - 'admin_panel_host' => array(''), - 'admin_panel_key' => array('admin'), 'force_https' => array(false), 'hide_x_mailer_header' => array(true), @@ -234,10 +245,11 @@ Default is "site=same-origin;site=none"') 'login' => array('admin', 'Login and password for web admin panel'), 'password' => array(''), 'totp' => array(''), +*/ 'host' => array(''), 'key' => array('admin'), -*/ - 'allow_update' => array(false) + 'allow_update' => array(false), + 'language' => array('en', 'Admin Panel interface language'), ), 'ssl' => array( @@ -270,9 +282,7 @@ When this value is gethostname, the gethostname() value is used. 'determine_user_language' => array(true, 'Detect language from browser header `Accept-Language`'), 'determine_user_domain' => array(false, 'Like default_domain but then HTTP_HOST/SERVER_NAME without www.'), - 'login_lowercase' => array(true), - - 'sign_me_auto' => array(\RainLoop\Enumerations\SignMeType::DEFAULT_OFF->value, + 'sign_me_auto' => array(\RainLoop\Enumerations\SignMeType::DefaultOff->value, 'This option allows webmail to remember the logged in user once they closed the browser window. diff --git a/snappymail/v/0.0.0/app/libraries/RainLoop/Enumerations/SignMeType.php b/snappymail/v/0.0.0/app/libraries/RainLoop/Enumerations/SignMeType.php index 75cadefd0..f44eca2ee 100644 --- a/snappymail/v/0.0.0/app/libraries/RainLoop/Enumerations/SignMeType.php +++ b/snappymail/v/0.0.0/app/libraries/RainLoop/Enumerations/SignMeType.php @@ -4,7 +4,8 @@ namespace RainLoop\Enumerations; enum SignMeType: string { - case DEFAULT_OFF = 'DefaultOff'; - case DEFAULT_ON = 'DefaultOn'; - case UNUSED = 'Unused'; + + const DefaultOff = 'DefaultOff'; + const DefaultOn = 'DefaultOn'; + const Unused = 'Unused'; } diff --git a/snappymail/v/0.0.0/app/libraries/RainLoop/Enumerations/UploadError.php b/snappymail/v/0.0.0/app/libraries/RainLoop/Enumerations/UploadError.php index 83d54b488..c31dd8f45 100644 --- a/snappymail/v/0.0.0/app/libraries/RainLoop/Enumerations/UploadError.php +++ b/snappymail/v/0.0.0/app/libraries/RainLoop/Enumerations/UploadError.php @@ -9,16 +9,16 @@ abstract class UploadError const CONFIG_SIZE = 1001; const ON_SAVING = 1002; - const EMPTY_FILES_DATA = 1003; + const EMPTY_FILE = 1003; private static $messages = [ - \UPLOAD_ERR_INI_SIZE => 'Filesize exceeds the upload_max_filesize directive in php.ini', - \UPLOAD_ERR_FORM_SIZE => 'Filesize exceeds the MAX_FILE_SIZE directive that was specified in the html form', - \UPLOAD_ERR_PARTIAL => 'File was only partially uploaded', - \UPLOAD_ERR_NO_FILE => 'No file was uploaded', - \UPLOAD_ERR_NO_TMP_DIR => 'Missing a temporary folder', - \UPLOAD_ERR_CANT_WRITE => 'Failed to write file to disk', - \UPLOAD_ERR_EXTENSION => 'File upload stopped by extension', + /*1*/\UPLOAD_ERR_INI_SIZE => 'Filesize exceeds the upload_max_filesize directive in php.ini', + /*2*/\UPLOAD_ERR_FORM_SIZE => 'Filesize exceeds the MAX_FILE_SIZE directive that was specified in the html form', + /*3*/\UPLOAD_ERR_PARTIAL => 'File was only partially uploaded', + /*4*/\UPLOAD_ERR_NO_FILE => 'No file was uploaded', + /*6*/\UPLOAD_ERR_NO_TMP_DIR => 'Missing a temporary folder', + /*7*/\UPLOAD_ERR_CANT_WRITE => 'Failed to write file to disk', + /*8*/\UPLOAD_ERR_EXTENSION => 'File upload stopped by extension', 98 => 'Invalid file type', 99 => 'Unknown error', 1001 => 'Filesize exceeds the config setting', @@ -28,7 +28,7 @@ abstract class UploadError public static function getMessage(int $code): string { - return isset(static::$messages[$code]) ? static::$messages[$code] : 0; + return isset(static::$messages[$code]) ? static::$messages[$code] : ''; } public static function getUserMessage(int $iError, int &$iClientError): string @@ -39,12 +39,12 @@ abstract class UploadError case \UPLOAD_ERR_PARTIAL: case \UPLOAD_ERR_NO_FILE: case static::FILE_TYPE: + case static::EMPTY_FILE: break; case \UPLOAD_ERR_INI_SIZE: case \UPLOAD_ERR_FORM_SIZE: case static::CONFIG_SIZE: - case static::EMPTY_FILES_DATA: return 'File is too big'; case \UPLOAD_ERR_NO_TMP_DIR: diff --git a/snappymail/v/0.0.0/app/libraries/RainLoop/KeyPathHelper.php b/snappymail/v/0.0.0/app/libraries/RainLoop/KeyPathHelper.php index 509711925..db07fc51e 100644 --- a/snappymail/v/0.0.0/app/libraries/RainLoop/KeyPathHelper.php +++ b/snappymail/v/0.0.0/app/libraries/RainLoop/KeyPathHelper.php @@ -9,11 +9,6 @@ class KeyPathHelper return '/Sso/Data/'.$sSsoHash.'/Login/'; } - static public function RepositoryCacheFile(string $sRepo, string $sRepoFile) : string - { - return '/RepositoryCache/Repo/'.$sRepo.'/File/'.$sRepoFile; - } - static public function ReadReceiptCache(string $sEmail, string $sFolderFullName, int $iUid) : string { return '/ReadReceipt/'.$sEmail.'/'.$sFolderFullName.'/'.$iUid; @@ -29,11 +24,6 @@ class KeyPathHelper return '/PluginsJsCache/'.$sPluginsHash.'/'.APP_VERSION.'/'; } - static public function CssCache(string $sTheme, string $sHash) : string - { - return '/CssCache/'.$sHash.'/'.$sTheme.'/'.APP_VERSION.'/'; - } - static public function SessionAdminKey(string $sRand) : string { return '/Session/AdminKey/'.\md5($sRand).'/'; diff --git a/snappymail/v/0.0.0/app/libraries/RainLoop/Model/Account.php b/snappymail/v/0.0.0/app/libraries/RainLoop/Model/Account.php index f2b9ad8b2..b6a4208d7 100644 --- a/snappymail/v/0.0.0/app/libraries/RainLoop/Model/Account.php +++ b/snappymail/v/0.0.0/app/libraries/RainLoop/Model/Account.php @@ -19,11 +19,7 @@ abstract class Account implements \JsonSerializable private string $sSmtpLogin = ''; - private ?SensitiveString $sSmtpPassword = null; - - private string $sProxyAuthUser = ''; - - private ?SensitiveString $sProxyAuthPassword = null; + private ?SensitiveString $oSmtpPassword = null; private Domain $oDomain; @@ -39,9 +35,7 @@ abstract class Account implements \JsonSerializable public function IncLogin() : string { - return $this->oDomain->IncShortLogin() - ? \MailSo\Base\Utils::GetAccountNameFromEmail($this->sLogin) - : $this->sLogin; + return $this->sLogin; } public function IncPassword() : string @@ -51,8 +45,7 @@ abstract class Account implements \JsonSerializable public function OutLogin() : string { - $sSmtpLogin = $this->sSmtpLogin ?: $this->sLogin; - return $this->oDomain->OutShortLogin() ? \MailSo\Base\Utils::GetAccountNameFromEmail($sSmtpLogin) : $sSmtpLogin; + return $this->sSmtpLogin ?: $this->sLogin; } public function Domain() : Domain @@ -80,20 +73,7 @@ abstract class Account implements \JsonSerializable string $sPassword ) : void { - $this->sSmtpPassword = new SensitiveString($sPassword); - } - - public function SetProxyAuthUser(string $sProxyAuthUser) : void - { - $this->sProxyAuthUser = $sProxyAuthUser; - } - - public function SetProxyAuthPassword( - #[\SensitiveParameter] - string $sProxyAuthPassword - ) : void - { - $this->sProxyAuthPassword = new SensitiveString($sProxyAuthPassword); + $this->oSmtpPassword = new SensitiveString($sPassword); } #[\ReturnTypeWillChange] @@ -105,16 +85,10 @@ abstract class Account implements \JsonSerializable 'pass' => $this->IncPassword(), 'name' => $this->sName ]; - if ($this->sSmtpLogin && $this->sSmtpPassword) { + if ($this->sSmtpLogin && $this->oSmtpPassword) { $result['smtp'] = [ 'user' => $this->sSmtpLogin, - 'pass' => $this->sSmtpPassword->getValue() - ]; - } - if ($this->sProxyAuthUser && $this->sProxyAuthPassword) { - $result['proxy'] = [ - 'user' => $this->sProxyAuthUser, - 'pass' => $this->sProxyAuthPassword->getValue() + 'pass' => $this->oSmtpPassword->getValue() ]; } return $result; @@ -127,13 +101,13 @@ abstract class Account implements \JsonSerializable { $oAccount = null; if ($sEmail && $sLogin && \strlen($oPassword)) { - $oDomain = $oActions->DomainProvider()->Load(\MailSo\Base\Utils::GetDomainFromEmail($sEmail), true); + $oDomain = $oActions->DomainProvider()->Load(\MailSo\Base\Utils::getEmailAddressDomain($sEmail), true); if ($oDomain) { if ($oDomain->ValidateWhiteList($sEmail, $sLogin)) { $oAccount = new static; - $oAccount->sEmail = \MailSo\Base\Utils::IdnToAscii($sEmail, true); - $oAccount->sLogin = \MailSo\Base\Utils::IdnToAscii($sLogin); + $oAccount->sEmail = \SnappyMail\IDN::emailToAscii($sEmail); + $oAccount->sLogin = \SnappyMail\IDN::emailToAscii($sLogin); $oAccount->SetPassword($oPassword); $oAccount->oDomain = $oDomain; @@ -164,18 +138,11 @@ abstract class Account implements \JsonSerializable if (empty($aAccount[0]) || 'account' != $aAccount[0] || 7 > \count($aAccount)) { return []; } - $aResult = [ + return [ 'email' => $aAccount[1] ?: '', 'login' => $aAccount[2] ?: '', 'pass' => $aAccount[3] ?: '' ]; - if ($aAccount[5] && $aAccount[6]) { - $aResult['proxy'] = [ - 'user' => $aAccount[5], - 'pass' => $aAccount[6] - ]; - } - return $aResult; } public static function NewInstanceFromTokenArray( @@ -202,11 +169,6 @@ abstract class Account implements \JsonSerializable $oAccount->sSmtpLogin = $aAccountHash['smtp']['user']; $oAccount->SetSmtpPassword($aAccountHash['smtp']['pass']); } - // init proxy user/password - if (isset($aAccountHash['proxy'])) { - $oAccount->sProxyAuthUser = $aAccountHash['proxy']['user']; - $oAccount->SetProxyAuthPassword($aAccountHash['proxy']['pass']); - } } } return $oAccount; @@ -216,7 +178,7 @@ abstract class Account implements \JsonSerializable { $oSettings = $this->Domain()->ImapSettings(); $oSettings->timeout = \max($oSettings->timeout, (int) $oConfig->Get('imap', 'timeout', $oSettings->timeout)); - $oSettings->Login = $this->IncLogin(); + $oSettings->username = $this->IncLogin(); $oSettings->expunge_all_on_delete |= !!$oConfig->Get('imap', 'use_expunge_all_on_delete', false); $oSettings->fast_simple_search = !(!$oSettings->fast_simple_search || !$oConfig->Get('imap', 'message_list_fast_simple_search', true)); @@ -233,14 +195,14 @@ abstract class Account implements \JsonSerializable $oImapClient->Connect($oSettings); $oPlugins->RunHook('imap.after-connect', array($this, $oImapClient, $oSettings)); - $oSettings->Password = $this->oPassword; + $oSettings->passphrase = $this->oPassword; return $this->netClientLogin($oImapClient, $oPlugins); } public function SmtpConnectAndLogin(\RainLoop\Plugins\Manager $oPlugins, \MailSo\Smtp\SmtpClient $oSmtpClient) : bool { $oSettings = $this->Domain()->SmtpSettings(); - $oSettings->Login = $this->OutLogin(); + $oSettings->username = $this->OutLogin(); $oSettings->Ehlo = \MailSo\Smtp\SmtpClient::EhloHelper(); $oSmtpClient->Settings = $oSettings; @@ -253,18 +215,18 @@ abstract class Account implements \JsonSerializable $oSmtpClient->Connect($oSettings); $oPlugins->RunHook('smtp.after-connect', array($this, $oSmtpClient, $oSettings)); /* - if ($this->oDomain->OutAskCredentials() && !($this->sSmtpPassword && $this->sSmtpLogin)) { + if ($this->oDomain->OutAskCredentials() && !($this->oSmtpPassword && $this->sSmtpLogin)) { throw new RequireCredentialsException } */ - $oSettings->Password = $this->sSmtpPassword ?: $this->oPassword; + $oSettings->passphrase = $this->oSmtpPassword ?: $this->oPassword; return $this->netClientLogin($oSmtpClient, $oPlugins); } public function SieveConnectAndLogin(\RainLoop\Plugins\Manager $oPlugins, \MailSo\Sieve\SieveClient $oSieveClient, \RainLoop\Config\Application $oConfig) { $oSettings = $this->Domain()->SieveSettings(); - $oSettings->Login = $this->IncLogin(); + $oSettings->username = $this->IncLogin(); $oSieveClient->Settings = $oSettings; @@ -272,7 +234,7 @@ abstract class Account implements \JsonSerializable $oSieveClient->Connect($oSettings); $oPlugins->RunHook('sieve.after-connect', array($this, $oSieveClient, $oSettings)); - $oSettings->Password = $this->oPassword; + $oSettings->passphrase = $this->oPassword; return $this->netClientLogin($oSieveClient, $oPlugins); } @@ -288,8 +250,6 @@ abstract class Account implements \JsonSerializable ) */ $oSettings = $oClient->Settings; - $oSettings->ProxyAuthUser = $this->sProxyAuthUser; - $oSettings->ProxyAuthPassword = $this->sProxyAuthPassword; $client_name = \strtolower($oClient->getLogName()); diff --git a/snappymail/v/0.0.0/app/libraries/RainLoop/Model/AdditionalAccount.php b/snappymail/v/0.0.0/app/libraries/RainLoop/Model/AdditionalAccount.php index 25709c884..6515025bc 100644 --- a/snappymail/v/0.0.0/app/libraries/RainLoop/Model/AdditionalAccount.php +++ b/snappymail/v/0.0.0/app/libraries/RainLoop/Model/AdditionalAccount.php @@ -9,7 +9,7 @@ class AdditionalAccount extends Account { public function ParentEmail() : string { - return \trim(\MailSo\Base\Utils::IdnToAscii(\RainLoop\Api::Actions()->getMainAccountFromToken()->Email(), true)); + return \SnappyMail\IDN::emailToAscii(\RainLoop\Api::Actions()->getMainAccountFromToken()->Email()); } public function Hash() : string @@ -35,9 +35,6 @@ class AdditionalAccount extends Account if (!empty($aData['smtp']['pass'])) { $aData['smtp']['pass'] = \SnappyMail\Crypt::EncryptUrlSafe($aData['smtp']['pass'], $sHash); } - if (!empty($aData['proxy']['pass'])) { - $aData['proxy']['pass'] = \SnappyMail\Crypt::EncryptUrlSafe($aData['proxy']['pass'], $sHash); // sProxyAuthPassword - } $aData['hmac'] = \hash_hmac('sha1', $aData['pass'], $sHash); return $aData; } @@ -58,17 +55,11 @@ class AdditionalAccount extends Account if (!empty($aData['smtp']['pass'])) { $aAccountHash['smtp']['pass'] = \SnappyMail\Crypt::DecryptUrlSafe($aAccountHash['smtp']['pass'], $sHash); } - if (!empty($aData['proxy']['pass'])) { - $aAccountHash['proxy']['pass'] = \SnappyMail\Crypt::DecryptUrlSafe($aAccountHash['proxy']['pass'], $sHash); - } } else { $aAccountHash['pass'] = ''; if (!empty($aData['smtp']['pass'])) { $aAccountHash['smtp']['pass'] = ''; } - if (!empty($aData['proxy']['pass'])) { - $aAccountHash['proxy']['pass'] = ''; - } } } return parent::NewInstanceFromTokenArray($oActions, $aAccountHash, $bThrowExceptionOnFalse); diff --git a/snappymail/v/0.0.0/app/libraries/RainLoop/Model/Domain.php b/snappymail/v/0.0.0/app/libraries/RainLoop/Model/Domain.php index 16479d7f0..e173c1418 100644 --- a/snappymail/v/0.0.0/app/libraries/RainLoop/Model/Domain.php +++ b/snappymail/v/0.0.0/app/libraries/RainLoop/Model/Domain.php @@ -1,12 +1,13 @@ Name = $sName; - +// $this->Name = \SnappyMail\IDN::toAscii($sName); + $this->Name = \strtolower(\idn_to_ascii($sName)); $this->IMAP = new \MailSo\Imap\Settings; $this->SMTP = new \MailSo\Smtp\Settings; $this->Sieve = new \MailSo\Sieve\Settings; } - /** - * Used by old ToIniString() - */ - public static function fromIniArray(string $sName, array $aDomain) : ?self - { - $oDomain = null; - if (\strlen($sName) && \strlen($aDomain['imap_host'])) { - $oDomain = new self($sName); - - $oDomain->IMAP->host = \SnappyMail\IDN::toUtf8($aDomain['imap_host']); - $oDomain->IMAP->port = (int) $aDomain['imap_port']; - $oDomain->IMAP->type = self::StrConnectionSecurityTypeToCons($aDomain['imap_secure'] ?? ''); - $oDomain->IMAP->shortLogin = !empty($aDomain['imap_short_login']); - - $oDomain->Sieve->enabled = !empty($aDomain['sieve_use']); - $oDomain->Sieve->host = \SnappyMail\IDN::toUtf8($aDomain['sieve_host']); - $oDomain->Sieve->port = (int) ($aDomain['sieve_port'] ?? 4190);; - $oDomain->Sieve->type = self::StrConnectionSecurityTypeToCons($aDomain['sieve_secure'] ?? ''); - - $oDomain->SMTP->host = \SnappyMail\IDN::toUtf8($aDomain['smtp_host']); - $oDomain->SMTP->port = (int) ($aDomain['smtp_port'] ?? 25); - $oDomain->SMTP->type = self::StrConnectionSecurityTypeToCons($aDomain['smtp_secure'] ?? ''); - $oDomain->SMTP->shortLogin = !empty($aDomain['smtp_short_login']); - $oDomain->SMTP->useAuth = !empty($aDomain['smtp_auth']); - $oDomain->SMTP->setSender = !empty($aDomain['smtp_set_sender']); - $oDomain->SMTP->usePhpMail = !empty($aDomain['smtp_php_mail']); - - $oDomain->whiteList = \trim($aDomain['white_list'] ?? ''); - - $oDomain->Normalize(); - } - return $oDomain; - } - - public function Normalize() + private function Normalize() { $this->IMAP->host = \trim($this->IMAP->host); $this->Sieve->host = \trim($this->Sieve->host); @@ -72,129 +39,91 @@ class Domain implements \JsonSerializable $this->whiteList = \trim($this->whiteList); } - /** - * Use by old fromIniArray() - */ - public static function StrConnectionSecurityTypeToCons(string $sType) : int - { - $iSecurityType = ConnectionSecurityType::NONE; - switch (\strtoupper($sType)) - { - case 'SSL': - $iSecurityType = ConnectionSecurityType::SSL; - break; - case 'TLS': - $iSecurityType = ConnectionSecurityType::STARTTLS; - break; - } - return $iSecurityType; - } - - /** - * deprecated - */ - public function SetConfig( - string $sIncHost, int $iIncPort, int $iIncSecure, bool $bIncShortLogin, - bool $bUseSieve, string $sSieveHost, int $iSievePort, int $iSieveSecure, - string $sOutHost, int $iOutPort, int $iOutSecure, bool $bOutShortLogin, - bool $bOutAuth, bool $bOutSetSender, bool $bOutUsePhpMail, - string $sWhiteList = '') : self - { - $this->IMAP->host = $sIncHost; - $this->IMAP->port = $iIncPort; - $this->IMAP->type = $iIncSecure; - $this->IMAP->shortLogin = $bIncShortLogin; - - $this->SMTP->host = $sOutHost; - $this->SMTP->port = $iOutPort; - $this->SMTP->type = $iOutSecure; - $this->SMTP->shortLogin = $bOutShortLogin; - $this->SMTP->useAuth = $bOutAuth; - $this->SMTP->setSender = $bOutSetSender; - $this->SMTP->usePhpMail = $bOutUsePhpMail; - - $this->Sieve->enabled = $bUseSieve; - $this->Sieve->host = $sSieveHost; - $this->Sieve->port = $iSievePort; - $this->Sieve->type = $iSieveSecure; - - $this->whiteList = \trim($sWhiteList); - - $this->Normalize(); - - return $this; - } - public function Name() : string { return $this->Name; } + /** + * @deprecated + */ public function IncHost() : string { + \trigger_error('Deprecated function called.', \E_USER_DEPRECATED); return $this->IMAP->host; } + /** + * @deprecated + */ public function IncPort() : int { + \trigger_error('Deprecated function called.', \E_USER_DEPRECATED); return $this->IMAP->port; } + /** + * @deprecated + */ public function IncShortLogin() : bool { + \trigger_error('Deprecated function called.', \E_USER_DEPRECATED); return $this->IMAP->shortLogin; } + /** + * @deprecated + */ public function UseSieve() : bool { + \trigger_error('Deprecated function called.', \E_USER_DEPRECATED); return $this->Sieve->enabled; } + /** + * @deprecated + */ public function OutHost() : string { + \trigger_error('Deprecated function called.', \E_USER_DEPRECATED); return $this->SMTP->host; } + /** + * @deprecated + */ public function OutPort() : int { + \trigger_error('Deprecated function called.', \E_USER_DEPRECATED); return $this->SMTP->port; } - public function OutShortLogin() : bool - { - return $this->SMTP->shortLogin; - } - - public function OutSetSender() : bool - { - return $this->SMTP->setSender; - } - - public function OutUsePhpMail() : bool - { - return $this->SMTP->usePhpMail; - } - public function SetAliasName(string $sAliasName) : void { - $this->aliasName = $sAliasName; +// $this->aliasName = \SnappyMail\IDN::toAscii($sAliasName); + $this->aliasName = \strtolower(\idn_to_ascii($sAliasName)); } - public function ValidateWhiteList(string $sEmail, string $sLogin = '') : bool + public function ValidateWhiteList(string $sEmail, string $sLogin) : bool { $sW = \trim($this->whiteList); - if (\strlen($sW)) - { - $sEmail = \MailSo\Base\Utils::IdnToUtf8($sEmail, true); - $sLogin = \MailSo\Base\Utils::IdnToUtf8($sLogin, true); - - $sW = \preg_replace('/([^\s]+)@[^\s]*/', '$1', $sW); - $sW = ' '.\trim(\preg_replace('/[\s;,\r\n\t]+/', ' ', $sW)).' '; - - $sUserPart = \MailSo\Base\Utils::GetAccountNameFromEmail(\strlen($sLogin) ? $sLogin : $sEmail); - return false !== \stripos($sW, ' '.$sUserPart.' '); + if ($sW) { + $sEmail = $this->IMAP->fixUsername($sEmail); + $sLogin = $this->IMAP->fixUsername($sLogin); + $sUserPart = \MailSo\Base\Utils::getEmailAddressLocalPart($sLogin ?: $sEmail); + $sItem = \strtok($sW, " ;,\n"); + while (false !== $sItem) { + $sItem = $this->IMAP->fixUsername(\trim($sItem)); + if ($sItem && ( + $sLogin === $sItem || $sEmail === $sItem + || $sUserPart === $sItem || \str_starts_with($sItem, "{$sUserPart}@") + )) { + return true; + } + $sItem = \strtok(" ;,\n"); + } + return false; } - return true; } @@ -203,16 +132,16 @@ class Domain implements \JsonSerializable return $this->IMAP; } - public function SmtpSettings() : \MailSo\Smtp\Settings - { - return $this->SMTP; - } - public function SieveSettings() : \MailSo\Sieve\Settings { return $this->Sieve; } + public function SmtpSettings() : \MailSo\Smtp\Settings + { + return $this->SMTP; + } + /** * See jsonSerialize() for valid values */ @@ -228,6 +157,7 @@ class Domain implements \JsonSerializable $oDomain->Sieve = \MailSo\Sieve\Settings::fromArray($aDomain['Sieve']); $oDomain->whiteList = (string) $aDomain['whiteList']; } else if (\strlen($aDomain['imapHost'])) { + // Old way $oDomain->IMAP->host = $aDomain['imapHost']; $oDomain->IMAP->port = (int) $aDomain['imapPort']; $oDomain->IMAP->type = (int) $aDomain['imapSecure']; @@ -255,12 +185,63 @@ class Domain implements \JsonSerializable return $oDomain; } + /** + * Used by old RainLoop ToIniString() + */ + public static function fromIniArray(string $sName, array $aDomain) : ?self + { + $oDomain = null; + if (\strlen($sName) && \strlen($aDomain['imap_host'])) { + $oDomain = new self($sName); + + $oDomain->IMAP->host = $aDomain['imap_host']; + $oDomain->IMAP->port = (int) $aDomain['imap_port']; + $oDomain->IMAP->type = self::StrConnectionSecurityTypeToCons($aDomain['imap_secure'] ?? ''); + $oDomain->IMAP->shortLogin = !empty($aDomain['imap_short_login']); + + $oDomain->Sieve->enabled = !empty($aDomain['sieve_use']); + $oDomain->Sieve->host = $aDomain['sieve_host']; + $oDomain->Sieve->port = (int) ($aDomain['sieve_port'] ?? 4190);; + $oDomain->Sieve->type = self::StrConnectionSecurityTypeToCons($aDomain['sieve_secure'] ?? ''); + + $oDomain->SMTP->host = $aDomain['smtp_host']; + $oDomain->SMTP->port = (int) ($aDomain['smtp_port'] ?? 25); + $oDomain->SMTP->type = self::StrConnectionSecurityTypeToCons($aDomain['smtp_secure'] ?? ''); + $oDomain->SMTP->shortLogin = !empty($aDomain['smtp_short_login']); + $oDomain->SMTP->useAuth = !empty($aDomain['smtp_auth']); + $oDomain->SMTP->setSender = !empty($aDomain['smtp_set_sender']); + $oDomain->SMTP->usePhpMail = !empty($aDomain['smtp_php_mail']); + + $oDomain->whiteList = \trim($aDomain['white_list'] ?? ''); + + $oDomain->Normalize(); + } + return $oDomain; + } + + /** + * Use by old RainLoop fromIniArray() + */ + public static function StrConnectionSecurityTypeToCons(string $sType) : int + { + $iSecurityType = ConnectionSecurityType::NONE; + switch (\strtoupper($sType)) + { + case 'SSL': + $iSecurityType = ConnectionSecurityType::SSL; + break; + case 'TLS': + $iSecurityType = ConnectionSecurityType::STARTTLS; + break; + } + return $iSecurityType; + } + #[\ReturnTypeWillChange] public function jsonSerialize() { $aResult = array( // '@Object' => 'Object/Domain', - 'name' => $this->Name, 'IMAP' => $this->IMAP, 'SMTP' => $this->SMTP, 'Sieve' => $this->Sieve, diff --git a/snappymail/v/0.0.0/app/libraries/RainLoop/Model/Identity.php b/snappymail/v/0.0.0/app/libraries/RainLoop/Model/Identity.php index 3cbd725ee..c18f13e0d 100644 --- a/snappymail/v/0.0.0/app/libraries/RainLoop/Model/Identity.php +++ b/snappymail/v/0.0.0/app/libraries/RainLoop/Model/Identity.php @@ -2,7 +2,6 @@ namespace RainLoop\Model; -use MailSo\Base\Utils; use SnappyMail\SensitiveString; class Identity implements \JsonSerializable @@ -102,7 +101,7 @@ class Identity implements \JsonSerializable if (!empty($aData['Email'])) { $this->sId = !empty($aData['Id']) ? $aData['Id'] : ''; $this->sLabel = isset($aData['Label']) ? $aData['Label'] : ''; - $this->sEmail = $bJson ? Utils::IdnToAscii($aData['Email'], true) : $aData['Email']; + $this->sEmail = $bJson ? \SnappyMail\IDN::emailToAscii($aData['Email']) : $aData['Email']; $this->sName = isset($aData['Name']) ? $aData['Name'] : ''; $this->sReplyTo = !empty($aData['ReplyTo']) ? $aData['ReplyTo'] : ''; $this->sBcc = !empty($aData['Bcc']) ? $aData['Bcc'] : ''; @@ -146,7 +145,7 @@ class Identity implements \JsonSerializable '@Object' => 'Object/Identity', 'id' => $this->sId, 'label' => $this->sLabel, - 'email' => Utils::IdnToUtf8($this->sEmail), + 'email' => \SnappyMail\IDN::emailToUtf8($this->sEmail), 'name' => $this->sName, 'replyTo' => $this->sReplyTo, 'bcc' => $this->sBcc, diff --git a/snappymail/v/0.0.0/app/libraries/RainLoop/Model/MainAccount.php b/snappymail/v/0.0.0/app/libraries/RainLoop/Model/MainAccount.php index f48a3b2b1..22f7d908f 100644 --- a/snappymail/v/0.0.0/app/libraries/RainLoop/Model/MainAccount.php +++ b/snappymail/v/0.0.0/app/libraries/RainLoop/Model/MainAccount.php @@ -4,65 +4,55 @@ namespace RainLoop\Model; use RainLoop\Utils; use RainLoop\Exceptions\ClientException; +use RainLoop\Notifications; use RainLoop\Providers\Storage\Enumerations\StorageType; use SnappyMail\SensitiveString; class MainAccount extends Account { private ?SensitiveString $sCryptKey = null; -/* - public function resealCryptKey( - #[\SensitiveParameter] - string $sOldPass, - #[\SensitiveParameter] - string $sNewPass - ) : bool + + public function resealCryptKey(SensitiveString $oOldPass) : bool { - $oStorage = \RainLoop\Api::Actions()->StorageProvider(); - $sKey = $oStorage->Get($this, StorageType::ROOT, '.cryptkey'); + $sKey = $this->CryptKey(); if ($sKey) { - $sKey = \SnappyMail\Crypt::DecryptUrlSafe($sKey, $sOldPass); + $sKey = \SnappyMail\Crypt::EncryptToJSON(\bin2hex($sKey), $this->IncPassword()); if ($sKey) { - $sKey = \SnappyMail\Crypt::EncryptUrlSafe($sKey, $sNewPass); + \RainLoop\Api::Actions()->StorageProvider()->Put($this, StorageType::ROOT, '.cryptkey', $sKey); + $sKey = \SnappyMail\Crypt::DecryptFromJSON($sKey, $this->IncPassword()); if ($sKey) { - $oStorage->Put($this, StorageType::ROOT, '.cryptkey', $sKey); - $sKey = \SnappyMail\Crypt::DecryptUrlSafe($sKey, $sNewPass); - $this->SetCryptKey($sKey); - return true; + $this->sCryptKey = new SensitiveString(\hex2bin($sKey)); } + return true; } } return false; } -*/ + public function CryptKey() : string { if (!$this->sCryptKey) { - $sKey = \sha1($this->IncPassword() . APP_SALT, true); -/* // Seal the cryptkey so that people who change their login password // can use the old password to re-seal the cryptkey $oStorage = \RainLoop\Api::Actions()->StorageProvider(); $sKey = $oStorage->Get($this, StorageType::ROOT, '.cryptkey'); if (!$sKey) { - $sKey = \sha1($this->IncPassword() . APP_SALT, true); - $sKey = \SnappyMail\Crypt::EncryptUrlSafe($sKey, $this->IncPassword()); + $sKey = \SnappyMail\Crypt::EncryptToJSON( + \sha1($this->IncPassword() . APP_SALT), + $this->IncPassword() + ); $oStorage->Put($this, StorageType::ROOT, '.cryptkey', $sKey); } - $sKey = \SnappyMail\Crypt::DecryptUrlSafe($sKey, $this->IncPassword()); -*/ - $this->SetCryptKey($sKey); + $sKey = \SnappyMail\Crypt::DecryptFromJSON($sKey, $this->IncPassword()); + if ($sKey) { + $this->sCryptKey = new SensitiveString(\hex2bin($sKey)); + } else { + throw new ClientException(Notifications::CryptKeyError); + } } return $this->sCryptKey; } - public function SetCryptKey( - #[\SensitiveParameter] - string $sKey - ) : void - { - $this->sCryptKey = new SensitiveString($sKey); - } /* // Stores settings in MainAccount public function settings() : \RainLoop\Settings diff --git a/snappymail/v/0.0.0/app/libraries/RainLoop/Notifications.php b/snappymail/v/0.0.0/app/libraries/RainLoop/Notifications.php index 6e376d4b9..036d6c078 100644 --- a/snappymail/v/0.0.0/app/libraries/RainLoop/Notifications.php +++ b/snappymail/v/0.0.0/app/libraries/RainLoop/Notifications.php @@ -17,6 +17,7 @@ class Notifications const ConnectionError = 104; const DomainNotAllowed = 109; const AccountNotAllowed = 110; + const CryptKeyError = 111; const ContactsSyncError = 140; @@ -58,7 +59,6 @@ class Notifications const MailServerError = 901; const ClientViewError = 902; const InvalidInputArgument = 903; -// const UnknownNotification = 998; const UnknownError = 999; // Admin diff --git a/snappymail/v/0.0.0/app/libraries/RainLoop/Providers/AddressBook/Classes/Contact.php b/snappymail/v/0.0.0/app/libraries/RainLoop/Providers/AddressBook/Classes/Contact.php index 3d88ba81d..8d08f28be 100644 --- a/snappymail/v/0.0.0/app/libraries/RainLoop/Providers/AddressBook/Classes/Contact.php +++ b/snappymail/v/0.0.0/app/libraries/RainLoop/Providers/AddressBook/Classes/Contact.php @@ -47,7 +47,7 @@ class Contact implements \JsonSerializable */ public function setVCard(VCard $oVCard) : void { - if ($oVCard->PHOTO && $oVCard->PHOTO->parameters['ENCODING']) { + if ($oVCard->PHOTO && !empty($oVCard->PHOTO->parameters['ENCODING'])) { $oVCard->VERSION = '3.0'; } if (VCard::VCARD40 != $oVCard->getDocumentType()) { diff --git a/snappymail/v/0.0.0/app/libraries/RainLoop/Providers/AddressBook/PdoAddressBook.php b/snappymail/v/0.0.0/app/libraries/RainLoop/Providers/AddressBook/PdoAddressBook.php index c072478c1..5d025bfaa 100644 --- a/snappymail/v/0.0.0/app/libraries/RainLoop/Providers/AddressBook/PdoAddressBook.php +++ b/snappymail/v/0.0.0/app/libraries/RainLoop/Providers/AddressBook/PdoAddressBook.php @@ -1161,7 +1161,7 @@ class PdoAddressBook { static $aCache = array(); - $sEmail = \MailSo\Base\Utils::IdnToAscii(\trim($sEmail), true); + $sEmail = \SnappyMail\IDN::emailToAscii(\trim($sEmail)); if (empty($sEmail)) { throw new \ValueError('Empty Email argument'); } diff --git a/snappymail/v/0.0.0/app/libraries/RainLoop/Providers/Domain/Autoconfig.php b/snappymail/v/0.0.0/app/libraries/RainLoop/Providers/Domain/Autoconfig.php index 9c101fe50..491d281c7 100644 --- a/snappymail/v/0.0.0/app/libraries/RainLoop/Providers/Domain/Autoconfig.php +++ b/snappymail/v/0.0.0/app/libraries/RainLoop/Providers/Domain/Autoconfig.php @@ -9,7 +9,10 @@ abstract class Autoconfig { public static function discover(string $emailaddress) : ?array { - $domain = \MailSo\Base\Utils::GetDomainFromEmail($emailaddress); +// $emailaddress = \SnappyMail\IDN::emailToAscii($emailaddress); + $domain = \MailSo\Base\Utils::getEmailAddressDomain($emailaddress); +// $domain = \SnappyMail\IDN::toAscii($domain); + $domain = \strtolower(\idn_to_ascii($domain)); // First try $autoconfig = static::resolve($domain, $emailaddress); if ($autoconfig) { @@ -89,7 +92,7 @@ abstract class Autoconfig private static function publicsuffixes() : array { $oCache = \RainLoop\Api::Actions()->Cacher(); - $list = $oCache->Get('public_suffix_list') ?: null; + $list = $oCache->Get('public_suffix_list'); if ($list) { $list = \json_decode($list, true); if ($list[1] < \time()) { diff --git a/snappymail/v/0.0.0/app/libraries/RainLoop/Providers/Domain/DefaultDomain.php b/snappymail/v/0.0.0/app/libraries/RainLoop/Providers/Domain/DefaultDomain.php index 7500739e3..de2c018c2 100644 --- a/snappymail/v/0.0.0/app/libraries/RainLoop/Providers/Domain/DefaultDomain.php +++ b/snappymail/v/0.0.0/app/libraries/RainLoop/Providers/Domain/DefaultDomain.php @@ -1,69 +1,69 @@ sDomainPath = \rtrim(\trim($sDomainPath), '\\/'); $this->oCacher = $oCacher; } - private static function codeFileName(string $sName, bool $bBack = false) : string + private static function decodeFileName(string $sName) : string { - if ($bBack && 'default' === $sName) { - return '*'; - } - - if (!$bBack && '*' === $sName) { - return 'default'; - } - - return $bBack - ? \str_replace('_wildcard_', '*', \MailSo\Base\Utils::IdnToUtf8($sName, true)) - : \str_replace('*', '_wildcard_', \MailSo\Base\Utils::IdnToAscii($sName, true)); + return ('default' === $sName) ? '*' : \str_replace('_wildcard_', '*', $sName); } - private static function wildcardDomainsCacheKey() : string + private static function encodeFileName(string $sName) : string { - return '/WildCard/DomainCache/'.\md5(APP_VERSION.APP_PRIVATE_DATA_NAME).'/'; +// return ('*' === $sName) ? 'default' : \str_replace('*', '_wildcard_', \SnappyMail\IDN::toAscii($sName)); + return ('*' === $sName) ? 'default' : \str_replace('*', '_wildcard_', \strtolower(\idn_to_ascii($sName))); } private function getWildcardDomainsLine() : string { if ($this->oCacher) { - $sResult = $this->oCacher->Get(static::wildcardDomainsCacheKey()); - if (\strlen($sResult)) { + $sResult = $this->oCacher->Get(static::CACHE_KEY); + if (\is_string($sResult)) { return $sResult; } } $aNames = array(); -// $aList = \glob($this->sDomainPath.'/*.{ini,json}', GLOB_BRACE); +// $aList = \glob($this->sDomainPath.'/*.{ini,json,alias}', GLOB_BRACE); $aList = \glob($this->sDomainPath.'/*.json'); foreach ($aList as $sFile) { $sName = \substr(\basename($sFile), 0, -5); if ('default' === $sName || false !== \strpos($sName, '_wildcard_')) { - $aNames[] = static::codeFileName($sName, true); + $aNames[] = static::decodeFileName($sName); } } $aList = \glob($this->sDomainPath.'/*.ini'); foreach ($aList as $sFile) { $sName = \substr(\basename($sFile), 0, -4); if ('default' === $sName || false !== \strpos($sName, '_wildcard_')) { - $aNames[] = static::codeFileName($sName, true); + $aNames[] = static::decodeFileName($sName); + } + } + $aList = \glob($this->sDomainPath.'/*.alias'); + foreach ($aList as $sFile) { + $sName = \substr(\basename($sFile), 0, -6); + if ('default' === $sName || false !== \strpos($sName, '_wildcard_')) { + $aNames[] = static::decodeFileName($sName); } } @@ -73,40 +73,35 @@ class DefaultDomain implements DomainInterface $sResult = \implode(' ', \array_unique($aNames)); } - if ($this->oCacher) { - $this->oCacher->Set(static::wildcardDomainsCacheKey(), $sResult); - } + $this->oCacher && $this->oCacher->Set(static::CACHE_KEY, $sResult); return $sResult; } public function Load(string $sName, bool $bFindWithWildCard = false, bool $bCheckDisabled = true, bool $bCheckAliases = true) : ?\RainLoop\Model\Domain { - if ($bCheckDisabled) { - $aDisabled = []; - if (\file_exists($this->sDomainPath.'/disabled')) { - $aDisabled = \explode(',', \file_get_contents($this->sDomainPath.'/disabled')); - if ($aDisabled && \in_array(\MailSo\Base\Utils::IdnToAscii($sName, true), $aDisabled)) { - return null; - } - } +// $sName = \SnappyMail\IDN::toAscii($sName); + $sName = \strtolower(\idn_to_ascii($sName)); + if ($bCheckDisabled && \in_array($sName, $this->getDisabled())) { + return null; } - $sRealFileName = static::codeFileName($sName); + $sRealFileBase = $this->sDomainPath . '/' . static::encodeFileName($sName); - if (\file_exists($this->sDomainPath.'/'.$sRealFileName.'.json')) { - $aDomain = \json_decode(\file_get_contents($this->sDomainPath.'/'.$sRealFileName.'.json'), true) ?: array(); + if (\file_exists($sRealFileBase.'.json')) { + $aDomain = \json_decode(\file_get_contents($sRealFileBase.'.json'), true) ?: array(); return \RainLoop\Model\Domain::fromArray($sName, $aDomain); } - if (\file_exists($this->sDomainPath.'/'.$sRealFileName.'.ini')) { - $aDomain = \parse_ini_file($this->sDomainPath.'/'.$sRealFileName.'.ini') ?: array(); + + if (\file_exists($sRealFileBase.'.ini')) { + $aDomain = \parse_ini_file($sRealFileBase.'.ini') ?: array(); return \RainLoop\Model\Domain::fromIniArray($sName, $aDomain); } - if ($bCheckAliases && \file_exists($this->sDomainPath.'/'.$sRealFileName.'.alias')) { - $sAlias = \trim(\file_get_contents($this->sDomainPath.'/'.$sRealFileName.'.alias')); - if (!empty($sAlias)) { - $oDomain = $this->Load($sAlias, false, false, false); + if ($bCheckAliases && \file_exists($sRealFileBase.'.alias')) { + $sTarget = \trim(\file_get_contents($sRealFileBase.'.alias')); + if (!empty($sTarget)) { + $oDomain = $this->Load($sTarget, false, false, false); $oDomain && $oDomain->SetAliasName($sName); return $oDomain; } @@ -116,10 +111,10 @@ class DefaultDomain implements DomainInterface $sNames = $this->getWildcardDomainsLine(); $sFoundValue = ''; if (\strlen($sNames) - && \RainLoop\Plugins\Helper::ValidateWildcardValues(\MailSo\Base\Utils::IdnToUtf8($sName, true), $sNames, $sFoundValue) + && \RainLoop\Plugins\Helper::ValidateWildcardValues($sName, $sNames, $sFoundValue) && \strlen($sFoundValue) ) { - return $this->Load($sFoundValue, false); + return $this->Load($sFoundValue); } } @@ -128,111 +123,78 @@ class DefaultDomain implements DomainInterface public function Save(\RainLoop\Model\Domain $oDomain) : bool { - $sRealFileName = static::codeFileName($oDomain->Name()); - - if ($this->oCacher) - { - $this->oCacher->Delete(static::wildcardDomainsCacheKey()); - } - + $this->Delete($oDomain->Name()); + $sRealFileName = static::encodeFileName($oDomain->Name()); \RainLoop\Utils::saveFile($this->sDomainPath.'/'.$sRealFileName.'.json', \json_encode($oDomain, \JSON_PRETTY_PRINT)); - + $this->oCacher && $this->oCacher->Delete(static::CACHE_KEY); return true; } - public function SaveAlias(string $sName, string $sAlias) : bool + public function SaveAlias(string $sName, string $sTarget) : bool { - $sRealFileName = static::codeFileName($sName); - - if ($this->oCacher) - { - $this->oCacher->Delete(static::wildcardDomainsCacheKey()); - } - - \RainLoop\Utils::saveFile($this->sDomainPath.'/'.$sRealFileName.'.alias', $sAlias); +// $this->Delete($sName); +// $sTarget = \SnappyMail\IDN::toAscii($sTarget); + $sTarget = \strtolower(\idn_to_ascii($sTarget)); + $sRealFileName = static::encodeFileName($sName); + \RainLoop\Utils::saveFile($this->sDomainPath.'/'.$sRealFileName.'.alias', $sTarget); + $this->oCacher && $this->oCacher->Delete(static::CACHE_KEY); return true; } + protected function getDisabled() : array + { + $sFile = ''; + if (\file_exists($this->sDomainPath.'/disabled')) { + $sFile = \file_get_contents($this->sDomainPath.'/disabled'); + } + $aDisabled = array(); + // RainLoop use comma, we use newline + $sItem = \strtok($sFile, ",\n"); + while (false !== $sItem) { +// $aDisabled[] = \SnappyMail\IDN::toAscii($sItem); + $aDisabled[] = \strtolower(\idn_to_ascii($sItem)); + $sItem = \strtok(",\n"); + } + return $aDisabled; +// return \array_unique($aDisabled); + } + public function Disable(string $sName, bool $bDisable) : bool { - $sName = \MailSo\Base\Utils::IdnToAscii($sName, true); - - $sFile = ''; - if (\file_exists($this->sDomainPath.'/disabled')) - { - $sFile = \file_get_contents($this->sDomainPath.'/disabled'); - } - - $aResult = array(); - $aNames = \explode(',', $sFile); - if ($bDisable) - { - \array_push($aNames, $sName); - $aResult = $aNames; - } - else - { - foreach ($aNames as $sItem) - { - if ($sName !== $sItem) - { - $aResult[] = $sItem; - } +// $sName = \SnappyMail\IDN::toAscii($sName); + $sName = \strtolower(\idn_to_ascii($sName)); + if ($sName) { + $aResult = $this->getDisabled(); + if ($bDisable) { + $aResult[] = $sName; + } else { + $aResult = \array_filter($aResult, fn($v) => $v !== $sName); } + \RainLoop\Utils::saveFile($this->sDomainPath.'/disabled', \implode("\n", \array_unique($aResult))); } - - \RainLoop\Utils::saveFile($this->sDomainPath.'/disabled', \trim(\implode(',', \array_unique($aResult)), ', ')); return true; } public function Delete(string $sName) : bool { $bResult = true; - $sRealFileName = static::codeFileName($sName); - - if (\strlen($sName)) - { - if (\file_exists($this->sDomainPath.'/'.$sRealFileName.'.json')) - { - $bResult = \unlink($this->sDomainPath.'/'.$sRealFileName.'.json'); - } - if (\file_exists($this->sDomainPath.'/'.$sRealFileName.'.ini')) - { - $bResult = \unlink($this->sDomainPath.'/'.$sRealFileName.'.ini'); - } - else if (\file_exists($this->sDomainPath.'/'.$sRealFileName.'.alias')) - { - $bResult = \unlink($this->sDomainPath.'/'.$sRealFileName.'.alias'); + if (\strlen($sName)) { + $sRealFileName = $this->sDomainPath . '/' . static::encodeFileName($sName); + $bResult = + (!\file_exists($sRealFileName.'.json') || \unlink($sRealFileName.'.json')) + && (!\file_exists($sRealFileName.'.ini') || \unlink($sRealFileName.'.ini')) + && (!\file_exists($sRealFileName.'.alias') || \unlink($sRealFileName.'.alias')); + $this->Disable($sName, !$bResult); + if ($this->oCacher) { + $this->oCacher->Delete(static::CACHE_KEY); } } - - if ($bResult) - { - $this->Disable($sName, false); - } - - if ($this->oCacher) - { - $this->oCacher->Delete(static::wildcardDomainsCacheKey()); - } - return $bResult; } public function GetList(bool $bIncludeAliases = true) : array { - $aDisabledNames = array(); - if (\file_exists($this->sDomainPath.'/disabled')) { - $sDisabled = \file_get_contents($this->sDomainPath.'/disabled'); - if (\strlen($sDisabled)) { - $aDisabledNames = \explode(',', $sDisabled); - foreach ($aDisabledNames as &$sValue) { - $sValue = \MailSo\Base\Utils::IdnToUtf8($sValue, true); - } - $aDisabledNames = \array_unique($aDisabledNames); - } - } - + $aDisabledNames = $this->getDisabled(); $aResult = array(); $aWildCards = array(); $aAliases = array(); @@ -242,7 +204,7 @@ class DefaultDomain implements DomainInterface foreach ($aList as $sFile) { $bAlias = '.alias' === \substr($sFile, -6); if ($bAlias || '.json' === \substr($sFile, -5) || '.ini' === \substr($sFile, -4)) { - $sName = static::codeFileName(\preg_replace('/\.(ini|json|alias)$/', '', $sFile), true); + $sName = static::decodeFileName(\preg_replace('/\.(ini|json|alias)$/', '', $sFile)); if ($bAlias) { if ($bIncludeAliases) { $aAliases[$sName] = array( diff --git a/snappymail/v/0.0.0/app/libraries/RainLoop/Service.php b/snappymail/v/0.0.0/app/libraries/RainLoop/Service.php index 131068985..e46e2541c 100644 --- a/snappymail/v/0.0.0/app/libraries/RainLoop/Service.php +++ b/snappymail/v/0.0.0/app/libraries/RainLoop/Service.php @@ -84,9 +84,9 @@ abstract class Service $aPaths = \explode('/', $sQuery); - $sAdminPanelHost = \trim($oConfig->Get('security', 'admin_panel_host', '')); + $sAdminPanelHost = \trim($oConfig->Get('admin_panel', 'host', '')); if (empty($sAdminPanelHost)) { - $bAdmin = !empty($aPaths[0]) && ($oConfig->Get('security', 'admin_panel_key', '') ?: 'admin') === $aPaths[0]; + $bAdmin = !empty($aPaths[0]) && ($oConfig->Get('admin_panel', 'key', '') ?: 'admin') === $aPaths[0]; $bAdmin && \array_shift($aPaths); } else { $bAdmin = \mb_strtolower($sAdminPanelHost) === \mb_strtolower($oHttp->GetHost()); diff --git a/snappymail/v/0.0.0/app/libraries/RainLoop/ServiceActions.php b/snappymail/v/0.0.0/app/libraries/RainLoop/ServiceActions.php index 5e7092b26..667a01473 100644 --- a/snappymail/v/0.0.0/app/libraries/RainLoop/ServiceActions.php +++ b/snappymail/v/0.0.0/app/libraries/RainLoop/ServiceActions.php @@ -218,7 +218,7 @@ class ServiceActions } else if (empty($_FILES)) { $iError = UPLOAD_ERR_INI_SIZE; } else { - $iError = Enumerations\UploadError::EMPTY_FILES_DATA; + $iError = Enumerations\UploadError::EMPTY_FILE; } if (\method_exists($this->oActions, $sAction) && \is_callable(array($this->oActions, $sAction))) { @@ -389,7 +389,7 @@ class ServiceActions $sResult = $this->Cacher()->Get($sCacheFileName); } - if (!\strlen($sResult)) { + if (!$sResult) { $sResult = $this->oActions->compileLanguage($sLanguage, $bAdmin); if ($sCacheFileName) { $this->Cacher()->Set($sCacheFileName, $sResult); @@ -458,7 +458,7 @@ class ServiceActions $bCacheEnabled = !$bAppDebug && $this->Config()->Get('cache', 'system_data', true); $sCacheFileName = ''; if ($bCacheEnabled) { - $sCacheFileName = KeyPathHelper::CssCache($sTheme, $this->oActions->Plugins()->Hash()) . $sMinify; + $sCacheFileName = '/CssCache/'.$this->oActions->Plugins()->Hash().'/'.$sTheme.'/'.APP_VERSION.'/' . $sMinify; $this->oActions->verifyCacheByKey(\md5($sCacheFileName . ($bJson ? 1 : 0))); $sResult = $this->Cacher()->Get($sCacheFileName); } diff --git a/snappymail/v/0.0.0/app/libraries/TNEFDecoder/LICENSE b/snappymail/v/0.0.0/app/libraries/TNEFDecoder/LICENSE new file mode 100644 index 000000000..d159169d1 --- /dev/null +++ b/snappymail/v/0.0.0/app/libraries/TNEFDecoder/LICENSE @@ -0,0 +1,339 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Lesser General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. diff --git a/snappymail/v/0.0.0/app/libraries/TNEFDecoder/TNEFAttachment.php b/snappymail/v/0.0.0/app/libraries/TNEFDecoder/TNEFAttachment.php new file mode 100644 index 000000000..973854786 --- /dev/null +++ b/snappymail/v/0.0.0/app/libraries/TNEFDecoder/TNEFAttachment.php @@ -0,0 +1,334 @@ + + * Copyright (c) 2003 Bernd Wiegmann + * Copyright (c) 2002 Graham Norburys + * + * Licensed under the GNU GPL. For full terms see the file COPYING. + * + * @package plugins + * @subpackage tnef_decoder + * + */ + +function tnef_log($string) +{ + echo $string . "\n"; +// \error_log($string . "\n", 3, '/tmp/squirrelmail_tnef_decoder.log'); +// \SnappyMail\Log::debug('TNEF', $string); +} + +class TNEFAttachment +{ + + public bool $debug; + public bool $validateChecksum; + public TNEFMailinfo $mailinfo; + public array + $files = [], + $attachments = [], + $body = []; + public ?array $files_nested = null; + public ?TNEFFile $current_receiver = null; + + public function __construct(bool $debug = false, bool $validateChecksum = false) + { + $this->debug = $debug; + $this->validateChecksum = $validateChecksum; + $this->mailinfo = new TNEFMailinfo(); + } + + /** + * @return TNEFFileBase[] + */ + public function &getFiles(): array + { + return $this->files; + } + + /** + * @return TNEFFileBase[] + */ + public function &getFilesNested(): array + { + if (null === $this->files_nested) { + $this->files_nested = array(); + $this->addFilesNested($this->files); + foreach ($this->attachments as $attachment) { + $this->addFilesNested($attachment->getFilesNested()); + } + } + return $this->files_nested; + } + + private function addFilesNested(array &$add): void + { + foreach ($add as $file) { + $this->files_nested[] = &$file; + } + } + + public function getAttachments(): array + { + return $this->attachments; + } + + public function getMailinfo(): TNEFMailinfo + { + return $this->mailinfo; + } + + public function getBodyElements(): array + { + return $this->body; + } + + public function decodeTnef($data): void + { + $buffer = new TNEFBuffer($data); + + $tnef_signature = $buffer->geti32(); + if (TNEF_SIGNATURE == $tnef_signature) { + $tnef_key = $buffer->geti16(); + $this->debug && tnef_log(\sprintf("Signature: 0x%08x\nKey: 0x%04x\n", $tnef_signature, $tnef_key)); + + while ($buffer->getRemainingBytes() > 0) { + $lvl_type = $buffer->geti8(); + + switch ($lvl_type) { + case TNEF_LVL_MESSAGE: + case TNEF_LVL_ATTACHMENT: + $this->tnef_decode_attribute($buffer); + break; + + default: + if ($this->debug) { + $len = $buffer->getRemainingBytes(); + if ($len) + tnef_log("Invalid file format! Unknown Level {$lvl_type}. Rest={$len}"); + } + break; + } + } + } else { + throw new \RuntimeException("TNEF: Invalid file format! Wrong signature."); + } + + // propagate parent message's code page to child files if given + // + $code_page = $this->mailinfo->getCodePage(); + if (!empty($code_page)) + foreach ($this->files as $i => $file) + $this->files[$i]->setMessageCodePage($code_page); + } + + private function tnef_decode_attribute(TNEFBuffer $buffer) + { + $attribute = $buffer->geti32(); // attribute if + $length = $buffer->geti32(); // length + $value = $buffer->getBytes($length); // data + $checksumAtt = $buffer->geti16(); // checksum + if ($value !== null && $this->validateChecksum) { + $checksum = \array_sum(\unpack('C*', $value)) & 0xFFFF; + if ($checksum !== $checksumAtt) { + throw new \Exception('Checksums do not match'); + } + } + + switch ($attribute) + { + case TNEF_ARENDDATA: // marks start of new attachment + $this->debug && tnef_log("Creating new File for Attachment"); + $this->current_receiver = new TNEFFile($this->debug); + $this->files[] = $this->current_receiver; + break; + + case TNEF_AMAPIATTRS: + $this->debug && tnef_log("mapi attrs"); + $this->extract_mapi_attrs(new TNEFBuffer($value)); + break; + + case TNEF_AMAPIPROPS: + $this->debug && tnef_log("mapi props"); + $this->extract_mapi_attrs(new TNEFBuffer($value)); + break; + + case TNEF_AMCLASS: + $value = substr($value, 0, $length - 1); + if ($value == 'IPM.Contact') { + $this->debug && tnef_log("Creating vCard Attachment"); + $this->current_receiver = new TNEFvCard($this->debug); + $this->files[] = $this->current_receiver; + } + break; + + default: + $this->mailinfo->receiveTnefAttribute($attribute, $value, $length); + if ($this->current_receiver) + $this->current_receiver->receiveTnefAttribute($attribute, $value, $length); + break; + } + } + + private function extract_mapi_attrs(TNEFBuffer $buffer): void + { + + $number = $buffer->geti32(); // number of attributes + $props = 0; + $ended = 0; + + while (($buffer->getRemainingBytes() > 0) && ($props++ < $number) && !$ended) { + $value = ''; + unset($named_id); + $length = 0; + $have_multivalue = false; + $num_multivalues = 1; + $attr_type = $buffer->geti16(); + $attr_name = $buffer->geti16(); + + if (($attr_type & TNEF_MAPI_MV_FLAG) != 0) { + $this->debug && tnef_log("Multivalue Attribute found."); + $have_multivalue = true; + $attr_type = $attr_type & ~TNEF_MAPI_MV_FLAG; + } + + // Named Attribute + if (($attr_name >= 0x8000) && ($attr_name < 0xFFFE)) { + $guid = $buffer->getBytes(16); + $named_type = $buffer->geti32(); + switch ($named_type) + { + case TNEF_MAPI_NAMED_TYPE_ID: + $named_id = $buffer->geti32(); + $attr_name = $named_id; + $this->debug && tnef_log(sprintf("Named Id='0x%04x'", $named_id)); + break; + + case TNEF_MAPI_NAMED_TYPE_STRING: + $attr_name = 0x9999; // dummy to identify strings + $idlen = $buffer->geti32(); + $this->debug && tnef_log("idlen={$idlen}"); + $buflen = $idlen + ((4 - ($idlen % 4)) % 4); // pad to next 4 byte boundary + $this->debug && tnef_log("buflen={$buflen}"); + $named_id = substr($buffer->getBytes($buflen), 0, $idlen ); // read and truncate to length + $this->debug && tnef_log("Named Id='{$named_id}'"); + break; + + default: + $this->debug && tnef_log(\sprintf("Unknown Named Type 0x%04x found", $named_type)); + break; + } + } + + if ($have_multivalue) { + $num_multivalues = $buffer->geti32(); + $this->debug && tnef_log("Number of multivalues={$num_multivalues}"); + } + + switch ($attr_type) + { + case TNEF_MAPI_NULL: + break; + + case TNEF_MAPI_SHORT: + $value = $buffer->geti16(); + break; + + case TNEF_MAPI_INT: + case TNEF_MAPI_BOOLEAN: + for ($cnt = 0; $cnt < $num_multivalues; $cnt++) + $value = $buffer->geti32(); + break; + + case TNEF_MAPI_FLOAT: + case TNEF_MAPI_ERROR: + $value = $buffer->getBytes(4); + break; + + case TNEF_MAPI_DOUBLE: + case TNEF_MAPI_APPTIME: + case TNEF_MAPI_CURRENCY: + case TNEF_MAPI_INT8BYTE: + case TNEF_MAPI_SYSTIME: + $value = $buffer->getBytes(8); + break; + + case TNEF_MAPI_CLSID: + $this->debug && tnef_log("What is a MAPI CLSID ????"); + break; + + case TNEF_MAPI_STRING: + case TNEF_MAPI_UNICODE_STRING: + case TNEF_MAPI_BINARY: + case TNEF_MAPI_OBJECT: + $num_vals = $have_multivalue ? $num_multivalues : $buffer->geti32(); + + if ($num_vals > 20) { + // A Sanity check. + $ended = 1; + $this->debug && tnef_log("Number of entries in String Attributes={$num_vals}. Aborting Mapi parsing."); + } else { + for ($cnt = 0; $cnt < $num_vals; ++$cnt) { + $length = $buffer->geti32(); + $buflen = $length + ((4 - ($length % 4)) % 4); // pad to next 4 byte boundary + if ($attr_type == TNEF_MAPI_STRING) + $length -= 1; + $value = \substr($buffer->getBytes($buflen), 0, $length); // read and truncate to length + } + } + break; + + default: + $this->debug && tnef_log("Unknown mapi attribute! {$attr_type}"); + break; + } + + switch ($attr_name) + { + case TNEF_MAPI_ATTACH_DATA: + $this->debug && tnef_log("MAPI Found nested attachment. Processing new one."); + $value = substr($value, 16); // skip the next 16 bytes (unknown data) + $att = new TNEFAttachment($this->debug, $this->validateChecksum); + $att->decodeTnef($value); + $this->attachments[] = $att; + $this->debug && tnef_log("MAPI Finished nested attachment. Continuing old one."); + break; + + case TNEF_MAPI_RTF_COMPRESSED: + $this->debug && tnef_log("MAPI Found Compressed RTF Attachment."); + $this->files[] = new TNEFFileRTF($this->debug, $value); + break; + case TNEF_MAPI_BODY: + case TNEF_MAPI_BODY_HTML: + $result = []; + $result['type'] = 'text'; + $result['subtype'] = $attr_name == TNEF_MAPI_BODY ? 'plain' : 'html'; + $result['name'] = ('Untitled') . ($attr_name == TNEF_MAPI_BODY ? '.txt' : '.html'); + $result['stream'] = $value; + $result['size'] = strlen($value); + $this->body[] = $result; + break; + default: + $this->mailinfo->receiveMapiAttribute($attr_type, $attr_name, $value, $length); + if ($this->current_receiver) + $this->current_receiver->receiveMapiAttribute($attr_type, $attr_name, $value, $length); + break; + } + } + if ($this->debug && $ended) { + $len = $buffer->getRemainingBytes(); + for ($cnt = 0; $cnt < $len; ++$cnt) { + $ord = $buffer->geti8(); + $char = $ord ? \chr($ord) : ''; + tnef_log(\sprintf("Char Nr. %6d = 0x%02x = '%s'", $cnt, $ord, $char)); + } + } + } +} diff --git a/snappymail/v/0.0.0/app/libraries/TNEFDecoder/TNEFBuffer.php b/snappymail/v/0.0.0/app/libraries/TNEFDecoder/TNEFBuffer.php new file mode 100644 index 000000000..d4dded488 --- /dev/null +++ b/snappymail/v/0.0.0/app/libraries/TNEFDecoder/TNEFBuffer.php @@ -0,0 +1,66 @@ + + * Copyright (c) 2003 Bernd Wiegmann + * Copyright (c) 2002 Graham Norburys + * + * Licensed under the GNU GPL. For full terms see the file COPYING. + * + * @package plugins + * @subpackage tnef_decoder + * + */ + +class TNEFBuffer +{ + private string $data; + private int $offset = 0; + + public function __construct(string $data) + { + $this->data = $data; + } + + public function getBytes(int $numBytes): ?string + { + if ($this->getRemainingBytes() < $numBytes) { + $this->offset = \strlen($this->data); + return null; + } + + $this->offset += $numBytes; + return \substr($this->data, $this->offset - $numBytes, $numBytes); + } + + public function getRemainingBytes(): int + { + return \strlen($this->data) - $this->offset; + } + + public function geti8(): ?int + { + $bytes = $this->getBytes(1); + return (null === $bytes) ? null : \ord($bytes[0]); + } + + public function geti16(): ?int + { + $bytes = $this->getBytes(2); + return (null === $bytes) ? null : \ord($bytes[0]) + (\ord($bytes[1]) << 8); + } + + public function geti32(): ?int + { + $bytes = $this->getBytes(4); + return (null === $bytes) ? null + : \ord($bytes[0]) + + (\ord($bytes[1]) << 8) + + (\ord($bytes[2]) << 16) + + (\ord($bytes[3]) << 24); + } +} diff --git a/snappymail/v/0.0.0/app/libraries/TNEFDecoder/TNEFDate.php b/snappymail/v/0.0.0/app/libraries/TNEFDecoder/TNEFDate.php new file mode 100644 index 000000000..369f88a4c --- /dev/null +++ b/snappymail/v/0.0.0/app/libraries/TNEFDecoder/TNEFDate.php @@ -0,0 +1,37 @@ + + * Copyright (c) 2003 Bernd Wiegmann + * Copyright (c) 2002 Graham Norburys + * + * Licensed under the GNU GPL. For full terms see the file COPYING. + * + * @package plugins + * @subpackage tnef_decoder + * + */ + +class TNEFDate extends \DateTime +{ + public function __construct(string $datetime = "now", ?\DateTimeZone $timezone = null) + { + parent::__construct($datetime, new \DateTimeZone('UTC')); + } + + public function setTnefBuffer(TNEFBuffer $buffer) + { + $this->setDate( + $buffer->geti16(), // year + $buffer->geti16(), // month + $buffer->geti16() // day + ); + $this->setTime( + $buffer->geti16(), // hour + $buffer->geti16(), // minute + $buffer->geti16() // second + ); + } +} diff --git a/snappymail/v/0.0.0/app/libraries/TNEFDecoder/TNEFFile.php b/snappymail/v/0.0.0/app/libraries/TNEFDecoder/TNEFFile.php new file mode 100644 index 000000000..2b96c56d0 --- /dev/null +++ b/snappymail/v/0.0.0/app/libraries/TNEFDecoder/TNEFFile.php @@ -0,0 +1,120 @@ + + * Copyright (c) 2003 Bernd Wiegmann + * Copyright (c) 2002 Graham Norburys + * + * Licensed under the GNU GPL. For full terms see the file COPYING. + * + * @package plugins + * @subpackage tnef_decoder + * + */ + +class TNEFFile extends TNEFFileBase +{ + + public string $metafile; + + public function getMetafile() + { + return $this->metafile; + } + + public function receiveTnefAttribute(int $attribute, string $value, int $length): void + { + switch ($attribute) + { + + // filename + // + case TNEF_AFILENAME: + // strip path + // + if (($pos = \strrpos($value, '/')) !== FALSE) + $this->name = \substr($value, $pos + 1); + else + $this->name = $value; + + // Strip trailing null bytes if present + $this->name = \trim($this->name); + break; + // code page + // + case TNEF_AOEMCODEPAGE: + $this->code_page = (new TNEFBuffer($value))->geti16(); + break; + + // the attachment itself + // + case TNEF_ATTACHDATA: + $this->content = $value; + break; + + // a metafile + // + case TNEF_ATTACHMETAFILE: + $this->metafile = $value; + break; + + case TNEF_AATTACHCREATEDATE: + $this->created = new TNEFDate(); + $this->created->setTnefBuffer(new TNEFBuffer($value)); + + case TNEF_AATTACHMODDATE: + $this->modified = new TNEFDate(); + $this->modified->setTnefBuffer(new TNEFBuffer($value)); + break; + } + } + + public function receiveMapiAttribute(int $attr_type, int $attr_name, string $value, int $length): void + { + switch ($attr_name) + { + + // used in preference to AFILENAME value + // + case TNEF_MAPI_ATTACH_LONG_FILENAME: + // strip path + // + if (($pos = \strrpos($value, '/')) !== FALSE) + $this->name = \substr($value, $pos + 1); + else + $this->name = $value; + + $this->name_is_unicode = TNEF_MAPI_UNICODE_STRING === $attr_type; + break; + + // Is this ever set, and what is format? + // + case TNEF_MAPI_ATTACH_MIME_TAG: + $type0 = $type1 = ''; + $mime_type = \explode('/', $value, 2); + if (!empty($mime_type[0])) + $type0 = $mime_type[0]; + if (!empty($mime_type[1])) + $type1 = $mime_type[1]; + $this->type = "{$type0}/{$type1}"; + if (TNEF_MAPI_UNICODE_STRING === $attr_type) { + $this->type = \substr(\mb_convert_encoding($this->type, "UTF-8" , "UTF-16LE"), 0, -1); + } + break; + + case TNEF_MAPI_ATTACH_EXTENSION: + $type = \SnappyMail\File\MimeType::fromFilename($value); + if ($type) + $this->type = $type; + break; + } + } + +} + + + diff --git a/snappymail/v/0.0.0/app/libraries/TNEFDecoder/TNEFFileBase.php b/snappymail/v/0.0.0/app/libraries/TNEFDecoder/TNEFFileBase.php new file mode 100644 index 000000000..6d36dede2 --- /dev/null +++ b/snappymail/v/0.0.0/app/libraries/TNEFDecoder/TNEFFileBase.php @@ -0,0 +1,82 @@ + + * Copyright (c) 2003 Bernd Wiegmann + * Copyright (c) 2002 Graham Norburys + * + * Licensed under the GNU GPL. For full terms see the file COPYING. + * + * @package plugins + * @subpackage tnef_decoder + * + */ + +class TNEFFileBase +{ + public bool $name_is_unicode = FALSE; + public string + $name = 'Untitled', + $code_page = '', + $message_code_page = '', // parent message's code page (the whole TNEF file) + $type = 'application/octet-stream', + $content = ''; + public ?TNEFDate + $created = null, + $modified = null; + public bool $debug; + + public function __construct(bool $debug) + { + $this->debug = $debug; + } + + public function setMessageCodePage(string $code_page): void + { + $this->message_code_page = $code_page; + } + + public function getCodePage(): string + { + return empty($this->code_page) + ? $this->message_code_page + : $this->code_page; + } + + public function getName(): string + { + return $this->name_is_unicode + ? \substr(\mb_convert_encoding($this->name, "UTF-8" , "UTF-16LE"), 0, -1) + : $this->name; + } + + public function getType(): string + { + return $this->type; + } + + public function getSize(): int + { + return \strlen($this->content); + } + + public function getCreated(): ?TNEFDate + { + return $this->created; + } + + public function getModified(): ?TNEFDate + { + return $this->modified; + } + + public function getContent(): string + { + return $this->content; + } + +} diff --git a/snappymail/v/0.0.0/app/libraries/TNEFDecoder/TNEFFileRTF.php b/snappymail/v/0.0.0/app/libraries/TNEFDecoder/TNEFFileRTF.php new file mode 100644 index 000000000..e477390b5 --- /dev/null +++ b/snappymail/v/0.0.0/app/libraries/TNEFDecoder/TNEFFileRTF.php @@ -0,0 +1,110 @@ + + * Copyright (c) 2003 Bernd Wiegmann + * Copyright (c) 2002 Graham Norburys + * + * Licensed under the GNU GPL. For full terms see the file COPYING. + * + * @package plugins + * @subpackage tnef_decoder + * + */ +class TNEFFileRTF extends TNEFFileBase +{ + public string $name = 'EmbeddedRTF.rtf'; + public string $type = 'application/rtf'; + protected int $size = 0; + const MAX_DICT_SIZE = 4096; + const INIT_DICT_SIZE = 207; + + public function __construct($debug, $data) + { + parent::__construct($debug); + $this->decode_crtf(new TNEFBuffer($data)); + } + + public function getSize(): int + { + return $this->size; + } + + public function decode_crtf(TNEFBuffer $buffer) + { + $size_compressed = $buffer->geti32(); + $this->size = $buffer->geti32(); + $magic = $buffer->geti32(); + $crc32 = $buffer->geti32(); + + $this->debug && tnef_log("CRTF: size comp={$size_compressed}, size={$this->size}"); + + $data = $buffer->getBytes($buffer->getRemainingBytes()); + + switch ($magic) { + case CRTF_COMPRESSED: + $this->uncompress($data); + break; + + case CRTF_UNCOMPRESSED: + $this->content = $data; + break; + + default: + $this->debug && tnef_log("Unknown Compressed RTF Format"); + break; + } + } + + public function uncompress($data) + { + $preload = "{\\rtf1\\ansi\\mac\\deff0\\deftab720{\\fonttbl;}{\\f0\\fnil \\froman \\fswiss \\fmodern \\fscript \\fdecor MS Sans SerifSymbolArialTimes New RomanCourier{\\colortbl\\red0\\green0\\blue0\n\r\\par \\pard\\plain\\f0\\fs20\\b\\i\\u\\tab\\tx"; + $length_preload = \strlen($preload); + $init_dict = []; + for ($cnt = 0; $cnt < $length_preload; ++$cnt) { + $init_dict[$cnt] = $preload[$cnt]; + } + $init_dict = \array_merge($init_dict, \array_fill(\count($init_dict), self::MAX_DICT_SIZE - $length_preload, ' ')); + $write_offset = self::INIT_DICT_SIZE; + $this->content = ''; + $end = false; + $in = 0; + $l = \strlen($data); + while (!$end) { + if ($in >= $l) { + break; + } + $control = \strrev(\str_pad(\decbin(\ord($data[$in++])), 8, 0, STR_PAD_LEFT)); + for ($i = 0; $i < 8; ++$i) { + if ($control[$i] == '1') { + $token = \unpack("n", $data[$in++] . $data[$in++])[1]; + $offset = ($token >> 4) & 0b111111111111; + $length = $token & 0b1111; + if ($write_offset == $offset) { + $end = true; + break; + } + $actual_length = $length + 2; + for ($step = 0; $step < $actual_length; ++$step) { + $read_offset = ($offset + $step) % self::MAX_DICT_SIZE; + $char = $init_dict[$read_offset]; + $this->content .= $char; + $init_dict[$write_offset] = $char; + $write_offset = ($write_offset + 1) % self::MAX_DICT_SIZE; + } + } else { + if ($in >= $l) { + break; + } + $val = $data[$in++]; + $this->content .= $val; + $init_dict[$write_offset] = $val; + $write_offset = ($write_offset + 1) % self::MAX_DICT_SIZE; + } + } + } + } + +} diff --git a/snappymail/v/0.0.0/app/libraries/TNEFDecoder/TNEFMailinfo.php b/snappymail/v/0.0.0/app/libraries/TNEFDecoder/TNEFMailinfo.php new file mode 100644 index 000000000..4b2e2537c --- /dev/null +++ b/snappymail/v/0.0.0/app/libraries/TNEFDecoder/TNEFMailinfo.php @@ -0,0 +1,107 @@ + + * Copyright (c) 2003 Bernd Wiegmann + * Copyright (c) 2002 Graham Norburys + * + * Licensed under the GNU GPL. For full terms see the file COPYING. + * + * @package plugins + * @subpackage tnef_decoder + * + */ + +class TNEFMailinfo +{ + + public string + $subject = '', + $topic = '', + $from = '', + $from_name = '', + $code_page = ''; + public ?TNEFDate $date_sent = null; + public bool + $topic_is_unicode = FALSE, + $from_is_unicode = FALSE, + $from_name_is_unicode = FALSE; + + public function getTopic(): string + { + return $this->topic; + } + + public function getSubject(): string + { + return $this->subject; + } + + public function getFrom(): string + { + return $this->from; + } + + public function getCodePage(): string + { + return $this->code_page; + } + + public function getFromName(): string + { + return $this->from_name; + } + + public function getDateSent(): TNEFDate + { + return $this->date_sent; + } + + public function receiveTnefAttribute(int $attribute, string $value, int $length): void + { + $value = new TNEFBuffer($value); + + switch ($attribute) + { + case TNEF_AOEMCODEPAGE: + $this->code_page = $value->geti16(); + break; + + case TNEF_ASUBJECT: + $this->subject = $value->getBytes($length - 1); + break; + + case TNEF_ADATERECEIVED: + if ($this->date_sent) { + break; + } + case TNEF_ADATESENT: + $this->date_sent = new TNEFDate(); + $this->date_sent->setTnefBuffer($value); + } + } + + public function receiveMapiAttribute(int $attr_type, int $attr_name, string $value, int $length): void + { + switch ($attr_name) + { + case TNEF_MAPI_CONVERSATION_TOPIC: + $this->topic = $value; + $this->topic_is_unicode = TNEF_MAPI_UNICODE_STRING === $attr_type; + break; + + case TNEF_MAPI_SENT_REP_EMAIL_ADDR: + $this->from = $value; + $this->from_is_unicode = TNEF_MAPI_UNICODE_STRING === $attr_type; + break; + + case TNEF_MAPI_SENT_REP_NAME: + $this->from_name = $value; + $this->from_name_is_unicode = TNEF_MAPI_UNICODE_STRING === $attr_type; + break; + } + } + +} diff --git a/snappymail/v/0.0.0/app/libraries/TNEFDecoder/TNEFvCard.php b/snappymail/v/0.0.0/app/libraries/TNEFDecoder/TNEFvCard.php new file mode 100644 index 000000000..546c83710 --- /dev/null +++ b/snappymail/v/0.0.0/app/libraries/TNEFDecoder/TNEFvCard.php @@ -0,0 +1,249 @@ + + * Copyright (c) 2003 Bernd Wiegmann + * Copyright (c) 2002 Graham Norburys + * + * Licensed under the GNU GPL. For full terms see the file COPYING. + * + * @package plugins + * @subpackage tnef_decoder + * + */ + +class TNEFvCard extends TNEFFileBase +{ + public string $type = 'text/x-vcard'; + + public bool + $surname_is_unicode = FALSE, + $given_name_is_unicode = FALSE, + $middle_name_is_unicode = FALSE, + $nickname_is_unicode = FALSE, + $company_is_unicode = FALSE; + public string + $surname, + $given_name, + $middle_name, + $nickname, + $company, + $metafile; + public array + $homepages = [], + $addresses = [], + $emails = [], + $telefones = []; + + private static + $address_mapping = array ( + TNEF_MAPI_LOCALTY => array ("Address", ADDRESS_CITY), + TNEF_MAPI_COUNTRY => array ("Address", ADDRESS_COUNTRY), + TNEF_MAPI_POSTAL_CODE => array ("Address", ADDRESS_ZIP), + TNEF_MAPI_STATE_OR_PROVINCE => array ("Address", ADDRESS_STATE), + TNEF_MAPI_STREET_ADDRESS => array ("Address", ADDRESS_STREET), + TNEF_MAPI_POST_OFFICE_BOX => array ("Address", ADDRESS_PO_BOX), + TNEF_MAPI_HOME_ADDR_CITY => array ("Home Address", ADDRESS_CITY), + TNEF_MAPI_HOME_ADDR_COUNTRY => array ("Home Address", ADDRESS_COUNTRY), + TNEF_MAPI_HOME_ADDR_ZIP => array ("Home Address", ADDRESS_ZIP), + TNEF_MAPI_HOME_ADDR_STATE => array ("Home Address", ADDRESS_STATE), + TNEF_MAPI_HOME_ADDR_STREET => array ("Home Address", ADDRESS_STREET), + TNEF_MAPI_HOME_ADDR_PO_BOX => array ("Home Address", ADDRESS_PO_BOX), + TNEF_MAPI_OTHER_ADDR_CITY => array ("Other Address", ADDRESS_CITY), + TNEF_MAPI_OTHER_ADDR_COUNTRY => array ("Other Address", ADDRESS_COUNTRY), + TNEF_MAPI_OTHER_ADDR_ZIP => array ("Other Address", ADDRESS_ZIP), + TNEF_MAPI_OTHER_ADDR_STATE => array ("Other Address", ADDRESS_STATE), + TNEF_MAPI_OTHER_ADDR_STREET => array ("Other Address", ADDRESS_STREET), + TNEF_MAPI_OTHER_ADDR_PO_BOX => array ("Other Address", ADDRESS_PO_BOX), + ), + $email_mapping = array ( + TNEF_MAPI_EMAIL1_DISPLAY => array ("Email 1", EMAIL_DISPLAY), + TNEF_MAPI_EMAIL1_TRANSPORT => array ("Email 1", EMAIL_TRANSPORT), + TNEF_MAPI_EMAIL1_EMAIL => array ("Email 1", EMAIL_EMAIL), + TNEF_MAPI_EMAIL1_EMAIL2 => array ("Email 1", EMAIL_EMAIL2), + TNEF_MAPI_EMAIL2_DISPLAY => array ("Email 2", EMAIL_DISPLAY), + TNEF_MAPI_EMAIL2_TRANSPORT => array ("Email 2", EMAIL_TRANSPORT), + TNEF_MAPI_EMAIL2_EMAIL => array ("Email 2", EMAIL_EMAIL), + TNEF_MAPI_EMAIL2_EMAIL2 => array ("Email 2", EMAIL_EMAIL2), + TNEF_MAPI_EMAIL3_DISPLAY => array ("Email 3", EMAIL_DISPLAY), + TNEF_MAPI_EMAIL3_TRANSPORT => array ("Email 3", EMAIL_TRANSPORT), + TNEF_MAPI_EMAIL3_EMAIL => array ("Email 3", EMAIL_EMAIL), + TNEF_MAPI_EMAIL3_EMAIL2 => array ("Email 3", EMAIL_EMAIL2), + ), + $homepage_mapping = array ( + TNEF_MAPI_PERSONAL_HOME_PAGE => "Personal Homepage", + TNEF_MAPI_BUSINESS_HOME_PAGE => "Business Homepage", + TNEF_MAPI_OTHER_HOME_PAGE => "Other Homepage", + ), + $telefone_mapping = array ( + TNEF_MAPI_PRIMARY_TEL_NUMBER => "Primary Telefone", + TNEF_MAPI_HOME_TEL_NUMBER => "Home Telefone", + TNEF_MAPI_HOME2_TEL_NUMBER => "Home2 Telefone", + TNEF_MAPI_BUSINESS_TEL_NUMBER => "Business Telefone", + TNEF_MAPI_BUSINESS2_TEL_NUMBER => "Business2 Telefone", + TNEF_MAPI_MOBILE_TEL_NUMBER => "Mobile Telefone", + TNEF_MAPI_RADIO_TEL_NUMBER => "Radio Telefone", + TNEF_MAPI_CAR_TEL_NUMBER => "Car Telefone", + TNEF_MAPI_OTHER_TEL_NUMBER => "Other Telefone", + TNEF_MAPI_PAGER_TEL_NUMBER => "Pager Telefone", + TNEF_MAPI_PRIMARY_FAX_NUMBER => "Primary Fax", + TNEF_MAPI_BUSINESS_FAX_NUMBER => "Business Fax", + TNEF_MAPI_HOME_FAX_NUMBER => "Home Fax", + ); + + public function getSurname(): string + { + return $this->surname; + } + + public function getGivenName(): string + { + return $this->given_name; + } + + public function getMiddleName(): string + { + return $this->middle_name; + } + + public function getNickname(): string + { + return $this->nickname; + } + + public function getCompany(): string + { + return $this->company; + } + + public function getAddresses(): array + { + return $this->addresses; + } + + public function getMetafile() + { + return $this->metafile; + } + + public function getTelefones(): array + { + return $this->telefones; + } + + public function getHomepages(): array + { + return $this->homepages; + } + + public function getEmails(): array + { + return $this->emails; + } + + public function receiveTnefAttribute(int $attribute, string $value, int $length): void + { + switch ($attribute) + { + // code page + // + case TNEF_AOEMCODEPAGE: + $this->code_page = (new TNEFBuffer($value))->geti16(); + break; + } + } + + public function receiveMapiAttribute(int $attr_type, int $attr_name, string $value, int $length) + { + switch ($attr_name) + { + case TNEF_MAPI_DISPLAY_NAME: + $this->name = $value; + $this->name_is_unicode = TNEF_MAPI_UNICODE_STRING === $attr_type; + break; + + case TNEF_MAPI_SURNAME: + $this->surname = $value; + $this->surname_is_unicode = TNEF_MAPI_UNICODE_STRING === $attr_type; + break; + + case TNEF_MAPI_GIVEN_NAME: + $this->given_name = $value; + $this->given_name_is_unicode = TNEF_MAPI_UNICODE_STRING === $attr_type; + break; + + case TNEF_MAPI_MIDDLE_NAME: + $this->middle_name = $value; + $this->middle_name_is_unicode = TNEF_MAPI_UNICODE_STRING === $attr_type; + break; + + case TNEF_MAPI_NICKNAME: + $this->nickname = $value; + $this->nickname_is_unicode = TNEF_MAPI_UNICODE_STRING === $attr_type; + break; + + case TNEF_MAPI_COMPANY_NAME: + $this->company = $value; + $this->company_is_unicode = TNEF_MAPI_UNICODE_STRING === $attr_type; + break; + + default: + $this->evaluateTelefoneAttribute($attr_type, $attr_name, $value, $length) + || $this->evaluateEmailAttribute($attr_type, $attr_name, $value, $length) + || $this->evaluateAddressAttribute($attr_type, $attr_name, $value, $length) + || $this->evaluateHomepageAttribute($attr_type, $attr_name, $value, $length); + break; + } + } + + private function evaluateTelefoneAttribute(int $attr_type, int $attr_name, string $value, int $length): bool + { + if ($length && \array_key_exists($attr_name, static::$telefone_mapping)) { + $telefone_key = static::$telefone_mapping[$attr_name]; + $this->telefones[$telefone_key] = $value; + $this->debug && tnef_log("Setting telefone '{$telefone_key}' to value '{$value}'"); + return true; + } + return false; + } + + private function evaluateEmailAttribute(int $attr_type, int $attr_name, string $value, int $length): bool + { + if ($length && \array_key_exists($attr_name, static::$email_mapping)) { + $email_key = static::$email_mapping[$attr_name]; + if (!\array_key_exists($email_key[0], $this->emails)) + $this->emails[$email_key[0]] = array(EMAIL_DISPLAY => "", EMAIL_TRANSPORT => "", EMAIL_EMAIL => "", EMAIL_EMAIL2 => ""); + $this->emails[$email_key[0]][$email_key[1]] = $value; + return true; + } + return false; + } + + private function evaluateAddressAttribute(int $attr_type, int $attr_name, string $value, int $length): bool + { + if ($length && \array_key_exists($attr_name, static::$address_mapping)) { + $address_key = static::$address_mapping[$attr_name]; + if (!\array_key_exists($address_key[0], $this->addresses)) + $this->addresses[$address_key[0]] = array(); + $this->addresses[$address_key[0]][$address_key[1]] = $value; + return true; + } + return false; + } + + private function evaluateHomepageAttribute(int $attr_type, int $attr_name, string $value, int $length): bool + { + if ($length && \array_key_exists($attr_name, static::$homepage_mapping)) { + $homepage_key = static::$homepage_mapping[$attr_name]; + $this->homepages[$homepage_key] = $value; + $this->debug && tnef_log("Setting homepage '{$homepage_key}' to value '{$value}'"); + return true; + } + return false; + } + +} diff --git a/snappymail/v/0.0.0/app/libraries/TNEFDecoder/constants.php b/snappymail/v/0.0.0/app/libraries/TNEFDecoder/constants.php new file mode 100644 index 000000000..13f73f284 --- /dev/null +++ b/snappymail/v/0.0.0/app/libraries/TNEFDecoder/constants.php @@ -0,0 +1,202 @@ + + * Copyright (c) 2003 Bernd Wiegmann + * Copyright (c) 2002 Graham Norburys + * + * Licensed under the GNU GPL. For full terms see the file COPYING. + * + * @package plugins + * @subpackage tnef_decoder + * + */ + +define("TNEF_SIGNATURE", 0x223e9f78); +define("TNEF_LVL_MESSAGE", 0x01); +define("TNEF_LVL_ATTACHMENT", 0x02); + +define("TNEF_TRIPLES", 0x00000000); +define("TNEF_STRING", 0x00010000); +define("TNEF_TEXT", 0x00020000); +define("TNEF_DATE", 0x00030000); +define("TNEF_SHORT", 0x00040000); +define("TNEF_LONG", 0x00050000); +define("TNEF_BYTE", 0x00060000); +define("TNEF_WORD", 0x00070000); +define("TNEF_DWORD", 0x00080000); +define("TNEF_MAX", 0x00090000); + +define("TNEF_AIDOWNER", TNEF_LONG | 0x0008); +define("TNEF_AREQUESTRES", TNEF_SHORT | 0x0009); +define("TNEF_AFROM", TNEF_TRIPLES | 0x8000); +define("TNEF_ASUBJECT", TNEF_STRING | 0x8004); +define("TNEF_ADATESENT", TNEF_DATE | 0x8005); +define("TNEF_ADATERECEIVED", TNEF_DATE | 0x8006); +define("TNEF_ASTATUS", TNEF_BYTE | 0x8007); +define("TNEF_AMCLASS", TNEF_WORD | 0x8008); +define("TNEF_AMESSAGEID", TNEF_STRING | 0x8009); +define("TNEF_ABODYTEXT", TNEF_TEXT | 0x800c); +define("TNEF_APRIORITY", TNEF_SHORT | 0x800d); +define("TNEF_ATTACHDATA", TNEF_BYTE | 0x800f); +define("TNEF_AFILENAME", TNEF_STRING | 0x8010); +define("TNEF_ATTACHMETAFILE", TNEF_BYTE | 0x8011); +define("TNEF_AATTACHCREATEDATE", TNEF_DATE | 0x8012); +define("TNEF_AATTACHMODDATE", TNEF_DATE | 0x8013); +define("TNEF_ADATEMODIFIED", TNEF_DATE | 0x8020); +define("TNEF_ARENDDATA", TNEF_BYTE | 0x9002); +define("TNEF_AMAPIPROPS", TNEF_BYTE | 0x9003); +define("TNEF_AMAPIATTRS", TNEF_BYTE | 0x9005); +define("TNEF_AVERSION", TNEF_DWORD | 0x9006); +define("TNEF_AOEMCODEPAGE", TNEF_BYTE | 0x9007); + +define("TNEF_MAPI_NULL", 0x0001); +define("TNEF_MAPI_SHORT", 0x0002); +define("TNEF_MAPI_INT", 0x0003); +define("TNEF_MAPI_FLOAT", 0x0004); +define("TNEF_MAPI_DOUBLE", 0x0005); +define("TNEF_MAPI_CURRENCY", 0x0006); +define("TNEF_MAPI_APPTIME", 0x0007); +define("TNEF_MAPI_ERROR", 0x000a); +define("TNEF_MAPI_BOOLEAN", 0x000b); +define("TNEF_MAPI_OBJECT", 0x000d); +define("TNEF_MAPI_INT8BYTE", 0x0014); +define("TNEF_MAPI_STRING", 0x001e); +define("TNEF_MAPI_UNICODE_STRING", 0x001f); +define("TNEF_MAPI_SYSTIME", 0x0040); +define("TNEF_MAPI_CLSID", 0x0048); +define("TNEF_MAPI_BINARY", 0x0102); + +define("TNEF_MAPI_MV_FLAG", 0x1000); +define("TNEF_MAPI_NAMED_TYPE_ID", 0x0000); +define("TNEF_MAPI_NAMED_TYPE_STRING", 0x0001); + +define("TNEF_MAPI_SUBJECT_PREFIX", 0x003D); +define("TNEF_MAPI_SENT_REP_NAME", 0x0042); +define("TNEF_MAPI_ORIGINAL_AUTHOR", 0x004D); +define("TNEF_MAPI_SENT_REP_ADDRTYPE", 0x0064); +define("TNEF_MAPI_SENT_REP_EMAIL_ADDR", 0x0065); +define("TNEF_MAPI_CONVERSATION_TOPIC", 0x0070); +define("TNEF_MAPI_SENDER_NAME", 0x0c1A); +define("TNEF_MAPI_SENDER_ADDRTYPE", 0x0c1E); +define("TNEF_MAPI_SENDER_EMAIL_ADDRESS", 0x0c1F); +define("TNEF_MAPI_NORMALIZED_SUBJECT", 0x0E1D); +define("TNEF_MAPI_ATTACH_SIZE", 0x0E20); +define("TNEF_MAPI_ATTACH_NUM", 0x0E21); +define("TNEF_MAPI_ACCESS_LEVEL", 0x0FF7); +define("TNEF_MAPI_MAPPING_SIGNATURE", 0x0FF8); +define("TNEF_MAPI_RECORD_KEY", 0x0FF9); +define("TNEF_MAPI_STORE_RECORD_KEY", 0x0FFA); +define("TNEF_MAPI_STORE_ENTRY_ID", 0x0FFB); +define("TNEF_MAPI_OBJECT_TYPE", 0x0FFE); +define("TNEF_MAPI_BODY", 0x1000); +define("TNEF_MAPI_RTF_SYNC_BODY_TAG", 0x1008); +define("TNEF_MAPI_RTF_COMPRESSED", 0x1009); +define("TNEF_MAPI_BODY_HTML", 0x1013); +define("TNEF_MAPI_NATIVE_BODY", 0x1016); +define("TNEF_MAPI_DISPLAY_NAME", 0x3001); +define("TNEF_MAPI_CREATION_TIME", 0x3007); +define("TNEF_MAPI_MODIFICATION_TIME", 0x3008); +define("TNEF_MAPI_ATTACH_DATA", 0x3701); +define("TNEF_MAPI_ATTACH_ENCODING", 0x3702); +define("TNEF_MAPI_ATTACH_EXTENSION", 0x3703); +define("TNEF_MAPI_ATTACH_METHOD", 0x3705); +define("TNEF_MAPI_ATTACH_LONG_FILENAME", 0x3707); +define("TNEF_MAPI_RENDERING_POSITION", 0x370B); +define("TNEF_MAPI_ATTACH_MIME_TAG", 0x370E); +define("TNEF_MAPI_ACCOUNT", 0x3A00); +define("TNEF_MAPI_GENERATION", 0x3A05); +define("TNEF_MAPI_GIVEN_NAME", 0x3A06); +define("TNEF_MAPI_BUSINESS_TEL_NUMBER", 0x3A08); +define("TNEF_MAPI_HOME_TEL_NUMBER", 0x3A09); +define("TNEF_MAPI_INITIALS", 0x3A0A); +define("TNEF_MAPI_KEYWORDS", 0x3A0B); +define("TNEF_MAPI_LANGUAGE", 0x3A0C); +define("TNEF_MAPI_LOCATION", 0x3A0D); +define("TNEF_MAPI_SURNAME", 0x3A11); +define("TNEF_MAPI_POSTAL_ADDRESS", 0x3A15); +define("TNEF_MAPI_COMPANY_NAME", 0x3A16); +define("TNEF_MAPI_TITLE", 0x3A17); +define("TNEF_MAPI_DEPARTMENT_NAME", 0x3A18); +define("TNEF_MAPI_OFFICE_LOCATION", 0x3A19); +define("TNEF_MAPI_PRIMARY_TEL_NUMBER", 0x3A1A); +define("TNEF_MAPI_BUSINESS2_TEL_NUMBER", 0x3A1B); +define("TNEF_MAPI_MOBILE_TEL_NUMBER", 0x3A1C); +define("TNEF_MAPI_RADIO_TEL_NUMBER", 0x3A1D); +define("TNEF_MAPI_CAR_TEL_NUMBER", 0x3A1E); +define("TNEF_MAPI_OTHER_TEL_NUMBER", 0x3A1F); +define("TNEF_MAPI_PAGER_TEL_NUMBER", 0x3A21); +define("TNEF_MAPI_PRIMARY_FAX_NUMBER", 0x3A23); +define("TNEF_MAPI_BUSINESS_FAX_NUMBER", 0x3A24); +define("TNEF_MAPI_HOME_FAX_NUMBER", 0x3A25); +define("TNEF_MAPI_COUNTRY", 0x3A26); +define("TNEF_MAPI_LOCALTY", 0x3A27); +define("TNEF_MAPI_STATE_OR_PROVINCE", 0x3A28); +define("TNEF_MAPI_STREET_ADDRESS", 0x3A29); +define("TNEF_MAPI_POSTAL_CODE", 0x3A2A); +define("TNEF_MAPI_POST_OFFICE_BOX", 0x3A2B); +define("TNEF_MAPI_TELEX_NUMBER", 0x3A2C); +define("TNEF_MAPI_ISDN_NUMBER", 0x3A2D); +define("TNEF_MAPI_ASSISTANT_TEL_NUMBER", 0x3A2E); +define("TNEF_MAPI_HOME2_TEL_NUMBER", 0x3A2F); +define("TNEF_MAPI_ASSISTANT", 0x3A30); +define("TNEF_MAPI_MIDDLE_NAME", 0x3A44); +define("TNEF_MAPI_DISPLAYNAME_PREFIX", 0x3A45); +define("TNEF_MAPI_PROFESSION", 0x3A46); +define("TNEF_MAPI_SPOUSE_NAME", 0x3A48); +define("TNEF_MAPI_MANAGER_NAME", 0x3A4E); +define("TNEF_MAPI_NICKNAME", 0x3A4F); +define("TNEF_MAPI_PERSONAL_HOME_PAGE", 0x3A50); +define("TNEF_MAPI_BUSINESS_HOME_PAGE", 0x3A51); +define("TNEF_MAPI_CONTACT_EMAIL_ADDR", 0x3A56); +define("TNEF_MAPI_HOME_ADDR_CITY", 0x3A59); +define("TNEF_MAPI_HOME_ADDR_COUNTRY", 0x3A5A); +define("TNEF_MAPI_HOME_ADDR_ZIP", 0x3A5B); +define("TNEF_MAPI_HOME_ADDR_STATE", 0x3A5C); +define("TNEF_MAPI_HOME_ADDR_STREET", 0x3A5D); +define("TNEF_MAPI_HOME_ADDR_PO_BOX", 0x3A5E); +define("TNEF_MAPI_OTHER_ADDR_CITY", 0x3A5F); +define("TNEF_MAPI_OTHER_ADDR_COUNTRY", 0x3A60); +define("TNEF_MAPI_OTHER_ADDR_ZIP", 0x3A61); +define("TNEF_MAPI_OTHER_ADDR_STATE", 0x3A62); +define("TNEF_MAPI_OTHER_ADDR_STREET", 0x3A63); +define("TNEF_MAPI_OTHER_ADDR_PO_BOX", 0x3A64); + +define("TNEF_MAPI_OTHER_HOME_PAGE", 0x804F); +define("TNEF_MAPI_EMAIL1_DISPLAY", 0x8080); +define("TNEF_MAPI_EMAIL1_TRANSPORT", 0x8082); +define("TNEF_MAPI_EMAIL1_EMAIL", 0x8083); +define("TNEF_MAPI_EMAIL1_EMAIL2", 0x8084); +define("TNEF_MAPI_EMAIL2_DISPLAY", 0x8090); +define("TNEF_MAPI_EMAIL2_TRANSPORT", 0x8092); +define("TNEF_MAPI_EMAIL2_EMAIL", 0x8093); +define("TNEF_MAPI_EMAIL2_EMAIL2", 0x8094); +define("TNEF_MAPI_EMAIL3_DISPLAY", 0x80A0); +define("TNEF_MAPI_EMAIL3_TRANSPORT", 0x80A2); +define("TNEF_MAPI_EMAIL3_EMAIL", 0x80A3); +define("TNEF_MAPI_EMAIL3_EMAIL2", 0x80A4); + + + +// used in RTF +// +define("CRTF_UNCOMPRESSED", 0x414c454d); +define("CRTF_COMPRESSED", 0x75465a4c); + + +// used in VCARD +// +define ("EMAIL_DISPLAY", 1); +define ("EMAIL_TRANSPORT", 2); +define ("EMAIL_EMAIL", 3); +define ("EMAIL_EMAIL2", 4); +define ("ADDRESS_STREET", "Street"); +define ("ADDRESS_ZIP", "Zip"); +define ("ADDRESS_CITY", "City"); +define ("ADDRESS_COUNTRY", "Country"); +define ("ADDRESS_STATE", "State"); +define ("ADDRESS_PO_BOX", "PO Box"); diff --git a/snappymail/v/0.0.0/app/libraries/polyfill/ctype.php b/snappymail/v/0.0.0/app/libraries/polyfill/ctype.php new file mode 100644 index 000000000..7f913e728 --- /dev/null +++ b/snappymail/v/0.0.0/app/libraries/polyfill/ctype.php @@ -0,0 +1,17 @@ +Cacher(); $sCacheKey = "dns-bimi-{$domain}-{$selector}"; - $BIMI = $oCache->Get($sCacheKey) ?: null; + $BIMI = $oCache->Get($sCacheKey); if ($BIMI) { $BIMI = \json_decode($BIMI); if ($BIMI[1] < \time()) { diff --git a/snappymail/v/0.0.0/app/libraries/snappymail/file/mimetype.php b/snappymail/v/0.0.0/app/libraries/snappymail/file/mimetype.php index e64b57293..62f186737 100644 --- a/snappymail/v/0.0.0/app/libraries/snappymail/file/mimetype.php +++ b/snappymail/v/0.0.0/app/libraries/snappymail/file/mimetype.php @@ -103,7 +103,7 @@ abstract class MimeType { $filename = \strtolower($filename); if ('winmail.dat' === $filename) { - return 'application/ms-tnef'; + return static::$types['tnef']; } $extension = \explode('.', $filename); $extension = \array_pop($extension); @@ -116,7 +116,7 @@ abstract class MimeType public static function toExtension(string $mime, bool $include_dot = true) : ?string { $mime = \strtolower($mime); - if ('application/pgp-signature' == $mime || 'application/pgp-keys' == $mime) { + if ('application/pgp-signature' === $mime || 'application/pgp-keys' === $mime) { $ext = 'asc'; } else { $mime = \str_replace('application/x-tar', 'application/gtar', $mime); @@ -149,7 +149,6 @@ abstract class MimeType 'epub' => 'application/epub', 'exe' => 'application/x-msdownload', 'gz' => 'application/gzip', - 'gz' => 'application/x-gzip', 'hlp' => 'application/winhlp', 'js' => 'application/javascript', 'json' => 'application/json', @@ -178,7 +177,9 @@ abstract class MimeType 'swf' => 'application/x-shockwave-flash', 'tar' => 'application/gtar', // 'tar' => 'application/x-tar', -// 'tgz' => 'application/x-gzip', +// 'tgz' => 'application/gzip', + 'tnef' => 'application/vnd.ms-tnef', +// 'tnef' => 'application/ms-tnef', // not IANA official 'torrent' => 'application/x-bittorrent', 'wgt' => 'application/widget', 'xls' => 'application/vnd.ms-excel', diff --git a/snappymail/v/0.0.0/app/libraries/snappymail/gpg/base.php b/snappymail/v/0.0.0/app/libraries/snappymail/gpg/base.php index 2518677a7..68e8d4c0f 100644 --- a/snappymail/v/0.0.0/app/libraries/snappymail/gpg/base.php +++ b/snappymail/v/0.0.0/app/libraries/snappymail/gpg/base.php @@ -43,10 +43,8 @@ abstract class Base protected $binary, $version = '2.0', - $passphrases = [], - $signKeys = [], + $pinentries = [], $encryptKeys = [], - $decryptKeys = [], // Create PEM encoded output $armor = true, @@ -91,7 +89,7 @@ abstract class Base public static function isSupported() : bool { - return \is_callable('proc_open'); + return \is_callable('shell_exec') && \is_callable('proc_open'); } protected function listDecryptKeys(/*string|resource*/ $input, /*string|resource*/ $output = null) @@ -284,8 +282,7 @@ abstract class Base */ public function addDecryptKey(string $fingerprint, SensitiveString $passphrase) : bool { - $this->decryptKeys[$fingerprint] = $passphrase; -// $this->decryptKeys[\substr($fingerprint, -16)] = $passphrase; + $this->addPinentry($fingerprint, $passphrase); return true; } @@ -303,9 +300,15 @@ abstract class Base */ public function addSignKey(string $fingerprint, SensitiveString $passphrase) : bool { - $this->signKeys[$fingerprint] = $passphrase; -// $this->signKeys[\substr($fingerprint, -16)] = $passphrase; - return false; + $this->addPinentry($fingerprint, $passphrase); + return true; + } + + public function clear() : bool + { + $this->clearPinentries(); + $this->clearEncryptKeys(); + return true; } /** @@ -313,8 +316,7 @@ abstract class Base */ public function clearDecryptKeys() : bool { - $this->decryptKeys = []; - return true; + return $this->clearPinentries(); } /** @@ -331,8 +333,7 @@ abstract class Base */ public function clearSignKeys() : bool { - $this->signKeys = []; - return true; + return $this->clearPinentries(); } /** @@ -348,12 +349,37 @@ abstract class Base ]; } - public function addPassphrase($keyId, SensitiveString $passphrase) + /** + * Add private key passphrase for decrypt, sign or export + * $keyId or fingerprint + */ + public function addPinentry(string $keyId, SensitiveString $passphrase) { - $this->passphrases[$keyId] = $passphrase; + /** + * Test first? + * gpg --dry-run --passwd + $_ENV['PINENTRY_USER_DATA'] = \json_encode(\array_map('strval', [$keyId => $passphrase])); + $result = $this->exec([ + '--dry-run', + '--passwd', + $passphrase + ]); + */ +// $this->export($keyId, $passphrase); + $this->pinentries[$keyId] = $passphrase; +// $this->pinentries[\substr($keyId, -16)] = $passphrase; return $this; } + /** + * Removes all keys which were set for decryption, signing and export + */ + public function clearPinentries() : bool + { + $this->pinentries = []; + return true; + } + /** * Toggle the armored output * When true the output is ASCII diff --git a/snappymail/v/0.0.0/app/libraries/snappymail/gpg/pgp.php b/snappymail/v/0.0.0/app/libraries/snappymail/gpg/pgp.php index c0c675202..213043bc3 100644 --- a/snappymail/v/0.0.0/app/libraries/snappymail/gpg/pgp.php +++ b/snappymail/v/0.0.0/app/libraries/snappymail/gpg/pgp.php @@ -96,8 +96,8 @@ class PGP extends Base implements \SnappyMail\PGP\PGPInterface $fclose = $this->setOutput($output); - if ($this->decryptKeys) { - $_ENV['PINENTRY_USER_DATA'] = \json_encode(\array_map('strval', $this->decryptKeys)); + if ($this->pinentries) { + $_ENV['PINENTRY_USER_DATA'] = \json_encode(\array_map('strval', $this->pinentries)); } $result = $this->exec(['--decrypt','--skip-verify']); @@ -238,14 +238,19 @@ class PGP extends Base implements \SnappyMail\PGP\PGPInterface return false; } - protected function _exportKey($keyId, bool $private = false) + /** + * Exports a public or private key + */ + public function export(string $fingerprint, ?SensitiveString $passphrase = null) /*: string|false*/ { - $keys = $this->keyInfo($keyId, $private); +// \SnappyMail\Log::debug('GnuPG', "export({$fingerprint}, {$passphrase})"); + $private = null !== $passphrase; + $keys = $this->keyInfo($fingerprint, $private); if (!$keys) { - throw new \Exception(($private ? 'Private' : 'Public') . ' key not found: ' . $keyId); + throw new \Exception(($private ? 'Private' : 'Public') . ' key not found: ' . $fingerprint); } - if ($private && $this->passphrases) { - $_ENV['PINENTRY_USER_DATA'] = \json_encode(\array_map('strval', $this->passphrases)); + if ($private) { + $_ENV['PINENTRY_USER_DATA'] = \json_encode([$fingerprint => \strval($passphrase)]); } $result = $this->exec([ $private ? '--export-secret-keys' : '--export', @@ -255,20 +260,6 @@ class PGP extends Base implements \SnappyMail\PGP\PGPInterface return $result['output']; } - /** - * Exports a public or private key - */ - public function export(string $fingerprint, ?SensitiveString $passphrase = null) /*: string|false*/ - { -// \SnappyMail\Log::debug('GnuPG', "export({$fingerprint}, {$passphrase})"); - if (null !== $passphrase) { - return $this - ->addPassphrase($fingerprint, $passphrase) - ->_exportKey($fingerprint, true); - } - return $this->_exportKey($fingerprint); - } - /** * Returns the errortext, if a function fails */ @@ -333,9 +324,6 @@ class PGP extends Base implements \SnappyMail\PGP\PGPInterface } } if (!$fingerprint) { - if (!empty($result['errors'])) { - \SnappyMail\Log::error('GPG', \implode("\n\t", $result['errors'])); - } return false; } @@ -368,8 +356,8 @@ class PGP extends Base implements \SnappyMail\PGP\PGPInterface { $arguments = ['--import']; - if ($this->passphrases) { - $_ENV['PINENTRY_USER_DATA'] = \json_encode(\array_map('strval', $this->passphrases)); + if ($this->pinentries) { + $_ENV['PINENTRY_USER_DATA'] = \json_encode(\array_map('strval', $this->pinentries)); } else { $arguments[] = '--batch'; } @@ -394,10 +382,6 @@ class PGP extends Base implements \SnappyMail\PGP\PGPInterface } } - if (!empty($result['errors'][0])) { - \SnappyMail\Log::warning('GPG', $result['errors'][0]); - } - return false; } @@ -625,7 +609,7 @@ class PGP extends Base implements \SnappyMail\PGP\PGPInterface protected function _sign(/*string|resource*/ $input, /*string|resource*/ $output = null, bool $textmode = true) /*: string|false*/ { - if (empty($this->signKeys)) { + if (empty($this->pinentries)) { throw new \Exception('No signing keys specified.'); } @@ -656,11 +640,11 @@ class PGP extends Base implements \SnappyMail\PGP\PGPInterface $arguments[] = '--textmode'; } - if ($this->signKeys) { - foreach ($this->signKeys as $fingerprint => $pass) { + if ($this->pinentries) { + foreach ($this->pinentries as $fingerprint => $pass) { $arguments[] = '--local-user ' . \escapeshellarg($fingerprint); } - $_ENV['PINENTRY_USER_DATA'] = \json_encode(\array_map('strval', $this->signKeys)); + $_ENV['PINENTRY_USER_DATA'] = \json_encode(\array_map('strval', $this->pinentries)); } $result = $this->exec($arguments); @@ -809,12 +793,11 @@ class PGP extends Base implements \SnappyMail\PGP\PGPInterface $this->_debug('BEGIN DETECT MESSAGE KEY IDs'); $this->setInput($data); // $_ENV['PINENTRY_USER_DATA'] = null; - $result = $this->exec(['--decrypt','--skip-verify']); + $result = $this->exec(['--decrypt','--skip-verify'], false); $info = [ 'ENC_TO' => [], // 'KEY_CONSIDERED' => [], // 'NO_SECKEY' => [], -// 'errors' => $result['errors'] ]; foreach ($result['status'] as $line) { $tokens = \explode(' ', $line); @@ -826,7 +809,7 @@ class PGP extends Base implements \SnappyMail\PGP\PGPInterface return $info['ENC_TO']; } - private function exec(array $arguments) /*: array|false*/ + private function exec(array $arguments, bool $throw = true) /*: array|false*/ { if (\version_compare($this->version, '2.2.5', '<')) { \SnappyMail\Log::error('GPG', "{$this->version} too old"); @@ -948,12 +931,7 @@ class PGP extends Base implements \SnappyMail\PGP\PGPInterface // Timeout after 5 seconds if (5 < \microtime(1) - $start) { $errors[] = 'timeout'; - return [ - 'output' => '', - 'status' => $status, - 'errors' => $errors - ]; - exit; + throw new \RuntimeException(\implode("\n", $errors)); } $inputStreams = []; @@ -1076,7 +1054,6 @@ class PGP extends Base implements \SnappyMail\PGP\PGPInterface // to use too much memory if (\in_array($this->_input, $inputStreams, true) && \strlen($inputBuffer) < self::CHUNK_SIZE) { $this->_debug('input stream is ready for reading'); - $this->_debug('=> about to read ' . self::CHUNK_SIZE . ' bytes from input stream'); $chunk = \fread($this->_input, self::CHUNK_SIZE); $length = \strlen($chunk); $inputBuffer .= $chunk; @@ -1099,7 +1076,6 @@ class PGP extends Base implements \SnappyMail\PGP\PGPInterface // read message (from PHP stream) if (\in_array($this->_message, $inputStreams, true)) { $this->_debug('message stream is ready for reading'); - $this->_debug('=> about to read ' . self::CHUNK_SIZE . ' bytes from message stream'); $chunk = \fread($this->_message, self::CHUNK_SIZE); $length = \strlen($chunk); $messageBuffer .= $chunk; @@ -1109,7 +1085,6 @@ class PGP extends Base implements \SnappyMail\PGP\PGPInterface // read output (from GPG) if (\in_array($fdOutput, $inputStreams, true)) { $this->_debug('output stream ready for reading'); - $this->_debug('=> about to read ' . self::CHUNK_SIZE . ' bytes from output'); $chunk = \fread($fdOutput, self::CHUNK_SIZE); $length = \strlen($chunk); $outputBuffer .= $chunk; @@ -1136,17 +1111,18 @@ class PGP extends Base implements \SnappyMail\PGP\PGPInterface // read error (from GPG) if (\in_array($fdError, $inputStreams, true)) { $this->_debug('error stream ready for reading'); - $this->_debug('=> about to read ' . self::CHUNK_SIZE . ' bytes from error'); foreach ($this->_openPipes->readPipeLines(self::FD_ERROR) as $line) { - $errors[] = $line; $this->_debug("\t{$line}"); + $errors[] = \preg_replace('/^gpg: /', '', $line); + if ($throw && (\str_contains($line, 'error') || \str_contains($line, 'failed'))) { + break 2; + } } } // read status (from GPG) if (\in_array($fdStatus, $inputStreams, true)) { $this->_debug('status stream ready for reading'); - $this->_debug('=> about to read ' . self::CHUNK_SIZE . ' bytes from status'); // pass lines to status handlers foreach ($this->_openPipes->readPipeLines(self::FD_STATUS) as $line) { // only pass lines beginning with magic prefix @@ -1199,12 +1175,16 @@ class PGP extends Base implements \SnappyMail\PGP\PGPInterface $this->_debug('END PROCESSING'); - $this->proc_close(); + $exitCode = $this->proc_close(); $this->_message = null; $this->_input = null; $this->_output = null; + if ($throw && $exitCode && $errors) { + throw new \RuntimeException(\implode(".\n", $errors), $exitCode); + } + return [ 'output' => $outputBuffer, 'status' => $status, diff --git a/snappymail/v/0.0.0/app/libraries/snappymail/gpg/smime.php b/snappymail/v/0.0.0/app/libraries/snappymail/gpg/smime.php index 3e8d76f5e..aaa1c839e 100644 --- a/snappymail/v/0.0.0/app/libraries/snappymail/gpg/smime.php +++ b/snappymail/v/0.0.0/app/libraries/snappymail/gpg/smime.php @@ -97,8 +97,8 @@ class SMIME extends Base $fclose = $this->setOutput($output); - if ($this->decryptKeys) { - $_ENV['PINENTRY_USER_DATA'] = \json_encode($this->decryptKeys); + if ($this->pinentries) { + $_ENV['PINENTRY_USER_DATA'] = \json_encode($this->pinentries); } $result = $this->exec(['--decrypt','--skip-verify']); @@ -237,14 +237,18 @@ class SMIME extends Base return false; } - protected function _exportKey($keyId, bool $private = false) + /** + * Exports a public or private key + */ + public function export(string $fingerprint, ?SensitiveString $passphrase = null) /*: string|false*/ { - $keys = $this->keyInfo($keyId, $private); + $private = null !== $passphrase; + $keys = $this->keyInfo($fingerprint, $private); if (!$keys) { - throw new \Exception(($private ? 'Private' : 'Public') . ' key not found: ' . $keyId); + throw new \Exception(($private ? 'Private' : 'Public') . ' key not found: ' . $fingerprint); } - if ($private && $this->passphrases) { - $_ENV['PINENTRY_USER_DATA'] = \json_encode($this->passphrases); + if ($private) { + $_ENV['PINENTRY_USER_DATA'] = \json_encode([$fingerprint => \strval($passphrase)]); } $result = $this->exec([ $private ? '--export-secret-key-p12' : '--export', @@ -254,25 +258,12 @@ class SMIME extends Base return $result['output']; } - /** - * Exports a public or private key - */ - public function export(string $fingerprint, ?SensitiveString $passphrase = null) /*: string|false*/ - { - if ($passphrase) { - return $this - ->addPassphrase($fingerprint, $passphrase) - ->_exportKey($fingerprint, true); - } - return $this->_exportKey($fingerprint); - } - protected function _importKey($input) /*: array|false*/ { $arguments = ['--import']; - if ($this->passphrases) { - $_ENV['PINENTRY_USER_DATA'] = \json_encode($this->passphrases); + if ($this->pinentries) { + $_ENV['PINENTRY_USER_DATA'] = \json_encode($this->pinentries); } else { $arguments[] = '--batch'; } @@ -482,7 +473,7 @@ class SMIME extends Base protected function _sign(/*string|resource*/ $input, /*string|resource*/ $output = null) /*: string|false*/ { - if (empty($this->signKeys)) { + if (empty($this->pinentries)) { throw new \Exception('No signing keys specified.'); } @@ -498,11 +489,11 @@ class SMIME extends Base $arguments[] = '--armor'; } - if ($this->signKeys) { - foreach ($this->signKeys as $fingerprint => $pass) { + if ($this->pinentries) { + foreach ($this->pinentries as $fingerprint => $pass) { $arguments[] = '--local-user ' . \escapeshellarg($fingerprint); } - $_ENV['PINENTRY_USER_DATA'] = \json_encode($this->signKeys); + $_ENV['PINENTRY_USER_DATA'] = \json_encode($this->pinentries); } $result = $this->exec($arguments); @@ -666,7 +657,7 @@ class SMIME extends Base return $info['ENC_TO']; } - private function exec(array $arguments) /*: array|false*/ + private function exec(array $arguments, bool $throw = true) /*: array|false*/ { if (\version_compare($this->version, '2.2.5', '<')) { \SnappyMail\Log::error('GPG', "{$this->version} too old"); diff --git a/snappymail/v/0.0.0/app/libraries/snappymail/http/csp.php b/snappymail/v/0.0.0/app/libraries/snappymail/http/csp.php index 08f7b46d5..79c7a0ff5 100755 --- a/snappymail/v/0.0.0/app/libraries/snappymail/http/csp.php +++ b/snappymail/v/0.0.0/app/libraries/snappymail/http/csp.php @@ -27,7 +27,7 @@ class CSP implements \Stringable 'img-src' => ["'self'", 'data:'], 'media-src' => ["'self'", 'data:'], 'style-src' => ["'self'", "'unsafe-inline'"], - 'connect-src' => ["'self'", "keys.openpgp.org"] + 'connect-src' => ["'self'", 'data:', "keys.openpgp.org"] ]; function __construct(string $default = '') diff --git a/snappymail/v/0.0.0/app/libraries/snappymail/idn.php b/snappymail/v/0.0.0/app/libraries/snappymail/idn.php index 4ae9c935d..583668c1f 100644 --- a/snappymail/v/0.0.0/app/libraries/snappymail/idn.php +++ b/snappymail/v/0.0.0/app/libraries/snappymail/idn.php @@ -1,121 +1,98 @@ $v) { + $conv = $toAscii ? \idn_to_ascii($v) : \idn_to_utf8($v); + if ($conv) $domain[$k] = $conv; + } + return \implode('.', $domain); +*/ } private static function emailAddress(string $address, bool $toAscii) : string { - if (\str_contains($address, '@')) { - $local = \explode('@', $address); - $domain = \array_pop($local); - $local = \implode('@', $local); - $arr = \explode('.', $domain); - foreach ($arr as $k => $v) { - $conv = $toAscii ? static::toAscii($v) : static::toUtf8($v); - if ($conv) $arr[$k] = $conv; - } - return $local . '@' . \implode('.', $arr); + if (!\str_contains($address, '@')) { +// throw new \RuntimeException("Invalid email address: {$address}"); + return $address; } - return $toAscii ? static::toAscii($address) : static::toUtf8($address); + $local = \explode('@', $address); + $domain = static::domain(\array_pop($local), $toAscii); + return \implode('@', $local) . '@' . $domain; } private static function uri(string $address, bool $toAscii) : string { - if (\preg_match('#[:\./]#', $address)) { - $parsed = \parse_url($address); - if (isset($parsed['host'])) { - $arr = \explode('.', $parsed['host']); - foreach ($arr as $k => $v) { - $conv = $toAscii ? static::toAscii($v) : static::toUtf8($v); - if ($conv) $arr[$k] = $conv; - } - $parsed['host'] = \implode('.', $arr); + $parsed = \parse_url($address); + if (isset($parsed['host'])) { + $parsed['host'] = static::domain($parsed['host'], $toAscii); - $url = '//'; - if (!empty($parsed['scheme'])) { $url = $parsed['scheme'] . (\strtolower($parsed['scheme']) === 'mailto' ? ':' : '://'); } + $url = empty($parsed['scheme']) ? '//' + : $parsed['scheme'] . (\strtolower($parsed['scheme']) === 'mailto' ? ':' : '://'); /* - if (!empty($parsed['user']) || !empty($parsed['pass'])) { - $ret_url .= $parts_arr['user']; - if (!empty($parts_arr['pass'])) { - $ret_url .= ':' . $parts_arr['pass']; - } - $ret_url .= '@'; + if (!empty($parsed['user']) || !empty($parsed['pass'])) { + $ret_url .= $parts_arr['user']; + if (!empty($parts_arr['pass'])) { + $ret_url .= ':' . $parts_arr['pass']; } + $ret_url .= '@'; + } */ - if (!empty($parsed['host'])) { $url .= $parsed['host']; } - if (!empty($parsed['port'])) { $url .= ':'.$parsed['port']; } - if (!empty($parsed['path'])) { $url .= $parsed['path']; } - if (!empty($parsed['query'])) { $url .= '?'.$parsed['query']; } - if (!empty($parsed['fragment'])) { $url .= '#'.$parsed['fragment']; } - return $url; - } - - // parse_url seems to have failed, try without it - $arr = \explode('.', $address); - foreach ($arr as $k => $v) { - $conv = $toAscii ? static::toAscii($v) : static::toUtf8($v); - if ($conv) $arr[$k] = $conv; - } - return \implode('.', $arr); + if (!empty($parsed['host'])) { $url .= $parsed['host']; } + if (!empty($parsed['port'])) { $url .= ':'.$parsed['port']; } + if (!empty($parsed['path'])) { $url .= $parsed['path']; } + if (!empty($parsed['query'])) { $url .= '?'.$parsed['query']; } + if (!empty($parsed['fragment'])) { $url .= '#'.$parsed['fragment']; } + return $url; } - return $toAscii ? static::toAscii($address) : static::toUtf8($address); + + // parse_url seems to have failed, try without it + return static::domain($address, $toAscii); } } diff --git a/snappymail/v/0.0.0/app/libraries/snappymail/integrity.php b/snappymail/v/0.0.0/app/libraries/snappymail/integrity.php index 7b1d301a9..e6d25e6bd 100644 --- a/snappymail/v/0.0.0/app/libraries/snappymail/integrity.php +++ b/snappymail/v/0.0.0/app/libraries/snappymail/integrity.php @@ -101,7 +101,6 @@ abstract class Integrity 'mbstring' => extension_loaded('mbstring'), 'Zlib' => extension_loaded('zlib'), // enabled by default: - 'ctype' => extension_loaded('ctype'), 'json' => function_exists('json_decode'), 'libxml' => function_exists('libxml_use_internal_errors'), 'dom' => class_exists('DOMDocument'), diff --git a/snappymail/v/0.0.0/app/libraries/snappymail/log.php b/snappymail/v/0.0.0/app/libraries/snappymail/log.php index 6e87a02be..a3c4db69f 100644 --- a/snappymail/v/0.0.0/app/libraries/snappymail/log.php +++ b/snappymail/v/0.0.0/app/libraries/snappymail/log.php @@ -37,28 +37,36 @@ abstract class Log protected static function log(int $level, string $prefix, string $msg) { - \RainLoop\Api::Logger()->Write($msg, $level, $prefix); + if (\RainLoop\Api::Logger()->IsEnabled()) { + \RainLoop\Api::Logger()->Write($msg, $level, $prefix); + } else { /* - static $log_level; - // Default to level 4, 0 = LOG_EMERG, 7 = LOG_DEBUG - if (!$log_level) { - $log_level = \max(3, \RainLoop\Api::Config()->Get('logs', 'level', \LOG_WARNING)); - } - if ($level <= $log_level) { - if (\RainLoop\Api::Config()->Get('logs', 'syslog') && \openlog('snappymail', \LOG_ODELAY, \LOG_USER)) { - \syslog($level, "{$prefix} {$msg}"); - \closelog(); + static $log_level; + // Default to level 4, 0 = LOG_EMERG, 7 = LOG_DEBUG + if (!$log_level) { + $log_level = \max(3, \RainLoop\Api::Config()->Get('logs', 'level', \LOG_WARNING)); + } + if ($level <= $log_level) { + if (\RainLoop\Api::Config()->Get('logs', 'syslog') && \openlog('snappymail', \LOG_ODELAY, \LOG_USER)) { + \syslog($level, "{$prefix} {$msg}"); + \closelog(); + } + + if (\filter_var(\ini_get('log_errors'), FILTER_VALIDATE_BOOLEAN) + && (($level < \LOG_WARNING && \error_reporting() & \E_ERROR) + || ($level == \LOG_WARNING && \error_reporting() & \E_WARNING) + || ($level > \LOG_WARNING && \error_reporting() & \E_NOTICE) + )) { + \error_log($prefix . ' ' . static::$levels[$level] . ': ' . $msg); +// \error_log($prefix . ' ' . static::$levels[$level] . ': ' . $msg, 3, 'filename'); + } } - if (\filter_var(\ini_get('log_errors'), FILTER_VALIDATE_BOOLEAN) - && (($level < \LOG_WARNING && \error_reporting() & \E_ERROR) - || ($level == \LOG_WARNING && \error_reporting() & \E_WARNING) - || ($level > \LOG_WARNING && \error_reporting() & \E_NOTICE) - )) { - \error_log($prefix . ' ' . static::$levels[$level] . ': ' . $msg); -// \error_log($prefix . ' ' . static::$levels[$level] . ': ' . $msg, 3, 'filename'); + if (\class_exists('OC')) { +// \OCP\Log\logger('snappymail')->log(\intval($level), $msg); + \OCP\Log\logger('snappymail')->{static::$levels[$level]}($msg); } - } */ + } } } diff --git a/snappymail/v/0.0.0/app/libraries/snappymail/pgp/backup.php b/snappymail/v/0.0.0/app/libraries/snappymail/pgp/backup.php index 3e7403a14..18b18cb1f 100644 --- a/snappymail/v/0.0.0/app/libraries/snappymail/pgp/backup.php +++ b/snappymail/v/0.0.0/app/libraries/snappymail/pgp/backup.php @@ -49,7 +49,6 @@ class Backup \RainLoop\Providers\Storage\Enumerations\StorageType::PGP, true ); - $hash = $oAccount->CryptKey(); foreach (\glob("{$dir}*") as $file) { if (\is_file($file)) { if ('_public.asc' === \substr($file, -11)) { @@ -61,6 +60,7 @@ class Backup $key = \json_decode(\file_get_contents($file)); if (\is_array($key)) { $mac = \array_pop($key); + $hash = $oAccount->CryptKey(); if (!empty($key[2]) && \hash_hmac('sha1', $key[2], $hash) === $mac) { $key[1] = \base64_decode($key[1]); $key[2] = \base64_decode($key[2]); diff --git a/snappymail/v/0.0.0/app/libraries/snappymail/pgp/gnupg.php b/snappymail/v/0.0.0/app/libraries/snappymail/pgp/gnupg.php index 72494fd68..bbea8bbb0 100644 --- a/snappymail/v/0.0.0/app/libraries/snappymail/pgp/gnupg.php +++ b/snappymail/v/0.0.0/app/libraries/snappymail/pgp/gnupg.php @@ -15,7 +15,7 @@ abstract class GnuPG return GPG::isSupported(); } - private static $instance; + private static $instance = null; public static function getInstance(string $homedir) : ?PGPInterface { if (!static::$instance) { diff --git a/snappymail/v/0.0.0/app/libraries/snappymail/pgp/pgpinterface.php b/snappymail/v/0.0.0/app/libraries/snappymail/pgp/pgpinterface.php index 50fc1350e..3a5dd2ad6 100644 --- a/snappymail/v/0.0.0/app/libraries/snappymail/pgp/pgpinterface.php +++ b/snappymail/v/0.0.0/app/libraries/snappymail/pgp/pgpinterface.php @@ -7,6 +7,8 @@ use SnappyMail\SensitiveString; interface PGPInterface { public static function isSupported() : bool; +// public function addPinentry(string $keyId, SensitiveString $passphrase); +// public function clearPinentries() : bool public function addDecryptKey(string $fingerprint, SensitiveString $passphrase) : bool; public function addEncryptKey(string $fingerprint) : bool; public function addSignKey(string $fingerprint, SensitiveString $passphrase) : bool; diff --git a/snappymail/v/0.0.0/app/libraries/snappymail/repository.php b/snappymail/v/0.0.0/app/libraries/snappymail/repository.php index c105364a0..2a36360b0 100644 --- a/snappymail/v/0.0.0/app/libraries/snappymail/repository.php +++ b/snappymail/v/0.0.0/app/libraries/snappymail/repository.php @@ -67,40 +67,27 @@ abstract class Repository $bReal = false; $aRep = null; - $sRep = ''; $sRepoFile = 'packages.json'; - $iRepTime = 0; $oCache = \RainLoop\Api::Actions()->Cacher(); - $sCacheKey = \RainLoop\KeyPathHelper::RepositoryCacheFile(static::BASE_URL, $sRepoFile); + $sCacheKey = '/RepositoryCache/Repo/' . static::BASE_URL . '/File/' . $sRepoFile; $sRep = $oCache->Get($sCacheKey); - if ('' !== $sRep) - { - $iRepTime = $oCache->GetTimer($sCacheKey); - } + $iRepTime = $sRep ? $oCache->GetTimer($sCacheKey) : 0; - if ('' === $sRep || 0 === $iRepTime || \time() - 3600 > $iRepTime) - { + if (!$sRep || !$iRepTime || \time() - 3600 > $iRepTime) { $sRep = static::get($sRepoFile); - if ($sRep) - { + if ($sRep) { $aRep = \json_decode($sRep); $bReal = \is_array($aRep) && \count($aRep); - - if ($bReal) - { + if ($bReal) { $oCache->Set($sCacheKey, $sRep); $oCache->SetTimer($sCacheKey); } - } - else - { + } else { throw new \Exception('Cannot read remote repository file: '.$sRepoFile); } - } - else if ('' !== $sRep) - { + } else if ($sRep) { $aRep = \json_decode($sRep, false, 10); $bReal = \is_array($aRep) && \count($aRep); } diff --git a/snappymail/v/0.0.0/app/libraries/snappymail/rtf/controlsymbol.php b/snappymail/v/0.0.0/app/libraries/snappymail/rtf/controlsymbol.php new file mode 100644 index 000000000..01cdddf41 --- /dev/null +++ b/snappymail/v/0.0.0/app/libraries/snappymail/rtf/controlsymbol.php @@ -0,0 +1,9 @@ + Groups\ColorTable::class, + 'filetbl' => Groups\FileTable::class, + 'fonttbl' => Groups\FontTable::class, + 'generator' => Groups\Generator::class, + 'info' => Groups\Info::class, + 'listtable' => Groups\ListTable::class, + 'listtext' => Groups\ListText::class, + 'listoverridetable' => Groups\ListOverrideTable::class, + 'pict' => Groups\Pict::class, + 'stylesheet' => Groups\StyleSheet::class + ]; + + public function __construct(string $rtf) + { + /* + * Attempt to parse an RTF string. + */ + $this->rtf = $rtf; + $this->len = \strlen($rtf); + $this->pos = 0; + $this->root = null; + $this->group = null; + $this->char = ''; + $this->uc = []; + + while ($this->pos < $this->len-1) { + // Read next character: + $this->nextChar(); + + // Ignore \r and \n + if ($this->is_endofline()) continue; + + // What type of character is this? + switch ($this->char) + { + // Group handling + case '{': + $this->startGroup(); + break; + case '}': + $this->endGroup(); + break; + + case '\\': + // If there is no current group, then this is not a valid RTF file. + if (!$this->group) { + throw new \RuntimeException('Parse error: RTF text outside of group.'); + } + // Beginning of an RTF control word or control symbol. + // Look ahead by one character to see if it starts with + // a letter (control word) or another symbol (control symbol): + $this->group->children[] = \ctype_alpha($this->nextChar(true)) + ? $this->parseControlWord() + : $this->parseControlSymbol(); + break; + + default: + $this->parseText(); + break; + } + } + $this->rtf = ''; + $this->group = null; + } + + // Get the next character from the RTF stream. + // Parsing is aborted when reading beyond end of input string. + protected function nextChar(bool $peek = false): string + { + if ($this->pos >= $this->len) { + $this->char = null; + throw new \RuntimeException("Parse error: Tried to read past end of input; RTF is probably truncated."); + } + if ($peek) { + return $this->rtf[$this->pos]; + } + $this->char = $this->rtf[$this->pos++]; + return $this->char; + } + + /* + * (Helper method) + * Is the current character end-of-line (EOL)? + */ + protected function is_endofline(): bool + { + if (\strspn($this->char, "\r\n")) { + // Checks for a Windows/Acron type EOL + if (\strspn($this->nextChar(true), "\r\n")) { + ++$this->pos; + } + return true; + } + return false; + } + + // Store state of document on stack. + protected function startGroup() + { + $group = new Group(); + if ('\\' === $this->nextChar(true)) { + $cword = null; + if (\ctype_alpha($this->rtf[$this->pos+1])) { + $this->nextChar(); + $cword = $this->parseControlWord(); + // Replace group with special group? + if (isset(static::$groups[$cword->word])) { + $group = new static::$groups[$cword->word](); + } else { + $group->children[] = $cword; + } + } + else if ('*' === $this->rtf[$this->pos+1]) { + $group = new Groups\Destination(); + $this->nextChar(); + $this->parseControlSymbol(); +// $group->children[] = $this->parseControlSymbol(); + if (\ctype_alpha($this->rtf[$this->pos+1])) { + /** + * Destinations added after the 1987 RTF Specification may be preceded by the control symbol \* + * This control symbol identifies destinations whose related text should be + * ignored if the RTF reader does not recognize the destination control word. + */ + $this->nextChar(); + $cword = $this->parseControlWord(); + // Replace group with special group? + if (isset(static::$groups[$cword->word])) { + $group = new static::$groups[$cword->word](); + } else { + $group->children[] = $cword; + } + } + } + } + + // Is there a current group? Then make the new group its child: + if ($this->group) { + $group->parent = $this->group; + $this->group->children[] = $group; + $this->uc[] = \end($this->uc); + } + // If there is no parent group, then set this group + // as the root group. + else { + $this->root = $group; + // Create uc stack and insert the first default value + $this->uc = [1]; + } + // Set the new group as the current group: + $this->group = $group; + } + + // Retrieve state of document from stack. + protected function endGroup() + { + $this->group = $this->group->parent; + \array_pop($this->uc); + } + + protected function parseControlSymbol(): Entity + { + // Read symbol (one character only). + $symbol = $this->nextChar(); + + // Exceptional case: + // Treat EOL symbols as \par control word + if ($this->is_endofline()) { + $cword = new ControlWord(); + $cword->word = 'par'; + $cword->parameter = 0; + return $cword; + } + + // Symbols ordinarily have no parameter. However, + // if this is \' (a single quote), then it is + // followed by a 2-digit hex-code: + $parameter = 0; + if ('\'' === $symbol) { + $parameter = \hexdec($this->nextChar() . $this->nextChar()); + } + + // Add new control symbol as a child to the current group: + $csymbol = new ControlSymbol(); + $csymbol->symbol = $symbol; + $csymbol->parameter = $parameter; + return $csymbol; + } + + protected function parseControlWord(): ControlWord + { + // Read letters until a non-letter is reached. + $word = ''; + $this->nextChar(); + while (\ctype_alpha($this->char)) { + $word .= $this->char; + $this->nextChar(); + } + + // Read parameter (if any) consisting of digits. + // Parameter may be negative, i.e., starting with a '-' + $negative = false; + if ('-' === $this->char) { + $this->nextChar(); + $negative = true; + } + $parameter = null; + while (\ctype_digit($this->char)) { + if (!$parameter) $parameter = 0; + $parameter = $parameter * 10 + (int) $this->char; + $this->nextChar(); + } + // If no parameter present, assume control word's default (usually 1) + // If no default then assign 0 to the parameter + if (null === $parameter) $parameter = 1; + + // Convert parameter to a negative number when applicable + if ($negative) $parameter = -$parameter; + + // Update uc value + if ('uc' === $word) { + \array_pop($this->uc); + $this->uc[] = $parameter; + } + + // Skip space delimiter + if (' ' !== $this->char && !$this->is_endofline()) { + --$this->pos; + } + + // If this is \u, then the parameter will be followed + // by {$this->uc} characters. + if ('u' === $word) { + // Convert parameter to unsigned decimal unicode + if ($negative) { + $parameter = 65536 + $parameter; + } + + // Will ignore replacement characters $uc times + $uc = \end($this->uc); + while (0 < $uc--) { + $this->nextChar(); + // If the replacement character is encoded as + // hexadecimal value \'hh then jump over it + if ('\\' === $this->char && '\'' === $this->rtf[$this->pos]) + $this->pos = $this->pos + 3; + + // Break if it's an RTF scope delimiter + else if (\strspn($this->char, '{}')) + break; + } + } + + $cword = new ControlWord(); + $cword->word = $word; + $cword->parameter = $parameter; + return $cword; + } + + protected function parseText(): void + { + // If there is no current group, then this is not a valid RTF file. + if (!$this->group) { + throw new \RuntimeException('Parse error: RTF text outside of group.'); + } + + // Parse plain text up to backslash or brace, + // unless escaped. + $text = ''; + $terminate = false; + do + { + // Ignore EOL characters + if ($this->is_endofline()) { + $this->nextChar(); + continue; + } + // Is this an escape? + if ('\\' == $this->char) { + // Perform lookahead to see if this + // is really an escape sequence. + $this->nextChar(); + switch ($this->char) + { + case '\\': break; + case '{': break; + case '}': break; + default: + // Not an escape. Roll back. + $this->pos = $this->pos - 2; + $terminate = true; + break; + } + } else if (\strspn($this->char, '{}')) { + --$this->pos; + $terminate = true; + } + + if (!$terminate) { + // Save plain text + $text .= $this->char; + $this->nextChar(); + } + } + while (!$terminate && $this->pos < $this->len); + + // Create new Text element: + $text = new Text($text); + + // Add text as a child to the current group: + $this->group->children[] = $text; + } + + public function toHTML(): string + { + return (new HtmlFormatter())->format($this); + } +} diff --git a/snappymail/v/0.0.0/app/libraries/snappymail/rtf/encoding.php b/snappymail/v/0.0.0/app/libraries/snappymail/rtf/encoding.php new file mode 100644 index 000000000..85f91af53 --- /dev/null +++ b/snappymail/v/0.0.0/app/libraries/snappymail/rtf/encoding.php @@ -0,0 +1,90 @@ + 'CP1252', // ANSI: Western Europe + 1 => 'CP1252', //*Default + 2 => 'CP1252', //*Symbol + 3 => null, // Invalid + 77 => 'MAC', //*also [MacRoman]: Macintosh + 128 => 'CP932', //*or [Shift_JIS]?: Japanese + 129 => 'CP949', //*also [UHC]: Korean (Hangul) + 130 => 'CP1361', //*also [JOHAB]: Korean (Johab) + 134 => 'CP936', //*or [GB2312]?: Simplified Chinese + 136 => 'CP950', //*or [BIG5]?: Traditional Chinese + 161 => 'CP1253', // Greek + 162 => 'CP1254', // Turkish (latin 5) + 163 => 'CP1258', // Vietnamese + 177 => 'CP1255', // Hebrew + 178 => 'CP1256', // Simplified Arabic + 179 => 'CP1256', //*Traditional Arabic + 180 => 'CP1256', //*Arabic User + 181 => 'CP1255', //*Hebrew User + 186 => 'CP1257', // Baltic + 204 => 'CP1251', // Russian (Cyrillic) + 222 => 'CP874', // Thai + 238 => 'CP1250', // Eastern European (latin 2) + 254 => 'CP437', //*also [IBM437][437]: PC437 + 255 => 'CP437' //*OEM still PC437 + ); + + if (isset($charset[$fcharset])) { + return $charset[$fcharset]; + } + \trigger_error("Unknown charset: {$fcharset}"); + return null; + } + + public static function getFromCodepage($cpg): ?string + { + $codePage = array ( + 'ansi' => 'CP1252', + 'mac' => 'MAC', + 'pc' => 'CP437', + 'pca' => 'CP850', + 437 => 'CP437', // United States IBM + 708 => 'ASMO-708', // also [ISO-8859-6][ARABIC] Arabic + /* Not supported by iconv + 709, => '' // Arabic (ASMO 449+, BCON V4) + 710, => '' // Arabic (transparent Arabic) + 711, => '' // Arabic (Nafitha Enhanced) + 720, => '' // Arabic (transparent ASMO) + */ + 819 => 'CP819', // Windows 3.1 (US and Western Europe) + 850 => 'CP850', // IBM multilingual + 852 => 'CP852', // Eastern European + 860 => 'CP860', // Portuguese + 862 => 'CP862', // Hebrew + 863 => 'CP863', // French Canadian + 864 => 'CP864', // Arabic + 865 => 'CP865', // Norwegian + 866 => 'CP866', // Soviet Union + 874 => 'CP874', // Thai + 932 => 'CP932', // Japanese + 936 => 'CP936', // Simplified Chinese + 949 => 'CP949', // Korean + 950 => 'CP950', // Traditional Chinese + 1250 => 'CP1250', // Windows 3.1 (Eastern European) + 1251 => 'CP1251', // Windows 3.1 (Cyrillic) + 1252 => 'CP1252', // Western European + 1253 => 'CP1253', // Greek + 1254 => 'CP1254', // Turkish + 1255 => 'CP1255', // Hebrew + 1256 => 'CP1256', // Arabic + 1257 => 'CP1257', // Baltic + 1258 => 'CP1258', // Vietnamese + 1361 => 'CP1361'); // Johab + + if (isset($codePage[$cpg])) { + return $codePage[$cpg]; + } + \trigger_error("Unknown codepage: {$cpg}"); + return null; + } +} diff --git a/snappymail/v/0.0.0/app/libraries/snappymail/rtf/entity.php b/snappymail/v/0.0.0/app/libraries/snappymail/rtf/entity.php new file mode 100644 index 000000000..1edc89dc8 --- /dev/null +++ b/snappymail/v/0.0.0/app/libraries/snappymail/rtf/entity.php @@ -0,0 +1,11 @@ +word) { + case 'f': + $this->uid = $entity->parameter; + break; + + // + case 'froman': $this->family = 'serif'; break; + case 'fswiss': $this->family = 'sans-serif'; break; + case 'fmodern': $this->family = 'monospace'; break; + case 'fscript': $this->family = 'cursive'; break; + case 'fdecor': $this->family = 'fantasy'; break; + // case 'fnil': break; // default font + // case 'ftech': break; // symbol + // case 'fbidi': break; // bidirectional font + + case 'fcharset': + $this->charset = Encoding::getFromCharset($entity->parameter); + break; + + case 'fprq': + $this->fprq = $entity->parameter; + break; + + case 'cpg': + $this->codepage = Encoding::getFromCodepage($entity->parameter); + break; + } + } + + // + else if ($entity instanceof Text) { + // Store font name (except ; delimiter at end) + $this->name = \substr($entity->text, 0, -1); + } + + /* + else if ($entity instanceof Group) { + // possible subgroups: + // + // + // + // + // + } + */ + } + + public function __toString(): string + { + $list = []; + if ($this->name) { + // If the name has double quotes replace them with single quotes + $this->name = \str_replace('"', "'", $this->name); + $list[] = $this->name; + } + if ($this->family) { + $list[] = $this->family; + } + return $list ? 'font-family:' . \implode(', ', $list) : ''; + } +} diff --git a/snappymail/v/0.0.0/app/libraries/snappymail/rtf/group.php b/snappymail/v/0.0.0/app/libraries/snappymail/rtf/group.php new file mode 100644 index 000000000..f27a2d160 --- /dev/null +++ b/snappymail/v/0.0.0/app/libraries/snappymail/rtf/group.php @@ -0,0 +1,32 @@ +children[0])) { + $child = $this->children[0]; + + // If the first child is a control word, then + // the group type is the word. + if ($child instanceof ControlWord) { + return $child->word; + } + + // If the first child is a control symbol, then + // the group type is * for a special symbol, or null. + if ($child instanceof ControlSymbol) { + return ('*' === $child->symbol) ? '*' : null; + } + } + + // If first child is neither word nor symbol, then + // group type is null. + return null; + } +} diff --git a/snappymail/v/0.0.0/app/libraries/snappymail/rtf/groups/colortable.php b/snappymail/v/0.0.0/app/libraries/snappymail/rtf/groups/colortable.php new file mode 100644 index 000000000..263c18541 --- /dev/null +++ b/snappymail/v/0.0.0/app/libraries/snappymail/rtf/groups/colortable.php @@ -0,0 +1,58 @@ + '{' \colortbl + '}' + * ? & \ctintN? & \cshadeN? \redN? & \greenN? & \blueN? ';' + * \cmaindarkone | \cmainlightone | \cmaindarktwo | \cmainlighttwo | + * \caccentone | \caccenttwo | \caccentthree | \caccentfour | \caccentfive | + * \caccentsix | \chyperlink | \cfollowedhyperlink | \cbackgroundone | + * \ctextone | \cbackgroundtwo | \ctexttwo + */ +class ColorTable extends Group +{ + private static $themecolors = [ + 'cmaindarkone', 'cmainlightone', 'cmaindarktwo', 'cmainlighttwo', + 'caccentone', 'caccenttwo', 'caccentthree', 'caccentfour', 'caccentfive', + 'caccentsix', 'chyperlink', 'cfollowedhyperlink', 'cbackgroundone', + 'ctextone', 'cbackgroundtwo', 'ctexttwo' + ]; + + public function getColors(): array + { + $colortbl = [ + null +// 'inherit' +// 'initial' +// 'unset' + ]; + $color = []; + foreach ($this->children as $i => $entity) { + if ($entity instanceof ControlWord) { + if (isset(static::$themecolors[$entity->word])) { + $color['themecolor'] = $entity->word; + } else { + $color[$entity->word] = $entity->parameter; + } + } else if ($entity instanceof Text) { + // This is a delimiter ';' === $entity->text + if ($i) { + if (isset($color['red'])) { + $colortbl[] = "rgb({$color['red']},{$color['green']},{$color['blue']})"; + // hex string format +// $colortbl[] = \sprintf('#%02x%02x%02x', $color['red'], $color['green'], $color['blue']); + } else if (isset($color['themecolor'])) { + $colortbl[] = "var(--rtf-{$color['themecolor']})"; + } + } + $color = []; + } + } + return $colortbl; + } +} diff --git a/snappymail/v/0.0.0/app/libraries/snappymail/rtf/groups/destination.php b/snappymail/v/0.0.0/app/libraries/snappymail/rtf/groups/destination.php new file mode 100644 index 000000000..df7edb56b --- /dev/null +++ b/snappymail/v/0.0.0/app/libraries/snappymail/rtf/groups/destination.php @@ -0,0 +1,7 @@ + '{' \fonttbl ( | ('{' '}'))+ '}' + * ? \fN \fcharsetN? \fprq? ? ? ? \cpgN? ? ';' + * \flomajor | \fhimajor | \fdbmajor | \fbimajor | \flominor | \fhiminor | \fdbminor | \fbiminor + * \fnil | \froman | \fswiss | \fmodern | \fscript | \fdecor | \ftech | \fbidi + * '{\*' \panose '}' + * '{\*' \fname #PCDATA ';}' + * #PCDATA + * '{\*' \falt #PCDATA '}' + * '{\*' \fontemb ? ? '}' + * \ftnil | \fttruetype + * '{\*' \fontfile \cpgN? #PCDATA '}' + */ +class FontTable extends Group +{ + public function getFonts(): array + { + $fonts = []; + $basefont = null; + foreach ($this->children as $fontinfo) { + if ($fontinfo instanceof Group) { + $font = new FontInfo(); + foreach ($fontinfo->children as $entity) { + $font->addEntity($entity); + } + $fonts[$font->uid] = $font; + } else { + $basefont = $basefont ?: new FontInfo(); + $basefont->addEntity($fontinfo); + } + } + if ($basefont && !$fonts) { + $fonts[$basefont->uid] = $basefont; + } + return $fonts; + } +} diff --git a/snappymail/v/0.0.0/app/libraries/snappymail/rtf/groups/generator.php b/snappymail/v/0.0.0/app/libraries/snappymail/rtf/groups/generator.php new file mode 100644 index 000000000..a2b4f17b9 --- /dev/null +++ b/snappymail/v/0.0.0/app/libraries/snappymail/rtf/groups/generator.php @@ -0,0 +1,7 @@ +children as $child) { + if ($child instanceof ControlWord) { + switch ($child->word) { + // Picture Format + case 'emfblip': + case 'pngblip': + case 'jpegblip': + $format = \substr($child->word, 0, -4); + break; + case 'macpict': + $format = 'pict'; + break; +// case 'wmetafile': $format = 'bmp'; break; + + // Picture size and scaling + case 'picw': + $style[] = "width:{$child->parameter}px"; + break; + case 'pich': + $style[] = "height:{$child->parameter}px"; + break; + case 'picscalex': +// $style[] = "width:{$child->parameter}%"; + break; + case 'picscaley': +// $style[] = "height:{$child->parameter}%"; + break; + + // Binary or Hexadecimal Data ? + case 'bin': $binarySize = $child->parameter; break; + default: break; + } + + } elseif ($child instanceof Text) { // store Data + $data = $child->text; + } + } + + if ($binarySize) { + $data = \substr($data, 0, $binarySize); + } else { + $data = \pack('H*',$data); + } + + if ($style) { + $style = ' style="' . \implode(';', $style) . '"'; + } + + return ""; + } +} diff --git a/snappymail/v/0.0.0/app/libraries/snappymail/rtf/groups/stylesheet.php b/snappymail/v/0.0.0/app/libraries/snappymail/rtf/groups/stylesheet.php new file mode 100644 index 000000000..3ae4ebed8 --- /dev/null +++ b/snappymail/v/0.0.0/app/libraries/snappymail/rtf/groups/stylesheet.php @@ -0,0 +1,7 @@ +output = ''; + // Keep track of style modifications + $this->previousStyle = null; + // and create a stack of styles + $this->styles = array(); + // Put an initial standard style onto the stack + $this->style = new Style(); + $this->styles[] = $this->style; + + // Keep track of opened html tags + $this->openedTags = [ + 'span' => false, + 'p' => false + ]; + // Create the first paragraph + $this->openTag('p'); + // Begin format + $this->processGroup($document->root); + // Close opened tags + $this->closeTags(true); + return $this->output; + } + + protected function processGroup(Group $group) + { + if ($group instanceof Groups\Destination) { + $this->processDestination($group->children); + return; + } + + if ($group instanceof Groups\ColorTable) { + Style::$colortbl = $group->getColors(); + return; + } + if ($group instanceof Groups\FontTable) { + Style::$fonttbl = $group->getFonts(); + return; + } + if ($group instanceof Groups\Pict) { + $this->output .= $group->getHTML(); + return; + } + if ($group instanceof Groups\StyleSheet) { + // Stylesheet extraction not yet supported + return; + } + + if ($group instanceof Groups\FileTable + || $group instanceof Groups\Info + || $group instanceof Groups\ListTable + || $group instanceof Groups\ListText + || $group instanceof Groups\ListOverrideTable + ) { + // Skip + return; + } + + // Special group processing: + switch ($group->getType()) + { + case 'nonshppict': + // Ignore alternative images + return; + } + + // Pictures extraction not yet supported + //if (\substr($group->getType(), 0, 4) == "pict") return; + + // Push a new style onto the stack: + $this->style = clone $this->style; + $this->styles[] = $this->style; + + foreach ($group->children as $child) { + $this->processEntity($child); + } + + // Pop style from stack + \array_pop($this->styles); + $this->style = \end($this->styles); + } + + protected function processDestination(array $dest) + { + // Check if this is a Word 97 picture + if ($dest[0] instanceof ControlWord && 'shppict' == $dest[0]->word) { + $c = \count($dest); + for ($i = 1; $i < $c; ++$i) { + $this->processEntity($dest[$i]); + } + } + } + + protected function processEntity(Entity $entry) + { + if ($entry instanceof Group) $this->processGroup($entry); + else if ($entry instanceof ControlWord) $this->processControlWord($entry); + else if ($entry instanceof ControlSymbol) $this->processControlSymbol($entry); + else if ($entry instanceof Text) $this->write($entry); + } + + protected function processControlWord(ControlWord $word) + { + switch ($word->word) { + case 'plain': // Reset font formatting properties to default. + case 'pard': // Reset to default paragraph properties. + $this->style->reset($this->defaultFont); + break; + + /* Font formatting properties: */ + + case 'b': // Bold + case 'caps': // All capitals + case 'embo': // Emboss + case 'i': // Italic + case 'impr': // Engrave (imprint) + case 'outl': // Outline + case 'scaps': // Small capitals + case 'shad': // Shadow + case 'strike': // Strike-through + case 'striked': // Double strike-through + $this->style->{$word->word} = $word->parameter; + break; + case 'sub': // Subscripts + case 'super': // Superscripts + $this->style->verticalAlign = $word->word; + break; + case 'nosupersub': // Turns off superscripting or subscripting + $this->style->verticalAlign = null; + break; + case 'ul': // Underline + case 'uld': // Dotted underline + case 'uldash': // Dashed underline + case 'uldashd': // Dash-dotted underline + case 'uldashdd': // Dash-dot-dotted underline + case 'uldb': // Double underline + case 'ulhwave': // Heavy wave underline + case 'ulldash': // Long dashed underline + case 'ulnone': // no underline + case 'ulth': // Thick underline + case 'ulthd': // Thick dotted underline + case 'ulthdash': // Thick dashed underline + case 'ulthdashd': // Thick dash-dotted underline + case 'ulthdashdd': // Thick dash-dot-dotted underline + case 'ulthldash': // Thick long dashed underline + case 'ululdbwave': // Double wave underline + case 'ulw': // Word underline + case 'ulwave': // Wave underline + $this->style->underline = $word->word; + break; + case 'v': // hidden + $this->style->hidden = $word->parameter; + break; + case 'fs': // Font size in half-points + $this->style->fs = $word->parameter; + break; + case 'f': // Font number + $this->style->font = $word->parameter; + break; + case 'deff': // Store default font + $this->defaultFont = $word->parameter; + break; +/* + case 'ltrmark': + $this->style->dir = 'ltr'; + break; + case 'rtlmark': + $this->style->dir = 'rtl'; + break; + + \ab* Associated font is bold. + \acaps* Associated font is all capitals. + \acfN Associated foreground color (default is 0). + \adnN Associated font is subscript position in half-points (default is 6). + \aexpndN Expansion or compression of the space between characters in quarter-points; a negative value compresses (default is 0). + \afN Associated font number (default is 0). + \afsN Associated font size in half-points (default is 24). + \ai* Associated font is italic. + \alangN Language ID (see the standard language table) for the associated font. + \aoutl* Associated font is outline. + \ascaps* Associated font is small capitals. + \ashad* Associated font is shadow. + \astrike* Associated font is strikethrough. + \aul Associated font is continuous underline. \aul0 turns off all underlining for the alternate font. + \auld Associated font is dotted underline. + \auldb Associated font is double underline. + \aulnone Associated font is no longer underlined. + \aulw Associated font is word underline. + \aupN Superscript position in half-points (default is 6). + \fcsN N = 1 means South East Asian complex script; N = 0 means not South East Asian script + \loch The text consists of single-byte low-ANSI (0x00–0x7F) characters. + \hich The text consists of single-byte high-ANSI (0x80–0xFF) characters. + \dbch The text consists of double-byte characters. +*/ + + /* Colors */ + + case 'cf': + case 'chcfpat': + $this->style->fontcolor = $word->parameter; + break; + case 'cb': + case 'chcbpat': + $this->style->background = $word->parameter; + break; + case 'highlight': + case 'ulc': // Underline color + $this->style->{$word->word} = $word->parameter; + break; + + /* Special characters */ + + case 'lquote': $this->write('‘'); break; + case 'rquote': $this->write('’'); break; + case 'ldblquote': $this->write('“'); break; + case 'rdblquote': $this->write('”'); break; + case 'bullet': $this->write('•'); break; + case 'endash': $this->write('–'); break; + case 'emdash': $this->write('—'); break; + case 'enspace': $this->write(' '); break; + case 'emspace': $this->write(' '); break; + case 'qmspace': $this->write(' '); break; + case 'tab': $this->write("\t"); break; + case 'line': $this->output .= '
'; break; // "\r\n" +// case 'lbr': $this->output .= '
'; break; + case 'zwbo': $this->write("\xC2\x82"); break; + case 'zwnbo': $this->write("\xC2\x83"); break; + case 'zwj': $this->write("\xE2\x80\x8D"); break; + case 'zwnj': $this->write("\xE2\x80\x8C"); break; + + /* Unicode characters */ + + case 'u': + $this->write($this->decodeUnicode($word->parameter)); + break; + + /* Paragraphs */ + + case 'par': // End of paragraph + case 'row': // End of table row + // Close previously opened tags + $this->closeTags(); + // Begin a new paragraph + $this->openTag('p'); + break; + + /* Tables */ + case 'cell': // End of table cell + case 'nestcell': // End of nested table cell + case 'nestrow': // End of nested table row + break; + + /* Code pages */ + case 'ansi': + case 'mac': + case 'pc': + case 'pca': + $this->RTFencoding = Encoding::getFromCodepage($word->word); + break; + case 'ansicpg': + if ($word->parameter) { + $this->RTFencoding = Encoding::getFromCodepage($word->parameter); + } + break; + + case 'fromtext': // Indicates document was originally plain text email. + case 'fromhtml': // Indicates document was originally HTML email and may contain encapsulated HTML tags. + break; + + } + } + + protected function decodeUnicode($code, $srcEnc = 'UTF-8') + { + $utf8 = ''; + if ($srcEnc && 'UTF-8' != $srcEnc) { + // convert character to Unicode + $utf8 = \iconv($srcEnc, 'UTF-8', \chr($code)); + } + return $utf8 ? $utf8 : \mb_convert_encoding("&#{$code};", 'UTF-8', 'HTML-ENTITIES'); + } + + protected function write(string $txt) + { + //$tag = \str_replace('er', '', $this->style->verticalAlign ?: 'span'); // sub / sup / span + // Create a new 'span' element only when a style change occurs. + // 1st case: style change occured + // 2nd case: there is no change in style but the already created 'span' + // element is somehow closed (ex. because of an end of paragraph) + if (!$this->style->equals($this->previousStyle) || + ($this->style->equals($this->previousStyle) && !$this->openedTags['span']) + ) { + $this->closeTag('span'); + $this->previousStyle = clone $this->style; + $this->openTag('span', $this->style); + } + $this->output .= $txt; + } + + protected function openTag(string $tag, string $attr = '') + { + $this->output .= $attr ? "<{$tag} {$attr}>" : "<{$tag}>"; + $this->openedTags[$tag] = true; + } + + protected function closeTag(string $tag, bool $end = false) + { + if ($this->openedTags[$tag]) { + // Check for empty html elements + if (\substr($this->output , -\strlen("<{$tag}>")) == "<{$tag}>") { + if ('p' === $tag && !$end) { + // Replace empty 'p' element with a line break + $this->output = \substr($this->output ,0, -3) . "
"; + } else { + // Delete empty elements + $this->output = \substr($this->output ,0, -\strlen("<{$tag}>")); + } + } else { + $this->output .= ""; + } + $this->openedTags[$tag] = false; + } + } + + protected function closeTags(bool $end = false) + { + foreach ($this->openedTags as $tag => $b) { + $this->closeTag($tag, $end); + } + } + + protected function processControlSymbol(ControlSymbol $symbol) + { + if ("'" === $symbol->symbol) { + // "\'hh" = A hexadecimal value, based on the specified character set + $enc = $this->getSourceEncoding(); + $uchar = $this->decodeUnicode($symbol->parameter, $enc); + $this->write($uchar); + } else if ('~' === $symbol->symbol) { + // Non breaking space +// $this->write(' '); +// $this->write("\xC2\xA0"); + $this->write(' '); + } else if ('-' === $symbol->symbol) { + // Optional hyphen +// $this->write("­"); +// $this->write("\xC2\xAD"); + $this->write('­'); + } else if ('_' === $symbol->symbol) { + // Non breaking hyphen +// $this->write("‑"); +// $this->write("\xE2\x80\x91"); + $this->write('‑'); + } + } + + protected function getSourceEncoding() + { + if (isset($this->style->font)) { + if (isset(Style::$fonttbl[$this->style->font]->codepage)) { + return Style::$fonttbl[$this->style->font]->codepage; + } + if (isset(Style::$fonttbl[$this->style->font]->charset)) { + return Style::$fonttbl[$this->style->font]->charset; + } + } + return $this->RTFencoding; + } + + protected function ord_utf8($chr) + { + $ord0 = \ord($chr); + if ($ord0 >= 0 && $ord0 <= 127) + return $ord0; + + $ord1 = \ord($chr[1]); + if ($ord0 >= 192 && $ord0 <= 223) + return ($ord0 - 192) * 64 + ($ord1 - 128); + + $ord2 = \ord($chr[2]); + if ($ord0 >= 224 && $ord0 <= 239) + return ($ord0 - 224) * 4096 + ($ord1 - 128) * 64 + ($ord2 - 128); + + $ord3 = \ord($chr[3]); + if ($ord0 >= 240 && $ord0 <= 247) + return ($ord0 - 240) * 262144 + ($ord1 - 128) * 4096 + ($ord2 - 128) * 64 + ($ord3 - 128); + + $ord4 = \ord($chr[4]); + if ($ord0 >= 248 && $ord0 <= 251) + return ($ord0 - 248) * 16777216 + ($ord1 - 128) * 262144 + ($ord2 - 128) * 4096 + ($ord3 - 128) * 64 + ($ord4 - 128); + + if ($ord0 >= 252 && $ord0 <= 253) + return ($ord0 - 252) * 1073741824 + ($ord1 - 128) * 16777216 + ($ord2 - 128) * 262144 + ($ord3 - 128) * 4096 + ($ord4 - 128) * 64 + (\ord($chr[5]) - 128); + + \trigger_error("Invalid Unicode character: {$chr}"); + } +} + +class Style implements \Stringable +{ + public static + $fonttbl = [], + $colortbl = []; + + private static + $highlights = [ + 1 => 'Black', + 2 => 'Blue', + 3 => 'Cyan', + 4 => 'Green', + 5 => 'Magenta', + 6 => 'Red', + 7 => 'Yellow', + 8 => 'Unused', + 9 => 'DarkBlue', + 10 => 'DarkCyan', + 11 => 'DarkGreen', + 12 => 'DarkMagenta', + 13 => 'DarkRed', + 14 => 'DarkYellow', + 15 => 'DarkGray', + 16 => 'LightGray' + ], + $underlineStyles = [ + 'ul' => 'solid 1px', // Underline + 'uld' => 'dotted 1px', // Dotted underline + 'uldash' => 'dashed 1px', // Dashed underline + 'uldashd' => 'dashed 1px', // Dash-dotted underline + 'uldashdd' => 'dashed 1px', // Dash-dot-dotted underline + 'uldb' => 'double 1px', // Double underline + 'ulhwave' => 'wavy 3px', // Heavy wave underline + 'ulldash' => 'dashed 1px', // Long dashed underline + 'ulnone' => '', // no underline + 'ulth' => 'solid 2px', // Thick underline + 'ulthd' => 'dotted 2px', // Thick dotted underline + 'ulthdash' => 'dashed 2px', // Thick dashed underline + 'ulthdashd' => 'dashed 2px', // Thick dash-dotted underline + 'ulthdashdd' => 'dashed 2px', // Thick dash-dot-dotted underline + 'ulthldash' => 'dashed 2px', // Thick long dashed underline + 'ululdbwave' => 'wavy 2px', // Double wave underline +// 'ulw' => 'wavy 1px', // Word underline + 'ulwave' => 'wavy 1px', // Wave underline + ]; + + public bool + $b = false, // Bold + $caps = false, // All capitals + $embo = false, // Emboss + $i = false, // Italic + $impr = false, // Engrave (imprint) + $outl = false, // Outline + $scaps = false, // Small capitals + $shad = false, // Shadow + $strike = false, // Strike-through + $striked = false, // Double strike-through + $hidden = false; + + public int + $fs = 0; // Font size in half-points, default is 24 = 12pt + + public + $underline = 'ulnone', + $verticalAlign = '', // sub / super + $fontcolor = null, + $background = null, + $font = null, + $highlight = null, + $ulc = null; // Underline color + + public function reset($defaultFont = null) : void + { + $this->b = false; + $this->caps = false; + $this->embo = false; + $this->i = false; + $this->impr = false; + $this->outl = false; + $this->scaps = false; + $this->shad = false; + $this->strike = false; + $this->striked = false; + $this->hidden = false; + $this->fs = 0; + $this->underline = 'ulnone'; + $this->verticalAlign = ''; + $this->fontcolor = null; + $this->background = null; + $this->highlight = null; + $this->ulc = null; + $this->font = isset($defaultFont) ? $defaultFont : null; + } + + public function __toString(): string + { + $style = []; + + if ($this->b ) $style[] = 'font-weight:bold'; + if ($this->caps ) $style[] = 'text-transform:uppercase'; +// if ($this->embo ) $style[] = 'text-shadow: 1px 4px 4px #555;background-clip:text'; + if ($this->i ) $style[] = 'font-style:italic'; +// if ($this->impr ) $style[] = 'text-shadow: rgba(245,245,245,0.5) 3px 5px 1px;background-clip:text'; +// if ($this->outl ) $style[] = ''; + if ($this->scaps ) $style[] = 'font-variant:small-caps'; +// if ($this->shad ) $style[] = ''; + if ($this->strike ) $style[] = 'text-decoration:line-through'; +// if ($this->striked ) $style[] = ''; + if ($this->verticalAlign) $style[] = "vertical-align:{$this->verticalAlign};font-size:smaller"; + if (!empty(static::$underlineStyles[$this->underline])) { + $value = 'text-decoration:underline'; + if (!empty(static::$colortbl[$this->ulc])) { + $value .= ' ' . static::$colortbl[$this->ulc]; + } + $value .= ' ' . static::$underlineStyles[$this->underline]; + $style[] = $value; + } + if ($this->hidden) $style[] = 'display:none'; + if (!empty(static::$fonttbl[$this->font])) { + $font = (string) static::$fonttbl[$this->font]; + if ($font) { + $style[] = $font; + } + } + if (0 < $this->fs) { +// $style[] = 'font-size:'.\ceil($this->fs / 2).'pt'; +// $style[] = 'font-size:'.\ceil($word->parameter * 16 / 24).'px'; + $style[] = 'font-size:'.\round($this->fs / 24, 2).'em'; + } + if (!empty(static::$colortbl[$this->fontcolor])) { + $style[] = 'color:' . static::$colortbl[$this->fontcolor]; + } + if (!empty(static::$colortbl[$this->background])) { + $style[] = 'background-color:' . static::$colortbl[$this->background]; + } else if (!empty(static::$highlights[$this->highlight])) { + $style[] = 'background-color:' . static::$highlights[$this->highlight]; + } + return $style ? 'style="'.\implode(';', $style).'"' : ''; + } + + /* + * Check whether this Style is equal to another Style. + */ + public function equals(?Style $style) : bool + { + return $style + && ($this->b == $style->b) + && ($this->caps == $style->caps) +// && ($this->embo == $style->embo) + && ($this->i == $style->i) +// && ($this->impr == $style->impr) +// && ($this->outl == $style->outl) + && ($this->scaps == $style->scaps) +// && ($this->shad == $style->shad) + && ($this->strike == $style->strike) + && ($this->striked == $style->striked) + && ($this->verticalAlign == $style->verticalAlign) + && ($this->underline == $style->underline) + && ($this->ulc == $style->ulc) + && ($this->hidden == $style->hidden) + && ($this->fs == $style->fs) + && ($this->fontcolor == $style->fontcolor) + && ($this->background == $style->background) + && ($this->highlight == $style->highlight) + && ($this->font == $style->font) + ; + } +} diff --git a/snappymail/v/0.0.0/app/libraries/snappymail/rtf/text.php b/snappymail/v/0.0.0/app/libraries/snappymail/rtf/text.php new file mode 100644 index 000000000..d0a229fee --- /dev/null +++ b/snappymail/v/0.0.0/app/libraries/snappymail/rtf/text.php @@ -0,0 +1,18 @@ +text = $text; + } + + public function __toString(): string + { + return $this->text; + } +} diff --git a/snappymail/v/0.0.0/app/libraries/snappymail/stream/tar.php b/snappymail/v/0.0.0/app/libraries/snappymail/stream/tar.php index f3a124f21..a64c87586 100644 --- a/snappymail/v/0.0.0/app/libraries/snappymail/stream/tar.php +++ b/snappymail/v/0.0.0/app/libraries/snappymail/stream/tar.php @@ -53,7 +53,7 @@ class TAR $name .= '.tgz'; $name = \preg_match('#^[\x01-\x7F]*$#D', $name) ? $name : '=?UTF-8?B?'.\base64_encode($name).'?='; \header("Content-Disposition: attachment; filename={$name}"); - \header("Content-Type: application/x-gzip; name={$name}"); + \header("Content-Type: application/gzip; name={$name}"); } else { $name .= '.tar'; $name = \preg_match('#^[\x01-\x7F]*$#D', $name) ? $name : '=?UTF-8?B?'.\base64_encode($name).'?='; diff --git a/snappymail/v/0.0.0/app/libraries/snappymail/upgrade.php b/snappymail/v/0.0.0/app/libraries/snappymail/upgrade.php index 25bc65979..5f9051cbf 100644 --- a/snappymail/v/0.0.0/app/libraries/snappymail/upgrade.php +++ b/snappymail/v/0.0.0/app/libraries/snappymail/upgrade.php @@ -70,14 +70,10 @@ abstract class Upgrade } try { $aNewAccounts[$sEmail] = [ - 'account', - $sEmail, - $sEmail, // sLogin - '', // sPassword - '', // sClientCert - '', // sProxyAuthUser - '', // sProxyAuthPassword - \hash_hmac('sha1', '', $sHash) + 'email' => $sEmail, + 'login' => $sEmail, + 'pass' => '', + 'hmac' => \hash_hmac('sha1', '', $sHash) ]; if (!$sToken) { \SnappyMail\Log::warning('UPGRADE', "ConvertInsecureAccount {$sEmail} no token"); @@ -92,15 +88,10 @@ abstract class Upgrade } $aAccountHash[3] = Crypt::EncryptUrlSafe($aAccountHash[3], $sHash); $aNewAccounts[$sEmail] = [ - 'account', - $aAccountHash[1], - $aAccountHash[2], - $aAccountHash[3], - $aAccountHash[11], - $aAccountHash[8], - $aAccountHash[9], - $oMainAccount->Email(), - \hash_hmac('sha1', $aAccountHash[3], $sHash) + 'email' => $aAccountHash[1], + 'login' => $aAccountHash[2], + 'pass' => $aAccountHash[3], + 'hmac' => \hash_hmac('sha1', $aAccountHash[3], $sHash) ]; } catch (\Throwable $e) { \SnappyMail\Log::warning('UPGRADE', "ConvertInsecureAccount {$sEmail} failed"); diff --git a/snappymail/v/0.0.0/app/localization/ar-SA/user.json b/snappymail/v/0.0.0/app/localization/ar-SA/user.json index 681ec190d..1c699a67e 100644 --- a/snappymail/v/0.0.0/app/localization/ar-SA/user.json +++ b/snappymail/v/0.0.0/app/localization/ar-SA/user.json @@ -139,6 +139,7 @@ "MENU_VIEW_ORIGINAL": "إظهار المصدر", "HTML_VIEW": "View HTML message", "PLAIN_VIEW": "View plain text message", + "SWAP_COLORS": "Swap default (background)color", "MENU_DOWNLOAD_ORIGINAL": ".eml التنزيل كملف", "MENU_FILTER_SIMILAR": "تصفية رسائل مثل هذه", "MENU_PRINT": "طباعة", @@ -283,7 +284,8 @@ "ERROR": "%TYPE% error: %ERROR%", "CREATE_SELF_SIGNED": "Create self-signed", "VALID_UNTIL": "Valid until", - "PRIVATE_KEY": "Private key" + "PRIVATE_KEY": "Private key", + "ASK_CRYPTKEY_PASS": "Your login passphrase has changed. Please fill in your old login password to decrypt data" }, "OPENPGP": { "POPUP_IMPORT_TITLE": "OpenPGP استيراد مفتاح", @@ -575,42 +577,43 @@ "LABEL_CLOSE_COMPOSE": "إغلاق الرسالة" }, "NOTIFICATIONS": { - "REQUEST_ERROR": "Request failed", - "REQUEST_ABORTED": "Request is aborted", - "REQUEST_TIMEOUT": "Request timed out", - "INVALID_TOKEN": "خاطىء token", - "AUTH_ERROR": "خطأ في كلمة السر \\اسم المستخدم", - "CONNECTION_ERROR": "لايمكن الإتصال بالمخدم", - "DOMAIN_ALREADY_EXISTS": "الموقع موجود مسبقاَ", - "DOMAIN_NOT_ALLOWED": "غير مسموح لهذا الموقع", - "ACCOUNT_NOT_ALLOWED": "الحساب غير مسموح له", - "CONTACTS_SYNC_ERROR": "خطأ في مزامنة جهات الإتصال", - "CANT_GET_MESSAGE_LIST": "لايمكن الوصول الى قائمة الرسائل", - "CANT_GET_MESSAGE": "لايمكن الوصول الى الرسالة", - "CANT_DELETE_MESSAGE": "لايمكن حذف الرسالة", - "CANT_COPY_MESSAGE": "Can't copy message", - "CANT_MOVE_MESSAGE": "لايمكن نقل الرسالة", - "CANT_SAVE_MESSAGE": "لايمكن حفظ الرسالة", - "CANT_SEND_MESSAGE": "لايمكن إرسال الرسالة", - "INVALID_RECIPIENTS": "يرجى التأكد من عناوين المرسل اليهم", - "CANT_SAVE_FILTERS": "لايمكن حفظ عامل التصفية", - "CANT_GET_FILTERS": "لايمكن الوصول الى عوامل التصفية", - "CANT_ACTIVATE_FILTERS_SCRIPT": "Can't activate filters script", - "CANT_DELETE_FILTERS_SCRIPT": "Can't delete filters script", - "FILTERS_ARE_NOT_CORRECT": "عوامل التصفية خطأ", - "CANT_CREATE_FOLDER": "لايمكن إنشاء مجلد", - "CANT_RENAME_FOLDER": "لايمكن إعادة تسمية المجلد", - "CANT_DELETE_FOLDER": "لايمكن حذف المجلد", - "CANT_DELETE_NON_EMPTY_FOLDER": "لايمكن حذف مجلد ممتلأ", - "CANT_SUBSCRIBE_FOLDER": "لايمكن الإشتراك بالمجلد", - "CANT_UNSUBSCRIBE_FOLDER": "لايمكن حذف الإشتراك من المجلد", - "DEMO_SEND_MESSAGE_ERROR": "لأسباب أمنية, هذا الحساب غير مسموح له إرسال رسائل", - "DEMO_ACCOUNT_ERROR": "لأسباب أمنية , هذا الحساب قد منع من هذا الإجراء", - "ACCOUNT_ALREADY_EXISTS": "الحساب موجود مسبقاَ", - "ACCOUNT_DOES_NOT_EXIST": "الحساب المطلوب غير موجود", - "ACCOUNT_SWITCH_FAILED": "Switch to account \"%EMAIL%\" failed", - "MAIL_SERVER_ERROR": "حدث خطأ أثناء محاولة الوصول الى المخدم البريدي", - "INVALID_INPUT_ARGUMENT": "Invalid input argument", - "UNKNOWN_ERROR": "خطأ غير معروف" + "RequestError": "Request failed", + "RequestAborted": "Request is aborted", + "RequestTimeout": "Request timed out", + "InvalidToken": "خاطىء token", + "AuthError": "خطأ في كلمة السر \\اسم المستخدم", + "ConnectionError": "لايمكن الإتصال بالمخدم", + "DomainAlreadyExists": "الموقع موجود مسبقاَ", + "DomainNotAllowed": "غير مسموح لهذا الموقع", + "AccountNotAllowed": "الحساب غير مسموح له", + "CryptKeyError": "Your login passphrase has changed", + "ContactsSyncError": "خطأ في مزامنة جهات الإتصال", + "CantGetMessageList": "لايمكن الوصول الى قائمة الرسائل", + "CantGetMessage": "لايمكن الوصول الى الرسالة", + "CantDeleteMessage": "لايمكن حذف الرسالة", + "CantCopyMessage": "Can't copy message", + "CantMoveMessage": "لايمكن نقل الرسالة", + "CantSaveMessage": "لايمكن حفظ الرسالة", + "CantSendMessage": "لايمكن إرسال الرسالة", + "InvalidRecipients": "يرجى التأكد من عناوين المرسل اليهم", + "CantSaveFilters": "لايمكن حفظ عامل التصفية", + "CantGetFilters": "لايمكن الوصول الى عوامل التصفية", + "CantActivateFiltersScript": "Can't activate filters script", + "CantDeleteFiltersScript": "Can't delete filters script", + "FiltersAreNotCorrect": "عوامل التصفية خطأ", + "CantCreateFolder": "لايمكن إنشاء مجلد", + "CantRenameFolder": "لايمكن إعادة تسمية المجلد", + "CantDeleteFolder": "لايمكن حذف المجلد", + "CantDeleteNonEmptyFolder": "لايمكن حذف مجلد ممتلأ", + "CantSubscribeFolder": "لايمكن الإشتراك بالمجلد", + "CantUnsubscribeFolder": "لايمكن حذف الإشتراك من المجلد", + "DemoSendMessageError": "لأسباب أمنية, هذا الحساب غير مسموح له إرسال رسائل", + "DemoAccountError": "لأسباب أمنية , هذا الحساب قد منع من هذا الإجراء", + "AccountAlreadyExists": "الحساب موجود مسبقاَ", + "AccountDoesNotExist": "الحساب المطلوب غير موجود", + "AccountSwitchFailed": "Switch to account \"%EMAIL%\" failed", + "MailServerError": "حدث خطأ أثناء محاولة الوصول الى المخدم البريدي", + "InvalidInputArgument": "Invalid input argument", + "UnknownError": "خطأ غير معروف" } } diff --git a/snappymail/v/0.0.0/app/localization/bg-BG/user.json b/snappymail/v/0.0.0/app/localization/bg-BG/user.json index 0bd9a57e2..0ac5b663d 100644 --- a/snappymail/v/0.0.0/app/localization/bg-BG/user.json +++ b/snappymail/v/0.0.0/app/localization/bg-BG/user.json @@ -139,6 +139,7 @@ "MENU_VIEW_ORIGINAL": "Покажи източника", "HTML_VIEW": "View HTML message", "PLAIN_VIEW": "View plain text message", + "SWAP_COLORS": "Swap default (background)color", "MENU_DOWNLOAD_ORIGINAL": "Изтегли като .eml файл", "MENU_FILTER_SIMILAR": "Филтрирай съобщения като това", "MENU_PRINT": "Принтирай", @@ -283,7 +284,8 @@ "ERROR": "%TYPE% грешка: %ERROR%", "CREATE_SELF_SIGNED": "Create self-signed", "VALID_UNTIL": "Valid until", - "PRIVATE_KEY": "Private key" + "PRIVATE_KEY": "Private key", + "ASK_CRYPTKEY_PASS": "Your login passphrase has changed. Please fill in your old login password to decrypt data" }, "OPENPGP": { "POPUP_IMPORT_TITLE": "Внасяне на OpenPGP ключ", @@ -509,7 +511,7 @@ "TO_MANY_FOLDERS_DESC_2": "Ще се показват само част от тях, за да се избегнат проблеми с производителността.", "HELP_DELETE_FOLDER": "Изтрий папка", "HELP_SHOW_HIDE_FOLDER": "Покажи\/скрий папка", - "HELP_CHECK_FOR_NEW_MESSAGES": "Провери\/не проверявай за нови съобщения", + "HELP_CHECK_FOR_NEW_MESSAGES": "Провери за нови съобщения", "HIDE_UNSUBSCRIBED": "Hide unsubscribed folders", "UNHIDE_KOLAB_FOLDERS": "Unhide Kolab folders", "TYPE_CALENDAR": "Calendar", @@ -575,42 +577,43 @@ "LABEL_CLOSE_COMPOSE": "Затвори създаването" }, "NOTIFICATIONS": { - "REQUEST_ERROR": "Request failed", - "REQUEST_ABORTED": "Request is aborted", - "REQUEST_TIMEOUT": "Request timed out", - "INVALID_TOKEN": "Невалиден токен", - "AUTH_ERROR": "Неуспешно удостоверяване", - "CONNECTION_ERROR": "Не може да се свърже със сървъра", - "DOMAIN_ALREADY_EXISTS": "Домейна вече съществува", - "DOMAIN_NOT_ALLOWED": "Този домейн не е разрешен", - "ACCOUNT_NOT_ALLOWED": "Този акаунт не е разрешен", - "CONTACTS_SYNC_ERROR": "Грешка при синхронизацията", - "CANT_GET_MESSAGE_LIST": "Не може да се изтегли списъка със съобщения", - "CANT_GET_MESSAGE": "Не може да се изтегли съобщението", - "CANT_DELETE_MESSAGE": "Не може да се изтрие съобщението", - "CANT_COPY_MESSAGE": "Can't copy message", - "CANT_MOVE_MESSAGE": "Не може да се премести съобщението", - "CANT_SAVE_MESSAGE": "Не може да се запамети съобщението", - "CANT_SEND_MESSAGE": "Не може да се изпрати съобщението", - "INVALID_RECIPIENTS": "Невалидни получатели", - "CANT_SAVE_FILTERS": "Не може да се запаметят филтрите", - "CANT_GET_FILTERS": "Не могат да се изтеглят филтрите", - "CANT_ACTIVATE_FILTERS_SCRIPT": "Can't activate filters script", - "CANT_DELETE_FILTERS_SCRIPT": "Can't delete filters script", - "FILTERS_ARE_NOT_CORRECT": "Грешни филтри", - "CANT_CREATE_FOLDER": "Не може да се създаде папка", - "CANT_RENAME_FOLDER": "Не може да се преименува папка", - "CANT_DELETE_FOLDER": "Не може да се изтрие папка", - "CANT_DELETE_NON_EMPTY_FOLDER": "Не може да се изтрие папка, която не е празна", - "CANT_SUBSCRIBE_FOLDER": "Не може да се абонира за папка", - "CANT_UNSUBSCRIBE_FOLDER": "Не може да се махне абонамента за папка", - "DEMO_SEND_MESSAGE_ERROR": "С цел сугурност, на този акаунт е забранено да изпраща съобщения до външни email адреси!", - "DEMO_ACCOUNT_ERROR": "С цел сигурност, това действие не е разрешено за този акаунт!", - "ACCOUNT_ALREADY_EXISTS": "Акаунта вече съществува", - "ACCOUNT_DOES_NOT_EXIST": "Акаунта не съществува", - "ACCOUNT_SWITCH_FAILED": "Switch to account \"%EMAIL%\" failed", - "MAIL_SERVER_ERROR": "Възникна грешка при комуникацията с пощенския сървър", - "INVALID_INPUT_ARGUMENT": "Невалидни входни аргументи", - "UNKNOWN_ERROR": "Неизвестна грешка" + "RequestError": "Request failed", + "RequestAborted": "Request is aborted", + "RequestTimeout": "Request timed out", + "InvalidToken": "Невалиден токен", + "AuthError": "Неуспешно удостоверяване", + "ConnectionError": "Не може да се свърже със сървъра", + "DomainAlreadyExists": "Домейна вече съществува", + "DomainNotAllowed": "Този домейн не е разрешен", + "AccountNotAllowed": "Този акаунт не е разрешен", + "CryptKeyError": "Your login passphrase has changed", + "ContactsSyncError": "Грешка при синхронизацията", + "CantGetMessageList": "Не може да се изтегли списъка със съобщения", + "CantGetMessage": "Не може да се изтегли съобщението", + "CantDeleteMessage": "Не може да се изтрие съобщението", + "CantCopyMessage": "Can't copy message", + "CantMoveMessage": "Не може да се премести съобщението", + "CantSaveMessage": "Не може да се запамети съобщението", + "CantSendMessage": "Не може да се изпрати съобщението", + "InvalidRecipients": "Невалидни получатели", + "CantSaveFilters": "Не може да се запаметят филтрите", + "CantGetFilters": "Не могат да се изтеглят филтрите", + "CantActivateFiltersScript": "Can't activate filters script", + "CantDeleteFiltersScript": "Can't delete filters script", + "FiltersAreNotCorrect": "Грешни филтри", + "CantCreateFolder": "Не може да се създаде папка", + "CantRenameFolder": "Не може да се преименува папка", + "CantDeleteFolder": "Не може да се изтрие папка", + "CantDeleteNonEmptyFolder": "Не може да се изтрие папка, която не е празна", + "CantSubscribeFolder": "Не може да се абонира за папка", + "CantUnsubscribeFolder": "Не може да се махне абонамента за папка", + "DemoSendMessageError": "С цел сугурност, на този акаунт е забранено да изпраща съобщения до външни email адреси!", + "DemoAccountError": "С цел сигурност, това действие не е разрешено за този акаунт!", + "AccountAlreadyExists": "Акаунта вече съществува", + "AccountDoesNotExist": "Акаунта не съществува", + "AccountSwitchFailed": "Switch to account \"%EMAIL%\" failed", + "MailServerError": "Възникна грешка при комуникацията с пощенския сървър", + "InvalidInputArgument": "Невалидни входни аргументи", + "UnknownError": "Неизвестна грешка" } } diff --git a/snappymail/v/0.0.0/app/localization/cs-CZ/admin.json b/snappymail/v/0.0.0/app/localization/cs-CZ/admin.json index 979e5ac7b..ea435917e 100644 --- a/snappymail/v/0.0.0/app/localization/cs-CZ/admin.json +++ b/snappymail/v/0.0.0/app/localization/cs-CZ/admin.json @@ -85,13 +85,14 @@ "TAB_SECURITY": { "LEGEND_SECURITY": "Zabezpečení", "LABEL_USE_IMAGE_PROXY": "Použít lokální proxy pro externí obrázky", - "LABEL_ALLOW_OPENPGP": "Povolit OpenPGP", + "LABEL_AUTO_VERIFY": "Verify signatures automatically", "LABEL_SHOW_PHP_INFO": "Zobrazit PHP informace (php_info)", "LEGEND_ADMIN_PANEL_ACCESS_CREDENTIALS": "Přístupové údaje administrace", "LABEL_CURRENT_PASSWORD": "Současné heslo", "LABEL_NEW_PASSWORD": "Nové heslo", "LABEL_REPEAT_PASSWORD": "Znovu", "LABEL_REQUIRE_VERIFICATION": "Vyžadovat ověření SSL certifikátu", + "CRYPTO": "Crypto", "LABEL_ALLOW_SELF_SIGNED": "Povolit self-signed certifikáty" }, "TAB_PACKAGES": { @@ -135,6 +136,7 @@ "SECURE_OPTION_NONE": "Žádné", "LABEL_ALLOW_SIEVE_SCRIPTS": "Povolit SIEVE skripty", "LABEL_USE_SHORT_LOGIN": "Používat krátký login", + "LABEL_LOWERCASE_LOGIN": "Lowercase login (case-insensitive)", "LABEL_USE_AUTH": "Používat autorizaci", "LABEL_SET_SENDER": "Use login as sender", "LABEL_AUTH_PLAIN_LINE": "Force single command AUTH PLAIN login", @@ -168,17 +170,17 @@ "UNKNOWN_ERROR": "Neznámá chyba" }, "NOTIFICATIONS": { - "REQUEST_ERROR": "Request failed", - "REQUEST_ABORTED": "Request is aborted", - "REQUEST_TIMEOUT": "Request timed out", - "INVALID_TOKEN": "Neplatný token", - "AUTH_ERROR": "Chyba autorizace", - "DOMAIN_ALREADY_EXISTS": "Tato doména již existuje", - "CANT_SAVE_PLUGIN_SETTINGS": "Nastavení se nepodařilo uložit", - "CANT_INSTALL_PACKAGE": "Instalace balíčku se nezdařila", - "CANT_DELETE_PACKAGE": "Odstranění balíčku se nezdařilo", - "INVALID_PLUGIN_PACKAGE": "Nevalidní balíček", - "UNSUPPORTED_PLUGIN_PACKAGE": "Nepodporovaný balíček rozšíření", - "UNKNOWN_ERROR": "Neznámá chyba" + "RequestError": "Request failed", + "RequestAborted": "Request is aborted", + "RequestTimeout": "Request timed out", + "InvalidToken": "Neplatný token", + "AuthError": "Chyba autorizace", + "DomainAlreadyExists": "Tato doména již existuje", + "CantSavePluginSettings": "Nastavení se nepodařilo uložit", + "CantInstallPackage": "Instalace balíčku se nezdařila", + "CantDeletePackage": "Odstranění balíčku se nezdařilo", + "InvalidPluginPackage": "Nevalidní balíček", + "UnsupportedPluginPackage": "Nepodporovaný balíček rozšíření", + "UnknownError": "Neznámá chyba" } } diff --git a/snappymail/v/0.0.0/app/localization/cs-CZ/user.json b/snappymail/v/0.0.0/app/localization/cs-CZ/user.json index 67b9c5efa..e662742a6 100644 --- a/snappymail/v/0.0.0/app/localization/cs-CZ/user.json +++ b/snappymail/v/0.0.0/app/localization/cs-CZ/user.json @@ -139,6 +139,7 @@ "MENU_VIEW_ORIGINAL": "Zobrazit zdroj", "HTML_VIEW": "View HTML message", "PLAIN_VIEW": "View plain text message", + "SWAP_COLORS": "Swap default (background)color", "MENU_DOWNLOAD_ORIGINAL": "Stáhnout jako soubor .eml", "MENU_FILTER_SIMILAR": "Filtrovat zprávy jako tato", "MENU_PRINT": "Tisknout", @@ -283,7 +284,8 @@ "ERROR": "%TYPE% chyba: %ERROR%", "CREATE_SELF_SIGNED": "Create self-signed", "VALID_UNTIL": "Valid until", - "PRIVATE_KEY": "Private key" + "PRIVATE_KEY": "Private key", + "ASK_CRYPTKEY_PASS": "Your login passphrase has changed. Please fill in your old login password to decrypt data" }, "OPENPGP": { "POPUP_IMPORT_TITLE": "Import OpenPGP klíče", @@ -509,7 +511,7 @@ "TO_MANY_FOLDERS_DESC_2": "Můžeme zobrazit jen část z nich, abychom zabránili problémům s výkonem.", "HELP_DELETE_FOLDER": "Smazat složku", "HELP_SHOW_HIDE_FOLDER": "Zobrazit\/skrýt složku", - "HELP_CHECK_FOR_NEW_MESSAGES": "Kontrovat\/nekontrolovat nové příchozí zprávy", + "HELP_CHECK_FOR_NEW_MESSAGES": "Kontrovat nové příchozí zprávy", "HIDE_UNSUBSCRIBED": "Hide unsubscribed folders", "UNHIDE_KOLAB_FOLDERS": "Unhide Kolab folders", "TYPE_CALENDAR": "Calendar", @@ -575,42 +577,43 @@ "LABEL_CLOSE_COMPOSE": "Zavřít okno se zprávou" }, "NOTIFICATIONS": { - "REQUEST_ERROR": "Request failed", - "REQUEST_ABORTED": "Request is aborted", - "REQUEST_TIMEOUT": "Request timed out", - "INVALID_TOKEN": "Neplatný token", - "AUTH_ERROR": "Ověření selhalo", - "CONNECTION_ERROR": "Nepodařilo se připojit k serveru", - "DOMAIN_ALREADY_EXISTS": "Tato doména již existuje", - "DOMAIN_NOT_ALLOWED": "Doména není povolená", - "ACCOUNT_NOT_ALLOWED": "Účet není povolený", - "CONTACTS_SYNC_ERROR": "Chyba při synchronizaci kontaktů", - "CANT_GET_MESSAGE_LIST": "Chyba při vytváření seznamu zpráv", - "CANT_GET_MESSAGE": "Zprávu se nepodařilo načíst", - "CANT_DELETE_MESSAGE": "Zprávu se nepodařilo odstranit", - "CANT_COPY_MESSAGE": "Can't copy message", - "CANT_MOVE_MESSAGE": "Zprávu se nepodařilo přesunout", - "CANT_SAVE_MESSAGE": "Zprávu se nepodařilo uložit", - "CANT_SEND_MESSAGE": "Zprávu se nepodařilo odeslat", - "INVALID_RECIPIENTS": "Neplatný příjemci", - "CANT_SAVE_FILTERS": "Nelze uložit filtry", - "CANT_GET_FILTERS": "Nelze načíst filtry", - "CANT_ACTIVATE_FILTERS_SCRIPT": "Can't activate filters script", - "CANT_DELETE_FILTERS_SCRIPT": "Can't delete filters script", - "FILTERS_ARE_NOT_CORRECT": "Filtry nejsou validní", - "CANT_CREATE_FOLDER": "Složku se nepodařilo vytvořit", - "CANT_RENAME_FOLDER": "Složku se nepodařilo přejmenovat", - "CANT_DELETE_FOLDER": "Složku se nepodařilo odstranit", - "CANT_DELETE_NON_EMPTY_FOLDER": "Nelze odstranit neprázdnou složku", - "CANT_SUBSCRIBE_FOLDER": "Nelze přihlásit adresář", - "CANT_UNSUBSCRIBE_FOLDER": "Nelze odhlásit adresář", - "DEMO_SEND_MESSAGE_ERROR": "Z důvodu bezpečnosti tento demo účet není oprávněn posílat zprávy na externí emaily!", - "DEMO_ACCOUNT_ERROR": "Z důvodu bezpečnosti tento účet nemá oprávnění toto provést!", - "ACCOUNT_ALREADY_EXISTS": "Účet už existuje", - "ACCOUNT_DOES_NOT_EXIST": "Účet neexistuje", - "ACCOUNT_SWITCH_FAILED": "Switch to account \"%EMAIL%\" failed", - "MAIL_SERVER_ERROR": "Nastala chyba během přístupu na poštovní server", - "INVALID_INPUT_ARGUMENT": "Nevalidní vstupní argument", - "UNKNOWN_ERROR": "Neznámá chyba" + "RequestError": "Request failed", + "RequestAborted": "Request is aborted", + "RequestTimeout": "Request timed out", + "InvalidToken": "Neplatný token", + "AuthError": "Ověření selhalo", + "ConnectionError": "Nepodařilo se připojit k serveru", + "DomainAlreadyExists": "Tato doména již existuje", + "DomainNotAllowed": "Doména není povolená", + "AccountNotAllowed": "Účet není povolený", + "CryptKeyError": "Your login passphrase has changed", + "ContactsSyncError": "Chyba při synchronizaci kontaktů", + "CantGetMessageList": "Chyba při vytváření seznamu zpráv", + "CantGetMessage": "Zprávu se nepodařilo načíst", + "CantDeleteMessage": "Zprávu se nepodařilo odstranit", + "CantCopyMessage": "Can't copy message", + "CantMoveMessage": "Zprávu se nepodařilo přesunout", + "CantSaveMessage": "Zprávu se nepodařilo uložit", + "CantSendMessage": "Zprávu se nepodařilo odeslat", + "InvalidRecipients": "Neplatný příjemci", + "CantSaveFilters": "Nelze uložit filtry", + "CantGetFilters": "Nelze načíst filtry", + "CantActivateFiltersScript": "Can't activate filters script", + "CantDeleteFiltersScript": "Can't delete filters script", + "FiltersAreNotCorrect": "Filtry nejsou validní", + "CantCreateFolder": "Složku se nepodařilo vytvořit", + "CantRenameFolder": "Složku se nepodařilo přejmenovat", + "CantDeleteFolder": "Složku se nepodařilo odstranit", + "CantDeleteNonEmptyFolder": "Nelze odstranit neprázdnou složku", + "CantSubscribeFolder": "Nelze přihlásit adresář", + "CantUnsubscribeFolder": "Nelze odhlásit adresář", + "DemoSendMessageError": "Z důvodu bezpečnosti tento demo účet není oprávněn posílat zprávy na externí emaily!", + "DemoAccountError": "Z důvodu bezpečnosti tento účet nemá oprávnění toto provést!", + "AccountAlreadyExists": "Účet už existuje", + "AccountDoesNotExist": "Účet neexistuje", + "AccountSwitchFailed": "Switch to account \"%EMAIL%\" failed", + "MailServerError": "Nastala chyba během přístupu na poštovní server", + "InvalidInputArgument": "Nevalidní vstupní argument", + "UnknownError": "Neznámá chyba" } } diff --git a/snappymail/v/0.0.0/app/localization/da-DK/admin.json b/snappymail/v/0.0.0/app/localization/da-DK/admin.json index efe48787c..7b2a266ef 100644 --- a/snappymail/v/0.0.0/app/localization/da-DK/admin.json +++ b/snappymail/v/0.0.0/app/localization/da-DK/admin.json @@ -85,13 +85,14 @@ "TAB_SECURITY": { "LEGEND_SECURITY": "Sikkerhed", "LABEL_USE_IMAGE_PROXY": "Brug lokal proxy til eksterne billeder", - "LABEL_ALLOW_OPENPGP": "Tillad OpenPGP", + "LABEL_AUTO_VERIFY": "Verify signatures automatically", "LABEL_SHOW_PHP_INFO": "Vis PHP informationer", "LEGEND_ADMIN_PANEL_ACCESS_CREDENTIALS": "Admin Panel Adgangsoplysninger", "LABEL_CURRENT_PASSWORD": "Nuværende adgangskode", "LABEL_NEW_PASSWORD": "Ny adgangskode", "LABEL_REPEAT_PASSWORD": "Gentag", "LABEL_REQUIRE_VERIFICATION": "Kræv verifikation af brugt SSL certifikat", + "CRYPTO": "Crypto", "LABEL_ALLOW_SELF_SIGNED": "Tillad selvsignerede certifikater" }, "TAB_PACKAGES": { @@ -135,6 +136,7 @@ "SECURE_OPTION_NONE": "Ingen", "LABEL_ALLOW_SIEVE_SCRIPTS": "Tillad sieve skripts", "LABEL_USE_SHORT_LOGIN": "Brug kort log ind", + "LABEL_LOWERCASE_LOGIN": "Lowercase login (case-insensitive)", "LABEL_USE_AUTH": "Brug godkendelse", "LABEL_SET_SENDER": "Use login as sender", "LABEL_AUTH_PLAIN_LINE": "Force single command AUTH PLAIN login", @@ -168,17 +170,17 @@ "UNKNOWN_ERROR": "Ukendt fejl" }, "NOTIFICATIONS": { - "REQUEST_ERROR": "Request failed", - "REQUEST_ABORTED": "Request is aborted", - "REQUEST_TIMEOUT": "Request timed out", - "INVALID_TOKEN": "Ugyldigt tegn", - "AUTH_ERROR": "Godkendelse fejlede", - "DOMAIN_ALREADY_EXISTS": "Domæne eksisterer allerede", - "CANT_SAVE_PLUGIN_SETTINGS": "Kan ikke gemme instillinger", - "CANT_INSTALL_PACKAGE": "Installation af pakke mislykkedes", - "CANT_DELETE_PACKAGE": "Fjernelse af pakke mislykkedes", - "INVALID_PLUGIN_PACKAGE": "Ugyldig tilføjelsespakke", - "UNSUPPORTED_PLUGIN_PACKAGE": "Tilføjelsespakke ikke understøttet", - "UNKNOWN_ERROR": "Ukendt fejl" + "RequestError": "Request failed", + "RequestAborted": "Request is aborted", + "RequestTimeout": "Request timed out", + "InvalidToken": "Ugyldigt tegn", + "AuthError": "Godkendelse fejlede", + "DomainAlreadyExists": "Domæne eksisterer allerede", + "CantSavePluginSettings": "Kan ikke gemme instillinger", + "CantInstallPackage": "Installation af pakke mislykkedes", + "CantDeletePackage": "Fjernelse af pakke mislykkedes", + "InvalidPluginPackage": "Ugyldig tilføjelsespakke", + "UnsupportedPluginPackage": "Tilføjelsespakke ikke understøttet", + "UnknownError": "Ukendt fejl" } } diff --git a/snappymail/v/0.0.0/app/localization/da-DK/user.json b/snappymail/v/0.0.0/app/localization/da-DK/user.json index a81a91d3b..2c1bd839e 100644 --- a/snappymail/v/0.0.0/app/localization/da-DK/user.json +++ b/snappymail/v/0.0.0/app/localization/da-DK/user.json @@ -139,6 +139,7 @@ "MENU_VIEW_ORIGINAL": "Vis kilder", "HTML_VIEW": "View HTML message", "PLAIN_VIEW": "View plain text message", + "SWAP_COLORS": "Swap default (background)color", "MENU_DOWNLOAD_ORIGINAL": "Hent som .eml fil", "MENU_FILTER_SIMILAR": "Find lignende meddelelser", "MENU_PRINT": "Udskriv", @@ -283,7 +284,8 @@ "ERROR": "%TYPE% fejl: %ERROR%", "CREATE_SELF_SIGNED": "Create self-signed", "VALID_UNTIL": "Valid until", - "PRIVATE_KEY": "Private key" + "PRIVATE_KEY": "Private key", + "ASK_CRYPTKEY_PASS": "Your login passphrase has changed. Please fill in your old login password to decrypt data" }, "OPENPGP": { "POPUP_IMPORT_TITLE": "Importer OpenPGP nøgle", @@ -509,7 +511,7 @@ "TO_MANY_FOLDERS_DESC_2": "Kun nogle mapper vises for at undgå problemer.", "HELP_DELETE_FOLDER": "Slet mappe", "HELP_SHOW_HIDE_FOLDER": "Vis\/skjul mappe", - "HELP_CHECK_FOR_NEW_MESSAGES": "Markér\/fjern markering for nye meddelelser", + "HELP_CHECK_FOR_NEW_MESSAGES": "Markér for nye meddelelser", "HIDE_UNSUBSCRIBED": "Hide unsubscribed folders", "UNHIDE_KOLAB_FOLDERS": "Unhide Kolab folders", "TYPE_CALENDAR": "Calendar", @@ -575,42 +577,43 @@ "LABEL_CLOSE_COMPOSE": "Luk forfatter mode" }, "NOTIFICATIONS": { - "REQUEST_ERROR": "Request failed", - "REQUEST_ABORTED": "Request is aborted", - "REQUEST_TIMEOUT": "Request timed out", - "INVALID_TOKEN": "Ugyldigt tegn", - "AUTH_ERROR": "Godkendelse fejlede", - "CONNECTION_ERROR": "Kan ikke kontakte server", - "DOMAIN_ALREADY_EXISTS": "Domæne eksisterer allerede", - "DOMAIN_NOT_ALLOWED": "Domæne ikke tilladt", - "ACCOUNT_NOT_ALLOWED": "Konto ikke tilladt", - "CONTACTS_SYNC_ERROR": "Fejl ved synkronisering af kontakter", - "CANT_GET_MESSAGE_LIST": "Kan ikke indlæse meddelelsesoversigt ", - "CANT_GET_MESSAGE": "Kan ikke indlæse meddelelse", - "CANT_DELETE_MESSAGE": "Kan ikke slette meddelelse", - "CANT_COPY_MESSAGE": "Can't copy message", - "CANT_MOVE_MESSAGE": "Kan ikke flytte meddelelse", - "CANT_SAVE_MESSAGE": "Kan ikke gemme meddelelse", - "CANT_SEND_MESSAGE": "Kan ikke sende meddelelse", - "INVALID_RECIPIENTS": "Ugyldige modtagere", - "CANT_SAVE_FILTERS": "Kan ikke gemme filtre", - "CANT_GET_FILTERS": "Kan ikke indlæse filtre", - "CANT_ACTIVATE_FILTERS_SCRIPT": "Can't activate filters script", - "CANT_DELETE_FILTERS_SCRIPT": "Can't delete filters script", - "FILTERS_ARE_NOT_CORRECT": "Fejl i filtre", - "CANT_CREATE_FOLDER": "Kan ikke oprette mappe", - "CANT_RENAME_FOLDER": "Kan ikke omdøbe mappe", - "CANT_DELETE_FOLDER": "Kan ikke slette mappe", - "CANT_DELETE_NON_EMPTY_FOLDER": "Kan ikke slette mappe som ikke er tom", - "CANT_SUBSCRIBE_FOLDER": "Kan ikke abonnere på mappe", - "CANT_UNSUBSCRIBE_FOLDER": "Kan ikke afmelde mappe", - "DEMO_SEND_MESSAGE_ERROR": "Denne konto kan af sikkerhedshensyn ikke sende e-mails til eksterne adresser!", - "DEMO_ACCOUNT_ERROR": "Denne konto kan af sikkerhedshensyn ikke udføre denne handling!", - "ACCOUNT_ALREADY_EXISTS": "Kontoen eksisterer allerede", - "ACCOUNT_DOES_NOT_EXIST": "Kontoen eksisterer ikke", - "ACCOUNT_SWITCH_FAILED": "Switch to account \"%EMAIL%\" failed", - "MAIL_SERVER_ERROR": "Der skete en fejl under adgang til e-mail serveren", - "INVALID_INPUT_ARGUMENT": "Ugyldigt argument", - "UNKNOWN_ERROR": "Ukendt fejl" + "RequestError": "Request failed", + "RequestAborted": "Request is aborted", + "RequestTimeout": "Request timed out", + "InvalidToken": "Ugyldigt tegn", + "AuthError": "Godkendelse fejlede", + "ConnectionError": "Kan ikke kontakte server", + "DomainAlreadyExists": "Domæne eksisterer allerede", + "DomainNotAllowed": "Domæne ikke tilladt", + "AccountNotAllowed": "Konto ikke tilladt", + "CryptKeyError": "Your login passphrase has changed", + "ContactsSyncError": "Fejl ved synkronisering af kontakter", + "CantGetMessageList": "Kan ikke indlæse meddelelsesoversigt ", + "CantGetMessage": "Kan ikke indlæse meddelelse", + "CantDeleteMessage": "Kan ikke slette meddelelse", + "CantCopyMessage": "Can't copy message", + "CantMoveMessage": "Kan ikke flytte meddelelse", + "CantSaveMessage": "Kan ikke gemme meddelelse", + "CantSendMessage": "Kan ikke sende meddelelse", + "InvalidRecipients": "Ugyldige modtagere", + "CantSaveFilters": "Kan ikke gemme filtre", + "CantGetFilters": "Kan ikke indlæse filtre", + "CantActivateFiltersScript": "Can't activate filters script", + "CantDeleteFiltersScript": "Can't delete filters script", + "FiltersAreNotCorrect": "Fejl i filtre", + "CantCreateFolder": "Kan ikke oprette mappe", + "CantRenameFolder": "Kan ikke omdøbe mappe", + "CantDeleteFolder": "Kan ikke slette mappe", + "CantDeleteNonEmptyFolder": "Kan ikke slette mappe som ikke er tom", + "CantSubscribeFolder": "Kan ikke abonnere på mappe", + "CantUnsubscribeFolder": "Kan ikke afmelde mappe", + "DemoSendMessageError": "Denne konto kan af sikkerhedshensyn ikke sende e-mails til eksterne adresser!", + "DemoAccountError": "Denne konto kan af sikkerhedshensyn ikke udføre denne handling!", + "AccountAlreadyExists": "Kontoen eksisterer allerede", + "AccountDoesNotExist": "Kontoen eksisterer ikke", + "AccountSwitchFailed": "Switch to account \"%EMAIL%\" failed", + "MailServerError": "Der skete en fejl under adgang til e-mail serveren", + "InvalidInputArgument": "Ugyldigt argument", + "UnknownError": "Ukendt fejl" } } diff --git a/snappymail/v/0.0.0/app/localization/de-DE/admin.json b/snappymail/v/0.0.0/app/localization/de-DE/admin.json index f78a4934c..ca7f2eaaf 100644 --- a/snappymail/v/0.0.0/app/localization/de-DE/admin.json +++ b/snappymail/v/0.0.0/app/localization/de-DE/admin.json @@ -85,13 +85,14 @@ "TAB_SECURITY": { "LEGEND_SECURITY": "Sicherheit", "LABEL_USE_IMAGE_PROXY": "Lokalen Proxy für externe Bilder verwenden", - "LABEL_ALLOW_OPENPGP": "OpenPGP erlauben", + "LABEL_AUTO_VERIFY": "Verify signatures automatically", "LABEL_SHOW_PHP_INFO": "PHP-Informationen anzeigen", "LEGEND_ADMIN_PANEL_ACCESS_CREDENTIALS": "Adminpanel-Zugangsdaten", "LABEL_CURRENT_PASSWORD": "Aktuelles Passwort", "LABEL_NEW_PASSWORD": "Neues Passwort", "LABEL_REPEAT_PASSWORD": "Wiederholen", "LABEL_REQUIRE_VERIFICATION": "Verlangen, dass das verwendete SSL-Zertifikat signiert ist", + "CRYPTO": "Krypto", "LABEL_ALLOW_SELF_SIGNED": "Selbst-signierte Zertifikate erlauben" }, "TAB_PACKAGES": { @@ -135,6 +136,7 @@ "SECURE_OPTION_NONE": "Ohne", "LABEL_ALLOW_SIEVE_SCRIPTS": "Sieve-Skripte erlauben", "LABEL_USE_SHORT_LOGIN": "Kurze Benutzernamen verwenden", + "LABEL_LOWERCASE_LOGIN": "Lowercase login (case-insensitive)", "LABEL_USE_AUTH": "Authentifizierung verwenden", "LABEL_SET_SENDER": "Verwenden Sie die Anmeldung als Absender", "LABEL_AUTH_PLAIN_LINE": "Erzwinge einzelnen AUTH PLAIN Befehl bei der Anmeldung", @@ -168,17 +170,17 @@ "UNKNOWN_ERROR": "Unbekannter Fehler" }, "NOTIFICATIONS": { - "REQUEST_ERROR": "Anfrage fehlgeschlagen", - "REQUEST_ABORTED": "Anfrage abgebrochen", - "REQUEST_TIMEOUT": "Antrag auf Auslieferung", - "INVALID_TOKEN": "Ungültiger Token", - "AUTH_ERROR": "Authentifizierung fehlgeschlagen", - "DOMAIN_ALREADY_EXISTS": "Diese Domain existiert bereits", - "CANT_SAVE_PLUGIN_SETTINGS": "Einstellungen können nicht gespeichert werden", - "CANT_INSTALL_PACKAGE": "Fehler beim Installieren der Erweiterung", - "CANT_DELETE_PACKAGE": "Fehler beim entfernen der Erweiterung", - "INVALID_PLUGIN_PACKAGE": "Erweiterungspaket ist ungültig", - "UNSUPPORTED_PLUGIN_PACKAGE": "Erweiterungspaket wird nicht unterstützt", - "UNKNOWN_ERROR": "Unbekannter Fehler" + "RequestError": "Anfrage fehlgeschlagen", + "RequestAborted": "Anfrage abgebrochen", + "RequestTimeout": "Antrag auf Auslieferung", + "InvalidToken": "Ungültiger Token", + "AuthError": "Authentifizierung fehlgeschlagen", + "DomainAlreadyExists": "Diese Domain existiert bereits", + "CantSavePluginSettings": "Einstellungen können nicht gespeichert werden", + "CantInstallPackage": "Fehler beim Installieren der Erweiterung", + "CantDeletePackage": "Fehler beim entfernen der Erweiterung", + "InvalidPluginPackage": "Erweiterungspaket ist ungültig", + "UnsupportedPluginPackage": "Erweiterungspaket wird nicht unterstützt", + "UnknownError": "Unbekannter Fehler" } } diff --git a/snappymail/v/0.0.0/app/localization/de-DE/user.json b/snappymail/v/0.0.0/app/localization/de-DE/user.json index 5e07f0630..9dbb8ff94 100644 --- a/snappymail/v/0.0.0/app/localization/de-DE/user.json +++ b/snappymail/v/0.0.0/app/localization/de-DE/user.json @@ -139,6 +139,7 @@ "MENU_VIEW_ORIGINAL": "Original anzeigen", "HTML_VIEW": "Zeige Nachricht als HTML", "PLAIN_VIEW": "Zeige Nachricht im Klartext", + "SWAP_COLORS": "Swap default (background)color", "MENU_DOWNLOAD_ORIGINAL": "Als .eml-Datei herunterladen", "MENU_FILTER_SIMILAR": "Ähnliche Nachrichten", "MENU_PRINT": "Drucken", @@ -283,7 +284,8 @@ "ERROR": "%TYPE%-Fehler: %ERROR%", "CREATE_SELF_SIGNED": "Create self-signed", "VALID_UNTIL": "Valid until", - "PRIVATE_KEY": "Private key" + "PRIVATE_KEY": "Private key", + "ASK_CRYPTKEY_PASS": "Your login passphrase has changed. Please fill in your old login password to decrypt data" }, "OPENPGP": { "POPUP_IMPORT_TITLE": "OpenPGP-Schlüssel importieren", @@ -575,42 +577,43 @@ "LABEL_CLOSE_COMPOSE": "Popup schließen" }, "NOTIFICATIONS": { - "REQUEST_ERROR": "Anfrage fehlgeschlagen", - "REQUEST_ABORTED": "Anfrage abgebrochen", - "REQUEST_TIMEOUT": "Antrag auf Auslieferung", - "INVALID_TOKEN": "Ungültiger Token", - "AUTH_ERROR": "Authentifizierung fehlgeschlagen", - "CONNECTION_ERROR": "Serververbindung kann nicht hergestellt werden", - "DOMAIN_ALREADY_EXISTS": "Diese Domain existiert bereits", - "DOMAIN_NOT_ALLOWED": "Diese Domain ist nicht zugelassen.", - "ACCOUNT_NOT_ALLOWED": "Konto ist nicht zugelassen.", - "CONTACTS_SYNC_ERROR": "Fehler bei Kontakte-Synchronisierung", - "CANT_GET_MESSAGE_LIST": "Die Nachrichtenliste ist nicht verfügbar", - "CANT_GET_MESSAGE": "Diese Nachricht ist nicht verfügbar", - "CANT_DELETE_MESSAGE": "Diese Nachricht kann nicht gelöscht werden", - "CANT_COPY_MESSAGE": "Diese Nachricht kann nicht kopiert werden", - "CANT_MOVE_MESSAGE": "Diese Nachricht kann nicht verschoben werden", - "CANT_SAVE_MESSAGE": "Diese Nachricht kann nicht gespeichert werden", - "CANT_SEND_MESSAGE": "Diese Nachricht kann nicht gesendet werden", - "INVALID_RECIPIENTS": "Ungültige Empfängeradressen", - "CANT_SAVE_FILTERS": "Die Filter können nicht gespeichert werden", - "CANT_GET_FILTERS": "Die Filter sind nicht verfügbar", - "CANT_ACTIVATE_FILTERS_SCRIPT": "Filters-Skript kann nicht aktiviert werden", - "CANT_DELETE_FILTERS_SCRIPT": "Filters-Skript kann nicht gelöscht werden", - "FILTERS_ARE_NOT_CORRECT": "Die Filter sind fehlerhaft", - "CANT_CREATE_FOLDER": "Dieser Ordner kann nicht angelegt werden", - "CANT_RENAME_FOLDER": "Dieser Ordner kann nicht umbenannt werden", - "CANT_DELETE_FOLDER": "Dieser Ordner kann nicht gelöscht werden", - "CANT_DELETE_NON_EMPTY_FOLDER": "Der Ordner kann nicht gelöscht werden; er ist nicht leer", - "CANT_SUBSCRIBE_FOLDER": "Dieser Ordner kann nicht abonniert werden", - "CANT_UNSUBSCRIBE_FOLDER": "Das Abonnement dieses Ordners kann nicht gelöscht werden", - "DEMO_SEND_MESSAGE_ERROR": "Dieses Demokonto darf aus Sicherheitsgründen keine Nachrichten an externe E-Mail-Adressen versenden!", - "DEMO_ACCOUNT_ERROR": "Aus Sicherheitsgründen ist dieses Konto nicht zu dieser Aktion berechtigt!", - "ACCOUNT_ALREADY_EXISTS": "Dieses Konto existiert bereits", - "ACCOUNT_DOES_NOT_EXIST": "Dieser Account existiert nicht.", - "ACCOUNT_SWITCH_FAILED": "Konto wechseln \"%EMAIL%\" versagt", - "MAIL_SERVER_ERROR": "Beim Zugriff auf den E-Mail-Server trat ein Fehler auf.", - "INVALID_INPUT_ARGUMENT": "Ungültige Eingabe", - "UNKNOWN_ERROR": "Unbekannter Fehler" + "RequestError": "Anfrage fehlgeschlagen", + "RequestAborted": "Anfrage abgebrochen", + "RequestTimeout": "Antrag auf Auslieferung", + "InvalidToken": "Ungültiger Token", + "AuthError": "Authentifizierung fehlgeschlagen", + "ConnectionError": "Serververbindung kann nicht hergestellt werden", + "DomainAlreadyExists": "Diese Domain existiert bereits", + "DomainNotAllowed": "Diese Domain ist nicht zugelassen.", + "AccountNotAllowed": "Konto ist nicht zugelassen.", + "CryptKeyError": "Your login passphrase has changed", + "ContactsSyncError": "Fehler bei Kontakte-Synchronisierung", + "CantGetMessageList": "Die Nachrichtenliste ist nicht verfügbar", + "CantGetMessage": "Diese Nachricht ist nicht verfügbar", + "CantDeleteMessage": "Diese Nachricht kann nicht gelöscht werden", + "CantCopyMessage": "Diese Nachricht kann nicht kopiert werden", + "CantMoveMessage": "Diese Nachricht kann nicht verschoben werden", + "CantSaveMessage": "Diese Nachricht kann nicht gespeichert werden", + "CantSendMessage": "Diese Nachricht kann nicht gesendet werden", + "InvalidRecipients": "Ungültige Empfängeradressen", + "CantSaveFilters": "Die Filter können nicht gespeichert werden", + "CantGetFilters": "Die Filter sind nicht verfügbar", + "CantActivateFiltersScript": "Filters-Skript kann nicht aktiviert werden", + "CantDeleteFiltersScript": "Filters-Skript kann nicht gelöscht werden", + "FiltersAreNotCorrect": "Die Filter sind fehlerhaft", + "CantCreateFolder": "Dieser Ordner kann nicht angelegt werden", + "CantRenameFolder": "Dieser Ordner kann nicht umbenannt werden", + "CantDeleteFolder": "Dieser Ordner kann nicht gelöscht werden", + "CantDeleteNonEmptyFolder": "Der Ordner kann nicht gelöscht werden; er ist nicht leer", + "CantSubscribeFolder": "Dieser Ordner kann nicht abonniert werden", + "CantUnsubscribeFolder": "Das Abonnement dieses Ordners kann nicht gelöscht werden", + "DemoSendMessageError": "Dieses Demokonto darf aus Sicherheitsgründen keine Nachrichten an externe E-Mail-Adressen versenden!", + "DemoAccountError": "Aus Sicherheitsgründen ist dieses Konto nicht zu dieser Aktion berechtigt!", + "AccountAlreadyExists": "Dieses Konto existiert bereits", + "AccountDoesNotExist": "Dieser Account existiert nicht.", + "AccountSwitchFailed": "Konto wechseln \"%EMAIL%\" versagt", + "MailServerError": "Beim Zugriff auf den E-Mail-Server trat ein Fehler auf.", + "InvalidInputArgument": "Ungültige Eingabe", + "UnknownError": "Unbekannter Fehler" } } diff --git a/snappymail/v/0.0.0/app/localization/el-GR/user.json b/snappymail/v/0.0.0/app/localization/el-GR/user.json index 8e71fbdda..0a457abf7 100644 --- a/snappymail/v/0.0.0/app/localization/el-GR/user.json +++ b/snappymail/v/0.0.0/app/localization/el-GR/user.json @@ -139,6 +139,7 @@ "MENU_VIEW_ORIGINAL": "Εμφάνιση πηγαίου μηνύματος", "HTML_VIEW": "View HTML message", "PLAIN_VIEW": "View plain text message", + "SWAP_COLORS": "Swap default (background)color", "MENU_DOWNLOAD_ORIGINAL": "Μεταφόρτωση σαν αρχείο .eml", "MENU_FILTER_SIMILAR": "Φιλτράρισμα μηνυμάτων όπως αυτό", "MENU_PRINT": "Εκτύπωση", @@ -283,7 +284,8 @@ "ERROR": "%TYPE% error: %ERROR%", "CREATE_SELF_SIGNED": "Create self-signed", "VALID_UNTIL": "Valid until", - "PRIVATE_KEY": "Private key" + "PRIVATE_KEY": "Private key", + "ASK_CRYPTKEY_PASS": "Your login passphrase has changed. Please fill in your old login password to decrypt data" }, "OPENPGP": { "POPUP_IMPORT_TITLE": "Εισαγωγή κλειδιού OpenPGP", @@ -509,7 +511,7 @@ "TO_MANY_FOLDERS_DESC_2": "We have shown only a part of them, to avoid performance problems.", "HELP_DELETE_FOLDER": "Delete folder", "HELP_SHOW_HIDE_FOLDER": "Show\/hide folder", - "HELP_CHECK_FOR_NEW_MESSAGES": "Check\/don't check for new messages", + "HELP_CHECK_FOR_NEW_MESSAGES": "Check for new messages", "HIDE_UNSUBSCRIBED": "Hide unsubscribed folders", "UNHIDE_KOLAB_FOLDERS": "Unhide Kolab folders", "TYPE_CALENDAR": "Calendar", @@ -575,42 +577,43 @@ "LABEL_CLOSE_COMPOSE": "Close compose" }, "NOTIFICATIONS": { - "REQUEST_ERROR": "Request failed", - "REQUEST_ABORTED": "Request is aborted", - "REQUEST_TIMEOUT": "Request timed out", - "INVALID_TOKEN": "Invalid token", - "AUTH_ERROR": "Authentication failed", - "CONNECTION_ERROR": "Can't connect to server", - "DOMAIN_ALREADY_EXISTS": "Domain already exists", - "DOMAIN_NOT_ALLOWED": "Domain is not allowed", - "ACCOUNT_NOT_ALLOWED": "Account is not allowed", - "CONTACTS_SYNC_ERROR": "Contacts synchronization error", - "CANT_GET_MESSAGE_LIST": "Can't get message list", - "CANT_GET_MESSAGE": "Can't get message", - "CANT_DELETE_MESSAGE": "Can't delete message", - "CANT_COPY_MESSAGE": "Can't copy message", - "CANT_MOVE_MESSAGE": "Can't move message", - "CANT_SAVE_MESSAGE": "Can't save message", - "CANT_SEND_MESSAGE": "Can't send message", - "INVALID_RECIPIENTS": "Invalid recipients", - "CANT_SAVE_FILTERS": "Can't save filters", - "CANT_GET_FILTERS": "Can't get filters", - "CANT_ACTIVATE_FILTERS_SCRIPT": "Can't activate filters script", - "CANT_DELETE_FILTERS_SCRIPT": "Can't delete filters script", - "FILTERS_ARE_NOT_CORRECT": "Filters are not correct", - "CANT_CREATE_FOLDER": "Can't create folder", - "CANT_RENAME_FOLDER": "Can't rename folder", - "CANT_DELETE_FOLDER": "Can't delete folder", - "CANT_DELETE_NON_EMPTY_FOLDER": "Can't delete non-empty directory", - "CANT_SUBSCRIBE_FOLDER": "Can't subscribe folder", - "CANT_UNSUBSCRIBE_FOLDER": "Can't unsubscribe folder", - "DEMO_SEND_MESSAGE_ERROR": "For security purposes, this account is not allowed to send messages to external e-mail addresses!", - "DEMO_ACCOUNT_ERROR": "For security purposes, this account is not allowed for this action!", - "ACCOUNT_ALREADY_EXISTS": "Account already exists", - "ACCOUNT_DOES_NOT_EXIST": "Account doesn't exist", - "ACCOUNT_SWITCH_FAILED": "Switch to account \"%EMAIL%\" failed", - "MAIL_SERVER_ERROR": "An error has occured while accessing mail server", - "INVALID_INPUT_ARGUMENT": "Invalid input argument", - "UNKNOWN_ERROR": "Unknown error" + "RequestError": "Request failed", + "RequestAborted": "Request is aborted", + "RequestTimeout": "Request timed out", + "InvalidToken": "Invalid token", + "AuthError": "Authentication failed", + "ConnectionError": "Can't connect to server", + "DomainAlreadyExists": "Domain already exists", + "DomainNotAllowed": "Domain is not allowed", + "AccountNotAllowed": "Account is not allowed", + "CryptKeyError": "Your login passphrase has changed", + "ContactsSyncError": "Contacts synchronization error", + "CantGetMessageList": "Can't get message list", + "CantGetMessage": "Can't get message", + "CantDeleteMessage": "Can't delete message", + "CantCopyMessage": "Can't copy message", + "CantMoveMessage": "Can't move message", + "CantSaveMessage": "Can't save message", + "CantSendMessage": "Can't send message", + "InvalidRecipients": "Invalid recipients", + "CantSaveFilters": "Can't save filters", + "CantGetFilters": "Can't get filters", + "CantActivateFiltersScript": "Can't activate filters script", + "CantDeleteFiltersScript": "Can't delete filters script", + "FiltersAreNotCorrect": "Filters are not correct", + "CantCreateFolder": "Can't create folder", + "CantRenameFolder": "Can't rename folder", + "CantDeleteFolder": "Can't delete folder", + "CantDeleteNonEmptyFolder": "Can't delete non-empty directory", + "CantSubscribeFolder": "Can't subscribe folder", + "CantUnsubscribeFolder": "Can't unsubscribe folder", + "DemoSendMessageError": "For security purposes, this account is not allowed to send messages to external e-mail addresses!", + "DemoAccountError": "For security purposes, this account is not allowed for this action!", + "AccountAlreadyExists": "Account already exists", + "AccountDoesNotExist": "Account doesn't exist", + "AccountSwitchFailed": "Switch to account \"%EMAIL%\" failed", + "MailServerError": "An error has occured while accessing mail server", + "InvalidInputArgument": "Invalid input argument", + "UnknownError": "Unknown error" } } diff --git a/snappymail/v/0.0.0/app/localization/en-GB/user.json b/snappymail/v/0.0.0/app/localization/en-GB/user.json index 599754e0c..939b70787 100644 --- a/snappymail/v/0.0.0/app/localization/en-GB/user.json +++ b/snappymail/v/0.0.0/app/localization/en-GB/user.json @@ -139,6 +139,7 @@ "MENU_VIEW_ORIGINAL": "Show Source", "HTML_VIEW": "View HTML message", "PLAIN_VIEW": "View plain text message", + "SWAP_COLORS": "Swap default (background)color", "MENU_DOWNLOAD_ORIGINAL": "Download as .eml file", "MENU_FILTER_SIMILAR": "Filter messages like this", "MENU_PRINT": "Print", @@ -283,7 +284,8 @@ "ERROR": "%TYPE% error: %ERROR%", "CREATE_SELF_SIGNED": "Create self-signed", "VALID_UNTIL": "Valid until", - "PRIVATE_KEY": "Private key" + "PRIVATE_KEY": "Private key", + "ASK_CRYPTKEY_PASS": "Your login passphrase has changed. Please fill in your old login password to decrypt data" }, "OPENPGP": { "POPUP_IMPORT_TITLE": "Import OpenPGP key", @@ -509,7 +511,7 @@ "TO_MANY_FOLDERS_DESC_2": "We have shown only a part of them, to avoid performance problems.", "HELP_DELETE_FOLDER": "Delete folder", "HELP_SHOW_HIDE_FOLDER": "Show\/hide folder", - "HELP_CHECK_FOR_NEW_MESSAGES": "Check\/don't check for new messages", + "HELP_CHECK_FOR_NEW_MESSAGES": "Check for new messages", "HIDE_UNSUBSCRIBED": "Hide unsubscribed folders", "UNHIDE_KOLAB_FOLDERS": "Unhide Kolab folders", "TYPE_CALENDAR": "Calendar", @@ -575,42 +577,43 @@ "LABEL_CLOSE_COMPOSE": "Close compose" }, "NOTIFICATIONS": { - "REQUEST_ERROR": "Request failed", - "REQUEST_ABORTED": "Request is aborted", - "REQUEST_TIMEOUT": "Request timed out", - "INVALID_TOKEN": "Invalid token", - "AUTH_ERROR": "Authentication failed", - "CONNECTION_ERROR": "Can't connect to server", - "DOMAIN_ALREADY_EXISTS": "Domain already exists", - "DOMAIN_NOT_ALLOWED": "Domain is not allowed", - "ACCOUNT_NOT_ALLOWED": "Account is not allowed", - "CONTACTS_SYNC_ERROR": "Contacts synchronization error", - "CANT_GET_MESSAGE_LIST": "Can't get message list", - "CANT_GET_MESSAGE": "Can't get message", - "CANT_DELETE_MESSAGE": "Can't delete message", - "CANT_COPY_MESSAGE": "Can't copy message", - "CANT_MOVE_MESSAGE": "Can't move message", - "CANT_SAVE_MESSAGE": "Can't save message", - "CANT_SEND_MESSAGE": "Can't send message", - "INVALID_RECIPIENTS": "Invalid recipients", - "CANT_SAVE_FILTERS": "Can't save filters", - "CANT_GET_FILTERS": "Can't get filters", - "CANT_ACTIVATE_FILTERS_SCRIPT": "Can't activate filters script", - "CANT_DELETE_FILTERS_SCRIPT": "Can't delete filters script", - "FILTERS_ARE_NOT_CORRECT": "Filters are not correct", - "CANT_CREATE_FOLDER": "Can't create folder", - "CANT_RENAME_FOLDER": "Can't rename folder", - "CANT_DELETE_FOLDER": "Can't delete folder", - "CANT_DELETE_NON_EMPTY_FOLDER": "Can't delete non-empty directory", - "CANT_SUBSCRIBE_FOLDER": "Can't subscribe folder", - "CANT_UNSUBSCRIBE_FOLDER": "Can't unsubscribe folder", - "DEMO_SEND_MESSAGE_ERROR": "For security purposes, this account is not allowed to send messages to external e-mail addresses!", - "DEMO_ACCOUNT_ERROR": "For security purposes, this account is not allowed for this action!", - "ACCOUNT_ALREADY_EXISTS": "Account already exists", - "ACCOUNT_DOES_NOT_EXIST": "Account doesn't exist", - "ACCOUNT_SWITCH_FAILED": "Switch to account \"%EMAIL%\" failed", - "MAIL_SERVER_ERROR": "An error has occured while accessing mail server", - "INVALID_INPUT_ARGUMENT": "Invalid input argument", - "UNKNOWN_ERROR": "Unknown error" + "RequestError": "Request failed", + "RequestAborted": "Request is aborted", + "RequestTimeout": "Request timed out", + "InvalidToken": "Invalid token", + "AuthError": "Authentication failed", + "ConnectionError": "Can't connect to server", + "DomainAlreadyExists": "Domain already exists", + "DomainNotAllowed": "Domain is not allowed", + "AccountNotAllowed": "Account is not allowed", + "CryptKeyError": "Your login passphrase has changed", + "ContactsSyncError": "Contacts synchronization error", + "CantGetMessageList": "Can't get message list", + "CantGetMessage": "Can't get message", + "CantDeleteMessage": "Can't delete message", + "CantCopyMessage": "Can't copy message", + "CantMoveMessage": "Can't move message", + "CantSaveMessage": "Can't save message", + "CantSendMessage": "Can't send message", + "InvalidRecipients": "Invalid recipients", + "CantSaveFilters": "Can't save filters", + "CantGetFilters": "Can't get filters", + "CantActivateFiltersScript": "Can't activate filters script", + "CantDeleteFiltersScript": "Can't delete filters script", + "FiltersAreNotCorrect": "Filters are not correct", + "CantCreateFolder": "Can't create folder", + "CantRenameFolder": "Can't rename folder", + "CantDeleteFolder": "Can't delete folder", + "CantDeleteNonEmptyFolder": "Can't delete non-empty directory", + "CantSubscribeFolder": "Can't subscribe folder", + "CantUnsubscribeFolder": "Can't unsubscribe folder", + "DemoSendMessageError": "For security purposes, this account is not allowed to send messages to external e-mail addresses!", + "DemoAccountError": "For security purposes, this account is not allowed for this action!", + "AccountAlreadyExists": "Account already exists", + "AccountDoesNotExist": "Account doesn't exist", + "AccountSwitchFailed": "Switch to account \"%EMAIL%\" failed", + "MailServerError": "An error has occured while accessing mail server", + "InvalidInputArgument": "Invalid input argument", + "UnknownError": "Unknown error" } } diff --git a/snappymail/v/0.0.0/app/localization/en/admin.json b/snappymail/v/0.0.0/app/localization/en/admin.json index ae7797e19..9bcdff2ca 100644 --- a/snappymail/v/0.0.0/app/localization/en/admin.json +++ b/snappymail/v/0.0.0/app/localization/en/admin.json @@ -85,13 +85,14 @@ "TAB_SECURITY": { "LEGEND_SECURITY": "Security", "LABEL_USE_IMAGE_PROXY": "Use local proxy for external images", - "LABEL_ALLOW_OPENPGP": "Allow OpenPGP", + "LABEL_AUTO_VERIFY": "Verify signatures automatically", "LABEL_SHOW_PHP_INFO": "Show PHP information", "LEGEND_ADMIN_PANEL_ACCESS_CREDENTIALS": "Admin Panel Access Credentials", "LABEL_CURRENT_PASSWORD": "Current password", "LABEL_NEW_PASSWORD": "New password", "LABEL_REPEAT_PASSWORD": "Repeat", "LABEL_REQUIRE_VERIFICATION": "Require verification of SSL certificate", + "CRYPTO": "Crypto", "LABEL_ALLOW_SELF_SIGNED": "Allow self signed certificates" }, "TAB_PACKAGES": { @@ -107,7 +108,7 @@ "LABEL_ALL_RIGHTS_RESERVED": "All Rights Reserved.", "HINT_READ_CHANGE_LOG": "Please read the change log before updating.", "HINT_IS_UP_TO_DATE": "SnappyMail is up to date.", - "HTML_NEW_VERSION": "New %VERSION% version is available.", + "HTML_NEW_VERSION": "New %VERSION%<\/b> version is available.", "LABEL_UPDATING": "Updating", "LABEL_CHECKING": "Checking for updates", "BUTTON_RELEASES": "Releases" @@ -135,6 +136,7 @@ "SECURE_OPTION_NONE": "None", "LABEL_ALLOW_SIEVE_SCRIPTS": "Allow sieve scripts", "LABEL_USE_SHORT_LOGIN": "Use short login", + "LABEL_LOWERCASE_LOGIN": "Lowercase login (case-insensitive)", "LABEL_USE_AUTH": "Use authentication", "LABEL_SET_SENDER": "Use login as sender", "LABEL_AUTH_PLAIN_LINE": "Force single command AUTH PLAIN login", @@ -168,17 +170,17 @@ "UNKNOWN_ERROR": "Unknown error" }, "NOTIFICATIONS": { - "REQUEST_ERROR": "Request failed", - "REQUEST_ABORTED": "Request is aborted", - "REQUEST_TIMEOUT": "Request timed out", - "INVALID_TOKEN": "Invalid token", - "AUTH_ERROR": "Authentication failed", - "DOMAIN_ALREADY_EXISTS": "Domain already exists", - "CANT_SAVE_PLUGIN_SETTINGS": "Can't save settings", - "CANT_INSTALL_PACKAGE": "Failed to install extension", - "CANT_DELETE_PACKAGE": "Failed to remove extension", - "INVALID_PLUGIN_PACKAGE": "Invalid extension package", - "UNSUPPORTED_PLUGIN_PACKAGE": "Unsupported extension package", - "UNKNOWN_ERROR": "Unknown error" + "RequestError": "Request failed", + "RequestAborted": "Request is aborted", + "RequestTimeout": "Request timed out", + "InvalidToken": "Invalid token", + "AuthError": "Authentication failed", + "DomainAlreadyExists": "Domain already exists", + "CantSavePluginSettings": "Can't save settings", + "CantInstallPackage": "Failed to install extension", + "CantDeletePackage": "Failed to remove extension", + "InvalidPluginPackage": "Invalid extension package", + "UnsupportedPluginPackage": "Unsupported extension package", + "UnknownError": "Unknown error" } } diff --git a/snappymail/v/0.0.0/app/localization/en/user.json b/snappymail/v/0.0.0/app/localization/en/user.json index c42199b18..e2a8e176c 100644 --- a/snappymail/v/0.0.0/app/localization/en/user.json +++ b/snappymail/v/0.0.0/app/localization/en/user.json @@ -139,6 +139,7 @@ "MENU_VIEW_ORIGINAL": "Show Source", "HTML_VIEW": "View HTML message", "PLAIN_VIEW": "View plain text message", + "SWAP_COLORS": "Swap default (background)color", "MENU_DOWNLOAD_ORIGINAL": "Download as .eml file", "MENU_FILTER_SIMILAR": "Filter messages like this", "MENU_PRINT": "Print", @@ -283,7 +284,8 @@ "ERROR": "%TYPE% error: %ERROR%", "CREATE_SELF_SIGNED": "Create self-signed", "VALID_UNTIL": "Valid until", - "PRIVATE_KEY": "Private key" + "PRIVATE_KEY": "Private key", + "ASK_CRYPTKEY_PASS": "Your login passphrase has changed. Please fill in your old login password to decrypt data" }, "OPENPGP": { "POPUP_IMPORT_TITLE": "Import OpenPGP key", @@ -303,12 +305,12 @@ "BACKUP_PRIVATE_KEY_ON_SERVER": "Backup private key on server" }, "SMIME": { - "POPUP_IMPORT_TITLE": "Import S/MIME certificate", + "POPUP_IMPORT_TITLE": "Import S\/MIME certificate", "CERTIFICATE": "Certificate", - "CERTIFICATES": "S/MIME Certificates", - "SIGNED_MESSAGE": "S/MIME signed message", - "ENCRYPTED_MESSAGE": "S/MIME encrypted message", - "PRIVATE_KEY_OF": "S/MIME private key of %EMAIL%" + "CERTIFICATES": "S\/MIME Certificates", + "SIGNED_MESSAGE": "S\/MIME signed message", + "ENCRYPTED_MESSAGE": "S\/MIME encrypted message", + "PRIVATE_KEY_OF": "S\/MIME private key of %EMAIL%" }, "POPUPS_FILTER": { "TITLE_CREATE_FILTER": "Create a filter?", @@ -509,7 +511,7 @@ "TO_MANY_FOLDERS_DESC_2": "We have shown only a part of them, to avoid performance problems.", "HELP_DELETE_FOLDER": "Delete folder", "HELP_SHOW_HIDE_FOLDER": "Show\/hide folder", - "HELP_CHECK_FOR_NEW_MESSAGES": "Check\/don't check for new messages", + "HELP_CHECK_FOR_NEW_MESSAGES": "Check for new messages", "HIDE_UNSUBSCRIBED": "Hide unsubscribed folders", "UNHIDE_KOLAB_FOLDERS": "Unhide Kolab folders", "TYPE_CALENDAR": "Calendar", @@ -575,42 +577,43 @@ "LABEL_CLOSE_COMPOSE": "Close compose" }, "NOTIFICATIONS": { - "REQUEST_ERROR": "Request failed", - "REQUEST_ABORTED": "Request is aborted", - "REQUEST_TIMEOUT": "Request timed out", - "INVALID_TOKEN": "Invalid token", - "AUTH_ERROR": "Authentication failed", - "CONNECTION_ERROR": "Can't connect to server", - "DOMAIN_ALREADY_EXISTS": "Domain already exists", - "DOMAIN_NOT_ALLOWED": "Domain is not allowed", - "ACCOUNT_NOT_ALLOWED": "Account is not allowed", - "CONTACTS_SYNC_ERROR": "Contacts synchronization error", - "CANT_GET_MESSAGE_LIST": "Can't get message list", - "CANT_GET_MESSAGE": "Can't get message", - "CANT_DELETE_MESSAGE": "Can't delete message", - "CANT_COPY_MESSAGE": "Can't copy message", - "CANT_MOVE_MESSAGE": "Can't move message", - "CANT_SAVE_MESSAGE": "Can't save message", - "CANT_SEND_MESSAGE": "Can't send message", - "INVALID_RECIPIENTS": "Invalid recipients", - "CANT_SAVE_FILTERS": "Can't save filters", - "CANT_GET_FILTERS": "Can't get filters", - "CANT_ACTIVATE_FILTERS_SCRIPT": "Can't activate filters script", - "CANT_DELETE_FILTERS_SCRIPT": "Can't delete filters script", - "FILTERS_ARE_NOT_CORRECT": "Filters are not correct", - "CANT_CREATE_FOLDER": "Can't create folder", - "CANT_RENAME_FOLDER": "Can't rename folder", - "CANT_DELETE_FOLDER": "Can't delete folder", - "CANT_DELETE_NON_EMPTY_FOLDER": "Can't delete non-empty directory", - "CANT_SUBSCRIBE_FOLDER": "Can't subscribe folder", - "CANT_UNSUBSCRIBE_FOLDER": "Can't unsubscribe folder", - "DEMO_SEND_MESSAGE_ERROR": "For security purposes, this account is not allowed to send messages to external e-mail addresses!", - "DEMO_ACCOUNT_ERROR": "For security purposes, this account is not allowed for this action!", - "ACCOUNT_ALREADY_EXISTS": "Account already exists", - "ACCOUNT_DOES_NOT_EXIST": "Account doesn't exist", - "ACCOUNT_SWITCH_FAILED": "Switch to account \"%EMAIL%\" failed", - "MAIL_SERVER_ERROR": "An error has occured while accessing mail server", - "INVALID_INPUT_ARGUMENT": "Invalid input argument", - "UNKNOWN_ERROR": "Unknown error" + "RequestError": "Request failed", + "RequestAborted": "Request is aborted", + "RequestTimeout": "Request timed out", + "InvalidToken": "Invalid token", + "AuthError": "Authentication failed", + "ConnectionError": "Can't connect to server", + "DomainAlreadyExists": "Domain already exists", + "DomainNotAllowed": "Domain is not allowed", + "AccountNotAllowed": "Account is not allowed", + "CryptKeyError": "Your login passphrase has changed", + "ContactsSyncError": "Contacts synchronization error", + "CantGetMessageList": "Can't get message list", + "CantGetMessage": "Can't get message", + "CantDeleteMessage": "Can't delete message", + "CantCopyMessage": "Can't copy message", + "CantMoveMessage": "Can't move message", + "CantSaveMessage": "Can't save message", + "CantSendMessage": "Can't send message", + "InvalidRecipients": "Invalid recipients", + "CantSaveFilters": "Can't save filters", + "CantGetFilters": "Can't get filters", + "CantActivateFiltersScript": "Can't activate filters script", + "CantDeleteFiltersScript": "Can't delete filters script", + "FiltersAreNotCorrect": "Filters are not correct", + "CantCreateFolder": "Can't create folder", + "CantRenameFolder": "Can't rename folder", + "CantDeleteFolder": "Can't delete folder", + "CantDeleteNonEmptyFolder": "Can't delete non-empty directory", + "CantSubscribeFolder": "Can't subscribe folder", + "CantUnsubscribeFolder": "Can't unsubscribe folder", + "DemoSendMessageError": "For security purposes, this account is not allowed to send messages to external e-mail addresses!", + "DemoAccountError": "For security purposes, this account is not allowed for this action!", + "AccountAlreadyExists": "Account already exists", + "AccountDoesNotExist": "Account doesn't exist", + "AccountSwitchFailed": "Switch to account \"%EMAIL%\" failed", + "MailServerError": "An error has occured while accessing mail server", + "InvalidInputArgument": "Invalid input argument", + "UnknownError": "Unknown error" } } diff --git a/snappymail/v/0.0.0/app/localization/es-ES/admin.json b/snappymail/v/0.0.0/app/localization/es-ES/admin.json index ccc13cdbf..9c6786f40 100644 --- a/snappymail/v/0.0.0/app/localization/es-ES/admin.json +++ b/snappymail/v/0.0.0/app/localization/es-ES/admin.json @@ -85,13 +85,14 @@ "TAB_SECURITY": { "LEGEND_SECURITY": "Seguridad", "LABEL_USE_IMAGE_PROXY": "Utilizar un proxy local para mostrar imágenes externas", - "LABEL_ALLOW_OPENPGP": "Permitir OpenPGP", + "LABEL_AUTO_VERIFY": "Verify signatures automatically", "LABEL_SHOW_PHP_INFO": "Mostrar información de PHP", "LEGEND_ADMIN_PANEL_ACCESS_CREDENTIALS": "Credenciales de Acceso al panel de control", "LABEL_CURRENT_PASSWORD": "Contraseña actual", "LABEL_NEW_PASSWORD": "Nueva contraseña", "LABEL_REPEAT_PASSWORD": "Re-ingresar", "LABEL_REQUIRE_VERIFICATION": "Requerir verificación del certificado SSL", + "CRYPTO": "Crypto", "LABEL_ALLOW_SELF_SIGNED": "Permitir certificados auto-firmados" }, "TAB_PACKAGES": { @@ -135,6 +136,7 @@ "SECURE_OPTION_NONE": "Ninguno", "LABEL_ALLOW_SIEVE_SCRIPTS": "Permitir scripts de filtro", "LABEL_USE_SHORT_LOGIN": "Inicio de Sesión corto", + "LABEL_LOWERCASE_LOGIN": "Lowercase login (case-insensitive)", "LABEL_USE_AUTH": "Usar autenticación", "LABEL_SET_SENDER": "Usar inicio de sesión como remitente", "LABEL_AUTH_PLAIN_LINE": "Force single command AUTH PLAIN login", @@ -168,17 +170,17 @@ "UNKNOWN_ERROR": "Error desconocido" }, "NOTIFICATIONS": { - "REQUEST_ERROR": "Request failed", - "REQUEST_ABORTED": "Request is aborted", - "REQUEST_TIMEOUT": "Request timed out", - "INVALID_TOKEN": "Token inválido", - "AUTH_ERROR": "Autenticación fallida", - "DOMAIN_ALREADY_EXISTS": "El Dominio ya existe", - "CANT_SAVE_PLUGIN_SETTINGS": "No se pudieron guardar las configuraciones", - "CANT_INSTALL_PACKAGE": "No se ha podido instalar el extensión", - "CANT_DELETE_PACKAGE": "No se ha podido eliminar el extensión", - "INVALID_PLUGIN_PACKAGE": "El paquete de extensión no válido", - "UNSUPPORTED_PLUGIN_PACKAGE": "El paquete del extensión es incompatible", - "UNKNOWN_ERROR": "Error desconocido" + "RequestError": "Request failed", + "RequestAborted": "Request is aborted", + "RequestTimeout": "Request timed out", + "InvalidToken": "Token inválido", + "AuthError": "Autenticación fallida", + "DomainAlreadyExists": "El Dominio ya existe", + "CantSavePluginSettings": "No se pudieron guardar las configuraciones", + "CantInstallPackage": "No se ha podido instalar el extensión", + "CantDeletePackage": "No se ha podido eliminar el extensión", + "InvalidPluginPackage": "El paquete de extensión no válido", + "UnsupportedPluginPackage": "El paquete del extensión es incompatible", + "UnknownError": "Error desconocido" } } diff --git a/snappymail/v/0.0.0/app/localization/es-ES/user.json b/snappymail/v/0.0.0/app/localization/es-ES/user.json index 15ae227ad..7b46bfcbf 100644 --- a/snappymail/v/0.0.0/app/localization/es-ES/user.json +++ b/snappymail/v/0.0.0/app/localization/es-ES/user.json @@ -139,6 +139,7 @@ "MENU_VIEW_ORIGINAL": "Mostrar original", "HTML_VIEW": "View HTML message", "PLAIN_VIEW": "View plain text message", + "SWAP_COLORS": "Swap default (background)color", "MENU_DOWNLOAD_ORIGINAL": "Descargar como archivo .eml", "MENU_FILTER_SIMILAR": "Filtrar mensajes como este", "MENU_PRINT": "Imprimir", @@ -283,7 +284,8 @@ "ERROR": "Error %TYPE%: %ERROR%", "CREATE_SELF_SIGNED": "Create self-signed", "VALID_UNTIL": "Valid until", - "PRIVATE_KEY": "Private key" + "PRIVATE_KEY": "Private key", + "ASK_CRYPTKEY_PASS": "Your login passphrase has changed. Please fill in your old login password to decrypt data" }, "OPENPGP": { "POPUP_IMPORT_TITLE": "Importar clave OpenPGP", @@ -509,7 +511,7 @@ "TO_MANY_FOLDERS_DESC_2": "We have shown only a part of them, to avoid performance problems.", "HELP_DELETE_FOLDER": "Eliminar carpeta", "HELP_SHOW_HIDE_FOLDER": "Mostrar\/ocultar carpeta", - "HELP_CHECK_FOR_NEW_MESSAGES": "Chequear\/no chequear nuevos mensajes", + "HELP_CHECK_FOR_NEW_MESSAGES": "Chequear nuevos mensajes", "HIDE_UNSUBSCRIBED": "Ocultar carpetas dadas de baja", "UNHIDE_KOLAB_FOLDERS": "Unhide Kolab folders", "TYPE_CALENDAR": "Calendario", @@ -575,42 +577,43 @@ "LABEL_CLOSE_COMPOSE": "Cerrar componer" }, "NOTIFICATIONS": { - "REQUEST_ERROR": "Request failed", - "REQUEST_ABORTED": "Request is aborted", - "REQUEST_TIMEOUT": "Request timed out", - "INVALID_TOKEN": "Elemento no válido", - "AUTH_ERROR": "Autentificación fallida", - "CONNECTION_ERROR": "No se puede conectar al servidor", - "DOMAIN_ALREADY_EXISTS": "El Dominio ya existe", - "DOMAIN_NOT_ALLOWED": "Dominio no permitido", - "ACCOUNT_NOT_ALLOWED": "La cuenta no está permitida", - "CONTACTS_SYNC_ERROR": "Error de sincronización de Contactos", - "CANT_GET_MESSAGE_LIST": "No se puede obtener la lista de mensajes", - "CANT_GET_MESSAGE": "No se puede obtener el mensaje", - "CANT_DELETE_MESSAGE": "No se puede eliminar el mensaje", - "CANT_COPY_MESSAGE": "No se puede copiar el mensaje", - "CANT_MOVE_MESSAGE": "No se puede mover el mensaje", - "CANT_SAVE_MESSAGE": "No se puede guardar el mensaje", - "CANT_SEND_MESSAGE": "No se puede enviar el mensaje", - "INVALID_RECIPIENTS": "Los destinatarios no son válidos", - "CANT_SAVE_FILTERS": "No se pueden guardar los filtros", - "CANT_GET_FILTERS": "No se pueden obtener los filtros", - "CANT_ACTIVATE_FILTERS_SCRIPT": "Can't activate filters script", - "CANT_DELETE_FILTERS_SCRIPT": "Can't delete filters script", - "FILTERS_ARE_NOT_CORRECT": "Filtros incorrectos", - "CANT_CREATE_FOLDER": "No se puede crear la carpeta", - "CANT_RENAME_FOLDER": "No se puede renombrar la carpeta", - "CANT_DELETE_FOLDER": "No se puede eliminar la carpeta", - "CANT_DELETE_NON_EMPTY_FOLDER": "No se puede eliminar un directorio que no está vacío", - "CANT_SUBSCRIBE_FOLDER": "No se puede suscribir carpeta", - "CANT_UNSUBSCRIBE_FOLDER": "No puede darse de baja de la carpeta", - "DEMO_SEND_MESSAGE_ERROR": "Por razones de seguridad, esta cuenta demo no permite enviar mensajes a direcciones de correo electrónico externas!", - "DEMO_ACCOUNT_ERROR": "For security purposes, this account is not allowed for this action!", - "ACCOUNT_ALREADY_EXISTS": "La cuenta ya existe", - "ACCOUNT_DOES_NOT_EXIST": "La cuenta no existe", - "ACCOUNT_SWITCH_FAILED": "Switch to account \"%EMAIL%\" failed", - "MAIL_SERVER_ERROR": "Ocurrió un error mientras se accedía al servidor", - "INVALID_INPUT_ARGUMENT": "Argumento no válido", - "UNKNOWN_ERROR": "Error desconocido" + "RequestError": "Request failed", + "RequestAborted": "Request is aborted", + "RequestTimeout": "Request timed out", + "InvalidToken": "Elemento no válido", + "AuthError": "Autentificación fallida", + "ConnectionError": "No se puede conectar al servidor", + "DomainAlreadyExists": "El Dominio ya existe", + "DomainNotAllowed": "Dominio no permitido", + "AccountNotAllowed": "La cuenta no está permitida", + "CryptKeyError": "Your login passphrase has changed", + "ContactsSyncError": "Error de sincronización de Contactos", + "CantGetMessageList": "No se puede obtener la lista de mensajes", + "CantGetMessage": "No se puede obtener el mensaje", + "CantDeleteMessage": "No se puede eliminar el mensaje", + "CantCopyMessage": "No se puede copiar el mensaje", + "CantMoveMessage": "No se puede mover el mensaje", + "CantSaveMessage": "No se puede guardar el mensaje", + "CantSendMessage": "No se puede enviar el mensaje", + "InvalidRecipients": "Los destinatarios no son válidos", + "CantSaveFilters": "No se pueden guardar los filtros", + "CantGetFilters": "No se pueden obtener los filtros", + "CantActivateFiltersScript": "Can't activate filters script", + "CantDeleteFiltersScript": "Can't delete filters script", + "FiltersAreNotCorrect": "Filtros incorrectos", + "CantCreateFolder": "No se puede crear la carpeta", + "CantRenameFolder": "No se puede renombrar la carpeta", + "CantDeleteFolder": "No se puede eliminar la carpeta", + "CantDeleteNonEmptyFolder": "No se puede eliminar un directorio que no está vacío", + "CantSubscribeFolder": "No se puede suscribir carpeta", + "CantUnsubscribeFolder": "No puede darse de baja de la carpeta", + "DemoSendMessageError": "Por razones de seguridad, esta cuenta demo no permite enviar mensajes a direcciones de correo electrónico externas!", + "DemoAccountError": "For security purposes, this account is not allowed for this action!", + "AccountAlreadyExists": "La cuenta ya existe", + "AccountDoesNotExist": "La cuenta no existe", + "AccountSwitchFailed": "Switch to account \"%EMAIL%\" failed", + "MailServerError": "Ocurrió un error mientras se accedía al servidor", + "InvalidInputArgument": "Argumento no válido", + "UnknownError": "Error desconocido" } } diff --git a/snappymail/v/0.0.0/app/localization/et-EE/user.json b/snappymail/v/0.0.0/app/localization/et-EE/user.json index 3629b8826..4cb8163ee 100644 --- a/snappymail/v/0.0.0/app/localization/et-EE/user.json +++ b/snappymail/v/0.0.0/app/localization/et-EE/user.json @@ -139,6 +139,7 @@ "MENU_VIEW_ORIGINAL": "Näita lähteteksti", "HTML_VIEW": "View HTML message", "PLAIN_VIEW": "View plain text message", + "SWAP_COLORS": "Swap default (background)color", "MENU_DOWNLOAD_ORIGINAL": "Laadi alla .eml failina", "MENU_FILTER_SIMILAR": "Filtreeri sarnased kirjad", "MENU_PRINT": "Prindi", @@ -283,7 +284,8 @@ "ERROR": "%TYPE% viga: %ERROR%", "CREATE_SELF_SIGNED": "Create self-signed", "VALID_UNTIL": "Valid until", - "PRIVATE_KEY": "Private key" + "PRIVATE_KEY": "Private key", + "ASK_CRYPTKEY_PASS": "Your login passphrase has changed. Please fill in your old login password to decrypt data" }, "OPENPGP": { "POPUP_IMPORT_TITLE": "Impordi OpenPGP võti", @@ -509,7 +511,7 @@ "TO_MANY_FOLDERS_DESC_2": "Näitame sulle neist vaid osa, et vältida jõudlusprobleeme.", "HELP_DELETE_FOLDER": "Kustuta kaust", "HELP_SHOW_HIDE_FOLDER": "Näita\/peida kaust", - "HELP_CHECK_FOR_NEW_MESSAGES": "Kontrolli\/ära kontrolli uusi kirju", + "HELP_CHECK_FOR_NEW_MESSAGES": "Kontrolli uusi kirju", "HIDE_UNSUBSCRIBED": "Hide unsubscribed folders", "UNHIDE_KOLAB_FOLDERS": "Unhide Kolab folders", "TYPE_CALENDAR": "Calendar", @@ -575,42 +577,43 @@ "LABEL_CLOSE_COMPOSE": "Sulge kirja koostamise aken" }, "NOTIFICATIONS": { - "REQUEST_ERROR": "Request failed", - "REQUEST_ABORTED": "Request is aborted", - "REQUEST_TIMEOUT": "Request timed out", - "INVALID_TOKEN": "Puudulik tõend", - "AUTH_ERROR": "Autentimine ebaõnnestus", - "CONNECTION_ERROR": "Serveriga ühendumine ebaõnnestus", - "DOMAIN_ALREADY_EXISTS": "Domeen juba eksisteerib", - "DOMAIN_NOT_ALLOWED": "Domeenil ei ole lubatud siseneda", - "ACCOUNT_NOT_ALLOWED": "Kasutajakontol ei ole lubatud siseneda", - "CONTACTS_SYNC_ERROR": "Kontaktide sünkroniseerimise viga", - "CANT_GET_MESSAGE_LIST": "Kirjade nimekirja laadimine ebaõnnestus", - "CANT_GET_MESSAGE": "Kirja laadimine ebaõnnestus", - "CANT_DELETE_MESSAGE": "Kirja kustutamine ebaõnnestus", - "CANT_COPY_MESSAGE": "Can't copy message", - "CANT_MOVE_MESSAGE": "Kirja liigutamine ebaõnnestus", - "CANT_SAVE_MESSAGE": "Kirja salvestamine ebaõnnestus", - "CANT_SEND_MESSAGE": "Kirja saatmine ebaõnnestus", - "INVALID_RECIPIENTS": "Puudulikud saajad", - "CANT_SAVE_FILTERS": "Filtri salvestamine ebaõnnestus", - "CANT_GET_FILTERS": "Filtrite laadimine ebaõnnestus", - "CANT_ACTIVATE_FILTERS_SCRIPT": "Can't activate filters script", - "CANT_DELETE_FILTERS_SCRIPT": "Can't delete filters script", - "FILTERS_ARE_NOT_CORRECT": "Filtrid on puudulikud", - "CANT_CREATE_FOLDER": "Kausta loomine ebaõnnestus", - "CANT_RENAME_FOLDER": "Kausta ümbernimetamine ebaõnnestus", - "CANT_DELETE_FOLDER": "Kausta kustutamine ebaõnnestus", - "CANT_DELETE_NON_EMPTY_FOLDER": "Kirju sisaldavat kausta pole võimalik kustutada", - "CANT_SUBSCRIBE_FOLDER": "Kausta tellimine ebaõnnestus.", - "CANT_UNSUBSCRIBE_FOLDER": "Kausta tellimisest loobumine ebaõnnestus.", - "DEMO_SEND_MESSAGE_ERROR": "Turvalisusega seotud põhjustel ei ole sellel kontol lubatud saata kirju välistele aadressidele!", - "DEMO_ACCOUNT_ERROR": "Turvalisusega seotud põhjustel ei ole sellel kontol lubatud soovitud toimingut teostada!", - "ACCOUNT_ALREADY_EXISTS": "Konto juba eksisteerib", - "ACCOUNT_DOES_NOT_EXIST": "Kontot veel ei eksisteeri", - "ACCOUNT_SWITCH_FAILED": "Switch to account \"%EMAIL%\" failed", - "MAIL_SERVER_ERROR": "E-posti serveri poole pöördumisel tekkis viga", - "INVALID_INPUT_ARGUMENT": "Puudulik sisendi arument", - "UNKNOWN_ERROR": "Tundmatu viga" + "RequestError": "Request failed", + "RequestAborted": "Request is aborted", + "RequestTimeout": "Request timed out", + "InvalidToken": "Puudulik tõend", + "AuthError": "Autentimine ebaõnnestus", + "ConnectionError": "Serveriga ühendumine ebaõnnestus", + "DomainAlreadyExists": "Domeen juba eksisteerib", + "DomainNotAllowed": "Domeenil ei ole lubatud siseneda", + "AccountNotAllowed": "Kasutajakontol ei ole lubatud siseneda", + "CryptKeyError": "Your login passphrase has changed", + "ContactsSyncError": "Kontaktide sünkroniseerimise viga", + "CantGetMessageList": "Kirjade nimekirja laadimine ebaõnnestus", + "CantGetMessage": "Kirja laadimine ebaõnnestus", + "CantDeleteMessage": "Kirja kustutamine ebaõnnestus", + "CantCopyMessage": "Can't copy message", + "CantMoveMessage": "Kirja liigutamine ebaõnnestus", + "CantSaveMessage": "Kirja salvestamine ebaõnnestus", + "CantSendMessage": "Kirja saatmine ebaõnnestus", + "InvalidRecipients": "Puudulikud saajad", + "CantSaveFilters": "Filtri salvestamine ebaõnnestus", + "CantGetFilters": "Filtrite laadimine ebaõnnestus", + "CantActivateFiltersScript": "Can't activate filters script", + "CantDeleteFiltersScript": "Can't delete filters script", + "FiltersAreNotCorrect": "Filtrid on puudulikud", + "CantCreateFolder": "Kausta loomine ebaõnnestus", + "CantRenameFolder": "Kausta ümbernimetamine ebaõnnestus", + "CantDeleteFolder": "Kausta kustutamine ebaõnnestus", + "CantDeleteNonEmptyFolder": "Kirju sisaldavat kausta pole võimalik kustutada", + "CantSubscribeFolder": "Kausta tellimine ebaõnnestus.", + "CantUnsubscribeFolder": "Kausta tellimisest loobumine ebaõnnestus.", + "DemoSendMessageError": "Turvalisusega seotud põhjustel ei ole sellel kontol lubatud saata kirju välistele aadressidele!", + "DemoAccountError": "Turvalisusega seotud põhjustel ei ole sellel kontol lubatud soovitud toimingut teostada!", + "AccountAlreadyExists": "Konto juba eksisteerib", + "AccountDoesNotExist": "Kontot veel ei eksisteeri", + "AccountSwitchFailed": "Switch to account \"%EMAIL%\" failed", + "MailServerError": "E-posti serveri poole pöördumisel tekkis viga", + "InvalidInputArgument": "Puudulik sisendi arument", + "UnknownError": "Tundmatu viga" } } diff --git a/snappymail/v/0.0.0/app/localization/eu/admin.json b/snappymail/v/0.0.0/app/localization/eu/admin.json index 8cf9cf9b7..0b9de0748 100644 --- a/snappymail/v/0.0.0/app/localization/eu/admin.json +++ b/snappymail/v/0.0.0/app/localization/eu/admin.json @@ -85,13 +85,14 @@ "TAB_SECURITY": { "LEGEND_SECURITY": "Segurtasuna", "LABEL_USE_IMAGE_PROXY": "Erabili proxy lokala kanpoko irudientzat", - "LABEL_ALLOW_OPENPGP": "Baimendu OpenPGP", + "LABEL_AUTO_VERIFY": "Verify signatures automatically", "LABEL_SHOW_PHP_INFO": "Erakutsi PHP informazioa", "LEGEND_ADMIN_PANEL_ACCESS_CREDENTIALS": "Admin Panelerako Sarbide Kredentzialak", "LABEL_CURRENT_PASSWORD": "Uneko pasahitza", "LABEL_NEW_PASSWORD": "Pasahitz berria", "LABEL_REPEAT_PASSWORD": "Errepikatu", "LABEL_REQUIRE_VERIFICATION": "SSL ziurtagiriaren baliozkotzea eskatu", + "CRYPTO": "Crypto", "LABEL_ALLOW_SELF_SIGNED": "Onartu norberak sinatutako ziurtagiriak" }, "TAB_PACKAGES": { @@ -135,6 +136,7 @@ "SECURE_OPTION_NONE": "Bat ere ez", "LABEL_ALLOW_SIEVE_SCRIPTS": "Onartu sieve scriptak", "LABEL_USE_SHORT_LOGIN": "Erabili login motza", + "LABEL_LOWERCASE_LOGIN": "Lowercase login (case-insensitive)", "LABEL_USE_AUTH": "Erabili autentikazioa", "LABEL_SET_SENDER": "Erabili logina bidaltzaile bezala", "LABEL_AUTH_PLAIN_LINE": "Force single command AUTH PLAIN login", @@ -168,17 +170,17 @@ "UNKNOWN_ERROR": "Errore ezezaguna" }, "NOTIFICATIONS": { - "REQUEST_ERROR": "Eskaerak huts egin du", - "REQUEST_ABORTED": "Eskaera bertan behera utzi da", - "REQUEST_TIMEOUT": "Eskaeraren denbora-muga gainditu da", - "INVALID_TOKEN": "Ez-baliozko tokena", - "AUTH_ERROR": "Autentikazioak huts egin du", - "DOMAIN_ALREADY_EXISTS": "Domeinua jadanik existitzen da", - "CANT_SAVE_PLUGIN_SETTINGS": "Ezin dira ezarpenak gorde", - "CANT_INSTALL_PACKAGE": "Akatsa gehigarria instalatzean", - "CANT_DELETE_PACKAGE": "Akatsa gehigarria kentzean", - "INVALID_PLUGIN_PACKAGE": "Ez-baliozko gehigarri paketea", - "UNSUPPORTED_PLUGIN_PACKAGE": "Gehigarri paketea ez da onartzen", - "UNKNOWN_ERROR": "Errore ezezaguna" + "RequestError": "Eskaerak huts egin du", + "RequestAborted": "Eskaera bertan behera utzi da", + "RequestTimeout": "Eskaeraren denbora-muga gainditu da", + "InvalidToken": "Ez-baliozko tokena", + "AuthError": "Autentikazioak huts egin du", + "DomainAlreadyExists": "Domeinua jadanik existitzen da", + "CantSavePluginSettings": "Ezin dira ezarpenak gorde", + "CantInstallPackage": "Akatsa gehigarria instalatzean", + "CantDeletePackage": "Akatsa gehigarria kentzean", + "InvalidPluginPackage": "Ez-baliozko gehigarri paketea", + "UnsupportedPluginPackage": "Gehigarri paketea ez da onartzen", + "UnknownError": "Errore ezezaguna" } } diff --git a/snappymail/v/0.0.0/app/localization/eu/user.json b/snappymail/v/0.0.0/app/localization/eu/user.json index 2a560f4b7..fd52481c4 100644 --- a/snappymail/v/0.0.0/app/localization/eu/user.json +++ b/snappymail/v/0.0.0/app/localization/eu/user.json @@ -139,6 +139,7 @@ "MENU_VIEW_ORIGINAL": "Erakutsi jatorrizkoa", "HTML_VIEW": "Ikusi HTML mezua", "PLAIN_VIEW": "Ikusi testu soil mezua", + "SWAP_COLORS": "Swap default (background)color", "MENU_DOWNLOAD_ORIGINAL": "Deskargatu .eml fitxategi bezala", "MENU_FILTER_SIMILAR": "Iragazi hau bezalako mezuak", "MENU_PRINT": "Inprimatu", @@ -283,7 +284,8 @@ "ERROR": "%TYPE% errorea: %ERROR%", "CREATE_SELF_SIGNED": "Create self-signed", "VALID_UNTIL": "Valid until", - "PRIVATE_KEY": "Private key" + "PRIVATE_KEY": "Private key", + "ASK_CRYPTKEY_PASS": "Your login passphrase has changed. Please fill in your old login password to decrypt data" }, "OPENPGP": { "POPUP_IMPORT_TITLE": "Inportatu OpenPGP gakoa", @@ -509,7 +511,7 @@ "TO_MANY_FOLDERS_DESC_2": "Beraien zati bat bakarrik erakutsi dugu, errendimendu arazoak saihesteko.", "HELP_DELETE_FOLDER": "Ezabatu karpeta", "HELP_SHOW_HIDE_FOLDER": "Erakutsi\/ezkutatu karpeta", - "HELP_CHECK_FOR_NEW_MESSAGES": "Bilatu\/ez bilatu mezu berriak", + "HELP_CHECK_FOR_NEW_MESSAGES": "Bilatu mezu berriak", "HIDE_UNSUBSCRIBED": "Ezkutatu harpidetzik gabeko karpetak", "UNHIDE_KOLAB_FOLDERS": "Erakutsi Kolab karpetak", "TYPE_CALENDAR": "Egutegia", @@ -575,42 +577,43 @@ "LABEL_CLOSE_COMPOSE": "Itxi mezua" }, "NOTIFICATIONS": { - "REQUEST_ERROR": "Eskaerak huts egin du", - "REQUEST_ABORTED": "Eskaera bertan behera utzi da", - "REQUEST_TIMEOUT": "Eskaera denboraz kantpo", - "INVALID_TOKEN": "Ez-baliozko tokena", - "AUTH_ERROR": "Autentikazio errorea", - "CONNECTION_ERROR": "Ezin da zerbitzarira konektatu", - "DOMAIN_ALREADY_EXISTS": "Domeinua jadanik existitzen da", - "DOMAIN_NOT_ALLOWED": "Domeinu ez baimendua", - "ACCOUNT_NOT_ALLOWED": "Kontu ez baimendua", - "CONTACTS_SYNC_ERROR": "Kontaktu sinkronizazio errorea", - "CANT_GET_MESSAGE_LIST": "Ezin da mezuen zerrenda eskuratu", - "CANT_GET_MESSAGE": "Ezin da mezua eskuratu", - "CANT_DELETE_MESSAGE": "Ezin da mezua ezabatu", - "CANT_COPY_MESSAGE": "Ezin da mezua kopiatu", - "CANT_MOVE_MESSAGE": "Ezin da mezua mugitu", - "CANT_SAVE_MESSAGE": "Ezin da mezua gorde", - "CANT_SEND_MESSAGE": "Ezin da mezua bidali", - "INVALID_RECIPIENTS": "Ez-baliozko hartzaileak", - "CANT_SAVE_FILTERS": "Ezin dira iragazkiak gorde", - "CANT_GET_FILTERS": "Ezin dira iragazkiak eskuratu", - "CANT_ACTIVATE_FILTERS_SCRIPT": "Ezin dira iragazki-scriptak aktibatu", - "CANT_DELETE_FILTERS_SCRIPT": "Ezin dira iragazki-scriptak ezabatu", - "FILTERS_ARE_NOT_CORRECT": "Iragazkiak ez dira zuzenak", - "CANT_CREATE_FOLDER": "Ezin da karpeta sortu", - "CANT_RENAME_FOLDER": "Ezin da karpeta berrizendatu", - "CANT_DELETE_FOLDER": "Ezin da karpeta ezabatu", - "CANT_DELETE_NON_EMPTY_FOLDER": "Ezin da hutsi ez dagoen direktorioa ezabatu", - "CANT_SUBSCRIBE_FOLDER": "Ezin da karpetara harpidetu", - "CANT_UNSUBSCRIBE_FOLDER": "Ezin da karpetatik harpidetza kendu", - "DEMO_SEND_MESSAGE_ERROR": "Segurtasunagatik, kontua honek ezin du kanpoko e-mail helbideetara mezurik bidali!", - "DEMO_ACCOUNT_ERROR": "Segurtasunagatik, kontu hau ez dago ahalbideratuta ekintza honetarako!", - "ACCOUNT_ALREADY_EXISTS": "Kontua jada existitzen da", - "ACCOUNT_DOES_NOT_EXIST": "Kontua ez da existitzen", - "ACCOUNT_SWITCH_FAILED": "\"%EMAIL%\" kontura aldaketak akatsa eman du", - "MAIL_SERVER_ERROR": "Errore bat gertatu da posta zebritzarira sartzean", - "INVALID_INPUT_ARGUMENT": "Ez-naliozko sarrera-argumentua", - "UNKNOWN_ERROR": "Errore ezezaguna" + "RequestError": "Eskaerak huts egin du", + "RequestAborted": "Eskaera bertan behera utzi da", + "RequestTimeout": "Eskaera denboraz kantpo", + "InvalidToken": "Ez-baliozko tokena", + "AuthError": "Autentikazio errorea", + "ConnectionError": "Ezin da zerbitzarira konektatu", + "DomainAlreadyExists": "Domeinua jadanik existitzen da", + "DomainNotAllowed": "Domeinu ez baimendua", + "AccountNotAllowed": "Kontu ez baimendua", + "CryptKeyError": "Your login passphrase has changed", + "ContactsSyncError": "Kontaktu sinkronizazio errorea", + "CantGetMessageList": "Ezin da mezuen zerrenda eskuratu", + "CantGetMessage": "Ezin da mezua eskuratu", + "CantDeleteMessage": "Ezin da mezua ezabatu", + "CantCopyMessage": "Ezin da mezua kopiatu", + "CantMoveMessage": "Ezin da mezua mugitu", + "CantSaveMessage": "Ezin da mezua gorde", + "CantSendMessage": "Ezin da mezua bidali", + "InvalidRecipients": "Ez-baliozko hartzaileak", + "CantSaveFilters": "Ezin dira iragazkiak gorde", + "CantGetFilters": "Ezin dira iragazkiak eskuratu", + "CantActivateFiltersScript": "Ezin dira iragazki-scriptak aktibatu", + "CantDeleteFiltersScript": "Ezin dira iragazki-scriptak ezabatu", + "FiltersAreNotCorrect": "Iragazkiak ez dira zuzenak", + "CantCreateFolder": "Ezin da karpeta sortu", + "CantRenameFolder": "Ezin da karpeta berrizendatu", + "CantDeleteFolder": "Ezin da karpeta ezabatu", + "CantDeleteNonEmptyFolder": "Ezin da hutsi ez dagoen direktorioa ezabatu", + "CantSubscribeFolder": "Ezin da karpetara harpidetu", + "CantUnsubscribeFolder": "Ezin da karpetatik harpidetza kendu", + "DemoSendMessageError": "Segurtasunagatik, kontua honek ezin du kanpoko e-mail helbideetara mezurik bidali!", + "DemoAccountError": "Segurtasunagatik, kontu hau ez dago ahalbideratuta ekintza honetarako!", + "AccountAlreadyExists": "Kontua jada existitzen da", + "AccountDoesNotExist": "Kontua ez da existitzen", + "AccountSwitchFailed": "\"%EMAIL%\" kontura aldaketak akatsa eman du", + "MailServerError": "Errore bat gertatu da posta zebritzarira sartzean", + "InvalidInputArgument": "Ez-naliozko sarrera-argumentua", + "UnknownError": "Errore ezezaguna" } } diff --git a/snappymail/v/0.0.0/app/localization/fa-IR/admin.json b/snappymail/v/0.0.0/app/localization/fa-IR/admin.json index 1bbc393c8..f417be92c 100644 --- a/snappymail/v/0.0.0/app/localization/fa-IR/admin.json +++ b/snappymail/v/0.0.0/app/localization/fa-IR/admin.json @@ -85,13 +85,14 @@ "TAB_SECURITY": { "LEGEND_SECURITY": "امنیت", "LABEL_USE_IMAGE_PROXY": "از پروکسی محلی برای عکسهای خارج از سرور استفاده شود", - "LABEL_ALLOW_OPENPGP": "فعال‌سازی OpenPGP", + "LABEL_AUTO_VERIFY": "Verify signatures automatically", "LABEL_SHOW_PHP_INFO": "نمایش اطلاعات PHP", "LEGEND_ADMIN_PANEL_ACCESS_CREDENTIALS": "اعتبار مجوز دسترسی به صفحه مدیریت", "LABEL_CURRENT_PASSWORD": "گذرواژه فعلی", "LABEL_NEW_PASSWORD": "گذرواژه جدید", "LABEL_REPEAT_PASSWORD": "تکرار", "LABEL_REQUIRE_VERIFICATION": "به بررسی گواهینامه SSL نیاز داشته باشد", + "CRYPTO": "Crypto", "LABEL_ALLOW_SELF_SIGNED": "به گواهی‌نامه‌هایی که توسط خودشان امضاء شده‌اند اجازه بده" }, "TAB_PACKAGES": { @@ -135,6 +136,7 @@ "SECURE_OPTION_NONE": "هیچ‌کدام", "LABEL_ALLOW_SIEVE_SCRIPTS": "اجازه به اجرا‌شونده‌های sieve", "LABEL_USE_SHORT_LOGIN": "استفاده از ورود کوتاه بدون دامنه", + "LABEL_LOWERCASE_LOGIN": "Lowercase login (case-insensitive)", "LABEL_USE_AUTH": "استفاده از احراز هویت", "LABEL_SET_SENDER": "Use login as sender", "LABEL_AUTH_PLAIN_LINE": "Force single command AUTH PLAIN login", @@ -168,17 +170,17 @@ "UNKNOWN_ERROR": "خطای نامشخص" }, "NOTIFICATIONS": { - "REQUEST_ERROR": "Request failed", - "REQUEST_ABORTED": "Request is aborted", - "REQUEST_TIMEOUT": "Request timed out", - "INVALID_TOKEN": "توکن نامعتبر", - "AUTH_ERROR": "احراز هویت انجام نشد", - "DOMAIN_ALREADY_EXISTS": "دامنه در حال حاضر موجود است", - "CANT_SAVE_PLUGIN_SETTINGS": "امکان ذخیره تنظیمات نیست", - "CANT_INSTALL_PACKAGE": "نصب بسته با خطا همراه بود", - "CANT_DELETE_PACKAGE": "حذف بسته با خطا همراه بو", - "INVALID_PLUGIN_PACKAGE": "بسته افزونه نامعتبر است", - "UNSUPPORTED_PLUGIN_PACKAGE": "بسته افزونه پشتیبانی نمی‌شود", - "UNKNOWN_ERROR": "خطای نامشخص" + "RequestError": "Request failed", + "RequestAborted": "Request is aborted", + "RequestTimeout": "Request timed out", + "InvalidToken": "توکن نامعتبر", + "AuthError": "احراز هویت انجام نشد", + "DomainAlreadyExists": "دامنه در حال حاضر موجود است", + "CantSavePluginSettings": "امکان ذخیره تنظیمات نیست", + "CantInstallPackage": "نصب بسته با خطا همراه بود", + "CantDeletePackage": "حذف بسته با خطا همراه بو", + "InvalidPluginPackage": "بسته افزونه نامعتبر است", + "UnsupportedPluginPackage": "بسته افزونه پشتیبانی نمی‌شود", + "UnknownError": "خطای نامشخص" } } diff --git a/snappymail/v/0.0.0/app/localization/fa-IR/user.json b/snappymail/v/0.0.0/app/localization/fa-IR/user.json index 90eae77c2..21e2221e9 100644 --- a/snappymail/v/0.0.0/app/localization/fa-IR/user.json +++ b/snappymail/v/0.0.0/app/localization/fa-IR/user.json @@ -139,6 +139,7 @@ "MENU_VIEW_ORIGINAL": "نمایش منبع", "HTML_VIEW": "View HTML message", "PLAIN_VIEW": "View plain text message", + "SWAP_COLORS": "Swap default (background)color", "MENU_DOWNLOAD_ORIGINAL": "دریافت با پسوند eml.", "MENU_FILTER_SIMILAR": " فیلتر کردن پیام‌های مشابه", "MENU_PRINT": "چاپ", @@ -283,7 +284,8 @@ "ERROR": "خطای %TYPE%: %ERROR%", "CREATE_SELF_SIGNED": "Create self-signed", "VALID_UNTIL": "Valid until", - "PRIVATE_KEY": "Private key" + "PRIVATE_KEY": "Private key", + "ASK_CRYPTKEY_PASS": "Your login passphrase has changed. Please fill in your old login password to decrypt data" }, "OPENPGP": { "POPUP_IMPORT_TITLE": "واردکردن کلید OpenPGP", @@ -509,7 +511,7 @@ "TO_MANY_FOLDERS_DESC_2": "جهت جلوگیری از کاهش کارایی سیستم، ما فقط قسمتی از آنها را به شما نشان می‌دهیم.", "HELP_DELETE_FOLDER": "حذف پوشه", "HELP_SHOW_HIDE_FOLDER": "نمایش\/مخفی‌بودن پوشه", - "HELP_CHECK_FOR_NEW_MESSAGES": "بررسی\/عدم‌بررسی برای پیام جدید", + "HELP_CHECK_FOR_NEW_MESSAGES": "پیام جدی را بررسی کنید", "HIDE_UNSUBSCRIBED": "Hide unsubscribed folders", "UNHIDE_KOLAB_FOLDERS": "Unhide Kolab folders", "TYPE_CALENDAR": "Calendar", @@ -575,42 +577,43 @@ "LABEL_CLOSE_COMPOSE": "بستن صفحه ایجاد" }, "NOTIFICATIONS": { - "REQUEST_ERROR": "Request failed", - "REQUEST_ABORTED": "Request is aborted", - "REQUEST_TIMEOUT": "Request timed out", - "INVALID_TOKEN": "توکن نامعتبر", - "AUTH_ERROR": "احراز هویت با موفقیت همراه نبود", - "CONNECTION_ERROR": "عدم اتصال به سرور", - "DOMAIN_ALREADY_EXISTS": "دامنه در حال حاضر موجود است", - "DOMAIN_NOT_ALLOWED": "به دامنه اجازه داده نشده است", - "ACCOUNT_NOT_ALLOWED": "به حساب کاربری اجازه داده نشده است", - "CONTACTS_SYNC_ERROR": "همگام‌سازی تماس‌ها با خطا همراه بود", - "CANT_GET_MESSAGE_LIST": "امکان دریافت لیست پیام‌ها نیست", - "CANT_GET_MESSAGE": "امکان دریافت پیام نیست", - "CANT_DELETE_MESSAGE": "امکان حذف پیام نیست", - "CANT_COPY_MESSAGE": "Can't copy message", - "CANT_MOVE_MESSAGE": "امکان انتقال پیام نیست", - "CANT_SAVE_MESSAGE": "امکان ذخیره پیام نیست", - "CANT_SEND_MESSAGE": "امکان ارسال پیام نیست", - "INVALID_RECIPIENTS": "گیرنده نامعتبر", - "CANT_SAVE_FILTERS": "امکان ذخیره فیلترها نیست", - "CANT_GET_FILTERS": "امکان دریافت فیلترها نیست", - "CANT_ACTIVATE_FILTERS_SCRIPT": "Can't activate filters script", - "CANT_DELETE_FILTERS_SCRIPT": "Can't delete filters script", - "FILTERS_ARE_NOT_CORRECT": "فیلترها صحیح نیستند", - "CANT_CREATE_FOLDER": "امکان ایجاد شاخه وجود ندارد", - "CANT_RENAME_FOLDER": "امکان تغییر نام شاخه نیست", - "CANT_DELETE_FOLDER": "امکان حذف شاخه نیست", - "CANT_DELETE_NON_EMPTY_FOLDER": "امکان حذف شاخه‌ای که خالی نیست وجود ندارد", - "CANT_SUBSCRIBE_FOLDER": "امکان اشتراک شاخه نیست", - "CANT_UNSUBSCRIBE_FOLDER": "امکان عدم اشتراک شاخه نیست", - "DEMO_SEND_MESSAGE_ERROR": "بنابر اهداف امنیتی، این کاربر اجازه ارسال ایمیل به یک ایمیل خارج از دامنه را ندارد!", - "DEMO_ACCOUNT_ERROR": "بنابر اهداف امنیتی، این کاربر اجازه انجام این عملیات را ندارد!", - "ACCOUNT_ALREADY_EXISTS": "کاربر وجود دارد", - "ACCOUNT_DOES_NOT_EXIST": "کاربر وجود ندارد", - "ACCOUNT_SWITCH_FAILED": "Switch to account \"%EMAIL%\" failed", - "MAIL_SERVER_ERROR": "یک خطا در زمان دسترسی به سرویس پست الکترونیک رخ داد", - "INVALID_INPUT_ARGUMENT": "پارامترهای ورودی نامعتبر", - "UNKNOWN_ERROR": "خطای نامشخص" + "RequestError": "Request failed", + "RequestAborted": "Request is aborted", + "RequestTimeout": "Request timed out", + "InvalidToken": "توکن نامعتبر", + "AuthError": "احراز هویت با موفقیت همراه نبود", + "ConnectionError": "عدم اتصال به سرور", + "DomainAlreadyExists": "دامنه در حال حاضر موجود است", + "DomainNotAllowed": "به دامنه اجازه داده نشده است", + "AccountNotAllowed": "به حساب کاربری اجازه داده نشده است", + "CryptKeyError": "Your login passphrase has changed", + "ContactsSyncError": "همگام‌سازی تماس‌ها با خطا همراه بود", + "CantGetMessageList": "امکان دریافت لیست پیام‌ها نیست", + "CantGetMessage": "امکان دریافت پیام نیست", + "CantDeleteMessage": "امکان حذف پیام نیست", + "CantCopyMessage": "Can't copy message", + "CantMoveMessage": "امکان انتقال پیام نیست", + "CantSaveMessage": "امکان ذخیره پیام نیست", + "CantSendMessage": "امکان ارسال پیام نیست", + "InvalidRecipients": "گیرنده نامعتبر", + "CantSaveFilters": "امکان ذخیره فیلترها نیست", + "CantGetFilters": "امکان دریافت فیلترها نیست", + "CantActivateFiltersScript": "Can't activate filters script", + "CantDeleteFiltersScript": "Can't delete filters script", + "FiltersAreNotCorrect": "فیلترها صحیح نیستند", + "CantCreateFolder": "امکان ایجاد شاخه وجود ندارد", + "CantRenameFolder": "امکان تغییر نام شاخه نیست", + "CantDeleteFolder": "امکان حذف شاخه نیست", + "CantDeleteNonEmptyFolder": "امکان حذف شاخه‌ای که خالی نیست وجود ندارد", + "CantSubscribeFolder": "امکان اشتراک شاخه نیست", + "CantUnsubscribeFolder": "امکان عدم اشتراک شاخه نیست", + "DemoSendMessageError": "بنابر اهداف امنیتی، این کاربر اجازه ارسال ایمیل به یک ایمیل خارج از دامنه را ندارد!", + "DemoAccountError": "بنابر اهداف امنیتی، این کاربر اجازه انجام این عملیات را ندارد!", + "AccountAlreadyExists": "کاربر وجود دارد", + "AccountDoesNotExist": "کاربر وجود ندارد", + "AccountSwitchFailed": "Switch to account \"%EMAIL%\" failed", + "MailServerError": "یک خطا در زمان دسترسی به سرویس پست الکترونیک رخ داد", + "InvalidInputArgument": "پارامترهای ورودی نامعتبر", + "UnknownError": "خطای نامشخص" } } diff --git a/snappymail/v/0.0.0/app/localization/fi-FI/admin.json b/snappymail/v/0.0.0/app/localization/fi-FI/admin.json index 3fed9ed01..37b37a19f 100644 --- a/snappymail/v/0.0.0/app/localization/fi-FI/admin.json +++ b/snappymail/v/0.0.0/app/localization/fi-FI/admin.json @@ -85,13 +85,14 @@ "TAB_SECURITY": { "LEGEND_SECURITY": "Tietoturva", "LABEL_USE_IMAGE_PROXY": "Käytä paikallista proxya ulkoisille kuville.", - "LABEL_ALLOW_OPENPGP": "Salli OpenPGP", + "LABEL_AUTO_VERIFY": "Verify signatures automatically", "LABEL_SHOW_PHP_INFO": "Näytä PHP tiedot", "LEGEND_ADMIN_PANEL_ACCESS_CREDENTIALS": "Hallintapaneeli kirjautumistiedot", "LABEL_CURRENT_PASSWORD": "Nykyinen salasana", "LABEL_NEW_PASSWORD": "Uusi salasana", "LABEL_REPEAT_PASSWORD": "Toista", "LABEL_REQUIRE_VERIFICATION": "Vaadi SSL sertifikaatin varmistus", + "CRYPTO": "Crypto", "LABEL_ALLOW_SELF_SIGNED": "Salli itse-allekirjoitetut sertifikaatit" }, "TAB_PACKAGES": { @@ -135,6 +136,7 @@ "SECURE_OPTION_NONE": "Ei mitään", "LABEL_ALLOW_SIEVE_SCRIPTS": "Salli sieve skriptit", "LABEL_USE_SHORT_LOGIN": "Käytä lyhyttä kirjautumista", + "LABEL_LOWERCASE_LOGIN": "Lowercase login (case-insensitive)", "LABEL_USE_AUTH": "Käytä tunnistautumista", "LABEL_SET_SENDER": "Use login as sender", "LABEL_AUTH_PLAIN_LINE": "Force single command AUTH PLAIN login", @@ -168,17 +170,17 @@ "UNKNOWN_ERROR": "Tuntematon virhe" }, "NOTIFICATIONS": { - "REQUEST_ERROR": "Request failed", - "REQUEST_ABORTED": "Request is aborted", - "REQUEST_TIMEOUT": "Request timed out", - "INVALID_TOKEN": "Väärä token", - "AUTH_ERROR": "Tunnistautuminen epäonnistui", - "DOMAIN_ALREADY_EXISTS": "Verkkotunnus on jo olemassa", - "CANT_SAVE_PLUGIN_SETTINGS": "Asetuksia ei voida tallentaa", - "CANT_INSTALL_PACKAGE": "Paketin asennus epäonnistui", - "CANT_DELETE_PACKAGE": "Paketin poisto epäonnistui", - "INVALID_PLUGIN_PACKAGE": "Virheellinen lisäosa-paketti", - "UNSUPPORTED_PLUGIN_PACKAGE": "Ei tuettu lisäosa-paketti", - "UNKNOWN_ERROR": "Tuntematon virhe" + "RequestError": "Request failed", + "RequestAborted": "Request is aborted", + "RequestTimeout": "Request timed out", + "InvalidToken": "Väärä token", + "AuthError": "Tunnistautuminen epäonnistui", + "DomainAlreadyExists": "Verkkotunnus on jo olemassa", + "CantSavePluginSettings": "Asetuksia ei voida tallentaa", + "CantInstallPackage": "Paketin asennus epäonnistui", + "CantDeletePackage": "Paketin poisto epäonnistui", + "InvalidPluginPackage": "Virheellinen lisäosa-paketti", + "UnsupportedPluginPackage": "Ei tuettu lisäosa-paketti", + "UnknownError": "Tuntematon virhe" } } diff --git a/snappymail/v/0.0.0/app/localization/fi-FI/user.json b/snappymail/v/0.0.0/app/localization/fi-FI/user.json index 61de7d2f1..6477d0fee 100644 --- a/snappymail/v/0.0.0/app/localization/fi-FI/user.json +++ b/snappymail/v/0.0.0/app/localization/fi-FI/user.json @@ -139,6 +139,7 @@ "MENU_VIEW_ORIGINAL": "Näytä lähdekoodi", "HTML_VIEW": "View HTML message", "PLAIN_VIEW": "View plain text message", + "SWAP_COLORS": "Swap default (background)color", "MENU_DOWNLOAD_ORIGINAL": "Lataa .eml tiedostona", "MENU_FILTER_SIMILAR": "Suodata samankaltaiset", "MENU_PRINT": "Tulosta", @@ -283,7 +284,8 @@ "ERROR": "%TYPE% virhe: %ERROR%", "CREATE_SELF_SIGNED": "Create self-signed", "VALID_UNTIL": "Valid until", - "PRIVATE_KEY": "Private key" + "PRIVATE_KEY": "Private key", + "ASK_CRYPTKEY_PASS": "Your login passphrase has changed. Please fill in your old login password to decrypt data" }, "OPENPGP": { "POPUP_IMPORT_TITLE": "Tuo OpenPGP avain", @@ -509,7 +511,7 @@ "TO_MANY_FOLDERS_DESC_2": "Näytetään vain osa.", "HELP_DELETE_FOLDER": "Poista kansio", "HELP_SHOW_HIDE_FOLDER": "Näytä\/Piilota kansio", - "HELP_CHECK_FOR_NEW_MESSAGES": "Tarkista\/Älä tarkista uudet viestit", + "HELP_CHECK_FOR_NEW_MESSAGES": "Tarkista uudet viestit", "HIDE_UNSUBSCRIBED": "Hide unsubscribed folders", "UNHIDE_KOLAB_FOLDERS": "Unhide Kolab folders", "TYPE_CALENDAR": "Calendar", @@ -575,42 +577,43 @@ "LABEL_CLOSE_COMPOSE": "Sule luo viesti" }, "NOTIFICATIONS": { - "REQUEST_ERROR": "Request failed", - "REQUEST_ABORTED": "Request is aborted", - "REQUEST_TIMEOUT": "Request timed out", - "INVALID_TOKEN": "Väärä suojaustunnus", - "AUTH_ERROR": "Tunnistusvirhe", - "CONNECTION_ERROR": "Ei voi yhdistää palvelimeen", - "DOMAIN_ALREADY_EXISTS": "Verkkotunnus on jo olemassa", - "DOMAIN_NOT_ALLOWED": "Verkkotunnus ei ole sallittu", - "ACCOUNT_NOT_ALLOWED": "Tili ei ole sallittu", - "CONTACTS_SYNC_ERROR": "Yhteystietojen synkronointivirhe", - "CANT_GET_MESSAGE_LIST": "Viestilistaa ei voi näyttää", - "CANT_GET_MESSAGE": "Viestiä ei voi näyttää", - "CANT_DELETE_MESSAGE": "Viestiä ei voi poistaa", - "CANT_COPY_MESSAGE": "Can't copy message", - "CANT_MOVE_MESSAGE": "Viestiä ei voi siirtää", - "CANT_SAVE_MESSAGE": "Viestiä ei voi tallentaa", - "CANT_SEND_MESSAGE": "Viestiä ei voi lähettää", - "INVALID_RECIPIENTS": "Vastaanottajassa virhe", - "CANT_SAVE_FILTERS": "Suodattimia ei voi tallentaa", - "CANT_GET_FILTERS": "Suodattimia ei voi ladata", - "CANT_ACTIVATE_FILTERS_SCRIPT": "Can't activate filters script", - "CANT_DELETE_FILTERS_SCRIPT": "Can't delete filters script", - "FILTERS_ARE_NOT_CORRECT": "Suodattimet väärin", - "CANT_CREATE_FOLDER": "Kansiota ei voi luoda", - "CANT_RENAME_FOLDER": "Kansiota ei voi uudelleennimetä", - "CANT_DELETE_FOLDER": "Kansiota ei voi poistaa", - "CANT_DELETE_NON_EMPTY_FOLDER": "Kansiossa on viestejä, ei voi poistaa", - "CANT_SUBSCRIBE_FOLDER": "Kansiota ei voi tilata", - "CANT_UNSUBSCRIBE_FOLDER": "Kansiotilausta ei voi peruuttaa", - "DEMO_SEND_MESSAGE_ERROR": "Turvallisuus syistä tältä tililtä ei voi lähettää viestejä ulkopuolisiin osoitteisiin!", - "DEMO_ACCOUNT_ERROR": "Turvallisuus syistä tämä ei ole sallittua!", - "ACCOUNT_ALREADY_EXISTS": "Tili on jo olemassa.", - "ACCOUNT_DOES_NOT_EXIST": "Tiliä ei ole olemassa", - "ACCOUNT_SWITCH_FAILED": "Switch to account \"%EMAIL%\" failed", - "MAIL_SERVER_ERROR": "Palvelinyhteydessä tapahtui virhe", - "INVALID_INPUT_ARGUMENT": "Virheelinen komento", - "UNKNOWN_ERROR": "Tuntematon virhe" + "RequestError": "Request failed", + "RequestAborted": "Request is aborted", + "RequestTimeout": "Request timed out", + "InvalidToken": "Väärä suojaustunnus", + "AuthError": "Tunnistusvirhe", + "ConnectionError": "Ei voi yhdistää palvelimeen", + "DomainAlreadyExists": "Verkkotunnus on jo olemassa", + "DomainNotAllowed": "Verkkotunnus ei ole sallittu", + "AccountNotAllowed": "Tili ei ole sallittu", + "CryptKeyError": "Your login passphrase has changed", + "ContactsSyncError": "Yhteystietojen synkronointivirhe", + "CantGetMessageList": "Viestilistaa ei voi näyttää", + "CantGetMessage": "Viestiä ei voi näyttää", + "CantDeleteMessage": "Viestiä ei voi poistaa", + "CantCopyMessage": "Can't copy message", + "CantMoveMessage": "Viestiä ei voi siirtää", + "CantSaveMessage": "Viestiä ei voi tallentaa", + "CantSendMessage": "Viestiä ei voi lähettää", + "InvalidRecipients": "Vastaanottajassa virhe", + "CantSaveFilters": "Suodattimia ei voi tallentaa", + "CantGetFilters": "Suodattimia ei voi ladata", + "CantActivateFiltersScript": "Can't activate filters script", + "CantDeleteFiltersScript": "Can't delete filters script", + "FiltersAreNotCorrect": "Suodattimet väärin", + "CantCreateFolder": "Kansiota ei voi luoda", + "CantRenameFolder": "Kansiota ei voi uudelleennimetä", + "CantDeleteFolder": "Kansiota ei voi poistaa", + "CantDeleteNonEmptyFolder": "Kansiossa on viestejä, ei voi poistaa", + "CantSubscribeFolder": "Kansiota ei voi tilata", + "CantUnsubscribeFolder": "Kansiotilausta ei voi peruuttaa", + "DemoSendMessageError": "Turvallisuus syistä tältä tililtä ei voi lähettää viestejä ulkopuolisiin osoitteisiin!", + "DemoAccountError": "Turvallisuus syistä tämä ei ole sallittua!", + "AccountAlreadyExists": "Tili on jo olemassa.", + "AccountDoesNotExist": "Tiliä ei ole olemassa", + "AccountSwitchFailed": "Switch to account \"%EMAIL%\" failed", + "MailServerError": "Palvelinyhteydessä tapahtui virhe", + "InvalidInputArgument": "Virheelinen komento", + "UnknownError": "Tuntematon virhe" } } diff --git a/snappymail/v/0.0.0/app/localization/fr-FR/admin.json b/snappymail/v/0.0.0/app/localization/fr-FR/admin.json index ae74e7f74..95c08c7c0 100644 --- a/snappymail/v/0.0.0/app/localization/fr-FR/admin.json +++ b/snappymail/v/0.0.0/app/localization/fr-FR/admin.json @@ -69,7 +69,7 @@ "LABEL_STORAGE_DSN": "Dsn", "LABEL_STORAGE_USER": "Utilisateur", "LABEL_STORAGE_PASSWORD": "Mot de passe", - "SQLITE_GLOBAL": "Use a global DB instead of per account", + "SQLITE_GLOBAL": "Utilisez une DB globale au lieu de par compte", "ALERT_NOTICE": "Note !", "SUGGESTIONS_LIMIT": "Limite de suggestions", "HTML_ALERT_DO_NOT_USE_THIS_DATABASE": "N'utilisez pas ce type de base de données avec un grand nombre d'utilisateurs actifs.", @@ -85,13 +85,14 @@ "TAB_SECURITY": { "LEGEND_SECURITY": "Sécurité", "LABEL_USE_IMAGE_PROXY": "Utiliser un proxy local pour les images externes", - "LABEL_ALLOW_OPENPGP": "Autoriser OpenPGP", + "LABEL_AUTO_VERIFY": "Vérifier automatiquement les signatures", "LABEL_SHOW_PHP_INFO": "Voir les informations PHP", "LEGEND_ADMIN_PANEL_ACCESS_CREDENTIALS": "Codes d'accès au panneau d'administration", "LABEL_CURRENT_PASSWORD": "Mot de passe actuel", "LABEL_NEW_PASSWORD": "Nouveau mot de passe", "LABEL_REPEAT_PASSWORD": "Répéter", "LABEL_REQUIRE_VERIFICATION": "S'assurer qu'un certificat SSL est utilisé", + "CRYPTO": "Chiffrement", "LABEL_ALLOW_SELF_SIGNED": "Autoriser les certificats auto-signés" }, "TAB_PACKAGES": { @@ -135,6 +136,7 @@ "SECURE_OPTION_NONE": "Aucun", "LABEL_ALLOW_SIEVE_SCRIPTS": "Autoriser les scripts sieve", "LABEL_USE_SHORT_LOGIN": "Utiliser l'identifiant court", + "LABEL_LOWERCASE_LOGIN": "Lowercase login (case-insensitive)", "LABEL_USE_AUTH": "Utiliser l'authentification", "LABEL_SET_SENDER": "Utiliser la connexion en tant qu'expéditeur", "LABEL_AUTH_PLAIN_LINE": "Forcer l'authentification AUTH PLAIN", @@ -168,17 +170,17 @@ "UNKNOWN_ERROR": "Erreur inconnue" }, "NOTIFICATIONS": { - "REQUEST_ERROR": "La requête a échouée", - "REQUEST_ABORTED": "La requête a été annulée", - "REQUEST_TIMEOUT": "La requête a expirée", - "INVALID_TOKEN": "Jeton invalide", - "AUTH_ERROR": "L'authentification a échoué", - "DOMAIN_ALREADY_EXISTS": "Le domaine existe déjà", - "CANT_SAVE_PLUGIN_SETTINGS": "Vous ne pouvez pas enregistrer les paramètres", - "CANT_INSTALL_PACKAGE": "Impossible d'installer le extension", - "CANT_DELETE_PACKAGE": "Impossible de supprimer le extension", - "INVALID_PLUGIN_PACKAGE": "Package d'extension non valide", - "UNSUPPORTED_PLUGIN_PACKAGE": "Package d'extension non pris en charge", - "UNKNOWN_ERROR": "Erreur inconnue" + "RequestError": "La requête a échouée", + "RequestAborted": "La requête a été annulée", + "RequestTimeout": "La requête a expirée", + "InvalidToken": "Jeton invalide", + "AuthError": "L'authentification a échoué", + "DomainAlreadyExists": "Le domaine existe déjà", + "CantSavePluginSettings": "Vous ne pouvez pas enregistrer les paramètres", + "CantInstallPackage": "Impossible d'installer le extension", + "CantDeletePackage": "Impossible de supprimer le extension", + "InvalidPluginPackage": "Package d'extension non valide", + "UnsupportedPluginPackage": "Package d'extension non pris en charge", + "UnknownError": "Erreur inconnue" } } diff --git a/snappymail/v/0.0.0/app/localization/fr-FR/user.json b/snappymail/v/0.0.0/app/localization/fr-FR/user.json index a7fb60907..e50e75440 100644 --- a/snappymail/v/0.0.0/app/localization/fr-FR/user.json +++ b/snappymail/v/0.0.0/app/localization/fr-FR/user.json @@ -139,6 +139,7 @@ "MENU_VIEW_ORIGINAL": "Voir la source", "HTML_VIEW": "Afficher le message HTML", "PLAIN_VIEW": "Afficher le message en texte brut", + "SWAP_COLORS": "Swap default (background)color", "MENU_DOWNLOAD_ORIGINAL": "Télécharger comme fichier .eml", "MENU_FILTER_SIMILAR": "Filtrer les messages similaires", "MENU_PRINT": "Imprimer", @@ -212,7 +213,7 @@ "ADD_MENU_TAGS": "Étiquettes", "BUTTON_SHARE_ALL": "Tout le monde", "BUTTON_SYNC": "Synchronisation (CardDAV)", - "SEND_TO_ALL_CONTACT_EMAILS": "Send to all contact emails" + "SEND_TO_ALL_CONTACT_EMAILS": "Envoyer à tous les e-mails contact" }, "COMPOSE": { "LINK_SHOW_INPUTS": "Voir tous les champs", @@ -282,8 +283,9 @@ "GOOD_SIGNATURE": "Signature valide pour %USER%", "ERROR": "Erreur %TYPE%: %ERROR%", "CREATE_SELF_SIGNED": "Create self-signed", - "VALID_UNTIL": "Valid until", - "PRIVATE_KEY": "Private key" + "VALID_UNTIL": "Valable jusque", + "PRIVATE_KEY": "Clé privée", + "ASK_CRYPTKEY_PASS": "Your login passphrase has changed. Please fill in your old login password to decrypt data" }, "OPENPGP": { "POPUP_IMPORT_TITLE": "Importer la clef OpenPGP", @@ -296,19 +298,19 @@ "SIGNED_MESSAGE": "Message signé par OpenPGP", "ENCRYPTED_MESSAGE": "Message chiffré par OpenPGP", "STORE_IN_GNUPG": "Stocker sur le serveur dans GnuPG", - "STORE_PUBLIC_KEY_IN_GNUPG": "Store public key on server in GnuPG", - "STORE_PRIVATE_KEY_IN_GNUPG": "Store private key on server in GnuPG", + "STORE_PUBLIC_KEY_IN_GNUPG": "Stocker la clé publique sur le serveur dans GnuPG", + "STORE_PRIVATE_KEY_IN_GNUPG": "Stocker la clé privée sur le serveur dans GnuPG", "BACKUP_ON_SERVER": "Stocker (chiffré) sur le serveur", - "BACKUP_PUBLIC_KEY_ON_SERVER": "Backup public key on server", - "BACKUP_PRIVATE_KEY_ON_SERVER": "Backup private key on server" + "BACKUP_PUBLIC_KEY_ON_SERVER": "Clé publique de sauvegarde sur le serveur", + "BACKUP_PRIVATE_KEY_ON_SERVER": "Clé privée de sauvegarde sur le serveur" }, "SMIME": { - "POPUP_IMPORT_TITLE": "Import S\/MIME certificate", - "CERTIFICATE": "Certificate", - "CERTIFICATES": "S\/MIME Certificates", + "POPUP_IMPORT_TITLE": "Importer le certificat S\/MIME", + "CERTIFICATE": "Certificat", + "CERTIFICATES": "Certificats S\/MIME", "SIGNED_MESSAGE": "Message signé par S\/MIME", "ENCRYPTED_MESSAGE": "Message chiffré par S\/MIME", - "PRIVATE_KEY_OF": "S\/MIME private key of %EMAIL%" + "PRIVATE_KEY_OF": "Clé privée S\/MIME de %EMAIL%" }, "POPUPS_FILTER": { "TITLE_CREATE_FILTER": "Créer un filtre ?", @@ -509,7 +511,7 @@ "TO_MANY_FOLDERS_DESC_2": "Seulement une partie d'entre eux ont été affichés afin de limiter les problèmes de performance.", "HELP_DELETE_FOLDER": "Supprimer le dossier", "HELP_SHOW_HIDE_FOLDER": "Afficher\/masquer le dossier", - "HELP_CHECK_FOR_NEW_MESSAGES": "Cocher\/Décocher pour les nouveaux messages", + "HELP_CHECK_FOR_NEW_MESSAGES": "Cocher pour les nouveaux messages", "HIDE_UNSUBSCRIBED": "Masquer les dossiers désabonnés", "UNHIDE_KOLAB_FOLDERS": "Afficher les dossiers Kolab", "TYPE_CALENDAR": "Calendrier", @@ -532,7 +534,7 @@ "SETTINGS_OPENPGP": { "BUTTON_IMPORT_KEY": "Importer la clé", "BUTTON_GENERATE_KEY_PAIR": "Générer les clés", - "IMPORT_FROM_SERVER": "Import from server", + "IMPORT_FROM_SERVER": "Importer depuis le serveur", "TITLE_PRIVATE": "Privée", "TITLE_PUBLIC": "Publique", "GET_MAILVELOPE": "Obtenez le module complémentaire de navigateur Mailvelope" @@ -575,42 +577,43 @@ "LABEL_CLOSE_COMPOSE": "Fermer la fenêtre d'édition" }, "NOTIFICATIONS": { - "REQUEST_ERROR": "La requête a échouée", - "REQUEST_ABORTED": "La requête a été annulée", - "REQUEST_TIMEOUT": "La requête a expirée", - "INVALID_TOKEN": "Jeton invalide", - "AUTH_ERROR": "L'authentification a échoué", - "CONNECTION_ERROR": "Serveur injoignable", - "DOMAIN_ALREADY_EXISTS": "Le domaine existe déjà", - "DOMAIN_NOT_ALLOWED": "Ce domaine n'est pas autorisé", - "ACCOUNT_NOT_ALLOWED": "Ce compte n'est pas autorisé", - "CONTACTS_SYNC_ERROR": "Erreur de synchronisation des contacts", - "CANT_GET_MESSAGE_LIST": "Impossible d'obtenir la liste des messages", - "CANT_GET_MESSAGE": "Impossible d'obtenir le message", - "CANT_DELETE_MESSAGE": "Impossible de supprimer le message", - "CANT_COPY_MESSAGE": "Impossible de copier le message", - "CANT_MOVE_MESSAGE": "Impossible de déplacer le message", - "CANT_SAVE_MESSAGE": "Impossible d'enregistrer le message", - "CANT_SEND_MESSAGE": "Impossible d'envoyer le message", - "INVALID_RECIPIENTS": "Destinataires invalides", - "CANT_SAVE_FILTERS": "Impossible d'enregistrer les filtres", - "CANT_GET_FILTERS": "Impossible d'obtenir les filtres", - "CANT_ACTIVATE_FILTERS_SCRIPT": "Impossible d'activer le script des filtres", - "CANT_DELETE_FILTERS_SCRIPT": "Impossible de supprimer le script des filtres", - "FILTERS_ARE_NOT_CORRECT": "Les filtres ne sont pas corrects", - "CANT_CREATE_FOLDER": "Impossible de créer le dossier", - "CANT_RENAME_FOLDER": "Impossible de renommer le dossier", - "CANT_DELETE_FOLDER": "Impossible de supprimer le dossier", - "CANT_DELETE_NON_EMPTY_FOLDER": "Impossible de supprimer un dossier non vide", - "CANT_SUBSCRIBE_FOLDER": "Impossible de s'abonner au dossier", - "CANT_UNSUBSCRIBE_FOLDER": "Impossible de se désabonner du dossier", - "DEMO_SEND_MESSAGE_ERROR": "Pour des raisons de sécurité, ce compte de démonstration n'est pas autorisé à envoyer des messages à des adresses e-mail externes !", - "DEMO_ACCOUNT_ERROR": "Pour des raisons de sécurité, ce compte n'est pas autorisé à faire cette action !", - "ACCOUNT_ALREADY_EXISTS": "Ce compte existe déjà", - "ACCOUNT_DOES_NOT_EXIST": "Ce compte n'existe pas", - "ACCOUNT_SWITCH_FAILED": "Switch vers le compte \"%EMAIL%\" a échoué", - "MAIL_SERVER_ERROR": "Une erreur est survenue lors de l'accès au serveur mail", - "INVALID_INPUT_ARGUMENT": "Argument invalide", - "UNKNOWN_ERROR": "Erreur inconnue" + "RequestError": "La requête a échouée", + "RequestAborted": "La requête a été annulée", + "RequestTimeout": "La requête a expirée", + "InvalidToken": "Jeton invalide", + "AuthError": "L'authentification a échoué", + "ConnectionError": "Serveur injoignable", + "DomainAlreadyExists": "Le domaine existe déjà", + "DomainNotAllowed": "Ce domaine n'est pas autorisé", + "AccountNotAllowed": "Ce compte n'est pas autorisé", + "CryptKeyError": "Your login passphrase has changed", + "ContactsSyncError": "Erreur de synchronisation des contacts", + "CantGetMessageList": "Impossible d'obtenir la liste des messages", + "CantGetMessage": "Impossible d'obtenir le message", + "CantDeleteMessage": "Impossible de supprimer le message", + "CantCopyMessage": "Impossible de copier le message", + "CantMoveMessage": "Impossible de déplacer le message", + "CantSaveMessage": "Impossible d'enregistrer le message", + "CantSendMessage": "Impossible d'envoyer le message", + "InvalidRecipients": "Destinataires invalides", + "CantSaveFilters": "Impossible d'enregistrer les filtres", + "CantGetFilters": "Impossible d'obtenir les filtres", + "CantActivateFiltersScript": "Impossible d'activer le script des filtres", + "CantDeleteFiltersScript": "Impossible de supprimer le script des filtres", + "FiltersAreNotCorrect": "Les filtres ne sont pas corrects", + "CantCreateFolder": "Impossible de créer le dossier", + "CantRenameFolder": "Impossible de renommer le dossier", + "CantDeleteFolder": "Impossible de supprimer le dossier", + "CantDeleteNonEmptyFolder": "Impossible de supprimer un dossier non vide", + "CantSubscribeFolder": "Impossible de s'abonner au dossier", + "CantUnsubscribeFolder": "Impossible de se désabonner du dossier", + "DemoSendMessageError": "Pour des raisons de sécurité, ce compte de démonstration n'est pas autorisé à envoyer des messages à des adresses e-mail externes !", + "DemoAccountError": "Pour des raisons de sécurité, ce compte n'est pas autorisé à faire cette action !", + "AccountAlreadyExists": "Ce compte existe déjà", + "AccountDoesNotExist": "Ce compte n'existe pas", + "AccountSwitchFailed": "Switch vers le compte \"%EMAIL%\" a échoué", + "MailServerError": "Une erreur est survenue lors de l'accès au serveur mail", + "InvalidInputArgument": "Argument invalide", + "UnknownError": "Erreur inconnue" } } diff --git a/snappymail/v/0.0.0/app/localization/hu-HU/admin.json b/snappymail/v/0.0.0/app/localization/hu-HU/admin.json index f400c4a73..161fe5c6c 100644 --- a/snappymail/v/0.0.0/app/localization/hu-HU/admin.json +++ b/snappymail/v/0.0.0/app/localization/hu-HU/admin.json @@ -85,13 +85,14 @@ "TAB_SECURITY": { "LEGEND_SECURITY": "Biztonság", "LABEL_USE_IMAGE_PROXY": "Helyi proxy használata a külső képekhez", - "LABEL_ALLOW_OPENPGP": "OpenPGP engedélyezése", + "LABEL_AUTO_VERIFY": "Verify signatures automatically", "LABEL_SHOW_PHP_INFO": "PHP információ megjelenítése", "LEGEND_ADMIN_PANEL_ACCESS_CREDENTIALS": "Admin panel hozzáférés hitelesítés", "LABEL_CURRENT_PASSWORD": "Jelenlegi jelszó", "LABEL_NEW_PASSWORD": "Új jelszó", "LABEL_REPEAT_PASSWORD": "Újra", "LABEL_REQUIRE_VERIFICATION": "A használt tanúsítvány ellenőrzés megkövetelése", + "CRYPTO": "Crypto", "LABEL_ALLOW_SELF_SIGNED": "Saját aláírt tanúsítványok engedélyezése" }, "TAB_PACKAGES": { @@ -135,6 +136,7 @@ "SECURE_OPTION_NONE": "Semmi", "LABEL_ALLOW_SIEVE_SCRIPTS": "SIEVE szkriptek engedélyezése", "LABEL_USE_SHORT_LOGIN": "Rövid felhasználónév használata", + "LABEL_LOWERCASE_LOGIN": "Lowercase login (case-insensitive)", "LABEL_USE_AUTH": "Hitelesítés használata", "LABEL_SET_SENDER": "Use login as sender", "LABEL_AUTH_PLAIN_LINE": "Force single command AUTH PLAIN login", @@ -168,17 +170,17 @@ "UNKNOWN_ERROR": "Ismeretlen hiba" }, "NOTIFICATIONS": { - "REQUEST_ERROR": "Request failed", - "REQUEST_ABORTED": "Request is aborted", - "REQUEST_TIMEOUT": "Request timed out", - "INVALID_TOKEN": "Érvénytelen token", - "AUTH_ERROR": "Sikertelen hitelesítés", - "DOMAIN_ALREADY_EXISTS": "A domain már létezik", - "CANT_SAVE_PLUGIN_SETTINGS": "Nem lehet menteni a beállításokat", - "CANT_INSTALL_PACKAGE": "Nem sikerült telepíteni a bővítményt", - "CANT_DELETE_PACKAGE": "Nem sikerült eltávolítani a bővítményt", - "INVALID_PLUGIN_PACKAGE": "Érvénytelen kiterjesztési csomag", - "UNSUPPORTED_PLUGIN_PACKAGE": "Nem támogatott bővítménycsomag", - "UNKNOWN_ERROR": "Ismeretlen hiba" + "RequestError": "Request failed", + "RequestAborted": "Request is aborted", + "RequestTimeout": "Request timed out", + "InvalidToken": "Érvénytelen token", + "AuthError": "Sikertelen hitelesítés", + "DomainAlreadyExists": "A domain már létezik", + "CantSavePluginSettings": "Nem lehet menteni a beállításokat", + "CantInstallPackage": "Nem sikerült telepíteni a bővítményt", + "CantDeletePackage": "Nem sikerült eltávolítani a bővítményt", + "InvalidPluginPackage": "Érvénytelen kiterjesztési csomag", + "UnsupportedPluginPackage": "Nem támogatott bővítménycsomag", + "UnknownError": "Ismeretlen hiba" } } diff --git a/snappymail/v/0.0.0/app/localization/hu-HU/user.json b/snappymail/v/0.0.0/app/localization/hu-HU/user.json index e52ba69b0..da85c7b75 100644 --- a/snappymail/v/0.0.0/app/localization/hu-HU/user.json +++ b/snappymail/v/0.0.0/app/localization/hu-HU/user.json @@ -139,6 +139,7 @@ "MENU_VIEW_ORIGINAL": "Eredeti megjelenítése", "HTML_VIEW": "View HTML message", "PLAIN_VIEW": "View plain text message", + "SWAP_COLORS": "Swap default (background)color", "MENU_DOWNLOAD_ORIGINAL": ".eml fájl letöltés", "MENU_FILTER_SIMILAR": "Hasonló üzenetek szűrése", "MENU_PRINT": "Nyomtatás", @@ -283,7 +284,8 @@ "ERROR": "%TYPE% hiba: %ERROR%", "CREATE_SELF_SIGNED": "Create self-signed", "VALID_UNTIL": "Valid until", - "PRIVATE_KEY": "Private key" + "PRIVATE_KEY": "Private key", + "ASK_CRYPTKEY_PASS": "Your login passphrase has changed. Please fill in your old login password to decrypt data" }, "OPENPGP": { "POPUP_IMPORT_TITLE": "OpenPGP kulcs importálás", @@ -509,7 +511,7 @@ "TO_MANY_FOLDERS_DESC_2": "Teljesítény problémák elkerülése miatt, csak egy részét jelenítjük meg.", "HELP_DELETE_FOLDER": "Mappa törlés", "HELP_SHOW_HIDE_FOLDER": "Mappa mutatás\/elrejtés", - "HELP_CHECK_FOR_NEW_MESSAGES": "Ellenőrizze\/ne ellenőrizze az új üzeneteket", + "HELP_CHECK_FOR_NEW_MESSAGES": "Ellenőrizze az új üzeneteket", "HIDE_UNSUBSCRIBED": "Hide unsubscribed folders", "UNHIDE_KOLAB_FOLDERS": "Unhide Kolab folders", "TYPE_CALENDAR": "Naptár", @@ -575,42 +577,43 @@ "LABEL_CLOSE_COMPOSE": "Levélírás bezárása" }, "NOTIFICATIONS": { - "REQUEST_ERROR": "Request failed", - "REQUEST_ABORTED": "Request is aborted", - "REQUEST_TIMEOUT": "Request timed out", - "INVALID_TOKEN": "Érvénytelen kulcs", - "AUTH_ERROR": "Érvénytelen hitelesítés", - "CONNECTION_ERROR": "Nem lehet kapcsolódni a szerverhez", - "DOMAIN_ALREADY_EXISTS": "A domain már létezik", - "DOMAIN_NOT_ALLOWED": "A domain nem engedélyezett", - "ACCOUNT_NOT_ALLOWED": "A fiók nem engedélyezett", - "CONTACTS_SYNC_ERROR": "Hiba lépett fel a névjegyek szinkronizálása közben", - "CANT_GET_MESSAGE_LIST": "Nem tudom letölteni az üzenetlistát", - "CANT_GET_MESSAGE": "Nem tudom letölteni az üzenetet", - "CANT_DELETE_MESSAGE": "Nem lehet törölni az üzenetet", - "CANT_COPY_MESSAGE": "Can't copy message", - "CANT_MOVE_MESSAGE": "Nem lehet áthelyezni az üzenetet", - "CANT_SAVE_MESSAGE": "Nem lehet menteni az üzenetet", - "CANT_SEND_MESSAGE": "Nem lehet elküldeni az üzenetet", - "INVALID_RECIPIENTS": "Érvénytelent címzettek", - "CANT_SAVE_FILTERS": "Nem lehet menteni a szűrőket", - "CANT_GET_FILTERS": "Nem lehet letölteni a szűrőket", - "CANT_ACTIVATE_FILTERS_SCRIPT": "Can't activate filters script", - "CANT_DELETE_FILTERS_SCRIPT": "Can't delete filters script", - "FILTERS_ARE_NOT_CORRECT": "A szűrők nem megfelelőek", - "CANT_CREATE_FOLDER": "Nem lehet létrehozni a mappát", - "CANT_RENAME_FOLDER": "Nem lehet átnevezni a mappát", - "CANT_DELETE_FOLDER": "Nem lehet törölni a mappát", - "CANT_DELETE_NON_EMPTY_FOLDER": "Nem üres mappát nem lehet törölni", - "CANT_SUBSCRIBE_FOLDER": "Nem lehet feliratkozni mappára", - "CANT_UNSUBSCRIBE_FOLDER": "Nem lehet leiratkozni mappáról", - "DEMO_SEND_MESSAGE_ERROR": "Biztonsági okokból, ebből a demó fiókból nem lehet külső e-mail címekre üzenetet küldeni.", - "DEMO_ACCOUNT_ERROR": "Biztonsági okokból, ebből a fiókból nem lehet elvégezni ezt a műveletet!", - "ACCOUNT_ALREADY_EXISTS": "A fiók már létezik", - "ACCOUNT_DOES_NOT_EXIST": "A fiók nem létezik", - "ACCOUNT_SWITCH_FAILED": "Switch to account \"%EMAIL%\" failed", - "MAIL_SERVER_ERROR": "Hiba történt a levelező szerverhez történő hozzáférés közben", - "INVALID_INPUT_ARGUMENT": "Érvénytelen input argumentum", - "UNKNOWN_ERROR": "Ismeretlen hiba" + "RequestError": "Request failed", + "RequestAborted": "Request is aborted", + "RequestTimeout": "Request timed out", + "InvalidToken": "Érvénytelen kulcs", + "AuthError": "Érvénytelen hitelesítés", + "ConnectionError": "Nem lehet kapcsolódni a szerverhez", + "DomainAlreadyExists": "A domain már létezik", + "DomainNotAllowed": "A domain nem engedélyezett", + "AccountNotAllowed": "A fiók nem engedélyezett", + "CryptKeyError": "Your login passphrase has changed", + "ContactsSyncError": "Hiba lépett fel a névjegyek szinkronizálása közben", + "CantGetMessageList": "Nem tudom letölteni az üzenetlistát", + "CantGetMessage": "Nem tudom letölteni az üzenetet", + "CantDeleteMessage": "Nem lehet törölni az üzenetet", + "CantCopyMessage": "Can't copy message", + "CantMoveMessage": "Nem lehet áthelyezni az üzenetet", + "CantSaveMessage": "Nem lehet menteni az üzenetet", + "CantSendMessage": "Nem lehet elküldeni az üzenetet", + "InvalidRecipients": "Érvénytelent címzettek", + "CantSaveFilters": "Nem lehet menteni a szűrőket", + "CantGetFilters": "Nem lehet letölteni a szűrőket", + "CantActivateFiltersScript": "Can't activate filters script", + "CantDeleteFiltersScript": "Can't delete filters script", + "FiltersAreNotCorrect": "A szűrők nem megfelelőek", + "CantCreateFolder": "Nem lehet létrehozni a mappát", + "CantRenameFolder": "Nem lehet átnevezni a mappát", + "CantDeleteFolder": "Nem lehet törölni a mappát", + "CantDeleteNonEmptyFolder": "Nem üres mappát nem lehet törölni", + "CantSubscribeFolder": "Nem lehet feliratkozni mappára", + "CantUnsubscribeFolder": "Nem lehet leiratkozni mappáról", + "DemoSendMessageError": "Biztonsági okokból, ebből a demó fiókból nem lehet külső e-mail címekre üzenetet küldeni.", + "DemoAccountError": "Biztonsági okokból, ebből a fiókból nem lehet elvégezni ezt a műveletet!", + "AccountAlreadyExists": "A fiók már létezik", + "AccountDoesNotExist": "A fiók nem létezik", + "AccountSwitchFailed": "Switch to account \"%EMAIL%\" failed", + "MailServerError": "Hiba történt a levelező szerverhez történő hozzáférés közben", + "InvalidInputArgument": "Érvénytelen input argumentum", + "UnknownError": "Ismeretlen hiba" } } diff --git a/snappymail/v/0.0.0/app/localization/id-ID/admin.json b/snappymail/v/0.0.0/app/localization/id-ID/admin.json index 9dd0666d0..b18ac3de1 100644 --- a/snappymail/v/0.0.0/app/localization/id-ID/admin.json +++ b/snappymail/v/0.0.0/app/localization/id-ID/admin.json @@ -85,13 +85,14 @@ "TAB_SECURITY": { "LEGEND_SECURITY": "Keamanan", "LABEL_USE_IMAGE_PROXY": "Gunakan proxy lokal untuk gambar dari luar", - "LABEL_ALLOW_OPENPGP": "Izinkan OpenPGP", + "LABEL_AUTO_VERIFY": "Verify signatures automatically", "LABEL_SHOW_PHP_INFO": "Tampilkan informasi PHP", "LEGEND_ADMIN_PANEL_ACCESS_CREDENTIALS": "Hak Akses Panel Admin", "LABEL_CURRENT_PASSWORD": "Password saat ini", "LABEL_NEW_PASSWORD": "Password baru", "LABEL_REPEAT_PASSWORD": "Ulangi", "LABEL_REQUIRE_VERIFICATION": "Diperlukan verifikasi sertifikat SSL dengan", + "CRYPTO": "Crypto", "LABEL_ALLOW_SELF_SIGNED": "Izinkan sertifikat yang dibuat sendiri" }, "TAB_PACKAGES": { @@ -135,6 +136,7 @@ "SECURE_OPTION_NONE": "Tidak satupun", "LABEL_ALLOW_SIEVE_SCRIPTS": "Izinkan script sieve", "LABEL_USE_SHORT_LOGIN": "Gunakan login singkat", + "LABEL_LOWERCASE_LOGIN": "Lowercase login (case-insensitive)", "LABEL_USE_AUTH": "Gunakan otentikasi", "LABEL_SET_SENDER": "Use login as sender", "LABEL_AUTH_PLAIN_LINE": "Force single command AUTH PLAIN login", @@ -168,17 +170,17 @@ "UNKNOWN_ERROR": "Kesalahan tidak diketahui" }, "NOTIFICATIONS": { - "REQUEST_ERROR": "Request failed", - "REQUEST_ABORTED": "Request is aborted", - "REQUEST_TIMEOUT": "Request timed out", - "INVALID_TOKEN": "Token invalid", - "AUTH_ERROR": "Otentikasi gagal", - "DOMAIN_ALREADY_EXISTS": "Domain sudah ada", - "CANT_SAVE_PLUGIN_SETTINGS": "Tidak bisa menyimpan pengaturan", - "CANT_INSTALL_PACKAGE": "Gagal memasang paket", - "CANT_DELETE_PACKAGE": "Gagal menghapus paket", - "INVALID_PLUGIN_PACKAGE": "Paket plugin tidak sah", - "UNSUPPORTED_PLUGIN_PACKAGE": "Paket plugin tidak didukung", - "UNKNOWN_ERROR": "Kesalahan tidak diketahui" + "RequestError": "Request failed", + "RequestAborted": "Request is aborted", + "RequestTimeout": "Request timed out", + "InvalidToken": "Token invalid", + "AuthError": "Otentikasi gagal", + "DomainAlreadyExists": "Domain sudah ada", + "CantSavePluginSettings": "Tidak bisa menyimpan pengaturan", + "CantInstallPackage": "Gagal memasang paket", + "CantDeletePackage": "Gagal menghapus paket", + "InvalidPluginPackage": "Paket plugin tidak sah", + "UnsupportedPluginPackage": "Paket plugin tidak didukung", + "UnknownError": "Kesalahan tidak diketahui" } } diff --git a/snappymail/v/0.0.0/app/localization/id-ID/user.json b/snappymail/v/0.0.0/app/localization/id-ID/user.json index a80534b95..c0b725c8c 100644 --- a/snappymail/v/0.0.0/app/localization/id-ID/user.json +++ b/snappymail/v/0.0.0/app/localization/id-ID/user.json @@ -139,6 +139,7 @@ "MENU_VIEW_ORIGINAL": "Tampilkan kode sumber", "HTML_VIEW": "View HTML message", "PLAIN_VIEW": "View plain text message", + "SWAP_COLORS": "Swap default (background)color", "MENU_DOWNLOAD_ORIGINAL": "Unduh sebagai berkas .eml", "MENU_FILTER_SIMILAR": "Saring pesan seperti ini", "MENU_PRINT": "Cetak", @@ -283,7 +284,8 @@ "ERROR": "Kesalahan %TYPE%: %ERROR%", "CREATE_SELF_SIGNED": "Create self-signed", "VALID_UNTIL": "Valid until", - "PRIVATE_KEY": "Private key" + "PRIVATE_KEY": "Private key", + "ASK_CRYPTKEY_PASS": "Your login passphrase has changed. Please fill in your old login password to decrypt data" }, "OPENPGP": { "POPUP_IMPORT_TITLE": "Impor kunci OpenPGP", @@ -509,7 +511,7 @@ "TO_MANY_FOLDERS_DESC_2": "Kami hanya menampilkan sebagian, untuk mencegah turunnya performa.", "HELP_DELETE_FOLDER": "Hapus folder", "HELP_SHOW_HIDE_FOLDER": "Tampil\/lenyapkan folder", - "HELP_CHECK_FOR_NEW_MESSAGES": "Cek\/jangan cek pesan baru", + "HELP_CHECK_FOR_NEW_MESSAGES": "Cek pesan baru", "HIDE_UNSUBSCRIBED": "Hide unsubscribed folders", "UNHIDE_KOLAB_FOLDERS": "Unhide Kolab folders", "TYPE_CALENDAR": "Calendar", @@ -575,42 +577,43 @@ "LABEL_CLOSE_COMPOSE": "Tutup buat pesan" }, "NOTIFICATIONS": { - "REQUEST_ERROR": "Request failed", - "REQUEST_ABORTED": "Request is aborted", - "REQUEST_TIMEOUT": "Request timed out", - "INVALID_TOKEN": "Token tidak sah", - "AUTH_ERROR": "Otentikasi gagal", - "CONNECTION_ERROR": "Tidak terhubung ke server", - "DOMAIN_ALREADY_EXISTS": "Domain telah ada", - "DOMAIN_NOT_ALLOWED": "Domain tidak diizinkan", - "ACCOUNT_NOT_ALLOWED": "Akun ini tidak diizinkan", - "CONTACTS_SYNC_ERROR": "Kesalahan sinkronisasi kontak", - "CANT_GET_MESSAGE_LIST": "Gagal memperoleh daftar pesan", - "CANT_GET_MESSAGE": "Gagal memperoleh pesan", - "CANT_DELETE_MESSAGE": "Gagal menghapus pesan", - "CANT_COPY_MESSAGE": "Can't copy message", - "CANT_MOVE_MESSAGE": "Gagal memindahkan pesan", - "CANT_SAVE_MESSAGE": "Gagal menyimpan pesan", - "CANT_SEND_MESSAGE": "Gagal mengirim pesan", - "INVALID_RECIPIENTS": "Penerima tidak valid", - "CANT_SAVE_FILTERS": "Gagal menyimpan filter", - "CANT_GET_FILTERS": "Gagal memperoleh filter", - "CANT_ACTIVATE_FILTERS_SCRIPT": "Can't activate filters script", - "CANT_DELETE_FILTERS_SCRIPT": "Can't delete filters script", - "FILTERS_ARE_NOT_CORRECT": "Filter tidak benar", - "CANT_CREATE_FOLDER": "Gagal membuat folder", - "CANT_RENAME_FOLDER": "Gagal mengubah nama folder", - "CANT_DELETE_FOLDER": "Gagal menghapus folder", - "CANT_DELETE_NON_EMPTY_FOLDER": "Gagal menghapus direkori yang berisi", - "CANT_SUBSCRIBE_FOLDER": "Gagal mengkaitkan folder", - "CANT_UNSUBSCRIBE_FOLDER": "Tidak bisa memutus folder", - "DEMO_SEND_MESSAGE_ERROR": "Atas alasan keamanan, akun ini tidak diizinkan mengirim pesan ke alamat luar!", - "DEMO_ACCOUNT_ERROR": "Atas alasan keamanan, akun ini tidak diizinkan melakukan aksi ini!", - "ACCOUNT_ALREADY_EXISTS": "Akun sudah ada", - "ACCOUNT_DOES_NOT_EXIST": "Akun tidak ada", - "ACCOUNT_SWITCH_FAILED": "Switch to account \"%EMAIL%\" failed", - "MAIL_SERVER_ERROR": "Terjadi kesalahan saat mengakses server mail", - "INVALID_INPUT_ARGUMENT": "Uraian input tidak sah", - "UNKNOWN_ERROR": "Kesalahan tidak diketahui" + "RequestError": "Request failed", + "RequestAborted": "Request is aborted", + "RequestTimeout": "Request timed out", + "InvalidToken": "Token tidak sah", + "AuthError": "Otentikasi gagal", + "ConnectionError": "Tidak terhubung ke server", + "DomainAlreadyExists": "Domain telah ada", + "DomainNotAllowed": "Domain tidak diizinkan", + "AccountNotAllowed": "Akun ini tidak diizinkan", + "CryptKeyError": "Your login passphrase has changed", + "ContactsSyncError": "Kesalahan sinkronisasi kontak", + "CantGetMessageList": "Gagal memperoleh daftar pesan", + "CantGetMessage": "Gagal memperoleh pesan", + "CantDeleteMessage": "Gagal menghapus pesan", + "CantCopyMessage": "Can't copy message", + "CantMoveMessage": "Gagal memindahkan pesan", + "CantSaveMessage": "Gagal menyimpan pesan", + "CantSendMessage": "Gagal mengirim pesan", + "InvalidRecipients": "Penerima tidak valid", + "CantSaveFilters": "Gagal menyimpan filter", + "CantGetFilters": "Gagal memperoleh filter", + "CantActivateFiltersScript": "Can't activate filters script", + "CantDeleteFiltersScript": "Can't delete filters script", + "FiltersAreNotCorrect": "Filter tidak benar", + "CantCreateFolder": "Gagal membuat folder", + "CantRenameFolder": "Gagal mengubah nama folder", + "CantDeleteFolder": "Gagal menghapus folder", + "CantDeleteNonEmptyFolder": "Gagal menghapus direkori yang berisi", + "CantSubscribeFolder": "Gagal mengkaitkan folder", + "CantUnsubscribeFolder": "Tidak bisa memutus folder", + "DemoSendMessageError": "Atas alasan keamanan, akun ini tidak diizinkan mengirim pesan ke alamat luar!", + "DemoAccountError": "Atas alasan keamanan, akun ini tidak diizinkan melakukan aksi ini!", + "AccountAlreadyExists": "Akun sudah ada", + "AccountDoesNotExist": "Akun tidak ada", + "AccountSwitchFailed": "Switch to account \"%EMAIL%\" failed", + "MailServerError": "Terjadi kesalahan saat mengakses server mail", + "InvalidInputArgument": "Uraian input tidak sah", + "UnknownError": "Kesalahan tidak diketahui" } } diff --git a/snappymail/v/0.0.0/app/localization/is-IS/user.json b/snappymail/v/0.0.0/app/localization/is-IS/user.json index 51d0207a3..aab43f7c3 100644 --- a/snappymail/v/0.0.0/app/localization/is-IS/user.json +++ b/snappymail/v/0.0.0/app/localization/is-IS/user.json @@ -139,6 +139,7 @@ "MENU_VIEW_ORIGINAL": "Sýna upprunalegt", "HTML_VIEW": "View HTML message", "PLAIN_VIEW": "View plain text message", + "SWAP_COLORS": "Swap default (background)color", "MENU_DOWNLOAD_ORIGINAL": "Sækja sem .eml skrá", "MENU_FILTER_SIMILAR": "Sía skilaboð eins og þessi", "MENU_PRINT": "Prenta", @@ -283,7 +284,8 @@ "ERROR": "%TYPE% villa: %ERROR%", "CREATE_SELF_SIGNED": "Create self-signed", "VALID_UNTIL": "Valid until", - "PRIVATE_KEY": "Private key" + "PRIVATE_KEY": "Private key", + "ASK_CRYPTKEY_PASS": "Your login passphrase has changed. Please fill in your old login password to decrypt data" }, "OPENPGP": { "POPUP_IMPORT_TITLE": "Flytja inn OpenPGP-lykil", @@ -509,7 +511,7 @@ "TO_MANY_FOLDERS_DESC_2": "Við höfum birt aðeins hluta þeirra, til að koma í veg fyrir vandamál með afköst.", "HELP_DELETE_FOLDER": "Eyða möppu", "HELP_SHOW_HIDE_FOLDER": "Birta\/fela möppu", - "HELP_CHECK_FOR_NEW_MESSAGES": "Athuga\/ekki athuga með ný skilaboð", + "HELP_CHECK_FOR_NEW_MESSAGES": "Athuga með ný skilaboð", "HIDE_UNSUBSCRIBED": "Hide unsubscribed folders", "UNHIDE_KOLAB_FOLDERS": "Unhide Kolab folders", "TYPE_CALENDAR": "Calendar", @@ -575,42 +577,43 @@ "LABEL_CLOSE_COMPOSE": "Loka ritli" }, "NOTIFICATIONS": { - "REQUEST_ERROR": "Request failed", - "REQUEST_ABORTED": "Request is aborted", - "REQUEST_TIMEOUT": "Request timed out", - "INVALID_TOKEN": "Rangt tákn", - "AUTH_ERROR": "Auðkenning mistókst", - "CONNECTION_ERROR": "Get ekki tengst á netþjón", - "DOMAIN_ALREADY_EXISTS": "Lén er nú þegar til staðar", - "DOMAIN_NOT_ALLOWED": "Lén ekki leyft", - "ACCOUNT_NOT_ALLOWED": "Aðgangur er ekki leyfður", - "CONTACTS_SYNC_ERROR": "Villa í samstillingu tengiliða", - "CANT_GET_MESSAGE_LIST": "Get ekki sótt skilaboðlista", - "CANT_GET_MESSAGE": "Get ekki sótt skilaboð", - "CANT_DELETE_MESSAGE": "Get ekki eytt skilaboðum", - "CANT_COPY_MESSAGE": "Can't copy message", - "CANT_MOVE_MESSAGE": "Get ekki fært skilaboð", - "CANT_SAVE_MESSAGE": "Get ekki vistað skilaboð", - "CANT_SEND_MESSAGE": "Get ekki sent skilaboð", - "INVALID_RECIPIENTS": "Rangir viðtakendur", - "CANT_SAVE_FILTERS": "Get ekki vistað síur", - "CANT_GET_FILTERS": "Get ekki sótt síur", - "CANT_ACTIVATE_FILTERS_SCRIPT": "Can't activate filters script", - "CANT_DELETE_FILTERS_SCRIPT": "Can't delete filters script", - "FILTERS_ARE_NOT_CORRECT": "Síur eru ekki réttar", - "CANT_CREATE_FOLDER": "Get ekki búið til möppu", - "CANT_RENAME_FOLDER": "Get ekki endurnefnt möppu", - "CANT_DELETE_FOLDER": "Get ekki eytt möppu", - "CANT_DELETE_NON_EMPTY_FOLDER": "Get ekki eytt möppu í notkun", - "CANT_SUBSCRIBE_FOLDER": "Get ekki gerst áskrifandi á möppu", - "CANT_UNSUBSCRIBE_FOLDER": "Get ekki hætt áskrift á möppu", - "DEMO_SEND_MESSAGE_ERROR": "Vegna öryggissjónarmiða, þá hefur þessi aðgangur ekki leyfi til að senda póst á utanaðkomandi netföng!", - "DEMO_ACCOUNT_ERROR": "Vegna öryggissjónarmiða, þá hefur þessi aðgangur ekki heimild fyrir þessa aðgerð!", - "ACCOUNT_ALREADY_EXISTS": "Aðgangur er nú þegar til staðar", - "ACCOUNT_DOES_NOT_EXIST": "Aðgangur er ekki til", - "ACCOUNT_SWITCH_FAILED": "Switch to account \"%EMAIL%\" failed", - "MAIL_SERVER_ERROR": "Villa kom upp við tilraun til að tengjast póstþjóni", - "INVALID_INPUT_ARGUMENT": "Óleyfilegt inntaksviðfang", - "UNKNOWN_ERROR": "Óþekkt villa" + "RequestError": "Request failed", + "RequestAborted": "Request is aborted", + "RequestTimeout": "Request timed out", + "InvalidToken": "Rangt tákn", + "AuthError": "Auðkenning mistókst", + "ConnectionError": "Get ekki tengst á netþjón", + "DomainAlreadyExists": "Lén er nú þegar til staðar", + "DomainNotAllowed": "Lén ekki leyft", + "AccountNotAllowed": "Aðgangur er ekki leyfður", + "CryptKeyError": "Your login passphrase has changed", + "ContactsSyncError": "Villa í samstillingu tengiliða", + "CantGetMessageList": "Get ekki sótt skilaboðlista", + "CantGetMessage": "Get ekki sótt skilaboð", + "CantDeleteMessage": "Get ekki eytt skilaboðum", + "CantCopyMessage": "Can't copy message", + "CantMoveMessage": "Get ekki fært skilaboð", + "CantSaveMessage": "Get ekki vistað skilaboð", + "CantSendMessage": "Get ekki sent skilaboð", + "InvalidRecipients": "Rangir viðtakendur", + "CantSaveFilters": "Get ekki vistað síur", + "CantGetFilters": "Get ekki sótt síur", + "CantActivateFiltersScript": "Can't activate filters script", + "CantDeleteFiltersScript": "Can't delete filters script", + "FiltersAreNotCorrect": "Síur eru ekki réttar", + "CantCreateFolder": "Get ekki búið til möppu", + "CantRenameFolder": "Get ekki endurnefnt möppu", + "CantDeleteFolder": "Get ekki eytt möppu", + "CantDeleteNonEmptyFolder": "Get ekki eytt möppu í notkun", + "CantSubscribeFolder": "Get ekki gerst áskrifandi á möppu", + "CantUnsubscribeFolder": "Get ekki hætt áskrift á möppu", + "DemoSendMessageError": "Vegna öryggissjónarmiða, þá hefur þessi aðgangur ekki leyfi til að senda póst á utanaðkomandi netföng!", + "DemoAccountError": "Vegna öryggissjónarmiða, þá hefur þessi aðgangur ekki heimild fyrir þessa aðgerð!", + "AccountAlreadyExists": "Aðgangur er nú þegar til staðar", + "AccountDoesNotExist": "Aðgangur er ekki til", + "AccountSwitchFailed": "Switch to account \"%EMAIL%\" failed", + "MailServerError": "Villa kom upp við tilraun til að tengjast póstþjóni", + "InvalidInputArgument": "Óleyfilegt inntaksviðfang", + "UnknownError": "Óþekkt villa" } } diff --git a/snappymail/v/0.0.0/app/localization/it-IT/admin.json b/snappymail/v/0.0.0/app/localization/it-IT/admin.json index 01c62b4cc..b997dfd1a 100644 --- a/snappymail/v/0.0.0/app/localization/it-IT/admin.json +++ b/snappymail/v/0.0.0/app/localization/it-IT/admin.json @@ -85,13 +85,14 @@ "TAB_SECURITY": { "LEGEND_SECURITY": "Sicurezza", "LABEL_USE_IMAGE_PROXY": "Use local proxy for external images", - "LABEL_ALLOW_OPENPGP": "Permetti OpenPGP", + "LABEL_AUTO_VERIFY": "Verify signatures automatically", "LABEL_SHOW_PHP_INFO": "Mostra informazioni PHP", "LEGEND_ADMIN_PANEL_ACCESS_CREDENTIALS": "Credenziali di Accesso Pannello Admin", "LABEL_CURRENT_PASSWORD": "Password Corrente", "LABEL_NEW_PASSWORD": "Nuova password", "LABEL_REPEAT_PASSWORD": "Ripeti", "LABEL_REQUIRE_VERIFICATION": "Richiesta verifica del certificato SSL utilizzato", + "CRYPTO": "Crypto", "LABEL_ALLOW_SELF_SIGNED": "Permetti certificati auto-firmati" }, "TAB_PACKAGES": { @@ -135,6 +136,7 @@ "SECURE_OPTION_NONE": "None", "LABEL_ALLOW_SIEVE_SCRIPTS": "Permetti scripts sieve", "LABEL_USE_SHORT_LOGIN": "Usa login breve", + "LABEL_LOWERCASE_LOGIN": "Lowercase login (case-insensitive)", "LABEL_USE_AUTH": "Usa autenticazione", "LABEL_SET_SENDER": "Usa login come mittente", "LABEL_AUTH_PLAIN_LINE": "Force login a comando singolo AUTH PLAIN", @@ -168,17 +170,17 @@ "UNKNOWN_ERROR": "Errore sconosciuto" }, "NOTIFICATIONS": { - "REQUEST_ERROR": "Richiesta fallita", - "REQUEST_ABORTED": "Richiesta annullata", - "REQUEST_TIMEOUT": "Richiesta scaduta", - "INVALID_TOKEN": "Token non valido", - "AUTH_ERROR": "Autenticazione fallita", - "DOMAIN_ALREADY_EXISTS": "Il dominio esiste già", - "CANT_SAVE_PLUGIN_SETTINGS": "Impossibile salvare le impostazioni", - "CANT_INSTALL_PACKAGE": "Installazione estensione fallita", - "CANT_DELETE_PACKAGE": "Rimozione estensione fallita", - "INVALID_PLUGIN_PACKAGE": "Pacchetto estensione non valido", - "UNSUPPORTED_PLUGIN_PACKAGE": "Pacchetto estensione non supportato", - "UNKNOWN_ERROR": "Errore sconosciuto" + "RequestError": "Richiesta fallita", + "RequestAborted": "Richiesta annullata", + "RequestTimeout": "Richiesta scaduta", + "InvalidToken": "Token non valido", + "AuthError": "Autenticazione fallita", + "DomainAlreadyExists": "Il dominio esiste già", + "CantSavePluginSettings": "Impossibile salvare le impostazioni", + "CantInstallPackage": "Installazione estensione fallita", + "CantDeletePackage": "Rimozione estensione fallita", + "InvalidPluginPackage": "Pacchetto estensione non valido", + "UnsupportedPluginPackage": "Pacchetto estensione non supportato", + "UnknownError": "Errore sconosciuto" } } diff --git a/snappymail/v/0.0.0/app/localization/it-IT/user.json b/snappymail/v/0.0.0/app/localization/it-IT/user.json index d8fb8b1dd..e562bd496 100644 --- a/snappymail/v/0.0.0/app/localization/it-IT/user.json +++ b/snappymail/v/0.0.0/app/localization/it-IT/user.json @@ -139,6 +139,7 @@ "MENU_VIEW_ORIGINAL": "Visualizza sorgente", "HTML_VIEW": "Visualizza messaggio in HTML", "PLAIN_VIEW": "Visualizza in formato Testo", + "SWAP_COLORS": "Swap default (background)color", "MENU_DOWNLOAD_ORIGINAL": "Scarica come file .eml", "MENU_FILTER_SIMILAR": "Mostra messaggi come questo", "MENU_PRINT": "Stampa", @@ -283,7 +284,8 @@ "ERROR": "Errore di %TYPE%: %ERROR%", "CREATE_SELF_SIGNED": "Create self-signed", "VALID_UNTIL": "Valid until", - "PRIVATE_KEY": "Private key" + "PRIVATE_KEY": "Private key", + "ASK_CRYPTKEY_PASS": "Your login passphrase has changed. Please fill in your old login password to decrypt data" }, "OPENPGP": { "POPUP_IMPORT_TITLE": "Importa chiave OpenPGP", @@ -509,7 +511,7 @@ "TO_MANY_FOLDERS_DESC_2": "Vengono visualizzate solo una parte per evitare problemi di performance.", "HELP_DELETE_FOLDER": "Cancella cartella", "HELP_SHOW_HIDE_FOLDER": "Mostra\/nascondi cartella", - "HELP_CHECK_FOR_NEW_MESSAGES": "Controlla\/non controlla la presenza di nuovi messaggi", + "HELP_CHECK_FOR_NEW_MESSAGES": "Controlla la presenza di nuovi messaggi", "HIDE_UNSUBSCRIBED": "Nascondi le cartelle non sottoscritte", "UNHIDE_KOLAB_FOLDERS": "Rendi visibili le cartelle Kolab", "TYPE_CALENDAR": "Calendario", @@ -575,42 +577,43 @@ "LABEL_CLOSE_COMPOSE": "Chiudi finestra di scrittura" }, "NOTIFICATIONS": { - "REQUEST_ERROR": "Richiesta fallita", - "REQUEST_ABORTED": "Richiesta annullata", - "REQUEST_TIMEOUT": "Richiesta scaduta", - "INVALID_TOKEN": "Token invalido", - "AUTH_ERROR": "Autenticazione fallita", - "CONNECTION_ERROR": "Impossibile connettersi al server", - "DOMAIN_ALREADY_EXISTS": "Il dominio esiste già", - "DOMAIN_NOT_ALLOWED": "Il dominio non è autorizzato", - "ACCOUNT_NOT_ALLOWED": "L'account non è autorizzato", - "CONTACTS_SYNC_ERROR": "Errore durante la sincronizzazione dei contatti", - "CANT_GET_MESSAGE_LIST": "Impossibile ottenere la lista dei messaggi", - "CANT_GET_MESSAGE": "Impossibile caricare il messaggio", - "CANT_DELETE_MESSAGE": "Impossibile eliminare il messaggio", - "CANT_COPY_MESSAGE": "Impossibile copiare il messaggio", - "CANT_MOVE_MESSAGE": "Impossibile spostare il messaggio", - "CANT_SAVE_MESSAGE": "Impossibile salvare il messaggio", - "CANT_SEND_MESSAGE": "Impossibile inviare il messaggio", - "INVALID_RECIPIENTS": "Destinatario non valido", - "CANT_SAVE_FILTERS": "Impossibile salvare i filtri", - "CANT_GET_FILTERS": "Impossibile caricare i filtri", - "CANT_ACTIVATE_FILTERS_SCRIPT": "Impossibile attivare i filtri script", - "CANT_DELETE_FILTERS_SCRIPT": "Impossibile eliminare i filtri script", - "FILTERS_ARE_NOT_CORRECT": "I filtri non sono corretti", - "CANT_CREATE_FOLDER": "Impossibile creare la cartella", - "CANT_RENAME_FOLDER": "Impossibile rinominare la cartella", - "CANT_DELETE_FOLDER": "Impossibile eliminare la cartella", - "CANT_DELETE_NON_EMPTY_FOLDER": "Impossibile eliminare una cartella non vuota", - "CANT_SUBSCRIBE_FOLDER": "Impossibile sottoscrivere la cartella", - "CANT_UNSUBSCRIBE_FOLDER": "Impossibile rimuovere la sottoscrizione alla cartella", - "DEMO_SEND_MESSAGE_ERROR": "Per ragioni di sicurezza, questa demo non è abilitata ad inviare email ad inidirizzi esterni", - "DEMO_ACCOUNT_ERROR": "Per ragioni di sicurezza, questo account non è autorizzato ad effettuare questa operazione!", - "ACCOUNT_ALREADY_EXISTS": "L'account esiste già", - "ACCOUNT_DOES_NOT_EXIST": "L'account non esiste", - "ACCOUNT_SWITCH_FAILED": "Passaggio all'account \"%EMAIL%\" fallito", - "MAIL_SERVER_ERROR": "Si è verificato un errore nel server mail", - "INVALID_INPUT_ARGUMENT": "Argomento non valido", - "UNKNOWN_ERROR": "Errore sconosciuto" + "RequestError": "Richiesta fallita", + "RequestAborted": "Richiesta annullata", + "RequestTimeout": "Richiesta scaduta", + "InvalidToken": "Token invalido", + "AuthError": "Autenticazione fallita", + "ConnectionError": "Impossibile connettersi al server", + "DomainAlreadyExists": "Il dominio esiste già", + "DomainNotAllowed": "Il dominio non è autorizzato", + "AccountNotAllowed": "L'account non è autorizzato", + "CryptKeyError": "Your login passphrase has changed", + "ContactsSyncError": "Errore durante la sincronizzazione dei contatti", + "CantGetMessageList": "Impossibile ottenere la lista dei messaggi", + "CantGetMessage": "Impossibile caricare il messaggio", + "CantDeleteMessage": "Impossibile eliminare il messaggio", + "CantCopyMessage": "Impossibile copiare il messaggio", + "CantMoveMessage": "Impossibile spostare il messaggio", + "CantSaveMessage": "Impossibile salvare il messaggio", + "CantSendMessage": "Impossibile inviare il messaggio", + "InvalidRecipients": "Destinatario non valido", + "CantSaveFilters": "Impossibile salvare i filtri", + "CantGetFilters": "Impossibile caricare i filtri", + "CantActivateFiltersScript": "Impossibile attivare i filtri script", + "CantDeleteFiltersScript": "Impossibile eliminare i filtri script", + "FiltersAreNotCorrect": "I filtri non sono corretti", + "CantCreateFolder": "Impossibile creare la cartella", + "CantRenameFolder": "Impossibile rinominare la cartella", + "CantDeleteFolder": "Impossibile eliminare la cartella", + "CantDeleteNonEmptyFolder": "Impossibile eliminare una cartella non vuota", + "CantSubscribeFolder": "Impossibile sottoscrivere la cartella", + "CantUnsubscribeFolder": "Impossibile rimuovere la sottoscrizione alla cartella", + "DemoSendMessageError": "Per ragioni di sicurezza, questa demo non è abilitata ad inviare email ad inidirizzi esterni", + "DemoAccountError": "Per ragioni di sicurezza, questo account non è autorizzato ad effettuare questa operazione!", + "AccountAlreadyExists": "L'account esiste già", + "AccountDoesNotExist": "L'account non esiste", + "AccountSwitchFailed": "Passaggio all'account \"%EMAIL%\" fallito", + "MailServerError": "Si è verificato un errore nel server mail", + "InvalidInputArgument": "Argomento non valido", + "UnknownError": "Errore sconosciuto" } } diff --git a/snappymail/v/0.0.0/app/localization/ja-JP/admin.json b/snappymail/v/0.0.0/app/localization/ja-JP/admin.json index effd6fb8a..9f782284a 100644 --- a/snappymail/v/0.0.0/app/localization/ja-JP/admin.json +++ b/snappymail/v/0.0.0/app/localization/ja-JP/admin.json @@ -85,13 +85,14 @@ "TAB_SECURITY": { "LEGEND_SECURITY": "セキュリティ", "LABEL_USE_IMAGE_PROXY": "外部イメージにローカルProxyを使う", - "LABEL_ALLOW_OPENPGP": "OpenPGPを使う", + "LABEL_AUTO_VERIFY": "Verify signatures automatically", "LABEL_SHOW_PHP_INFO": "PHP情報を表示", "LEGEND_ADMIN_PANEL_ACCESS_CREDENTIALS": "管理画面アクセス情報", "LABEL_CURRENT_PASSWORD": "現在のパスワード", "LABEL_NEW_PASSWORD": "新しいパスワード", "LABEL_REPEAT_PASSWORD": "再入力", "LABEL_REQUIRE_VERIFICATION": "のSSL証明書の検証を有効にする", + "CRYPTO": "Crypto", "LABEL_ALLOW_SELF_SIGNED": "自己署名証明書を使う" }, "TAB_PACKAGES": { @@ -135,6 +136,7 @@ "SECURE_OPTION_NONE": "なし", "LABEL_ALLOW_SIEVE_SCRIPTS": "SIEVEスクリプトを使用する", "LABEL_USE_SHORT_LOGIN": "短いログイン名を使う", + "LABEL_LOWERCASE_LOGIN": "Lowercase login (case-insensitive)", "LABEL_USE_AUTH": "認証を使用する", "LABEL_SET_SENDER": "ログイン名を送信者として使う", "LABEL_AUTH_PLAIN_LINE": "Force single command AUTH PLAIN login", @@ -168,17 +170,17 @@ "UNKNOWN_ERROR": "不明なエラー" }, "NOTIFICATIONS": { - "REQUEST_ERROR": "要求は失敗しました", - "REQUEST_ABORTED": "要求は中断されました", - "REQUEST_TIMEOUT": "要求はタイムアウトしました", - "INVALID_TOKEN": "無効なトークン", - "AUTH_ERROR": "認証に失敗しました", - "DOMAIN_ALREADY_EXISTS": "ドメインが既に存在しています", - "CANT_SAVE_PLUGIN_SETTINGS": "設定が保存できません", - "CANT_INSTALL_PACKAGE": "パッケージのインストールに失敗しました", - "CANT_DELETE_PACKAGE": "パッケージの削除に失敗しました", - "INVALID_PLUGIN_PACKAGE": "無効なプラグインパッケージ", - "UNSUPPORTED_PLUGIN_PACKAGE": "サポートされていないプラグインパッケージ", - "UNKNOWN_ERROR": "不明なエラー" + "RequestError": "要求は失敗しました", + "RequestAborted": "要求は中断されました", + "RequestTimeout": "要求はタイムアウトしました", + "InvalidToken": "無効なトークン", + "AuthError": "認証に失敗しました", + "DomainAlreadyExists": "ドメインが既に存在しています", + "CantSavePluginSettings": "設定が保存できません", + "CantInstallPackage": "パッケージのインストールに失敗しました", + "CantDeletePackage": "パッケージの削除に失敗しました", + "InvalidPluginPackage": "無効なプラグインパッケージ", + "UnsupportedPluginPackage": "サポートされていないプラグインパッケージ", + "UnknownError": "不明なエラー" } } diff --git a/snappymail/v/0.0.0/app/localization/ja-JP/user.json b/snappymail/v/0.0.0/app/localization/ja-JP/user.json index 77b27be1e..d91ea6cdb 100644 --- a/snappymail/v/0.0.0/app/localization/ja-JP/user.json +++ b/snappymail/v/0.0.0/app/localization/ja-JP/user.json @@ -139,6 +139,7 @@ "MENU_VIEW_ORIGINAL": "メールのソースを表示", "HTML_VIEW": "メッセージをHTMLで表示", "PLAIN_VIEW": "メッセージをプレーンテキストで表示", + "SWAP_COLORS": "Swap default (background)color", "MENU_DOWNLOAD_ORIGINAL": ".emlファイルでダウンロード", "MENU_FILTER_SIMILAR": "このようなメッセージをフィルタ", "MENU_PRINT": "印刷", @@ -283,7 +284,8 @@ "ERROR": "%TYPE% エラー: %ERROR%", "CREATE_SELF_SIGNED": "Create self-signed", "VALID_UNTIL": "Valid until", - "PRIVATE_KEY": "Private key" + "PRIVATE_KEY": "Private key", + "ASK_CRYPTKEY_PASS": "Your login passphrase has changed. Please fill in your old login password to decrypt data" }, "OPENPGP": { "POPUP_IMPORT_TITLE": "OpenPGPキーをインポート", @@ -509,7 +511,7 @@ "TO_MANY_FOLDERS_DESC_2": "パフォーマンスの問題を回避するために、それらの一部のみを示しています。", "HELP_DELETE_FOLDER": "フォルダーを削除", "HELP_SHOW_HIDE_FOLDER": "フォルダーの表示\/非表示", - "HELP_CHECK_FOR_NEW_MESSAGES": "新しいメッセージをチェックする\/しない", + "HELP_CHECK_FOR_NEW_MESSAGES": "新しいメッセージを確認する", "HIDE_UNSUBSCRIBED": "購読していないフォルダを隠す", "UNHIDE_KOLAB_FOLDERS": "Unhide Kolab folders", "TYPE_CALENDAR": "Calendar", @@ -575,42 +577,43 @@ "LABEL_CLOSE_COMPOSE": "作成画面を閉じる" }, "NOTIFICATIONS": { - "REQUEST_ERROR": "要求は失敗しました", - "REQUEST_ABORTED": "要求は中断されました", - "REQUEST_TIMEOUT": "要求はタイムアウトしました", - "INVALID_TOKEN": "無効なトークン", - "AUTH_ERROR": "認証に失敗しました", - "CONNECTION_ERROR": "サーバーへ接続できません", - "DOMAIN_ALREADY_EXISTS": "ドメインが既に存在しています", - "DOMAIN_NOT_ALLOWED": "ドメインは、許可されていません", - "ACCOUNT_NOT_ALLOWED": "アカウントは、許可されていません", - "CONTACTS_SYNC_ERROR": "連絡先 同期 エラー", - "CANT_GET_MESSAGE_LIST": "メッセージ一覧が取得できません", - "CANT_GET_MESSAGE": "メッセージを取得できません", - "CANT_DELETE_MESSAGE": "メッセージを削除できません", - "CANT_COPY_MESSAGE": "メッセージをコピーできません", - "CANT_MOVE_MESSAGE": "メッセージを移動できません", - "CANT_SAVE_MESSAGE": "メッセージを保存できません", - "CANT_SEND_MESSAGE": "メッセージ送信できません", - "INVALID_RECIPIENTS": "無効な受信者", - "CANT_SAVE_FILTERS": "フィルターを保存できません", - "CANT_GET_FILTERS": "フィルターを取得できません", - "CANT_ACTIVATE_FILTERS_SCRIPT": "フィルタースクリプトを有効にできません", - "CANT_DELETE_FILTERS_SCRIPT": "フィルタースクリプトを削除できません", - "FILTERS_ARE_NOT_CORRECT": "フィルターが正しくありません", - "CANT_CREATE_FOLDER": "フォルダを作成できません", - "CANT_RENAME_FOLDER": "フォルダの名前を変更できません", - "CANT_DELETE_FOLDER": "フォルダを削除できません", - "CANT_DELETE_NON_EMPTY_FOLDER": "空ではないディレクトリを削除できません", - "CANT_SUBSCRIBE_FOLDER": "フォルダを購読できません", - "CANT_UNSUBSCRIBE_FOLDER": "フォルダを購読解除できません", - "DEMO_SEND_MESSAGE_ERROR": "セキュリティ上の理由から、アカウントは、外部の電子メールアドレスにメッセージを送信することを許可されていません!", - "DEMO_ACCOUNT_ERROR": "セキュリティ上の理由から、アカウントには、このアクションが許可されていません!", - "ACCOUNT_ALREADY_EXISTS": "アカウントがすでに存在します", - "ACCOUNT_DOES_NOT_EXIST": "アカウントは存在しません", - "ACCOUNT_SWITCH_FAILED": "アカウントを\"%EMAIL%\"に切り換えるのに失敗しました", - "MAIL_SERVER_ERROR": "メールサーバーへのアクセス中にエラーが発生しました", - "INVALID_INPUT_ARGUMENT": "無効な入力引数", - "UNKNOWN_ERROR": "不明なエラー" + "RequestError": "要求は失敗しました", + "RequestAborted": "要求は中断されました", + "RequestTimeout": "要求はタイムアウトしました", + "InvalidToken": "無効なトークン", + "AuthError": "認証に失敗しました", + "ConnectionError": "サーバーへ接続できません", + "DomainAlreadyExists": "ドメインが既に存在しています", + "DomainNotAllowed": "ドメインは、許可されていません", + "AccountNotAllowed": "アカウントは、許可されていません", + "CryptKeyError": "Your login passphrase has changed", + "ContactsSyncError": "連絡先 同期 エラー", + "CantGetMessageList": "メッセージ一覧が取得できません", + "CantGetMessage": "メッセージを取得できません", + "CantDeleteMessage": "メッセージを削除できません", + "CantCopyMessage": "メッセージをコピーできません", + "CantMoveMessage": "メッセージを移動できません", + "CantSaveMessage": "メッセージを保存できません", + "CantSendMessage": "メッセージ送信できません", + "InvalidRecipients": "無効な受信者", + "CantSaveFilters": "フィルターを保存できません", + "CantGetFilters": "フィルターを取得できません", + "CantActivateFiltersScript": "フィルタースクリプトを有効にできません", + "CantDeleteFiltersScript": "フィルタースクリプトを削除できません", + "FiltersAreNotCorrect": "フィルターが正しくありません", + "CantCreateFolder": "フォルダを作成できません", + "CantRenameFolder": "フォルダの名前を変更できません", + "CantDeleteFolder": "フォルダを削除できません", + "CantDeleteNonEmptyFolder": "空ではないディレクトリを削除できません", + "CantSubscribeFolder": "フォルダを購読できません", + "CantUnsubscribeFolder": "フォルダを購読解除できません", + "DemoSendMessageError": "セキュリティ上の理由から、アカウントは、外部の電子メールアドレスにメッセージを送信することを許可されていません!", + "DemoAccountError": "セキュリティ上の理由から、アカウントには、このアクションが許可されていません!", + "AccountAlreadyExists": "アカウントがすでに存在します", + "AccountDoesNotExist": "アカウントは存在しません", + "AccountSwitchFailed": "アカウントを\"%EMAIL%\"に切り換えるのに失敗しました", + "MailServerError": "メールサーバーへのアクセス中にエラーが発生しました", + "InvalidInputArgument": "無効な入力引数", + "UnknownError": "不明なエラー" } } diff --git a/snappymail/v/0.0.0/app/localization/ko-KR/user.json b/snappymail/v/0.0.0/app/localization/ko-KR/user.json index 1c74e40a2..6b5cb148d 100644 --- a/snappymail/v/0.0.0/app/localization/ko-KR/user.json +++ b/snappymail/v/0.0.0/app/localization/ko-KR/user.json @@ -139,6 +139,7 @@ "MENU_VIEW_ORIGINAL": "원본 보기", "HTML_VIEW": "View HTML message", "PLAIN_VIEW": "View plain text message", + "SWAP_COLORS": "Swap default (background)color", "MENU_DOWNLOAD_ORIGINAL": ".eml 파일로 다운로드", "MENU_FILTER_SIMILAR": "이런 유형의 메시지에 필터 적용", "MENU_PRINT": "인쇄", @@ -283,7 +284,8 @@ "ERROR": "%TYPE% 오류: %ERROR%", "CREATE_SELF_SIGNED": "Create self-signed", "VALID_UNTIL": "Valid until", - "PRIVATE_KEY": "Private key" + "PRIVATE_KEY": "Private key", + "ASK_CRYPTKEY_PASS": "Your login passphrase has changed. Please fill in your old login password to decrypt data" }, "OPENPGP": { "POPUP_IMPORT_TITLE": "OpenPGP 키 가져오기", @@ -509,7 +511,7 @@ "TO_MANY_FOLDERS_DESC_2": "We have shown only a part of them, to avoid performance problems.", "HELP_DELETE_FOLDER": "폴더 삭제", "HELP_SHOW_HIDE_FOLDER": "폴더 표시\/숨기기", - "HELP_CHECK_FOR_NEW_MESSAGES": "새 메시지 확인\/확인 취소", + "HELP_CHECK_FOR_NEW_MESSAGES": "새 메시지 확인", "HIDE_UNSUBSCRIBED": "Hide unsubscribed folders", "UNHIDE_KOLAB_FOLDERS": "Unhide Kolab folders", "TYPE_CALENDAR": "Calendar", @@ -575,42 +577,43 @@ "LABEL_CLOSE_COMPOSE": "쓰기 창 닫기" }, "NOTIFICATIONS": { - "REQUEST_ERROR": "Request failed", - "REQUEST_ABORTED": "Request is aborted", - "REQUEST_TIMEOUT": "Request timed out", - "INVALID_TOKEN": "잘못된 토큰입니다.", - "AUTH_ERROR": "인증에 실패했습니다", - "CONNECTION_ERROR": "서버에 연결할 수 없습니다.", - "DOMAIN_ALREADY_EXISTS": "이미 존재하는 도메인입니다.", - "DOMAIN_NOT_ALLOWED": "허용된 도메인이 아닙니다.", - "ACCOUNT_NOT_ALLOWED": "허용된 계정이 아닙니다.", - "CONTACTS_SYNC_ERROR": "연락처 동기화 오류", - "CANT_GET_MESSAGE_LIST": "메시지 목록을 불러 올 수 없습니다.", - "CANT_GET_MESSAGE": "메시지를 가져올 수 없습니다.", - "CANT_DELETE_MESSAGE": "메시지를 삭제할 수 없습니다.", - "CANT_COPY_MESSAGE": "Can't copy message", - "CANT_MOVE_MESSAGE": "메시지를 이동할 수 없습니다.", - "CANT_SAVE_MESSAGE": "메시지를 저장할 수 없습니다.", - "CANT_SEND_MESSAGE": "메시지를 보낼 수 없습니다.", - "INVALID_RECIPIENTS": "유효하지 않은 수신인", - "CANT_SAVE_FILTERS": "필터를 저장할 수 없습니다", - "CANT_GET_FILTERS": "필터를 가져올 수 없습니다", - "CANT_ACTIVATE_FILTERS_SCRIPT": "Can't activate filters script", - "CANT_DELETE_FILTERS_SCRIPT": "Can't delete filters script", - "FILTERS_ARE_NOT_CORRECT": "필터가 올바르지 않습니다", - "CANT_CREATE_FOLDER": "폴더를 생성할 수 없습니다.", - "CANT_RENAME_FOLDER": "폴더명을 변경할 수 없습니다.", - "CANT_DELETE_FOLDER": "폴더를 삭제할 수 없습니다.", - "CANT_DELETE_NON_EMPTY_FOLDER": "비어있지 않은 디렉터리를 삭제할 수 없습니다.", - "CANT_SUBSCRIBE_FOLDER": "폴더를 볼 수 없습니다.", - "CANT_UNSUBSCRIBE_FOLDER": "폴더를 보이지 않게 할 수 없습니다.", - "DEMO_SEND_MESSAGE_ERROR": "이 테스트 계정은 외부 메일 발송이 금지되어있습니다.", - "DEMO_ACCOUNT_ERROR": "보안을 사유로 이 계정에서는 해당 동작이 허용되지 않습니다.", - "ACCOUNT_ALREADY_EXISTS": "이미 존재하는 계정입니다.", - "ACCOUNT_DOES_NOT_EXIST": "존재하지 않는 계정입니다", - "ACCOUNT_SWITCH_FAILED": "Switch to account \"%EMAIL%\" failed", - "MAIL_SERVER_ERROR": "메일 서버에 연결하는 도중 오류가 발생했습니다.", - "INVALID_INPUT_ARGUMENT": "유효하지 않은 input argument", - "UNKNOWN_ERROR": "알 수 없는 오류" + "RequestError": "Request failed", + "RequestAborted": "Request is aborted", + "RequestTimeout": "Request timed out", + "InvalidToken": "잘못된 토큰입니다.", + "AuthError": "인증에 실패했습니다", + "ConnectionError": "서버에 연결할 수 없습니다.", + "DomainAlreadyExists": "이미 존재하는 도메인입니다.", + "DomainNotAllowed": "허용된 도메인이 아닙니다.", + "AccountNotAllowed": "허용된 계정이 아닙니다.", + "CryptKeyError": "Your login passphrase has changed", + "ContactsSyncError": "연락처 동기화 오류", + "CantGetMessageList": "메시지 목록을 불러 올 수 없습니다.", + "CantGetMessage": "메시지를 가져올 수 없습니다.", + "CantDeleteMessage": "메시지를 삭제할 수 없습니다.", + "CantCopyMessage": "Can't copy message", + "CantMoveMessage": "메시지를 이동할 수 없습니다.", + "CantSaveMessage": "메시지를 저장할 수 없습니다.", + "CantSendMessage": "메시지를 보낼 수 없습니다.", + "InvalidRecipients": "유효하지 않은 수신인", + "CantSaveFilters": "필터를 저장할 수 없습니다", + "CantGetFilters": "필터를 가져올 수 없습니다", + "CantActivateFiltersScript": "Can't activate filters script", + "CantDeleteFiltersScript": "Can't delete filters script", + "FiltersAreNotCorrect": "필터가 올바르지 않습니다", + "CantCreateFolder": "폴더를 생성할 수 없습니다.", + "CantRenameFolder": "폴더명을 변경할 수 없습니다.", + "CantDeleteFolder": "폴더를 삭제할 수 없습니다.", + "CantDeleteNonEmptyFolder": "비어있지 않은 디렉터리를 삭제할 수 없습니다.", + "CantSubscribeFolder": "폴더를 볼 수 없습니다.", + "CantUnsubscribeFolder": "폴더를 보이지 않게 할 수 없습니다.", + "DemoSendMessageError": "이 테스트 계정은 외부 메일 발송이 금지되어있습니다.", + "DemoAccountError": "보안을 사유로 이 계정에서는 해당 동작이 허용되지 않습니다.", + "AccountAlreadyExists": "이미 존재하는 계정입니다.", + "AccountDoesNotExist": "존재하지 않는 계정입니다", + "AccountSwitchFailed": "Switch to account \"%EMAIL%\" failed", + "MailServerError": "메일 서버에 연결하는 도중 오류가 발생했습니다.", + "InvalidInputArgument": "유효하지 않은 input argument", + "UnknownError": "알 수 없는 오류" } } diff --git a/snappymail/v/0.0.0/app/localization/lt-LT/admin.json b/snappymail/v/0.0.0/app/localization/lt-LT/admin.json index c4417e2c3..c448090c7 100644 --- a/snappymail/v/0.0.0/app/localization/lt-LT/admin.json +++ b/snappymail/v/0.0.0/app/localization/lt-LT/admin.json @@ -85,13 +85,14 @@ "TAB_SECURITY": { "LEGEND_SECURITY": "Sauga", "LABEL_USE_IMAGE_PROXY": "naudoti vietinį šliuzą (proxy) išoriniams paveikslėliams", - "LABEL_ALLOW_OPENPGP": "Leisti OpenPGP", + "LABEL_AUTO_VERIFY": "Verify signatures automatically", "LABEL_SHOW_PHP_INFO": "Rodyti PHP informaciją", "LEGEND_ADMIN_PANEL_ACCESS_CREDENTIALS": "Valdymo skydo prieigos rekvizitai", "LABEL_CURRENT_PASSWORD": "Dabartinis slaptažodis", "LABEL_NEW_PASSWORD": "Naujas slaptažodis", "LABEL_REPEAT_PASSWORD": "Pakartokite", "LABEL_REQUIRE_VERIFICATION": "Reikalauti SSL sertifikato", + "CRYPTO": "Crypto", "LABEL_ALLOW_SELF_SIGNED": "Leisti pačių sukurtus sertifikatus" }, "TAB_PACKAGES": { @@ -135,6 +136,7 @@ "SECURE_OPTION_NONE": "Nėra", "LABEL_ALLOW_SIEVE_SCRIPTS": "Leisti sieve programavimą", "LABEL_USE_SHORT_LOGIN": "Naudoti trumpą prisijungimo vardą", + "LABEL_LOWERCASE_LOGIN": "Lowercase login (case-insensitive)", "LABEL_USE_AUTH": "Naudoti autentifikavimą ", "LABEL_SET_SENDER": "Use login as sender", "LABEL_AUTH_PLAIN_LINE": "Force single command AUTH PLAIN login", @@ -168,17 +170,17 @@ "UNKNOWN_ERROR": "Nežinoma klaida" }, "NOTIFICATIONS": { - "REQUEST_ERROR": "Request failed", - "REQUEST_ABORTED": "Request is aborted", - "REQUEST_TIMEOUT": "Request timed out", - "INVALID_TOKEN": "Neteisingas raktas", - "AUTH_ERROR": "Autorizacija nepavyko", - "DOMAIN_ALREADY_EXISTS": "Toks domenas jau yra", - "CANT_SAVE_PLUGIN_SETTINGS": "Nepavyksta išsaugoti nustatymų", - "CANT_INSTALL_PACKAGE": "Nepavyksta įdiegti paketo", - "CANT_DELETE_PACKAGE": "Nepavyksta pašalinti paketo", - "INVALID_PLUGIN_PACKAGE": "Netinkamas įskiepio paketas", - "UNSUPPORTED_PLUGIN_PACKAGE": "Nepalaikomas įskiepio paketas", - "UNKNOWN_ERROR": "Nežinoma klaida" + "RequestError": "Request failed", + "RequestAborted": "Request is aborted", + "RequestTimeout": "Request timed out", + "InvalidToken": "Neteisingas raktas", + "AuthError": "Autorizacija nepavyko", + "DomainAlreadyExists": "Toks domenas jau yra", + "CantSavePluginSettings": "Nepavyksta išsaugoti nustatymų", + "CantInstallPackage": "Nepavyksta įdiegti paketo", + "CantDeletePackage": "Nepavyksta pašalinti paketo", + "InvalidPluginPackage": "Netinkamas įskiepio paketas", + "UnsupportedPluginPackage": "Nepalaikomas įskiepio paketas", + "UnknownError": "Nežinoma klaida" } } diff --git a/snappymail/v/0.0.0/app/localization/lt-LT/user.json b/snappymail/v/0.0.0/app/localization/lt-LT/user.json index 91f6a5883..b2b66ef33 100644 --- a/snappymail/v/0.0.0/app/localization/lt-LT/user.json +++ b/snappymail/v/0.0.0/app/localization/lt-LT/user.json @@ -139,6 +139,7 @@ "MENU_VIEW_ORIGINAL": "Rodyti šaltinį", "HTML_VIEW": "View HTML message", "PLAIN_VIEW": "View plain text message", + "SWAP_COLORS": "Swap default (background)color", "MENU_DOWNLOAD_ORIGINAL": "Atsisiųsti .eml failą", "MENU_FILTER_SIMILAR": "Filtruoti laiškus, kaip šis", "MENU_PRINT": "Spausdinti", @@ -283,7 +284,8 @@ "ERROR": "%TYPE% klaida: %ERROR%", "CREATE_SELF_SIGNED": "Create self-signed", "VALID_UNTIL": "Valid until", - "PRIVATE_KEY": "Private key" + "PRIVATE_KEY": "Private key", + "ASK_CRYPTKEY_PASS": "Your login passphrase has changed. Please fill in your old login password to decrypt data" }, "OPENPGP": { "POPUP_IMPORT_TITLE": "Importuoti OpenPGP raktą", @@ -509,7 +511,7 @@ "TO_MANY_FOLDERS_DESC_2": "Mes rodome ne visus juos, kad išvengti našumo problemų.", "HELP_DELETE_FOLDER": "Naikinti katalogą", "HELP_SHOW_HIDE_FOLDER": "Rodyti\/Slėpti katalogą", - "HELP_CHECK_FOR_NEW_MESSAGES": "Tikrinti\/Netikrinti ar yra naujų laiškų", + "HELP_CHECK_FOR_NEW_MESSAGES": "Tikrinti ar yra naujų laiškų", "HIDE_UNSUBSCRIBED": "Hide unsubscribed folders", "UNHIDE_KOLAB_FOLDERS": "Unhide Kolab folders", "TYPE_CALENDAR": "Calendar", @@ -575,42 +577,43 @@ "LABEL_CLOSE_COMPOSE": "Uždaryti laišką" }, "NOTIFICATIONS": { - "REQUEST_ERROR": "Request failed", - "REQUEST_ABORTED": "Request is aborted", - "REQUEST_TIMEOUT": "Request timed out", - "INVALID_TOKEN": "Neteisingas raktas", - "AUTH_ERROR": "Autorizacija nepavyko", - "CONNECTION_ERROR": "Nepavyskta prisijungti prie serverio", - "DOMAIN_ALREADY_EXISTS": "Subdomenas jau egzistuoja", - "DOMAIN_NOT_ALLOWED": "Šis subdomenas neleidžiamas", - "ACCOUNT_NOT_ALLOWED": "Paskyra neleidžiama", - "CONTACTS_SYNC_ERROR": "Kontaktų sinchronizavimo klaida", - "CANT_GET_MESSAGE_LIST": "Nepavyksta gauti laiškų sąrašo", - "CANT_GET_MESSAGE": "Nepavyksta gauti laiško", - "CANT_DELETE_MESSAGE": "Nepavyko pašalinti laiško", - "CANT_COPY_MESSAGE": "Can't copy message", - "CANT_MOVE_MESSAGE": "Nepavyko perkelti laiško", - "CANT_SAVE_MESSAGE": "Nepavyko išsaugoti laiško", - "CANT_SEND_MESSAGE": "Nepavyko išsiųsti laiško", - "INVALID_RECIPIENTS": "Netinkamas gavėjas", - "CANT_SAVE_FILTERS": "Nepavyko išsaugoti filtrų", - "CANT_GET_FILTERS": "Nepavyko gauti filtrų", - "CANT_ACTIVATE_FILTERS_SCRIPT": "Can't activate filters script", - "CANT_DELETE_FILTERS_SCRIPT": "Can't delete filters script", - "FILTERS_ARE_NOT_CORRECT": "Filtrai neteisingi", - "CANT_CREATE_FOLDER": "Nepavyksta sukurti katalogo", - "CANT_RENAME_FOLDER": "Nepavyksta pervadinti katalogo", - "CANT_DELETE_FOLDER": "Nepavyksta pašalinti katalogo", - "CANT_DELETE_NON_EMPTY_FOLDER": "Negalima pašalinti ne tuščio katalogo", - "CANT_SUBSCRIBE_FOLDER": "Nepavyskta užprenumeruoti katalogo", - "CANT_UNSUBSCRIBE_FOLDER": "Nepavyksta nutraukti katalogo prenumeratos", - "DEMO_SEND_MESSAGE_ERROR": "Saugumo sumetimais, ši demo paskyra neleidžia siųsti laiškų išoriniais e-pašto adresais!", - "DEMO_ACCOUNT_ERROR": "Saugumo sumetimais, su šia paskyra negalima atlikti norimo veiksmo!", - "ACCOUNT_ALREADY_EXISTS": "Paskyra jau egzistuoja", - "ACCOUNT_DOES_NOT_EXIST": "Nėra tokios paskyros", - "ACCOUNT_SWITCH_FAILED": "Switch to account \"%EMAIL%\" failed", - "MAIL_SERVER_ERROR": "Klaida bandant pasiekti pašto serverį", - "INVALID_INPUT_ARGUMENT": "Neteisingas įvesties argumentas", - "UNKNOWN_ERROR": "Nežinoma klaida" + "RequestError": "Request failed", + "RequestAborted": "Request is aborted", + "RequestTimeout": "Request timed out", + "InvalidToken": "Neteisingas raktas", + "AuthError": "Autorizacija nepavyko", + "ConnectionError": "Nepavyskta prisijungti prie serverio", + "DomainAlreadyExists": "Subdomenas jau egzistuoja", + "DomainNotAllowed": "Šis subdomenas neleidžiamas", + "AccountNotAllowed": "Paskyra neleidžiama", + "CryptKeyError": "Your login passphrase has changed", + "ContactsSyncError": "Kontaktų sinchronizavimo klaida", + "CantGetMessageList": "Nepavyksta gauti laiškų sąrašo", + "CantGetMessage": "Nepavyksta gauti laiško", + "CantDeleteMessage": "Nepavyko pašalinti laiško", + "CantCopyMessage": "Can't copy message", + "CantMoveMessage": "Nepavyko perkelti laiško", + "CantSaveMessage": "Nepavyko išsaugoti laiško", + "CantSendMessage": "Nepavyko išsiųsti laiško", + "InvalidRecipients": "Netinkamas gavėjas", + "CantSaveFilters": "Nepavyko išsaugoti filtrų", + "CantGetFilters": "Nepavyko gauti filtrų", + "CantActivateFiltersScript": "Can't activate filters script", + "CantDeleteFiltersScript": "Can't delete filters script", + "FiltersAreNotCorrect": "Filtrai neteisingi", + "CantCreateFolder": "Nepavyksta sukurti katalogo", + "CantRenameFolder": "Nepavyksta pervadinti katalogo", + "CantDeleteFolder": "Nepavyksta pašalinti katalogo", + "CantDeleteNonEmptyFolder": "Negalima pašalinti ne tuščio katalogo", + "CantSubscribeFolder": "Nepavyskta užprenumeruoti katalogo", + "CantUnsubscribeFolder": "Nepavyksta nutraukti katalogo prenumeratos", + "DemoSendMessageError": "Saugumo sumetimais, ši demo paskyra neleidžia siųsti laiškų išoriniais e-pašto adresais!", + "DemoAccountError": "Saugumo sumetimais, su šia paskyra negalima atlikti norimo veiksmo!", + "AccountAlreadyExists": "Paskyra jau egzistuoja", + "AccountDoesNotExist": "Nėra tokios paskyros", + "AccountSwitchFailed": "Switch to account \"%EMAIL%\" failed", + "MailServerError": "Klaida bandant pasiekti pašto serverį", + "InvalidInputArgument": "Neteisingas įvesties argumentas", + "UnknownError": "Nežinoma klaida" } } diff --git a/snappymail/v/0.0.0/app/localization/lv-LV/user.json b/snappymail/v/0.0.0/app/localization/lv-LV/user.json index 9b06be54d..4b7cc332d 100644 --- a/snappymail/v/0.0.0/app/localization/lv-LV/user.json +++ b/snappymail/v/0.0.0/app/localization/lv-LV/user.json @@ -139,6 +139,7 @@ "MENU_VIEW_ORIGINAL": "Rādīt orģinālu", "HTML_VIEW": "View HTML message", "PLAIN_VIEW": "View plain text message", + "SWAP_COLORS": "Swap default (background)color", "MENU_DOWNLOAD_ORIGINAL": "Lejuplādēt kā .eml failu", "MENU_FILTER_SIMILAR": "Atfiltrēt šādus ziņojumus", "MENU_PRINT": "Printēt", @@ -283,7 +284,8 @@ "ERROR": "%TYPE% error: %ERROR%", "CREATE_SELF_SIGNED": "Create self-signed", "VALID_UNTIL": "Valid until", - "PRIVATE_KEY": "Private key" + "PRIVATE_KEY": "Private key", + "ASK_CRYPTKEY_PASS": "Your login passphrase has changed. Please fill in your old login password to decrypt data" }, "OPENPGP": { "POPUP_IMPORT_TITLE": "Import OpenPGP key", @@ -509,7 +511,7 @@ "TO_MANY_FOLDERS_DESC_2": "We have shown only a part of them, to avoid performance problems.", "HELP_DELETE_FOLDER": "Delete folder", "HELP_SHOW_HIDE_FOLDER": "Show\/hide folder", - "HELP_CHECK_FOR_NEW_MESSAGES": "Check\/don't check for new messages", + "HELP_CHECK_FOR_NEW_MESSAGES": "Check for new messages", "HIDE_UNSUBSCRIBED": "Hide unsubscribed folders", "UNHIDE_KOLAB_FOLDERS": "Unhide Kolab folders", "TYPE_CALENDAR": "Calendar", @@ -575,42 +577,43 @@ "LABEL_CLOSE_COMPOSE": "Close compose" }, "NOTIFICATIONS": { - "REQUEST_ERROR": "Request failed", - "REQUEST_ABORTED": "Request is aborted", - "REQUEST_TIMEOUT": "Request timed out", - "INVALID_TOKEN": "Nepareizs tokens", - "AUTH_ERROR": "Autorizācija neizdevās", - "CONNECTION_ERROR": "Nevar savienoties ar serveri", - "DOMAIN_ALREADY_EXISTS": "Domeņes jau eksistē", - "DOMAIN_NOT_ALLOWED": "Šis domēns nav atļauts", - "ACCOUNT_NOT_ALLOWED": "Account is not allowed", - "CONTACTS_SYNC_ERROR": "Contacts synchronization error", - "CANT_GET_MESSAGE_LIST": "Nevar ielādēt ziņojumu sarakstu", - "CANT_GET_MESSAGE": "Nevar ielādēt ziņojumu", - "CANT_DELETE_MESSAGE": "Nevar izdzēst ziņojumu", - "CANT_COPY_MESSAGE": "Can't copy message", - "CANT_MOVE_MESSAGE": "Nevar pārvietot ziņojumu", - "CANT_SAVE_MESSAGE": "Nevar saglabāt ziņojumu", - "CANT_SEND_MESSAGE": "Nevar nosūtīt ziņojumu", - "INVALID_RECIPIENTS": "Nepareizi saņēmēji", - "CANT_SAVE_FILTERS": "Can't save filters", - "CANT_GET_FILTERS": "Can't get filters", - "CANT_ACTIVATE_FILTERS_SCRIPT": "Can't activate filters script", - "CANT_DELETE_FILTERS_SCRIPT": "Can't delete filters script", - "FILTERS_ARE_NOT_CORRECT": "Filters are not correct", - "CANT_CREATE_FOLDER": "Nevar izveidot mapi", - "CANT_RENAME_FOLDER": "Nevar pārsaukt mapi", - "CANT_DELETE_FOLDER": "Nevar izdzēst mapi", - "CANT_DELETE_NON_EMPTY_FOLDER": "Nevar izdzēst pilnu mapi", - "CANT_SUBSCRIBE_FOLDER": "Nevar pierakstīties mapei", - "CANT_UNSUBSCRIBE_FOLDER": "Nevar atrakstīties mapei", - "DEMO_SEND_MESSAGE_ERROR": "Drošīnas iemeslu dēļ, demo konts nedrīks sūtīt e-pastu uz ārējiem e-pastiem!", - "DEMO_ACCOUNT_ERROR": "For security purposes, this account is not allowed for this action!", - "ACCOUNT_ALREADY_EXISTS": "Konts jau eksistē", - "ACCOUNT_DOES_NOT_EXIST": "Konts neeksistē", - "ACCOUNT_SWITCH_FAILED": "Switch to account \"%EMAIL%\" failed", - "MAIL_SERVER_ERROR": "Radās kļūda savienojoties ar serveri", - "INVALID_INPUT_ARGUMENT": "Invalid input argument", - "UNKNOWN_ERROR": "Nezināma kļūda" + "RequestError": "Request failed", + "RequestAborted": "Request is aborted", + "RequestTimeout": "Request timed out", + "InvalidToken": "Nepareizs tokens", + "AuthError": "Autorizācija neizdevās", + "ConnectionError": "Nevar savienoties ar serveri", + "DomainAlreadyExists": "Domeņes jau eksistē", + "DomainNotAllowed": "Šis domēns nav atļauts", + "AccountNotAllowed": "Account is not allowed", + "CryptKeyError": "Your login passphrase has changed", + "ContactsSyncError": "Contacts synchronization error", + "CantGetMessageList": "Nevar ielādēt ziņojumu sarakstu", + "CantGetMessage": "Nevar ielādēt ziņojumu", + "CantDeleteMessage": "Nevar izdzēst ziņojumu", + "CantCopyMessage": "Can't copy message", + "CantMoveMessage": "Nevar pārvietot ziņojumu", + "CantSaveMessage": "Nevar saglabāt ziņojumu", + "CantSendMessage": "Nevar nosūtīt ziņojumu", + "InvalidRecipients": "Nepareizi saņēmēji", + "CantSaveFilters": "Can't save filters", + "CantGetFilters": "Can't get filters", + "CantActivateFiltersScript": "Can't activate filters script", + "CantDeleteFiltersScript": "Can't delete filters script", + "FiltersAreNotCorrect": "Filters are not correct", + "CantCreateFolder": "Nevar izveidot mapi", + "CantRenameFolder": "Nevar pārsaukt mapi", + "CantDeleteFolder": "Nevar izdzēst mapi", + "CantDeleteNonEmptyFolder": "Nevar izdzēst pilnu mapi", + "CantSubscribeFolder": "Nevar pierakstīties mapei", + "CantUnsubscribeFolder": "Nevar atrakstīties mapei", + "DemoSendMessageError": "Drošīnas iemeslu dēļ, demo konts nedrīks sūtīt e-pastu uz ārējiem e-pastiem!", + "DemoAccountError": "For security purposes, this account is not allowed for this action!", + "AccountAlreadyExists": "Konts jau eksistē", + "AccountDoesNotExist": "Konts neeksistē", + "AccountSwitchFailed": "Switch to account \"%EMAIL%\" failed", + "MailServerError": "Radās kļūda savienojoties ar serveri", + "InvalidInputArgument": "Invalid input argument", + "UnknownError": "Nezināma kļūda" } } diff --git a/snappymail/v/0.0.0/app/localization/nb-NO/admin.json b/snappymail/v/0.0.0/app/localization/nb-NO/admin.json index 354730265..4bc6ca21d 100644 --- a/snappymail/v/0.0.0/app/localization/nb-NO/admin.json +++ b/snappymail/v/0.0.0/app/localization/nb-NO/admin.json @@ -85,13 +85,14 @@ "TAB_SECURITY": { "LEGEND_SECURITY": "Sikkerhet", "LABEL_USE_IMAGE_PROXY": "Bruk lokal mellomtjener for eksterne bilder", - "LABEL_ALLOW_OPENPGP": "Tillat OpenPGP", + "LABEL_AUTO_VERIFY": "Verify signatures automatically", "LABEL_SHOW_PHP_INFO": "Vis PHP-informasjon", "LEGEND_ADMIN_PANEL_ACCESS_CREDENTIALS": "Akkreditiver for admin-panel", "LABEL_CURRENT_PASSWORD": "Gjeldende passord", "LABEL_NEW_PASSWORD": "Nytt passord", "LABEL_REPEAT_PASSWORD": "Gjenta", "LABEL_REQUIRE_VERIFICATION": "Krev bekreftelse av SSL-sertifikat", + "CRYPTO": "Crypto", "LABEL_ALLOW_SELF_SIGNED": "Tillat selvutstedt sertifikat" }, "TAB_PACKAGES": { @@ -135,6 +136,7 @@ "SECURE_OPTION_NONE": "Ingen", "LABEL_ALLOW_SIEVE_SCRIPTS": "Tillat sieve-skript", "LABEL_USE_SHORT_LOGIN": "Bruk forkortet brukernavn", + "LABEL_LOWERCASE_LOGIN": "Lowercase login (case-insensitive)", "LABEL_USE_AUTH": "Bruk autentisering", "LABEL_SET_SENDER": "Bruk pålogging som avsender", "LABEL_AUTH_PLAIN_LINE": "Force single command AUTH PLAIN login", @@ -168,17 +170,17 @@ "UNKNOWN_ERROR": "Ukjent feil" }, "NOTIFICATIONS": { - "REQUEST_ERROR": "Forespørsel feilet", - "REQUEST_ABORTED": "Forespørselen er avbrutt", - "REQUEST_TIMEOUT": "Forespørselen ble tidsavbrutt", - "INVALID_TOKEN": "Ugyldig symbol", - "AUTH_ERROR": "Autentisering mislyktes", - "DOMAIN_ALREADY_EXISTS": "Domenet finnes allerede", - "CANT_SAVE_PLUGIN_SETTINGS": "Klarte ikke å lagre innstillinger", - "CANT_INSTALL_PACKAGE": "Klarte ikke å installere pakke", - "CANT_DELETE_PACKAGE": "Klarte ikke å fjerne pakke", - "INVALID_PLUGIN_PACKAGE": "Ugyldig tilleggspakke", - "UNSUPPORTED_PLUGIN_PACKAGE": "Tilleggspakke støttes ikke", - "UNKNOWN_ERROR": "Ukjent feil" + "RequestError": "Forespørsel feilet", + "RequestAborted": "Forespørselen er avbrutt", + "RequestTimeout": "Forespørselen ble tidsavbrutt", + "InvalidToken": "Ugyldig symbol", + "AuthError": "Autentisering mislyktes", + "DomainAlreadyExists": "Domenet finnes allerede", + "CantSavePluginSettings": "Klarte ikke å lagre innstillinger", + "CantInstallPackage": "Klarte ikke å installere pakke", + "CantDeletePackage": "Klarte ikke å fjerne pakke", + "InvalidPluginPackage": "Ugyldig tilleggspakke", + "UnsupportedPluginPackage": "Tilleggspakke støttes ikke", + "UnknownError": "Ukjent feil" } } diff --git a/snappymail/v/0.0.0/app/localization/nb-NO/user.json b/snappymail/v/0.0.0/app/localization/nb-NO/user.json index c3bfd3c08..06e1ca6db 100644 --- a/snappymail/v/0.0.0/app/localization/nb-NO/user.json +++ b/snappymail/v/0.0.0/app/localization/nb-NO/user.json @@ -139,6 +139,7 @@ "MENU_VIEW_ORIGINAL": "Vis original", "HTML_VIEW": "Vis HTML-melding", "PLAIN_VIEW": "Vis ren tekst", + "SWAP_COLORS": "Swap default (background)color", "MENU_DOWNLOAD_ORIGINAL": "Last ned som .eml-fil", "MENU_FILTER_SIMILAR": "Filtrer slike meldinger", "MENU_PRINT": "Skriv ut", @@ -283,7 +284,8 @@ "ERROR": "%TYPE%-feil: %ERROR%", "CREATE_SELF_SIGNED": "Create self-signed", "VALID_UNTIL": "Valid until", - "PRIVATE_KEY": "Private key" + "PRIVATE_KEY": "Private key", + "ASK_CRYPTKEY_PASS": "Your login passphrase has changed. Please fill in your old login password to decrypt data" }, "OPENPGP": { "POPUP_IMPORT_TITLE": "Importer OpenPGP-nøkkel", @@ -509,7 +511,7 @@ "TO_MANY_FOLDERS_DESC_2": "For å unngå ytelsesproblemer får du bare se noen av dem.", "HELP_DELETE_FOLDER": "Slett mappe", "HELP_SHOW_HIDE_FOLDER": "Vis\/skjul mappe", - "HELP_CHECK_FOR_NEW_MESSAGES": "Se\/ikke se etter nye meldinger", + "HELP_CHECK_FOR_NEW_MESSAGES": "Se etter nye meldinger", "HIDE_UNSUBSCRIBED": "Skjul mapper som bruker ikke abonnerer på", "UNHIDE_KOLAB_FOLDERS": "Unhide Kolab folders", "TYPE_CALENDAR": "Kalender", @@ -575,42 +577,43 @@ "LABEL_CLOSE_COMPOSE": "Lukk ny melding" }, "NOTIFICATIONS": { - "REQUEST_ERROR": "Request failed", - "REQUEST_ABORTED": "Request is aborted", - "REQUEST_TIMEOUT": "Request timed out", - "INVALID_TOKEN": "Ugyldig info", - "AUTH_ERROR": "Autentisering mislyktes", - "CONNECTION_ERROR": "Klarte ikke å koble til tjeneren", - "DOMAIN_ALREADY_EXISTS": "Domenet finnes allerede", - "DOMAIN_NOT_ALLOWED": "Dette domenet tillates ikke", - "ACCOUNT_NOT_ALLOWED": "Denne kontoen tillates ikke", - "CONTACTS_SYNC_ERROR": "Synkronisering av kontakter mislyktes", - "CANT_GET_MESSAGE_LIST": "Klarte ikke å hente liste over meldinger", - "CANT_GET_MESSAGE": "Klarte ikke å hente melding", - "CANT_DELETE_MESSAGE": "Klarte ikke å slette melding", - "CANT_COPY_MESSAGE": "Can't copy message", - "CANT_MOVE_MESSAGE": "Klarte ikke å flytte melding", - "CANT_SAVE_MESSAGE": "Klarte ikke å lagre melding", - "CANT_SEND_MESSAGE": "Klarte ikke å sende melding", - "INVALID_RECIPIENTS": "Ugyldige mottakere", - "CANT_SAVE_FILTERS": "Klarte ikke å lagre filtre", - "CANT_GET_FILTERS": "Klarte ikke å hente filtre", - "CANT_ACTIVATE_FILTERS_SCRIPT": "Can't activate filters script", - "CANT_DELETE_FILTERS_SCRIPT": "Can't delete filters script", - "FILTERS_ARE_NOT_CORRECT": "Feilutformet filter", - "CANT_CREATE_FOLDER": "Klarte ikke å lage mappe", - "CANT_RENAME_FOLDER": "Klarte ikke å endre navn på mappe", - "CANT_DELETE_FOLDER": "Klarte ikke å slette mappe", - "CANT_DELETE_NON_EMPTY_FOLDER": "Du må slette mappeinnholdet før du kan slette mappa", - "CANT_SUBSCRIBE_FOLDER": "Klarte ikke å abonnere på mappe", - "CANT_UNSUBSCRIBE_FOLDER": "Klarte ikke å avslutte abonnement", - "DEMO_SEND_MESSAGE_ERROR": "Denne demo-kontoen er blokkert fra å sende meldinger til eksterne e-postadresser av sikkerhetshensyn.", - "DEMO_ACCOUNT_ERROR": "Av sikkerhetshensyn er denne kontoen blokkert fra å utføre denne handlinga.", - "ACCOUNT_ALREADY_EXISTS": "Kontoen finnes allerede", - "ACCOUNT_DOES_NOT_EXIST": "Kontoen finnes ikke", - "ACCOUNT_SWITCH_FAILED": "Switch to account \"%EMAIL%\" failed", - "MAIL_SERVER_ERROR": "Fikk ikke tilgang til e-posttjener", - "INVALID_INPUT_ARGUMENT": "Ugyldig inndata-argument", - "UNKNOWN_ERROR": "Ukjent feil" + "RequestError": "Request failed", + "RequestAborted": "Request is aborted", + "RequestTimeout": "Request timed out", + "InvalidToken": "Ugyldig info", + "AuthError": "Autentisering mislyktes", + "ConnectionError": "Klarte ikke å koble til tjeneren", + "DomainAlreadyExists": "Domenet finnes allerede", + "DomainNotAllowed": "Dette domenet tillates ikke", + "AccountNotAllowed": "Denne kontoen tillates ikke", + "CryptKeyError": "Your login passphrase has changed", + "ContactsSyncError": "Synkronisering av kontakter mislyktes", + "CantGetMessageList": "Klarte ikke å hente liste over meldinger", + "CantGetMessage": "Klarte ikke å hente melding", + "CantDeleteMessage": "Klarte ikke å slette melding", + "CantCopyMessage": "Can't copy message", + "CantMoveMessage": "Klarte ikke å flytte melding", + "CantSaveMessage": "Klarte ikke å lagre melding", + "CantSendMessage": "Klarte ikke å sende melding", + "InvalidRecipients": "Ugyldige mottakere", + "CantSaveFilters": "Klarte ikke å lagre filtre", + "CantGetFilters": "Klarte ikke å hente filtre", + "CantActivateFiltersScript": "Can't activate filters script", + "CantDeleteFiltersScript": "Can't delete filters script", + "FiltersAreNotCorrect": "Feilutformet filter", + "CantCreateFolder": "Klarte ikke å lage mappe", + "CantRenameFolder": "Klarte ikke å endre navn på mappe", + "CantDeleteFolder": "Klarte ikke å slette mappe", + "CantDeleteNonEmptyFolder": "Du må slette mappeinnholdet før du kan slette mappa", + "CantSubscribeFolder": "Klarte ikke å abonnere på mappe", + "CantUnsubscribeFolder": "Klarte ikke å avslutte abonnement", + "DemoSendMessageError": "Denne demo-kontoen er blokkert fra å sende meldinger til eksterne e-postadresser av sikkerhetshensyn.", + "DemoAccountError": "Av sikkerhetshensyn er denne kontoen blokkert fra å utføre denne handlinga.", + "AccountAlreadyExists": "Kontoen finnes allerede", + "AccountDoesNotExist": "Kontoen finnes ikke", + "AccountSwitchFailed": "Switch to account \"%EMAIL%\" failed", + "MailServerError": "Fikk ikke tilgang til e-posttjener", + "InvalidInputArgument": "Ugyldig inndata-argument", + "UnknownError": "Ukjent feil" } } diff --git a/snappymail/v/0.0.0/app/localization/nl-NL/admin.json b/snappymail/v/0.0.0/app/localization/nl-NL/admin.json index c58142a1c..e9922dc37 100644 --- a/snappymail/v/0.0.0/app/localization/nl-NL/admin.json +++ b/snappymail/v/0.0.0/app/localization/nl-NL/admin.json @@ -85,13 +85,14 @@ "TAB_SECURITY": { "LEGEND_SECURITY": "Beveiliging", "LABEL_USE_IMAGE_PROXY": "Gebruik de server als proxy voor externe afbeeldingen", - "LABEL_ALLOW_OPENPGP": "OpenPGP toestaan", + "LABEL_AUTO_VERIFY": "Ondertekening automatisch verifiëren", "LABEL_SHOW_PHP_INFO": "PHP informatie tonen", "LEGEND_ADMIN_PANEL_ACCESS_CREDENTIALS": "Toegangsgegevens beheer paneel", "LABEL_CURRENT_PASSWORD": "Huidig wachtwoord", "LABEL_NEW_PASSWORD": "Nieuw wachtwoord", "LABEL_REPEAT_PASSWORD": "Herhaal nieuw wachtwoord", "LABEL_REQUIRE_VERIFICATION": "Verificatie van SSL certificaten", + "CRYPTO": "Crypto", "LABEL_ALLOW_SELF_SIGNED": "Sta zelf ondertekende certificaten toe" }, "TAB_PACKAGES": { @@ -135,6 +136,7 @@ "SECURE_OPTION_NONE": "Geen", "LABEL_ALLOW_SIEVE_SCRIPTS": "Sta Sieve scripts toe", "LABEL_USE_SHORT_LOGIN": "Gebruik verkorte login", + "LABEL_LOWERCASE_LOGIN": "Lowercase login (case-insensitive)", "LABEL_USE_AUTH": "Gebruik authenticatie", "LABEL_SET_SENDER": "Gebruik login als verzender", "LABEL_AUTH_PLAIN_LINE": "Forceer een enkele opdracht AUTH PLAIN-aanmelding", @@ -168,17 +170,17 @@ "UNKNOWN_ERROR": "Onbekende fout" }, "NOTIFICATIONS": { - "REQUEST_ERROR": "Aanvraag mislukt", - "REQUEST_ABORTED": "Aanvraag is afgebroken", - "REQUEST_TIMEOUT": "Aanvraag verlopen", - "INVALID_TOKEN": "Onjuiste token", - "AUTH_ERROR": "Authenticatie mislukt", - "DOMAIN_ALREADY_EXISTS": "Domein bestaat al", - "CANT_SAVE_PLUGIN_SETTINGS": "Kan instellingen niet opslaan", - "CANT_INSTALL_PACKAGE": "Extensie installeren mislukt", - "CANT_DELETE_PACKAGE": "Extensie verwijderen mislukt", - "INVALID_PLUGIN_PACKAGE": "Onjuist extensie pakket", - "UNSUPPORTED_PLUGIN_PACKAGE": "Niet ondersteund extensie pakket", - "UNKNOWN_ERROR": "Onbekende fout" + "RequestError": "Aanvraag mislukt", + "RequestAborted": "Aanvraag is afgebroken", + "RequestTimeout": "Aanvraag verlopen", + "InvalidToken": "Onjuiste token", + "AuthError": "Authenticatie mislukt", + "DomainAlreadyExists": "Domein bestaat al", + "CantSavePluginSettings": "Kan instellingen niet opslaan", + "CantInstallPackage": "Extensie installeren mislukt", + "CantDeletePackage": "Extensie verwijderen mislukt", + "InvalidPluginPackage": "Onjuist extensie pakket", + "UnsupportedPluginPackage": "Niet ondersteund extensie pakket", + "UnknownError": "Onbekende fout" } } diff --git a/snappymail/v/0.0.0/app/localization/nl-NL/user.json b/snappymail/v/0.0.0/app/localization/nl-NL/user.json index c7b374905..139a7da2f 100644 --- a/snappymail/v/0.0.0/app/localization/nl-NL/user.json +++ b/snappymail/v/0.0.0/app/localization/nl-NL/user.json @@ -139,6 +139,7 @@ "MENU_VIEW_ORIGINAL": "Toon origineel", "HTML_VIEW": "Bekijk HTML bericht", "PLAIN_VIEW": "Bekijk platte tekst bericht", + "SWAP_COLORS": "Swap default (background)color", "MENU_DOWNLOAD_ORIGINAL": "Download als .eml bestand", "MENU_FILTER_SIMILAR": "Filter gelijksoortige berichten", "MENU_PRINT": "Afdrukken", @@ -279,11 +280,12 @@ "ENCRYPT": "Versleutelen", "DECRYPT": "Ontsleutelen", "VERIFY": "Verifiëren", - "GOOD_SIGNATURE": "Juiste handtekening van %USER%", + "GOOD_SIGNATURE": "Juiste ondertekening van %USER%", "ERROR": "%TYPE% foutmelding: %ERROR%", "CREATE_SELF_SIGNED": "Zelfondertekend certificaat aanmaken", "VALID_UNTIL": "Geldig tot", - "PRIVATE_KEY": "Privésleutel" + "PRIVATE_KEY": "Privésleutel", + "ASK_CRYPTKEY_PASS": "Your login passphrase has changed. Please fill in your old login password to decrypt data" }, "OPENPGP": { "POPUP_IMPORT_TITLE": "Importeer OpenPGP sleutel", @@ -509,7 +511,7 @@ "TO_MANY_FOLDERS_DESC_2": "Er wordt maar een deel weergegeven om prestatieproblemen te voorkomen.", "HELP_DELETE_FOLDER": "Verwijder map", "HELP_SHOW_HIDE_FOLDER": "Toon\/verberg map", - "HELP_CHECK_FOR_NEW_MESSAGES": "Controleer wel of niet op nieuwe berichten", + "HELP_CHECK_FOR_NEW_MESSAGES": "Controleer op nieuwe berichten", "HIDE_UNSUBSCRIBED": "Verberg niet geabonneerde mappen", "UNHIDE_KOLAB_FOLDERS": "Toon Kolab mappen", "TYPE_CALENDAR": "Kalender", @@ -575,42 +577,43 @@ "LABEL_CLOSE_COMPOSE": "Nieuw bericht venster sluiten" }, "NOTIFICATIONS": { - "REQUEST_ERROR": "Verzoek mislukt", - "REQUEST_ABORTED": "Verzoek is afgebroken", - "REQUEST_TIMEOUT": "Verzoek time-out", - "INVALID_TOKEN": "Ongeldige token", - "AUTH_ERROR": "Authenticatie mislukt", - "CONNECTION_ERROR": "Kan geen verbinding maken met de Server", - "DOMAIN_ALREADY_EXISTS": "Domein bestaat reeds", - "DOMAIN_NOT_ALLOWED": "Domein is niet toegestaan", - "ACCOUNT_NOT_ALLOWED": "Account is niet toegestaan", - "CONTACTS_SYNC_ERROR": "Contactpersonen synchronisatie fout", - "CANT_GET_MESSAGE_LIST": "Berichtenlijst kan niet worden opgehaald", - "CANT_GET_MESSAGE": "Bericht kan niet worden opgehaald", - "CANT_DELETE_MESSAGE": "Kan bericht niet verwijderen", - "CANT_COPY_MESSAGE": "Kan bericht niet kopiëren", - "CANT_MOVE_MESSAGE": "Kan bericht niet verplaatsen", - "CANT_SAVE_MESSAGE": "Kan bericht niet opslaan", - "CANT_SEND_MESSAGE": "Kan bericht niet verzenden", - "INVALID_RECIPIENTS": "Onjuiste ontvangers", - "CANT_SAVE_FILTERS": "Kan filters niet opslaan", - "CANT_GET_FILTERS": "Kan filters niet ophalen", - "CANT_ACTIVATE_FILTERS_SCRIPT": "Kan filters script niet activeren", - "CANT_DELETE_FILTERS_SCRIPT": "Kan filters script niet verwijderen", - "FILTERS_ARE_NOT_CORRECT": "Filters zijn onjuist", - "CANT_CREATE_FOLDER": "Kan map niet aanmaken", - "CANT_RENAME_FOLDER": "Kan map niet hernoemen", - "CANT_DELETE_FOLDER": "Kan map niet verwijderen", - "CANT_DELETE_NON_EMPTY_FOLDER": "Een niet lege map kan niet verwijderd worden", - "CANT_SUBSCRIBE_FOLDER": "Kan niet abonneren op folder", - "CANT_UNSUBSCRIBE_FOLDER": "Kan abonnement op folder niet opzeggen", - "DEMO_SEND_MESSAGE_ERROR": "Vanwege beveiliging is het niet toegestaan dat deze demo account e-mail naar extern verzend!", - "DEMO_ACCOUNT_ERROR": "Vanwege beveiliging is deze actie niet toegestaan voor deze account!", - "ACCOUNT_ALREADY_EXISTS": "Account bestaat al", - "ACCOUNT_DOES_NOT_EXIST": "Account bestaat niet.", - "ACCOUNT_SWITCH_FAILED": "Overschakelen naar account \"%EMAIL%\" mislukt", - "MAIL_SERVER_ERROR": "Fout bij toegang tot de mail Server", - "INVALID_INPUT_ARGUMENT": "Ongeldig invoer argument", - "UNKNOWN_ERROR": "Er is een onbekende fout opgetreden" + "RequestError": "Verzoek mislukt", + "RequestAborted": "Verzoek is afgebroken", + "RequestTimeout": "Verzoek time-out", + "InvalidToken": "Ongeldige token", + "AuthError": "Authenticatie mislukt", + "ConnectionError": "Kan geen verbinding maken met de Server", + "DomainAlreadyExists": "Domein bestaat reeds", + "DomainNotAllowed": "Domein is niet toegestaan", + "AccountNotAllowed": "Account is niet toegestaan", + "CryptKeyError": "Your login passphrase has changed", + "ContactsSyncError": "Contactpersonen synchronisatie fout", + "CantGetMessageList": "Berichtenlijst kan niet worden opgehaald", + "CantGetMessage": "Bericht kan niet worden opgehaald", + "CantDeleteMessage": "Kan bericht niet verwijderen", + "CantCopyMessage": "Kan bericht niet kopiëren", + "CantMoveMessage": "Kan bericht niet verplaatsen", + "CantSaveMessage": "Kan bericht niet opslaan", + "CantSendMessage": "Kan bericht niet verzenden", + "InvalidRecipients": "Onjuiste ontvangers", + "CantSaveFilters": "Kan filters niet opslaan", + "CantGetFilters": "Kan filters niet ophalen", + "CantActivateFiltersScript": "Kan filters script niet activeren", + "CantDeleteFiltersScript": "Kan filters script niet verwijderen", + "FiltersAreNotCorrect": "Filters zijn onjuist", + "CantCreateFolder": "Kan map niet aanmaken", + "CantRenameFolder": "Kan map niet hernoemen", + "CantDeleteFolder": "Kan map niet verwijderen", + "CantDeleteNonEmptyFolder": "Een niet lege map kan niet verwijderd worden", + "CantSubscribeFolder": "Kan niet abonneren op folder", + "CantUnsubscribeFolder": "Kan abonnement op folder niet opzeggen", + "DemoSendMessageError": "Vanwege beveiliging is het niet toegestaan dat deze demo account e-mail naar extern verzend!", + "DemoAccountError": "Vanwege beveiliging is deze actie niet toegestaan voor deze account!", + "AccountAlreadyExists": "Account bestaat al", + "AccountDoesNotExist": "Account bestaat niet.", + "AccountSwitchFailed": "Overschakelen naar account \"%EMAIL%\" mislukt", + "MailServerError": "Fout bij toegang tot de mail Server", + "InvalidInputArgument": "Ongeldig invoer argument", + "UnknownError": "Er is een onbekende fout opgetreden" } } diff --git a/snappymail/v/0.0.0/app/localization/pl-PL/admin.json b/snappymail/v/0.0.0/app/localization/pl-PL/admin.json index 4b65c8437..bd200dc10 100644 --- a/snappymail/v/0.0.0/app/localization/pl-PL/admin.json +++ b/snappymail/v/0.0.0/app/localization/pl-PL/admin.json @@ -85,14 +85,15 @@ "TAB_SECURITY": { "LEGEND_SECURITY": "Bezpieczeństwo", "LABEL_USE_IMAGE_PROXY": "Użyj lokalnego serwera proxy dla zewnętrznych obrazów", - "LABEL_ALLOW_OPENPGP": "Zezwól na używanie OpenPGP", + "LABEL_AUTO_VERIFY": "Weryfikuj podpisy automatycznie", "LABEL_SHOW_PHP_INFO": "Pokaż informacje o wersji PHP", "LEGEND_ADMIN_PANEL_ACCESS_CREDENTIALS": "Poświadczenia dostępu do panelu administracyjnego", "LABEL_CURRENT_PASSWORD": "Bieżące hasło", "LABEL_NEW_PASSWORD": "Nowe hasło", "LABEL_REPEAT_PASSWORD": "Powtórz hasło", "LABEL_REQUIRE_VERIFICATION": "Wymagaj weryfikacji używanego certyfikatu SSL", - "LABEL_ALLOW_SELF_SIGNED": "Zezwól na certyfikaty z podpisem własnym" + "CRYPTO": "Kryptografia", + "LABEL_ALLOW_SELF_SIGNED": "Zezwól na certyfikaty podpisane samodzielnie" }, "TAB_PACKAGES": { "LABEL_ENABLE_PLUGINS": "Włącz obsługę wtyczek", @@ -135,6 +136,7 @@ "SECURE_OPTION_NONE": "Bez zabezpieczeń", "LABEL_ALLOW_SIEVE_SCRIPTS": "Zezwól na skrypty sieve", "LABEL_USE_SHORT_LOGIN": "Użyj krótkiego loginu", + "LABEL_LOWERCASE_LOGIN": "Lowercase login (case-insensitive)", "LABEL_USE_AUTH": "Użyj uwierzytelnienia", "LABEL_SET_SENDER": "Użyj loginu jako nadawcy", "LABEL_AUTH_PLAIN_LINE": "Wymuś logowanie pojedynczą komendą AUTH PLAIN", @@ -168,17 +170,17 @@ "UNKNOWN_ERROR": "Nieznany błąd" }, "NOTIFICATIONS": { - "REQUEST_ERROR": "Zapytanie zakończone niepowodzeniem", - "REQUEST_ABORTED": "Zapytanie przerwane", - "REQUEST_TIMEOUT": "Upłynął limit czasu", - "INVALID_TOKEN": "Nieprawidłowy token", - "AUTH_ERROR": "Uwierzytelnienie zakończone niepowodzeniem", - "DOMAIN_ALREADY_EXISTS": "Ta domena już istnieje", - "CANT_SAVE_PLUGIN_SETTINGS": "Nie można zapisać ustawień wtyczki", - "CANT_INSTALL_PACKAGE": "Instalacja wtyczki zakończona niepowodzeniem", - "CANT_DELETE_PACKAGE": "Usunięcie wtyczki zakończone niepowodzeniem", - "INVALID_PLUGIN_PACKAGE": "Nieprawidłowa wtyczka", - "UNSUPPORTED_PLUGIN_PACKAGE": "Nieobsługiwana wtyczka", - "UNKNOWN_ERROR": "Nieznany błąd" + "RequestError": "Zapytanie zakończone niepowodzeniem", + "RequestAborted": "Zapytanie przerwane", + "RequestTimeout": "Upłynął limit czasu", + "InvalidToken": "Nieprawidłowy token", + "AuthError": "Uwierzytelnienie zakończone niepowodzeniem", + "DomainAlreadyExists": "Ta domena już istnieje", + "CantSavePluginSettings": "Nie można zapisać ustawień wtyczki", + "CantInstallPackage": "Instalacja wtyczki zakończona niepowodzeniem", + "CantDeletePackage": "Usunięcie wtyczki zakończone niepowodzeniem", + "InvalidPluginPackage": "Nieprawidłowa wtyczka", + "UnsupportedPluginPackage": "Nieobsługiwana wtyczka", + "UnknownError": "Nieznany błąd" } } diff --git a/snappymail/v/0.0.0/app/localization/pl-PL/user.json b/snappymail/v/0.0.0/app/localization/pl-PL/user.json index 64d30cb08..ed4e5f07a 100644 --- a/snappymail/v/0.0.0/app/localization/pl-PL/user.json +++ b/snappymail/v/0.0.0/app/localization/pl-PL/user.json @@ -139,6 +139,7 @@ "MENU_VIEW_ORIGINAL": "Pokaż źródło", "HTML_VIEW": "Wyświetl jako HTML", "PLAIN_VIEW": "Wyświetl jako czysty tekst", + "SWAP_COLORS": "Swap default (background)color", "MENU_DOWNLOAD_ORIGINAL": "Pobierz jako plik .eml", "MENU_FILTER_SIMILAR": "Filtruj podobne wiadomości", "MENU_PRINT": "Drukuj", @@ -154,8 +155,8 @@ "HAS_VIRUS_WARNING": "Uwaga: wykryto wirusa!", "TAGS": "Tagi", "NEW_TAG": "Nowy tag", - "ENABLE_TRACKING_LINKS": "Enable tracking links", - "TRACKING_ENABLED": "Links are now tracked when clicked!" + "ENABLE_TRACKING_LINKS": "Włącz linki śledzące", + "TRACKING_ENABLED": "Linki są teraz śledzone!" }, "MESSAGE_TAGS": { "$important": "Ważne", @@ -283,7 +284,8 @@ "ERROR": "Błąd %TYPE%: %ERROR%", "CREATE_SELF_SIGNED": "Utwórz samodzielnie podpisany certyfikat", "VALID_UNTIL": "Ważny do", - "PRIVATE_KEY": "Private key" + "PRIVATE_KEY": "Private key", + "ASK_CRYPTKEY_PASS": "Your login passphrase has changed. Please fill in your old login password to decrypt data" }, "OPENPGP": { "POPUP_IMPORT_TITLE": "Importowanie klucza OpenPGP", @@ -509,7 +511,7 @@ "TO_MANY_FOLDERS_DESC_2": "Pokazaliśmy tylko część z nich aby uniknąć problemów z wydajnością.", "HELP_DELETE_FOLDER": "Usuń folder", "HELP_SHOW_HIDE_FOLDER": "Pokaż\/ukryj folder", - "HELP_CHECK_FOR_NEW_MESSAGES": "Sprawdzaj\/nie sprawdzaj nowych wiadomości", + "HELP_CHECK_FOR_NEW_MESSAGES": "Sprawdzaj nowych wiadomości", "HIDE_UNSUBSCRIBED": "Ukryj niesubskrybowane foldery", "UNHIDE_KOLAB_FOLDERS": "Pokaż foldery Kolab", "TYPE_CALENDAR": "Kalendarz", @@ -575,42 +577,43 @@ "LABEL_CLOSE_COMPOSE": "Zamknij okno tworzenia wiadomości" }, "NOTIFICATIONS": { - "REQUEST_ERROR": "Zapytanie zakończone niepowodzeniem", - "REQUEST_ABORTED": "Zapytanie przerwane", - "REQUEST_TIMEOUT": "Upłynął limit czasu", - "INVALID_TOKEN": "Nieprawidłowy token", - "AUTH_ERROR": "Uwierzytelnienie zakończone niepowodzeniem", - "CONNECTION_ERROR": "Nie można połączyć z serwerem", - "DOMAIN_ALREADY_EXISTS": "Domena już istnieje", - "DOMAIN_NOT_ALLOWED": "Domena jest niedozwolona", - "ACCOUNT_NOT_ALLOWED": "Konto nie jest dozwolone", - "CONTACTS_SYNC_ERROR": "Błąd synchronizacji kontaktów", - "CANT_GET_MESSAGE_LIST": "Nie można pobrać listy wiadomości", - "CANT_GET_MESSAGE": "Nie można pobrać wiadomości", - "CANT_DELETE_MESSAGE": "Nie można usunąć wiadomości", - "CANT_COPY_MESSAGE": "Nie można skopiować wiadomości", - "CANT_MOVE_MESSAGE": "Nie można przenieść wiadomości", - "CANT_SAVE_MESSAGE": "Nie można zapisać wiadomości", - "CANT_SEND_MESSAGE": "Nie można wysłać wiadomości", - "INVALID_RECIPIENTS": "Nieprawidłowy adres odbiorcy", - "CANT_SAVE_FILTERS": "Nie można zapisać filtrów", - "CANT_GET_FILTERS": "Nie można pobrać filtrów", - "CANT_ACTIVATE_FILTERS_SCRIPT": "Nie można aktywować skryptu filtrów", - "CANT_DELETE_FILTERS_SCRIPT": "Nie można usunąć skryptu filtrów", - "FILTERS_ARE_NOT_CORRECT": "Filtry są nieprawidłowe", - "CANT_CREATE_FOLDER": "Nie można utworzyć folderu", - "CANT_RENAME_FOLDER": "Nie można zmienić nazwy folderu", - "CANT_DELETE_FOLDER": "Nie można usunąć folderu", - "CANT_DELETE_NON_EMPTY_FOLDER": "Nie można usunąć folderu, w którym znajdują się wiadomości", - "CANT_SUBSCRIBE_FOLDER": "Nie można zasubskrybować folderu", - "CANT_UNSUBSCRIBE_FOLDER": "Nie można usunąć subskrypcji folderu", - "DEMO_SEND_MESSAGE_ERROR": "Ze względów bezpieczeństwa konto testowe nie ma możliwości przesyłania wiadomości na zewnętrzne adresy e-mail.", - "DEMO_ACCOUNT_ERROR": "Ze względów bezpieczeństwa ta akcja jest niedozwolona na tym koncie!", - "ACCOUNT_ALREADY_EXISTS": "Konto o takiej nazwie już istnieje", - "ACCOUNT_DOES_NOT_EXIST": "Konto nie istnieje", - "ACCOUNT_SWITCH_FAILED": "Przełączenie na konto \"%EMAIL%\" nie powiodło się", - "MAIL_SERVER_ERROR": "Wystąpił błąd w trakcie połączenia z serwerem poczty", - "INVALID_INPUT_ARGUMENT": "Nieprawidłowy argument", - "UNKNOWN_ERROR": "Nieznany błąd" + "RequestError": "Zapytanie zakończone niepowodzeniem", + "RequestAborted": "Zapytanie przerwane", + "RequestTimeout": "Upłynął limit czasu", + "InvalidToken": "Nieprawidłowy token", + "AuthError": "Uwierzytelnienie zakończone niepowodzeniem", + "ConnectionError": "Nie można połączyć z serwerem", + "DomainAlreadyExists": "Domena już istnieje", + "DomainNotAllowed": "Domena jest niedozwolona", + "AccountNotAllowed": "Konto nie jest dozwolone", + "CryptKeyError": "Your login passphrase has changed", + "ContactsSyncError": "Błąd synchronizacji kontaktów", + "CantGetMessageList": "Nie można pobrać listy wiadomości", + "CantGetMessage": "Nie można pobrać wiadomości", + "CantDeleteMessage": "Nie można usunąć wiadomości", + "CantCopyMessage": "Nie można skopiować wiadomości", + "CantMoveMessage": "Nie można przenieść wiadomości", + "CantSaveMessage": "Nie można zapisać wiadomości", + "CantSendMessage": "Nie można wysłać wiadomości", + "InvalidRecipients": "Nieprawidłowy adres odbiorcy", + "CantSaveFilters": "Nie można zapisać filtrów", + "CantGetFilters": "Nie można pobrać filtrów", + "CantActivateFiltersScript": "Nie można aktywować skryptu filtrów", + "CantDeleteFiltersScript": "Nie można usunąć skryptu filtrów", + "FiltersAreNotCorrect": "Filtry są nieprawidłowe", + "CantCreateFolder": "Nie można utworzyć folderu", + "CantRenameFolder": "Nie można zmienić nazwy folderu", + "CantDeleteFolder": "Nie można usunąć folderu", + "CantDeleteNonEmptyFolder": "Nie można usunąć folderu, w którym znajdują się wiadomości", + "CantSubscribeFolder": "Nie można zasubskrybować folderu", + "CantUnsubscribeFolder": "Nie można usunąć subskrypcji folderu", + "DemoSendMessageError": "Ze względów bezpieczeństwa konto testowe nie ma możliwości przesyłania wiadomości na zewnętrzne adresy e-mail.", + "DemoAccountError": "Ze względów bezpieczeństwa ta akcja jest niedozwolona na tym koncie!", + "AccountAlreadyExists": "Konto o takiej nazwie już istnieje", + "AccountDoesNotExist": "Konto nie istnieje", + "AccountSwitchFailed": "Przełączenie na konto \"%EMAIL%\" nie powiodło się", + "MailServerError": "Wystąpił błąd w trakcie połączenia z serwerem poczty", + "InvalidInputArgument": "Nieprawidłowy argument", + "UnknownError": "Nieznany błąd" } } diff --git a/snappymail/v/0.0.0/app/localization/pt-BR/admin.json b/snappymail/v/0.0.0/app/localization/pt-BR/admin.json index 56bcfd7eb..9e2219f5c 100644 --- a/snappymail/v/0.0.0/app/localization/pt-BR/admin.json +++ b/snappymail/v/0.0.0/app/localization/pt-BR/admin.json @@ -85,13 +85,14 @@ "TAB_SECURITY": { "LEGEND_SECURITY": "Segurança", "LABEL_USE_IMAGE_PROXY": "Usar proxy local para imagens externas", - "LABEL_ALLOW_OPENPGP": "Permitir OpenPGP", + "LABEL_AUTO_VERIFY": "Verify signatures automatically", "LABEL_SHOW_PHP_INFO": "Mostar informações PHP", "LEGEND_ADMIN_PANEL_ACCESS_CREDENTIALS": "Credenciais de acesso ao Painel Administrativo", "LABEL_CURRENT_PASSWORD": "Senha atual", "LABEL_NEW_PASSWORD": "Nova senha", "LABEL_REPEAT_PASSWORD": "Confirmar nova senha", "LABEL_REQUIRE_VERIFICATION": "Exigir verificação do certificado SSL", + "CRYPTO": "Crypto", "LABEL_ALLOW_SELF_SIGNED": "Permitir certificados auto-assinados" }, "TAB_PACKAGES": { @@ -135,6 +136,7 @@ "SECURE_OPTION_NONE": "Nenhuma", "LABEL_ALLOW_SIEVE_SCRIPTS": "Permitir scripts sieve", "LABEL_USE_SHORT_LOGIN": "Usar login curto", + "LABEL_LOWERCASE_LOGIN": "Lowercase login (case-insensitive)", "LABEL_USE_AUTH": "Usar autenticação", "LABEL_SET_SENDER": "Use login as sender", "LABEL_AUTH_PLAIN_LINE": "Force single command AUTH PLAIN login", @@ -168,17 +170,17 @@ "UNKNOWN_ERROR": "Erro desconhecido" }, "NOTIFICATIONS": { - "REQUEST_ERROR": "Request failed", - "REQUEST_ABORTED": "Request is aborted", - "REQUEST_TIMEOUT": "Request timed out", - "INVALID_TOKEN": "Senha inválida", - "AUTH_ERROR": "Falha na autenticação", - "DOMAIN_ALREADY_EXISTS": "O domínio já existe", - "CANT_SAVE_PLUGIN_SETTINGS": "Não foi possível salvar as configurações", - "CANT_INSTALL_PACKAGE": "Erro ao instalar pacote de plugin", - "CANT_DELETE_PACKAGE": "Erro ao remover pacote de plugin", - "INVALID_PLUGIN_PACKAGE": "Pacote de plugins inválido", - "UNSUPPORTED_PLUGIN_PACKAGE": "Pacote de plugins insuportados", - "UNKNOWN_ERROR": "Erro desconhecido" + "RequestError": "Request failed", + "RequestAborted": "Request is aborted", + "RequestTimeout": "Request timed out", + "InvalidToken": "Senha inválida", + "AuthError": "Falha na autenticação", + "DomainAlreadyExists": "O domínio já existe", + "CantSavePluginSettings": "Não foi possível salvar as configurações", + "CantInstallPackage": "Erro ao instalar pacote de plugin", + "CantDeletePackage": "Erro ao remover pacote de plugin", + "InvalidPluginPackage": "Pacote de plugins inválido", + "UnsupportedPluginPackage": "Pacote de plugins insuportados", + "UnknownError": "Erro desconhecido" } } diff --git a/snappymail/v/0.0.0/app/localization/pt-BR/user.json b/snappymail/v/0.0.0/app/localization/pt-BR/user.json index 917bb3189..2065b4ef5 100644 --- a/snappymail/v/0.0.0/app/localization/pt-BR/user.json +++ b/snappymail/v/0.0.0/app/localization/pt-BR/user.json @@ -139,6 +139,7 @@ "MENU_VIEW_ORIGINAL": "Mostrar original", "HTML_VIEW": "View HTML message", "PLAIN_VIEW": "View plain text message", + "SWAP_COLORS": "Swap default (background)color", "MENU_DOWNLOAD_ORIGINAL": "Baixar como arquivo .eml", "MENU_FILTER_SIMILAR": "Filtrar mensagens como esta", "MENU_PRINT": "Imprimir", @@ -283,7 +284,8 @@ "ERROR": "Erro %TYPE%: %ERROR%", "CREATE_SELF_SIGNED": "Create self-signed", "VALID_UNTIL": "Valid until", - "PRIVATE_KEY": "Private key" + "PRIVATE_KEY": "Private key", + "ASK_CRYPTKEY_PASS": "Your login passphrase has changed. Please fill in your old login password to decrypt data" }, "OPENPGP": { "POPUP_IMPORT_TITLE": "Importar chave OpenPGP", @@ -509,7 +511,7 @@ "TO_MANY_FOLDERS_DESC_2": "Mostraremos apenas uma parte delas, para evitar problemas de desempenho.", "HELP_DELETE_FOLDER": "Deletar pasta", "HELP_SHOW_HIDE_FOLDER": "Mostrar\/esconder pasta", - "HELP_CHECK_FOR_NEW_MESSAGES": "Ativar\/Desativar verificação por novas mensagens", + "HELP_CHECK_FOR_NEW_MESSAGES": "Ativar verificação por novas mensagens", "HIDE_UNSUBSCRIBED": "Hide unsubscribed folders", "UNHIDE_KOLAB_FOLDERS": "Unhide Kolab folders", "TYPE_CALENDAR": "Calendar", @@ -575,42 +577,43 @@ "LABEL_CLOSE_COMPOSE": "Fechar composição" }, "NOTIFICATIONS": { - "REQUEST_ERROR": "Request failed", - "REQUEST_ABORTED": "Request is aborted", - "REQUEST_TIMEOUT": "Request timed out", - "INVALID_TOKEN": "Senha inválida", - "AUTH_ERROR": "Falha na autenticação", - "CONNECTION_ERROR": "Não foi possível conectar-se ao servidor", - "DOMAIN_ALREADY_EXISTS": "O domínio já existe", - "DOMAIN_NOT_ALLOWED": "Este domínio não é permitido", - "ACCOUNT_NOT_ALLOWED": "Conta não permitida", - "CONTACTS_SYNC_ERROR": "Erro na sincronização dos contatos", - "CANT_GET_MESSAGE_LIST": "Não foi possível obter a lista de mensagens", - "CANT_GET_MESSAGE": "Não foi possível obter a mensagem", - "CANT_DELETE_MESSAGE": "Não foi possível excluir a mensagem", - "CANT_COPY_MESSAGE": "Can't copy message", - "CANT_MOVE_MESSAGE": "Não foi possível mover a mensagem", - "CANT_SAVE_MESSAGE": "Não foi possível salvar a mensagem", - "CANT_SEND_MESSAGE": "Não foi possível enviar a mensagem", - "INVALID_RECIPIENTS": "Destinatário inválido", - "CANT_SAVE_FILTERS": "Não é possível salvar os filtros", - "CANT_GET_FILTERS": "Não é possível obter os filtros", - "CANT_ACTIVATE_FILTERS_SCRIPT": "Can't activate filters script", - "CANT_DELETE_FILTERS_SCRIPT": "Can't delete filters script", - "FILTERS_ARE_NOT_CORRECT": "Os filtros não estão corretos", - "CANT_CREATE_FOLDER": "Não foi possível criar a pasta", - "CANT_RENAME_FOLDER": "Não foi possível renomear a pasta", - "CANT_DELETE_FOLDER": "Não foi possível exluir a pasta", - "CANT_DELETE_NON_EMPTY_FOLDER": "Não é possível excluir diretório não vazio", - "CANT_SUBSCRIBE_FOLDER": "Não foi possível inscrever esta pasta", - "CANT_UNSUBSCRIBE_FOLDER": "Não foi possível desinscrever esta pasta", - "DEMO_SEND_MESSAGE_ERROR": "Por motivos de segurança, esta conta demo não tem permissão para enviar mensagens para endereços de e-mail externo!", - "DEMO_ACCOUNT_ERROR": "For security purposes, this account is not allowed for this action!", - "ACCOUNT_ALREADY_EXISTS": "Esta conta já existe", - "ACCOUNT_DOES_NOT_EXIST": "Conta não existente", - "ACCOUNT_SWITCH_FAILED": "Switch to account \"%EMAIL%\" failed", - "MAIL_SERVER_ERROR": "Ocorreu um erro ao acessar o servidor de e-mail", - "INVALID_INPUT_ARGUMENT": "Argumento de entrada inválido", - "UNKNOWN_ERROR": "Erro desconhecido" + "RequestError": "Request failed", + "RequestAborted": "Request is aborted", + "RequestTimeout": "Request timed out", + "InvalidToken": "Senha inválida", + "AuthError": "Falha na autenticação", + "ConnectionError": "Não foi possível conectar-se ao servidor", + "DomainAlreadyExists": "O domínio já existe", + "DomainNotAllowed": "Este domínio não é permitido", + "AccountNotAllowed": "Conta não permitida", + "CryptKeyError": "Your login passphrase has changed", + "ContactsSyncError": "Erro na sincronização dos contatos", + "CantGetMessageList": "Não foi possível obter a lista de mensagens", + "CantGetMessage": "Não foi possível obter a mensagem", + "CantDeleteMessage": "Não foi possível excluir a mensagem", + "CantCopyMessage": "Can't copy message", + "CantMoveMessage": "Não foi possível mover a mensagem", + "CantSaveMessage": "Não foi possível salvar a mensagem", + "CantSendMessage": "Não foi possível enviar a mensagem", + "InvalidRecipients": "Destinatário inválido", + "CantSaveFilters": "Não é possível salvar os filtros", + "CantGetFilters": "Não é possível obter os filtros", + "CantActivateFiltersScript": "Can't activate filters script", + "CantDeleteFiltersScript": "Can't delete filters script", + "FiltersAreNotCorrect": "Os filtros não estão corretos", + "CantCreateFolder": "Não foi possível criar a pasta", + "CantRenameFolder": "Não foi possível renomear a pasta", + "CantDeleteFolder": "Não foi possível exluir a pasta", + "CantDeleteNonEmptyFolder": "Não é possível excluir diretório não vazio", + "CantSubscribeFolder": "Não foi possível inscrever esta pasta", + "CantUnsubscribeFolder": "Não foi possível desinscrever esta pasta", + "DemoSendMessageError": "Por motivos de segurança, esta conta demo não tem permissão para enviar mensagens para endereços de e-mail externo!", + "DemoAccountError": "For security purposes, this account is not allowed for this action!", + "AccountAlreadyExists": "Esta conta já existe", + "AccountDoesNotExist": "Conta não existente", + "AccountSwitchFailed": "Switch to account \"%EMAIL%\" failed", + "MailServerError": "Ocorreu um erro ao acessar o servidor de e-mail", + "InvalidInputArgument": "Argumento de entrada inválido", + "UnknownError": "Erro desconhecido" } } diff --git a/snappymail/v/0.0.0/app/localization/pt-PT/admin.json b/snappymail/v/0.0.0/app/localization/pt-PT/admin.json index 12adf1893..61bf31db4 100644 --- a/snappymail/v/0.0.0/app/localization/pt-PT/admin.json +++ b/snappymail/v/0.0.0/app/localization/pt-PT/admin.json @@ -85,13 +85,14 @@ "TAB_SECURITY": { "LEGEND_SECURITY": "Segurança", "LABEL_USE_IMAGE_PROXY": "Usar proxy local para imagens externas", - "LABEL_ALLOW_OPENPGP": "Permitir OpenPGP", + "LABEL_AUTO_VERIFY": "Verificar assinaturas automaticamente", "LABEL_SHOW_PHP_INFO": "Mostrar informação do PHP", "LEGEND_ADMIN_PANEL_ACCESS_CREDENTIALS": "Credenciais de acesso ao Painel de Admin", "LABEL_CURRENT_PASSWORD": "Palavra-passe atual", "LABEL_NEW_PASSWORD": "Nova palavra-passe", "LABEL_REPEAT_PASSWORD": "Repetir", "LABEL_REQUIRE_VERIFICATION": "Exigir a verificação dos certificados SSL", + "CRYPTO": "Criptografia", "LABEL_ALLOW_SELF_SIGNED": "Permitir certificados autoassinados" }, "TAB_PACKAGES": { @@ -135,6 +136,7 @@ "SECURE_OPTION_NONE": "Nenhuma", "LABEL_ALLOW_SIEVE_SCRIPTS": "Permitir scripts sieve", "LABEL_USE_SHORT_LOGIN": "Usar nome de utilizador curto", + "LABEL_LOWERCASE_LOGIN": "Não diferenciar maiúsculas e minúsculas no nome de utilizador", "LABEL_USE_AUTH": "Usar autenticação", "LABEL_SET_SENDER": "Usar nome de utilizador como remetente", "LABEL_AUTH_PLAIN_LINE": "Forçar comando AUTH PLAIN único para autenticar", @@ -168,17 +170,17 @@ "UNKNOWN_ERROR": "Erro desconhecido" }, "NOTIFICATIONS": { - "REQUEST_ERROR": "A solicitação falhou", - "REQUEST_ABORTED": "A solicitação foi abortada", - "REQUEST_TIMEOUT": "A solicitação expirou", - "INVALID_TOKEN": "Token inválido", - "AUTH_ERROR": "A autenticação falhou", - "DOMAIN_ALREADY_EXISTS": "O domínio já existe", - "CANT_SAVE_PLUGIN_SETTINGS": "Não é possível guardar as definições", - "CANT_INSTALL_PACKAGE": "Não foi possível instalar a extensão", - "CANT_DELETE_PACKAGE": "Não foi possível remover a extensão", - "INVALID_PLUGIN_PACKAGE": "Pacote de extensão inválido", - "UNSUPPORTED_PLUGIN_PACKAGE": "Pacote de extensão não suportado", - "UNKNOWN_ERROR": "Erro desconhecido" + "RequestError": "A solicitação falhou", + "RequestAborted": "A solicitação foi abortada", + "RequestTimeout": "A solicitação expirou", + "InvalidToken": "Token inválido", + "AuthError": "A autenticação falhou", + "DomainAlreadyExists": "O domínio já existe", + "CantSavePluginSettings": "Não é possível guardar as definições", + "CantInstallPackage": "Não foi possível instalar a extensão", + "CantDeletePackage": "Não foi possível remover a extensão", + "InvalidPluginPackage": "Pacote de extensão inválido", + "UnsupportedPluginPackage": "Pacote de extensão não suportado", + "UnknownError": "Erro desconhecido" } } diff --git a/snappymail/v/0.0.0/app/localization/pt-PT/user.json b/snappymail/v/0.0.0/app/localization/pt-PT/user.json index cdd061104..594b6ddbf 100644 --- a/snappymail/v/0.0.0/app/localization/pt-PT/user.json +++ b/snappymail/v/0.0.0/app/localization/pt-PT/user.json @@ -139,6 +139,7 @@ "MENU_VIEW_ORIGINAL": "Mostrar original", "HTML_VIEW": "Ver mensagem em HTML", "PLAIN_VIEW": "Ver mensagem em texto simples", + "SWAP_COLORS": "Swap default (background)color", "MENU_DOWNLOAD_ORIGINAL": "Transferir como ficheiro .eml", "MENU_FILTER_SIMILAR": "Filtrar mensagens semelhantes a esta", "MENU_PRINT": "Imprimir", @@ -283,7 +284,8 @@ "ERROR": "Erro %TYPE%: %ERROR%", "CREATE_SELF_SIGNED": "Criar auto-assinado", "VALID_UNTIL": "Válido até", - "PRIVATE_KEY": "Private key" + "PRIVATE_KEY": "Chave privada", + "ASK_CRYPTKEY_PASS": "Your login passphrase has changed. Please fill in your old login password to decrypt data" }, "OPENPGP": { "POPUP_IMPORT_TITLE": "Importar chave OpenPGP", @@ -304,7 +306,7 @@ }, "SMIME": { "POPUP_IMPORT_TITLE": "Importar certificado S\/MIME", - "CERTIFICATE": "Certificate", + "CERTIFICATE": "Certificado", "CERTIFICATES": "Certificados S\/MIME", "SIGNED_MESSAGE": "Mensagem assinada com S\/MIME", "ENCRYPTED_MESSAGE": "Mensagem encriptada com S\/MIME", @@ -509,7 +511,7 @@ "TO_MANY_FOLDERS_DESC_2": "Apenas apresentámos parte delas, para evitar problemas de desempenho.", "HELP_DELETE_FOLDER": "Eliminar pasta", "HELP_SHOW_HIDE_FOLDER": "Mostrar\/ocultar pasta", - "HELP_CHECK_FOR_NEW_MESSAGES": "Procurar\/não procurar novas mensagens", + "HELP_CHECK_FOR_NEW_MESSAGES": "Procurar novas mensagens", "HIDE_UNSUBSCRIBED": "Esconder pastas não subscritas", "UNHIDE_KOLAB_FOLDERS": "Tonar pastas Kolab visíveis", "TYPE_CALENDAR": "Calendário", @@ -575,42 +577,43 @@ "LABEL_CLOSE_COMPOSE": "Fechar janela de composição" }, "NOTIFICATIONS": { - "REQUEST_ERROR": "A solicitação falhou", - "REQUEST_ABORTED": "A solicitação foi abortada", - "REQUEST_TIMEOUT": "A solicitação expirou", - "INVALID_TOKEN": "Token inválido", - "AUTH_ERROR": "A autenticação falhou", - "CONNECTION_ERROR": "Não foi possível ligar ao servidor", - "DOMAIN_ALREADY_EXISTS": "O domínio já existe", - "DOMAIN_NOT_ALLOWED": "Este domínio não é permitido", - "ACCOUNT_NOT_ALLOWED": "Esta conta não é permitida", - "CONTACTS_SYNC_ERROR": "Erro na sincronização de contactos", - "CANT_GET_MESSAGE_LIST": "Não é possível obter a lista de mensagens", - "CANT_GET_MESSAGE": "Não é possível obter a mensagem", - "CANT_DELETE_MESSAGE": "Não é possível eliminar a mensagem", - "CANT_COPY_MESSAGE": "Não é possível copiar a mensagem", - "CANT_MOVE_MESSAGE": "Não é possível mover a mensagem", - "CANT_SAVE_MESSAGE": "Não é possível guardar a mensagem", - "CANT_SEND_MESSAGE": "Não é possível enviar a mensagem", - "INVALID_RECIPIENTS": "Destinatários inválidos", - "CANT_SAVE_FILTERS": "Não é possível guardar os filtros", - "CANT_GET_FILTERS": "Não é possível obter os filtros", - "CANT_ACTIVATE_FILTERS_SCRIPT": "Não é possível ativar o script de filtração", - "CANT_DELETE_FILTERS_SCRIPT": "Não é possível apagar o script de filtração", - "FILTERS_ARE_NOT_CORRECT": "Os filtros não estão corretos", - "CANT_CREATE_FOLDER": "Não é possível criar a pasta", - "CANT_RENAME_FOLDER": "Não é possível renomear a pasta", - "CANT_DELETE_FOLDER": "Não é possível eliminar a pasta", - "CANT_DELETE_NON_EMPTY_FOLDER": "Não é possível eliminar pastas com conteúdos", - "CANT_SUBSCRIBE_FOLDER": "Não é possível subscrever a esta pasta", - "CANT_UNSUBSCRIBE_FOLDER": "Não é possível anular a subsrição desta pasta", - "DEMO_SEND_MESSAGE_ERROR": "Por motivos de segurança, esta conta de demonstração não tem permissão para enviar mensagens para endereços de e-mail externos!", - "DEMO_ACCOUNT_ERROR": "Por motivos de segurança, esta conta não tem permissão de executar esta ação!", - "ACCOUNT_ALREADY_EXISTS": "Esta conta já existe", - "ACCOUNT_DOES_NOT_EXIST": "Esta conta não existe", - "ACCOUNT_SWITCH_FAILED": "A mudança para a conta \"%EMAIL%\" falhou", - "MAIL_SERVER_ERROR": "Ocorreu um erro ao aceder ao servidor de correio", - "INVALID_INPUT_ARGUMENT": "Parâmetro de entrada inválido", - "UNKNOWN_ERROR": "Erro desconhecido" + "RequestError": "A solicitação falhou", + "RequestAborted": "A solicitação foi abortada", + "RequestTimeout": "A solicitação expirou", + "InvalidToken": "Token inválido", + "AuthError": "A autenticação falhou", + "ConnectionError": "Não foi possível ligar ao servidor", + "DomainAlreadyExists": "O domínio já existe", + "DomainNotAllowed": "Este domínio não é permitido", + "AccountNotAllowed": "Esta conta não é permitida", + "CryptKeyError": "Your login passphrase has changed", + "ContactsSyncError": "Erro na sincronização de contactos", + "CantGetMessageList": "Não é possível obter a lista de mensagens", + "CantGetMessage": "Não é possível obter a mensagem", + "CantDeleteMessage": "Não é possível eliminar a mensagem", + "CantCopyMessage": "Não é possível copiar a mensagem", + "CantMoveMessage": "Não é possível mover a mensagem", + "CantSaveMessage": "Não é possível guardar a mensagem", + "CantSendMessage": "Não é possível enviar a mensagem", + "InvalidRecipients": "Destinatários inválidos", + "CantSaveFilters": "Não é possível guardar os filtros", + "CantGetFilters": "Não é possível obter os filtros", + "CantActivateFiltersScript": "Não é possível ativar o script de filtração", + "CantDeleteFiltersScript": "Não é possível apagar o script de filtração", + "FiltersAreNotCorrect": "Os filtros não estão corretos", + "CantCreateFolder": "Não é possível criar a pasta", + "CantRenameFolder": "Não é possível renomear a pasta", + "CantDeleteFolder": "Não é possível eliminar a pasta", + "CantDeleteNonEmptyFolder": "Não é possível eliminar pastas com conteúdos", + "CantSubscribeFolder": "Não é possível subscrever a esta pasta", + "CantUnsubscribeFolder": "Não é possível anular a subsrição desta pasta", + "DemoSendMessageError": "Por motivos de segurança, esta conta de demonstração não tem permissão para enviar mensagens para endereços de e-mail externos!", + "DemoAccountError": "Por motivos de segurança, esta conta não tem permissão de executar esta ação!", + "AccountAlreadyExists": "Esta conta já existe", + "AccountDoesNotExist": "Esta conta não existe", + "AccountSwitchFailed": "A mudança para a conta \"%EMAIL%\" falhou", + "MailServerError": "Ocorreu um erro ao aceder ao servidor de correio", + "InvalidInputArgument": "Parâmetro de entrada inválido", + "UnknownError": "Erro desconhecido" } } diff --git a/snappymail/v/0.0.0/app/localization/pt/admin.json b/snappymail/v/0.0.0/app/localization/pt/admin.json index 12adf1893..61bf31db4 100644 --- a/snappymail/v/0.0.0/app/localization/pt/admin.json +++ b/snappymail/v/0.0.0/app/localization/pt/admin.json @@ -85,13 +85,14 @@ "TAB_SECURITY": { "LEGEND_SECURITY": "Segurança", "LABEL_USE_IMAGE_PROXY": "Usar proxy local para imagens externas", - "LABEL_ALLOW_OPENPGP": "Permitir OpenPGP", + "LABEL_AUTO_VERIFY": "Verificar assinaturas automaticamente", "LABEL_SHOW_PHP_INFO": "Mostrar informação do PHP", "LEGEND_ADMIN_PANEL_ACCESS_CREDENTIALS": "Credenciais de acesso ao Painel de Admin", "LABEL_CURRENT_PASSWORD": "Palavra-passe atual", "LABEL_NEW_PASSWORD": "Nova palavra-passe", "LABEL_REPEAT_PASSWORD": "Repetir", "LABEL_REQUIRE_VERIFICATION": "Exigir a verificação dos certificados SSL", + "CRYPTO": "Criptografia", "LABEL_ALLOW_SELF_SIGNED": "Permitir certificados autoassinados" }, "TAB_PACKAGES": { @@ -135,6 +136,7 @@ "SECURE_OPTION_NONE": "Nenhuma", "LABEL_ALLOW_SIEVE_SCRIPTS": "Permitir scripts sieve", "LABEL_USE_SHORT_LOGIN": "Usar nome de utilizador curto", + "LABEL_LOWERCASE_LOGIN": "Não diferenciar maiúsculas e minúsculas no nome de utilizador", "LABEL_USE_AUTH": "Usar autenticação", "LABEL_SET_SENDER": "Usar nome de utilizador como remetente", "LABEL_AUTH_PLAIN_LINE": "Forçar comando AUTH PLAIN único para autenticar", @@ -168,17 +170,17 @@ "UNKNOWN_ERROR": "Erro desconhecido" }, "NOTIFICATIONS": { - "REQUEST_ERROR": "A solicitação falhou", - "REQUEST_ABORTED": "A solicitação foi abortada", - "REQUEST_TIMEOUT": "A solicitação expirou", - "INVALID_TOKEN": "Token inválido", - "AUTH_ERROR": "A autenticação falhou", - "DOMAIN_ALREADY_EXISTS": "O domínio já existe", - "CANT_SAVE_PLUGIN_SETTINGS": "Não é possível guardar as definições", - "CANT_INSTALL_PACKAGE": "Não foi possível instalar a extensão", - "CANT_DELETE_PACKAGE": "Não foi possível remover a extensão", - "INVALID_PLUGIN_PACKAGE": "Pacote de extensão inválido", - "UNSUPPORTED_PLUGIN_PACKAGE": "Pacote de extensão não suportado", - "UNKNOWN_ERROR": "Erro desconhecido" + "RequestError": "A solicitação falhou", + "RequestAborted": "A solicitação foi abortada", + "RequestTimeout": "A solicitação expirou", + "InvalidToken": "Token inválido", + "AuthError": "A autenticação falhou", + "DomainAlreadyExists": "O domínio já existe", + "CantSavePluginSettings": "Não é possível guardar as definições", + "CantInstallPackage": "Não foi possível instalar a extensão", + "CantDeletePackage": "Não foi possível remover a extensão", + "InvalidPluginPackage": "Pacote de extensão inválido", + "UnsupportedPluginPackage": "Pacote de extensão não suportado", + "UnknownError": "Erro desconhecido" } } diff --git a/snappymail/v/0.0.0/app/localization/pt/user.json b/snappymail/v/0.0.0/app/localization/pt/user.json index cdd061104..594b6ddbf 100644 --- a/snappymail/v/0.0.0/app/localization/pt/user.json +++ b/snappymail/v/0.0.0/app/localization/pt/user.json @@ -139,6 +139,7 @@ "MENU_VIEW_ORIGINAL": "Mostrar original", "HTML_VIEW": "Ver mensagem em HTML", "PLAIN_VIEW": "Ver mensagem em texto simples", + "SWAP_COLORS": "Swap default (background)color", "MENU_DOWNLOAD_ORIGINAL": "Transferir como ficheiro .eml", "MENU_FILTER_SIMILAR": "Filtrar mensagens semelhantes a esta", "MENU_PRINT": "Imprimir", @@ -283,7 +284,8 @@ "ERROR": "Erro %TYPE%: %ERROR%", "CREATE_SELF_SIGNED": "Criar auto-assinado", "VALID_UNTIL": "Válido até", - "PRIVATE_KEY": "Private key" + "PRIVATE_KEY": "Chave privada", + "ASK_CRYPTKEY_PASS": "Your login passphrase has changed. Please fill in your old login password to decrypt data" }, "OPENPGP": { "POPUP_IMPORT_TITLE": "Importar chave OpenPGP", @@ -304,7 +306,7 @@ }, "SMIME": { "POPUP_IMPORT_TITLE": "Importar certificado S\/MIME", - "CERTIFICATE": "Certificate", + "CERTIFICATE": "Certificado", "CERTIFICATES": "Certificados S\/MIME", "SIGNED_MESSAGE": "Mensagem assinada com S\/MIME", "ENCRYPTED_MESSAGE": "Mensagem encriptada com S\/MIME", @@ -509,7 +511,7 @@ "TO_MANY_FOLDERS_DESC_2": "Apenas apresentámos parte delas, para evitar problemas de desempenho.", "HELP_DELETE_FOLDER": "Eliminar pasta", "HELP_SHOW_HIDE_FOLDER": "Mostrar\/ocultar pasta", - "HELP_CHECK_FOR_NEW_MESSAGES": "Procurar\/não procurar novas mensagens", + "HELP_CHECK_FOR_NEW_MESSAGES": "Procurar novas mensagens", "HIDE_UNSUBSCRIBED": "Esconder pastas não subscritas", "UNHIDE_KOLAB_FOLDERS": "Tonar pastas Kolab visíveis", "TYPE_CALENDAR": "Calendário", @@ -575,42 +577,43 @@ "LABEL_CLOSE_COMPOSE": "Fechar janela de composição" }, "NOTIFICATIONS": { - "REQUEST_ERROR": "A solicitação falhou", - "REQUEST_ABORTED": "A solicitação foi abortada", - "REQUEST_TIMEOUT": "A solicitação expirou", - "INVALID_TOKEN": "Token inválido", - "AUTH_ERROR": "A autenticação falhou", - "CONNECTION_ERROR": "Não foi possível ligar ao servidor", - "DOMAIN_ALREADY_EXISTS": "O domínio já existe", - "DOMAIN_NOT_ALLOWED": "Este domínio não é permitido", - "ACCOUNT_NOT_ALLOWED": "Esta conta não é permitida", - "CONTACTS_SYNC_ERROR": "Erro na sincronização de contactos", - "CANT_GET_MESSAGE_LIST": "Não é possível obter a lista de mensagens", - "CANT_GET_MESSAGE": "Não é possível obter a mensagem", - "CANT_DELETE_MESSAGE": "Não é possível eliminar a mensagem", - "CANT_COPY_MESSAGE": "Não é possível copiar a mensagem", - "CANT_MOVE_MESSAGE": "Não é possível mover a mensagem", - "CANT_SAVE_MESSAGE": "Não é possível guardar a mensagem", - "CANT_SEND_MESSAGE": "Não é possível enviar a mensagem", - "INVALID_RECIPIENTS": "Destinatários inválidos", - "CANT_SAVE_FILTERS": "Não é possível guardar os filtros", - "CANT_GET_FILTERS": "Não é possível obter os filtros", - "CANT_ACTIVATE_FILTERS_SCRIPT": "Não é possível ativar o script de filtração", - "CANT_DELETE_FILTERS_SCRIPT": "Não é possível apagar o script de filtração", - "FILTERS_ARE_NOT_CORRECT": "Os filtros não estão corretos", - "CANT_CREATE_FOLDER": "Não é possível criar a pasta", - "CANT_RENAME_FOLDER": "Não é possível renomear a pasta", - "CANT_DELETE_FOLDER": "Não é possível eliminar a pasta", - "CANT_DELETE_NON_EMPTY_FOLDER": "Não é possível eliminar pastas com conteúdos", - "CANT_SUBSCRIBE_FOLDER": "Não é possível subscrever a esta pasta", - "CANT_UNSUBSCRIBE_FOLDER": "Não é possível anular a subsrição desta pasta", - "DEMO_SEND_MESSAGE_ERROR": "Por motivos de segurança, esta conta de demonstração não tem permissão para enviar mensagens para endereços de e-mail externos!", - "DEMO_ACCOUNT_ERROR": "Por motivos de segurança, esta conta não tem permissão de executar esta ação!", - "ACCOUNT_ALREADY_EXISTS": "Esta conta já existe", - "ACCOUNT_DOES_NOT_EXIST": "Esta conta não existe", - "ACCOUNT_SWITCH_FAILED": "A mudança para a conta \"%EMAIL%\" falhou", - "MAIL_SERVER_ERROR": "Ocorreu um erro ao aceder ao servidor de correio", - "INVALID_INPUT_ARGUMENT": "Parâmetro de entrada inválido", - "UNKNOWN_ERROR": "Erro desconhecido" + "RequestError": "A solicitação falhou", + "RequestAborted": "A solicitação foi abortada", + "RequestTimeout": "A solicitação expirou", + "InvalidToken": "Token inválido", + "AuthError": "A autenticação falhou", + "ConnectionError": "Não foi possível ligar ao servidor", + "DomainAlreadyExists": "O domínio já existe", + "DomainNotAllowed": "Este domínio não é permitido", + "AccountNotAllowed": "Esta conta não é permitida", + "CryptKeyError": "Your login passphrase has changed", + "ContactsSyncError": "Erro na sincronização de contactos", + "CantGetMessageList": "Não é possível obter a lista de mensagens", + "CantGetMessage": "Não é possível obter a mensagem", + "CantDeleteMessage": "Não é possível eliminar a mensagem", + "CantCopyMessage": "Não é possível copiar a mensagem", + "CantMoveMessage": "Não é possível mover a mensagem", + "CantSaveMessage": "Não é possível guardar a mensagem", + "CantSendMessage": "Não é possível enviar a mensagem", + "InvalidRecipients": "Destinatários inválidos", + "CantSaveFilters": "Não é possível guardar os filtros", + "CantGetFilters": "Não é possível obter os filtros", + "CantActivateFiltersScript": "Não é possível ativar o script de filtração", + "CantDeleteFiltersScript": "Não é possível apagar o script de filtração", + "FiltersAreNotCorrect": "Os filtros não estão corretos", + "CantCreateFolder": "Não é possível criar a pasta", + "CantRenameFolder": "Não é possível renomear a pasta", + "CantDeleteFolder": "Não é possível eliminar a pasta", + "CantDeleteNonEmptyFolder": "Não é possível eliminar pastas com conteúdos", + "CantSubscribeFolder": "Não é possível subscrever a esta pasta", + "CantUnsubscribeFolder": "Não é possível anular a subsrição desta pasta", + "DemoSendMessageError": "Por motivos de segurança, esta conta de demonstração não tem permissão para enviar mensagens para endereços de e-mail externos!", + "DemoAccountError": "Por motivos de segurança, esta conta não tem permissão de executar esta ação!", + "AccountAlreadyExists": "Esta conta já existe", + "AccountDoesNotExist": "Esta conta não existe", + "AccountSwitchFailed": "A mudança para a conta \"%EMAIL%\" falhou", + "MailServerError": "Ocorreu um erro ao aceder ao servidor de correio", + "InvalidInputArgument": "Parâmetro de entrada inválido", + "UnknownError": "Erro desconhecido" } } diff --git a/snappymail/v/0.0.0/app/localization/ro-RO/user.json b/snappymail/v/0.0.0/app/localization/ro-RO/user.json index f6bf1d448..b52d0ff3c 100644 --- a/snappymail/v/0.0.0/app/localization/ro-RO/user.json +++ b/snappymail/v/0.0.0/app/localization/ro-RO/user.json @@ -139,6 +139,7 @@ "MENU_VIEW_ORIGINAL": "Vezi originalul", "HTML_VIEW": "View HTML message", "PLAIN_VIEW": "View plain text message", + "SWAP_COLORS": "Swap default (background)color", "MENU_DOWNLOAD_ORIGINAL": "Descărcați ca fișier eml", "MENU_FILTER_SIMILAR": "Filtrează similarele", "MENU_PRINT": "Printează", @@ -283,7 +284,8 @@ "ERROR": "%TYPE% error: %ERROR%", "CREATE_SELF_SIGNED": "Create self-signed", "VALID_UNTIL": "Valid until", - "PRIVATE_KEY": "Private key" + "PRIVATE_KEY": "Private key", + "ASK_CRYPTKEY_PASS": "Your login passphrase has changed. Please fill in your old login password to decrypt data" }, "OPENPGP": { "POPUP_IMPORT_TITLE": "Import OpenPGP key", @@ -509,7 +511,7 @@ "TO_MANY_FOLDERS_DESC_2": "We have shown only a part of them, to avoid performance problems.", "HELP_DELETE_FOLDER": "Delete folder", "HELP_SHOW_HIDE_FOLDER": "Show\/hide folder", - "HELP_CHECK_FOR_NEW_MESSAGES": "Check\/don't check for new messages", + "HELP_CHECK_FOR_NEW_MESSAGES": "Check for new messages", "HIDE_UNSUBSCRIBED": "Hide unsubscribed folders", "UNHIDE_KOLAB_FOLDERS": "Unhide Kolab folders", "TYPE_CALENDAR": "Calendar", @@ -575,42 +577,43 @@ "LABEL_CLOSE_COMPOSE": "Close compose" }, "NOTIFICATIONS": { - "REQUEST_ERROR": "Request failed", - "REQUEST_ABORTED": "Request is aborted", - "REQUEST_TIMEOUT": "Request timed out", - "INVALID_TOKEN": "Semn invalid", - "AUTH_ERROR": "Nu se poate realiza conectarea", - "CONNECTION_ERROR": "Eroare la conectarea la server.", - "DOMAIN_ALREADY_EXISTS": "Domeniu există deja.", - "DOMAIN_NOT_ALLOWED": "Domeniul nu apartine de Wey5", - "ACCOUNT_NOT_ALLOWED": "Contul nu are permisiunea de conectare", - "CONTACTS_SYNC_ERROR": "Contacts synchronization error", - "CANT_GET_MESSAGE_LIST": "Nu găsesc o lista de scrisori", - "CANT_GET_MESSAGE": "Nu pot obține scrisoarea. Încercați din nou", - "CANT_DELETE_MESSAGE": "Nu pot șterge scrisoarea. Încercați din nou", - "CANT_COPY_MESSAGE": "Can't copy message", - "CANT_MOVE_MESSAGE": "Nu pot muta scrisoarea.Încercați din nou", - "CANT_SAVE_MESSAGE": "Nu pot salva mesajul. Încercați din nou", - "CANT_SEND_MESSAGE": "Nu pot trimite scrisoarea.Încercați din nou", - "INVALID_RECIPIENTS": "Verificați dacă ați introdus toate adresele.", - "CANT_SAVE_FILTERS": "Can't save filters", - "CANT_GET_FILTERS": "Can't get filters", - "CANT_ACTIVATE_FILTERS_SCRIPT": "Can't activate filters script", - "CANT_DELETE_FILTERS_SCRIPT": "Can't delete filters script", - "FILTERS_ARE_NOT_CORRECT": "Filters are not correct", - "CANT_CREATE_FOLDER": "Nu crea dosarul. Încercați din nou", - "CANT_RENAME_FOLDER": "Nu pot redenumi dosarul. Încercați din nou", - "CANT_DELETE_FOLDER": "Nu pot șterge dosarul. Încercați din nou", - "CANT_DELETE_NON_EMPTY_FOLDER": "Nu pot șterge un dosar care nu este gol", - "CANT_SUBSCRIBE_FOLDER": "Nu pot semna dosarul. Încercați din nou", - "CANT_UNSUBSCRIBE_FOLDER": "Nu mă pot dezbona. Încercați din nou", - "DEMO_SEND_MESSAGE_ERROR": "Cont demo trimite e-mail la adresele de e-mail externe este interzisă!", - "DEMO_ACCOUNT_ERROR": "For security purposes, this account is not allowed for this action!", - "ACCOUNT_ALREADY_EXISTS": "Contul deja există", - "ACCOUNT_DOES_NOT_EXIST": "contul nu există", - "ACCOUNT_SWITCH_FAILED": "Switch to account \"%EMAIL%\" failed", - "MAIL_SERVER_ERROR": "Nu am reușit să accesez serverul de e-mail", - "INVALID_INPUT_ARGUMENT": "Invalid input argument", - "UNKNOWN_ERROR": "Eroare necunoscută" + "RequestError": "Request failed", + "RequestAborted": "Request is aborted", + "RequestTimeout": "Request timed out", + "InvalidToken": "Semn invalid", + "AuthError": "Nu se poate realiza conectarea", + "ConnectionError": "Eroare la conectarea la server.", + "DomainAlreadyExists": "Domeniu există deja.", + "DomainNotAllowed": "Domeniul nu apartine de Wey5", + "AccountNotAllowed": "Contul nu are permisiunea de conectare", + "CryptKeyError": "Your login passphrase has changed", + "ContactsSyncError": "Contacts synchronization error", + "CantGetMessageList": "Nu găsesc o lista de scrisori", + "CantGetMessage": "Nu pot obține scrisoarea. Încercați din nou", + "CantDeleteMessage": "Nu pot șterge scrisoarea. Încercați din nou", + "CantCopyMessage": "Can't copy message", + "CantMoveMessage": "Nu pot muta scrisoarea.Încercați din nou", + "CantSaveMessage": "Nu pot salva mesajul. Încercați din nou", + "CantSendMessage": "Nu pot trimite scrisoarea.Încercați din nou", + "InvalidRecipients": "Verificați dacă ați introdus toate adresele.", + "CantSaveFilters": "Can't save filters", + "CantGetFilters": "Can't get filters", + "CantActivateFiltersScript": "Can't activate filters script", + "CantDeleteFiltersScript": "Can't delete filters script", + "FiltersAreNotCorrect": "Filters are not correct", + "CantCreateFolder": "Nu crea dosarul. Încercați din nou", + "CantRenameFolder": "Nu pot redenumi dosarul. Încercați din nou", + "CantDeleteFolder": "Nu pot șterge dosarul. Încercați din nou", + "CantDeleteNonEmptyFolder": "Nu pot șterge un dosar care nu este gol", + "CantSubscribeFolder": "Nu pot semna dosarul. Încercați din nou", + "CantUnsubscribeFolder": "Nu mă pot dezbona. Încercați din nou", + "DemoSendMessageError": "Cont demo trimite e-mail la adresele de e-mail externe este interzisă!", + "DemoAccountError": "For security purposes, this account is not allowed for this action!", + "AccountAlreadyExists": "Contul deja există", + "AccountDoesNotExist": "contul nu există", + "AccountSwitchFailed": "Switch to account \"%EMAIL%\" failed", + "MailServerError": "Nu am reușit să accesez serverul de e-mail", + "InvalidInputArgument": "Invalid input argument", + "UnknownError": "Eroare necunoscută" } } diff --git a/snappymail/v/0.0.0/app/localization/ru-RU/admin.json b/snappymail/v/0.0.0/app/localization/ru-RU/admin.json index d516e2a7f..a96033931 100644 --- a/snappymail/v/0.0.0/app/localization/ru-RU/admin.json +++ b/snappymail/v/0.0.0/app/localization/ru-RU/admin.json @@ -85,13 +85,14 @@ "TAB_SECURITY": { "LEGEND_SECURITY": "Безопасность", "LABEL_USE_IMAGE_PROXY": "Использовать локальный прокси для внешних изображений", - "LABEL_ALLOW_OPENPGP": "Разрешить OpenPGP", + "LABEL_AUTO_VERIFY": "Verify signatures automatically", "LABEL_SHOW_PHP_INFO": "Показать PHP информацию", "LEGEND_ADMIN_PANEL_ACCESS_CREDENTIALS": "Учетные данные для доступа к панели администратора", "LABEL_CURRENT_PASSWORD": "Текущий пароль", "LABEL_NEW_PASSWORD": "Новый пароль", "LABEL_REPEAT_PASSWORD": "Повторить пароль", "LABEL_REQUIRE_VERIFICATION": "Требовать проверку SSL сертификата для", + "CRYPTO": "Шифрование", "LABEL_ALLOW_SELF_SIGNED": "Разрешить самоподписанные сертификаты" }, "TAB_PACKAGES": { @@ -135,6 +136,7 @@ "SECURE_OPTION_NONE": "Нет", "LABEL_ALLOW_SIEVE_SCRIPTS": "Разрешить Sieve сценарии", "LABEL_USE_SHORT_LOGIN": "Использовать короткий логин", + "LABEL_LOWERCASE_LOGIN": "Lowercase login (case-insensitive)", "LABEL_USE_AUTH": "Использовать аутентификацию", "LABEL_SET_SENDER": "Использовать логин в качестве отправителя", "LABEL_AUTH_PLAIN_LINE": "Принудительный вход командой AUTH PLAIN", @@ -168,17 +170,17 @@ "UNKNOWN_ERROR": "Неизвестная ошибка" }, "NOTIFICATIONS": { - "REQUEST_ERROR": "Запрос не выполнен", - "REQUEST_ABORTED": "Запрос прерван", - "REQUEST_TIMEOUT": "Истекло время запроса", - "INVALID_TOKEN": "Неверный токен запроса", - "AUTH_ERROR": "Не удалось авторизоваться", - "DOMAIN_ALREADY_EXISTS": "Домен уже существует", - "CANT_SAVE_PLUGIN_SETTINGS": "Не могу сохранить настройки", - "CANT_INSTALL_PACKAGE": "Ошибка установки плагина", - "CANT_DELETE_PACKAGE": "Ошибка удаления плагина", - "INVALID_PLUGIN_PACKAGE": "Ошибка пакета плагина", - "UNSUPPORTED_PLUGIN_PACKAGE": "Для работы плагина необходима полная поддержка сервера", - "UNKNOWN_ERROR": "Неизвестная ошибка" + "RequestError": "Запрос не выполнен", + "RequestAborted": "Запрос прерван", + "RequestTimeout": "Истекло время запроса", + "InvalidToken": "Неверный токен запроса", + "AuthError": "Не удалось авторизоваться", + "DomainAlreadyExists": "Домен уже существует", + "CantSavePluginSettings": "Не могу сохранить настройки", + "CantInstallPackage": "Ошибка установки плагина", + "CantDeletePackage": "Ошибка удаления плагина", + "InvalidPluginPackage": "Ошибка пакета плагина", + "UnsupportedPluginPackage": "Для работы плагина необходима полная поддержка сервера", + "UnknownError": "Неизвестная ошибка" } } diff --git a/snappymail/v/0.0.0/app/localization/ru-RU/user.json b/snappymail/v/0.0.0/app/localization/ru-RU/user.json index a9b841bc7..3ad363bbe 100644 --- a/snappymail/v/0.0.0/app/localization/ru-RU/user.json +++ b/snappymail/v/0.0.0/app/localization/ru-RU/user.json @@ -139,6 +139,7 @@ "MENU_VIEW_ORIGINAL": "Просмотреть оригинал", "HTML_VIEW": "Просмотр сообщения в HTML", "PLAIN_VIEW": "Просмотр простого текстового сообщения", + "SWAP_COLORS": "Swap default (background)color", "MENU_DOWNLOAD_ORIGINAL": "Скачать как .eml файл", "MENU_FILTER_SIMILAR": "Фильтровать похожие", "MENU_PRINT": "Распечатать", @@ -283,7 +284,8 @@ "ERROR": "%TYPE% ошибка: %ERROR%", "CREATE_SELF_SIGNED": "Create self-signed", "VALID_UNTIL": "Valid until", - "PRIVATE_KEY": "Private key" + "PRIVATE_KEY": "Private key", + "ASK_CRYPTKEY_PASS": "Your login passphrase has changed. Please fill in your old login password to decrypt data" }, "OPENPGP": { "POPUP_IMPORT_TITLE": "Импорт OpenPGP ключа", @@ -509,7 +511,7 @@ "TO_MANY_FOLDERS_DESC_2": "Мы показали только часть из них, чтобы избежать проблем с производительностью.", "HELP_DELETE_FOLDER": "Удалить папку", "HELP_SHOW_HIDE_FOLDER": "Показывать\/прятать папку", - "HELP_CHECK_FOR_NEW_MESSAGES": "Проверять\/не проверять новые сообщения", + "HELP_CHECK_FOR_NEW_MESSAGES": "Проверять новые сообщения", "HIDE_UNSUBSCRIBED": "Скрыть папки, на которые нет подписки", "UNHIDE_KOLAB_FOLDERS": "Показывать папки Kolab", "TYPE_CALENDAR": "Календарь", @@ -575,42 +577,43 @@ "LABEL_CLOSE_COMPOSE": "Закрыть сообщение" }, "NOTIFICATIONS": { - "REQUEST_ERROR": "Запрос не выполнен", - "REQUEST_ABORTED": "Запрос прерван", - "REQUEST_TIMEOUT": "Истекло время запроса", - "INVALID_TOKEN": "Неверный токен запроса", - "AUTH_ERROR": "Не удалось авторизоваться", - "CONNECTION_ERROR": "Ошибка соединения с сервером.", - "DOMAIN_ALREADY_EXISTS": "Домен уже существует", - "DOMAIN_NOT_ALLOWED": "Данный домен не разрешен", - "ACCOUNT_NOT_ALLOWED": "Данный аккаунт не разрешен", - "CONTACTS_SYNC_ERROR": "Ошибка синхронизации контактов", - "CANT_GET_MESSAGE_LIST": "Не могу получить список писем", - "CANT_GET_MESSAGE": "Не могу получить письмо", - "CANT_DELETE_MESSAGE": "Не могу удалить письмо", - "CANT_COPY_MESSAGE": "Не могу скопировать сообщение", - "CANT_MOVE_MESSAGE": "Не могу переместить письмо", - "CANT_SAVE_MESSAGE": "Не могу сохранить письмо", - "CANT_SEND_MESSAGE": "Не могу отправить письмо", - "INVALID_RECIPIENTS": "Проверьте правильность ввода всех адресов.", - "CANT_SAVE_FILTERS": "Не могу сохранить фильтры", - "CANT_GET_FILTERS": "Не могу загрузить фильтры", - "CANT_ACTIVATE_FILTERS_SCRIPT": "Не могу активировать сценарий фильтров", - "CANT_DELETE_FILTERS_SCRIPT": "Не могу удалить сценарий фильтров", - "FILTERS_ARE_NOT_CORRECT": "Фильтры неправильны", - "CANT_CREATE_FOLDER": "Не могу создать папку", - "CANT_RENAME_FOLDER": "Не могу переименовать папку", - "CANT_DELETE_FOLDER": "Не могу удалить папку", - "CANT_DELETE_NON_EMPTY_FOLDER": "Не могу удалить непустую папку", - "CANT_SUBSCRIBE_FOLDER": "Не могу подписать папку", - "CANT_UNSUBSCRIBE_FOLDER": "Не могу отписать папку", - "DEMO_SEND_MESSAGE_ERROR": "Демо аккаунту отправка писем на внешние почтовые адреса запрещена!", - "DEMO_ACCOUNT_ERROR": "По соображениям безопасности данный аккаунт не может выполнить это действие.", - "ACCOUNT_ALREADY_EXISTS": "Аккаунт уже добавлен", - "ACCOUNT_DOES_NOT_EXIST": "Аккаунт не существует", - "ACCOUNT_SWITCH_FAILED": "Не удалось переключиться на учетную запись \"%EMAIL%\"", - "MAIL_SERVER_ERROR": "Ошибка доступа к почтовому серверу", - "INVALID_INPUT_ARGUMENT": "Неверный параметр", - "UNKNOWN_ERROR": "Неизвестная ошибка" + "RequestError": "Запрос не выполнен", + "RequestAborted": "Запрос прерван", + "RequestTimeout": "Истекло время запроса", + "InvalidToken": "Неверный токен запроса", + "AuthError": "Не удалось авторизоваться", + "ConnectionError": "Ошибка соединения с сервером.", + "DomainAlreadyExists": "Домен уже существует", + "DomainNotAllowed": "Данный домен не разрешен", + "AccountNotAllowed": "Данный аккаунт не разрешен", + "CryptKeyError": "Your login passphrase has changed", + "ContactsSyncError": "Ошибка синхронизации контактов", + "CantGetMessageList": "Не могу получить список писем", + "CantGetMessage": "Не могу получить письмо", + "CantDeleteMessage": "Не могу удалить письмо", + "CantCopyMessage": "Не могу скопировать сообщение", + "CantMoveMessage": "Не могу переместить письмо", + "CantSaveMessage": "Не могу сохранить письмо", + "CantSendMessage": "Не могу отправить письмо", + "InvalidRecipients": "Проверьте правильность ввода всех адресов.", + "CantSaveFilters": "Не могу сохранить фильтры", + "CantGetFilters": "Не могу загрузить фильтры", + "CantActivateFiltersScript": "Не могу активировать сценарий фильтров", + "CantDeleteFiltersScript": "Не могу удалить сценарий фильтров", + "FiltersAreNotCorrect": "Фильтры неправильны", + "CantCreateFolder": "Не могу создать папку", + "CantRenameFolder": "Не могу переименовать папку", + "CantDeleteFolder": "Не могу удалить папку", + "CantDeleteNonEmptyFolder": "Не могу удалить непустую папку", + "CantSubscribeFolder": "Не могу подписать папку", + "CantUnsubscribeFolder": "Не могу отписать папку", + "DemoSendMessageError": "Демо аккаунту отправка писем на внешние почтовые адреса запрещена!", + "DemoAccountError": "По соображениям безопасности данный аккаунт не может выполнить это действие.", + "AccountAlreadyExists": "Аккаунт уже добавлен", + "AccountDoesNotExist": "Аккаунт не существует", + "AccountSwitchFailed": "Не удалось переключиться на учетную запись \"%EMAIL%\"", + "MailServerError": "Ошибка доступа к почтовому серверу", + "InvalidInputArgument": "Неверный параметр", + "UnknownError": "Неизвестная ошибка" } } diff --git a/snappymail/v/0.0.0/app/localization/sk-SK/admin.json b/snappymail/v/0.0.0/app/localization/sk-SK/admin.json index 71e3f1772..b046351be 100644 --- a/snappymail/v/0.0.0/app/localization/sk-SK/admin.json +++ b/snappymail/v/0.0.0/app/localization/sk-SK/admin.json @@ -85,13 +85,14 @@ "TAB_SECURITY": { "LEGEND_SECURITY": "Bezpečnosť", "LABEL_USE_IMAGE_PROXY": "Use local proxy for external images", - "LABEL_ALLOW_OPENPGP": "Povoliť OpenPGP", + "LABEL_AUTO_VERIFY": "Verify signatures automatically", "LABEL_SHOW_PHP_INFO": "Zobraziť informácie o PHP", "LEGEND_ADMIN_PANEL_ACCESS_CREDENTIALS": "Admin Panel Access Credentials", "LABEL_CURRENT_PASSWORD": "Súčasné heslo", "LABEL_NEW_PASSWORD": "Nové heslo", "LABEL_REPEAT_PASSWORD": "Opakovať", "LABEL_REQUIRE_VERIFICATION": "Require verification of SSL certificate used", + "CRYPTO": "Crypto", "LABEL_ALLOW_SELF_SIGNED": "Allow self signed certificates" }, "TAB_PACKAGES": { @@ -135,6 +136,7 @@ "SECURE_OPTION_NONE": "Žiadne", "LABEL_ALLOW_SIEVE_SCRIPTS": "Povoliť Sieve skripty", "LABEL_USE_SHORT_LOGIN": "Použiť skrátené prihlásenie", + "LABEL_LOWERCASE_LOGIN": "Lowercase login (case-insensitive)", "LABEL_USE_AUTH": "Použiť overenie", "LABEL_SET_SENDER": "Use login as sender", "LABEL_AUTH_PLAIN_LINE": "Force single command AUTH PLAIN login", @@ -168,17 +170,17 @@ "UNKNOWN_ERROR": "Neznáma chyba" }, "NOTIFICATIONS": { - "REQUEST_ERROR": "Request failed", - "REQUEST_ABORTED": "Request is aborted", - "REQUEST_TIMEOUT": "Request timed out", - "INVALID_TOKEN": "Neplatný token", - "AUTH_ERROR": "Overenie zlyhalo", - "DOMAIN_ALREADY_EXISTS": "Táto Doména už existuje", - "CANT_SAVE_PLUGIN_SETTINGS": "Nastavenia sa nepodarilo uložiť", - "CANT_INSTALL_PACKAGE": "Inštalácia balíčka zlyhala", - "CANT_DELETE_PACKAGE": "Odstránenie balíčka zlyhalo", - "INVALID_PLUGIN_PACKAGE": "Invalid plugin package", - "UNSUPPORTED_PLUGIN_PACKAGE": "Unsupported plugin package", - "UNKNOWN_ERROR": "Neznáma chyba" + "RequestError": "Request failed", + "RequestAborted": "Request is aborted", + "RequestTimeout": "Request timed out", + "InvalidToken": "Neplatný token", + "AuthError": "Overenie zlyhalo", + "DomainAlreadyExists": "Táto Doména už existuje", + "CantSavePluginSettings": "Nastavenia sa nepodarilo uložiť", + "CantInstallPackage": "Inštalácia balíčka zlyhala", + "CantDeletePackage": "Odstránenie balíčka zlyhalo", + "InvalidPluginPackage": "Invalid plugin package", + "UnsupportedPluginPackage": "Unsupported plugin package", + "UnknownError": "Neznáma chyba" } } diff --git a/snappymail/v/0.0.0/app/localization/sk-SK/user.json b/snappymail/v/0.0.0/app/localization/sk-SK/user.json index 9f9eab751..a45b9c453 100644 --- a/snappymail/v/0.0.0/app/localization/sk-SK/user.json +++ b/snappymail/v/0.0.0/app/localization/sk-SK/user.json @@ -139,6 +139,7 @@ "MENU_VIEW_ORIGINAL": "Zobraziť zdroj", "HTML_VIEW": "View HTML message", "PLAIN_VIEW": "View plain text message", + "SWAP_COLORS": "Swap default (background)color", "MENU_DOWNLOAD_ORIGINAL": "Prevziať ako súbor .eml", "MENU_FILTER_SIMILAR": "Filtrovať správy ako táto", "MENU_PRINT": "Tlačiť", @@ -283,7 +284,8 @@ "ERROR": "%TYPE% error: %ERROR%", "CREATE_SELF_SIGNED": "Create self-signed", "VALID_UNTIL": "Valid until", - "PRIVATE_KEY": "Private key" + "PRIVATE_KEY": "Private key", + "ASK_CRYPTKEY_PASS": "Your login passphrase has changed. Please fill in your old login password to decrypt data" }, "OPENPGP": { "POPUP_IMPORT_TITLE": "Importovať kľúč OpenPGP", @@ -509,7 +511,7 @@ "TO_MANY_FOLDERS_DESC_2": "We have shown only a part of them, to avoid performance problems.", "HELP_DELETE_FOLDER": "Delete folder", "HELP_SHOW_HIDE_FOLDER": "Show\/hide folder", - "HELP_CHECK_FOR_NEW_MESSAGES": "Check\/don't check for new messages", + "HELP_CHECK_FOR_NEW_MESSAGES": "Check for new messages", "HIDE_UNSUBSCRIBED": "Hide unsubscribed folders", "UNHIDE_KOLAB_FOLDERS": "Unhide Kolab folders", "TYPE_CALENDAR": "Calendar", @@ -575,42 +577,43 @@ "LABEL_CLOSE_COMPOSE": "Close compose" }, "NOTIFICATIONS": { - "REQUEST_ERROR": "Request failed", - "REQUEST_ABORTED": "Request is aborted", - "REQUEST_TIMEOUT": "Request timed out", - "INVALID_TOKEN": "Neplatný token", - "AUTH_ERROR": "Overenie zlyhalo", - "CONNECTION_ERROR": "Nepodarilo sa pripojiť k serveru", - "DOMAIN_ALREADY_EXISTS": "Táto Doména už existuje", - "DOMAIN_NOT_ALLOWED": "Doména nie je povolená", - "ACCOUNT_NOT_ALLOWED": "Účet nie je povolený", - "CONTACTS_SYNC_ERROR": "Contacts synchronization error", - "CANT_GET_MESSAGE_LIST": "Chyba pri vytváraní zoznamu správ.", - "CANT_GET_MESSAGE": "Správu sa nepodarilo načítať", - "CANT_DELETE_MESSAGE": "Správu sa nepodarilo odstrániť", - "CANT_COPY_MESSAGE": "Can't copy message", - "CANT_MOVE_MESSAGE": "Správu sa nepodarilo presunúť", - "CANT_SAVE_MESSAGE": "Správu sa nepodarilo uložiť", - "CANT_SEND_MESSAGE": "Správu sa nepodarilo odoslať", - "INVALID_RECIPIENTS": "Neplatný príjemcovia", - "CANT_SAVE_FILTERS": "Can't save filters", - "CANT_GET_FILTERS": "Can't get filters", - "CANT_ACTIVATE_FILTERS_SCRIPT": "Can't activate filters script", - "CANT_DELETE_FILTERS_SCRIPT": "Can't delete filters script", - "FILTERS_ARE_NOT_CORRECT": "Filters are not correct", - "CANT_CREATE_FOLDER": "Priečinok sa nepodarilo vytvoriť", - "CANT_RENAME_FOLDER": "Priečinok sa nepodarilo premenovať", - "CANT_DELETE_FOLDER": "Priečinok sa nepodarilo odstrániť", - "CANT_DELETE_NON_EMPTY_FOLDER": "Nemôžem odstrániť neprázdny priečinok", - "CANT_SUBSCRIBE_FOLDER": "Can't subscribe folder", - "CANT_UNSUBSCRIBE_FOLDER": "Can't unsubscribe folder", - "DEMO_SEND_MESSAGE_ERROR": "For security purposes, this demo account is not allowed to send messages to external e-mail addresses!", - "DEMO_ACCOUNT_ERROR": "For security purposes, this account is not allowed for this action!", - "ACCOUNT_ALREADY_EXISTS": "účet už existuje", - "ACCOUNT_DOES_NOT_EXIST": "Účet neexistuje", - "ACCOUNT_SWITCH_FAILED": "Switch to account \"%EMAIL%\" failed", - "MAIL_SERVER_ERROR": "Nastala chyba počas prístupu na poštový server", - "INVALID_INPUT_ARGUMENT": "Invalid input argument", - "UNKNOWN_ERROR": "Neznáma chyba" + "RequestError": "Request failed", + "RequestAborted": "Request is aborted", + "RequestTimeout": "Request timed out", + "InvalidToken": "Neplatný token", + "AuthError": "Overenie zlyhalo", + "ConnectionError": "Nepodarilo sa pripojiť k serveru", + "DomainAlreadyExists": "Táto Doména už existuje", + "DomainNotAllowed": "Doména nie je povolená", + "AccountNotAllowed": "Účet nie je povolený", + "CryptKeyError": "Your login passphrase has changed", + "ContactsSyncError": "Contacts synchronization error", + "CantGetMessageList": "Chyba pri vytváraní zoznamu správ.", + "CantGetMessage": "Správu sa nepodarilo načítať", + "CantDeleteMessage": "Správu sa nepodarilo odstrániť", + "CantCopyMessage": "Can't copy message", + "CantMoveMessage": "Správu sa nepodarilo presunúť", + "CantSaveMessage": "Správu sa nepodarilo uložiť", + "CantSendMessage": "Správu sa nepodarilo odoslať", + "InvalidRecipients": "Neplatný príjemcovia", + "CantSaveFilters": "Can't save filters", + "CantGetFilters": "Can't get filters", + "CantActivateFiltersScript": "Can't activate filters script", + "CantDeleteFiltersScript": "Can't delete filters script", + "FiltersAreNotCorrect": "Filters are not correct", + "CantCreateFolder": "Priečinok sa nepodarilo vytvoriť", + "CantRenameFolder": "Priečinok sa nepodarilo premenovať", + "CantDeleteFolder": "Priečinok sa nepodarilo odstrániť", + "CantDeleteNonEmptyFolder": "Nemôžem odstrániť neprázdny priečinok", + "CantSubscribeFolder": "Can't subscribe folder", + "CantUnsubscribeFolder": "Can't unsubscribe folder", + "DemoSendMessageError": "For security purposes, this demo account is not allowed to send messages to external e-mail addresses!", + "DemoAccountError": "For security purposes, this account is not allowed for this action!", + "AccountAlreadyExists": "účet už existuje", + "AccountDoesNotExist": "Účet neexistuje", + "AccountSwitchFailed": "Switch to account \"%EMAIL%\" failed", + "MailServerError": "Nastala chyba počas prístupu na poštový server", + "InvalidInputArgument": "Invalid input argument", + "UnknownError": "Neznáma chyba" } } diff --git a/snappymail/v/0.0.0/app/localization/sl-SI/admin.json b/snappymail/v/0.0.0/app/localization/sl-SI/admin.json index 3e9494052..8e5ee249a 100644 --- a/snappymail/v/0.0.0/app/localization/sl-SI/admin.json +++ b/snappymail/v/0.0.0/app/localization/sl-SI/admin.json @@ -85,13 +85,14 @@ "TAB_SECURITY": { "LEGEND_SECURITY": "Varnost", "LABEL_USE_IMAGE_PROXY": "Uporabni lokalni posredniški strežnik za zunanje slike", - "LABEL_ALLOW_OPENPGP": "Dovoli OpenPGP", + "LABEL_AUTO_VERIFY": "Verify signatures automatically", "LABEL_SHOW_PHP_INFO": "Prikaz PHP informacij", "LEGEND_ADMIN_PANEL_ACCESS_CREDENTIALS": "Prijavni podatki za administracijsko ploščo", "LABEL_CURRENT_PASSWORD": "Trenutno geslo", "LABEL_NEW_PASSWORD": "Novo geslo", "LABEL_REPEAT_PASSWORD": "Ponovno", "LABEL_REQUIRE_VERIFICATION": "Obvezno overjanje uporabljenega SSL certifikata", + "CRYPTO": "Crypto", "LABEL_ALLOW_SELF_SIGNED": "Dovoli samopodpisane certifikate" }, "TAB_PACKAGES": { @@ -135,6 +136,7 @@ "SECURE_OPTION_NONE": "Nobeno", "LABEL_ALLOW_SIEVE_SCRIPTS": "Dovoljene sieve skripte", "LABEL_USE_SHORT_LOGIN": "Uporaba uporabniškega imena brez domene", + "LABEL_LOWERCASE_LOGIN": "Lowercase login (case-insensitive)", "LABEL_USE_AUTH": "Uporaba overjanja", "LABEL_SET_SENDER": "Use login as sender", "LABEL_AUTH_PLAIN_LINE": "Force single command AUTH PLAIN login", @@ -168,17 +170,17 @@ "UNKNOWN_ERROR": "Neznana napaka" }, "NOTIFICATIONS": { - "REQUEST_ERROR": "Request failed", - "REQUEST_ABORTED": "Request is aborted", - "REQUEST_TIMEOUT": "Request timed out", - "INVALID_TOKEN": "Neveljaven žeton", - "AUTH_ERROR": "Spodletelo overjanje", - "DOMAIN_ALREADY_EXISTS": "Domena že obstaja", - "CANT_SAVE_PLUGIN_SETTINGS": "Ni mogoče shraniti nastavitev", - "CANT_INSTALL_PACKAGE": "Neuspešna namestitev paketa", - "CANT_DELETE_PACKAGE": "Neuspešno odstranjevanje paketa", - "INVALID_PLUGIN_PACKAGE": "Neveljaven paket vtičnika", - "UNSUPPORTED_PLUGIN_PACKAGE": "Nepodprt paket vtičnika", - "UNKNOWN_ERROR": "Neznana napaka" + "RequestError": "Request failed", + "RequestAborted": "Request is aborted", + "RequestTimeout": "Request timed out", + "InvalidToken": "Neveljaven žeton", + "AuthError": "Spodletelo overjanje", + "DomainAlreadyExists": "Domena že obstaja", + "CantSavePluginSettings": "Ni mogoče shraniti nastavitev", + "CantInstallPackage": "Neuspešna namestitev paketa", + "CantDeletePackage": "Neuspešno odstranjevanje paketa", + "InvalidPluginPackage": "Neveljaven paket vtičnika", + "UnsupportedPluginPackage": "Nepodprt paket vtičnika", + "UnknownError": "Neznana napaka" } } diff --git a/snappymail/v/0.0.0/app/localization/sl-SI/user.json b/snappymail/v/0.0.0/app/localization/sl-SI/user.json index a62f793f0..11a085163 100644 --- a/snappymail/v/0.0.0/app/localization/sl-SI/user.json +++ b/snappymail/v/0.0.0/app/localization/sl-SI/user.json @@ -139,6 +139,7 @@ "MENU_VIEW_ORIGINAL": "Pokaži izvorno", "HTML_VIEW": "View HTML message", "PLAIN_VIEW": "View plain text message", + "SWAP_COLORS": "Swap default (background)color", "MENU_DOWNLOAD_ORIGINAL": "Prenesi kot .eml datoteko", "MENU_FILTER_SIMILAR": "Filtriraj sporočila, kot je to", "MENU_PRINT": "Natisni", @@ -283,7 +284,8 @@ "ERROR": "%TYPE% napaka: %ERROR%", "CREATE_SELF_SIGNED": "Create self-signed", "VALID_UNTIL": "Valid until", - "PRIVATE_KEY": "Private key" + "PRIVATE_KEY": "Private key", + "ASK_CRYPTKEY_PASS": "Your login passphrase has changed. Please fill in your old login password to decrypt data" }, "OPENPGP": { "POPUP_IMPORT_TITLE": "Uvoz OpenPGP ključa", @@ -509,7 +511,7 @@ "TO_MANY_FOLDERS_DESC_2": "Prikazanih je le del, da se izognemo tehničnim težavam.", "HELP_DELETE_FOLDER": "Izbriši mapo", "HELP_SHOW_HIDE_FOLDER": "Prikaži\/skrij mapo", - "HELP_CHECK_FOR_NEW_MESSAGES": "Preverjaj\/ne preverjaj za nova sporočila", + "HELP_CHECK_FOR_NEW_MESSAGES": "Preverjaj za nova sporočila", "HIDE_UNSUBSCRIBED": "Hide unsubscribed folders", "UNHIDE_KOLAB_FOLDERS": "Unhide Kolab folders", "TYPE_CALENDAR": "Calendar", @@ -575,42 +577,43 @@ "LABEL_CLOSE_COMPOSE": "Zapri sestavljanje" }, "NOTIFICATIONS": { - "REQUEST_ERROR": "Request failed", - "REQUEST_ABORTED": "Request is aborted", - "REQUEST_TIMEOUT": "Request timed out", - "INVALID_TOKEN": "Neveljaven žeton", - "AUTH_ERROR": "Spodletelo overjanje", - "CONNECTION_ERROR": "Ni mogoče vzpostaviti povezave s strežnikom", - "DOMAIN_ALREADY_EXISTS": "Domena že obstaja", - "DOMAIN_NOT_ALLOWED": "Domena ni dovoljena", - "ACCOUNT_NOT_ALLOWED": "Račun ni dovoljen", - "CONTACTS_SYNC_ERROR": "Napaka sinhronizacije stikov", - "CANT_GET_MESSAGE_LIST": "Ni mogoče pridobiti seznama sporočil", - "CANT_GET_MESSAGE": "Ni mogoče pridobiti sporočila", - "CANT_DELETE_MESSAGE": "Ni mogoče izbrisati sporočila", - "CANT_COPY_MESSAGE": "Can't copy message", - "CANT_MOVE_MESSAGE": "Ni mogoče premakniti sporočila", - "CANT_SAVE_MESSAGE": "Ni mogoče shraniti sporočila", - "CANT_SEND_MESSAGE": "Ni mogoče poslati sporočila", - "INVALID_RECIPIENTS": "Neveljavni prejemniki", - "CANT_SAVE_FILTERS": "Ni mogoče shraniti filtrov", - "CANT_GET_FILTERS": "Ni mogoče pridobiti filtrov", - "CANT_ACTIVATE_FILTERS_SCRIPT": "Can't activate filters script", - "CANT_DELETE_FILTERS_SCRIPT": "Can't delete filters script", - "FILTERS_ARE_NOT_CORRECT": "Filtri so nepravilni", - "CANT_CREATE_FOLDER": "Ni mogoče ustvariti mape", - "CANT_RENAME_FOLDER": "Ni mogoče preimenovati mape", - "CANT_DELETE_FOLDER": "Ni mogoče izbrisati mape", - "CANT_DELETE_NON_EMPTY_FOLDER": "Ni mogoče izbrisati neprazne mape", - "CANT_SUBSCRIBE_FOLDER": "Ni mogoče slediti mapi", - "CANT_UNSUBSCRIBE_FOLDER": "Ni mogoče prenehati slediti mapi", - "DEMO_SEND_MESSAGE_ERROR": "Iz varnostnih razlogov ta račun ne sme pošiljati pošte na zunanje e-naslove!", - "DEMO_ACCOUNT_ERROR": "Iz varnostnih razlogov ta račun ne sme izvršiti tega dejanja!", - "ACCOUNT_ALREADY_EXISTS": "Račun že obstaja", - "ACCOUNT_DOES_NOT_EXIST": "Račun ne obstaja", - "ACCOUNT_SWITCH_FAILED": "Switch to account \"%EMAIL%\" failed", - "MAIL_SERVER_ERROR": "Napaka pri dostopu do poštnega strežnika", - "INVALID_INPUT_ARGUMENT": "Neveljaven vhodni argument", - "UNKNOWN_ERROR": "Neznana napaka" + "RequestError": "Request failed", + "RequestAborted": "Request is aborted", + "RequestTimeout": "Request timed out", + "InvalidToken": "Neveljaven žeton", + "AuthError": "Spodletelo overjanje", + "ConnectionError": "Ni mogoče vzpostaviti povezave s strežnikom", + "DomainAlreadyExists": "Domena že obstaja", + "DomainNotAllowed": "Domena ni dovoljena", + "AccountNotAllowed": "Račun ni dovoljen", + "CryptKeyError": "Your login passphrase has changed", + "ContactsSyncError": "Napaka sinhronizacije stikov", + "CantGetMessageList": "Ni mogoče pridobiti seznama sporočil", + "CantGetMessage": "Ni mogoče pridobiti sporočila", + "CantDeleteMessage": "Ni mogoče izbrisati sporočila", + "CantCopyMessage": "Can't copy message", + "CantMoveMessage": "Ni mogoče premakniti sporočila", + "CantSaveMessage": "Ni mogoče shraniti sporočila", + "CantSendMessage": "Ni mogoče poslati sporočila", + "InvalidRecipients": "Neveljavni prejemniki", + "CantSaveFilters": "Ni mogoče shraniti filtrov", + "CantGetFilters": "Ni mogoče pridobiti filtrov", + "CantActivateFiltersScript": "Can't activate filters script", + "CantDeleteFiltersScript": "Can't delete filters script", + "FiltersAreNotCorrect": "Filtri so nepravilni", + "CantCreateFolder": "Ni mogoče ustvariti mape", + "CantRenameFolder": "Ni mogoče preimenovati mape", + "CantDeleteFolder": "Ni mogoče izbrisati mape", + "CantDeleteNonEmptyFolder": "Ni mogoče izbrisati neprazne mape", + "CantSubscribeFolder": "Ni mogoče slediti mapi", + "CantUnsubscribeFolder": "Ni mogoče prenehati slediti mapi", + "DemoSendMessageError": "Iz varnostnih razlogov ta račun ne sme pošiljati pošte na zunanje e-naslove!", + "DemoAccountError": "Iz varnostnih razlogov ta račun ne sme izvršiti tega dejanja!", + "AccountAlreadyExists": "Račun že obstaja", + "AccountDoesNotExist": "Račun ne obstaja", + "AccountSwitchFailed": "Switch to account \"%EMAIL%\" failed", + "MailServerError": "Napaka pri dostopu do poštnega strežnika", + "InvalidInputArgument": "Neveljaven vhodni argument", + "UnknownError": "Neznana napaka" } } diff --git a/snappymail/v/0.0.0/app/localization/sv-SE/admin.json b/snappymail/v/0.0.0/app/localization/sv-SE/admin.json index 1d7730891..435be73b9 100644 --- a/snappymail/v/0.0.0/app/localization/sv-SE/admin.json +++ b/snappymail/v/0.0.0/app/localization/sv-SE/admin.json @@ -85,13 +85,14 @@ "TAB_SECURITY": { "LEGEND_SECURITY": "Säkerhet", "LABEL_USE_IMAGE_PROXY": "Använd lokal proxy för externa bilder", - "LABEL_ALLOW_OPENPGP": "Tillåt OpenPGP", + "LABEL_AUTO_VERIFY": "Verify signatures automatically", "LABEL_SHOW_PHP_INFO": "Visa PHP-information", "LEGEND_ADMIN_PANEL_ACCESS_CREDENTIALS": "Administrativsuppgifter", "LABEL_CURRENT_PASSWORD": "Nuvarande lösenord", "LABEL_NEW_PASSWORD": "Nytt lösenord", "LABEL_REPEAT_PASSWORD": "Upprepa", "LABEL_REQUIRE_VERIFICATION": "Kräver verifiering av SSL-certifikat som används", + "CRYPTO": "Crypto", "LABEL_ALLOW_SELF_SIGNED": "Låt självsignerade certifikat" }, "TAB_PACKAGES": { @@ -135,6 +136,7 @@ "SECURE_OPTION_NONE": "Ingen", "LABEL_ALLOW_SIEVE_SCRIPTS": "Tillåta sikt skript", "LABEL_USE_SHORT_LOGIN": "Använda korta inloggning", + "LABEL_LOWERCASE_LOGIN": "Lowercase login (case-insensitive)", "LABEL_USE_AUTH": "Använd autentisering", "LABEL_SET_SENDER": "Use login as sender", "LABEL_AUTH_PLAIN_LINE": "Force single command AUTH PLAIN login", @@ -168,17 +170,17 @@ "UNKNOWN_ERROR": "Okänt fel" }, "NOTIFICATIONS": { - "REQUEST_ERROR": "Request failed", - "REQUEST_ABORTED": "Request is aborted", - "REQUEST_TIMEOUT": "Request timed out", - "INVALID_TOKEN": "ogiltiga symboler", - "AUTH_ERROR": "Autentisering misslyckades", - "DOMAIN_ALREADY_EXISTS": "Domänen finns redan", - "CANT_SAVE_PLUGIN_SETTINGS": "Kan inte spara inställningarna", - "CANT_INSTALL_PACKAGE": "Det gick inte att installera tillägget", - "CANT_DELETE_PACKAGE": "Det gick inte att ta bort tillägget", - "INVALID_PLUGIN_PACKAGE": "Ogiltigt tilläggspaket", - "UNSUPPORTED_PLUGIN_PACKAGE": "Tillägg som inte stöds", - "UNKNOWN_ERROR": "Okänt fel" + "RequestError": "Request failed", + "RequestAborted": "Request is aborted", + "RequestTimeout": "Request timed out", + "InvalidToken": "ogiltiga symboler", + "AuthError": "Autentisering misslyckades", + "DomainAlreadyExists": "Domänen finns redan", + "CantSavePluginSettings": "Kan inte spara inställningarna", + "CantInstallPackage": "Det gick inte att installera tillägget", + "CantDeletePackage": "Det gick inte att ta bort tillägget", + "InvalidPluginPackage": "Ogiltigt tilläggspaket", + "UnsupportedPluginPackage": "Tillägg som inte stöds", + "UnknownError": "Okänt fel" } } diff --git a/snappymail/v/0.0.0/app/localization/sv-SE/user.json b/snappymail/v/0.0.0/app/localization/sv-SE/user.json index 4041f143c..3a2471969 100644 --- a/snappymail/v/0.0.0/app/localization/sv-SE/user.json +++ b/snappymail/v/0.0.0/app/localization/sv-SE/user.json @@ -139,6 +139,7 @@ "MENU_VIEW_ORIGINAL": "Visa källa", "HTML_VIEW": "View HTML message", "PLAIN_VIEW": "View plain text message", + "SWAP_COLORS": "Swap default (background)color", "MENU_DOWNLOAD_ORIGINAL": "Ladda ner som .eml fil", "MENU_FILTER_SIMILAR": "Filtrera meddelanden som detta", "MENU_PRINT": "Skriv ut", @@ -283,7 +284,8 @@ "ERROR": "%TYPE%-fel: %ERROR%", "CREATE_SELF_SIGNED": "Create self-signed", "VALID_UNTIL": "Valid until", - "PRIVATE_KEY": "Private key" + "PRIVATE_KEY": "Private key", + "ASK_CRYPTKEY_PASS": "Your login passphrase has changed. Please fill in your old login password to decrypt data" }, "OPENPGP": { "POPUP_IMPORT_TITLE": "Import av OpenPGP-nyckel", @@ -509,7 +511,7 @@ "TO_MANY_FOLDERS_DESC_2": "Vi har visat endast en del av dem, för att undvika prestandaproblem.", "HELP_DELETE_FOLDER": "Ta bort mapp", "HELP_SHOW_HIDE_FOLDER": "Visa\/dölj mapp", - "HELP_CHECK_FOR_NEW_MESSAGES": "Kontrollera\/inte söka efter nya meddelanden", + "HELP_CHECK_FOR_NEW_MESSAGES": "Kontrollera efter nya meddelanden", "HIDE_UNSUBSCRIBED": "Hide unsubscribed folders", "UNHIDE_KOLAB_FOLDERS": "Unhide Kolab folders", "TYPE_CALENDAR": "Kalender", @@ -575,42 +577,43 @@ "LABEL_CLOSE_COMPOSE": "Stäng fönster" }, "NOTIFICATIONS": { - "REQUEST_ERROR": "Request failed", - "REQUEST_ABORTED": "Request is aborted", - "REQUEST_TIMEOUT": "Request timed out", - "INVALID_TOKEN": "Ogiltigt tecken", - "AUTH_ERROR": "Autentisering misslyckades", - "CONNECTION_ERROR": "Kan inte ansluta till servern", - "DOMAIN_ALREADY_EXISTS": "Domänen finns redan", - "DOMAIN_NOT_ALLOWED": "Domän är inte tillåtet", - "ACCOUNT_NOT_ALLOWED": "Konto är inte tillåtet", - "CONTACTS_SYNC_ERROR": "Kontakt-synkroniseringsfel", - "CANT_GET_MESSAGE_LIST": "Kan inte hämta meddelandelista", - "CANT_GET_MESSAGE": "Kan inte hämta meddelande", - "CANT_DELETE_MESSAGE": "Kan inte ta bort meddelande", - "CANT_COPY_MESSAGE": "Can't copy message", - "CANT_MOVE_MESSAGE": "Kan inte flytta meddelande", - "CANT_SAVE_MESSAGE": "Kan inte spara meddelande", - "CANT_SEND_MESSAGE": "Kan inte skicka meddelande", - "INVALID_RECIPIENTS": "Ogiltig mottagare", - "CANT_SAVE_FILTERS": "Kan inte spara filter", - "CANT_GET_FILTERS": "Kan inte ta emot filter", - "CANT_ACTIVATE_FILTERS_SCRIPT": "Can't activate filters script", - "CANT_DELETE_FILTERS_SCRIPT": "Can't delete filters script", - "FILTERS_ARE_NOT_CORRECT": "Filter är inte korrekt", - "CANT_CREATE_FOLDER": "Kan inte skapa mapp", - "CANT_RENAME_FOLDER": "Kan inte byta namn på mapp", - "CANT_DELETE_FOLDER": "Kan inte ta bort mapp", - "CANT_DELETE_NON_EMPTY_FOLDER": "Kan inte ta bort mapp med innehåll", - "CANT_SUBSCRIBE_FOLDER": "Kan inte prenumerera på mapp", - "CANT_UNSUBSCRIBE_FOLDER": "Kan inte avprenumerera på mapp", - "DEMO_SEND_MESSAGE_ERROR": "Av säkerhets skäl kan inte detta demo-konto skicka epost till externa adressater!", - "DEMO_ACCOUNT_ERROR": "Av säkerhetsskäl är det här kontot inte tillåtet för denna åtgärd!", - "ACCOUNT_ALREADY_EXISTS": "Kontot finns redan", - "ACCOUNT_DOES_NOT_EXIST": "Kontot finns inte", - "ACCOUNT_SWITCH_FAILED": "Switch to account \"%EMAIL%\" failed", - "MAIL_SERVER_ERROR": "Ett fel har inträffat vid anslutning till epostservern", - "INVALID_INPUT_ARGUMENT": "Ogiltigt argument", - "UNKNOWN_ERROR": "Okänt fel" + "RequestError": "Request failed", + "RequestAborted": "Request is aborted", + "RequestTimeout": "Request timed out", + "InvalidToken": "Ogiltigt tecken", + "AuthError": "Autentisering misslyckades", + "ConnectionError": "Kan inte ansluta till servern", + "DomainAlreadyExists": "Domänen finns redan", + "DomainNotAllowed": "Domän är inte tillåtet", + "AccountNotAllowed": "Konto är inte tillåtet", + "CryptKeyError": "Your login passphrase has changed", + "ContactsSyncError": "Kontakt-synkroniseringsfel", + "CantGetMessageList": "Kan inte hämta meddelandelista", + "CantGetMessage": "Kan inte hämta meddelande", + "CantDeleteMessage": "Kan inte ta bort meddelande", + "CantCopyMessage": "Can't copy message", + "CantMoveMessage": "Kan inte flytta meddelande", + "CantSaveMessage": "Kan inte spara meddelande", + "CantSendMessage": "Kan inte skicka meddelande", + "InvalidRecipients": "Ogiltig mottagare", + "CantSaveFilters": "Kan inte spara filter", + "CantGetFilters": "Kan inte ta emot filter", + "CantActivateFiltersScript": "Can't activate filters script", + "CantDeleteFiltersScript": "Can't delete filters script", + "FiltersAreNotCorrect": "Filter är inte korrekt", + "CantCreateFolder": "Kan inte skapa mapp", + "CantRenameFolder": "Kan inte byta namn på mapp", + "CantDeleteFolder": "Kan inte ta bort mapp", + "CantDeleteNonEmptyFolder": "Kan inte ta bort mapp med innehåll", + "CantSubscribeFolder": "Kan inte prenumerera på mapp", + "CantUnsubscribeFolder": "Kan inte avprenumerera på mapp", + "DemoSendMessageError": "Av säkerhets skäl kan inte detta demo-konto skicka epost till externa adressater!", + "DemoAccountError": "Av säkerhetsskäl är det här kontot inte tillåtet för denna åtgärd!", + "AccountAlreadyExists": "Kontot finns redan", + "AccountDoesNotExist": "Kontot finns inte", + "AccountSwitchFailed": "Switch to account \"%EMAIL%\" failed", + "MailServerError": "Ett fel har inträffat vid anslutning till epostservern", + "InvalidInputArgument": "Ogiltigt argument", + "UnknownError": "Okänt fel" } } diff --git a/snappymail/v/0.0.0/app/localization/tr-TR/user.json b/snappymail/v/0.0.0/app/localization/tr-TR/user.json index 6d0e83dd6..3fb6d7079 100644 --- a/snappymail/v/0.0.0/app/localization/tr-TR/user.json +++ b/snappymail/v/0.0.0/app/localization/tr-TR/user.json @@ -139,6 +139,7 @@ "MENU_VIEW_ORIGINAL": "Kaynağı görüntüle", "HTML_VIEW": "View HTML message", "PLAIN_VIEW": "View plain text message", + "SWAP_COLORS": "Swap default (background)color", "MENU_DOWNLOAD_ORIGINAL": ".eml dosyası olarak indir", "MENU_FILTER_SIMILAR": "Bunun gibi iletileri filtrele", "MENU_PRINT": "yazdır", @@ -283,7 +284,8 @@ "ERROR": "%TYPE% hatası: %ERROR%", "CREATE_SELF_SIGNED": "Create self-signed", "VALID_UNTIL": "Valid until", - "PRIVATE_KEY": "Private key" + "PRIVATE_KEY": "Private key", + "ASK_CRYPTKEY_PASS": "Your login passphrase has changed. Please fill in your old login password to decrypt data" }, "OPENPGP": { "POPUP_IMPORT_TITLE": "OpenPGP key'i içe aktar", @@ -509,7 +511,7 @@ "TO_MANY_FOLDERS_DESC_2": "We have shown only a part of them, to avoid performance problems.", "HELP_DELETE_FOLDER": "Delete folder", "HELP_SHOW_HIDE_FOLDER": "Show\/hide folder", - "HELP_CHECK_FOR_NEW_MESSAGES": "Check\/don't check for new messages", + "HELP_CHECK_FOR_NEW_MESSAGES": "Check for new messages", "HIDE_UNSUBSCRIBED": "Hide unsubscribed folders", "UNHIDE_KOLAB_FOLDERS": "Unhide Kolab folders", "TYPE_CALENDAR": "Calendar", @@ -575,42 +577,43 @@ "LABEL_CLOSE_COMPOSE": "Oluşturulanı Kapat" }, "NOTIFICATIONS": { - "REQUEST_ERROR": "Request failed", - "REQUEST_ABORTED": "Request is aborted", - "REQUEST_TIMEOUT": "Request timed out", - "INVALID_TOKEN": "Geçersiz token", - "AUTH_ERROR": "Kimlik doğrulama başarısız oldu", - "CONNECTION_ERROR": "Sunucuya bağlanılamadı", - "DOMAIN_ALREADY_EXISTS": "Alanadı bulunuyor", - "DOMAIN_NOT_ALLOWED": "Alan Adına izin verilmemiş", - "ACCOUNT_NOT_ALLOWED": "Hesaba izin verilmemiş", - "CONTACTS_SYNC_ERROR": "Kişi listesi senkronizasyon hatası", - "CANT_GET_MESSAGE_LIST": "Mesaj listesi alınamadı", - "CANT_GET_MESSAGE": "Mesaj alınamadı", - "CANT_DELETE_MESSAGE": "Mesaj silinemedi", - "CANT_COPY_MESSAGE": "Can't copy message", - "CANT_MOVE_MESSAGE": "Mesaj taşınamadı", - "CANT_SAVE_MESSAGE": "Mesaj kaydedilemedi", - "CANT_SEND_MESSAGE": "Mesaj gönderilemedi", - "INVALID_RECIPIENTS": "Geçersiz alıcılar", - "CANT_SAVE_FILTERS": "Can't save filters", - "CANT_GET_FILTERS": "Can't get filters", - "CANT_ACTIVATE_FILTERS_SCRIPT": "Can't activate filters script", - "CANT_DELETE_FILTERS_SCRIPT": "Can't delete filters script", - "FILTERS_ARE_NOT_CORRECT": "Filters are not correct", - "CANT_CREATE_FOLDER": "Klasör oluşturulamadı", - "CANT_RENAME_FOLDER": "Klasör oluşturulamadı", - "CANT_DELETE_FOLDER": "Klasör silimedi", - "CANT_DELETE_NON_EMPTY_FOLDER": "Boş olmayan dizin silinemedi", - "CANT_SUBSCRIBE_FOLDER": "Klasöre abone olunamadı", - "CANT_UNSUBSCRIBE_FOLDER": "Klasör Aboneliğinden çıkılamadı", - "DEMO_SEND_MESSAGE_ERROR": "Güvenlik amacıyla, bu demo hesabı harici e-posta adreslerine mesaj göndermek için izin verilmez!", - "DEMO_ACCOUNT_ERROR": "For security purposes, this account is not allowed for this action!", - "ACCOUNT_ALREADY_EXISTS": "Hesap zaten var", - "ACCOUNT_DOES_NOT_EXIST": "Account doesn't exist", - "ACCOUNT_SWITCH_FAILED": "Switch to account \"%EMAIL%\" failed", - "MAIL_SERVER_ERROR": "Posta sunucusuna erişirken bir hata oluştu", - "INVALID_INPUT_ARGUMENT": "Invalid input argument", - "UNKNOWN_ERROR": "Bilinmeyen Hata" + "RequestError": "Request failed", + "RequestAborted": "Request is aborted", + "RequestTimeout": "Request timed out", + "InvalidToken": "Geçersiz token", + "AuthError": "Kimlik doğrulama başarısız oldu", + "ConnectionError": "Sunucuya bağlanılamadı", + "DomainAlreadyExists": "Alanadı bulunuyor", + "DomainNotAllowed": "Alan Adına izin verilmemiş", + "AccountNotAllowed": "Hesaba izin verilmemiş", + "CryptKeyError": "Your login passphrase has changed", + "ContactsSyncError": "Kişi listesi senkronizasyon hatası", + "CantGetMessageList": "Mesaj listesi alınamadı", + "CantGetMessage": "Mesaj alınamadı", + "CantDeleteMessage": "Mesaj silinemedi", + "CantCopyMessage": "Can't copy message", + "CantMoveMessage": "Mesaj taşınamadı", + "CantSaveMessage": "Mesaj kaydedilemedi", + "CantSendMessage": "Mesaj gönderilemedi", + "InvalidRecipients": "Geçersiz alıcılar", + "CantSaveFilters": "Can't save filters", + "CantGetFilters": "Can't get filters", + "CantActivateFiltersScript": "Can't activate filters script", + "CantDeleteFiltersScript": "Can't delete filters script", + "FiltersAreNotCorrect": "Filters are not correct", + "CantCreateFolder": "Klasör oluşturulamadı", + "CantRenameFolder": "Klasör oluşturulamadı", + "CantDeleteFolder": "Klasör silimedi", + "CantDeleteNonEmptyFolder": "Boş olmayan dizin silinemedi", + "CantSubscribeFolder": "Klasöre abone olunamadı", + "CantUnsubscribeFolder": "Klasör Aboneliğinden çıkılamadı", + "DemoSendMessageError": "Güvenlik amacıyla, bu demo hesabı harici e-posta adreslerine mesaj göndermek için izin verilmez!", + "DemoAccountError": "For security purposes, this account is not allowed for this action!", + "AccountAlreadyExists": "Hesap zaten var", + "AccountDoesNotExist": "Account doesn't exist", + "AccountSwitchFailed": "Switch to account \"%EMAIL%\" failed", + "MailServerError": "Posta sunucusuna erişirken bir hata oluştu", + "InvalidInputArgument": "Invalid input argument", + "UnknownError": "Bilinmeyen Hata" } } diff --git a/snappymail/v/0.0.0/app/localization/uk-UA/user.json b/snappymail/v/0.0.0/app/localization/uk-UA/user.json index b4742ae0d..167097704 100644 --- a/snappymail/v/0.0.0/app/localization/uk-UA/user.json +++ b/snappymail/v/0.0.0/app/localization/uk-UA/user.json @@ -139,6 +139,7 @@ "MENU_VIEW_ORIGINAL": "Подивитися оригінал", "HTML_VIEW": "View HTML message", "PLAIN_VIEW": "View plain text message", + "SWAP_COLORS": "Swap default (background)color", "MENU_DOWNLOAD_ORIGINAL": "Завантажити як .eml файл", "MENU_FILTER_SIMILAR": "Фільтрувати схожі", "MENU_PRINT": "Надрукувати", @@ -283,7 +284,8 @@ "ERROR": "%TYPE% помилка: %ERROR%", "CREATE_SELF_SIGNED": "Create self-signed", "VALID_UNTIL": "Valid until", - "PRIVATE_KEY": "Private key" + "PRIVATE_KEY": "Private key", + "ASK_CRYPTKEY_PASS": "Your login passphrase has changed. Please fill in your old login password to decrypt data" }, "OPENPGP": { "POPUP_IMPORT_TITLE": "Імпорт OpenPGP ключа", @@ -509,7 +511,7 @@ "TO_MANY_FOLDERS_DESC_2": "We have shown only a part of them, to avoid performance problems.", "HELP_DELETE_FOLDER": "Видалити теку", "HELP_SHOW_HIDE_FOLDER": "Відобразити\/приховати теку", - "HELP_CHECK_FOR_NEW_MESSAGES": "Check\/don't check for new messages", + "HELP_CHECK_FOR_NEW_MESSAGES": "Check for new messages", "HIDE_UNSUBSCRIBED": "Hide unsubscribed folders", "UNHIDE_KOLAB_FOLDERS": "Unhide Kolab folders", "TYPE_CALENDAR": "Calendar", @@ -575,42 +577,43 @@ "LABEL_CLOSE_COMPOSE": "Закрити повідомлення" }, "NOTIFICATIONS": { - "REQUEST_ERROR": "Request failed", - "REQUEST_ABORTED": "Request is aborted", - "REQUEST_TIMEOUT": "Request timed out", - "INVALID_TOKEN": "Невірний токен запиту", - "AUTH_ERROR": "Не вдалося авторизуватися", - "CONNECTION_ERROR": "Помилка з'єднання з сервером.", - "DOMAIN_ALREADY_EXISTS": "Домен вже існує", - "DOMAIN_NOT_ALLOWED": "Цей домен заборонений", - "ACCOUNT_NOT_ALLOWED": "Цей акаунт заборонений", - "CONTACTS_SYNC_ERROR": "Помилка синхронізації контактів", - "CANT_GET_MESSAGE_LIST": "Не можу отримати список листів", - "CANT_GET_MESSAGE": "Не можу отримати листа", - "CANT_DELETE_MESSAGE": "Не можу видалити листа", - "CANT_COPY_MESSAGE": "Can't copy message", - "CANT_MOVE_MESSAGE": "Не можу перемістити листа", - "CANT_SAVE_MESSAGE": "Не можу зберегти листа", - "CANT_SEND_MESSAGE": "Не можу відправити листа", - "INVALID_RECIPIENTS": "Перевірити правильність вводу всіх адрес.", - "CANT_SAVE_FILTERS": "Не можу зберегти фільтри", - "CANT_GET_FILTERS": "Can't get filters", - "CANT_ACTIVATE_FILTERS_SCRIPT": "Can't activate filters script", - "CANT_DELETE_FILTERS_SCRIPT": "Can't delete filters script", - "FILTERS_ARE_NOT_CORRECT": "Filters are not correct", - "CANT_CREATE_FOLDER": "Не можу створити теку", - "CANT_RENAME_FOLDER": "Не можу перейменувати теку", - "CANT_DELETE_FOLDER": "Не можу видалити теку", - "CANT_DELETE_NON_EMPTY_FOLDER": "Не можу видалити не порожню теку", - "CANT_SUBSCRIBE_FOLDER": "Не можу підписати теку", - "CANT_UNSUBSCRIBE_FOLDER": "Не можу відписати теку", - "DEMO_SEND_MESSAGE_ERROR": "Демо акаунту надсилання листів на зовнішні поштові адреси заборонена!", - "DEMO_ACCOUNT_ERROR": "For security purposes, this account is not allowed for this action!", - "ACCOUNT_ALREADY_EXISTS": "акаунт вже додано", - "ACCOUNT_DOES_NOT_EXIST": "Account doesn't exist", - "ACCOUNT_SWITCH_FAILED": "Switch to account \"%EMAIL%\" failed", - "MAIL_SERVER_ERROR": "Помилка доступу до поштового серверу", - "INVALID_INPUT_ARGUMENT": "Invalid input argument", - "UNKNOWN_ERROR": "Невідома помилка" + "RequestError": "Request failed", + "RequestAborted": "Request is aborted", + "RequestTimeout": "Request timed out", + "InvalidToken": "Невірний токен запиту", + "AuthError": "Не вдалося авторизуватися", + "ConnectionError": "Помилка з'єднання з сервером.", + "DomainAlreadyExists": "Домен вже існує", + "DomainNotAllowed": "Цей домен заборонений", + "AccountNotAllowed": "Цей акаунт заборонений", + "CryptKeyError": "Your login passphrase has changed", + "ContactsSyncError": "Помилка синхронізації контактів", + "CantGetMessageList": "Не можу отримати список листів", + "CantGetMessage": "Не можу отримати листа", + "CantDeleteMessage": "Не можу видалити листа", + "CantCopyMessage": "Can't copy message", + "CantMoveMessage": "Не можу перемістити листа", + "CantSaveMessage": "Не можу зберегти листа", + "CantSendMessage": "Не можу відправити листа", + "InvalidRecipients": "Перевірити правильність вводу всіх адрес.", + "CantSaveFilters": "Не можу зберегти фільтри", + "CantGetFilters": "Can't get filters", + "CantActivateFiltersScript": "Can't activate filters script", + "CantDeleteFiltersScript": "Can't delete filters script", + "FiltersAreNotCorrect": "Filters are not correct", + "CantCreateFolder": "Не можу створити теку", + "CantRenameFolder": "Не можу перейменувати теку", + "CantDeleteFolder": "Не можу видалити теку", + "CantDeleteNonEmptyFolder": "Не можу видалити не порожню теку", + "CantSubscribeFolder": "Не можу підписати теку", + "CantUnsubscribeFolder": "Не можу відписати теку", + "DemoSendMessageError": "Демо акаунту надсилання листів на зовнішні поштові адреси заборонена!", + "DemoAccountError": "For security purposes, this account is not allowed for this action!", + "AccountAlreadyExists": "акаунт вже додано", + "AccountDoesNotExist": "Account doesn't exist", + "AccountSwitchFailed": "Switch to account \"%EMAIL%\" failed", + "MailServerError": "Помилка доступу до поштового серверу", + "InvalidInputArgument": "Invalid input argument", + "UnknownError": "Невідома помилка" } } diff --git a/snappymail/v/0.0.0/app/localization/vi-VN/admin.json b/snappymail/v/0.0.0/app/localization/vi-VN/admin.json index e8ea1a3b0..9a54b6899 100644 --- a/snappymail/v/0.0.0/app/localization/vi-VN/admin.json +++ b/snappymail/v/0.0.0/app/localization/vi-VN/admin.json @@ -85,13 +85,14 @@ "TAB_SECURITY": { "LEGEND_SECURITY": "An ninh", "LABEL_USE_IMAGE_PROXY": "Sử dụng proxy tại máy chủ cho những hình ảnh tải ngoài về", - "LABEL_ALLOW_OPENPGP": "Cho phép OpenPGP", + "LABEL_AUTO_VERIFY": "Verify signatures automatically", "LABEL_SHOW_PHP_INFO": "Hiển thị thông tin PHP", "LEGEND_ADMIN_PANEL_ACCESS_CREDENTIALS": "Thông tin truy cập bảng quản trị", "LABEL_CURRENT_PASSWORD": "Mật khẩu hiện tại", "LABEL_NEW_PASSWORD": "Mật khẩu mới", "LABEL_REPEAT_PASSWORD": "Lập lại mật khẩu", "LABEL_REQUIRE_VERIFICATION": "Yêu cầu xác minh chứng chỉ SSL được dùng", + "CRYPTO": "Crypto", "LABEL_ALLOW_SELF_SIGNED": "Cho phép chứng chỉ tự ký" }, "TAB_PACKAGES": { @@ -135,6 +136,7 @@ "SECURE_OPTION_NONE": "Không có", "LABEL_ALLOW_SIEVE_SCRIPTS": "Cho phép script của sieve", "LABEL_USE_SHORT_LOGIN": "Dùng đăng nhập ngắn", + "LABEL_LOWERCASE_LOGIN": "Lowercase login (case-insensitive)", "LABEL_USE_AUTH": "Dùng xác minh", "LABEL_SET_SENDER": "Dùng tên đăng nhập là email người dùng", "LABEL_AUTH_PLAIN_LINE": "Force single command AUTH PLAIN login", @@ -168,17 +170,17 @@ "UNKNOWN_ERROR": "Lỗi chưa xác định" }, "NOTIFICATIONS": { - "REQUEST_ERROR": "Request failed", - "REQUEST_ABORTED": "Request is aborted", - "REQUEST_TIMEOUT": "Request timed out", - "INVALID_TOKEN": "Token không hợp lệ", - "AUTH_ERROR": "Xác minh thất bại", - "DOMAIN_ALREADY_EXISTS": "Tên miền đã tồn tại", - "CANT_SAVE_PLUGIN_SETTINGS": "Không thể lưu cài đặt", - "CANT_INSTALL_PACKAGE": "Không thể cài đặt tiện ích mở rộng", - "CANT_DELETE_PACKAGE": "Không thể xóa bỏ tiện ích mở rộng", - "INVALID_PLUGIN_PACKAGE": "Gói tiện ích mở rộng không đúng", - "UNSUPPORTED_PLUGIN_PACKAGE": "Gói tiện ích mở rộng chưa được hỗ trợ", - "UNKNOWN_ERROR": "Lỗi chưa xác định" + "RequestError": "Request failed", + "RequestAborted": "Request is aborted", + "RequestTimeout": "Request timed out", + "InvalidToken": "Token không hợp lệ", + "AuthError": "Xác minh thất bại", + "DomainAlreadyExists": "Tên miền đã tồn tại", + "CantSavePluginSettings": "Không thể lưu cài đặt", + "CantInstallPackage": "Không thể cài đặt tiện ích mở rộng", + "CantDeletePackage": "Không thể xóa bỏ tiện ích mở rộng", + "InvalidPluginPackage": "Gói tiện ích mở rộng không đúng", + "UnsupportedPluginPackage": "Gói tiện ích mở rộng chưa được hỗ trợ", + "UnknownError": "Lỗi chưa xác định" } } diff --git a/snappymail/v/0.0.0/app/localization/vi-VN/user.json b/snappymail/v/0.0.0/app/localization/vi-VN/user.json index 358270e2d..ca57b3e16 100644 --- a/snappymail/v/0.0.0/app/localization/vi-VN/user.json +++ b/snappymail/v/0.0.0/app/localization/vi-VN/user.json @@ -139,6 +139,7 @@ "MENU_VIEW_ORIGINAL": "Hiển thị nguồn", "HTML_VIEW": "Xem thư dạng HTML", "PLAIN_VIEW": "Xem thư dạng văn bản thuần", + "SWAP_COLORS": "Swap default (background)color", "MENU_DOWNLOAD_ORIGINAL": "Tải xuống dưới dạng tập tin .eml", "MENU_FILTER_SIMILAR": "Lọc ra những thư dạng này", "MENU_PRINT": "In", @@ -283,7 +284,8 @@ "ERROR": "Lỗi %TYPE%: %ERROR%", "CREATE_SELF_SIGNED": "Create self-signed", "VALID_UNTIL": "Valid until", - "PRIVATE_KEY": "Private key" + "PRIVATE_KEY": "Private key", + "ASK_CRYPTKEY_PASS": "Your login passphrase has changed. Please fill in your old login password to decrypt data" }, "OPENPGP": { "POPUP_IMPORT_TITLE": "Nhập mật mã OpenPGP", @@ -509,7 +511,7 @@ "TO_MANY_FOLDERS_DESC_2": "Chúng tôi chỉ hiển thị một vài trong số đó, để tránh vấn đề hiệu năng sử dụng.", "HELP_DELETE_FOLDER": "Xóa thư mục", "HELP_SHOW_HIDE_FOLDER": "Hiển thị\/ẩn đi thư mục", - "HELP_CHECK_FOR_NEW_MESSAGES": "Kiểm tra\/không kiểm tra xem có thư mới không", + "HELP_CHECK_FOR_NEW_MESSAGES": "Kiểm tra xem có thư mới không", "HIDE_UNSUBSCRIBED": "Ẩn đi những thư mục đã bỏ theo dõi", "UNHIDE_KOLAB_FOLDERS": "Unhide Kolab folders", "TYPE_CALENDAR": "Lịch", @@ -575,42 +577,43 @@ "LABEL_CLOSE_COMPOSE": "Đóng bảng viết thư" }, "NOTIFICATIONS": { - "REQUEST_ERROR": "Request failed", - "REQUEST_ABORTED": "Request is aborted", - "REQUEST_TIMEOUT": "Request timed out", - "INVALID_TOKEN": "Token không hợp lệ", - "AUTH_ERROR": "Xác minh thất bại", - "CONNECTION_ERROR": "Không thể kết nối với máy chủ", - "DOMAIN_ALREADY_EXISTS": "Tên miền đã tồn tại", - "DOMAIN_NOT_ALLOWED": "Tên miền không được cho phép truy cập", - "ACCOUNT_NOT_ALLOWED": "Tài khoản không được cho phép truy cập", - "CONTACTS_SYNC_ERROR": "Lỗi đồng bộ danh bạ", - "CANT_GET_MESSAGE_LIST": "Không thể lấy được danh sách thư", - "CANT_GET_MESSAGE": "Không lấy được thư", - "CANT_DELETE_MESSAGE": "Không thể xóa được thư", - "CANT_COPY_MESSAGE": "Không copy được thư", - "CANT_MOVE_MESSAGE": "Không di chuyển được thư", - "CANT_SAVE_MESSAGE": "Không lưu được thư", - "CANT_SEND_MESSAGE": "Không gửi được thư", - "INVALID_RECIPIENTS": "Người nhận thư không hợp lệ", - "CANT_SAVE_FILTERS": "Không thể lưu những tấm lọc thư", - "CANT_GET_FILTERS": "Không thể lấy về những tấm lọc thư", - "CANT_ACTIVATE_FILTERS_SCRIPT": "Không thể kích hoạt script lọc thư", - "CANT_DELETE_FILTERS_SCRIPT": "Không thể xóa script lọc thư", - "FILTERS_ARE_NOT_CORRECT": "Tấm lọc thư chưa đúng", - "CANT_CREATE_FOLDER": "Không thể tạo thư mục", - "CANT_RENAME_FOLDER": "Không thể đổi tên thư mục", - "CANT_DELETE_FOLDER": "Không thể xóa thư mục", - "CANT_DELETE_NON_EMPTY_FOLDER": "Không thể xóa thư mục đang chứa dữ liệu", - "CANT_SUBSCRIBE_FOLDER": "Không thể theo dõi thư mục", - "CANT_UNSUBSCRIBE_FOLDER": "Không thể bỏ theo dõi thư mục", - "DEMO_SEND_MESSAGE_ERROR": "Vì lý do an ninh, tài khoản này không được phép gửi thư ra cho những địa chỉ email bên ngoài!", - "DEMO_ACCOUNT_ERROR": "Vì lý do an ninh, tài khoản này không được phép thực hiện hành động này!", - "ACCOUNT_ALREADY_EXISTS": "Tài khoản đã tồn tại", - "ACCOUNT_DOES_NOT_EXIST": "Tài khoản không tồn tại", - "ACCOUNT_SWITCH_FAILED": "Không chuyển sang được tài khoản \"%EMAIL%\"", - "MAIL_SERVER_ERROR": "Có lỗi xảy ra khi truy cập vào máy chủ quản lý mail", - "INVALID_INPUT_ARGUMENT": "Thông tin nhập vào không hợp lệ", - "UNKNOWN_ERROR": "Lỗi chưa xác định" + "RequestError": "Request failed", + "RequestAborted": "Request is aborted", + "RequestTimeout": "Request timed out", + "InvalidToken": "Token không hợp lệ", + "AuthError": "Xác minh thất bại", + "ConnectionError": "Không thể kết nối với máy chủ", + "DomainAlreadyExists": "Tên miền đã tồn tại", + "DomainNotAllowed": "Tên miền không được cho phép truy cập", + "AccountNotAllowed": "Tài khoản không được cho phép truy cập", + "CryptKeyError": "Your login passphrase has changed", + "ContactsSyncError": "Lỗi đồng bộ danh bạ", + "CantGetMessageList": "Không thể lấy được danh sách thư", + "CantGetMessage": "Không lấy được thư", + "CantDeleteMessage": "Không thể xóa được thư", + "CantCopyMessage": "Không copy được thư", + "CantMoveMessage": "Không di chuyển được thư", + "CantSaveMessage": "Không lưu được thư", + "CantSendMessage": "Không gửi được thư", + "InvalidRecipients": "Người nhận thư không hợp lệ", + "CantSaveFilters": "Không thể lưu những tấm lọc thư", + "CantGetFilters": "Không thể lấy về những tấm lọc thư", + "CantActivateFiltersScript": "Không thể kích hoạt script lọc thư", + "CantDeleteFiltersScript": "Không thể xóa script lọc thư", + "FiltersAreNotCorrect": "Tấm lọc thư chưa đúng", + "CantCreateFolder": "Không thể tạo thư mục", + "CantRenameFolder": "Không thể đổi tên thư mục", + "CantDeleteFolder": "Không thể xóa thư mục", + "CantDeleteNonEmptyFolder": "Không thể xóa thư mục đang chứa dữ liệu", + "CantSubscribeFolder": "Không thể theo dõi thư mục", + "CantUnsubscribeFolder": "Không thể bỏ theo dõi thư mục", + "DemoSendMessageError": "Vì lý do an ninh, tài khoản này không được phép gửi thư ra cho những địa chỉ email bên ngoài!", + "DemoAccountError": "Vì lý do an ninh, tài khoản này không được phép thực hiện hành động này!", + "AccountAlreadyExists": "Tài khoản đã tồn tại", + "AccountDoesNotExist": "Tài khoản không tồn tại", + "AccountSwitchFailed": "Không chuyển sang được tài khoản \"%EMAIL%\"", + "MailServerError": "Có lỗi xảy ra khi truy cập vào máy chủ quản lý mail", + "InvalidInputArgument": "Thông tin nhập vào không hợp lệ", + "UnknownError": "Lỗi chưa xác định" } } diff --git a/snappymail/v/0.0.0/app/localization/zh-CN/admin.json b/snappymail/v/0.0.0/app/localization/zh-CN/admin.json index 280df7686..74d63cf9e 100644 --- a/snappymail/v/0.0.0/app/localization/zh-CN/admin.json +++ b/snappymail/v/0.0.0/app/localization/zh-CN/admin.json @@ -85,13 +85,14 @@ "TAB_SECURITY": { "LEGEND_SECURITY": "安全", "LABEL_USE_IMAGE_PROXY": "对外部图片使用本地代理", - "LABEL_ALLOW_OPENPGP": "允许使用 OpenPGP", + "LABEL_AUTO_VERIFY": "Verify signatures automatically", "LABEL_SHOW_PHP_INFO": "显示 PHP 信息", "LEGEND_ADMIN_PANEL_ACCESS_CREDENTIALS": "管理员面板访问凭据", "LABEL_CURRENT_PASSWORD": "当前密码", "LABEL_NEW_PASSWORD": "新密码", "LABEL_REPEAT_PASSWORD": "重复输入新密码", "LABEL_REQUIRE_VERIFICATION": "要求验证 SSL 证书", + "CRYPTO": "加密", "LABEL_ALLOW_SELF_SIGNED": "允许自签名证书" }, "TAB_PACKAGES": { @@ -135,6 +136,7 @@ "SECURE_OPTION_NONE": "无", "LABEL_ALLOW_SIEVE_SCRIPTS": "可使用筛选脚本", "LABEL_USE_SHORT_LOGIN": "使用短用户名登录", + "LABEL_LOWERCASE_LOGIN": "Lowercase login (case-insensitive)", "LABEL_USE_AUTH": "使用认证", "LABEL_SET_SENDER": "使用登录名作为发件人", "LABEL_AUTH_PLAIN_LINE": "强制单一 AUTH PLAIN 命令登录", @@ -168,17 +170,17 @@ "UNKNOWN_ERROR": "未知错误" }, "NOTIFICATIONS": { - "REQUEST_ERROR": "请求失败", - "REQUEST_ABORTED": "请求中止", - "REQUEST_TIMEOUT": "请求超时", - "INVALID_TOKEN": "无效标记", - "AUTH_ERROR": "认证失败", - "DOMAIN_ALREADY_EXISTS": "域名已经存在", - "CANT_SAVE_PLUGIN_SETTINGS": "无法保存设置", - "CANT_INSTALL_PACKAGE": "安装插件包失败", - "CANT_DELETE_PACKAGE": "移除插件包失败", - "INVALID_PLUGIN_PACKAGE": "无效插件包", - "UNSUPPORTED_PLUGIN_PACKAGE": "不支持的插件包", - "UNKNOWN_ERROR": "未知错误" + "RequestError": "请求失败", + "RequestAborted": "请求中止", + "RequestTimeout": "请求超时", + "InvalidToken": "无效标记", + "AuthError": "认证失败", + "DomainAlreadyExists": "域名已经存在", + "CantSavePluginSettings": "无法保存设置", + "CantInstallPackage": "安装插件包失败", + "CantDeletePackage": "移除插件包失败", + "InvalidPluginPackage": "无效插件包", + "UnsupportedPluginPackage": "不支持的插件包", + "UnknownError": "未知错误" } } diff --git a/snappymail/v/0.0.0/app/localization/zh-CN/user.json b/snappymail/v/0.0.0/app/localization/zh-CN/user.json index 60edd171f..4a0b5ac11 100644 --- a/snappymail/v/0.0.0/app/localization/zh-CN/user.json +++ b/snappymail/v/0.0.0/app/localization/zh-CN/user.json @@ -139,6 +139,7 @@ "MENU_VIEW_ORIGINAL": "显示原始内容", "HTML_VIEW": "显示 HTML 消息", "PLAIN_VIEW": "显示纯文本消息", + "SWAP_COLORS": "Swap default (background)color", "MENU_DOWNLOAD_ORIGINAL": "下载为 .eml 文件", "MENU_FILTER_SIMILAR": "筛选类似邮件", "MENU_PRINT": "打印邮件", @@ -283,7 +284,8 @@ "ERROR": "%TYPE% 错误: %ERROR%", "CREATE_SELF_SIGNED": "Create self-signed", "VALID_UNTIL": "Valid until", - "PRIVATE_KEY": "Private key" + "PRIVATE_KEY": "Private key", + "ASK_CRYPTKEY_PASS": "Your login passphrase has changed. Please fill in your old login password to decrypt data" }, "OPENPGP": { "POPUP_IMPORT_TITLE": "导入 OpenPGP 密钥", @@ -575,42 +577,43 @@ "LABEL_CLOSE_COMPOSE": "退出新邮件" }, "NOTIFICATIONS": { - "REQUEST_ERROR": "请求失败", - "REQUEST_ABORTED": "请求中止", - "REQUEST_TIMEOUT": "请求超时", - "INVALID_TOKEN": "无效标记", - "AUTH_ERROR": "认证失败", - "CONNECTION_ERROR": "无法连接至服务器", - "DOMAIN_ALREADY_EXISTS": "域已经存在", - "DOMAIN_NOT_ALLOWED": "不允许此域名", - "ACCOUNT_NOT_ALLOWED": "不允许此账户", - "CONTACTS_SYNC_ERROR": "联系人同步错误", - "CANT_GET_MESSAGE_LIST": "无法获取邮件列表", - "CANT_GET_MESSAGE": "无法获取邮件", - "CANT_DELETE_MESSAGE": "无法删除邮件", - "CANT_COPY_MESSAGE": "无法复制邮件", - "CANT_MOVE_MESSAGE": "无法移动邮件", - "CANT_SAVE_MESSAGE": "无法保存邮件", - "CANT_SEND_MESSAGE": "无法发送邮件", - "INVALID_RECIPIENTS": "无效接收人", - "CANT_SAVE_FILTERS": "无法保存筛选器规则", - "CANT_GET_FILTERS": "无法获取筛选器规则", - "CANT_ACTIVATE_FILTERS_SCRIPT": "无法激活过滤器脚本", - "CANT_DELETE_FILTERS_SCRIPT": "无法删除过滤器脚本", - "FILTERS_ARE_NOT_CORRECT": "不正确的筛选器规则", - "CANT_CREATE_FOLDER": "无法创建文件夹", - "CANT_RENAME_FOLDER": "无法重命名文件夹", - "CANT_DELETE_FOLDER": "无法删除文件夹", - "CANT_DELETE_NON_EMPTY_FOLDER": "无法删除非空文件夹", - "CANT_SUBSCRIBE_FOLDER": "无法订阅文件夹", - "CANT_UNSUBSCRIBE_FOLDER": "无法取消订阅文件夹", - "DEMO_SEND_MESSAGE_ERROR": "出于安全考虑,测试账号无法发送外部邮件。", - "DEMO_ACCOUNT_ERROR": "出于安全考虑,测试账号无法进行此操作!", - "ACCOUNT_ALREADY_EXISTS": "账户已存在", - "ACCOUNT_DOES_NOT_EXIST": "账户不存在", - "ACCOUNT_SWITCH_FAILED": "切换到账户 \"%EMAIL%\" 失败", - "MAIL_SERVER_ERROR": "访问邮件服务器遇到错误。", - "INVALID_INPUT_ARGUMENT": "无效的参数输入", - "UNKNOWN_ERROR": "未知错误" + "RequestError": "请求失败", + "RequestAborted": "请求中止", + "RequestTimeout": "请求超时", + "InvalidToken": "无效标记", + "AuthError": "认证失败", + "ConnectionError": "无法连接至服务器", + "DomainAlreadyExists": "域已经存在", + "DomainNotAllowed": "不允许此域名", + "AccountNotAllowed": "不允许此账户", + "CryptKeyError": "Your login passphrase has changed", + "ContactsSyncError": "联系人同步错误", + "CantGetMessageList": "无法获取邮件列表", + "CantGetMessage": "无法获取邮件", + "CantDeleteMessage": "无法删除邮件", + "CantCopyMessage": "无法复制邮件", + "CantMoveMessage": "无法移动邮件", + "CantSaveMessage": "无法保存邮件", + "CantSendMessage": "无法发送邮件", + "InvalidRecipients": "无效接收人", + "CantSaveFilters": "无法保存筛选器规则", + "CantGetFilters": "无法获取筛选器规则", + "CantActivateFiltersScript": "无法激活过滤器脚本", + "CantDeleteFiltersScript": "无法删除过滤器脚本", + "FiltersAreNotCorrect": "不正确的筛选器规则", + "CantCreateFolder": "无法创建文件夹", + "CantRenameFolder": "无法重命名文件夹", + "CantDeleteFolder": "无法删除文件夹", + "CantDeleteNonEmptyFolder": "无法删除非空文件夹", + "CantSubscribeFolder": "无法订阅文件夹", + "CantUnsubscribeFolder": "无法取消订阅文件夹", + "DemoSendMessageError": "出于安全考虑,测试账号无法发送外部邮件。", + "DemoAccountError": "出于安全考虑,测试账号无法进行此操作!", + "AccountAlreadyExists": "账户已存在", + "AccountDoesNotExist": "账户不存在", + "AccountSwitchFailed": "切换到账户 \"%EMAIL%\" 失败", + "MailServerError": "访问邮件服务器遇到错误。", + "InvalidInputArgument": "无效的参数输入", + "UnknownError": "未知错误" } } diff --git a/snappymail/v/0.0.0/app/localization/zh-TW/user.json b/snappymail/v/0.0.0/app/localization/zh-TW/user.json index 907397303..4d47d3432 100644 --- a/snappymail/v/0.0.0/app/localization/zh-TW/user.json +++ b/snappymail/v/0.0.0/app/localization/zh-TW/user.json @@ -139,6 +139,7 @@ "MENU_VIEW_ORIGINAL": "顯示原始內容", "HTML_VIEW": "檢視 HTML 郵件", "PLAIN_VIEW": "檢視純文字郵件", + "SWAP_COLORS": "Swap default (background)color", "MENU_DOWNLOAD_ORIGINAL": "下載為 .eml 檔案", "MENU_FILTER_SIMILAR": "篩選這類郵件", "MENU_PRINT": "列印郵件", @@ -283,7 +284,8 @@ "ERROR": "%TYPE% 錯誤: %ERROR%", "CREATE_SELF_SIGNED": "Create self-signed", "VALID_UNTIL": "Valid until", - "PRIVATE_KEY": "Private key" + "PRIVATE_KEY": "Private key", + "ASK_CRYPTKEY_PASS": "Your login passphrase has changed. Please fill in your old login password to decrypt data" }, "OPENPGP": { "POPUP_IMPORT_TITLE": "匯入 OpenPGP 金鑰", @@ -509,7 +511,7 @@ "TO_MANY_FOLDERS_DESC_2": "僅顯示部分資料夾,以避免性能問題。", "HELP_DELETE_FOLDER": "刪除資料夾", "HELP_SHOW_HIDE_FOLDER": "顯示\/隱藏資料夾", - "HELP_CHECK_FOR_NEW_MESSAGES": "檢查\/不檢查新郵件", + "HELP_CHECK_FOR_NEW_MESSAGES": "檢查新郵件", "HIDE_UNSUBSCRIBED": "隱藏取消訂閱的資料夾", "UNHIDE_KOLAB_FOLDERS": "取消隱藏 Kolab 資料夾", "TYPE_CALENDAR": "日曆", @@ -575,42 +577,43 @@ "LABEL_CLOSE_COMPOSE": "關閉撰寫" }, "NOTIFICATIONS": { - "REQUEST_ERROR": "請求失敗", - "REQUEST_ABORTED": "已中止請求", - "REQUEST_TIMEOUT": "請求逾時", - "INVALID_TOKEN": "標記無效", - "AUTH_ERROR": "驗證失敗", - "CONNECTION_ERROR": "無法連線至伺服器", - "DOMAIN_ALREADY_EXISTS": "域名已經存在", - "DOMAIN_NOT_ALLOWED": "不可使用該域名", - "ACCOUNT_NOT_ALLOWED": "不可使用該帳戶", - "CONTACTS_SYNC_ERROR": "連絡人同步錯誤", - "CANT_GET_MESSAGE_LIST": "無法取得郵件清單", - "CANT_GET_MESSAGE": "無法取得郵件", - "CANT_DELETE_MESSAGE": "無法刪除郵件", - "CANT_COPY_MESSAGE": "無法複製郵件", - "CANT_MOVE_MESSAGE": "無法移動郵件", - "CANT_SAVE_MESSAGE": "無法儲存郵件", - "CANT_SEND_MESSAGE": "無法傳送郵件", - "INVALID_RECIPIENTS": "收件人無效", - "CANT_SAVE_FILTERS": "無法儲存篩選器", - "CANT_GET_FILTERS": "無法取得篩選器", - "CANT_ACTIVATE_FILTERS_SCRIPT": "無法啟用篩選器指令檔", - "CANT_DELETE_FILTERS_SCRIPT": "無法刪除篩選器指令檔", - "FILTERS_ARE_NOT_CORRECT": "篩選器不正確", - "CANT_CREATE_FOLDER": "無法新增資料夾", - "CANT_RENAME_FOLDER": "無法重新命名資料夾", - "CANT_DELETE_FOLDER": "無法刪除資料夾", - "CANT_DELETE_NON_EMPTY_FOLDER": "無法刪除非空資料夾", - "CANT_SUBSCRIBE_FOLDER": "無法訂閱資料夾", - "CANT_UNSUBSCRIBE_FOLDER": "無法取消訂閱資料夾", - "DEMO_SEND_MESSAGE_ERROR": "基於安全性考量,此帳戶不允許傳送郵件至外部電子郵件地址。", - "DEMO_ACCOUNT_ERROR": "基於安全性考量,此帳戶不允許執行此操作!", - "ACCOUNT_ALREADY_EXISTS": "帳戶已經存在", - "ACCOUNT_DOES_NOT_EXIST": "帳戶不存在", - "ACCOUNT_SWITCH_FAILED": "切換至「%EMAIL%」帳戶時失敗", - "MAIL_SERVER_ERROR": "存取郵件伺服器時發生錯誤。", - "INVALID_INPUT_ARGUMENT": "輸入引數無效", - "UNKNOWN_ERROR": "未知錯誤" + "RequestError": "請求失敗", + "RequestAborted": "已中止請求", + "RequestTimeout": "請求逾時", + "InvalidToken": "標記無效", + "AuthError": "驗證失敗", + "ConnectionError": "無法連線至伺服器", + "DomainAlreadyExists": "域名已經存在", + "DomainNotAllowed": "不可使用該域名", + "AccountNotAllowed": "不可使用該帳戶", + "CryptKeyError": "Your login passphrase has changed", + "ContactsSyncError": "連絡人同步錯誤", + "CantGetMessageList": "無法取得郵件清單", + "CantGetMessage": "無法取得郵件", + "CantDeleteMessage": "無法刪除郵件", + "CantCopyMessage": "無法複製郵件", + "CantMoveMessage": "無法移動郵件", + "CantSaveMessage": "無法儲存郵件", + "CantSendMessage": "無法傳送郵件", + "InvalidRecipients": "收件人無效", + "CantSaveFilters": "無法儲存篩選器", + "CantGetFilters": "無法取得篩選器", + "CantActivateFiltersScript": "無法啟用篩選器指令檔", + "CantDeleteFiltersScript": "無法刪除篩選器指令檔", + "FiltersAreNotCorrect": "篩選器不正確", + "CantCreateFolder": "無法新增資料夾", + "CantRenameFolder": "無法重新命名資料夾", + "CantDeleteFolder": "無法刪除資料夾", + "CantDeleteNonEmptyFolder": "無法刪除非空資料夾", + "CantSubscribeFolder": "無法訂閱資料夾", + "CantUnsubscribeFolder": "無法取消訂閱資料夾", + "DemoSendMessageError": "基於安全性考量,此帳戶不允許傳送郵件至外部電子郵件地址。", + "DemoAccountError": "基於安全性考量,此帳戶不允許執行此操作!", + "AccountAlreadyExists": "帳戶已經存在", + "AccountDoesNotExist": "帳戶不存在", + "AccountSwitchFailed": "切換至「%EMAIL%」帳戶時失敗", + "MailServerError": "存取郵件伺服器時發生錯誤。", + "InvalidInputArgument": "輸入引數無效", + "UnknownError": "未知錯誤" } } diff --git a/snappymail/v/0.0.0/app/templates/Views/Admin/AdminSettingsSecurity.html b/snappymail/v/0.0.0/app/templates/Views/Admin/AdminSettingsSecurity.html index a4721ccdf..ce8aa0937 100644 --- a/snappymail/v/0.0.0/app/templates/Views/Admin/AdminSettingsSecurity.html +++ b/snappymail/v/0.0.0/app/templates/Views/Admin/AdminSettingsSecurity.html @@ -1,19 +1,38 @@
+
+
+
+
+
+
+
@@ -46,6 +65,7 @@ +
diff --git a/snappymail/v/0.0.0/app/templates/Views/Admin/PopupsDomain.html b/snappymail/v/0.0.0/app/templates/Views/Admin/PopupsDomain.html index ea5dac24c..b5d07b8aa 100644 --- a/snappymail/v/0.0.0/app/templates/Views/Admin/PopupsDomain.html +++ b/snappymail/v/0.0.0/app/templates/Views/Admin/PopupsDomain.html @@ -60,7 +60,17 @@ value: imapShortLogin } }">
- (user@domain.com → user) + (user@example.com → user) + (user@example.com → user) +
+
+ (USER@example.com → user@example.com)

SSL/TLS

- (user@domain.com → user) + (user@example.com → user) +
+
+ (USER@example.com → user@example.com)
-
+
-
+
@@ -310,10 +314,11 @@
+
-
+
diff --git a/snappymail/v/0.0.0/app/templates/Views/User/PopupsFolder.html b/snappymail/v/0.0.0/app/templates/Views/User/PopupsFolder.html index 7e98d6a7b..2c7b71b24 100644 --- a/snappymail/v/0.0.0/app/templates/Views/User/PopupsFolder.html +++ b/snappymail/v/0.0.0/app/templates/Views/User/PopupsFolder.html @@ -17,7 +17,7 @@
- +
/
+ +
+ +
+
+ + +
+ + +
+
diff --git a/snappymail/v/0.0.0/app/templates/Views/User/PopupsIdentity.html b/snappymail/v/0.0.0/app/templates/Views/User/PopupsIdentity.html index 57c451116..2f7934fdc 100644 --- a/snappymail/v/0.0.0/app/templates/Views/User/PopupsIdentity.html +++ b/snappymail/v/0.0.0/app/templates/Views/User/PopupsIdentity.html @@ -88,11 +88,11 @@ -->
S/MIME -
+
-
+
diff --git a/snappymail/v/0.0.0/app/templates/Views/User/SystemDropDown.html b/snappymail/v/0.0.0/app/templates/Views/User/SystemDropDown.html index 8639eecb7..dbc72954d 100644 --- a/snappymail/v/0.0.0/app/templates/Views/User/SystemDropDown.html +++ b/snappymail/v/0.0.0/app/templates/Views/User/SystemDropDown.html @@ -18,7 +18,7 @@
  • -
  • diff --git a/snappymail/v/0.0.0/imapsync.php b/snappymail/v/0.0.0/imapsync.php index 2325afc73..e7beb1178 100755 --- a/snappymail/v/0.0.0/imapsync.php +++ b/snappymail/v/0.0.0/imapsync.php @@ -154,8 +154,6 @@ function getImapClient(int $host) { global $options; - $oConfig = \RainLoop\API::Config(); - $type = SecurityType::AUTO_DETECT; if (isset($options["tls{$host}"])) { $type = SecurityType::STARTTLS; @@ -168,6 +166,7 @@ function getImapClient(int $host) 'host' => $options["host{$host}"] ?? 'localhost', 'port' => $options["port{$host}"] ?? 143, 'type' => $type, +// 'sasl' => [], 'ssl' => [] ]); if (993 === $ImapSettings->port) { @@ -178,19 +177,8 @@ function getImapClient(int $host) if (isset($options["timeout{$host}"])) { $ImapSettings->timeout = (int) $options["timeout{$host}"]; } - if ($oConfig->Get('labs', 'sasl_allow_scram_sha', false)) { - \array_push($ImapSettings->SASLMechanisms, 'SCRAM-SHA3-512', 'SCRAM-SHA-512', 'SCRAM-SHA-256', 'SCRAM-SHA-1'); - } - if ($oConfig->Get('labs', 'sasl_allow_cram_md5', false)) { - $ImapSettings->SASLMechanisms[] = 'CRAM-MD5'; - } - if ($oConfig->Get('labs', 'sasl_allow_plain', true)) { - $ImapSettings->SASLMechanisms[] = 'PLAIN'; - } - $ImapSettings->Login = $options["user{$host}"]; + $ImapSettings->Login = $options["user{$host}"]; // convert to punycode? $ImapSettings->Password = $options["password{$host}"]; -// $ImapSettings1->ProxyAuthUser = ''; -// $ImapSettings1->ProxyAuthPassword = ''; $ImapSettings->useAuth = true; $oImapClient = new \MailSo\Imap\ImapClient; diff --git a/snappymail/v/0.0.0/include.php b/snappymail/v/0.0.0/include.php index 6c770f7c5..36fd600e1 100644 --- a/snappymail/v/0.0.0/include.php +++ b/snappymail/v/0.0.0/include.php @@ -4,10 +4,18 @@ if (defined('APP_VERSION_ROOT_PATH')) { } // PHP 8 -if (\PHP_VERSION_ID < 80000) { +if (PHP_VERSION_ID < 80000) { require __DIR__ . '/app/libraries/polyfill/php8.php'; } +if (!extension_loaded('ctype')) { + require __DIR__ . '/app/libraries/polyfill/ctype.php'; +} + +if (!extension_loaded('intl')) { + require __DIR__ . '/app/libraries/polyfill/intl.php'; +} + if (!defined('APP_VERSION')) { define('APP_VERSION', basename(__DIR__)); } diff --git a/snappymail/v/0.0.0/setup.php b/snappymail/v/0.0.0/setup.php index b073eaeac..5dfa799fd 100644 --- a/snappymail/v/0.0.0/setup.php +++ b/snappymail/v/0.0.0/setup.php @@ -107,7 +107,7 @@ if (defined('APP_VERSION')) { } } - $sName = \SnappyMail\IDN::toAscii(mb_strtolower(gethostname())); + $sName = idn_to_ascii(mb_strtolower(gethostname())); $sFile = APP_PRIVATE_DATA.'domains/'.$sName.'.json'; if (!file_exists($sFile) && !file_exists(APP_PRIVATE_DATA.'domains/'.$sName.'.ini')) { $config = json_decode(file_get_contents(__DIR__ . '/app/domains/default.json'), true); diff --git a/snappymail/v/0.0.0/themes/NextcloudV25+/styles.css b/snappymail/v/0.0.0/themes/NextcloudV25+/styles.css index 7cb38a5a7..d86fd5b1d 100644 --- a/snappymail/v/0.0.0/themes/NextcloudV25+/styles.css +++ b/snappymail/v/0.0.0/themes/NextcloudV25+/styles.css @@ -1322,6 +1322,7 @@ html.rl-left-panel-disabled #rl-app .b-folders .b-toolbar { #rl-app .b-folders .b-content { top: 60px; bottom: 162px; + color: unset; } #rl-app .b-folders .b-content.show-on-panel-disabled { diff --git a/tasks/config.js b/tasks/config.js index 638390983..01bd89b2a 100644 --- a/tasks/config.js +++ b/tasks/config.js @@ -64,6 +64,7 @@ config.paths.js = { 'vendors/knockout/build/output/knockout-latest.js', // 'vendors/knockout/build/output/knockout-latest.debug.js', 'vendors/squire/build/squire-raw.js', + 'vendors/mathiasbynens/punycode.js', 'dev/External/SquireUI.js' ] }, diff --git a/vendors/knockout/build/fragments/source-references.js b/vendors/knockout/build/fragments/source-references.js index 0ed63d61b..71edd3e1f 100644 --- a/vendors/knockout/build/fragments/source-references.js +++ b/vendors/knockout/build/fragments/source-references.js @@ -1,7 +1,7 @@ knockoutDebugCallback([ 'src/namespace.js', 'src/google-closure-compiler-utils.js', - 'src/version.js', +// 'src/version.js', // 'src/options.js', 'src/utils.js', 'src/utils.domData.js', diff --git a/vendors/knockout/build/output/knockout-latest.debug.js b/vendors/knockout/build/output/knockout-latest.debug.js index a1ebe1f6a..f7fa32710 100644 --- a/vendors/knockout/build/output/knockout-latest.debug.js +++ b/vendors/knockout/build/output/knockout-latest.debug.js @@ -24,7 +24,6 @@ ko.exportSymbol = (koPath, object) => { target = target[tokens[i]]; target[tokens[l]] = object; }; -ko['version'] = "3.5.1-sm"; ko.utils = { extend: (target, source) => source ? Object.assign(target, source) : target, @@ -111,7 +110,7 @@ ko.utils = { triggerEvent: (element, eventType) => { if (!element?.nodeType) - throw new Error("element must be a DOM node when calling triggerEvent"); + throw Error("element must be a DOM node when calling triggerEvent"); element.dispatchEvent(new Event(eventType)); }, @@ -198,7 +197,7 @@ ko.utils.domNodeDisposal = (() => { return { 'addDisposeCallback' : (node, callback) => { if (typeof callback != "function") - throw new Error("Callback must be a function"); + throw Error("Callback must be a function"); getDisposeCallbacksCollection(node, 1).add(callback); }, @@ -246,9 +245,8 @@ ko['extenders'] = { } }; -var primitiveTypes = { 'undefined':1, 'boolean':1, 'number':1, 'string':1 }; function valuesArePrimitiveAndEqual(a, b) { - return (a === null || primitiveTypes[typeof(a)]) ? (a === b) : false; + return a === b && a !== Object(a); } function throttle(callback, timeout) { @@ -484,7 +482,7 @@ ko.dependencyDetection = { registerDependency: subscribable => { if (currentFrame) { if (!ko.isSubscribable(subscribable)) - throw new Error("Only subscribable things can act as dependencies"); + throw Error("Only subscribable things can act as dependencies"); currentFrame.callback.call(currentFrame.callbackTarget, subscribable, subscribable._id || (subscribable._id = ++lastId)); } @@ -586,8 +584,8 @@ ko.exportSymbol('observable.fn', observableFn); ko['observableArray'] = initialValues => { initialValues = initialValues || []; - if (typeof initialValues != 'object' || !('length' in initialValues)) - throw new Error("The argument passed when initializing an observable array must be an array, or null, or undefined."); + if (!Array.isArray(initialValues)) + throw Error("The argument passed when initializing an observable array must be an array, or null, or undefined."); return Object.setPrototypeOf(ko.observable(initialValues), ko['observableArray']['fn']).extend({'trackArrayChanges':true}); }; @@ -690,29 +688,28 @@ ko['extenders']['trackArrayChanges'] = (target, options) => { } }; - function trackChanges() { + function notifyChanges() { + if (pendingChanges) { + // Make a copy of the current contents and ensure it's an array + var currentContents = [].concat(target.peek() || []), changes; - function notifyChanges() { - if (pendingChanges) { - // Make a copy of the current contents and ensure it's an array - var currentContents = [].concat(target.peek() || []), changes; + // Compute the diff and issue notifications, but only if someone is listening + if (target.hasSubscriptionsForEvent(arrayChangeEventName)) { + changes = getChanges(previousContents, currentContents); + } - // Compute the diff and issue notifications, but only if someone is listening - if (target.hasSubscriptionsForEvent(arrayChangeEventName)) { - changes = getChanges(previousContents, currentContents); - } + // Eliminate references to the old, removed items, so they can be GCed + previousContents = currentContents; + cachedDiff = null; + pendingChanges = 0; - // Eliminate references to the old, removed items, so they can be GCed - previousContents = currentContents; - cachedDiff = null; - pendingChanges = 0; - - if (changes?.length) { - target.notifySubscribers(changes, arrayChangeEventName); - } + if (changes?.length) { + target.notifySubscribers(changes, arrayChangeEventName); } } + } + function trackChanges() { if (trackingChanges) { // Whenever there's a new subscription and there are pending notifications, make sure all previous // subscriptions are notified of the change so that all subscriptions are in sync. @@ -753,16 +750,15 @@ ko['extenders']['trackArrayChanges'] = (target, options) => { var diff = [], arrayLength = rawArray.length, argsLength = args.length, - offset = 0; + offset = 0, + pushDiff = (status, value, index) => + diff[diff.length] = { 'status': status, 'value': value, 'index': index }; - function pushDiff(status, value, index) { - return diff[diff.length] = { 'status': status, 'value': value, 'index': index }; - } switch (operationName) { case 'push': offset = arrayLength; case 'unshift': - for (let index = 0; index < argsLength; index++) { + for (let index = 0; index < argsLength; ++index) { pushDiff('added', args[index], offset + index); } break; @@ -824,8 +820,8 @@ ko.computed = (evaluatorFunctionOrOptions, options) => { pure: false, isSleeping: false, readFunction: options["read"], - disposeWhenNodeIsRemoved: options["disposeWhenNodeIsRemoved"] || options.disposeWhenNodeIsRemoved || null, - disposeWhen: options["disposeWhen"] || options.disposeWhen, + disposeWhenNodeIsRemoved: options.disposeWhenNodeIsRemoved || null, + disposeWhen: options.disposeWhen, domNodeDisposalCallback: null, dependencyTracking: {}, dependenciesCount: 0, @@ -835,7 +831,7 @@ ko.computed = (evaluatorFunctionOrOptions, options) => { function computedObservable() { if (arguments.length > 0) { if (typeof writeFunction !== "function") { - throw new Error("Cannot write a value to a ko.computed unless you specify a 'write' option. If you wish to read the current value, don't pass any parameters."); + throw Error("Cannot write a value to a ko.computed unless you specify a 'write' option. If you wish to read the current value, don't pass any parameters."); } // Writing a value writeFunction(...arguments); @@ -1004,14 +1000,8 @@ var computedFn = { return target['subscribe'](this.evaluatePossiblyAsync, this); }, evaluatePossiblyAsync() { - var computedObservable = this, - throttleEvaluationTimeout = computedObservable['throttleEvaluation']; - if (throttleEvaluationTimeout >= 0) { - clearTimeout(this[computedState].evaluationTimeoutInstance); - this[computedState].evaluationTimeoutInstance = setTimeout(() => - computedObservable.evaluateImmediate(true /*notifyChange*/) - , throttleEvaluationTimeout); - } else if (computedObservable._evalDelayed) { + var computedObservable = this; + if (computedObservable._evalDelayed) { computedObservable._evalDelayed(true /*isChange*/); } else { computedObservable.evaluateImmediate(true /*notifyChange*/); @@ -1285,7 +1275,7 @@ ko.selectExtensions = { } }, - writeValue: (element, value, allowUnset) => { + writeValue: (element, value) => { switch (element.nodeName) { case 'OPTION': if (typeof value === "string") { @@ -1314,7 +1304,7 @@ ko.selectExtensions = { break; } } - if (allowUnset || selection >= 0 || (noValue && element.size > 1)) { + if (selection >= 0 || (noValue && element.size > 1)) { element.selectedIndex = selection; } break; @@ -1438,9 +1428,6 @@ ko.expressionRewriting = (() => { return result; }, - // Two-way bindings include a write function that allow the handler to update the value even if it's not an observable. -// twoWayBindings = new Set, - preProcessBindings = (bindingsStringOrKeyValueArray) => { var resultStrings = [], @@ -1448,16 +1435,6 @@ ko.expressionRewriting = (() => { keyValueArray = parseObjectLiteral(bindingsStringOrKeyValueArray), processKeyValue = (key, val) => { - var /*writableVal,*/ obj = ko.bindingHandlers[key]; - if (obj?.['preprocess'] && !obj['preprocess'](val, key, processKeyValue)) - return; -/* - if (twoWayBindings.has(key) && (writableVal = getWriteableValue(val))) { - // For two-way bindings, provide a write method in case the value - // isn't a writable observable. - propertyAccessorResultStrings.push("'" + key + "':function(_z){" + writableVal + "=_z}"); - } -*/ // Values are wrapped in a function so that each value can be accessed independently val = 'function(){return ' + val + ' }'; resultStrings.push("'" + key + "':" + val); @@ -1474,10 +1451,6 @@ ko.expressionRewriting = (() => { }; return { - bindingRewriteValidators: [], - -// twoWayBindings: twoWayBindings, - parseObjectLiteral: parseObjectLiteral, preProcessBindings: preProcessBindings, @@ -1547,7 +1520,7 @@ ko.expressionRewriting = (() => { ++depth; } if (!allowUnbalanced) - throw new Error("Cannot find closing comment tag to match: " + startComment.nodeValue); + throw Error("Cannot find closing comment tag to match: " + startComment.nodeValue); return null; } @@ -1602,7 +1575,7 @@ ko.expressionRewriting = (() => { } let first = node.firstChild; if (first && isEndComment(first)) { - throw new Error("Found invalid end comment, as the first child of " + node); + throw Error("Found invalid end comment, as the first child of " + node); } return first; }, @@ -1696,7 +1669,7 @@ ko.bindingHandlers = {}; // The ko.bindingContext constructor is only called directly to create the root context. For child // contexts, use bindingContext.createChildContext or bindingContext.extend. ko.bindingContext = class { - constructor(dataItemOrAccessor, parentContext, dataItemAlias, extendCallback, options) + constructor(dataItemOrAccessor, parentContext, extendCallback, options) { var self = this, shouldInheritData = dataItemOrAccessor === inheritParentVm, @@ -1725,7 +1698,6 @@ ko.bindingContext = class { self[contextAncestorBindingInfo] = parentContext[contextAncestorBindingInfo]; } } else { - self['$parents'] = []; self['$root'] = dataItem; // Export 'ko' in the binding context so it will be available in bindings and templates @@ -1739,13 +1711,9 @@ ko.bindingContext = class { if (shouldInheritData) { dataItem = self['$data']; } else { - self['$rawData'] = dataItemOrObservable; self['$data'] = dataItem; } - if (dataItemAlias) - self[dataItemAlias] = dataItem; - // The extendCallback function is provided when creating a child context or extending a context. // It handles the specific actions needed to finish setting up the binding context. Actions in this // function could also add dependencies to this binding context. @@ -1790,21 +1758,11 @@ ko.bindingContext = class { // But this does not mean that the $data value of the child context will also get updated. If the child // view model also depends on the parent view model, you must provide a function that returns the correct // view model on each update. - 'createChildContext'(dataItemOrAccessor, dataItemAlias, extendCallback, options) { - if (!options && dataItemAlias && typeof dataItemAlias == "object") { - options = dataItemAlias; - dataItemAlias = options['as']; - extendCallback = options['extend']; - } - - return new ko.bindingContext(dataItemOrAccessor, this, dataItemAlias, (self, parentContext) => { + 'createChildContext'(dataItemOrAccessor, options) { + return new ko.bindingContext(dataItemOrAccessor, this, (self, parentContext) => { // Extend the context hierarchy by setting the appropriate pointers - self['$parentContext'] = parentContext; self['$parent'] = parentContext['$data']; - self['$parents'] = (parentContext['$parents'] || []).slice(0); - self['$parents'].unshift(self['$parent']); - if (extendCallback) - extendCallback(self); + options['extend']?.(self); }, options); } @@ -1812,7 +1770,7 @@ ko.bindingContext = class { // Similarly to "child" contexts, provide a function here to make sure that the correct values are set // when an observable view model is updated. 'extend'(properties, options) { - return new ko.bindingContext(inheritParentVm, this, null, self => + return new ko.bindingContext(inheritParentVm, this, self => ko.utils.extend(self, typeof(properties) == "function" ? properties(self) : properties) , options); } @@ -1885,7 +1843,7 @@ ko.bindingEvent = { } else if (bindingInfo.asyncContext === undefined && bindingInfo.eventSubscribable?.hasSubscriptionsForEvent(ko.bindingEvent.descendantsComplete)) { // It's currently an error to register a descendantsComplete handler for a node that was never registered as completing asynchronously. // That's because without the asyncContext, we don't have a way to know that all descendants have completed. - throw new Error("descendantsComplete event not supported for bindings on this node"); + throw Error("descendantsComplete event not supported for bindings on this node"); } } } @@ -1909,12 +1867,6 @@ ko.bindingEvent = { } }; -function validateThatBindingIsAllowedForVirtualElements(bindingName) { - var validator = ko.virtualElements.allowedBindings[bindingName]; - if (!validator) - throw new Error("The binding '" + bindingName + "' cannot be used with virtual elements") -} - function applyBindingsToDescendantsInternal(bindingContext, elementOrVirtualElement) { var currentChild, nextInQueue = ko.virtualElements.firstChild(elementOrVirtualElement); @@ -1935,7 +1887,7 @@ function applyBindingsToNodeAndDescendantsInternal(bindingContext, nodeVerified) // (1) We need to store the binding info for the node (all element nodes) // (2) It might have bindings (e.g., it has a data-bind attribute, or it's a marker for a containerless template) if (isElement || ko.bindingProvider.nodeHasBindings(nodeVerified)) - bindingContextForDescendants = applyBindingsToNodeInternal(nodeVerified, null, bindingContext)['bindingContextForDescendants']; + bindingContextForDescendants = applyBindingsToNodeInternal(nodeVerified, null, bindingContext); // Don't want bindings that operate on text nodes to mutate