Posted on Leave a comment

Setting actual sender for e-mails in shared Sent Items folder in Roundcube Webmail

Recently, I came across the requirement to set up shared IMAP folders in Roundcube Webmail for a Dovecot IMAP server setting.

Everything went smoothly using the respective Dovecot setup in conjunction with the Roundcube ACL plugin for IMAP Folders Access Control Lists Management (RFC4314, RFC2086).

Everything? Well not quite everything.

The problem was that for e-mails in shared Sent Items folders not the actual sender was shown but the owner of the shared folder. So, for instance if user-1@mail.com had subscribed to the Sent Items of user-2@mail.com then user-2@mail.com was shown as sender instead of the actual sender (e.g. someone-else@domain.com).

Which makes it quite cumbersome to retrieve the actual sender, e.g. by having a look at the e-mail headers…

Let’s write a Roundcube Plugin

Since there is no evident setting in Roundcube for achieving this (expected) behavior I had quick look at the possibilities from a programmatic point of view.

Roundcube has a very nice plugin architecture for accessing hooks to e.g. manipulate e-mails when the e-mail list of being loaded before it is actually sent out.

So, the idea was to simply alter all e-mail senders for shared Sent Items folders. In my setup, shared folders are conveniently configured with the prefix “shared/” via the Dovecot configuration, leading the shared IMAP folders such as “shared/user-1@mail.com/Sent Items“.

Without further ado, find the Roundcube plugin code below that matches those requirements quite nicely with only a couple of lines of code.

<?php

/**
 * Displays actual sender for e-mails in shared Sent folder.
 *
 * @author Matthias Kerstner
 */
class show_actual_sender_for_shared_sent_folder extends rcube_plugin
{
  public $task = 'mail';
	
  function init()  {
    $this->add_hook('messages_list', array($this, 'do_show_actual_sender_for_shared_sent_folder'));
  }

  function do_show_actual_sender_for_shared_sent_folder($args) {

    // $this->load_config(); -> Hint: we could load the regexp here to make it configurable
		
    foreach($args['messages'] as $msg) {		
      if(preg_match('#^shared\/[^\/]+\/Sent( Items)?$#', $msg->folder)) {
        $msg->from = $msg->to;
      }
    }
		
    return $args;
  }
}

Potential improvements

As you can see in the code comment the regexp for matching the shared Sent Items (or any other folder structure you prefer) could be easily loaded via the plugin configuration. But that’s something up to you 🙂

If there’s time I’ll publish this wonderful plugin on the Roundcube Plugin repository. In the meantime I’ll link the plugin download file here.

Posted on Leave a comment

WordPress Simple Latest Posts Shortcode Plugin

Wordpress Logo

Although there exist numerous WordPress plugins for integrating the latest posts into pages this new Simple Latest Posts Shortcode Plugin was created to specifically serve those people who just want to have a single line of shortcode in order to display the latest posts in their blog. Nothing more, nothing less.

And yes, there also will be a settings page for the plugin in the administration area of you WordPress just in case you want to customize the way the plugin behaves. This plugin is inspired by the Simple Random Posts Shortcode WordPress Plugin that I released earlier. Based on the great feedback via mail I’ve decided to add this Simple Latest Posts Shortcode Plugin too.

Demonstration

Below you see the Simple Latest Posts Shortcode Plugin in action. It is called with the shortcode

simple_latest_posts

without parameters: [simple_latest_posts]

Download

Feel free to download the Simple Latest Posts Shortcode Plugin via the WordPress plugin repository.

Posted on Leave a comment

jQuery Lights out Plugin

jQuery Logo

In current times, saving energy in all its variants is becoming more and more important each day. When it comes to websites one simple way to save energy is to dim the display when idle.

Both Interact has released a simple yet versatile cross-browser jQuery lights out plugin. In order to keep things simple you only need to make sure to include jQuery 1.3 and higher and the plugin itself. It will then do the magic of automatically adding the required DOM elements for the overlays and register some event handlers to track user interactions and idle times.

Feel free to download the jQuery lights out plugin from Github and adjust it to your needs.

Posted on 6 Comments

Simple Random Posts Shortcode WordPress Plugin

Wordpress Logo

A simple but effective WordPress plugin to render random blog posts in pages and/or posts. This is the first public release and features the following shortcode options to customize the rendering process:

  1. header: whether to output header text (default=)
  2. show_featured_image: whether to output featured post image (default=TRUE)
  3. featured_image_height: height of featured image in px (default=100)
  4. preview_text_chars: amount of chars to be displayed for preview text (default=200)
  5. container_css_class: CSS class of outer container (default=articles-preview-container)
  6. container_article_css_class: CSS class of article containe (default=article-preview)
  7. append_css_clear: whether to append a CSS clear container (default=TRUE)
  8. container_left_width: px or % (default=65%)
  9. container_right_width: px or % (default=30%)

Usage

In order to use the plugin just insert the shortcode

[simple_random_post]

and use the options provided above to customize the plugin. Feel free to download it from the official WordPress plugin repository.

Demo

Below you find a demo using the following shortcode:

simple_random_posts header="<b>Custom header</b>

[simple_random_posts header=”Custom header“]

Posted on 58 Comments

ClamAV Plugin for Kerio Connect 8+

Kerio Connect Logo

Unfortunately, starting with version 8 of Kerio Connect built-in support for ClamAV has been dropped. Up until this version it was possible to easily setup ClamAV in conjunction with Kerio Connect, as described in Setting up ClamAV for Kerio Connect. Fortunately, Kerio provides a SDK for developing AV plugins, including code for ClamAV. This post describes the steps required to compile and setup the ClamAV plugin using Windows and Cygwin for Kerio Connect 8.

In case you just want to download the compiled version scroll down to the Download section. 32bit and 64bit versions are available.

Download SDK

First of all download the SDK from GitHub and extract it to your Cygwin /home/user directory, e.g. /home/mkerstner/antivirus-sdk-master.

Compile Plugin

Next we need to compile the ClamAV plugin. Open a Cygwin terminal and open the folder where you’ve just extracted the SDK, e.g. /home/mkerstner/antivirus-sdk-master. For the compilation process to work you need to following tools:

  1. gcc (gcc-core and gcc-g++)
  2. cmake
  3. make
  4. boost (libboost-devel and runtime)

Please refer to the README included in the SDK for further information regarding the setup process of these tools. Once you’ve installed these tools (via your Cygwin setup.exe) you are ready to compile the plugin. Note this guide has been tested to work with gcc 3.4.4, cmake 2.8.9-2, make 3.82.90-1 and libboost 1.48.0-1 on Cygwin setup.exe version 2.774 on a Windows 7 64bit machine.

1. run CMake

From withing the clam folder of your SDK directory run cmake as follows:

mkerstner@Homer ~/antivirus-sdk-master/clam
$ cmake .
-- The C compiler identification is GNU 4.5.3
-- The CXX compiler identification is GNU 4.5.3
CMake Warning at /usr/share/cmake-2.8.9/Modules/Platform/CYGWIN.cmake:15 (message):
  CMake no longer defines WIN32 on Cygwin!

  (1) If you are just trying to build this project, ignore this warning or
  quiet it by setting CMAKE_LEGACY_CYGWIN_WIN32=0 in your environment or in
  the CMake cache.  If later configuration or build errors occur then this
  project may have been written under the assumption that Cygwin is WIN32.
  In that case, set CMAKE_LEGACY_CYGWIN_WIN32=1 instead.

  (2) If you are developing this project, add the line

    set(CMAKE_LEGACY_CYGWIN_WIN32 0) # Remove when CMake &amp;amp;gt;= 2.8.4 is required

  at the top of your top-level CMakeLists.txt file or set the minimum
  required version of CMake to 2.8.4 or higher.  Then teach your project to
  build on Cygwin without WIN32.
Call Stack (most recent call first):
  /usr/share/cmake-2.8.9/Modules/CMakeSystemSpecificInformation.cmake:36 (INCLUD                         E)
  CMakeLists.txt:1 (PROJECT)

-- Check for working C compiler: /usr/bin/gcc.exe
-- Check for working C compiler: /usr/bin/gcc.exe -- works
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Check for working CXX compiler: /usr/bin/c++.exe
-- Check for working CXX compiler: /usr/bin/c++.exe -- works
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
cygwin warning:
  MS-DOS style path detected: C:/boost/lib
  Preferred POSIX equivalent is: /cygdrive/c/boost/lib
  CYGWIN environment variable option &amp;amp;quot;nodosfilewarning&amp;amp;quot; turns off this warning.
  Consult the user's guide for more details about POSIX paths:
    http://cygwin.com/cygwin-ug-net/using.html#using-pathnames
-- Boost version: 1.48.0
-- Found the following Boost libraries:
--   thread
--   filesystem
--   system
--   date_time
--   regex
--   chrono
-- Configuring done
-- Generating done
-- Build files have been written to: /home/Anmat/antivirus-sdk-master/clam

2. Run make

Once all dependencies have been satisfied it’s time to run make to start the compilation process:

mkerstner@Homer ~/antivirus-sdk-master/clam
$ make all
Scanning dependencies of target avir_clam
[ 33%] Building CXX object CMakeFiles/avir_clam.dir/avPlugin.cpp.o
[ 66%] Building CXX object CMakeFiles/avir_clam.dir/ClamPlugin.cpp.o
[100%] Building C object CMakeFiles/avir_clam.dir/home/Anmat/antivirus-sdk-master/api/avCommon.c.o
Linking CXX shared library avir_clam.dll
Creating library file: libavir_clam.dll.a
[100%] Built target avir_clam

Done! The plugin was successfully compiled.

Copy Plugin to Plugin Container

In order for Kerio Connect to recognize the plugin we need to copy the the compiled plugin to Kerio’s AV connect plugin dir. Thus, copy avir_clam.dll and libavir_clam.dll.a to your Kerio installation folder, e.g. C:Program Files (x86)KerioMailServerpluginsAvirs Be sure to restart Kerio Connect server afterwards.

Setting up the Plugin

In versions prior to 8 external antivirus plugins could be easily selected via the “Use external antivirus” option in the administration web console. It seems like this option has been made invisible by default. Luckily, you can still edit this option in mailserver.cfg directly:

&amp;amp;lt;table name=&amp;amp;quot;Antivir&amp;amp;quot;&amp;amp;gt; 
 &amp;amp;lt;variable name=&amp;amp;quot;AvirEnabled&amp;amp;quot;&amp;amp;gt;0&amp;amp;lt;/variable&amp;amp;gt; 
 &amp;amp;lt;variable name=&amp;amp;quot;UsedInternal&amp;amp;quot;&amp;amp;gt;0&amp;amp;lt;/variable&amp;amp;gt; 
 &amp;amp;lt;variable name=&amp;amp;quot;UseMcAfee&amp;amp;quot;&amp;amp;gt;0&amp;amp;lt;/variable&amp;amp;gt; 
 &amp;amp;lt;variable name=&amp;amp;quot;UsedPlugin&amp;amp;quot;&amp;amp;gt;avir_clam&amp;amp;lt;/variable&amp;amp;gt; 
 &amp;amp;lt;variable name=&amp;amp;quot;EnabledExtAV&amp;amp;quot;&amp;amp;gt;avir_clam&amp;amp;lt;/variable&amp;amp;gt; 
 &amp;amp;lt;variable name=&amp;amp;quot;JpegEnabled&amp;amp;quot;&amp;amp;gt;1&amp;amp;lt;/variable&amp;amp;gt; 
 &amp;amp;lt;variable name=&amp;amp;quot;AdminNotify&amp;amp;quot;&amp;amp;gt;&amp;amp;lt;/variable&amp;amp;gt; 
 &amp;amp;lt;variable name=&amp;amp;quot;AdminNotifyFiltered&amp;amp;quot;&amp;amp;gt;&amp;amp;lt;/variable&amp;amp;gt; 
 &amp;amp;lt;variable name=&amp;amp;quot;Bounce&amp;amp;quot;&amp;amp;gt;1&amp;amp;lt;/variable&amp;amp;gt; 
 &amp;amp;lt;variable name=&amp;amp;quot;RemoveAttachments&amp;amp;quot;&amp;amp;gt;1&amp;amp;lt;/variable&amp;amp;gt; 
 &amp;amp;lt;variable name=&amp;amp;quot;InsertSubjectPrefix&amp;amp;quot;&amp;amp;gt;0&amp;amp;lt;/variable&amp;amp;gt; 
 &amp;amp;lt;variable name=&amp;amp;quot;SubjectPrefix&amp;amp;quot;&amp;amp;gt;**VIRUS**&amp;amp;lt;/variable&amp;amp;gt; 
 &amp;amp;lt;variable name=&amp;amp;quot;CheckImpossibleMode&amp;amp;quot;&amp;amp;gt;1&amp;amp;lt;/variable&amp;amp;gt; 
 &amp;amp;lt;variable name=&amp;amp;quot;DelayIfFailed&amp;amp;quot;&amp;amp;gt;1&amp;amp;lt;/variable&amp;amp;gt; 
 &amp;amp;lt;variable name=&amp;amp;quot;UpdateInterval&amp;amp;quot;&amp;amp;gt;6&amp;amp;lt;/variable&amp;amp;gt; 
 &amp;amp;lt;variable name=&amp;amp;quot;UpdateDatabaseTime&amp;amp;quot;&amp;amp;gt;0&amp;amp;lt;/variable&amp;amp;gt; 
 &amp;amp;lt;variable name=&amp;amp;quot;UpdateLastCheck&amp;amp;quot;&amp;amp;gt;0&amp;amp;lt;/variable&amp;amp;gt; 
 &amp;amp;lt;variable name=&amp;amp;quot;ShortTimeout&amp;amp;quot;&amp;amp;gt;60&amp;amp;lt;/variable&amp;amp;gt; 
 &amp;amp;lt;variable name=&amp;amp;quot;LongTimeout&amp;amp;quot;&amp;amp;gt;120&amp;amp;lt;/variable&amp;amp;gt; 
 &amp;amp;lt;variable name=&amp;amp;quot;UpdateTimeout&amp;amp;quot;&amp;amp;gt;3600&amp;amp;lt;/variable&amp;amp;gt; 
 &amp;amp;lt;variable name=&amp;amp;quot;RestartWait&amp;amp;quot;&amp;amp;gt;300&amp;amp;lt;/variable&amp;amp;gt; 
 &amp;amp;lt;variable name=&amp;amp;quot;MaxScanningThreads&amp;amp;quot;&amp;amp;gt;8&amp;amp;lt;/variable&amp;amp;gt; 
&amp;amp;lt;/table&amp;amp;gt;

The options you need to edit are:

&amp;amp;lt;variable name=&amp;amp;quot;UsedPlugin&amp;amp;quot;&amp;amp;gt;avir_clam&amp;amp;lt;/variable&amp;amp;gt; 
&amp;amp;lt;variable name=&amp;amp;quot;EnabledExtAV&amp;amp;quot;&amp;amp;gt;avir_clam&amp;amp;lt;/variable&amp;amp;gt;

Be sure to restart the server again. Finally, open the administration web interface and select your shiny new ClamAV plugin from the external antivirus plugins selection box: kerio_connect_8_clamav_plugin You will get a warning message upon startup saying that support for external plugins will be discountinued: kerio_connect_8_clamav_plugin_warning You can ignore this warning. The plugin should work as expected. That’s it! Enjoy 🙂

Download ClamAV Plugin

Posted on Leave a comment

WordPress Plugin for checking Compatibility prior to Upgrade

Wordpress Logo

Like with every good piece of software updates and upgrades are mandatory to keep up-to-date with the latest features and security fixes. Since WordPress offers a vast range of plugins to extend its core functionality these plugins should be kept up-to-date too. Now, to be able to use these plugins after a WordPress upgrade it is important to carry out prior compatibility checks not to break any existing functionality.

Especially if you have numerous plugins installed this check can be time consuming. Thus, it would be nice to have another plugins doing exactly these checks in advance – that’s where Better Plugin Compatibility Control (BPCC) comes into play. BPCC nicely formats the list of currently installed plugins and provides information on the compatibility.

Another useful plugin when it comes to updates and upgrades is Changelogger. This handy plugin saves you some time by displaying changelogs messages for each plugin in the list view.

Using these two plugins managing WordPress upgrades and plugin updates has become much more comfortable and easier.

Posted on Leave a comment

Zend_Mail Plugin abstraction class

Zend Framework Logo

From time to time it is handy to be able quickly set special options when sending mails using Zend_Mail, such as automatically adding footer text or toggling between HTML and plaintext mails. In order to satisfy these needs I wrote the following simple abstraction class as a wrapper around Zend_Mail.

/**
 * An abstraction class for Zend_Mail.
 * @author matthias.kerstner
 */
class Custom_Plugin_Mailer {

    /**
     * Assembles $mail but does not send it.
     * @param array $to
     * @param string $subject
     * @param string $body
     * @param array? $from
     * @param bool? $html
     * @param bool? $appendSignature
     * @param bool? $archive
     * @param string? $encoding
     * @param Zend_Locale? $locale
     * @return bool|Zend_Mail 
     */
    public static function assemble($to, $subject, $body, $from = null, $html = false, $appendSignature = true, $archive = true, $encoding = 'UTF-8', $locale = null) {
        return self::send($to, $subject, $body, $from, $html, $appendSignature, $archive, $encoding, false, $locale);
    }

    /**
     * Sends mail.
     * @param array $to [0=email, 1=name]
     * @param string $subject
     * @param string $body
     * @param array? $from [0=email, 1=name]
     * @param bool? $html
     * @param bool? $appendSignature
     * @param bool? $archive
     * @param string? $encoding
     * @param bool? $sendDirectly
     * @param Zend_Locale? $locale
     * @param array? $toBCC [0=email, 1=name]
     * @return bool|Zend_Mail true on success, false on error, Zend_Mail 
     *         if $sendDirectly is false
     */
    public static function send($to, $subject, $body, $from = null, $html = false, $appendSignature = true, $archive = true, $encoding = 'UTF-8', $sendDirectly = true, $locale = null, $toBCC = null) {
        $config = Zend_Registry::get('Zend_Config');
        $translate = Zend_Registry::get('Zend_Translate');
        $_locale = $locale instanceof Zend_Locale ? $locale : Zend_Registry::get('Zend_Locale');
        $mail = new Zend_Mail($encoding);

        if (APPLICATION_ENV !== 'production') { // DEBUG MODE
            $mail->setFrom($config->email->noreply->email, $config->email->noreply->sender);
            $mail->addTo($config->email->debug->email, $config->email->debug->sender);
        } else { // PRODUCTION MODE
            if ($from === null) {
                $mail->setFrom($config->email->noreply->email, $config->email->noreply->sender);
            } else {
                if (is_array($from) && count($from) == 2) {
                    $mail->setFrom($from[0], $from[1]);
                } else {
                    throw new Exception('Failed to send email');
                }
            }

            if (is_array($to) && count($to) == 2) {
                $mail->addTo($to[0], $to[1]);
            } else {
                throw new Exception('Failed to send email');
            }
        }

        $mail->setSubject($subject);

        if ($appendSignature) {
            $br = $html ? "<br>" : "\n";
            $body .= $br . $br
                    . $translate->_('DEFAULT.MAIL.SIGNATURE', $_locale);
        }

        if ($html) {
            $mail->setBodyHtml($body);
        } else {
            $mail->setBodyText($body);
        }

        if ($archive) {
            $mail->addBcc($config->email->archive->email, $config->email->archive->sender);
        }

        // BCC
        if (is_array($toBCC) && count($toBCC) == 2) {
            $mailBCC = new Zend_Mail($encoding);
            $mailBCC->setFrom($config->email->noreply->email, $config->email->noreply->sender);
            $mailBCC->addTo($toBCC[0], $toBCC[1]);
            $mailBCC->setSubject("BCC: " . $subject);
            if ($html) {
                $mailBCC->setBodyHtml("BCC:<br />" . $body);
            } else {
                $mailBCC->setBodyText("BCC:\n" . $body);
            }
            $mailBCC->send();
        }

        if (!$sendDirectly) {
            return $mail;
        }

        try {
            if (!$mail->send()) {
                throw new Exception('Failed to send mail to "'
                        . $to[0] . '" with subject "' . $subject . '"');
            }
        } catch (Exception $e) {
            return false;
        }

        return true;
    }

}