Skip to content

Developer Documentation

MI-KY edited this page Dec 22, 2022 · 35 revisions

General overview about SnappyMail

⚠ Please note that this documentation is not complete yet - any help is appreciated. ⚠

History of SnappyMail

SnappyMail is a fork of the RainLoop Webmail Project. RainLoop was initially written by Timur Usenko who worked at the company AfterLogic and who wrote the MailSo library. In RainLoop and SnappyMail MailSo is used to handle the main part of the IMAP communication. The SnappyMail fork was initiated by the-djmaze because RainLoop does not seemed to be maintained regularly and had multiple security issues. SnappyMail therefore contains the code of Rainloop, but has been modified a lot to bring it to a next level.

Logging and debugging

Please have a look to https://github.com/the-djmaze/snappymail/wiki/FAQ#how-do-i-enable-logging how you can enable logging inside of SnappyMail.
Developers should additionally know that the logs normally do not contain passwords and other sensitive data because MailSo\Log\Logger::Write replaces those words by *******.
If you really have to log passwords you can set the parameter bool $bSearchSecretWords of the function Logger::Write to false or modify the configuration of SnappyMail by changing the switch hide_passwords inside of application.ini.

SnappyMail front-end

The current front-end of SnappyMail is written in KnockoutJS and communicates with the back-end by JSON-format data. SnappyMail tries to render the most on the clients side to reduce server load on big installations. More details on why at the moment KnockoutJS is used can be read here.

The SnappyMail Extensions System (Plugins)

General information

Plugins extend the functionality of SnappyMail. Administrators of a SnappyMail installation can activate extensions by entering in the Admin Panel of SnappyMail -> menu "Extensions" and checking the checkbox "Enable extensions".
On the same menu you can find a list of plugins available for installation. This list points to the official plugin repository of SnappyMail. The source code of these plugins can be found here.
The source code of installed extensions is placed on your webserver inside the SnappyMail folder under _data_/_default_/plugins/. Therefore you can also install "non official" extensions or plugins in development by coping their code into a subfolder of this folder - keep in mind to set the correct access rights on this new files.

How SnappyMail initializes the plugins

The following shall describe how SnappyMail initializes the activated extensions. All files can be found in the SnappyMail repository under snappymail/v/0.0.0/ and the following paths are relative to that folder.

  1. include.php calls Rainloop\Service::Handle() inside of app/libraries/RainLoop/Service.php
  2. Service.php calls Api::Actions() inside of app/libraries/RainLoop/Api.php.
    • Additional info: Service.php afterwards goes ahead initializing for example the caching functions etc. and ends with a launch of the function BootEnd() in Actions.php
  3. Api.phpcreates the object $oActions by the class app/libraries/RainLoop/Actions.php.
  4. The constructor of Actions.php creates an object $oPlugins by the class app/libraries/RainLoop/Plugins/Manager.php.
  5. The constructor of Manager.php searches for active plugins. It then calls the Init() function of every active plugin.

Because the Init() function of every plugin is called by Manager.php inside this function each plugin can register itself to be launched (=callback function) at various points in the code of SnappyMail (=hooks). Hooks and therefore your registered functions of the plugin are called on multiple points inside the source of SnappyMail by launching the function RunHook of app/libraries/RainLoop/Plugins/Manager.php.

Getting started with your plugin

Plugins always extend the AbstractPlugin class. To get an idea what is possible see the example plugin and the other plugins in the plugin repository.
You plugin class inside the index.php of your plugin first should declare some information about it. This info will be shown inside the Admin Panel -> Extensions Menu and lets the user know what the plugin is intended for or if a new version of your plugin is available.

	const
		NAME     = 'Avatars',
		AUTHOR   = 'SnappyMail',
		URL      = 'https://snappymail.eu/',
		VERSION  = '1.5',
		RELEASE  = '2022-12-15',
		REQUIRED = '2.23.0',
		CATEGORY = 'Contacts',
		LICENSE  = 'MIT',
		DESCRIPTION = 'Show graphic of sender in message and messages list (supports BIMI, Gravatar and identicon, Contacts is still TODO)';

In many cases your plugin will also need some configuration. For example the administrator could insert credentials for a database connection that your plugin needs to work properly. All these text fields, dropdown etc. are defined inside your plugin class inside protected function configMapping(): array. See for example this plugin to get an idea what you need to define inside configMapping(). SnappyMail will take this definitions and create a configuration dialog that is reachable inside the Extensions menu of the Admin Panel (little cogwheel beside the name of your plugin).
The list of available PluginPropertyType (text fields, dropdown boxes...) can be found here.

Hooks

Register your plugin to a specific hook

The Init() function of a plugin can register one or more functions to be called when SnappyMail hits a specific hook inside the code. This can happen for example when a user inserts his credentials, an IMAP connection was successful or a message is going to be saved.

To register the function myCallbackFunction inside of your index.php of your extension to the hook login.success you have to add the following to your Init() function:

$this->addHook('login.success', 'myCallbackFunction');

If in SnappyMail the hook login.success is executed, SnappyMail will pass an array with parameters to the function RunHook defined in snappymail/v/0.0.0/app/libraries/RainLoop/Plugins/Manager.php. The length and content of the array with parameters depends on the specific hook. The function RunHook takes the array and passes the content as single parameters to your function myCallbackFunction - it will not hand over the array.

Therefore the example function myCallbackFunction should be defined to handle one parameter Account $oAccount because login.success passes you over the object of the account that was logged on.

Finding the best hook for your plugin

A possible way to find an ideal hook for your extension is to do a fulltext search for the string RunHook( over the SnappyMail source code. This returns every active hook and his circumstances like parameters that are passed to your callback function.
Also, this is the only way to make sure that your plugin is called at the moment you need.

Available hooks

For a complete list of available hooks inside of SnappyMail see https://github.com/the-djmaze/snappymail/blob/master/plugins/README.md#hooks. Here we will describe some hooks to let you get an idea what in detail is described inside the README.md.

Example: login.credentials.step-1

Hook will run before the checks if the given username (=mail address) of a user is valid and contains a domain-part. This hook can modify the mail address before other checks are done if this is necessary.

Parameters passed to the plugins:

	string &$sEmail

Example: login.credentials.step-2

Hook will run after the checks described in login.credentials.step-1 when also the password of the users who tries to log in is available.

Parameters passed to the plugins:

	string &$sEmail
	string &$sPassword

JavaScript inside your plugin

Your plugin can use the function addJs (definition) to inject JavaScript to SnappyMail. Inside this JavaScript code you could for example react on JavaScript Events.

Modify the UI of SnappyMail at runtime

SnappyMail uses templates to define how for example the SystemDropDown menu (see image below) has to look like.
grafik
If your plugin needs to modify something inside this templates (for example add an additional button to the UI or modify the list of available functions inside the SystemDropDown) you can add JavaScript code to your plugin that modifies this template in the right moment. Detailed information on this topic can be found here: https://github.com/the-djmaze/snappymail/issues/733#issuecomment-1333725216 .