Net::Server::POP3 - The Server Side of the POP3 Protocol for email
use Net::Server::POP3; my $server = Net::Server::POP3->new( severopts => \%options, authenticate => \&auth, list => \&list, retrieve => \&retrieve, delete => \&delete, size => \&size, welcome => "Welcome to my mail server.", ); $server->startserver();
Net::Server::POP3 is intended to handle the nitty-gritty details of talking to mail clients, so that in writing a custom POP3 server you don't have to actually read RFC documents. The backend things (such as where mail comes from and what messages are in the user's mailbox at any given time) are left up to your code (or another module), but this module handles the POP3 protocol for you. Also, the details of listening for client connections and so on are handled by Net::Server.
This approach allows for some flexibility. Your code may choose to generate messages on the fly, proxy them from another mail server, retrieve them from a local maildir or mailbox of some kind, or whatever. See the sample scripts in this distribution for examples.
This code is still very much beta. There are known bugs. Some things (e.g., APOP) haven't even been implemented yet. You have been warned. See the Bugs section for details.
The code as it stands now works, for some definition of "works". With the included simpletest.pl script I have successfully served test messages that I have retrieved with Mozilla Mail/News. Additionally, with the included proxytest.pl script I have successfully proxied mail from an ISP mail server to a client. However, much remains to be done.
It is strongly recommended to run with Taint checking enabled.
These are the RFCs that I know about and intend to implement:
If you know of any other RFCs that seem pertinent, let me know.
This module is designed to be the server/daemon itself and so to handle all of the communication to/from the client(s). The actual details of obtaining, storing, and keeping track of messages are left to other modules or to the user's own code. (See the sample scripts simpletest.pl (simple) and proxytest.pl (somewhat more involved) in this distribution for examples.)
The main method is startserver(), which starts the server. The following named arguments may be passed either to new() or to startserver(). All callbacks should be passed as coderefs. If you pass an argument to new() and then pass an argument of the same name to startserver(), the one passed to startserver() overrides the one passed to new(). stopserver() has not been implemented yet and so neither has restartserver(), but they are planned for an eventual future version.
A string containing the characters that should be printed on a socket to cause perl to emit an RFC-compliant CRLF. The new default is "\015\012", which theoretically should work perfectly everywhere, from what I have read, but if you get mangled newlines, this may be why. This new default needs testing on as many platforms as possible to ensure that it is, in fact, correct everywhere.
The default used to be "\n", which worked on my development platform (Linux Mandrake 9.2). On some systems this needed to be set to "\r\n". Setting it to the wrong thing caused breakage either way. If the default doesn't work for you, experiment. If you set it to "\r\n" on systems where "\n" will work, you get extraneous ^M characters on the ends of all the lines at the recieving end, which among other things can cause most of the headers not to be recognized as such and instead to be displayed in the body, depending on the mail client. Going the other way, if you set it to "\n" on platforms where "\r\n" would work correctly, the whole thing may just not work at all, or other weirdness may ensue, such as the entire message (headers, body, and all) appearing on one line.
The EOL string is optional; you only need to specify it if the default is the wrong value.
The port number to listen on. 110 is the default. The user or group you are running as needs permission to listen on this port.
The port number is optional. You only need to specify it if you want to listen on a different port than 110.
A type of server implemented by Net::Server (q.v.) The default is 'Fork', which is suitable for installations with a small number of users.
The servertype is optional. You only need to specify it if you want to use a different type other than 'Fork'.
A hashref containing extra named arguments to pass to Net::Server. Particularly recommended for security reasons are user, group, and chroot. See the docs for Net::Server for more information.
The serveropts hashref is optional. You only need to supply it if you have optional arguments to pass through to Net::Server.
This callback, if supplied, will be called when a client connects. This is the recommended place to allocate resources such as a database connection handle.
The connect callback is optional; you only need to supply it if you have setup to do when a client connects.
This callback, if supplied, is called when the client disconnects. If there is any cleanup to do, this is the place to do it. Note that message deletion should not be handled here, but in the delete callback.
The disconnect callback is optional; you only need to supply it if you have cleanup to do when a client disconnects.
The authenticate callback is passed a username, password, and IP address. If the username and password are valid and the user is allowed to connect from that address and authenticate by the USER/PASS method, then the callback should try to get a changelock on the mailbox and return 1 if successful; it must return something other than 1 if any of that fails. (Returning 0 does not specify the details of what went wrong; other values may in future versions have particular meanings.)
The authenticate callback is technically optional, but you need to supply it if you want any users to be able to log in using the USER and PASS commands.
Optional callback for handling APOP auth. If the user attempts APOP auth and this callback exists, it will be passed the username, the digest sent by the user, and the server greeting. If the user's digest is indeed the MD5 digest of the concatenation of the server greeting and the shared secret for that user, then the callback should attempt to lock the mailbox and return true if successful; otherwise, return false.
The apop callback is only needed if you want to supply APOP authentication.
This is not implemented yet, but I plan to implement it in an eventual future version.
The list callback, given a valid, authenticated username, must return a list of message-ids of available messages. (Most implementations will ingore the username, since they will already be locked in to the correct mailbox after authentication. That's fine. The username is passed as a help for minimalist implementations.)
The list callback is required.
The size callback if it exists will be passed a valid, authenticated username and a message id (from the list returned by the list callback) and must return the message size in octets. If the size callback does not exist, the size will be calculated using the retrieve callback, which is inefficient. Providing the size callback will prevent the retrieve callback from being called unnecessarily, thus improving performance. (Most implementations will ingore the username, since they will already be locked in to the correct mailbox after authentication. That's fine. The username is passed as a help for minimalist implementations.)
Note that very early versions passed only the message id, not the username, to the size callback. This changed in 0.0005, breaking backward-compatibility for the size callback.
The size callback is optional. You only need to provide it if you care about performance.
The retrieve callback must accept a valid, authenticated username and a message-id (from the list returned by the list callback) and must return the message as a string. (Most implementations will ingore the username, since they will already be locked in to the correct mailbox after authentication. That's fine. The username is passed as a help for minimalist implementations.)
The retrieve callback is required.
The delete callback gets called with a valid, authenticated username and a message-id that the user/client has asked to delete. (Most implementations will ingore the username, since they will already be locked in to the correct mailbox after authentication. That's fine. The username is passed as a help for minimalist implementations.)
The delete callback is only called in cases where the POP3 protocol says the message should actually be deleted. If the connection terminates abnormally before entering the UPDATE state, the callback is not called, so code using this module does not need to concern itself with marking and unmarking for deletion. When called, it can do whatever it wants, such as actually delete the message, archive it permanently, mark it as no longer to be given to this specific user, or whatever.
This callback is technically optional, but you'll need to supply one if you want to know when to remove messages from the user's maildrop.
This string is used as the welcome string sent to the client upon connection. It must not be longer than 506 bytes, for arcane reasons involving RFC1939. (startserver will generate a warning at runtime if it is too long.)
The welcome string is optional; a default welcome is supplied.
If a number is given, it will be announced in the capabilities list as the minimum delay (in seconds) between successive logins by the same user (which applies to any user). This does NOT enforce the delay; it only causes it to be announced in the capabilities list. The authenticate callback is responsible for enforcement of the delay. The delay SHOULD be enforced if it is announced (RFC 2449).
If the delay may vary per user, logindelay should be a callback routine. If the callback is passed no arguments, it is being asked for the maximum delay for all users; if it is passed an argument, this will be a valid, authenticated username and the callback should return the delay for that particular user. Either way, the return value should be a number of seconds. Again, this does NOT enforce the delay; it only causes it to be announced in the capabilities list. (Some clients may not even ask for the capabilities list, if they do not implement POP3 Extensions (RFC 2449).)
The default is not to announce any particular delay.
If a number or the string 'NEVER' is given, it will be announced in the capabilities list as the length of time a message may remain on the server before it expires and may be automatically deleted by the server. (The number is a whole number of days.)
This does NOT actually delete anything; it just announces the timeframe to the client. Clients that do not support POP3 Extensions will not get this announcement. 'NEVER' means the server will never expire messages; 0 means that expiration is immanent and the client should not count on leaving messages on the server. 0 should be announced for example if the mere act of retrieving a message may cause it to expire shortly afterward.
If the message expiration time may vary by user, expiretime should be a callback routine. If the callback is passed no arguments, it is being asked for the minimum expiration time for all users, which it should return (as a whole number of days; 0 is acceptable); if it is passed an argument, this will be a valid, authenticated username and the callback should return the expiration time for this particular user, either as a whole number of days or the string 'NEVER'.
The default is not to announce an expiration time.
Set the level of debugging information desired on standard output. undef means no debug info at all. 0 means only warn when the client uses commands that are not understood. A value of 1 produces various other information about functions that are being called, arguments they are passed, and so on. A value of 2 also uses Data::Dumper to show the state of certain data structures at various times, possibly including entire messages, possibly more than once per message. This can get really verbose. A value of 3 is even more verbose and really doesn't add anything for debugging your code. (Level 3 is intended for debugging the module itself. Actually, all of it was mainly intended for that originally, but the lower levels also proved useful for debugging sample scripts.)
The DEBUG level is optional. The default is 0.
Give the mail client (or user) this many seconds to type or send each line. My reading of RFC1939 is that this shouldn't be less than ten minutes (at least, between commands), but Net::Server::POP3 does not enforce this minimum.
The linetimeout is optional. The default is currently 600 (ten minutes), the minimum specified by the RFC. The default value may change in a future version.
- DEBUG makes authentication fail
This is really bad, and I hope to fix it very soon. My apologies to anyone who has been bitten by this. The gplproxy example has uncovered a bug wherein passing a nonzero value for DEBUG to new (or probably also startserver) causes authentication to fail. Fixing this will be top priority for the next release.
- APOP is not implemented yet.
- stopserver and restartserver are not implemented
For now, the only way to stop the server is to kill it. Actually, this may not be true; I'm still investigating this stuff about the Net::Server module.
The UIDL implementation uses the message-id as the unique id, rather than calculating a hash as suggested by RFC 1939. In practice, this seems to be what my ISP's mail server does (it calls itself InterMail), which has worked with every client I've thrown at it, so it should be mostly okay, but it's not strictly up to spec I think and may be changed in a later version. I intend to investigate what other major POP3 servers do in this regard before making any changes; if you happen to know e.g. what the POP3 servers do that are usually used with Postfix, Exim, or Qmail, et cetera, drop me a line and let me know. Data about what the POP3 servers used by various ISPs do would also be appreciated.
The issue of thread safety has not even been considered, other than to include this warning that it has not been considered. If someone who actually has experience with threaded programming wants to look it over, that would be great; otherwise, I may try to get to it eventually, but for now it's several items down the Todo list.
- character handling
My code all assumes that each character is stored in one byte. I suspect most mail servers do this, but if your code that uses the module produces any Unicode strings, this could make issues. The sample proxy modules are naively assuming that Mail::POP3Client returns strings with octet symantics; I do not know whether this is actually the case. At minimum, this could cause the sizes of the messages to be reported incorrectly (e.g. by LIST).
- client IP address
The authenticate callback was not passed the client's IP address as documented, but I think this is fixed now.
- line endings
I think the new default value for EOL fixes this bug, but I'm leaving the old information here until I confirm that:
Depending on your platform and possibly your perl version, you might need to set the EOL to "\r\n" instead of the default "\n". However, if your perl version already handles this the way mine does (Linux Mandrake 9.2), setting it to "\r\n" will break it, resulting in the mail client only seeing the first header you send as a header and viewing the rest of the headers as part of the body, which is ugly; in that case you should use "\n". You can now pass an EOL parameter to new or to startserver for this, until I figure out how to fix it for real. The default is "\n" if you don't specify.
- Caveat user
There may be other bugs as well; this is not release-quality code yet. Significant changes may be made to the code interface before release quality is reached, so if you use this module now you may have to change your code when you upgrade.
The Todo list is long, and contributions are welcome, especially code but also documentation, sample scripts, or other information such as how the module works with various clients, what platforms and perl versions need which setting for EOL (and how to determine this at runtime), what POP3 servers do what for the UIDL, ...
Use the source, Luke. You can also contact the author with questions, but the code is supplied on an as-is basis with no warranty. I will try to answer any questions, but this is spare-time stuff for me.
Jonadab the Unsightly One (Nathan Eady) email@example.com http://www.bright.net/~jonadab/
This program is free software licensed under the terms of...
The BSD License
The full text of the license can be found in the LICENSE file included with this module.
Note that the modules (such as Net::Server) that are used by this module or by the sample scripts are covered under their respective license agreements and are not governed by the license of Net::Server::POP3.
perl(1) Net::Server http://search.cpan.org/search?query=Net::Server Mail::POP3Client http://search.cpan.org/search?query=Mail::POP3Client The simpletest.pl script included in the scripts directory in this distribution. The proxytest.pl script also included in the scripts directory in this distribution.
For a more minimalist framework with a different interface, see Net::Server::POP3::Skeleton