Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Added tests, added more cogent documentation, added demos directory

  • Loading branch information...
commit 0608bd0538935691abe10b74819f0b7991f122af 1 parent c5ae8f7
@ditesh authored
View
239 README.md
@@ -1,15 +1,21 @@
# node-poplib
-node-poplib offers an MIT-licensed client library for the POP3 protocol. It is currently compliant to the relevant RFC's and offers the following capabilities:
+node-poplib offers an MIT-licensed client library for the POP3 protocol. It is currently provides the following capabilities:
* USER, PASS, APOP
* LIST, TOP, RETR, DELE
* UIDL, NOOP, CAPA
* RSET, QUIT
-* Plaintext and TLS support
+* Plaintext and encrypted TLS support
* STLS
+* SASL PLAIN CRAM-MD5
-It complies to RFC 1939 (POP3) and RFC 2595 (STLS);
+It complies to:
+
+* RFC 1939 (POP3)
+* RFC 2595 (STLS);
+* RFC 5034 (SASL AUTH)
+* RFC 2195 (CRAM-MD5)
## Installation
@@ -47,7 +53,7 @@ client.on("error", function(err) {
client.on("connect", function() {
console.log("CONNECT success");
- client.auth(username, password);
+ client.login(username, password);
});
@@ -60,13 +66,13 @@ client.on("locked", function(cmd) {
});
````
-The error event is emitted when there is a network error. The Node.js error object is passed back to user-code.
+The `error` event is emitted when there is a network error. The underlying error object is passed back to user-code.
-The connect event is emitted when the connection to the remote server is successful.
+The `connect` event is emitted when the connection to the remote server is successful.
-The invalid-state event is emitted when you try to carry out an action not allowed within your current state (eg, attempting to RETR-ieve a message when authentication has not been completed).
+The `invalid-state` event is emitted when you try to carry out an action not allowed within your current state (eg, attempting to `RETR`-ieve a message when authentication has not been completed).
-The locked event is emitted when you try to execute another command while the current command has not finished executing successfully (eg, attempting to RETR-ieve a message while the remote server has not finished sending LIST data).
+The `locked` event is emitted when you try to execute another command while the current command has not finished executing successfully (eg, attempting to `RETR`-ieve a message while the remote server has not finished sending `LIST` data).
On a successful connect, we try authenticating:
@@ -82,7 +88,7 @@ client.on("connect", function() {
Note that on successful auth, we try listing. For all events, the first received argument is always a boolean indicating whether the command succeeded. The last received argument is always the raw unparsed data received from the remote server. The intermediate arguments contain parsed data.
````javascript
-client.on("auth", function(status, rawdata) {
+client.on("login", function(status, * rawdata) {
if (status) {
@@ -98,7 +104,7 @@ client.on("auth", function(status, rawdata) {
});
// Data is a 1-based index of messages, if there are any messages
-client.on("list", function(status, msgcount, msgnumber, data, rawdata) {
+client.on("list", function(status, msgcount, msgnumber, data, * rawdata) {
if (status === false) {
@@ -117,7 +123,7 @@ client.on("list", function(status, msgcount, msgnumber, data, rawdata) {
}
});
-client.on("retr", function(status, msgnumber, data, rawdata) {
+client.on("retr", function(status, msgnumber, data, * rawdata) {
if (status === true) {
@@ -133,7 +139,7 @@ client.on("retr", function(status, msgnumber, data, rawdata) {
}
});
-client.on("dele", function(status, msgnumber, data, rawdata) {
+client.on("dele", function(status, msgnumber, data, * rawdata) {
if (status === true) {
@@ -148,29 +154,218 @@ client.on("dele", function(status, msgnumber, data, rawdata) {
}
});
-client.on("quit", function(status, rawdata) {
+client.on("quit", function(status, * rawdata) {
if (status === true) console.log("QUIT success");
else console.log("QUIT failed");
});
-
````
-See tests and demos for more examples.
+## API
+
+`login(username, password)`
+
+Self explanatory. This executes `USER` and `PASS`. Do not use over cleartext channels. Preferably don't use it at all as `auth()` implements `AUTH` which deprecates the need for USER and PASS. Emits `login` event.
+
+`apop(username, password)`
+
+This executes `APOP`. Requires server side support. Preferably don't use it as `auth()` implements `AUTH` which deprecates the need for USER and PASS. Emits `apop` event.
+
+`auth(type, username, password)`
+
+This executes `AUTH`. Requires server side support. Currently only "PLAIN" and "CRAM-MD5" types are supported. Emits `auth` event.
+
+`stls()`
+
+This executes `STLS`. Requires server side support (check using `capa()` first). According to the RFC's, using `STLS` is preferable to a purely TLS connection (although some servers only support purely TLS connections). Emits `stls` event.
+
+`capa()`
+
+This executes `CAPA`. Requires server side support. Emits `capa` event.
+
+`list([msgnumber])`
+
+This executes `LIST`. If the optional `msgnumber` is provided, then `LIST msgnumber` is executed. Emits `list` event.
+
+`top(msgnumber, lines)`
+
+This executes `TOP`. Requires server side support. `msgnumber` and `lines` must be provided. TEmits `top` event.
+
+`stat()`
+
+This executes `STAT`. Emits `stat` event.
+
+`uidl([msgnumber])`
+
+This executes `UIDL`. If the optional `msgnumber` is provided, then `UIDL msgnumber` is executed. Emits `uidl` event.
+
+`retr(msgnumber)`
+
+This executes `RETR`. `msgnumber` must be provided. Emits `retr` event.
+
+`dele(msgnumber)`
+
+This executes `DELE`. `msgnumber` must be provided. Emits `dele` event.
+
+`rset()`
+
+This executes `RSET`. Emits `rset` event.
+
+`noop()`
+
+This executes `NOOP`. Emits `noop` event.
+
+`quit()`
+
+This executes `QUIT`. Emits `quit` event.
+
+
+## Events
+
+`connect`
+
+The `connect` event is emitted upon competion of connection attempt (initiated in the constructor). The arguments, in order, are:
+
+* status: boolean true or false, indicating whether the execution was successful
+* rawdata: string containing success or error message from the server
+
+`login`
+
+The `login` event is emitted upon competion of `login()` method. The arguments, in order, are:
+
+* status: boolean true or false, indicating whether the execution was successful
+* rawdata: string containing success or error message from the server
+
+`apop`
+
+The `apop` event is emitted upon competion of `apop()` method. The arguments, in order, are:
+
+* status: boolean true or false, indicating whether the execution was successful
+* rawdata: string containing success or error message from the server
+
+`auth`
+
+The `auth` event is emitted upon competion of `auth()` method. The arguments, in order, are:
+
+* status: boolean true or false, indicating whether the execution was successful
+* rawdata: string containing success or error message from the server
+
+`stls`
+
+The `stls` event is emitted upon competion of `stls()` method. The arguments, in order, are:
+
+* status: boolean true or false, indicating whether the execution was successful
+* rawdata: string containing success or error message from the server
+
+`capa`
+
+The `capa` event is emitted upon competion of `capa()` method. The arguments, in order, are:
+
+* status: boolean true or false, indicating whether the execution was successful
+* data: if status is true, this is an array containing list of server capabilities
+* rawdata: string containing success or error message from the server
+
+`list`
+
+The `list` event is emitted upon competion of `list()` method. The arguments, in order, are:
+
+* status: boolean true or false, indicating whether the execution was successful
+msgcount: this contains the number of messages return by the `list()` method. If a valid msgnumber was provided, this value will naturally be `1` (else `null`)
+* msgnumber: if msgnumber was provided to the method, the provided value will be reflected here (else `undefined`)
+* data: if status is true, this is an array containing list of server capabilities (else `null`)
+* rawdata: string containing success or error message from the server
+
+`top`
+
+The `top` event is emitted upon competion of `top()` method. The arguments, in order, are:
+
+* status: boolean true or false, indicating whether the execution was successful
+* msgnumber: if msgnumber was provided to the method, the provided value will be reflected here (else `undefined`)
+* data: if status is true, this is an ASCII string containing the returnValue (else `null`)
+* rawdata: string containing success or error message from the server
+
+`stat`
+
+The `stat` event is emitted upon competion of `stat()` method. The arguments, in order, are:
+
+* status: boolean true or false, indicating whether the execution was successful
+* data: if status is true, an object with keys `count` and `octet` (else `null`)
+* rawdata: string containing success or error message from the server
+
+`uidl`
+
+The `uidl` event is emitted upon competion of `uidl()` method. The arguments, in order, are:
+
+* status: boolean true or false, indicating whether the execution was successful
+* msgnumber: if msgnumber was provided to the method, the provided value will be reflected here (else `undefined`)
+* data: if status is true, this is an array containing the UIDL list (else `null`)
+* rawdata: string containing success or error message from the server
+
+`retr`
+
+The `retr` event is emitted upon competion of `retr()` method. The arguments, in order, are:
+
+* status: boolean true or false, indicating whether the execution was successful
+* msgnumber: the `msgnumber` provided to the method
+* data: if status is `true`, the results are returned as an ASCII string (else `null`)
+* rawdata: string containing success or error message from the server
+
+`dele`
+
+The `dele` event is emitted upon competion of the `dele()` method. The arguments, in order, are:
+
+* status: boolean true or false, indicating whether the execution was successful
+* msgnumber: the `msgnumber` provided to the method
+* rawdata: string containing success or error message from the server
+
+`rset`
+
+The `rset` event is emitted upon competion of the `rset()` method. The arguments, in order, are:
+
+* status: boolean true or false, indicating whether the execution was successful
+* rawdata: string containing success or error message from the server
+
+`noop`
+
+The `noop` event is emitted upon competion of the `noop()` method. The arguments, in order, are:
+
+* status: boolean true or false, indicating whether the execution was successful
+* rawdata: string containing success or error message from the server
+
+`quit`
+
+The `quit` event is emitted upon competion of the `quit()` method. The arguments, in order, are:
+
+* status: boolean true or false, indicating whether the execution was successful
+* * rawdata: string containing success or error message from the server
+
+`error`
+
+The `error` event is emitted if there is an `error` event from the underlying socket. The original error object is passed as an argument.
+
+`invalid-state`
+
+The `invalid-state` event is emitted when an action not allowed within the current state s attmempted (eg, attempting to `RETR`-ieve a message when `AUTH`-entication has not been completed).
+
+`locked`
+
+The `locked` event is emitted when a method is called while existing execution has not finished executing (eg, attempting to `RETR`-ieve a message while the remote server has not finished sending `LIST` data).
## Tests & Demos
-For test purposes, you can use the following sendmail.sh script to pump email into your SMTP server for retrieval via POP3:
+Tests are in `tests`. Demos are in `demos`.
+
+There is a full-featured POP3 client example in `demos/demo.js`. There is also a simple example of downloading all emails in a POP3 server and saving it locally in an mbox formatted file in `demos/retrieve-all.js`.
+
+For testing purposes, you can use the following sendmail.sh script to pump email into your SMTP server for retrieval via POP3:
````bash
./sendmail.sh 10 "user@example.com" "this is my subject" "this is my body"
````
-There is a full-featured POP3 client example in `tests/demo.js`.
-
-There is also a simple example of downloading all emails in a POP3 server and saving it locally in an mbox formatted file in `tests/retrieve-all.js`.
-
-There is a TLS example in `tests/tls.js`. STLS example is available in `tests/stls.js`.
+You can execute the test-runner as follows:
-If you want to try APOP support, see `tests/apop.js`.
+````bash
+./runner.sh username password pop3server pop3port pop3tlsport testemail@address.com
+````
View
3  tests/demo.js → demos/demo.js
@@ -34,12 +34,11 @@ var argv = require('optimist')
var host = argv.host || "localhost";
var port = argv.port || 110;
var debug = argv.debug === "on" ? true : false;
-var networkdebug = argv.networkdebug || false;
var msgnumber = argv.msgnumber;
var username = argv.username;
var password = argv.password;
-var client = new POP3Client(port, host, false, networkdebug);
+var client = new POP3Client(port, host, { argv.networkdebug: (argv.debug === "on" ? true: false) });
client.on("error", function(err) {
View
13 tests/retrieve-all.js → demos/retrieve-all.js
@@ -35,7 +35,6 @@ var host = argv.host || "localhost";
var port = argv.port || 110;
var debug = argv.debug === "on" ? true : false;
var tls = argv.tls === "on" ? true : false;
-var networkdebug = argv.networkdebug || false;
var filename = argv.filename;
var username = argv.username;
var password = argv.password;
@@ -44,12 +43,18 @@ var currentmsg = 0;
var fd = fs.openSync(filename, "a+");
-var client = new POP3Client(port, host, tls, networkdebug);
+var client = new POP3Client(port, host, {
+
+ tlserrs: false,
+ enabletls: (argv.tls === "on" ? true: false),
+ debug: (argv.networkdebug === "on" ? true: false)
+
+ });
client.on("error", function(err) {
- if (err.errno === 111) console.log("Unable to connect to server");
- else console.log("Server error occurred");
+ if (err.errno === 111) console.log("Unable to connect to server, failed");
+ else console.log("Server error occurred, failed");
console.log(err);
View
227 main.js
@@ -27,11 +27,17 @@
var net = require("net"),
tls = require("tls"),
util = require("util"),
+ crypto = require("crypto"),
events = require("events"),
hashlib = require("hashlib");
// Constructor
-function POP3Client(port, host, enabletls, debug) {
+function POP3Client(port, host, options) {
+
+ // Optional constructor arguments
+ var enabletls = options.enabletls || "";
+ var ignoretlserrs = (options.ignoretlserrs !== undefined ? options.ignoretlserrs: false);
+ var debug = options.debug || false;
// Private variables follow
var self = this;
@@ -49,7 +55,7 @@ function POP3Client(port, host, enabletls, debug) {
locked = false;
callback = function() {};
- self.emit("connect-failure", data);
+ self.emit("connect", false, data);
} else {
@@ -70,7 +76,7 @@ function POP3Client(port, host, enabletls, debug) {
state = 1;
self.data["banner"] = banner;
- self.emit("connect", data);
+ self.emit("connect", true, data);
}
};
@@ -78,12 +84,14 @@ function POP3Client(port, host, enabletls, debug) {
// Public variables follow
this.data = {
- apop: false,
- banner: "",
- username: "",
host: host,
port: port,
- tls: enabletls
+ banner: "",
+ stls: false,
+ apop: false,
+ username: "",
+ tls: enabletls,
+ ignoretlserrs: ignoretlserrs
};
@@ -105,7 +113,7 @@ function POP3Client(port, host, enabletls, debug) {
if (argument !== undefined) text = text + " " + argument + "\r\n";
else text = text + "\r\n";
- if (debug) util.log(text);
+ if (debug) console.log("Client: " + util.inspect(text));
socket.write(text);
@@ -189,18 +197,27 @@ function POP3Client(port, host, enabletls, debug) {
data = data.toString("ascii");
bufferedData += data;
- if (debug) util.log(data);
+ if (debug) console.log("Server: " + util.inspect(data));
+
+ if (checkResp === true) {
- if (checkResp === true && bufferedData.substr(0, 3) === "+OK") {
+ if (bufferedData.substr(0, 3) === "+OK") {
- checkResp = false;
- response = true;
+ checkResp = false;
+ response = true;
- } else if (checkResp === true && bufferedData.substr(0, 4) === "-ERR") {
+ } else if (bufferedData.substr(0, 4) === "-ERR") {
- checkResp = false;
- response = false;
+ checkResp = false;
+ response = false;
+ // The following is only used for SASL
+ } else if (multiline === false) {
+
+ checkResp = false;
+ response = true;
+
+ }
}
if (checkResp === false) {
@@ -236,7 +253,10 @@ function POP3Client(port, host, enabletls, debug) {
}
function onError(err) {
- self.emit("error", err);
+
+ if (err.errno === 111) self.emit("connect", false, err);
+ else self.emit("error", err);
+
}
function onEnd(data) {
@@ -244,6 +264,9 @@ function POP3Client(port, host, enabletls, debug) {
socket = null;
}
+ function onClose() {
+ self.emit("close");
+ }
// Constructor code follows
// Set up EventEmitter constructor function
@@ -253,7 +276,14 @@ function POP3Client(port, host, enabletls, debug) {
if (enabletls === true) {
tlssock = tls.connect(port, host, function() {
- if (tlssock.authorized === false) self.emit("tls-error", tlssock.authorizationError);
+
+ if (tlssock.authorized === false) {
+
+ if ((self.data["ignoretlserrs"] === false) || (self.data["ignoretlserrs"] === true && tlssock.authorizationError !== "DEPTH_ZERO_SELF_SIGNED_CERT"))
+ self.emit("tls-error", tlssock.authorizationError);
+
+
+ }
});
socket = tlssock;
@@ -264,82 +294,137 @@ function POP3Client(port, host, enabletls, debug) {
socket.on("data", onData);
socket.on("error", onError);
socket.on("end", onEnd);
+ socket.on("close", onClose);
}
util.inherits(POP3Client, events.EventEmitter);
-POP3Client.prototype.stls = function() {
+POP3Client.prototype.login = function (username, password) {
var self = this;
- if (self.getState() !== 1) self.emit("invalid-state", "stls");
- else if (self.getLocked() === true) self.emit("locked", "stls");
- else if (self.data["tls"] === true) self.emit("stls", false, "Unable to execute STLS as TLS connection already established");
+ if (self.getState() !== 1) self.emit("invalid-state", "login");
+ else if (self.getLocked() === true) self.emit("locked", "login");
else {
self.setLocked(true);
self.setCallback(function(resp, data) {
- self.setLocked(false);
- self.setCallback(function() {});
+ if (resp === false) {
- if (resp === true) {
+ self.setLocked(false);
+ self.setCallback(function() {});
+ self.emit("login", false, data);
+
+ } else {
+
+ self.setCallback(function(resp, data) {
+
+ self.setLocked(false);
+ self.setCallback(function() {});
+
+ if (resp !== false) self.setState(2);
+ self.emit("login", resp, data);
- self.setCallback(function() {
- self.emit("stls", true, data);
});
- self.starttls();
+ self.setMultiline(false);
+ self.write("PASS", password);
- } else self.emit("stls", false, data);
+ }
});
self.setMultiline(false);
- self.write("STLS");
+ self.write("USER", username);
}
};
-POP3Client.prototype.auth = function (username, password) {
+// SASL AUTH implementation
+// Currently supports SASL PLAIN and CRAM-MD5
+POP3Client.prototype.auth = function (type, username, password) {
+ type = type.toUpperCase();
var self = this;
+ var types = {"PLAIN": 1, "CRAM-MD5": 1};
+ var initialresp = "";
if (self.getState() !== 1) self.emit("invalid-state", "auth");
else if (self.getLocked() === true) self.emit("locked", "auth");
- else {
- self.setLocked(true);
- self.setCallback(function(resp, data) {
+ if ((type in types) === false) {
- if (resp === false) {
+ self.emit("auth", false, "Invalid auth type", null);
+ return;
- self.setLocked(false);
- self.setCallback(function() {});
- self.emit("auth", false, data);
+ }
- } else {
+ function tlsok() {
- self.setCallback(function(resp, data) {
+ if (type === "PLAIN") {
- self.setLocked(false);
- self.setCallback(function() {});
+ initialresp = " " + new Buffer(username + "\u0000" + username + "\u0000" + password).toString("base64") + "=";
+ self.setCallback(function(resp, data) {
- if (resp !== false) self.setState(2);
- self.emit("auth", resp, data);
+ if (resp !== false) self.setState(2);
+ self.emit("auth", resp, data, data);
- });
+ });
- self.setMultiline(false);
- self.write("PASS", password);
+ } else if (type === "CRAM-MD5") {
- }
- });
+ self.setCallback(function(resp, data) {
- self.setMultiline(false);
- self.write("USER", username);
+ if (resp === false) self.emit("auth", resp, "Server responded -ERR to AUTH CRAM-MD5", data);
+ else {
+
+ var challenge = new Buffer(data.trim().substr(2), "base64").toString();
+ var hmac = crypto.createHmac("md5", password);
+ var response = new Buffer(username + " " + hmac.update(challenge).digest("hex")).toString("base64");
+
+ self.setCallback(function(resp, data) {
+
+ var errmsg = null;
+
+ if (resp !== false) self.setState(2);
+ else errmsg = "Server responded -ERR to response";
+
+ self.emit("auth", resp, null, data);
+
+ });
+
+ self.write(response);
+
+ }
+ });
+ }
+
+ self.write("AUTH " + type + initialresp);
}
+
+ if (self.data["tls"] === false && self.data["stls"] === false) {
+
+ // Remove all existing STLS listeners
+ self.removeAllListeners("stls");
+
+ self.on("stls", function(resp, rawdata) {
+
+ if (resp === false) {
+
+ // We (optionally) ignore self signed cert errors,
+ // in blatant violation of RFC 2595, Section 2.4
+ if (self.data["ignoretlserrs"] === true && rawdata === "DEPTH_ZERO_SELF_SIGNED_CERT") tlsok();
+ else self.emit("auth", false, "Unable to upgrade connection to STLS", rawdata);
+
+ } else tlsok();
+
+ });
+
+ self.stls();
+
+ } else tlsok();
};
POP3Client.prototype.apop = function (username, password) {
@@ -368,6 +453,45 @@ POP3Client.prototype.apop = function (username, password) {
}
};
+POP3Client.prototype.stls = function() {
+
+ var self = this;
+
+ if (self.getState() !== 1) self.emit("invalid-state", "stls");
+ else if (self.getLocked() === true) self.emit("locked", "stls");
+ else if (self.data["tls"] === true) self.emit("stls", false, "Unable to execute STLS as TLS connection already established");
+ else {
+
+ self.setLocked(true);
+ self.setCallback(function(resp, data) {
+
+ self.setLocked(false);
+ self.setCallback(function() {});
+
+ if (resp === true) {
+
+ self.setCallback(function(resp, data) {
+
+ if (resp === false && self.data["ignoretlserrs"] === true && data === "DEPTH_ZERO_SELF_SIGNED_CERT")
+ resp = true;
+
+ self.data["stls"] = true;
+ self.emit("stls", resp, data);
+
+ });
+
+ self.starttls();
+
+ } else self.emit("stls", false, data);
+ });
+
+ self.setMultiline(false);
+ self.write("STLS");
+
+ }
+};
+
+
POP3Client.prototype.top = function(msgnumber, lines) {
var self = this;
@@ -682,7 +806,8 @@ POP3Client.prototype.capa = function() {
var self = this;
- if (self.getLocked() === true) self.emit("locked", "capa");
+ if (self.getState() === 0) self.emit("invalid-state", "quit");
+ else if (self.getLocked() === true) self.emit("locked", "capa");
else {
self.setLocked(true);
View
74 tests/apop.js
@@ -26,67 +26,72 @@
var POP3Client = require("../main.js");
var argv = require('optimist')
- .usage("Usage: $0 --host [host] --port [port] --username [username] --password [password] --debug [on/off] --networkdebug [on/off]")
+ .usage("Usage: $0 --host [host] --port [port] --username [username] --password [password] --debug [on/off] --networkdebug [on/off] --login [on/off] --download [on/off]")
.demand(['username', 'password'])
.argv;
var host = argv.host || "localhost";
-var port = argv.port || 995;
+var port = argv.port || 110;
var debug = argv.debug === "on" ? true : false;
-var networkdebug = argv.networkdebug || false;
-var filename = argv.filename;
+var login = argv.login === "on" ? true : false;
+var download = argv.download === "on" ? true : false;
+
var username = argv.username;
var password = argv.password;
var totalmsgcount = 0;
var currentmsg = 0;
-var client = new POP3Client(port, host, true, networkdebug);
+var client = new POP3Client(port, host, { debug: (argv.debug === "on" ? true: false) });
client.on("error", function(err) {
+ console.log("Server error occurred, failed, " + err);
+});
- if (err.errno === 111) console.log("Unable to connect to server");
- else console.log("Server error occurred");
+client.on("connect", function(status, rawdata) {
- console.log(err);
+ if (status) {
-});
+ console.log("CONNECT success");
+ if (login) client.apop(username, password);
+ else client.quit();
-client.on("connect", function() {
+ } else {
- console.log("CONNECT success");
- client.apop(username, password);
+ console.log("CONNECT failed because " + rawdata);
+ return;
+ }
});
client.on("invalid-state", function(cmd) {
- console.log("Invalid state. You tried calling " + cmd);
+ console.log("Invalid state, failed. You tried calling " + cmd);
});
client.on("locked", function(cmd) {
- console.log("Current command has not finished yet. You tried calling " + cmd);
+ console.log("Current command has not finished yet, failed. You tried calling " + cmd);
});
-client.on("apop", function(status, data) {
+client.on("apop", function(status, rawdata) {
if (status) {
console.log("APOP success");
- client.list();
+ if (download) client.list();
+ else client.quit();
} else {
- console.log("APOP failed");
+ console.log("APOP failed because " + rawdata);
client.quit();
}
-
});
client.on("list", function(status, msgcount, msgnumber, data, rawdata) {
if (status === false) {
- console.log("LIST failed");
+ console.log("LIST failed because " + rawdata);
client.quit();
} else if (msgcount > 0) {
@@ -108,16 +113,33 @@ client.on("retr", function(status, msgnumber, data, rawdata) {
if (status === true) {
- console.log("RETR success " + msgnumber);
- currentmsg += 1;
+ console.log("RETR success " + msgnumber + " with data " + data);
+ client.dele(msgnumber);
+
+ } else {
+
+ console.log("RETR failed for msgnumber " + msgnumber + " because " + rawdata);
+ client.quit();
+
+ }
+});
+
+client.on("dele", function(status, msgnumber, data, rawdata) {
+
+ if (status === true) {
+
+ console.log("DELE success for msgnumber " + msgnumber);
+
+ if (currentmsg < totalmsgcount) {
+
+ currentmsg += 1;
+ client.retr(currentmsg);
- fs.write(fd, new Buffer(data + "\r\n\r\n"), 0, data.length+4, null, function(err, written, buffer) {
- client.quit();
- });
+ } else client.quit();
} else {
- console.log("RETR failed for msgnumber " + msgnumber);
+ console.log("DELE failed for msgnumber " + msgnumber);
client.quit();
}
@@ -126,6 +148,6 @@ client.on("retr", function(status, msgnumber, data, rawdata) {
client.on("quit", function(status, rawdata) {
if (status === true) console.log("QUIT success");
- else console.log("QUIT failed");
+ else console.log("QUIT failed because " + rawdata);
});
View
45 tests/apop.sh
@@ -0,0 +1,45 @@
+#!/bin/sh
+# Test script
+#
+# Copyright (C) 2011 by Ditesh Shashikant Gathani <ditesh@gathani.org>
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to deal
+# in the Software without restriction, including without limitation the rights
+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in
+# all copies or substantial portions of the Software.
+#
+# 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 AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+# THE SOFTWARE.
+
+print_title "apop.js"
+RANDOMID=$RANDOM
+
+print_test "Sending test message (str: $RANDOMID)"
+OUTPUT=`./sendmail.sh 1 $EMAIL "subject with $RANDOMID" "body with $RANDOMID"`
+print_result 0 $OUTPUT
+
+print_test "Correct auth"
+OUTPUT=`node apop.js --username $USER --password $PASS --host $HOST --port $PORT --login on`;
+print_result 0 $OUTPUT
+
+print_test "Invalid auth"
+OUTPUT=`node apop.js --username $USER --password ${PASS}a --host $HOST --port $PORT --login on`;
+print_result 1 $OUTPUT
+
+print_test "Login and message download"
+OUTPUT=`node apop.js --username $USER --password $PASS --host $HOST --port $PORT --login on --download on`
+OUTPUT=`echo $OUTPUT | grep $RANDOMID`
+
+if [ $? -eq 1 ]; then OUTPUT="fail"; fi
+
+print_result 0 $OUTPUT
View
197 tests/basic.js
@@ -0,0 +1,197 @@
+/*
+
+ Node.js POP3 client demo in retrieving all POP3 messages into mbox file
+
+ Copyright (C) 2011 by Ditesh Shashikant Gathani <ditesh@gathani.org>
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to deal
+ in the Software without restriction, including without limitation the rights
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in
+ all copies or substantial portions of the Software.
+
+ 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 AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ THE SOFTWARE.
+
+*/
+
+var POP3Client = require("../main.js");
+var argv = require('optimist')
+ .usage("Usage: $0 --host [host] --port [port] --username [username] --password [password] --debug [on/off] --networkdebug [on/off] --download [on/off] --dele [on/off] --rset [on/off]")
+ .demand(['username', 'password'])
+ .argv;
+
+var host = argv.host || "localhost";
+var port = argv.port || 110;
+var debug = argv.debug === "on" ? true : false;
+var dele = argv.dele === "on" ? true : false;
+var rset = argv.rset === "on" ? true : false;
+var download = argv.download === "on" ? true : false;
+
+var username = argv.username;
+var password = argv.password;
+var totalmsgcount = 0;
+var currentmsg = 0;
+
+var client = new POP3Client(port, host, {
+
+ debug: (argv.networkdebug === "on" ? true: false)
+
+ });
+
+
+client.on("error", function(err) {
+ console.log("Server error occurred, failed, " + err);
+});
+
+client.on("connect", function(status, rawdata) {
+
+ if (status) {
+
+ console.log("CONNECT success");
+ client.login(username, password);
+
+ } else {
+
+ console.log("CONNECT failed because " + rawdata);
+ return;
+
+ }
+});
+
+client.on("invalid-state", function(cmd) {
+ console.log("Invalid state, failed. You tried calling " + cmd);
+});
+
+client.on("locked", function(cmd) {
+ console.log("Current command has not finished yet, failed. You tried calling " + cmd);
+});
+
+client.on("login", function(status, data, rawdata) {
+
+ if (status) {
+
+ console.log("LOGIN/PASS success");
+ if (download || dele) client.list();
+ else client.capa();
+
+ } else {
+
+ console.log("LOGIN/PASS failed because " + rawdata);
+ client.quit();
+
+ }
+
+});
+
+client.on("capa", function(status, data, rawdata) {
+
+ if (download) client.list();
+ else client.quit();
+
+});
+
+client.on("rset", function(status,rawdata) {
+
+ if (status) {
+
+ console.log("RSET success");
+ rset=false;
+ currentmsg=1;
+ client.retr(1);
+
+ } else {
+
+ console.log("RSET failed because " + rawdata);
+ client.quit();
+
+ }
+});
+
+client.on("list", function(status, msgcount, msgnumber, data, rawdata) {
+
+ if (status === false) {
+
+ console.log("LIST failed");
+ client.quit();
+
+ } else if (msgcount > 0) {
+
+ totalmsgcount = msgcount;
+ currentmsg = 1;
+ console.log("LIST success with " + msgcount + " message(s)");
+
+ if (download) client.retr(1);
+ else if (dele) client.dele(1);
+ else client.quit();
+
+ } else {
+
+ console.log("LIST success with 0 message(s)");
+ client.quit();
+
+ }
+});
+
+client.on("retr", function(status, msgnumber, data, rawdata) {
+
+ if (status === true) {
+
+ console.log("RETR success " + msgnumber + " with data " + data);
+ if (dele) client.dele(msgnumber);
+ else {
+
+ if (currentmsg < totalmsgcount) {
+
+ currentmsg += 1;
+ client.retr(currentmsg);
+
+ } else client.quit();
+ }
+
+ } else {
+
+ console.log("RETR failed for msgnumber " + msgnumber + " because " + rawdata);
+ client.quit();
+
+ }
+});
+
+client.on("dele", function(status, msgnumber, data, rawdata) {
+
+ if (status === true) {
+
+ console.log("DELE success for msgnumber " + msgnumber);
+
+ if (currentmsg < totalmsgcount) {
+
+ currentmsg += 1;
+ if (!rset && download) client.retr(currentmsg);
+ else client.dele(currentmsg);
+
+ } else if (rset) client.rset()
+ else client.quit();
+
+ } else {
+
+ console.log("DELE failed for msgnumber " + msgnumber);
+ client.quit();
+
+ }
+});
+
+client.on("quit", function(status, rawdata) {
+
+ if (status === true) console.log("QUIT success");
+ else console.log("QUIT failed");
+
+});
View
77 tests/basic.sh
@@ -0,0 +1,77 @@
+#!/bin/sh
+# Test script
+#
+# Copyright (C) 2011 by Ditesh Shashikant Gathani <ditesh@gathani.org>
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to deal
+# in the Software without restriction, including without limitation the rights
+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in
+# all copies or substantial portions of the Software.
+#
+# 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 AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+# THE SOFTWARE.
+
+print_title "basic.js"
+RANDOMID=$RANDOM
+
+print_test "Sending test message to $EMAIL (str: $RANDOMID)"
+OUTPUT=`./sendmail.sh -q 1 $EMAIL "subject with $RANDOMID" "body with $RANDOMID"`
+print_result 0 $OUTPUT
+
+print_test "CAPA test"
+OUTPUT=`node basic.js --username $USER --password $PASS --host $HOST --port $PORT`;
+print_result 0 $OUTPUT
+
+print_test "RETR test"
+OUTPUT=`node basic.js --username $USER --password $PASS --host $HOST --port $PORT --download on`;
+OUTPUT=`echo $OUTPUT | grep $RANDOMID`
+if [ $? -eq 1 ]; then OUTPUT="fail"; fi
+print_result 0 $OUTPUT
+
+print_test "RETR, DELE test"
+OUTPUT=`node basic.js --username $USER --password $PASS --host $HOST --port $PORT --download on --dele on`;
+OUTPUT=`node basic.js --username $USER --password $PASS --host $HOST --port $PORT --download on`;
+OUTPUT=`echo $OUTPUT | grep $RANDOMID`
+if [ $? -eq 0 ]; then OUTPUT="fail"; fi
+print_result 0 $OUTPUT
+
+RANDOMID=$RANDOM
+print_test "Sending test message to $EMAIL (str: $RANDOMID)"
+OUTPUT=`./sendmail.sh -q 1 $EMAIL "subject with $RANDOMID" "body with $RANDOMID"`
+print_result 0 $OUTPUT
+
+print_test "Sleeping 5 seconds"
+OUTPUT=`sleep 5`
+print_result 0 $OUTPUT
+
+print_test "DELE test"
+OUTPUT=`node basic.js --username $USER --password $PASS --host $HOST --port $PORT --dele on`;
+OUTPUT=`node basic.js --username $USER --password $PASS --host $HOST --port $PORT --download on`;
+OUTPUT=`echo $OUTPUT | grep $RANDOMID`
+if [ $? -eq 0 ]; then OUTPUT="fail"; fi
+print_result 0 $OUTPUT
+
+RANDOMID=$RANDOM
+print_test "Sending test message to $EMAIL (str: $RANDOMID)"
+OUTPUT=`./sendmail.sh -q 1 $EMAIL "subject with $RANDOMID" "body with $RANDOMID"`
+print_result 0 $OUTPUT
+
+print_test "Sleeping 5 seconds"
+OUTPUT=`sleep 5`
+print_result 0 $OUTPUT
+
+print_test "DELE, RSET, RETR test"
+OUTPUT=`node basic.js --username $USER --password $PASS --host $HOST --port $PORT --dele on --rset on --download on`;
+OUTPUT=`echo $OUTPUT | grep $RANDOMID`
+if [ $? -eq 1 ]; then OUTPUT="fail"; fi
+print_result 0 $OUTPUT
View
90 tests/login.js
@@ -0,0 +1,90 @@
+/*
+
+ Node.js POP3 client demo in retrieving all POP3 messages into mbox file
+
+ Copyright (C) 2011 by Ditesh Shashikant Gathani <ditesh@gathani.org>
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to deal
+ in the Software without restriction, including without limitation the rights
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in
+ all copies or substantial portions of the Software.
+
+ 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 AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ THE SOFTWARE.
+
+*/
+
+var POP3Client = require("../main.js");
+var argv = require('optimist')
+ .usage("Usage: $0 --host [host] --port [port] --username [username] --password [password] --debug [on/off] --networkdebug [on/off]")
+ .demand(['username', 'password'])
+ .argv;
+
+var host = argv.host || "localhost";
+var port = argv.port || 110;
+var debug = argv.debug === "on" ? true : false;
+var filename = argv.filename;
+var username = argv.username;
+var password = argv.password;
+var totalmsgcount = 0;
+var currentmsg = 0;
+
+var client = new POP3Client(port, host, { debug: (argv.debug === "on" ? true: false) });
+
+client.on("connect-error", function(err) {
+
+console.log("potato");
+
+});
+
+client.on("error", function(err) {
+ console.log("Server error occurred, failed, " + err);
+});
+
+client.on("connect", function(status, rawdata) {
+
+ if (status) {
+
+ console.log("CONNECT success");
+ client.login(username, password);
+
+ } else {
+
+ console.log("CONNECT failed because " + rawdata);
+ return;
+
+ }
+});
+
+client.on("invalid-state", function(cmd) {
+ console.log("Invalid state, failed. You tried calling " + cmd);
+});
+
+client.on("locked", function(cmd) {
+ console.log("Current command has not finished yet, failed. You tried calling " + cmd);
+});
+
+client.on("login", function(status, rawdata) {
+
+ if (status) console.log("LOGIN success");
+ else console.log("LOGIN failed because " + rawdata);
+ client.quit();
+
+});
+
+client.on("quit", function(status, rawdata) {
+
+ if (status === true) console.log("QUIT success");
+ else console.log("QUIT failed because " + rawdata);
+
+});
View
47 tests/login.sh
@@ -0,0 +1,47 @@
+#!/bin/sh
+# Test script
+#
+# Copyright (C) 2011 by Ditesh Shashikant Gathani <ditesh@gathani.org>
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to deal
+# in the Software without restriction, including without limitation the rights
+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in
+# all copies or substantial portions of the Software.
+#
+# 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 AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+# THE SOFTWARE.
+
+print_title "login.js"
+print_test "Correct auth"
+OUTPUT=`node login.js --username $USER --password $PASS --host $HOST --port $PORT`;
+print_result 0 $OUTPUT
+
+print_test "Invalid auth"
+OUTPUT=`node login.js --username $USER --password ${PASS}a --host $HOST --port $PORT`
+print_result 1 $OUTPUT
+
+print_test "Correct host"
+OUTPUT=`node login.js --username $USER --password $PASS --host $HOST --port $PORT`;
+print_result 0 $OUTPUT
+
+print_test "Invalid host"
+OUTPUT=`node login.js --username $USER --password $PASS --host ${HOST}a --port $PORT`
+print_result 1 $OUTPUT
+
+print_test "Correct port"
+OUTPUT=`node login.js --username $USER --password $PASS --host $HOST --port $PORT`;
+print_result 0 $OUTPUT
+
+print_test "Invalid port"
+OUTPUT=`node login.js --username $USER --password $PASS --host $HOST --port ${PORT}1`
+print_result 1 $OUTPUT
View
98 tests/runner.sh
@@ -0,0 +1,98 @@
+#!/bin/sh
+# Test runner
+#
+# Copyright (C) 2011 by Ditesh Shashikant Gathani <ditesh@gathani.org>
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to deal
+# in the Software without restriction, including without limitation the rights
+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in
+# all copies or substantial portions of the Software.
+#
+# 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 AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+# THE SOFTWARE.
+
+print_title() {
+ echo -e "\033[1;30mTesting ${1}\033[0;30m";
+}
+
+print_test() {
+ printf "%-60s" "$1";
+}
+
+print_result() {
+
+ echo $* | grep -q "fail"
+
+ # $1: 0 is expecting no failure, 1 is expecting failure
+ if [ $? -eq $1 ]; then
+ FAILCOUNT=`expr $FAILCOUNT + 1`
+ echo -e " [\033[1;31mFAIL\033[0;30m]";
+ else
+ PASSCOUNT=`expr $PASSCOUNT + 1`
+ echo -e " [\033[1;32mPASS\033[0;30m]";
+ fi
+
+}
+
+echo "runner.sh v0.1 - a test runner utility"
+echo "Copyright (c) 2011 Ditesh Shashikant Gathani <ditesh@gathani.org>"
+
+if [ $# -lt 6 ]; then
+
+ echo "Usage:"
+ echo " runner.sh username password host standard-port tls-port testemail@example.com [test]"
+ echo " username: POP3 username"
+ echo " password: POP3 password"
+ echo " host: POP3 host"
+ echo " standard-port: POP3 port (eg 110)"
+ echo " tls-port: POP3 TLS port (eg 995)"
+ echo " email: valid email address on POP3 server which can receive emails"
+ echo " test: which test to run (default all"
+ exit 1
+
+fi
+
+USER=$1
+PASS=$2
+HOST=$3
+PORT=$4
+TLSPORT=$5
+EMAIL=$6
+FAILCOUNT=0
+PASSCOUNT=0
+
+if [ $# -eq 7 ]; then
+
+ echo
+ source ./$7
+
+else
+
+ echo
+ source ./login.sh
+ echo
+ source ./basic.sh
+ echo
+ source ./apop.sh
+ echo
+ source ./stls.sh
+ echo
+ source ./tls.sh
+
+fi
+
+echo
+echo -e "\033[1;30mSummary:"
+echo -e " \033[1;32mPassed tests: ${PASSCOUNT}\033[0;30m"
+echo -e " \033[1;31mFailed tests: ${FAILCOUNT}\033[0;30m"
+echo
View
177 tests/sasl.js
@@ -0,0 +1,177 @@
+/*
+
+ Node.js POP3 client demo in retrieving all POP3 messages into mbox file
+
+ Copyright (C) 2011 by Ditesh Shashikant Gathani <ditesh@gathani.org>
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to deal
+ in the Software without restriction, including without limitation the rights
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in
+ all copies or substantial portions of the Software.
+
+ 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 AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ THE SOFTWARE.
+
+*/
+
+var POP3Client = require("../main.js");
+var argv = require('optimist')
+ .usage("Usage: $0 --host [host] --port [port] --username [username] --password [password] --debug [on/off] --networkdebug [on/off] --auth [plain/cram-md5] --tls [on/off] --download [on/off]")
+ .demand(['username', 'password', 'auth'])
+ .describe('auth', 'Valid AUTH types: plain, cram-md5')
+ .argv;
+
+var host = argv.host || "localhost";
+var port = argv.port || 110;
+var debug = argv.debug === "on" ? true : false;
+var tls = argv.tls === "on" ? true : false;
+var auth = argv.auth;
+var download = argv.download === "on" ? true : false;
+
+var username = argv.username;
+var password = argv.password;
+var totalmsgcount = 0;
+var currentmsg = 0;
+
+// We carefully ignore self signed cert errors (bad practice!)
+var client = new POP3Client(port, host, {
+
+ enabletls: tls,
+ ignoretlserrs: true,
+ debug: (argv.networkdebug === "on" ? true: false)
+
+ });
+
+client.on("tls-error", function(err) {
+
+ console.log("TLS error occurred, failed");
+ console.log(err);
+
+});
+
+client.on("close", function() {
+ console.log("close event unexpectedly received, failed");
+});
+
+client.on("error", function(err) {
+ console.log("Server error occurred, failed, " + err);
+});
+
+client.on("connect", function(status, rawdata) {
+
+ if (status) {
+
+ console.log("CONNECT success");
+ client.auth(auth, username, password);
+
+ } else {
+
+ console.log("CONNECT failed because " + rawdata);
+ return;
+
+ }
+});
+
+client.on("invalid-state", function(cmd) {
+ console.log("Invalid state. You tried calling " + cmd);
+ client.quit();
+});
+
+client.on("locked", function(cmd) {
+ console.log("Current command has not finished yet. You tried calling " + cmd);
+ client.quit();
+});
+
+client.on("auth", function(status, errmsg, rawdata) {
+
+ if (status) {
+
+ console.log("AUTH success");
+ if (download) client.list();
+ else client.quit();
+
+ } else {
+
+ console.log("AUTH failed (" + errmsg + ")");
+ client.quit();
+
+ }
+
+});
+
+client.on("list", function(status, msgcount, msgnumber, data, rawdata) {
+
+ if (status === false) {
+
+ console.log("LIST failed");
+ client.quit();
+
+ } else if (msgcount > 0) {
+
+ totalmsgcount = msgcount;
+ currentmsg = 1;
+ console.log("LIST success with " + msgcount + " message(s)");
+ client.retr(1);
+
+ } else {
+
+ console.log("LIST success with 0 message(s)");
+ client.quit();
+
+ }
+});
+
+client.on("retr", function(status, msgnumber, data, rawdata) {
+
+ if (status === true) {
+
+ console.log("RETR success " + msgnumber + " with data " + data);
+ client.dele(msgnumber);
+
+ } else {
+
+ console.log("RETR failed for msgnumber " + msgnumber + " because " + rawdata);
+ client.quit();
+
+ }
+});
+
+client.on("dele", function(status, msgnumber, data, rawdata) {
+
+ if (status === true) {
+
+ console.log("DELE success for msgnumber " + msgnumber);
+
+ if (currentmsg < totalmsgcount) {
+
+ currentmsg += 1;
+ client.retr(currentmsg);
+
+ } else client.quit();
+
+ } else {
+
+ console.log("DELE failed for msgnumber " + msgnumber);
+ client.quit();
+
+ }
+});
+
+
+client.on("quit", function(status, rawdata) {
+
+ client.removeAllListeners("close");
+ if (status === true) console.log("QUIT success");
+ else console.log("QUIT failed");
+
+});
View
85 tests/sasl.sh
@@ -0,0 +1,85 @@
+#!/bin/sh
+# Test script
+#
+# Copyright (C) 2011 by Ditesh Shashikant Gathani <ditesh@gathani.org>
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to deal
+# in the Software without restriction, including without limitation the rights
+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in
+# all copies or substantial portions of the Software.
+#
+# 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 AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+# THE SOFTWARE.
+
+print_title "sasl.js"
+RANDOMID=$RANDOM
+
+print_test "Sending test message to $EMAIL (str: $RANDOMID)"
+OUTPUT=`./sendmail.sh -q 1 $EMAIL "subject with $RANDOMID" "body with $RANDOMID"`
+print_result 0 $OUTPUT
+
+print_test "Valid PLAIN login without TLS"
+OUTPUT=`node sasl.js --username $USER --password $PASS --host $HOST --port $PORT --auth plain`;
+print_result 0 $OUTPUT
+
+print_test "Valid CRAM-MD5 login without TLS"
+OUTPUT=`node sasl.js --username $USER --password $PASS --host $HOST --port $PORT --auth "cram-md5"`;
+print_result 0 $OUTPUT
+
+print_test "Invalid PLAIN login without TLS"
+OUTPUT=`node sasl.js --username $USER --password ${PASS}a --host $HOST --port $PORT --auth plain`;
+print_result 1 $OUTPUT
+
+print_test "Invalid CRAM-MD5 login without TLS"
+OUTPUT=`node sasl.js --username $USER --password ${PASS}a --host $HOST --port $PORT --auth "cram-md5"`;
+print_result 1 $OUTPUT
+
+print_test "Valid PLAIN login with TLS"
+OUTPUT=`node sasl.js --username $USER --password $PASS --host $HOST --port $TLSPORT --auth plain --tls on`;
+print_result 0 $OUTPUT
+
+print_test "Valid CRAM-MD5 login with TLS"
+OUTPUT=`node sasl.js --username $USER --password $PASS --host $HOST --port $TLSPORT --auth "cram-md5" --tls on`;
+print_result 0 $OUTPUT
+
+print_test "Invalid PLAIN login with TLS"
+OUTPUT=`node sasl.js --username $USER --password ${PASS}a --host $HOST --port $TLSPORT --auth plain --tls on`;
+print_result 1 $OUTPUT
+
+print_test "Invalid CRAM-MD5 login with TLS"
+OUTPUT=`node sasl.js --username $USER --password ${PASS}a --host $HOST --port $TLSPORT --auth "cram-md5" --tls on`;
+print_result 1 $OUTPUT
+
+print_test "PLAIN login and message download"
+OUTPUT=`node sasl.js --username $USER --password $PASS --host $HOST --port $PORT --auth plain --download on`
+OUTPUT=`echo $OUTPUT | grep $RANDOMID`
+
+if [ $? -eq 1 ]; then OUTPUT="fail"; fi
+
+print_result 0 $OUTPUT
+
+print_test "Sending another test message to $EMAIL (str: $RANDOMID)"
+OUTPUT=`./sendmail.sh -q 1 $EMAIL "subject with $RANDOMID" "body with $RANDOMID"`
+print_result 0 $OUTPUT
+
+print_test "Sleeping 5 seconds"
+OUTPUT=`sleep 5`
+print_result 0 $OUTPUT
+
+print_test "CRAM-MD5 login and message download"
+OUTPUT=`node sasl.js --username $USER --password $PASS --host $HOST --port $PORT --auth "cram-md5" --download on`
+OUTPUT=`echo $OUTPUT | grep $RANDOMID`
+
+if [ $? -eq 1 ]; then OUTPUT="fail"; fi
+
+print_result 0 $OUTPUT
View
39 tests/sendmail.sh
@@ -1,4 +1,4 @@
-#!/bin/bash
+#!/bin/sh
# Email pumper helper script
#
@@ -22,28 +22,39 @@
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
-echo "sendmail.sh v0.1 - a utility to pump email into an SMTP server"
-echo "Copyright (c) 2011 Ditesh Shashikant Gathani <ditesh@gathani.org>"
-echo
+QUIET=0
+if [ "$1" = "-q" ]; then
+ QUIET=1
+ shift
+fi
+
+if [ $QUIET -eq 0 ]; then
+
+ echo "sendmail.sh v0.1 - a utility to pump email into an SMTP server"
+ echo "Copyright (c) 2011 Ditesh Shashikant Gathani <ditesh@gathani.org>"
+ echo
+
+fi
+
if [ $# -ne 4 ]; then
echo "Usage:"
- echo " sendmail.sh <number of emails> <to> <subject> <body>"
+ echo " sendmail.sh [-q] [number of emails] [to] [subject] [body]"
exit 1
fi
-echo "Sending $1 email(s)"
-echo " to: $2"
-echo " subject: \"$3\""
-echo " body: \"$4\""
-echo
+if [ $QUIET -eq 0 ]; then
+
+ echo "Sending $1 email(s)"
+ echo " to: $2"
+ echo " subject: \"$3\""
+ echo " body: \"$4\""
+ echo
+
+fi
for i in `seq 1 $1`; do
- echo -n "."
echo "$4" | mail -s "$3" "$2";
done
-
-echo " done."
-echo
View
83 tests/stls.js
@@ -26,71 +26,85 @@
var POP3Client = require("../main.js");
var argv = require('optimist')
- .usage("Usage: $0 --host [host] --port [port] --username [username] --password [password] --debug [on/off] --networkdebug [on/off]")
+ .usage("Usage: $0 --host [host] --port [port] --username [username] --password [password] --debug [on/off] --networkdebug [on/off] --login [on/off] --download [on/off]")
.demand(['username', 'password'])
.argv;
var host = argv.host || "localhost";
var port = argv.port || 110;
var debug = argv.debug === "on" ? true : false;
-var networkdebug = argv.networkdebug || false;
-var filename = argv.filename;
+var login = argv.login === "on" ? true : false;
+var download = argv.download === "on" ? true : false;
+
var username = argv.username;
var password = argv.password;
var totalmsgcount = 0;
var currentmsg = 0;
-var client = new POP3Client(port, host, false, networkdebug);
+var client = new POP3Client(port, host, {
-client.on("error", function(err) {
+ ignoretlserrs: true,
+ debug: (argv.networkdebug === "on" ? true: false)
- if (err.errno === 111) console.log("Unable to connect to server");
- else console.log("Server error occurred");
+ });
- console.log(err);
+client.on("error", function(err, rawdata) {
+ console.log("Server error occurred, failed, " + err);
});
-client.on("connect", function() {
+client.on("connect", function(status, rawdata) {
+
+ if (status) {
+
+ console.log("CONNECT success");
+ client.stls();
+
+ } else {
- console.log("CONNECT success");
- client.stls();
+ console.log("CONNECT failed because " + rawdata);
+ return;
+ }
});
client.on("invalid-state", function(cmd) {
- console.log("Invalid state. You tried calling " + cmd);
+ console.log("Invalid state, failed. You tried calling " + cmd);
});
client.on("locked", function(cmd) {
- console.log("Current command has not finished yet. You tried calling " + cmd);
+ console.log("Current command has not finished yet, failed. You tried calling " + cmd);
});
-client.on("stls", function(status, data) {
+client.on("stls", function(status, rawdata) {
if (status) {
console.log("STLS success");
- client.auth(username, password);
+
+ if (login) client.login(username, password);
+ else client.quit();
} else {
- console.log("STLS failed");
+ console.log("STLS failed because " + rawdata);
client.quit();
}
});
-client.on("auth", function(status, data) {
+client.on("login", function(status, data, rawdata) {
if (status) {
console.log("LOGIN/PASS success");
- client.list();
+
+ if (download) client.list();
+ else client.quit();
} else {
- console.log("LOGIN/PASS failed");
+ console.log("LOGIN/PASS failed because " + rawdata);
client.quit();
}
@@ -101,7 +115,7 @@ client.on("list", function(status, msgcount, msgnumber, data, rawdata) {
if (status === false) {
- console.log("LIST failed");
+ console.log("LIST failed because " + rawdata);
client.quit();
} else if (msgcount > 0) {
@@ -123,16 +137,33 @@ client.on("retr", function(status, msgnumber, data, rawdata) {
if (status === true) {
- console.log("RETR success " + msgnumber);
- currentmsg += 1;
+ console.log("RETR success " + msgnumber + " with data " + data);
+ client.dele(msgnumber);
+
+ } else {
+
+ console.log("RETR failed for msgnumber " + msgnumber + " because " + rawdata);
+ client.quit();
+
+ }
+});
+
+client.on("dele", function(status, msgnumber, data, rawdata) {
+
+ if (status === true) {
+
+ console.log("DELE success for msgnumber " + msgnumber);
+
+ if (currentmsg < totalmsgcount) {
+
+ currentmsg += 1;
+ client.retr(currentmsg);
- fs.write(fd, new Buffer(data + "\r\n\r\n"), 0, data.length+4, null, function(err, written, buffer) {
- client.quit();
- });
+ } else client.quit();
} else {
- console.log("RETR failed for msgnumber " + msgnumber);
+ console.log("DELE failed for msgnumber " + msgnumber);
client.quit();
}
View
45 tests/stls.sh
@@ -0,0 +1,45 @@
+#!/bin/sh
+# Test script
+#
+# Copyright (C) 2011 by Ditesh Shashikant Gathani <ditesh@gathani.org>
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to deal
+# in the Software without restriction, including without limitation the rights
+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in
+# all copies or substantial portions of the Software.
+#
+# 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 AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+# THE SOFTWARE.
+
+print_title "stls.js"
+RANDOMID=$RANDOM
+
+print_test "Sending test message to $EMAIL (str: $RANDOMID)"
+OUTPUT=`./sendmail.sh 1 $EMAIL "subject with $RANDOMID" "body with $RANDOMID"`
+print_result 0 $OUTPUT
+
+print_test "No login"
+OUTPUT=`node stls.js --username $USER --password $PASS --host $HOST --port $PORT --login off`;
+print_result 0 $OUTPUT
+
+print_test "Login only"
+OUTPUT=`node stls.js --username $USER --password $PASS --host $HOST --port $PORT --login on`;
+print_result 0 $OUTPUT
+
+print_test "Login and message download"
+OUTPUT=`node stls.js --username $USER --password $PASS --host $HOST --port $PORT --login on --download on`
+OUTPUT=`echo $OUTPUT | grep $RANDOMID`
+
+if [ $? -eq 1 ]; then OUTPUT="fail"; fi
+
+print_result 0 $OUTPUT
View
91 tests/tls.js
@@ -26,52 +26,75 @@
var POP3Client = require("../main.js");
var argv = require('optimist')
- .usage("Usage: $0 --host [host] --port [port] --username [username] --password [password] --debug [on/off] --networkdebug [on/off]")
+ .usage("Usage: $0 --host [host] --port [port] --username [username] --password [password] --debug [on/off] --networkdebug [on/off] --login [on/off] --download [on/off]")
.demand(['username', 'password'])
.argv;
var host = argv.host || "localhost";
var port = argv.port || 995;
var debug = argv.debug === "on" ? true : false;
-var networkdebug = argv.networkdebug || false;
-var filename = argv.filename;
+var login = argv.login === "on" ? true : false;
+var download = argv.download === "on" ? true : false;
+
var username = argv.username;
var password = argv.password;
var totalmsgcount = 0;
var currentmsg = 0;
-var client = new POP3Client(port, host, true, networkdebug);
+var client = new POP3Client(port, host, {
-client.on("error", function(err) {
+ enabletls: true,
+ ignoretlserrs: true,
+ debug: (argv.networkdebug === "on" ? true: false)
+
+ });
- if (err.errno === 111) console.log("Unable to connect to server");
- else console.log("Server error occurred");
+client.on("tls-error", function(err) {
+ console.log("TLS error occurred, failed");
console.log(err);
});
-client.on("connect", function() {
+client.on("close", function() {
+ console.log("close event unexpectedly received, failed");
+});
+
+client.on("error", function(err) {
+ console.log("Server error occurred, failed, " + err);
+});
+
+client.on("connect", function(status, rawdata) {
+
+ if (status) {
+
+ console.log("CONNECT success");
+ if (login) client.login(username, password);
+ else client.quit();
+
+ } else {
- console.log("CONNECT success");
- client.auth(username, password);
+ console.log("CONNECT failed because " + rawdata);
+ return;
+ }
});
client.on("invalid-state", function(cmd) {
- console.log("Invalid state. You tried calling " + cmd);
+ console.log("Invalid state, failed. You tried calling " + cmd);
});
client.on("locked", function(cmd) {
- console.log("Current command has not finished yet. You tried calling " + cmd);
+ console.log("Current command has not finished yet, failed. You tried calling " + cmd);
});
-client.on("auth", function(status, data) {
+client.on("login", function(status, data, rawdata) {
if (status) {
console.log("LOGIN/PASS success");
- client.list();
+ if (download) client.list();
+ else client.quit();
} else {
@@ -94,7 +117,7 @@ client.on("list", function(status, msgcount, msgnumber, data, rawdata) {
totalmsgcount = msgcount;
currentmsg = 1;
console.log("LIST success with " + msgcount + " message(s)");
- client.quit();
+ client.retr(1);
} else {
@@ -104,8 +127,46 @@ client.on("list", function(status, msgcount, msgnumber, data, rawdata) {
}
});
+client.on("retr", function(status, msgnumber, data, rawdata) {
+
+ if (status === true) {
+
+ console.log("RETR success " + msgnumber + " with data " + data);
+ client.dele(msgnumber);
+
+ } else {
+
+ console.log("RETR failed for msgnumber " + msgnumber + " because " + rawdata);
+ client.quit();
+
+ }
+});
+
+client.on("dele", function(status, msgnumber, data, rawdata) {
+
+ if (status === true) {
+
+ console.log("DELE success for msgnumber " + msgnumber);
+
+ if (currentmsg < totalmsgcount) {
+
+ currentmsg += 1;
+ client.retr(currentmsg);
+
+ } else client.quit();
+
+ } else {
+
+ console.log("DELE failed for msgnumber " + msgnumber);
+ client.quit();
+
+ }
+});
+
+
client.on("quit", function(status, rawdata) {
+ client.removeAllListeners("close");
if (status === true) console.log("QUIT success");
else console.log("QUIT failed");
View
49 tests/tls.sh
@@ -0,0 +1,49 @@
+#!/bin/sh
+# Test script
+#
+# Copyright (C) 2011 by Ditesh Shashikant Gathani <ditesh@gathani.org>
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to deal
+# in the Software without restriction, including without limitation the rights
+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in
+# all copies or substantial portions of the Software.
+#
+# 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 AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+# THE SOFTWARE.
+
+print_title "tls.js"
+RANDOMID=$RANDOM
+
+print_test "Sending test message to $EMAIL (str: $RANDOMID)"
+OUTPUT=`./sendmail.sh -q 1 $EMAIL "subject with $RANDOMID" "body with $RANDOMID"`
+print_result 0 $OUTPUT
+
+print_test "Wrong port"
+OUTPUT=`node tls.js --username $USER --password $PASS --host $HOST --port $PORT --login off`;
+print_result 1 $OUTPUT
+
+print_test "No login"
+OUTPUT=`node tls.js --username $USER --password $PASS --host $HOST --port $TLSPORT --login off`;
+print_result 0 $OUTPUT
+
+print_test "Login only"
+OUTPUT=`node tls.js --username $USER --password $PASS --host $HOST --port $TLSPORT --login on`;
+print_result 0 $OUTPUT
+
+print_test "Login and message download"
+OUTPUT=`node tls.js --username $USER --password $PASS --host $HOST --port $TLSPORT --login on --download on`
+OUTPUT=`echo $OUTPUT | grep $RANDOMID`
+
+if [ $? -eq 1 ]; then OUTPUT="fail"; fi
+
+print_result 0 $OUTPUT
Please sign in to comment.
Something went wrong with that request. Please try again.