396 lines
12 KiB
Markdown
396 lines
12 KiB
Markdown
|
# node-poplib
|
||
|
|
||
|
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 encrypted TLS support
|
||
|
* STLS
|
||
|
* SASL PLAIN CRAM-MD5
|
||
|
|
||
|
It complies to:
|
||
|
|
||
|
* RFC 1939 (POP3)
|
||
|
* RFC 2595 (STLS);
|
||
|
* RFC 5034 (SASL AUTH)
|
||
|
* RFC 2195 (CRAM-MD5)
|
||
|
|
||
|
## Installation
|
||
|
|
||
|
You have two installation options:
|
||
|
|
||
|
0. Via npm: `npm install poplib`
|
||
|
|
||
|
1. Download the source and install it yourself
|
||
|
|
||
|
## Quick demo
|
||
|
|
||
|
Connect to GMail's POP3 servers using the provided demo script as follows:
|
||
|
|
||
|
````bash
|
||
|
$ node demos/demo.js --host pop.gmail.com --port 995 --username user@gmail.com --password potato --tls on --debug on --networkdebug on
|
||
|
Server: '+OK Gpop ready for requests from 1.2.3.4 bh7pf61475604pab.24\r\n'
|
||
|
CONNECT success
|
||
|
Client: 'USER user@gmail.com\r\n'
|
||
|
Server: '+OK send PASS\r\n'
|
||
|
Client: 'PASS potato\r\n'
|
||
|
Server: '-ERR [AUTH] Username and password not accepted.\r\n'
|
||
|
LOGIN/PASS failed
|
||
|
Client: 'QUIT\r\n'
|
||
|
Server: '+OK Farewell.\r\n'
|
||
|
QUIT success
|
||
|
````
|
||
|
|
||
|
## Detailed Usage
|
||
|
|
||
|
node-poplib is event based. It is best to illustrate via examples:
|
||
|
|
||
|
Here we initialize the client (for plain text transmission):
|
||
|
|
||
|
````javascript
|
||
|
var POP3Client = require("poplib");
|
||
|
var client = new POP3Client(port, host, {
|
||
|
|
||
|
tlserrs: false,
|
||
|
enabletls: true,
|
||
|
debug: false
|
||
|
|
||
|
});
|
||
|
````
|
||
|
|
||
|
The third parameter, `options`, takes three options. If `enabletls` is true, the library will use a TLS connection. Note that you will have to set the correct port (generally 995). If `tlserrs` is true, then TLS errors will be ignored. Finally, the `debug` parameter prints out requests and responses.
|
||
|
|
||
|
Next, we trap several common states:
|
||
|
|
||
|
````javascript
|
||
|
client.on("error", function(err) {
|
||
|
|
||
|
if (err.errno === 111) console.log("Unable to connect to server");
|
||
|
else console.log("Server error occurred");
|
||
|
|
||
|
console.log(err);
|
||
|
|
||
|
});
|
||
|
|
||
|
client.on("connect", function() {
|
||
|
|
||
|
console.log("CONNECT success");
|
||
|
client.login(username, password);
|
||
|
|
||
|
});
|
||
|
|
||
|
client.on("invalid-state", function(cmd) {
|
||
|
console.log("Invalid state. You tried calling " + cmd);
|
||
|
});
|
||
|
|
||
|
client.on("locked", function(cmd) {
|
||
|
console.log("Current command has not finished yet. You tried calling " + cmd);
|
||
|
});
|
||
|
````
|
||
|
|
||
|
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 `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).
|
||
|
|
||
|
On a successful connect, we try authenticating:
|
||
|
|
||
|
````javascript
|
||
|
client.on("connect", function() {
|
||
|
|
||
|
console.log("CONNECT success");
|
||
|
client.login(username, password);
|
||
|
|
||
|
});
|
||
|
````
|
||
|
|
||
|
Note that on successful login, 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("login", function(status, rawdata) {
|
||
|
|
||
|
if (status) {
|
||
|
|
||
|
console.log("LOGIN/PASS success");
|
||
|
client.list();
|
||
|
|
||
|
} else {
|
||
|
|
||
|
console.log("LOGIN/PASS failed");
|
||
|
client.quit();
|
||
|
|
||
|
}
|
||
|
});
|
||
|
|
||
|
// Data is a 1-based index of messages, if there are any messages
|
||
|
client.on("list", function(status, msgcount, msgnumber, data, rawdata) {
|
||
|
|
||
|
if (status === false) {
|
||
|
|
||
|
console.log("LIST failed");
|
||
|
client.quit();
|
||
|
|
||
|
} else {
|
||
|
|
||
|
console.log("LIST success with " + msgcount + " element(s)");
|
||
|
|
||
|
if (msgcount > 0)
|
||
|
client.retr(1);
|
||
|
else
|
||
|
client.quit();
|
||
|
|
||
|
}
|
||
|
});
|
||
|
|
||
|
client.on("retr", function(status, msgnumber, data, rawdata) {
|
||
|
|
||
|
if (status === true) {
|
||
|
|
||
|
console.log("RETR success for msgnumber " + msgnumber);
|
||
|
client.dele(msgnumber);
|
||
|
client.quit();
|
||
|
|
||
|
} else {
|
||
|
|
||
|
console.log("RETR failed for msgnumber " + msgnumber);
|
||
|
client.quit();
|
||
|
|
||
|
}
|
||
|
});
|
||
|
|
||
|
client.on("dele", function(status, msgnumber, data, rawdata) {
|
||
|
|
||
|
if (status === true) {
|
||
|
|
||
|
console.log("DELE success for msgnumber " + msgnumber);
|
||
|
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");
|
||
|
|
||
|
});
|
||
|
````
|
||
|
|
||
|
## 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
|
||
|
|
||
|
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"
|
||
|
````
|
||
|
|
||
|
You can execute the test-runner as follows:
|
||
|
|
||
|
````bash
|
||
|
./runner.sh username password pop3server pop3port pop3tlsport testemail@address.com
|
||
|
````
|