In addition, we can save a lot of resources by caching the information the server sends to the client, and only regenerating that information when needed. Say someone POPs every minute-- that's 1,440 POPs per day per user. Most POP servers will go through the mailbox and inspect every mail to create a UIDL list every time the user POPs. nupop, however, only rebuilds its list when the user has new mail, and the rest of the time uses a cache file. This makes the average pop session very effecient.
Download the latest tarball.
After initializing the build environment, you'll probably want to make some changes in the .h files. Sorry, but since nupop runs out of inetd, having it read and parse a configuration file seemed pretty wasteful. Here are the directives and files you may want to change:
|PAM_SERVICE_NAME||src/misc.h||nupop||The PAM service name|
|SYSLOGNAME||src/misc.h||nupop||syslog will prepend each line in the logfile with this|
|SYSLOGFACILITY||src/misc.h||LOG_LOCAL7||The logging facility to which the messages will be sent|
|FAKE_UID_MAX||src/fake_uid.h||60000||Emails with mtimes below this number go through the rewrite engine.|
|FAKE_UID_FILE||src/fake_uid.h||/Maildir/.fake_uids||The default filename where the fake uids are stored|
|MAILDIR_DIRECTORY||src/maildir.h||/Maildir||Use this if your maildir directory is called something else|
|POPUSERTABLE||private/virtservers.h||/etc/mail/popusertable.db||The location of the database used for user rewrites|
|virtservers||private/virtservers.h||..undefined..||A null terminated array of mappings of IP address to default domain|
|localdomains||private/virtservers.h||..undefined..||A list of domains whose usernames correspond exactly to real unix usernames|
|UID_MIN||src/main.c||99||Won't authenticate a user whose uid is lower than this|
|GID_MIN||src/main.c||99||Won't authenticate a user whose gid is lower than this|
|MAXHUNKS||src/maildir.h||50||Maximuim number of hunks (of emails) that will be malloc'd|
|HUNKSIZE||src/maildir.h||500||Number of emails in each hunk|
|AUTHORIZATION_TIMEOUT||src/main.c||15 sec||Max time that connection can sit idle in AUTH state|
|TRANSACTION_TIMEOUT||src/main.c||30 sec||Max time that connection can sit idle in TRANSACTION|
Set what you want, then make and make install.
Authentication: There is a single function called to authenticate the user. (This facilitates making a drop in replacement if your environment dictates.) After a user is authenticated, nupop does a setuid, setgid, and a chroot. This makes the string manipulation a lot easier in the actual function handlers.
Loading the emails: The MAILDIR_DIRECTORY/.statcache file is read if possible. If not, calc_mail_stats() stats all of the emails in new and cur, building the list of emails. The message list is then sorted, and mails with duplicate uids have their timestamps adjusted.
Retrieving an email: The file is mmapped, crlf's are added as needed (RFC822), and a uw-style "Status:" header is added. The RFC822 size of the output is printed to the client before the mail is sent. (see __send_file().)
Data structures: Each email is in a struct email; a hunk is an array of struct emails of size HUNKSIZE; which are indexed in array of size MAXHUNKS of pointers to hunks. (Basically it's one big array of emails split up into segments.) The cusor_reset, cursor_next, and cursor_seek functions set a global cursor pointer that lets you access any email you want by its message number.
After the data is calculated from the mailbox, it is sorted. This is done by creating an array of size nemails of pointers to struct emails. The list of pointers is sorted. If a sorted list exists, the cursor_next, cursor_reset, and cursor_seek functions will act on the sorted list transparently.
Saving the cache: The cache contains a version number, the timestamps on the new and cur directories, and all of the struct emails. If the list is sorted, then the cache will be output sorted and will not need to be resorted on a cache load. When the user gets new email the new and cur timestamps won't agree with the mailbox, and the cache will be discarded. Also note that the cache is _not_ written unless something has changed during the session, and the cache is deleted if there is an error during the session.