Online Book Reader

Home Category

Beautiful Code [158]

By Root 4998 0
deleting and copying mail; encryption, decryption and signature verification; and parsing multipart MIME messages.

The Service class is used by Cryptonite::Mail::Server to implement a server that receives serialized Cryptonite API calls and dispatches them to a Service object.

Serialization was initially achieved via SOAP calls, but the SOAP object parsing and handling added too much needless complexity and overhead. So, a simple home-brewed serialization scheme was implemented instead. (Seven years in, this looks like a really good move, judging from http://wanderingbarque.com/nonintersecting/2006/11/15/the-s-stands-for-simple and its comments.) This is the command dispatcher in Cryptonite::Mail::Server:

Code View: Scroll / Show All

package Cryptonite::Mail::Server;

use Net::Daemon;

use vars qw(@ISA);

use Cryptonite::Mail::Service;

@ISA = qw(Net::Daemon);

my $debug = 1;

my $cmail = new Cryptonite::Mail::Service;

sub process_request {

my $self = shift; my ($retcode, $input);

# Wrap in eval to catch timeout exception.

eval {

local $SIG{ALRM} = sub { die "Timed Out!\n" };

# Timeout after 2 minutes of no input.

my $timeout = 120;

my $previous_alarm = alarm($timeout);

while( ){

s/\r?\n$//;

# Get caller, command and cmd args.

my ($caller, $command, @args) = split /(? $debug ? $debug == 2 ? warn "$$: $_\n" :

warn "$$: $caller:$command:$args[0]\n" : '';

# Unescape arg separators in the stream.

for (@args) { s/(? return if $command =~ /^\s*quit\s*$/i;

# Validate command.

my $valid = $cmail->valid_cmd;

if ($command=~/$valid/x) {

# Call service method.

$ret = join ("\n", ($cmail->$command (@args), ''));

print STDOUT $ret;

}

else {

# Invalid command.

print STDOUT ($cmail->cluebat (ECOMMAND, $command) . "\n");

}

alarm($timeout);

}

alarm($previous_alarm);

};

if( $@=~/timed out/i ){

print STDOUT "Timed Out.\r\n";

return;

}

}

The Cryptonite Mail Daemon (cmaild) receives serialized method calls via Unix or TCP sockets, calls the method on the service object, and returns a result code (+OK or -ERR) along with a human-readable status message (e.g., "Everything is under control!") and optional return values (such as a list of messages in a folder, or the text of a message part). If multiple lines of return values are being returned, the status message indicates how many lines the client should expect to read.

The server forks a new process every time a new client connects, so Perl's built-in alarm function is used to send each new server process a SIGALRM $timeout seconds after the last message received from the client, which causes the server to time out and disconnect the client.

Secure Communication: The Technology Of Freedom > The Test Suite

11.5. The Test Suite

Because automated testing is a crucial component of long-term development, I developed a test suite simultaneously with the project code.

The clean separation of the core from the interface makes it easy to test both components separately, as well as to quickly diagnose bugs and pinpoint where they are in the code. Writing tests for cmaild is just a matter of calling its methods with valid (or invalid) inputs and making sure that the return codes and values are as expected.

The test suite for cmaild uses the client API calls cmdopen (to open a connection to the Cryptonite Mail Daemon), cmdsend (to send an API call to the daemon), and cmdayt (to send an "Are you there?" ping to the server):

Code View: Scroll / Show All

use strict;

use Test;

BEGIN { plan tests => 392, todo => [] }

use Cryptonite::Mail::HTML qw (&cmdopen &cmdsend &cmdayt);

$Test::Harness::Verbose = 1;

my ($cmailclient, $select, $sessionkey);

my ($USER, $CMAILID, $PASSWORD) = 'test';

my $a = $Cryptonite::Mail::Config::CONFIG{ADMINPW};

ok(sub { # 1: cmdopen

my $status;

($status, $cmailclient, $select) = cmdopen;

return $status unless $cmailclient;

1;

}, 1);

ok(sub

Return Main Page Previous Page Next Page

®Online Book Reader