Skip to content

Commit

Permalink
init
Browse files Browse the repository at this point in the history
  • Loading branch information
choonkeat committed Feb 2, 2014
0 parents commit b16c3d3
Show file tree
Hide file tree
Showing 7 changed files with 206 additions and 0 deletions.
18 changes: 18 additions & 0 deletions README.md
@@ -0,0 +1,18 @@
# diymailin

## example

```
CONFIG=config.json PASSWORD=topsecret SMTP_PORT=1025 PORT=3000 npm start
```

* use `config.json` to store and retrieve email-to-url configurations
* use `http://localhost:3000/` as web interface to add config into `config.json`
* only update config if password matches `topsecret`
* run SMTP server at port `1025`
* when email comes into SMTP server
* if any of the recipient is configured in `config.json`
* HTTP POST `message` param to configured `url`
* if HTTP POST get error, tell remote SMTP client email was rejected
* otherwise, tell remote SMTP client email was accepted
* otherwise, tell remote SMTP client email was rejected
50 changes: 50 additions & 0 deletions http.js
@@ -0,0 +1,50 @@
var url = require('url');
var path = require('path');
var http = require('http');
var querystring = require('querystring');
var node_static = require('node-static');

function reply404(res) {
res = res || this;
res.writeHead(404, {'content-type': 'text/plain'});
res.end();
}

module.exports = {
start: function(httpPort, callback) {
httpPort = httpPort || 3000;
var static_directory = new node_static.Server(path.join(__dirname, 'public'));
var server = http.createServer();
server.addListener('request', function(req, res) {
var uri = url.parse(req.url, !!'true:query as object');
if (req.method == 'POST') {
var content_type = (req.headers && req.headers['content-type'] || "");
var content_length = +(req.headers && req.headers['content-length'] || 0);
if (content_type.match(/www-form-urlencoded/i)) {
var chunks = [];
req.on('data', chunks.push.bind(chunks));
req.on('end', callback ? function() {
uri = querystring.parse(chunks.join("").toString());
callback(uri.password, uri.email, uri.url, function(err) {
res.writeHead(200, {'content-type': 'text/plain'});
res.write(err || "Ok");
res.end();
});
} : reply404.bind(res));
} else {
var chunks = [];
req.on('data', chunks.push.bind(chunks));
req.on('end', reply404.bind(res));
}
} else if (process.env.HIDE_FORM) {
reply404(res);
} else {
static_directory.serve(req, res);
}
});

server.listen(httpPort, '0.0.0.0', function(err) {
console.log( 'HTTP server on 0.0.0.0:' + httpPort, err || "");
});
}
}
28 changes: 28 additions & 0 deletions package.json
@@ -0,0 +1,28 @@
{
"name": "diymailin.js",
"version": "0.1.0",
"description": "standalone incoming-email-to-http-post server",
"repository": "none",
"readme": "See README.md",
"readmeFilename": "README.md",
"main": "server.js",
"dependencies": {
"request": "~2.33.0",
"node-static": "~0.7.3",
"simplesmtp": "~0.3.20",
"stream-buffers": "~0.2.5"
},
"devDependencies": {
"jasmine-node": "~1.11.0"
},
"scripts": {
"test": "which guard && guard || jasmine-node spec/",
"start": "node server.js"
},
"author": "Chew Choon Keat <choonkeat@gmail.com>",
"license": "None",
"engines": {
"node": "0.10.x",
"npm": "1.3.x"
}
}
30 changes: 30 additions & 0 deletions public/index.html
@@ -0,0 +1,30 @@
<!DOCTYPE html>
<html>
<head>
<style>
body { font-family: helvetica; color: #888; }
</style>
</head>
<body>
<h1>
diymailin
</h1>
<form method="post">
<p>
<label>Email</label><br/>
<input size="40" name="email" type="email" placeholder="your.email@host.com">
</p>
<p>
<label>URL</label><br/>
<input size="40" name="url" type="url" placeholder="http://your.webhook.url.com/path">
</p>
<p>
<label>Password</label><br/>
<input size="40" name="password" type="password">
</p>
<p>
<input type="submit" value="Add config">
</p>
</form>
</body>
</html>
45 changes: 45 additions & 0 deletions server.js
@@ -0,0 +1,45 @@
var fs = require('fs'),
path = require('path'),
request = require('request'),
smtp = require('./smtp'),
http = require('./http');

var config = process.env.CONFIG || process.env.TMPDIR + path.sep + 'config.json';
fs.readFile(config, function(err, data) {
var whiteList = (data ? JSON.parse(data) : {});
console.log("Loaded", config, whiteList);

var url_for = function(req) {
var url = null;
for (var email in whiteList) {
url = whiteList[email];
if (url && req.to.indexOf(email) != -1) return url;
}
}

smtp.start(process.env.SMTP_PORT, url_for, function(req, buffer) {
var url = url_for(req);
console.log('POST', url);
var r = request.post(url);
r.form().append('message', buffer.getContents());
r.on('error', function(err) {
req.reject(err);
console.log("FAIL", url, err);
});
r.on('end', function(err) {
if (! err) return req.accept();
req.reject(err);
console.log("FAIL", url, err);
});
});

http.start(process.env.PORT, function(password, email, url, next) {
if (password == process.env.PASSWORD) {
whiteList[email] = url;
console.log("Writing", config, whiteList);
fs.writeFile(config, JSON.stringify(whiteList, null, 2), next);
} else {
next();
}
});
});
35 changes: 35 additions & 0 deletions smtp.js
@@ -0,0 +1,35 @@
var simplesmtp = require("simplesmtp"),
streamBuffers = require("stream-buffers"),
config = {
requireAuthentication: false,
enableAuthentication: false,
timeout: 30000,
disableEHLO: true,
disableDNSValidation: true,
SMTPBanner: "server"
};

module.exports = {
start: function(smtpPort, allowing, callback) {
smtpPort = smtpPort || 25;
allowing = allowing || function() { };
simplesmtp.createSimpleServer(config, function(request) {
if (allowing(request)) {
console.log((new Date()) + "\tGOOD\t" + request.remoteAddress + "\t" + request.from + "\t" + request.to.join(' '));
var buffer = new streamBuffers.WritableStreamBuffer({
initialSize: (100 * 1024), // start as 100 kilobytes.
incrementAmount: (10 * 1024) // grow by 10 kilobytes each time buffer overflows.
});
request.pipe(buffer);
request.on("end", function() {
callback(request, buffer);
});
} else {
console.log((new Date()) + "\tFAIL\t" + request.remoteAddress + "\t" + request.from + "\t" + request.to.join(' '));
request.reject();
}
}).listen(smtpPort, '0.0.0.0', function(err) {
console.log('SMTP server on 0.0.0.0:' + smtpPort, err || "");
});
}
}
Empty file added spec/.gitkeep
Empty file.

0 comments on commit b16c3d3

Please sign in to comment.