Overview
Features
Download
Documentation
Community
Add-Ons & Services

Drop2Email-combining DirectoryWatcher and SMTPClientSession

Tips and tricks for POCO C++ Libraries users.

Drop2Email-combining DirectoryWatcher and SMTPClientSession

Postby guenter » 25 Nov 2012, 21:37

Here's a little app I wrote today. It uses Poco::DirectoryWatcher to watch a directory for files dropped into it, then sends these files as attachment to an email recipient, using SMTPClientSession. Also uses Poco::Util::ServerApplication and Poco::Util::Timer.
Background: After getting tired of receiving fax spam, I replaced my HP all-in-one used for receiving and sending faxes with an old 2005 Mac mini I had laying around. The Mac mini has an internal modem, and OS X has support for sending and reciving faxes, so I turned it into a Fax server. Only problem is, forwarding received faxes via email does not work, due to some issues with the Mac mini's email configuration (it was once used as a server, so it's running Mac OS X Server 10.4 with mail server, spam filter, etc.). So I simply wrote this little app, which neatly solves the problem. It just watches the faxes folder and emails every file that ends up there.


Code: Select all
//
// Drop2Email.cpp
//
// $Id: //projects/Drop2Email/src/Drop2Email.cpp#1 $
//
// A server application that sends files dropped into a directory
// to an email recipient.
//
// Copyright (c) 2012, Applied Informatics Software Engineering GmbH.
// and Contributors.
//
// Permission is hereby granted, free of charge, to any person or organization
// obtaining a copy of the software and accompanying documentation covered by
// this license (the "Software") to use, reproduce, display, distribute,
// execute, and transmit the Software, and to prepare derivative works of the
// Software, and to permit third-parties to whom the Software is furnished to
// do so, all subject to the following:
//
// The copyright notices in the Software and this entire statement, including
// the above license grant, this restriction and the following disclaimer,
// must be included in all copies of the Software, in whole or in part, and
// all derivative works of the Software, unless such copies or derivative
// works are solely in the form of machine-executable object code generated by
// a source language processor.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
// SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
// FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
//


#include "Poco/Net/MailMessage.h"
#include "Poco/Net/MailRecipient.h"
#include "Poco/Net/SMTPClientSession.h"
#include "Poco/Net/StringPartSource.h"
#include "Poco/Net/FilePartSource.h"
#include "Poco/Util/ServerApplication.h"
#include "Poco/Util/Option.h"
#include "Poco/Util/OptionSet.h"
#include "Poco/Util/HelpFormatter.h"
#include "Poco/Util/Timer.h"
#include "Poco/Util/TimerTask.h"
#include "Poco/Logger.h"
#include "Poco/Path.h"
#include "Poco/Delegate.h"
#include "Poco/DirectoryWatcher.h"
#include "Poco/Exception.h"
#include <iostream>


using Poco::Util::Application;
using Poco::Util::ServerApplication;
using Poco::Util::Option;
using Poco::Util::OptionSet;
using Poco::Util::OptionCallback;
using Poco::Util::HelpFormatter;


class EmailTask: public Poco::Util::TimerTask
{
public:
    EmailTask(const Poco::Util::AbstractConfiguration& config, const std::string& path):
        _config(config),
        _path(path),
        _logger(Poco::Logger::get("EmailTask"))
    {
    }
   
    void run()
    {
        try
        {
            Poco::Net::MailMessage message;
            message.setSender(_config.getString("email.sender"));
            message.addRecipient(Poco::Net::MailRecipient(Poco::Net::MailRecipient::PRIMARY_RECIPIENT, _config.getString("email.recipient")));
            message.setSubject(_config.getString("email.subject", _path));
            std::string content(_config.getString("email.content", ""));
            message.addContent(new Poco::Net::StringPartSource(content));
           
            Poco::Path p(_path);
            std::string mimeType(_config.getString("mimeType." + p.getExtension(), "application/binary"));
            message.addAttachment(p.getFileName(), new Poco::Net::FilePartSource(_path, mimeType));
           
            Poco::Net::SMTPClientSession session(_config.getString("smtp.host", "mailhost"));
            session.login();
            session.sendMessage(message);
            session.close();
        }
        catch (Poco::Exception& exc)
        {
            _logger.error("Failed to send email: " + exc.displayText());
        }
    }
   
private:
    const Poco::Util::AbstractConfiguration& _config;
    std::string _path;
    Poco::Logger& _logger;
};


class Drop2EmailApp: public ServerApplication
{
public:
    Drop2EmailApp():
        _helpRequested(false),
        _pTimer(0)
    {
    }
   
    ~Drop2EmailApp()
    {
        delete _pTimer;
    }

protected:
    void initialize(Application& self)
    {
        loadConfiguration(); // load default configuration files, if present
        ServerApplication::initialize(self);
        _pTimer = new Poco::Util::Timer;
    }
       
    void uninitialize()
    {
        ServerApplication::uninitialize();
    }

    void defineOptions(OptionSet& options)
    {
        ServerApplication::defineOptions(options);
       
        options.addOption(
            Option("help", "h", "Display help information on command line arguments.")
                .required(false)
                .repeatable(false)
                .callback(OptionCallback<Drop2EmailApp>(this, &Drop2EmailApp::handleHelp)));

        options.addOption(
            Option("config-file", "c", "Load configuration data from a file.")
                .required(false)
                .repeatable(true)
                .argument("file")
                .callback(OptionCallback<Drop2EmailApp>(this, &Drop2EmailApp::handleConfig)));
    }

    void handleConfig(const std::string& name, const std::string& value)
    {
        loadConfiguration(value);
    }

    void handleHelp(const std::string& name, const std::string& value)
    {
        _helpRequested = true;
        displayHelp();
        stopOptionsProcessing();
    }

    void displayHelp()
    {
        HelpFormatter helpFormatter(options());
        helpFormatter.setCommand(commandName());
        helpFormatter.setUsage("OPTIONS");
        helpFormatter.setHeader("A server application that watches a directory and sends all files dropped into that directory to an email recipient.");
        helpFormatter.format(std::cout);
    }
   
    void onFileDropped(const Poco::DirectoryWatcher::DirectoryEvent& ev)
    {
        if (!ev.item.isHidden())
        {
            Poco::Timestamp ts;
            ts += 1000000*config().getInt("delay", 10);
            _pTimer->schedule(new EmailTask(config(), ev.item.path()), ts);
        }
    }

    int main(const std::vector<std::string>& args)
    {
        if (!_helpRequested)
        {
            try
            {
                Poco::DirectoryWatcher dirWatcher(config().getString("path"), Poco::DirectoryWatcher::DW_ITEM_ADDED);
                dirWatcher.itemAdded += Poco::delegate(this, &Drop2EmailApp::onFileDropped);
               
                waitForTerminationRequest();

                dirWatcher.itemAdded -= Poco::delegate(this, &Drop2EmailApp::onFileDropped);
            }
            catch (Poco::Exception& exc)
            {
                logger().log(exc);
            }
        }
        return Application::EXIT_OK;
    }
   
private:
    bool _helpRequested;
    Poco::Util::Timer* _pTimer;
};


POCO_SERVER_MAIN(Drop2EmailApp)


Makefile:
Code: Select all
#
# Makefile
#
# $Id: //projects/Drop2Email/Makefile#1 $
#
# Makefile for Drop2Email
#

include $(POCO_BASE)/build/rules/global

objects = Drop2Email

target         = Drop2Email
target_version = 1
target_libs    = PocoUtil PocoXML PocoNet PocoFoundation

include $(POCO_BASE)/build/rules/exec


Configuration File:
Code: Select all
#
# The directory to watch
#
path = /home/user/Drop2Email

#
# The delay in seconds after which the file is mailed
#
delay = 10

#
# Email Addressing
#
email.recipient = user@company.com
email.sender = user@company.com
email.subject = Here's a file for you
email.content = Please see attached file.

#
# Mail Server
#
smtp.host = mailhost.company.com

#
# Mapping Extensions to MIME Types
#
mimeType.txt = text/plain
mimeType.pdf = application/pdf
mimeType.jpg = image/jpeg
mimeType.png = image/png
mimeType.mp3 = audio/mpeg
mimeType.m4a = audio/m4a
mimeType.mp4 = video/mp4

#
# Logging
#
logging.loggers.root.level = information
logging.loggers.root.channel = console

logging.channels.console.class = ConsoleChannel
logging.channels.console.pattern = %q %s: %t


Visual Studio project files are left as an exercise for the reader.
guenter
 
Posts: 1105
Joined: 11 Jul 2006, 16:27
Location: Austria

Return to Tips & Tricks

Who is online

Users browsing this forum: No registered users and 1 guest