Beautiful Code [161]
Even though some IMAP servers might have suited my needs, I also didn't want Cryptonite to be dependent on and tied down to the capabilities of any specific IMAP server implementation. All in all, this turned out to be a good decision, even though it did lead to a lot of effort being expended on code that was later demoted to a less central role in the system.
Mail store replication was hacked up using two Perl modules I wrote: Replication::Recall and DBD::Recall, which used Eric Newton's Recall replication framework (http://www.fault-tolerant.org/recall) to replicate databases across multiple servers. The idea was to use this as a prototype and to custom-build a new database replication system in the future.
With the encryption, database, and mail store backends revamped, and with a new, cleaner theme, the first internal beta of Cryptonite went online in October 2001. It was tested by many users of varying skill levels, some of whom even used it as their primary mail client. Usability testing during the internal beta indicated that novice users were able to successfully generate and import keys, and to send and read encrypted and signed messages without much trouble.
11.7.2. Persistence of Decryption
An essential feature for an encrypted mail client is the ability to keep decrypted messages available in decrypted form for the duration of the user's session. A secure mail client that lacks this facility can get very irritating and inefficient to use, as it would require typing in long passphrases and waiting for decryption every time you want to read an encrypted message or search within encrypted messages.
Persistence for previously decrypted messages in Cryptonite was accomplished by creating a new Mail::Folder class, based on Mail::Folder::SQL. Mail::Folder::Shadow would delegate mailbox accesses to a shadow folder if the message had a counterpart in the shadow folder; otherwise, it would access the underlying (or shadowed) folder.
By this means, decrypted messages could be kept in the shadow folder while a session was alive, and little modification of the code was necessary to add persistent decrypts, other than to plug in the Mail::Folder::Shadow module everywhere Mail::Folder::SQL was used. Mail::Folder::Shadow implements its magic with a simple, tweakable delegation table:
my %method =
qw (get_message 1 get_mime_message 1 get_message_file 1 get_header 1
get_mime_message 1 mime_type 1 get_mime_header 1 get_fields 1
get_header_fields 1 refile 1 add_label 2 delete_label 2
label_exists 2 list_labels 2 message_exists 1 delete_message 5
sync 2 delete 2 open 2 set_header_fields 2 close 2 DESTROY 2
get_mime_skeleton 1 get_body_part 1);
Mail::Folder::Shadow delegates method calls as appropriate to the shadow folder, the original shadowed folder, or to both. Perl's powerful AUTOLOAD feature, which provides a mechanism to handle methods that are not explicitly defined in a class, is a simple way to accomplish this delegation, while also providing a simple mechanism to tweak at runtime how different methods are handled.
Methods that have to check the shadow store, such as get_message and get_header, are delegated to the shadow if the message concerned exists in the shadow folder; otherwise, they are delegated to the original shadowed folder. Other methods, such as add_label and delete (which deletes a folder), need to be dispatched to both the shadow and the shadowed folder, as these messages must change the state of the original folder, as well as that of the shadow folder.
Yet other methods, such as delete_message, can accept a message list through an array reference. Some of the messages in the message list may be shadowed, and others may not. Mail::Folder::Shadow's AUTOLOAD handles such methods by building two lists from the