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.





