snappymail/plugins
S-A-L13 876ed221c3
Update Fork (#2)
* Cleanup OpenPgpImportPopupView code

* update polish translation

* small fix

* Added Import S/MIME certificate popup
And much better handling of the sign and encrypt options

* bugfix: store in Passphrases

* Resolve #1448

* pre-verify S/MIME opaque signed messages so we have a body to view

* Fix timestampToString() for future dates

* Move php8.php to /app/libraries/polyfill/

* Improved Settings handling to prevent bugs in outer code

* Changed AbstractProvider::IsActive() to be abstract

* Example for #1449

* bugfix: previous IsActive() commit

* OpenSSL required due to S/MIME

* Use get_debug_type() instead of gettype()

* update polish translation

* Make all Enumerations classes abstract

* Added search functionality in Admin -> Config
And removed the unused ['capa']['quota']

* Cleanup Quota handling

* OPEN_PGP should be OPENPGP as it is one word

* Improve Capa handling

* Resolve #1451

* Bugfix TypeError: b64Encode(...).match(...) is null

* Small StorageType change

* Bugfix: mailvelope editor failed

* Bugfix: undefined getMailvelopePrivateKeyFor()

* Bugfix: MIME parser RegExp didn't escape `boundary` which caused issues

* Return detailed info on PgpImportKey

* Show GnuPG verify error

* Sort PGP keys by email and id

* Sort S/MIME certificates on emailAddress else validTo

* S/MIME import from signature use `BEGIN PKCS7`

* Optionally use existing private key to generate S/MIME certificate

* Chaned some error_log() to MailSo Logger()

* Force reload of S/MIME certificates list on import

* Make better use of SnappyMail\SensitiveString

* Fix view PGP key button

* Mask all POST data that has a key which contains `pass`

* v2.35.1

* Resolve #1455

* Improved GnuPG error handling

* Update pt/pt-PT translation

* update Polish translation

* Drop support for gnupg pecl extension as it fails with "no passphrase" issues

* Resolve #1456

* Resolve #1458

* v2.35.2

* fix changelog

* Resolve #1461

* Update pt/pt-PT translation

* compact-composer plugin v1.0.0

* Resolve #1462

* Fix decrypt error message

* `new Error()` to `Error()`

* Resolve #1463

* Show url for #1466

* Simplify SignMe/Remember me code

* Simplify language Notifications

* Bugfix: SetPassword expects \SnappyMail\SensitiveString

* https://github.com/the-djmaze/snappymail/issues/1450#issuecomment-1972147950

* improve: fire the 'squire2-toolbar' event after more props are added

* improve: add dark theme support and use 'button' element as menu trigger for consistent styling

* fix: use compact template in non-destructive way (do not replace the PopupsCompose template if a different wysiwyg is used)

* Update admin.json

* Update user.json

* CSS rainloopErrorTip location

* Improved error handling on PGP and S/MIME decrypt

* KnockoutJS remove unused `beforeRemove`

* KnockoutJS drop unused `as`

* KnockoutJS simplify renderMode because only 1 option is used

* KnoutJS cleanup templating.js a bit

* KnockoutJS drop unused `bindingRewriteValidators`

* KnockoutJS drop the twoWayBindings code

* KnockoutJS simplify virtualElements binding check

* KnockoutJS simplify applyBindingsToNodeInternal

* KnockoutJS use Array.isArray

* KnockoutJS drop alias `textinput` for `textInput`

* KnockoutJS scramble `createChildContext`

* KnockoutJS scramble `controlsDescendantBindings`

* KnockoutJS scramble `exportDependencies`

* KnockoutJS drop unused `throttleEvaluation`

* KnockoutJS drop unused `valueAllowUnset`

* KnockoutJS drop unused `templateNodes`

* KnockoutJS drop unused `optionsCaption`

* KnockoutJS drop unused `dontLimitMoves`

* KnockoutJS drop unused `uniqueName`

* KnockoutJS drop IE leftovers

* KnockoutJS drop unused `preprocess`

* KnockoutJS drop unused "disposeWhenNodeIsRemoved" and "disposeWhen"

* KnockoutJS don't scramble exportDependencies. controlsDescendantBindings, createChildContext

* KnockoutJS drop unused `$parentContext` and `$parents`

* KnockoutJS drop unused `$rawData`

* Knockoutjs built latest

* KnockoutJS drop unused template options `nodes`, `if`, `ifnot`

* KnockoutJS use more Array.isArray

* KnockoutJS cleanup code a bit

* KnockoutJS primitiveTypes can just be checked with Object()

* KnockoutJS rebuilt

* Verify S/MIME signed automatically and log Exception

* Automatically verify PGP and S/MIME signed messages

* `new Error` to `Error`

* By default throw AccountNotAllowed as confused in #1478

* GPG use pinentries for decrypt, sign and export

* Better GPG error handling

* GPG show error on view/export

* OpenPGP fix handling of importing keys

* Make "verify signatures automatically" optional, as it requires more IMAP fetching

* S/MIME don't post identity key and certificate, just fetch from server

* Show error to old browsers, instead of crashing

* Automatically verify S/MIME decrypted signed message

---------

Co-authored-by: the-djmaze <>
Co-authored-by: tinola <tinola@poczta.onet.pl>
Co-authored-by: Maarten <3752035+the-djmaze@users.noreply.github.com>
Co-authored-by: lmperfis <joint.striker@gmail.com>
Co-authored-by: Sergey Mosin <sergey@srgdev.com>
Co-authored-by: hguilbert <51283484+hguilbert@users.noreply.github.com>
2024-03-04 17:00:27 +01:00
..
add-x-originating-ip-header Improve plugin Description/README functionality 2021-03-04 12:15:11 +01:00
auto-domain-grab Update plugins to use new Net/Imap/Smtp/Sieve Settings object 2022-11-11 14:32:24 +01:00
avatars Resolve #1402 2024-01-23 10:23:06 +01:00
backup Backup plugin don't use tar stream yet (was a test) 2023-12-12 22:55:02 +01:00
black-list Update Fork (#2) 2024-03-04 17:00:27 +01:00
change-password Update ChangePassword version number 2023-04-11 09:18:00 +02:00
change-password-froxlor Wrong Froxlor plugin class name 2022-07-19 12:43:31 +02:00
change-password-hestia Update index.php 2023-05-17 22:33:06 +02:00
change-password-hmailserver Solve the massive logger bloat by simplifying it to be RFC 5424 2022-10-15 17:39:49 +02:00
change-password-ispconfig Update ChangePassword version number 2023-04-11 09:18:00 +02:00
change-password-poppassd Resolve #1356 2023-12-08 21:48:03 +01:00
change-smtp-ehlo-message Merge all example plugins 2022-05-31 10:47:14 +02:00
compact-composer Update Fork (#2) 2024-03-04 17:00:27 +01:00
contact-group-excel-paste Release plugins as tar.gz because PharData is more common then ZipArchive 2021-02-10 09:50:20 +01:00
custom-login-mapping v2.25.2 2023-02-02 13:26:23 +01:00
custom-system-folders [snappymail] More changes to use the word "extensions" rather than plugins/packages 2021-08-27 23:49:03 +02:00
demo-account set smimeKey and smimeCertificate for demo 2024-02-20 15:21:32 +01:00
example Update example extension/plugin 2024-02-08 00:46:16 +01:00
imap-contacts-suggestions ImapConnectAndLoginHelper() to ImapConnectAndLogin() 2023-01-19 10:55:34 +01:00
kolab v2.26.0 2023-02-10 11:38:41 +01:00
ldap-contacts-suggestions #1243 version bump 2023-10-01 11:18:37 +02:00
ldap-identities Changed version to 2.3 2024-02-27 14:30:57 +01:00
ldap-login-mapping Resolve #863 2023-01-19 17:27:14 +01:00
ldap-mail-accounts Update release date 2023-03-14 14:50:22 +01:00
login-autoconfig Added Autoconfig as extension 2024-02-06 18:43:33 +01:00
login-external Fix 'Location:' headers using proper '302 Found' header 2023-03-14 09:58:30 +01:00
login-external-sso Bugfix: undefined $_POST array key 2022-11-11 17:11:44 +01:00
login-oauth2 Other solutions for nextcloud auto login #1247 due to a TOTP conflict 2023-10-02 15:51:19 +02:00
login-override v2.25.5 2023-02-09 10:37:43 +01:00
login-register Resolve issue #82 by adding back all languages 2021-10-21 22:02:06 +02:00
login-remote Fix 'Location:' headers using proper '302 Found' header 2023-03-14 09:58:30 +01:00
mailbox-detect Prevent folder/messages flags conflict by using the right name attributes for Folders 2023-03-13 11:49:40 +01:00
nextcloud Update Fork (#2) 2024-03-04 17:00:27 +01:00
override-smtp-credentials Bugfix and improved 2023-01-19 10:55:01 +01:00
proxy-auth Version bump for #1400 2024-01-22 00:05:06 +01:00
proxyauth-login-example Improve solution for #1241 2023-10-24 11:43:47 +02:00
recaptcha Redesign \SnappyMail\HTTP\CSP for better control 2023-02-22 13:57:37 +01:00
set-remote-addr Resolve #1195 2023-07-03 15:33:38 +02:00
smtp-use-from-adr-account Small change for #1350 2023-12-11 11:07:10 +01:00
snowfall-on-login-screen Fix snowfall plugin #434 2022-06-13 21:21:51 +02:00
two-factor-auth Resolve #1439 2024-02-15 23:56:26 +01:00
video-on-login-screen add video-on-login-screen plugin 2023-11-09 21:24:48 +01:00
view-ics Added link to schema.org #1422 2024-02-08 17:41:50 +01:00
white-list Update Fork (#2) 2024-03-04 17:00:27 +01:00
wysiwyg-example v2.34.0 2024-02-13 13:24:45 +01:00
README.md Update example extension/plugin 2024-02-08 00:46:16 +01:00

Also see https://github.com/the-djmaze/snappymail/tree/master/plugins/example

PHP

class Plugin extends \RainLoop\Plugins\AbstractPlugin
{
	public function __construct();

	/** Returns static::NAME */
	public function Name(): string;

	/** Returns /README file contents or static::DESCRIPTION */
	public function Description(): string;

	/** When $bLangs is boolean it sets the value, else returns current value */
	public function UseLangs(?bool $bLangs = null): bool;

	/** When true the result is empty string, else the error message */
	public function Supported(): string;

	/** Initialize settings */
	public function Init(): void;

	public function FilterAppDataPluginSection(bool $bAdmin, bool $bAuth, array &$aConfig): void;

	/** Returns array of all plugin Property options for use in Admin -> Extensions -> Plugin cog wheel */
	protected function configMapping(): array;

	/** With this function you hook to an event
	 * $sHookName see chapter "Hooks" below for available names
	 * $sFunctionName the name of a function in this class
	 */
	final protected function addHook(string $sHookName, string $sFunctionName): self;

	final protected function addCss(string $sFile, bool $bAdminScope = false): self;

	final protected function addJs(string $sFile, bool $bAdminScope = false): self;

	final protected function addTemplate(string $sFile, bool $bAdminScope = false): self;

	final protected function addJsonHook(string $sActionName, string $sFunctionName): self;

	/**
	 * You may register your own service actions.
	 * Url is like /?{actionname}/etc.
	 * Predefined actions of \RainLoop\ServiceActions that can't be registered are:
	 * - admin
	 * - AdminAppData
	 * - AppData
	 * - Append
	 * - Backup
	 * - BadBrowser
	 * - CspReport
	 * - Css
	 * - Json
	 * - Lang
	 * - Mailto
	 * - NoCookie
	 * - NoScript
	 * - Ping
	 * - Plugins
	 * - ProxyExternal
	 * - Raw
	 * - Sso
	 * - Upload
	 * - UploadBackground
	 * - UploadContacts
	 */
	final protected function addPartHook(string $sActionName, string $sFunctionName): self

	final public function Config(): \RainLoop\Config\Plugin;
	final public function Manager(): \RainLoop\Plugins\Manager;
	final public function Path(): string;
	final public function ConfigMap(bool $flatten = false): array;

	/**
	 * Returns result of Actions->DefaultResponse($sFunctionName, $mData) or json_encode($mData)
	 */
	final protected function jsonResponse(string $sFunctionName, $mData): mixed;

	final public function jsonParam(string $sKey, $mDefault = null): mixed;

	final public function getUserSettings(): array;

	final public function saveUserSettings(array $aSettings): bool;
}

JavaScript

class PluginPopupView extends rl.pluginPopupView
{
	// Happens when DOM is created
	onBuild(dom) {}

	// Happens before showModal()
	beforeShow(...params) {}
	// Happens after showModal()
	onShow(...params) {}
	// Happens after showModal() animation transitionend
	afterShow() {}

	// Happens when user hits Escape or Close key
	// return false to prevent closing, use close() manually
	onClose() {}
	// Happens before animation transitionend
	onHide() {}
	// Happens after animation transitionend
	afterHide() {}
}

PluginPopupView.showModal();

Hooks

$Plugin->addHook('hook.name', 'functionName');

Login

login.credentials.step-1

params:
	string &$sEmail

login.credentials.step-2

params:
	string &$sEmail
	string &$sPassword

login.credentials

params:
	string &$sEmail
	string &$sLogin
	string &$sPassword

login.success

params:
	\RainLoop\Model\MainAccount $oAccount

IMAP

imap.before-connect

params:
	\RainLoop\Model\Account $oAccount
	\MailSo\Imap\ImapClient $oImapClient
	\MailSo\Imap\Settings $oSettings

imap.after-connect

params:
	\RainLoop\Model\Account $oAccount
	\MailSo\Imap\ImapClient $oImapClient
	\MailSo\Imap\Settings $oSettings

imap.before-login

params:
	\RainLoop\Model\Account $oAccount
	\MailSo\Imap\ImapClient $oImapClient
	\MailSo\Imap\Settings $oSettings

imap.after-login

params:
	\RainLoop\Model\Account $oAccount
	\MailSo\Imap\ImapClient $oImapClient
	bool $bSuccess
	\MailSo\Imap\Settings $oSettings

imap.message-headers

params:
	array &$aHeaders

Allows you to fetch more MIME headers for messages.

Sieve

sieve.before-connect

params:
	\RainLoop\Model\Account $oAccount
	\MailSo\Sieve\SieveClient $oSieveClient
	\MailSo\Sieve\Settings $oSettings

sieve.after-connect

params:
	\RainLoop\Model\Account $oAccount
	\MailSo\Sieve\SieveClient $oSieveClient
	\MailSo\Sieve\Settings $oSettings

sieve.before-login

params:
	\RainLoop\Model\Account $oAccount
	\MailSo\Sieve\SieveClient $oSieveClient
	\MailSo\Sieve\Settings $oSettings

sieve.after-login

params:
	\RainLoop\Model\Account $oAccount
	\MailSo\Sieve\SieveClient $oSieveClient
	bool $bSuccess
	\MailSo\Sieve\Settings $oSettings

SMTP

smtp.before-connect

params:
	\RainLoop\Model\Account $oAccount
	\MailSo\Smtp\SmtpClient $oSmtpClient
	\MailSo\Smtp\Settings $oSettings

smtp.after-connect

params:
	\RainLoop\Model\Account $oAccount
	\MailSo\Smtp\SmtpClient $oSmtpClient
	\MailSo\Smtp\Settings $oSettings

smtp.before-login

params:
	\RainLoop\Model\Account $oAccount
	\MailSo\Smtp\SmtpClient $oSmtpClient
	\MailSo\Smtp\Settings $oSettings

smtp.after-login

params:
	\RainLoop\Model\Account $oAccount
	\MailSo\Smtp\SmtpClient $oSmtpClient
	bool $bSuccess
	\MailSo\Smtp\Settings $oSettings

Json service actions

Called by RainLoop\ServiceActions::ServiceJson() {actionname} is one of the RainLoop\Actions::Do{ActionName}(), or an extension action as "Plugin{ActionName}" added with Plugin::addJsonHook() and called in JavaScript using rl.pluginRemoteRequest().

json.before-{actionname}

params: none

json.after-{actionname}

params:
	array &$aResponse

json.action-post-call

Obsolete, use json.after-{actionname}

json.action-pre-call

Obsolete, use json.before-{actionname}

filter.json-response

Obsolete, use json.after-{actionname}

Others

filter.account

params:
	\RainLoop\Model\Account $oAccount

filter.action-params

params:
	string $sMethodName
	array &$aCurrentActionParams

filter.app-data

params:
	bool $bAdmin
	array &$aAppData

filter.application-config

params:
	\RainLoop\Config\Application $oConfig

filter.build-message

params:
	\MailSo\Mime\Message $oMessage

Happens before send/save message

filter.build-read-receipt-message

params:
	\MailSo\Mime\Message $oMessage
	\RainLoop\Model\Account $oAccount

filter.domain

params:
	\RainLoop\Model\Domain $oDomain

filter.fabrica

params:
	string $sName
	mixed &$mResult
	\RainLoop\Model\Account $oAccount

filter.http-paths

params:
	array &$aPaths

filter.language

params:
	string &$sLanguage
	bool $bAdmin

Allows you to set a different language

filter.message-html

params:
	\RainLoop\Model\Account $oAccount
	\MailSo\Mime\Message $oMessage
	string &$sTextConverted

Happens before send/save message

filter.message-plain

params:
	\RainLoop\Model\Account $oAccount
	\MailSo\Mime\Message $oMessage
	string &$sTextConverted

Happens before send/save message

filter.message-rcpt

Called by DoSendMessage and DoSendReadReceiptMessage
params:
	\RainLoop\Model\Account $oAccount
	\MailSo\Mime\EmailCollection $oRcpt

filter.read-receipt-message-plain

params:
	\RainLoop\Model\Account $oAccount
	\MailSo\Mime\Message $oMessage
	string &$sText

filter.result-message

params:
	\MailSo\Mime\Message $oMessage

Happens when reading message

filter.save-message

params:
	\MailSo\Mime\Message $oMessage

Happens before save message

filter.send-message

params:
	\MailSo\Mime\Message $oMessage

Happens before send message

filter.send-message-stream

params:
	\RainLoop\Model\Account $oAccount
	resource &$rMessageStream
	int &$iMessageStreamSize

filter.send-read-receipt-message

params:
	\MailSo\Mime\Message $oMessage
	\RainLoop\Model\Account $oAccount

filter.smtp-from

params:
	\RainLoop\Model\Account $oAccount
	\MailSo\Mime\Message $oMessage
	string &$sFrom

filter.smtp-hidden-rcpt

params:
	\RainLoop\Model\Account $oAccount
	\MailSo\Mime\Message $oMessage
	array &$aHiddenRcpt

filter.smtp-message-stream

Called by DoSendMessage and DoSendReadReceiptMessage
params:
	\RainLoop\Model\Account $oAccount
	resource &$rMessageStream
	int &$iMessageStreamSize

filter.upload-response

params:
	array &$aResponse

json.attachments

params:
	\SnappyMail\AttachmentsAction $oData

json.suggestions-input-parameters

params:
	string &$sQuery
	int &$iLimit
	\RainLoop\Model\Account $oAccount

main.content-security-policy

params:
	\SnappyMail\HTTP\CSP $oCSP

Allows you to edit the policy, like:
`$oCSP->script[] = "'strict-dynamic'";`

main.default-response

Obsolete, use json.after-{actionname}

main.default-response-data

Obsolete, use json.after-{actionname}

main.default-response-error-data

Obsolete, use json.after-{actionname}

main.fabrica

params:
	string $sName
	mixed &$mResult

service.app-delay-start-begin

no params

service.app-delay-start-end

no params

JavaScript Events

mailbox

mailbox.inbox-unread-count

mailbox.message-list.selector.go-up

mailbox.message-list.selector.go-down

mailbox.message.show

Use to show a specific message.
	dispatchEvent(
		new CustomEvent(
			'mailbox.message.show',
			{
				detail: {
					folder: 'INBOX',
					uid: 1
				},
				cancelable: false
			}
		)
	);

audio

audio.start

audio.stop

audio.api.stop

Misc

rl-layout

event.detail value is one of:
0. NoPreview
1. SidePreview
2. BottomPreview

rl-view-model.create

event.detail = the ViewModel class
Happens immediately after the ViewModel constructor

rl-view-model

event.detail = the ViewModel class
Happens after the full build (vm.onBuild()) and contains viewModelDom

sm-admin-login

event.detail = FormData
cancelable using preventDefault()

sm-admin-login-response

event.detail = { error: int, data: {JSON response} }

sm-user-login

event.detail = FormData
cancelable using preventDefault()

sm-user-login-response

event.detail = { error: int, data: {JSON response} }

sm-show-screen

event.detail = 'screenname'
cancelable using preventDefault()

squire-toolbar

event.detail = { squire: SquireUI, actions: object }
`actions` is the toolbar structure.
```javascript
block-of-buttons: {
	button-name: {
		select: ['selectbox options'],
		html: 'button text',
		cmd: () => `command to execute`,
		key: 'keyboard shortcut',
		matches: 'HTML elements that match'
	}
}
```
See [SquireUI.js](https://github.com/the-djmaze/snappymail/blob/master/dev/External/SquireUI.js)
for all default toolbar actions.

JavaScript rl object

rl.Enums.StorageResultType

rl.Enums.StorageResultType.Abort

rl.Enums.StorageResultType.Error

rl.Enums.StorageResultType.Success

rl.Utils.htmlToPlain(html)

Converts HTML to text

rl.Utils.plainToHtml(plain)

Converts text to HTML

rl.addSettingsViewModel(SettingsViewModelClass, template, labelName, route)

Examples in

  • ./change-password/js/ChangePasswordUserSettings.js
  • ./example/js/ExampleUserSettings.js
  • ./kolab/js/settings.js
  • ./two-factor-auth/js/TwoFactorAuthSettings.js

rl.addSettingsViewModelForAdmin(SettingsViewModelClass, template, labelName, route)

Examples in

  • ./example/js/ExampleAdminSettings.js:34: rl.addSettingsViewModelForAdmin(ExampleAdminSettings, 'ExampleAdminSettingsTab',

rl.adminArea()

Returns true or false when in '?admin' area

rl.app.Remote.abort(action)

rl.app.Remote.get(action, url)

rl.app.Remote.getPublicKey(fCallback)

rl.app.Remote.post(action, fTrigger, params, timeOut)

rl.app.Remote.request(action, fCallback, params, iTimeout, sGetAdd)

rl.app.Remote.setTrigger(trigger, value)

rl.app.Remote.streamPerLine(fCallback, sGetAdd, postData)

rl.app.folderList

A knockout observable array of all folders/mailboxes

rl.fetch(resource, init, postData)

rl.fetchJSON(resource, init, postData)

rl.i18n(key, valueList, defaulValue)

rl.loadScript(src)

rl.logoutReload(url)

rl.pluginPopupView

class AbstractViewPopup

rl.pluginRemoteRequest(callback, action, parameters, timeout)

rl.pluginSettingsGet(pluginSection, name)

rl.registerWYSIWYG(name, construct)

rl.route.root()

rl.route.reload()

rl.route.off()

rl.setTitle(title)

rl.settings.get(name)

rl.settings.set(name, value)

rl.settings.app(name)