diff --git a/Makefile.am b/Makefile.am index 7586e902..c70a340e 100644 --- a/Makefile.am +++ b/Makefile.am @@ -28,7 +28,13 @@ else mu4e= endif -SUBDIRS=m4 man lib $(guile) mu $(mu4e) contrib toys +if BUILD_PERL +perl=perl +else +perl= +endif + +SUBDIRS=m4 man lib $(guile) mu $(mu4e) contrib toys $(perl) ACLOCAL_AMFLAGS=-I m4 diff --git a/configure.ac b/configure.ac index 609ffa88..e6d6bb4a 100644 --- a/configure.ac +++ b/configure.ac @@ -70,6 +70,20 @@ AS_IF([test "x$enable_mu4e" != "xno"], [ ]) AM_CONDITIONAL(BUILD_MU4E, test "x$build_mu4e" = "xyes") +# Perl interface requires Data::SExpression +build_perl=no +AC_ARG_ENABLE([perl], + AS_HELP_STRING([--enable-perl],[Enable building the Perl interface])) +AC_ARG_VAR([PERL], [the Perl interpreter command]) +AC_CHECK_PROGS([PERL], [perl], [no]) +AS_IF([test x"$enable_perl" != "xno" -a x"$PERL" != "xno"], [ + AM_PERL_MODULE([Data::SExpression],[build_perl=yes]) + if test x"$build_perl" = "xyes"; then + perl_version=`$PERL -Iperl/lib -Mmup -e 'print "$mup::VERSION\n";'` + fi +]) +AM_CONDITIONAL(BUILD_PERL, test "x$build_perl" = "xyes") + # we need some special tricks for filesystems that don't have d_type; # e.g. Solaris. See mu-maildir.c. Explicitly disabling it is for # testing purposes only @@ -269,6 +283,13 @@ contrib/Makefile ]) AC_OUTPUT +if test x"$build_perl" != "xno"; then + echo "Configuring Perl interface..." + cd perl + $PERL Makefile.PL + cd .. +fi + echo echo "mu configuration is complete." echo "------------------------------------------------" @@ -299,6 +320,10 @@ if test "x$build_mu4e" = "xyes"; then echo "Emacs version : $emacs_version" fi +AM_COND_IF([BUILD_PERL],[ +echo "Perl interface version : $perl_version" +]) + echo echo "Have wordexp : $ac_cv_header_wordexp_h" echo "Build mu4e emacs frontend : $build_mu4e" @@ -308,7 +333,6 @@ echo "Build 'mug' toy-ui (gtk+/webkit) : yes"],[ echo "Build 'mug' toy-ui (gtk+/webkit) : no" ]) - echo "McCabe's Cyclomatic Complexity tool : $have_pmccabe" echo diff --git a/m4/Makefile.am b/m4/Makefile.am index c22275b0..33a874dd 100644 --- a/m4/Makefile.am +++ b/m4/Makefile.am @@ -21,6 +21,5 @@ EXTRA_DIST = \ ltoptions.m4 \ ltversion.m4 \ lt~obsolete.m4 \ - ltsugar.m4 - - + ltsugar.m4 \ + perlmod.m4 diff --git a/perl/LICENSE b/perl/LICENSE new file mode 100644 index 00000000..75b94046 --- /dev/null +++ b/perl/LICENSE @@ -0,0 +1,15 @@ +Copyright (C) 2015 by attila + +Permission to use, copy, modify, and distribute this software for +any purpose with or without fee is hereby granted, provided that the +above copyright notice and this permission notice appear in all +copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL +WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE +AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL +DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR +PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER +TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +PERFORMANCE OF THIS SOFTWARE. diff --git a/perl/MANIFEST b/perl/MANIFEST new file mode 100644 index 00000000..814de6b7 --- /dev/null +++ b/perl/MANIFEST @@ -0,0 +1,19 @@ +lib/mup.pm +LICENSE +Makefile.PL +MANIFEST This list of files +README.md +t/001-basics.t +t/002-index.t +t/003-contacts.t +t/004-add.t +t/005-find.t +t/006-move.t +t/007-remove.t +t/008-view.t +t/009-extract.t +t/010-compose.t +t/lib.pm +t/sample.eml +t/sample.maildir/cur/1435599858.6310_1.tldr.l.stalphonsos.net +t/sample.maildir/new/1416411736.20539_44.geronimo.l.stalphonsos.net diff --git a/perl/Makefile.PL b/perl/Makefile.PL new file mode 100644 index 00000000..4f5403bf --- /dev/null +++ b/perl/Makefile.PL @@ -0,0 +1,31 @@ +# mu - perl interface to mu + +use ExtUtils::MakeMaker; +do "./lib/mup.pm"; + +sub SRC { "lib/@_" } +sub DST { '$(INST_LIBDIR)/'."@_" } +sub PAIR { ( SRC(@_) => DST(@_) ) } + +print STDERR "Welcome to mup $mup::VERSION\n\n"; +WriteMakefile( + AUTHOR => 'attila ', + ABSTRACT => 'perl interface to mu', + NAME => 'mup', + VERSION_FROM => 'lib/mup.pm', + PREREQ_PM => { + 'Data::SExpression' => 0, + 'Moose' => 0, + }, + PM => { + PAIR('mup.pm'), + } +); + +sub MY::postamble { + return <<__MyStuff__; + +check: + +__MyStuff__ +} diff --git a/perl/README.md b/perl/README.md new file mode 100644 index 00000000..892ac0e6 --- /dev/null +++ b/perl/README.md @@ -0,0 +1,31 @@ +# mup # + +mup is a perl interface to [mu](http://www.djcbsoftware.nl/code/mu/) +([GitHub](https://github.com/djcb/mu)), a Maildir indexing and +search system that also implements the core functionality needed +by pretty much any MUA. + +mup replicates the API described in the +[mu-server(1)](http://manpages.ubuntu.com/manpages/precise/man1/mu-server.1.html) man page in a pleasingly Perly style. It is licensed under the +[ISC license](http://en.wikipedia.org/ISC_licnse), a simplified +variant of the [BSD license](http://en.wikipedia.org/BSD_licenses). + +mup works in the same way the elisp code in mu4e does: it forks +a `mu-server` process and communicates with it. I use the +[Data::SExpression](http://search.cpan.org/~nelhage/Data-SExpression-0.41/lib/Data/SExpression.pm) CPAN module to deal with `mu-server`'s LISPy result syntax, +which we transform into the obvious hashrefian results our callers crave +(they've got electrolytes). + +## Tests ## + +I use standard perl testing stuff (`Test::More`). The tests all +operate on a temporary Maildir/mu index created by `t/lib.pm`. If you +are interested in hacking on or understanding the tests you should +first look at `t/lib.pm` to see how the temporary setup is created and +torn down. All tests should have + + use t::lib; + +in them somewhere near the top. This is all that is necessary to make +sure the code in the test does not e.g. hose down your actual +~/Maildir and/or ~/.mu. diff --git a/perl/lib/mup.pm b/perl/lib/mup.pm new file mode 100644 index 00000000..cf331ac4 --- /dev/null +++ b/perl/lib/mup.pm @@ -0,0 +1,863 @@ +#! perl + +=pod + +=head1 NAME + +mup - perl interface to mu + +=head1 SYNOPSIS + + use mup; + + my $mu = mup->new(); + + my $results = $mu->find({ subject => 'something'}); + print "$results->{found} results for subject:something\n"; + +=head1 DESCRIPTION + +This is a perl interface to mu, the Maildir search-and-destroy system. +It presents the same API as described in the L man page. +In fact it works by communicating with a C process, just +like the C emacs interface to mu does. + +=head1 METHODS + +All of the following methods take arguments named as described in the +L man page per each command, again either as a single +hashref argument or as a hash of pairs in-line. If there are any +doubts, make sure to read the L man page. Where +relevant any C argument defaults to C<~/Maildir> (not our +doing, that's just how C rolls). + +In order to stay agnostic with respect to the use our clients put us +to, all exported methods return plain, unblessed hashrefs as their +result. The shape of this hashref corresponds to the S-Expression +described in the L man page for each command. Since +everything that comes back from the server is represented as a list +(to us: array), we use a simple heuristic to determine if a an array +in the output is hashrefian or not: if it is of non-zero, even length +and if every even-numbered key is a symbol that starts with a colon we +consider the array hashrefian. In these cases we perform the obvious +transformation of stripping the leading colon and turning the thing +into a hashref. + +=cut + +package mup; +use strict; +use warnings; +use vars qw($VERSION); +use Data::SExpression; +use IO::Select; +use IPC::Open2; +use Moose; +use Time::HiRes; +use Data::Dumper; + +$VERSION = '0.1.0'; + +has 'dying' => ( + is => 'rw', + isa => 'Bool', + required => 1, + default => 0 +); +has 'dead' => ( + is => 'rw', + isa => 'Bool', + required => 1, + default => 0 +); +has 'pid' => ( + is => 'rw', + isa => 'Int', + required => 1, + default => 0 +); +has 'in' => ( + is => 'rw' +); +has 'out' => ( + is => 'rw' +); +has 'tout' => ( + is => 'rw', + isa => 'Num', + default => 0.5, + required => 1, +); +has 'orig_tout' => ( + is => 'rw', + isa => 'Num', + default => 0.5, + required => 1, +); +has 'select' => ( + is => 'ro', + isa => 'Object', + default => sub { IO::Select->new() }, + required => 1, +); +has 'inbuf' => ( + is => 'rw', + isa => 'Str', + default => '', + required => 1, +); +has 'ds' => ( + is => 'ro', + isa => 'Object', + default => sub { + Data::SExpression->new({ fold_alists => 1, use_symbol_class => 1}) + }, + required => 1, +); +has 'max_tries' => ( + is => 'rw', + isa => 'Int', + default => 0, + required => 1, +); +has 'mu_bin' => ( + is => 'rw', + isa => 'Str', + default => 'mu', + required => 1, +); +has 'mu_server_cmd' => ( + is => 'rw', + isa => 'Str', + default => 'server', + required => 1, +); +has 'verbose' => ( + is => 'rw', + isa => 'Bool', + default => 0, + required => 1, +); +has 'bufsiz' => ( + is => 'rw', + isa => 'Int', + default => 2048, + required => 1, +); +has 'cur_cmd' => ( + is => 'rw', + isa => 'Str', + default => '', + required => 1, +); +has 'maildir' => ( + is => 'rw', + isa => 'Str', + default => $ENV{'MAILDIR'} || '', + required => 1, +); +has 'mu_home' => ( + is => 'rw', + isa => 'Str', + default => '', + required => 1, +); +has 'debug' => ( + is => 'rw', + isa => 'Bool', + default => 0, + required => 1, +); +has 'update_callback' => ( + is => 'rw', + default => undef, + required => 0, +); +has 'no_updates' => ( + is => 'rw', + isa => 'Bool', + default => 0, + required => 1, +); + +sub _init { + my $self = shift(@_); + my($in,$out); + # The only way I know of to tell mu server what Maildir to use is + # the MAILDIR environment variable. If our caller specifies a + # maildir, set the envvar before we fork the server process. + if ($self->maildir) { + $ENV{'MAILDIR'} = $self->maildir; + warn("mup: setting MAILDIR=".$self->maildir."\n") if $self->verbose; + } + # Opposite logic here... a bit confusing: The testing code + # (c.f. t/lib.pm) wants to point us at a different .mu directory + # than the default (~/.mu); normally you don't want to do this + # but if we see a special envar ($MUP_MU_HOME) then set mu_home + # to this value - mu_home defaults to ''. In any event, if + # mu_home is set somehow, obey it, otherwise let mu use its + # default. + if ($ENV{'MUP_MU_HOME'}) { + $self->mu_home($ENV{'MUP_MU_HOME'}); + warn("mup: set --muhome ".$self->mu_home."\n") if $self->verbose; + } + # Same for MUP_MU_BIN + if ($ENV{'MUP_MU_BIN'}) { + $self->mu_bin($ENV{'MUP_MU_BIN'}); + warn("mup: set mu_bin to ".$self->mu_bin."\n") if $self->verbose; + } + my @cmdargs = ($self->mu_bin,$self->mu_server_cmd); + push(@cmdargs, "--muhome=".$self->mu_home) if $self->mu_home; + warn("mup: mu server cmd: @cmdargs\n") if $self->verbose; + my $pid = open2($out,$in,@cmdargs); + $self->orig_tout($self->tout); + $self->pid($pid); + $self->out($out); + $self->in($in); + $self->select->add($out); + my $junk = $self->_read(); + warn("mup: _init junk: $junk\n") if $self->verbose; + return $self; +} + +sub BUILD { shift->_init(); } + +sub _cleanup { + my($self) = @_; + if ($self->pid) { + warn("mup: reaping mu server pid ".$self->pid."\n") if $self->verbose; + waitpid($self->pid,0); + $self->pid(0); + } + if ($self->inbuf) { + warn("mup: restart pitching inbuf: |".$self->inbuf."|\n") + if $self->verbose; + $self->inbuf(''); + } +} + +sub restart { + my($self) = @_; + $self->_cleanup(); +} + +sub reset { + my($self) = @_; + $self->_reset_parser(); + return $self; +} + +sub _read { + my($self) = @_; + my $restart_needed = 0; + my @ready = $self->select->can_read($self->tout); + while (@ready && !$restart_needed) { + foreach my $handle (@ready) { + my $buf = ''; + my $nread = $handle->sysread($buf,$self->bufsiz); + if (!$nread) { + warn("mup: mu server died - restarting") if $self->verbose(); + $restart_needed = 1; + } else { + $self->inbuf($self->inbuf . $buf); + warn("mup: <<< |$buf|\n") if $self->verbose; + } + } + @ready = $self->select->can_read($self->tout) + unless $restart_needed; + } + my $result = $self->inbuf; + $self->_cleanup() if ($self->dying || $restart_needed); + $self->_init() if $restart_needed && !$self->dying; + return $result; +} + +sub _reset_parser { +} + +sub _parse { + my($self,$in_update) = @_; + $in_update ||= $self->no_updates; + my($tries,$max_tries) = (0,$self->max_tries); + INCOMPLETE: + my $raw = $self->inbuf; + return undef unless $raw; + my($xcount,$left) = ($1,$2) if $raw =~ /^\376([\da-f]+)\377(.*)$/s; + my $count = hex($xcount); + my $nleft = length($left); + warn("mup: count=$count length=$nleft: |$left|\n") + if $self->verbose; + if ($count > $nleft) { + ++$tries; + die("mup: FATAL: waiting for $count, tried $tries, only got $nleft") + if ($max_tries && $tries >= $max_tries); + warn("mup: short buffer, reading more ($tries)...\n") + if $self->verbose; + $self->_read(); + goto INCOMPLETE; + } + chomp(my $sexp = substr($left,0,$count)); + $self->inbuf(substr($left,$count)); + my $data = $self->ds->read($sexp); + return undef unless defined($data); + warn("mup: parsed sexp: $data\n") if $self->verbose; + my $href = $self->_hashify($data); + if (!$in_update && $self->update_callback && $self->inbuf =~ /:update/) { + # We have an update callback. We have an update. + # Peanut butter, meet chocolate. + local $Data::Dumper::Terse = 1; + local $Data::Dumper::Indent = 0; + my $upd = $self->_parse(1); + warn("mup: update: ".Dumper($upd)."\n") if $self->verbose; + unless ($upd && exists($upd->{'update'})) { + die("mup: next msg was not pending update !? ".Dumper($upd)."\n"); + } + &{$self->update_callback}($upd); + } + return $href; + +} + +# turn a LISPy keyword into a perlier hashref key + +sub _delispify { + my $key = shift(@_); + $key = "$1" if "$key" =~ /^:(.*)$/; + $key =~ s/-/_/g; + return $key; +} + +# turn a perly argument name into a lispy one + +sub _lispify { + my $key = shift(@_); + $key =~ s/_/-/g; + return $key; +} + +# _hashify - turn raw Data::SExpression result into canonical hashref + +sub _hashify { + my($self,$thing) = @_; + my $rthing = ref($thing); + my $result = $thing; + warn("mup: rthing=$rthing: $thing\n") if $self->debug; + return $result unless $rthing; + if ($rthing eq 'Data::SExpression::Symbol') { + # nil is undef, t is 1, everything else becomes a string + if ($thing eq 'nil') { + $result = undef; + } elsif ($thing eq 't') { + $result = 1; + } else { + $result = "$thing"; + } + } elsif ($rthing eq 'ARRAY') { + my $count = scalar(@$thing); + my $looks_hashrefian = $count && !($count & 1); + if ($looks_hashrefian) { + # Non-null array with even cardinality: check for hashrefian keys + my $i; + for ($i = 0; $i < $count; $i += 2) { + my $elt = $thing->[$i]; + my $relt = ref($elt); + last if (($relt && $relt eq 'Data::SExpression::Symbol') && + "$elt" !~ /^:/); + } + $looks_hashrefian = ($i < $count) ? 0 : 1; + if ($self->debug) { + local $Data::Dumper::Terse = 1; + local $Data::Dumper::Indent = 0; + warn("mup: looks_hashrefian=$looks_hashrefian: ".Dumper($thing)."\n"); + } + } + if (!$looks_hashrefian) { + # just a plain old array + $result = [ map { $self->_hashify($_) } @$thing ]; + } else { + # hashref in array's clothing + $result = {}; + while (scalar(@$thing)) { + my($key,$val) = splice(@$thing,0,2); + $key = _delispify($key); + { no strict 'vars'; + warn("mup: ARRAY key=$key val=(".ref($val).") |$val|\n") + if $self->debug; + } + $result->{$key} = $self->_hashify($val); + } + } + } elsif ($rthing eq 'HASH') { + # xxx can this happen? Data::SExpression says so but mu will + # probably never send us one... + $result = {}; + foreach my $key (keys(%$thing)) { + my $val = $thing->{$key}; + $key = _delispify($key); + { no strict 'vars'; + warn("mup: HASH key=$key val=(".ref($val).") |$val|\n") + if $self->debug; + } + $result->{$key} = $self->_hashify($val); + } + } + return $result; +} + +=pod + +=over 4 + +=item * new (verbose => 1|0, ... other options... ) + +Construct a new interface object; this will cause a C +process to be started. + +Options can be specified Moose-style, either as a hashref +or as a hash of pairs: + +=over 4 + +=item * verbose + +If non-zero we spew debug output to C via L. + +=item * tout + +Timeout in seconds for responses from L. The +Can be fractional. The default is C<0.5> (500 msec). + +=item * bufsiz + +Buffer size for reads from the server. Default is 2048. + +=item * max_tries + +Max number of times we will try to read from the server to complete +a single transaction. By default this is zero, which means no limit. + +=item * mu_bin + +Name of the C binary to use to start the server. + +=item * mu_server_cmd + +C subcommand used to start the server. + +=item * mu_home + +Directory for Mu to use to store the Xapian database and other +Mu-specific files. Defaults to C<~/.mu>. + +=item * maildir + +Root of the C tree that L should operate on. Defaults +to whatever the C<$MAILDIR> environment variable is set to or +C<~/Maildir> if it is not set. + +=item * debug + +If non-zero additional debug output will be spewed via L, mainly +related to the transformation of L objects into +hashrefs. This output is spewed independently of the value of C. + +=back + +=back + +=cut + + +=pod + +=over 4 + +=item * finish + +Shut down the mu server and clean up. + +=back + +=cut + +sub finish { + my($self) = @_; + if ($self->pid) { + $self->dying(1); + $self->_send("cmd:quit"); + my $junk = $self->_read(); + warn("mup: trailing garbage in finish: |$junk|\n") if $self->verbose; + } + return 1; +} + +sub DEMOLISH { shift->finish(); } + +sub _refify { + my $href = ((@_ == 1) && (ref($_[0]) eq 'HASH')) ? $_[0] : { @_ }; + return { map { _lispify($_) => $href->{$_} } keys(%$href) }; +} + +sub _quote { + my($val) = @_; + $val = qq|"$val"| if (!ref($val) && $val =~ /\s/); + $val; +} + +sub _argify { + my $self = shift(@_); + my $href = _refify(@_); + if (exists($href->{'timeout'})) { + $self->tout($href->{'timeout'}); + warn("mup: tout ".$self->orig_tout." => ".$self->tout."\n") + if $self->verbose; + delete($href->{'timeout'}); + } + return join(' ', map { "$_:"._quote($href->{$_}) } keys(%$href)); +} + +sub _send { + my($self,$str) = @_; + $self->in->write("$str\n"); + $self->in->flush(); + return $self; +} + +sub _execute { + my($self,$cmd,@args) = @_; + my $args = $self->_argify(@args); + my $cmdstr = "cmd:$cmd $args"; + warn("mup: >>> $cmdstr\n") if $self->verbose; + if ($self->inbuf) { + my $junk = $self->inbuf; + warn("mup: pitching |$junk|\n") if $self->verbose; + } + $self->inbuf(''); + $self->cur_cmd($cmd); + $self->_send($cmdstr); + $self->_read(); + $self->tout($self->orig_tout); + return $self->_parse(); +} + +=pod + +=over 4 + +=item * add (path => "/path/to/file", maildir => "/my/Maildir") + +Add a message (document) to the database. If C is not +specified the right thing is filled in. + +=back + +=cut + +sub add { + my $self = shift(@_); + my $argref = _refify(@_); + $argref->{'maildir'} = $self->_our_maildir() unless $argref->{'maildir'}; + $self->_execute('add',$argref); +} + + + +=pod + +=over 4 + +=item * compose (type => 'reply|forward|edit|new', docid => $docid) + +Compose a message, either in regard to an existing one (in which case +you must specify C) or as a new message. + +=back + +=cut + +sub compose { shift->_execute('compose',@_); } + + + +=pod + +=over 4 + +=item * contacts (personal => 1|0, after => $epoch_time) + +Search contacts. + +=back + +=cut + +sub contacts { shift->_execute('contacts',@_); } + + + +=pod + +=over 4 + +=item * extract (action => 'save|open|temp', index => $index, path => $path, what => $what, param => $param) + +Save a message into a file. + +=back + +=cut + +sub extract { shift->_execute('extract',@_); } + + + +=pod + +=over 4 + +=item * find (query => $mu_query, threads => 1|0, sortfield => $field, reverse => 1|0, maxnum => $max_results) + +Search the message Xapian database. + +=back + +=cut + +sub _next { + my($self) = @_; + my $href = $self->_parse(); + unless ($href) { + $self->_read(); + $href = $self->_parse(); + } + return $href; +} + +sub find { + my($self,@args) = @_; + my $results = { 'found' => 0, 'results' => [] }; + my $resp = $self->_execute('find',@args); + return undef unless $resp; + die("mup: protocol broken!? missing (:erase t)") unless $resp->{'erase'}; + my $result = $self->_next(); + while ($result) { + if (exists($result->{'found'})) { + $results->{'found'} = int($result->{'found'}); + $result = undef; + } elsif (!exists($result->{'docid'})) { + local $Data::Dumper::Terse = 1; + local $Data::Dumper::Indent = 0; + warn("mup: unexpected find result w/no docid: ".Dumper($result)); + $result = undef; + } else { + push(@{$results->{'results'}}, $result); + $result = $self->_next(); + } + } + return $results; +} + + + +=pod + +=over 4 + +=item * index (path => $path, my-addresses: 'me,and,mine', callback => $sub) + +(Re)index the messagebase. The mu server updates us with progress +every 500 messages. By default we only return the final result +after all indexing has occurred but if the caller wants to e.g. update +a progress meter or something it can pass us a special C +argument that is invoked with every progress report given to us by +the server. + +=back + +=cut + +sub default_maildir { $ENV{'HOME'} . '/Maildir' }; + +sub _our_maildir { + return shift->maildir || $ENV{'MAILDIR'} || default_maildir(); +} + +sub index { + my $self = shift(@_); + my $argref = _refify(@_); + $argref->{'path'} ||= $self->_our_maildir(); + my $cb = $argref->{'callback'}; + delete($argref->{'callback'}) if $cb; + # The index command is special. Unlike the others, we don't + # necessarily send a command and get back a single response. + # Instead we may get back a series of responses, one for each + # 500 messages indexed. Only the last one will be marked with + # 'status' => 'complete', so wait for that and swallow the rest. + my $href = $self->_execute('index',$argref); + return undef unless $href; + while ($href && $href->{'status'} ne 'complete') { + my($status,$pr,$up,$cl) = + map { $href->{$_} } qw(status processed updated cleaned_up); + warn("mup: index $status: $pr processed, $up updated, $cl cleaned\n") + if $self->verbose; + &$cb($href) if $cb; + $self->_read(); + my $tmp = $href; + do { + $tmp = $self->_parse(); # can call _read() + &$cb($tmp) if $cb; + $href = $tmp if $tmp; + } while ($tmp && $tmp->{'status'} ne 'complete'); + } + return $href; +} + + + +=pod + +=over 4 + +=item * mkdir (path => $path) + +Make a new maildir under your Maildir basedir. + +=back + +=cut + +sub mkdir { + my $self = shift(@_); + my $argref = _refify(@_); + # Make path relative to our Maildir unless it is absolute + my $path = $argref->{'path'}; + die("mup: path is required") unless $path; + $path = join("/",$self->_our_maildir(),$path) unless $path =~ /^\//; + warn("mup: mkdir ".$argref->{'path'}." => mkdir $path\n") + if $self->verbose; + $argref->{'path'} = $path; + $self->_execute('mkdir',$argref); +} + + +=pod + +=over 4 + +=item * move ( docid => $docid | msgid => $msgid, maildir => $path, flags => $flags) + +Move a message from one maildir folder to another. + +=back + +=cut + +sub move { + my $self = shift(@_); + my $argref = _refify(@_); + # Unlike mkdir's path arg, our maildir arg must start with a slash + my $maildir = $argref->{'maildir'}; + $argref->{'maildir'} = "/$maildir" unless $maildir =~ /^\//; + $self->no_updates(1); + my $result = $self->_execute('move',$argref); + $self->no_updates(0); + return $result; +} + +=pod + +=over 4 + +=item * ping () + +Ping the server to make sure it is alive. + +=back + +=cut + +sub ping { shift->_execute('ping',@_); } + + + +=pod + +=over 4 + +=item * remove (docid => $docid) + +Remove a message by document ID. + +=back + +=cut + +sub remove { shift->_execute('remove',@_); } + + + +=pod + +=over 4 + +=item * view ( docid => $docid | msgid => $msgid | path => $path, extract_images => 1|0, use_agent => 1|0, auto_retrieve_key => 1|0) + +Return a canonicalized view of a message, optionally with images +and/or cryptography (PGP) dealt with. The message can be specified by +C, C or as a path to a file containing the message. + +=back + +=cut + +sub view { shift->_execute('view',@_); } + +######################################################################## + +no Moose; +__PACKAGE__->meta->make_immutable; + +1; + +__END__ + +=pod + +=head1 SEE ALSO + +L, L + +=head1 AUTHOR + +attila + +=head1 LICENSE + +Copyright (C) 2015 by attila + +Permission to use, copy, modify, and distribute this software for any +purpose with or without fee is hereby granted, provided that the above +copyright notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL +WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE +AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL +DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR +PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER +TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +PERFORMANCE OF THIS SOFTWARE. + +=cut + +## +# Local variables: +# mode: perl +# tab-width: 4 +# perl-indent-level: 4 +# cperl-indent-level: 4 +# cperl-continued-statement-offset: 4 +# indent-tabs-mode: nil +# comment-column: 40 +# End: +## diff --git a/perl/t/001-basics.t b/perl/t/001-basics.t new file mode 100644 index 00000000..e6fd6012 --- /dev/null +++ b/perl/t/001-basics.t @@ -0,0 +1,47 @@ +#!/usr/bin/perl +## +# 001-basics.t - basic mup tests +## +# Copyright (C) 2015 by attila +# +# Permission to use, copy, modify, and distribute this software for any +# purpose with or without fee is hereby granted, provided that the above +# copyright notice and this permission notice appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL +# WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED +# WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE +# AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL +# DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR +# PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER +# TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +# PERFORMANCE OF THIS SOFTWARE. +## +use strict; +use warnings; +use Test::More tests => 4; +use Data::Dumper; +use mup; + +use t::lib; + +# basic tests: constructor, ping, finish + +my $mu = mup->new(verbose => $ENV{'TEST_VERBOSE'}); +ok($mu,"constructor won: $mu"); +my $p = $mu->ping(timeout => 2); +ok($p,"ping returned:".Dumper($p)); +my $p2 = $mu->ping(); +ok($p2,"ping2 returned:".Dumper($p2)); +ok($mu->finish(),"finish won"); + +## +# Local variables: +# mode: perl +# tab-width: 4 +# perl-indent-level: 4 +# perl-continued-statement-offset: 4 +# indent-tabs-mode: nil +# comment-column: 40 +# End: +## diff --git a/perl/t/002-index.t b/perl/t/002-index.t new file mode 100644 index 00000000..72bb4dcd --- /dev/null +++ b/perl/t/002-index.t @@ -0,0 +1,53 @@ +#!/usr/bin/perl +## +# 002-index.t - try the index command +## +# Copyright (C) 2015 by attila +# +# Permission to use, copy, modify, and distribute this software for any +# purpose with or without fee is hereby granted, provided that the above +# copyright notice and this permission notice appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL +# WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED +# WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE +# AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL +# DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR +# PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER +# TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +# PERFORMANCE OF THIS SOFTWARE. +## +use strict; +use warnings; +use Data::Dumper; +use mup; +use Test::More tests => 4; + +use t::lib; + +sub update_status { + my($href) = @_; + return unless $ENV{'TEST_VERBOSE'}; + local $Data::Dumper::Terse = 1; # cheesy + local $Data::Dumper::Indent = 0; + warn("$0: update_status: ".Dumper($href)."\n"); +} + +my $mu = mup->new(verbose => $ENV{'TEST_VERBOSE'}); +ok($mu,"constructor won"); +my $i = $mu->index(); +ok($i,"index won: ".Dumper($i)); +my $j = $mu->index(callback => \&update_status); +ok($j,"index w/callback won"); +ok($mu->finish(),"finish won"); + +## +# Local variables: +# mode: perl +# tab-width: 4 +# perl-indent-level: 4 +# perl-continued-statement-offset: 4 +# indent-tabs-mode: nil +# comment-column: 40 +# End: +## diff --git a/perl/t/003-contacts.t b/perl/t/003-contacts.t new file mode 100644 index 00000000..d3614d54 --- /dev/null +++ b/perl/t/003-contacts.t @@ -0,0 +1,55 @@ +#!/usr/bin/perl +## +# 003-contacts.t - exercise 'contacts' command +## +# Copyright (C) 2015 by attila +# +# Permission to use, copy, modify, and distribute this software for any +# purpose with or without fee is hereby granted, provided that the above +# copyright notice and this permission notice appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL +# WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED +# WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE +# AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL +# DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR +# PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER +# TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +# PERFORMANCE OF THIS SOFTWARE. +## +use strict; +use warnings; +use mup; +use Test::More tests => 10; + +use t::lib; + +my $mu = mup->new(verbose => $ENV{'TEST_VERBOSE'}); +ok($mu,"constructor won"); +my $c = $mu->contacts; +ok(ref($c) eq 'HASH',"contacts returned a hashref"); +ok(ref($c->{'contacts'}) eq 'ARRAY',"contacts returned an array"); +my @clist = map { $_->{'mail'} } @{$c->{'contacts'}}; +ok(scalar(@clist) == 5,"right number of contacts in array".join(", ",@clist)); +sub have { + my($a,@l) = @_; + my $n = grep { $_ eq $a } @l; + ok($n, "$a is in the list"); +} +have('tech@openbsd.org',@clist); +have('habeus@stalphonsos.com',@clist); +have('guenther@gmail.com',@clist); +have('mark.kettenis@xs4all.nl',@clist); +have('slashdot@newsletters.slashdot.org',@clist); +ok($mu->finish(),"finish won"); + +## +# Local variables: +# mode: perl +# tab-width: 4 +# perl-indent-level: 4 +# perl-continued-statement-offset: 4 +# indent-tabs-mode: nil +# comment-column: 40 +# End: +## diff --git a/perl/t/004-add.t b/perl/t/004-add.t new file mode 100644 index 00000000..2177361b --- /dev/null +++ b/perl/t/004-add.t @@ -0,0 +1,55 @@ +#!/usr/bin/perl +## +# 004-add.t - try the 'add' command +## +# Copyright (C) 2015 by attila +# +# Permission to use, copy, modify, and distribute this software for any +# purpose with or without fee is hereby granted, provided that the above +# copyright notice and this permission notice appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL +# WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED +# WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE +# AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL +# DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR +# PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER +# TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +# PERFORMANCE OF THIS SOFTWARE. +## +use strict; +use warnings; +use mup; +use Test::More tests => 3; +use Cwd qw(abs_path); +use Data::Dumper; + +use t::lib; + +sub up { + return unless $ENV{'TEST_VERBOSE'}; + my($upd) = @_; + local $Data::Dumper::Terse = 1; + local $Data::Dumper::Indent = 0; + warn("004-add.t: update: ".Dumper($upd)."\n"); +} + +die("004-add.t: where is my t/sample.eml?") unless -f "t/sample.eml"; +my $sample = abs_path("t/sample.eml"); + +my $mu = mup->new(verbose => $ENV{'TEST_VERBOSE'}, update_callback => \&up); +ok($mu,"constructor won"); +my $a = $mu->add(path => $sample); +ok($a,"add won: ".Dumper($a)); +ok($mu->finish(),"finish won"); + +## +# Local variables: +# mode: perl +# tab-width: 4 +# perl-indent-level: 4 +# perl-continued-statement-offset: 4 +# indent-tabs-mode: nil +# comment-column: 40 +# End: +## diff --git a/perl/t/005-find.t b/perl/t/005-find.t new file mode 100644 index 00000000..af684faa --- /dev/null +++ b/perl/t/005-find.t @@ -0,0 +1,48 @@ +#!/usr/bin/perl +## +# 005-find.t - exercise the 'find' command +## +# Copyright (C) 2015 by attila +# +# Permission to use, copy, modify, and distribute this software for any +# purpose with or without fee is hereby granted, provided that the above +# copyright notice and this permission notice appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL +# WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED +# WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE +# AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL +# DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR +# PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER +# TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +# PERFORMANCE OF THIS SOFTWARE. +## +use strict; +use warnings; +use mup; +use Test::More tests => 8; + +use t::lib; + +my $mu = mup->new(verbose => $ENV{'TEST_VERBOSE'}); +ok($mu,"constructor won"); +my $f = $mu->find(query => "openbsd"); +ok($f,"find returned something"); +ok($f->{'found'} == 1,"found the right number of messages"); +ok(scalar(@{$f->{'results'}}) == 1,"count of results agrees"); +ok($f->{'results'}->[0]->{'subject'} eq 'Re: C-state FFH on x41',"found the right one"); +$f = $mu->find(query => "supercalifragilistic"); +ok($f,"query worked"); +ok($f->{'found'} == 0,"and found nothing, as expected"); +ok($mu->finish(),"finish won"); + +## +# Local variables: +# mode: perl +# tab-width: 4 +# perl-indent-level: 4 +# perl-continued-statement-offset: 4 +# indent-tabs-mode: nil +# comment-column: 40 +# End: +## diff --git a/perl/t/006-move.t b/perl/t/006-move.t new file mode 100644 index 00000000..0c82803c --- /dev/null +++ b/perl/t/006-move.t @@ -0,0 +1,50 @@ +#!/usr/bin/perl +## +# 006-move.t - move messages between folders +## +# Copyright (C) 2015 by attila +# +# Permission to use, copy, modify, and distribute this software for any +# purpose with or without fee is hereby granted, provided that the above +# copyright notice and this permission notice appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL +# WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED +# WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE +# AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL +# DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR +# PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER +# TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +# PERFORMANCE OF THIS SOFTWARE. +## +use strict; +use warnings; +use mup; +use Test::More tests => 7; +use Data::Dumper; + +use t::lib; + +my $mu = mup->new(verbose => $ENV{'TEST_VERBOSE'}); +ok($mu,"constructor won"); +my $f = $mu->find(query => "openbsd"); +ok($f,"find returned something"); +ok($f->{'found'} > 0,"found something"); +my $d = $f->{'results'}->[0]->{'docid'}; +my $m = $mu->mkdir(path => 'foo'); +ok($m,"mkdir returned something"); +my $v = $mu->move(docid => $d, maildir => 'foo'); +ok($v,"move returned something"); +ok(exists($v->{'update'}),"move result looks right"); +ok($mu->finish(),"finish won"); + +## +# Local variables: +# mode: perl +# tab-width: 4 +# perl-indent-level: 4 +# perl-continued-statement-offset: 4 +# indent-tabs-mode: nil +# comment-column: 40 +# End: +## diff --git a/perl/t/007-remove.t b/perl/t/007-remove.t new file mode 100644 index 00000000..75c2b2e7 --- /dev/null +++ b/perl/t/007-remove.t @@ -0,0 +1,47 @@ +#!/usr/bin/perl +## +# 007-remove.t - test removing messages +## +# Copyright (C) 2015 by attila +# +# Permission to use, copy, modify, and distribute this software for any +# purpose with or without fee is hereby granted, provided that the above +# copyright notice and this permission notice appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL +# WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED +# WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE +# AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL +# DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR +# PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER +# TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +# PERFORMANCE OF THIS SOFTWARE. +## +use strict; +use warnings; +use mup; +use Test::More tests => 6; + +use t::lib; + +my $mu = mup->new(verbose => $ENV{'TEST_VERBOSE'}); +ok($mu,"constructor won"); +my $f = $mu->find(query => "openbsd"); +ok($f,"find returned something"); +ok($f->{'found'} > 0,"found something"); +my $d = $f->{'results'}->[0]->{'docid'}; +my $r = $mu->remove(docid => $d); +ok($r,"remove returned something"); +ok($r->{'remove'} eq $d,"and it even looks right"); +ok($mu->finish(),"finish won"); + +## +# Local variables: +# mode: perl +# tab-width: 4 +# perl-indent-level: 4 +# perl-continued-statement-offset: 4 +# indent-tabs-mode: nil +# comment-column: 40 +# End: +## diff --git a/perl/t/008-view.t b/perl/t/008-view.t new file mode 100644 index 00000000..0132820a --- /dev/null +++ b/perl/t/008-view.t @@ -0,0 +1,44 @@ +#!/usr/bin/perl +## +# 008-view.t - test the 'view' command +## +# Copyright (C) 2015 by attila +# +# Permission to use, copy, modify, and distribute this software for any +# purpose with or without fee is hereby granted, provided that the above +# copyright notice and this permission notice appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL +# WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED +# WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE +# AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL +# DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR +# PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER +# TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +# PERFORMANCE OF THIS SOFTWARE. +## +use strict; +use warnings; +use mup; +use Test::More tests => 5; + +use t::lib; + +my $mu = mup->new(verbose => $ENV{'TEST_VERBOSE'}); +ok($mu,"constructor won"); +my $v = $mu->view(docid => 1); +ok($v,"view returned something"); +ok(exists($v->{'view'}),"view has a view element"); +ok($v->{'view'}->{'docid'} == 1,"it has the right docid"); +ok($mu->finish(),"finish won"); + +## +# Local variables: +# mode: perl +# tab-width: 4 +# perl-indent-level: 4 +# perl-continued-statement-offset: 4 +# indent-tabs-mode: nil +# comment-column: 40 +# End: +## diff --git a/perl/t/009-extract.t b/perl/t/009-extract.t new file mode 100644 index 00000000..748c9c46 --- /dev/null +++ b/perl/t/009-extract.t @@ -0,0 +1,56 @@ +#!/usr/bin/perl +## +# 008-extract.t - test the 'extract' command +## +# Copyright (C) 2015 by attila +# +# Permission to use, copy, modify, and distribute this software for any +# purpose with or without fee is hereby granted, provided that the above +# copyright notice and this permission notice appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL +# WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED +# WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE +# AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL +# DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR +# PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER +# TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +# PERFORMANCE OF THIS SOFTWARE. +## +use strict; +use warnings; +use mup; +use Test::More tests => 8; +use Cwd qw(abs_path); +use File::Temp qw/ :POSIX /; + +use t::lib; + +die("009-extract.t: where is my t/sample.eml?") unless -f "t/sample.eml"; +my $sample = abs_path("t/sample.eml"); +my $tmpx = tmpnam(); +END { unlink($tmpx) if -f $tmpx; } + +my $mu = mup->new(verbose => $ENV{'TEST_VERBOSE'}); +ok($mu,"constructor won"); +my $a = $mu->add(path => $sample); +ok($a,"add seems to have won"); +my $id = $a->{'docid'}; +ok($id,"new email has docid $id"); +ok(!(-f $tmpx),"temp file does not yet exist"); +my $x = $mu->extract(docid => $id,path => $tmpx,action => 'save',index => 1); +ok($x,"extract returned something"); +ok($x->{'info'} eq 'save',"looks right"); +ok(-f $tmpx,"looks like extraction worked"); +ok($mu->finish(),"finish won"); + +## +# Local variables: +# mode: perl +# tab-width: 4 +# perl-indent-level: 4 +# perl-continued-statement-offset: 4 +# indent-tabs-mode: nil +# comment-column: 40 +# End: +## diff --git a/perl/t/010-compose.t b/perl/t/010-compose.t new file mode 100644 index 00000000..313d5deb --- /dev/null +++ b/perl/t/010-compose.t @@ -0,0 +1,55 @@ +#!/usr/bin/perl +## +# 008-compose.t - test the 'compose' command +## +# Copyright (C) 2015 by attila +# +# Permission to use, copy, modify, and distribute this software for any +# purpose with or without fee is hereby granted, provided that the above +# copyright notice and this permission notice appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL +# WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED +# WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE +# AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL +# DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR +# PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER +# TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +# PERFORMANCE OF THIS SOFTWARE. +## +use strict; +use warnings; +use mup; +use Test::More tests => 9; +use Cwd qw(abs_path); + +use t::lib; + +die("010-compose.t: where is my t/sample.eml?") unless -f "t/sample.eml"; +my $sample = abs_path("t/sample.eml"); + +my $mu = mup->new(verbose => $ENV{'TEST_VERBOSE'}); +ok($mu,"constructor won"); +my $a = $mu->add(path => $sample); +ok($a,"add seems to have won"); +my $id = $a->{'docid'}; +ok($id,"new email has docid $id"); +my $c = $mu->compose(type => 'reply', docid => $id); +ok($c,"compose returned something"); +my @k = sort keys(%$c); +ok(scalar(@k) == 3,"right number of keys"); +ok($k[0] eq 'compose',"first is compose"); +ok($k[1] eq 'include',"second is include"); +ok($k[2] eq 'original',"third is original"); +ok($mu->finish(),"finish won"); + +## +# Local variables: +# mode: perl +# tab-width: 4 +# perl-indent-level: 4 +# perl-continued-statement-offset: 4 +# indent-tabs-mode: nil +# comment-column: 40 +# End: +## diff --git a/perl/t/lib.pm b/perl/t/lib.pm new file mode 100644 index 00000000..f618ce78 --- /dev/null +++ b/perl/t/lib.pm @@ -0,0 +1,97 @@ +#! perl +# Copyright (C) 2015 by attila +# +# Permission to use, copy, modify, and distribute this software for any +# purpose with or without fee is hereby granted, provided that the above +# copyright notice and this permission notice appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL +# WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED +# WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE +# AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL +# DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR +# PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER +# TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +# PERFORMANCE OF THIS SOFTWARE. + +package main; +use File::Path qw(make_path remove_tree); + +# ensure a testworthy Maildir is available and pointed at by $ENV{MAILDIR} + +sub setup_testing_envars { + if (-x '../../mu/mu') { + $ENV{'MUP_MU_BIN'} = '../../mu/mu'; + } + if (-x '../mu/mu') { + $ENV{'MUP_MU_BIN'} = '../mu/mu'; + } + warn("MUP_MU_BIN is $ENV{MUP_MU_BIN}\n") if $ENV{'TEST_VERBOSE'}; +} + +sub setup_testing_maildir { + my $verbose = $ENV{'TEST_VERBOSE'}; + if ($ENV{'MAILDIR'}) { + warn("setup_testing_maildir: MAILDIR is already ".$ENV{'MAILDIR'}."\n") + if $verbose; + return; + } + my $tmpd = $ENV{'TMPDIR'} || '/tmp'; + my $md = $tmpd . "/mup_t_maildir.$$"; + my $cmd; + # 1. ensure the test maildir did not exist first and then create it + die("setup_testing_maildir: $md exists!") if (-d $md || -f $md); + make_path($md) or die("setup_testing_maildir: mkdir $md: $!"); + $ENV{'MAILDIR'} = $md; + $ENV{'MUP_TEST_MAILDIR_SET'} = 1; + warn("setup_testing_maildir: $md\n") if $verbose; + # 2. populate it with some test data + if (!(-d "t/sample.maildir")) { + warn("setup_testing_maildir: no t/sample.maildir available!\n") + if $verbose; + } else { + my $tar = $ENV{'MUP_TEST_TAR'} || 'tar'; + my $xf = $verbose ? 'xvf' : 'xf'; + $cmd = qq{sh -c '(cd t/sample.maildir; ${tar} -cf - .) | (cd $md; ${tar} -$xf -)'}; + system($cmd) == 0 or die("seutp_testing_maildir: $cmd: $!"); + } + # 3. (optional) set up .mu under the maildir unless we are told not to + unless ($ENV{'MUP_MU_HOME'}) { + my $muhome = "$md/.mu"; + make_path($muhome) or die("setup_testing_maildir: $md/.mu: $!"); + $ENV{'MUP_MU_HOME'} = $muhome; + warn("seting_testing_maildir: $muhome\n") if $verbose; + my $mu_opts = $verbose ? '-d --log-stderr ' : ''; + my $bin = $ENV{'MUP_MU_BIN'} || 'mu'; + my $cmd = qq{$bin index ${mu_opts}--muhome=${muhome}}; + warn("indexing: $cmd\n") if $verbose; + system($cmd) == 0 or die("setup_testing_maildir: $cmd: $!"); + } +} + +sub setup_testing_env { + setup_testing_envars; + setup_testing_maildir; +} + +END { + remove_tree($ENV{'MAILDIR'},{ verbose => $ENV{'TEST_VERBOSE'} }) + if ($ENV{'MAILDIR'} && $ENV{'MUP_TEST_MAILDIR_SET'} && + !$ENV{'MUP_TEST_KEEP_MAILDIR'}); +} + +setup_testing_env unless $ENV{'MUP_TEST_NO_SETUP'}; + +1; + +## +# Local variables: +# mode: perl +# tab-width: 4 +# perl-indent-level: 4 +# cperl-indent-level: 4 +# cperl-continued-statement-offset: 4 +# indent-tabs-mode: nil +# comment-column: 40 +# End: +## diff --git a/perl/t/sample.eml b/perl/t/sample.eml new file mode 100644 index 00000000..0f093b35 --- /dev/null +++ b/perl/t/sample.eml @@ -0,0 +1,319 @@ +From: attila +Content-Transfer-Encoding: 7bit +Subject: [PATCH] Man pages: usbd_open_pipe(9), usbd_close_pipe(9) +MIME-Version: 1.0 +Content-Type: multipart/mixed; boundary="=-=-=" +To: tech@openbsd.org +Sender: attila +X-Mailer: flail 1.0.2 - http://flail.org +Date: Sat, 02 May 2015 08:44:11 CDT +Return-Path: +0: + +--=-=-= +Content-Type: text/plain + +Hi tech@, + +This patch adds man pages for usbd_open_pipe, usbd_open_pipe_intr, +usbd_close_pipe and usbd_abort_pipe, done as two files: +usbd_open_pipe.9 and usbd_close_pipe.9. It also adds these two new .9 +files to the appropriate Makefile and tweaks usbd_transfer(9) to refer +to usbd_open_pipe(9). + +Comments, feedback most welcome. + +Pax, -A + +P.S. I f'ing love mandoc. Just sayin... +-- +attila@stalphonsos.com | http://trac.haqistan.net/~attila +keyid E6CC1EDB | 4D91 1B98 A210 1D71 2A0E AC29 9677 D0A6 E6CC 1EDB + + +--=-=-= +Content-Type: text/x-patch +Content-Disposition: inline; filename=usbd_man_pages.diff +Content-Description: man pages: usbd_open_pipe(9), usbd_close_pipe(9) + +Index: Makefile +=================================================================== +RCS file: /cvs/src/share/man/man9/Makefile,v +retrieving revision 1.230 +diff -u -p -r1.230 Makefile +--- Makefile 10 Feb 2015 21:56:08 -0000 1.230 ++++ Makefile 2 May 2015 00:07:16 -0000 +@@ -31,7 +31,7 @@ MAN= aml_evalnode.9 atomic_add_int.9 ato + tsleep.9 spl.9 startuphook_establish.9 \ + socreate.9 sosplice.9 style.9 syscall.9 systrace.9 sysctl_int.9 \ + task_add.9 tc_init.9 time.9 timeout.9 tvtohz.9 uiomove.9 uvm.9 \ +- usbd_transfer.9 \ ++ usbd_transfer.9 usbd_open_pipe.9 usbd_close_pipe.9 \ + vfs.9 vfs_busy.9 \ + vfs_cache.9 vaccess.9 vclean.9 vcount.9 vdevgone.9 vfinddev.9 vflush.9 \ + vflushbuf.9 vget.9 vgone.9 vhold.9 vinvalbuf.9 vnode.9 vnsubr.9 \ +Index: usbd_close_pipe.9 +=================================================================== +RCS file: usbd_close_pipe.9 +diff -N usbd_close_pipe.9 +--- /dev/null 1 Jan 1970 00:00:00 -0000 ++++ usbd_close_pipe.9 2 May 2015 00:07:16 -0000 +@@ -0,0 +1,59 @@ ++.\" $OpenBSD$ ++.\" ++.\" Copyright (c) 2015 Sean Levy ++.\" ++.\" Permission to use, copy, modify, and distribute this software for any ++.\" purpose with or without fee is hereby granted, provided that the above ++.\" copyright notice and this permission notice appear in all copies. ++.\" ++.\" THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES ++.\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF ++.\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ++.\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES ++.\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ++.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF ++.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ++.\" ++.Dd $Mdocdate$ ++.Dt USBD_CLOSE_PIPE 9 ++.Os ++.Sh NAME ++.Nm usbd_close_pipe , usbd_abort_pipe ++.Nd close or abort transfers on a USB pipe ++.Sh SYNOPSIS ++.In dev/usb/usb.h ++.In dev/usb/usbdi.h ++.Ft usbd_status ++.Fn usbd_close_pipe "struct usbd_pipe *pipe" ++.Ft usbd_status ++.Fn usbd_abort_pipe "struct usbd_pipe *pipe" ++.Sh DESCRIPTION ++A pipe is a logical connection between the host and an endpoint ++on a USB device, created by one of ++.Xr usbd_open_pipe 9 ++or ++.Xr usbd_open_pipe_intr 9 . ++.Pp ++The ++.Fn usbd_abort_pipe ++function aborts any transfers queued on the pipe and ensures it is quiescent ++before returning. ++.Pp ++The ++.Fn usbd_close_pipe ++function first calls ++.Fn usbd_abort_pipe , ++then removes the pipe from the relevant USB interface's list of pipes ++and cleans up any memory associated with the pipe, including any ++implicit transfer created by ++.Xr usbd_open_pipe_intr 9 . ++.Sh CONTEXT ++.Fn usbd_abort_pipe ++and ++.Fn usbd_close_pipe ++can be called during autoconf, from process context or from interrupt ++context. ++.Sh SEE ALSO ++.Xr usbd_open_pipe 9 , ++.Xr usb 4 , ++.Xr intro 4 +Index: usbd_open_pipe.9 +=================================================================== +RCS file: usbd_open_pipe.9 +diff -N usbd_open_pipe.9 +--- /dev/null 1 Jan 1970 00:00:00 -0000 ++++ usbd_open_pipe.9 2 May 2015 00:07:16 -0000 +@@ -0,0 +1,162 @@ ++.\" $OpenBSD$ ++.\" ++.\" Copyright (c) 2015 Sean Levy ++.\" ++.\" Permission to use, copy, modify, and distribute this software for any ++.\" purpose with or without fee is hereby granted, provided that the above ++.\" copyright notice and this permission notice appear in all copies. ++.\" ++.\" THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES ++.\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF ++.\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ++.\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES ++.\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ++.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF ++.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ++.\" ++.Dd $Mdocdate$ ++.Dt USBD_OPEN_PIPE 9 ++.Os ++.Sh NAME ++.Nm usbd_open_pipe , usbd_open_pipe_intr ++.Nd create USB pipe ++.Sh SYNOPSIS ++.In dev/usb/usb.h ++.In dev/usb/usbdi.h ++.Ft usbd_status ++.Fn usbd_open_pipe "struct usbd_interface *iface" "u_int8_t address" "u_int8_t flags" "struct usbd_pipe **pipe" ++.Ft usbd_status ++.Fn usbd_open_pipe_intr "struct usbd_interface *iface" "u_int8_t address" "u_int8_t flags" "struct usbd_pipe **pipe" "void *priv" "void *buffer" "u_int32_t len" "usbd_callback cb" "int ival" ++.Sh DESCRIPTION ++The ++.Fn usbd_open_pipe ++and ++.Fn usbd_open_pipe_intr ++functions create pipes. ++A pipe is a logical connection between the host and an endpoint on a ++USB device. ++USB drivers use pipes to manage transfers to or from a USB ++endpoint. ++.Pp ++The ++.Fn usbd_open_pipe ++function takes the following arguments: ++.Bl -tag -width callback ++.It Fa iface ++the USB interface for which the pipe is to be created. ++.It Fa address ++The endpoint in that interface to which the pipe should be connected. ++.It Fa flags ++A bitmask of flags. Currently there is only one flag bit defined: ++.Bl -tag -width xxx -offset indent ++.It Dv USBD_EXCLUSIVE_ACCESS ++Do not allow other pipes to use this endpoint on this interface ++while this pipe exists. ++.El ++.It Fa pipe ++A pointer to where the resulting ++.Ql struct usbd_pipe * ++should be stored if the call is successful. ++.El ++.Pp ++The ++.Fn usbd_open_pipe_intr ++takes the following arguments: ++.Bl -tag -width callback ++.It Fa iface ++The USB interface for which the pipe is to be created. ++.It Fa address ++The endpoint in that interface to which the pipe should be connected. ++.It Fa flags ++A bitmask of flags. These flags are not interpreted in the same ++way as the ++.Fa flags ++passed to ++.Fn usbd_open_pipe . ++Instead, ++.Fn usbd_open_pipe_intr ++implicitly turns on the ++.Dv USBD_EXCLUSIVE_ACCESS ++bit for the pipe, disallowing multiple interrupt pipes for ++the same endpoint. The ++.Fa flags ++argument in this case is instead passed directly to ++.Xr usbd_setup_xfer 9 ++as its ++.Fa flags ++argument, whose interpretation is documented in ++its man page. ++.It Fa pipe ++A pointer to where the resulting ++.Ql struct usbd_pipe * ++should be stored if the call is successful. ++.It Fa priv ++A pointer to a private cookie untouched by the USB stack for reuse in ++the callback specified by the ++.Fa cb ++argument. ++.It Fa buffer ++A pointer to the data buffer for use by the implicit transfer ++(see below). ++.It Fa len ++The length in bytes of ++.Fa buffer . ++.It Fa cb ++A callback invoked every time the interrupt transfer completes. ++.It Fa ival ++The interval in milliseconds with which the interrupt pipe ++should be polled by the USB stack. ++.El ++.Pp ++Pipes created by ++.Fn usbd_open_pipe_intr ++implicitly have a repeating transfer queued on them which ++is run every ++.Fa ival ++milliseconds. ++This implicit transfer is not automatically removed from the list of ++transfers maintained by the pipe, unlike normal transfers, and will ++continue to be processed every ++.Fa ival ++milliseconds. ++.Pp ++These functions return ++.Dv USBD_NORMAL_COMPLETION ++when they are successful; in both cases ++.Ql *pipe ++will contain the newly created pipe. ++Both functions can return ++.Dv USBD_IN_USE ++if the interface/address pair already has a pipe associated with it; ++in the case of ++.Fn usbd_open_pipe ++this happens only if ++.Dv USBD_EXCLUSIVE_ACCESS ++is turned on in ++.Fa flags . ++For ++.Fn usbd_open_pipe_intr ++this flag is always assumed, so any attempt to create multiple ++pipes to the same interrupt endpoint on the same interface will ++return ++.Dv USBD_IN_USE. ++Both functions can also return ++.Dv USBD_NOMEM ++if they fail to allocate memory for any reason. ++In addition, ++.Fn usbd_open_pipe_intr ++can return any error code returned by ++.Xr usbd_setup_xfer 9 ++or ++.Xr usbd_transfer 9 . ++.Sh CONTEXT ++.Fn usbd_open_pipe ++and ++.Fn usbd_open_pipe_intr ++can be called during autoconf, from process context, ++or from interrupt context. ++.Sh SEE ALSO ++.Xr usbd_transfer 9 , ++.Xr usbd_close_pipe 9 , ++.Xr usb 4 , ++.Xr intro 4 +Index: usbd_transfer.9 +=================================================================== +RCS file: /cvs/src/share/man/man9/usbd_transfer.9,v +retrieving revision 1.5 +diff -u -p -r1.5 usbd_transfer.9 +--- usbd_transfer.9 12 Jul 2014 16:07:06 -0000 1.5 ++++ usbd_transfer.9 2 May 2015 00:07:16 -0000 +@@ -31,7 +31,10 @@ + .Fn usbd_transfer "struct usbd_xfer *xfer" + .Sh DESCRIPTION + These functions provide a controller independent mechanism to perform USB +-data transfers. ++data transfers. They make use of a pipe created by ++.Xr usbd_open_pipe 9 ++or ++.Xr usbd_open_pipe_intr 9 . + .Pp + The function + .Fn usbd_setup_xfer +@@ -104,6 +107,8 @@ if + has not been passed via + .Fa flags . + .Sh SEE ALSO ++.Xr usbd_open_pipe 9 , ++.Xr usbd_open_pipe_intr 9 , + .Xr ehci 4 , + .Xr ohci 4 , + .Xr uhci 4 , + +--=-=-=-- diff --git a/perl/t/sample.maildir/cur/1435599858.6310_1.tldr.l.stalphonsos.net b/perl/t/sample.maildir/cur/1435599858.6310_1.tldr.l.stalphonsos.net new file mode 100644 index 00000000..1ec84096 --- /dev/null +++ b/perl/t/sample.maildir/cur/1435599858.6310_1.tldr.l.stalphonsos.net @@ -0,0 +1,200 @@ +Return-Path: +Delivered-To: attila@stalphonsos.com +Received: (qmail 37697 invoked from network); 28 Jun 2015 22:20:10 -0000 +Received: from unknown (HELO shear.ucar.edu) (192.43.244.163) + by x.stalphonsos.net with SMTP; 28 Jun 2015 22:20:10 -0000 +Received: from openbsd.org (localhost [127.0.0.1]) + by shear.ucar.edu (8.14.7/8.14.7) with ESMTP id t5SMJRQT000203; + Sun, 28 Jun 2015 16:19:27 -0600 (MDT) +Received: from mail-pa0-f51.google.com (mail-pa0-f51.google.com [209.85.220.51]) + by shear.ucar.edu (8.14.7/8.14.7) with ESMTP id t5SMJGwf030092 + (version=TLSv1/SSLv3 cipher=DHE-DSS-AES128-SHA bits=128 verify=FAIL) + for ; Sun, 28 Jun 2015 16:19:17 -0600 (MDT) +Received: by padev16 with SMTP id ev16so94981036pad.0 + for ; Sun, 28 Jun 2015 15:19:16 -0700 (PDT) +DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; + d=gmail.com; s=20120113; + h=date:from:to:cc:subject:in-reply-to:message-id:references + :user-agent:mime-version:content-type; + bh=4q12s9+CtyHZEDQlC5XvVo3VnQHQCA5Vt6JwqbU9Adc=; + b=UVS7CgpSvUmIQMP7SOv8c1FO5cj2765fmTE2FwHVGkUahh/tuqT1uQGa7tfG7/1vbL + y4FPwhokPb+Q8mLKI3qIEfdgmQzhq1QcT6kkvNrRe7UuybMQhDJKwZuPAd7KcaS1kigX + GMS8P3DxB3PMoNOKoYUomd5X4Xm4MT/68ifl+4g1WiWD5BkLdqG5G8MaFo3B6W0E+h+1 + XNNK2B4BSJF28WswgAXzEshV4Sr/Uju0a5I/1sh3QYkZ3XCZRJeQpd14je0O5Z9ivj3P + 3EW+CQZmiStJ+A9CCsMhGUoMFhJFcbZ6LDd8bJKkcsGPZ85zmFfcUubODSA/lRtkW6Gi + RtkA== +X-Received: by 10.70.48.68 with SMTP id j4mr25375231pdn.111.1435529956338; + Sun, 28 Jun 2015 15:19:16 -0700 (PDT) +Received: from the-guenther.attlocal.net (76-253-1-113.lightspeed.sntcca.sbcglobal.net. [76.253.1.113]) + by mx.google.com with ESMTPSA id v8sm40045232pdm.89.2015.06.28.15.19.14 + (version=TLSv1.2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); + Sun, 28 Jun 2015 15:19:15 -0700 (PDT) +Date: Sun, 28 Jun 2015 15:19:13 -0700 +From: Philip Guenther +X-X-Sender: guenther@morgaine.local +To: Mark Kettenis +cc: tech@openbsd.org +Subject: Re: C-state FFH on x41 +In-Reply-To: <201506281537.t5SFbZs6031688@glazunov.sibelius.xs4all.nl> +Message-ID: +References: <201506281537.t5SFbZs6031688@glazunov.sibelius.xs4all.nl> +User-Agent: Alpine 2.20 (BSO 67 2015-01-07) +MIME-Version: 1.0 +Content-Type: text/plain; charset=US-ASCII +List-Help: +List-ID: +List-Owner: +List-Post: +List-Subscribe: +List-Unsubscribe: +X-Loop: tech@openbsd.org +Precedence: list +Sender: owner-tech@openbsd.org + +On Sun, 28 Jun 2015, Mark Kettenis wrote: +> I have an x41 that prints the following in dmesg: +> +> acpicpu0 at acpi0 +> C1: unknown FFH vendor 8: !C3(250@85 io@0x1015), C2(500@1 io@0x1014), PSS +> +> The relevant AML indead has that strange value in the "Bit Width" field: +... +> Obviously this ACPI BIOS is buggy and perhaps we should indeed +> complain. But we also should include a C1 HALT state in the list +> regardless, and I don't think we currently do that. + +Give the diff below a try. It inserts a fallback "C1-via-halt" state +before parsing the _CST objects, overwriting it if a valid _CST C1 state +is found. If no valid _CST C1 was found, the fallback should show in +dmesg as "C1(@1 halt!)". + +As a side-benefit, this renders a couple paranoia checks obsolete in +acpicpu_idle(). + + +Index: dev/acpi/acpicpu.c +=================================================================== +RCS file: /data/src/openbsd/src/sys/dev/acpi/acpicpu.c,v +retrieving revision 1.64 +diff -u -p -r1.64 acpicpu.c +--- dev/acpi/acpicpu.c 13 Jun 2015 21:41:42 -0000 1.64 ++++ dev/acpi/acpicpu.c 28 Jun 2015 21:55:51 -0000 +@@ -77,7 +77,7 @@ void acpicpu_setperf_ppc_change(struct a + /* flags on Intel's FFH mwait method */ + #define CST_FLAG_MWAIT_HW_COORD 0x1 + #define CST_FLAG_MWAIT_BM_AVOIDANCE 0x2 +-#define CST_FLAG_UP_ONLY 0x4000 /* ignore if MP */ ++#define CST_FLAG_FALLBACK 0x4000 /* fallback for broken _CST */ + #define CST_FLAG_SKIP 0x8000 /* state is worse choice */ + + #define FLAGS_MWAIT_ONLY 0x02 +@@ -339,7 +339,13 @@ acpicpu_add_cstate(struct acpicpu_softc + dnprintf(10," C%d: latency:.%4x power:%.4x addr:%.16llx\n", + state, latency, power, address); + +- cx = malloc(sizeof(*cx), M_DEVBUF, M_WAITOK); ++ /* add a new state, or overwrite the fallback C1 state? */ ++ if (state != ACPI_STATE_C1 || ++ (cx = SLIST_FIRST(&sc->sc_cstates)) == NULL || ++ (cx->flags & CST_FLAG_FALLBACK) == 0) { ++ cx = malloc(sizeof(*cx), M_DEVBUF, M_WAITOK); ++ SLIST_INSERT_HEAD(&sc->sc_cstates, cx, link); ++ } + + cx->state = state; + cx->method = method; +@@ -347,8 +353,6 @@ acpicpu_add_cstate(struct acpicpu_softc + cx->latency = latency; + cx->power = power; + cx->address = address; +- +- SLIST_INSERT_HEAD(&sc->sc_cstates, cx, link); + } + + /* Found a _CST object, add new cstate for each entry */ +@@ -489,9 +493,18 @@ acpicpu_getcst(struct acpicpu_softc *sc) + if (aml_evalname(sc->sc_acpi, sc->sc_devnode, "_CST", 0, NULL, &res)) + return (1); + ++ /* provide a fallback C1-via-halt in case _CST's C1 is bogus */ ++ acpicpu_add_cstate(sc, ACPI_STATE_C1, CST_METH_HALT, ++ CST_FLAG_FALLBACK, 1, -1, 0); ++ + aml_foreachpkg(&res, 1, acpicpu_add_cstatepkg, sc); + aml_freevalue(&res); + ++ /* only have fallback state? then no _CST objects were understood */ ++ cx = SLIST_FIRST(&sc->sc_cstates); ++ if (cx->flags & CST_FLAG_FALLBACK) ++ return (1); ++ + /* + * Scan the list for states that are neither lower power nor + * lower latency than the next state; mark them to be skipped. +@@ -500,19 +513,15 @@ acpicpu_getcst(struct acpicpu_softc *sc) + * Also keep track if all the states we'll use use mwait. + */ + use_nonmwait = 0; +- if ((cx = SLIST_FIRST(&sc->sc_cstates)) == NULL) +- use_nonmwait = 1; +- else { +- while ((next_cx = SLIST_NEXT(cx, link)) != NULL) { +- if ((cx->power >= next_cx->power && +- cx->latency >= next_cx->latency) || +- (cx->state > 1 && +- (sc->sc_ci->ci_feature_tpmflags & TPM_ARAT) == 0)) +- cx->flags |= CST_FLAG_SKIP; +- else if (cx->method != CST_METH_MWAIT) +- use_nonmwait = 1; +- cx = next_cx; +- } ++ while ((next_cx = SLIST_NEXT(cx, link)) != NULL) { ++ if ((cx->power >= next_cx->power && ++ cx->latency >= next_cx->latency) || ++ (cx->state > 1 && ++ (sc->sc_ci->ci_feature_tpmflags & TPM_ARAT) == 0)) ++ cx->flags |= CST_FLAG_SKIP; ++ else if (cx->method != CST_METH_MWAIT) ++ use_nonmwait = 1; ++ cx = next_cx; + } + if (use_nonmwait) + sc->sc_flags &= ~FLAGS_MWAIT_ONLY; +@@ -581,8 +590,12 @@ acpicpu_print_one_cst(struct acpi_cstate + if (cx->power != -1) + printf("%d", cx->power); + printf("@%d%s", cx->latency, meth); +- if (cx->flags & ~CST_FLAG_SKIP) +- printf(".%x", (cx->flags & ~CST_FLAG_SKIP)); ++ if (cx->flags & ~CST_FLAG_SKIP) { ++ if (cx->flags & CST_FLAG_FALLBACK) ++ printf("!"); ++ else ++ printf(".%x", (cx->flags & ~CST_FLAG_SKIP)); ++ } + if (show_addr) + printf("@0x%llx", cx->address); + printf(")"); +@@ -1114,12 +1127,6 @@ acpicpu_idle(void) + * states marked skippable + */ + best = cx = SLIST_FIRST(&sc->sc_cstates); +- if (cx == NULL) { +-halt: +- __asm volatile("sti; hlt"); +- return; +- } +- + while ((cx->flags & CST_FLAG_SKIP) || + cx->latency * 3 > sc->sc_prev_sleep) { + if ((cx = SLIST_NEXT(cx, link)) == NULL) +@@ -1140,8 +1147,6 @@ halt: + (cx->flags & CST_FLAG_MWAIT_BM_AVOIDANCE) == 0) + break; + } +- if (cx == NULL) +- goto halt; + best = cx; + } + + diff --git a/perl/t/sample.maildir/new/1416411736.20539_44.geronimo.l.stalphonsos.net b/perl/t/sample.maildir/new/1416411736.20539_44.geronimo.l.stalphonsos.net new file mode 100644 index 00000000..5ce08a3d --- /dev/null +++ b/perl/t/sample.maildir/new/1416411736.20539_44.geronimo.l.stalphonsos.net @@ -0,0 +1,168 @@ +Return-Path: +Delivered-To: attila@mail.stalphonsos.net +Received: (qmail 12481 invoked by alias); 16 Nov 2014 17:02:11 -0000 +Delivered-To: habeus@stalphonsos.com +Received: (qmail 12477 invoked from network); 16 Nov 2014 17:02:10 -0000 +Received: from unknown (HELO smtp.clicks.slashdot.org) (74.116.233.131) + by x.stalphonsos.net with SMTP; 16 Nov 2014 17:02:10 -0000 +Received: from mail3.elabs10.com (10.10.10.53) by smtp.clicks.slashdot.org id hd3c941lf140 for ; Sun, 16 Nov 2014 09:02:10 -0800 (envelope-from ) +To: +Subject: Slashdot Newsletter 2014-11-16 +Date: Sun, 16 Nov 2014 09:02:10 -0800 +DKIM-Signature: v=1; a=rsa-sha1; c=relaxed/relaxed; d=newsletters.slashdot.org; s=s2010001400b; + h=Reply-To:From:List-Unsubscribe:Content-description:Content-Type:Subject:To:Date; + bh=qb2VwtQJpdaiWdlAeC1G6/iatq0=; + b=qe5NrC1E3eG48ri5aonHzV9Jnbrit2dTAqm6tcbJvNSHHb3utEiLyVSiO4RcCNPG6Tj + /hx6ndWVOfcjM8Q85p76S6JCkpd5V+vnMmktRPb9/FYCSTckqdlSyBYtHJz+oWWZawl + TW1QU40j/H+QEN/gI3rJtuxnyjIuFAr4xElHc= +X-Delivery: Custom 2010001400 +Reply-To: slashdot@newsletters.slashdot.org +List-Unsubscribe: +Content-description: 009e97a71fhabeus%40stalphonsos.com!ba3c!3551f9!77ce2ff8!rynof10.pbz! +X-Complaints-To: abuse@elabs10.com +Message-Id: <20141116170227.009E97A71F47@elabs10.com> +Content-Type: text/plain; charset="us-ascii" +From: "Slashdot Headlines" + +Slashdot Daily News + +__________________________________________________________________________ +Do You Manage Your Data or Does It Manage You? +Databases, warehouses and Big Data analytics projects are changing the way IT and the enterprise think about data management. How is your organization evolving to handle the data explosion that continues to stress developers, IT and users alike? Take our quick four-question Slashdot Pulse poll and let us know what is most important to your data efforts. Learn More! +http://clicks.slashdot.org/ct.html?ufl=0&rtr=on&s=x8pb08,22wah,10sc,g3m8,52q8,1yh,aqzj +__________________________________________________________________________ + +The Downside to Low Gas Prices +http://clicks.slashdot.org/ct.html?ufl=0&rtr=on&s=x8pb08,22wah,10sc,1g2t,dck5,1yh,aqzj + +Japanese Maglev Train Hits 500kph +http://clicks.slashdot.org/ct.html?ufl=0&rtr=on&s=x8pb08,22wah,10sc,1w2f,ipk8,1yh,aqzj + +Philae's Batteries Have Drained; Comet Lander Sleeps +http://clicks.slashdot.org/ct.html?ufl=0&rtr=on&s=x8pb08,22wah,10sc,i6u7,2e4k,1yh,aqzj + +Former Police Officer Indicted For Teaching How To Pass a Polygraph Test +http://clicks.slashdot.org/ct.html?ufl=0&rtr=on&s=x8pb08,22wah,10sc,hale,m1sq,1yh,aqzj + +Comcast Kisses-Up To Obama, Publicly Agrees On Net Neutrality +http://clicks.slashdot.org/ct.html?ufl=0&rtr=on&s=x8pb08,22wah,10sc,b9tp,5bcl,1yh,aqzj + +Ask Slashdot: Programming Education Resources For a Year Offline? +http://clicks.slashdot.org/ct.html?ufl=0&rtr=on&s=x8pb08,22wah,10sc,2g0d,dx6s,1yh,aqzj + +Alleged Satellite Photo Says Ukraine Shootdown of MH17 +http://clicks.slashdot.org/ct.html?ufl=0&rtr=on&s=x8pb08,22wah,10sc,f19n,a6i,1yh,aqzj + +US DOE Sets Sights On 300 Petaflop Supercomputer +http://clicks.slashdot.org/ct.html?ufl=0&rtr=on&s=x8pb08,22wah,10sc,mao5,9w51,1yh,aqzj + +Ask Slashdot: Who's the Doctors Without Borders of Technology? +http://clicks.slashdot.org/ct.html?ufl=0&rtr=on&s=x8pb08,22wah,10sc,l56s,51e3,1yh,aqzj + +Entrepreneur Injects Bitcoin Wallets Into Hands +http://clicks.slashdot.org/ct.html?ufl=0&rtr=on&s=x8pb08,22wah,10sc,d39v,e9ov,1yh,aqzj + +Microsoft Aims To Offer Windows 10 Upgrades For All Windows Phone 8 Lumias +http://clicks.slashdot.org/ct.html?ufl=0&rtr=on&s=x8pb08,22wah,10sc,xnb,b4xz,1yh,aqzj + +R. A. Montgomery, Creator of the "Choose Your Own Adventure" Books, Dead At 78 +http://clicks.slashdot.org/ct.html?ufl=0&rtr=on&s=x8pb08,22wah,10sc,4ze8,vwk,1yh,aqzj + +Real Steampunk Computer Brought Back To Life +http://clicks.slashdot.org/ct.html?ufl=0&rtr=on&s=x8pb08,22wah,10sc,im3k,9yj0,1yh,aqzj + +AT&T Stops Using 'Super Cookies' To Track Cellphone Data +http://clicks.slashdot.org/ct.html?ufl=0&rtr=on&s=x8pb08,22wah,10sc,dbjg,lwow,1yh,aqzj + +New Trial Brings Skype to (Some) Browsers +http://clicks.slashdot.org/ct.html?ufl=0&rtr=on&s=x8pb08,22wah,10sc,4v80,jg5u,1yh,aqzj + + +__________________________________________________________________________ +What Is the Best Way to Launch an Online Business? +As businesses plan their move to the cloud, the choice of provider can be daunting. Performance, reliability and security concerns can paralyze IT before a migration ever begins. Take our quick poll to let us know what is most important to your organization when considering hosting your online presence in the cloud. Learn More! +http://clicks.slashdot.org/ct.html?ufl=0&rtr=on&s=x8pb08,22wah,10sc,fhdr,2xhz,1yh,aqzj +__________________________________________________________________________ + +The Downside to Low Gas Prices http://clicks.slashdot.org/ct.html?ufl=0&rtr=on&s=x8pb08,22wah,10sc,etth,jk8t,1yh,aqzj +From the speak-for-yourself-hummer-buyers department +HughPickens.com writes Pat Garofalo writes in an op-ed in US News & World Report that with the recent drop in oil prices, there's something policymakers can do that will offset at least some of the negative effects of the currently low prices,... Read More http://clicks.slashdot.org/ct.html?ufl=0&rtr=on&s=x8pb08,22wah,10sc,4pdc,d2pk,1yh,aqzj + +Japanese Maglev Train Hits 500kph http://clicks.slashdot.org/ct.html?ufl=0&rtr=on&s=x8pb08,22wah,10sc,2qfj,ft06,1yh,aqzj +From the for-amtrak-that-takes-negligence department + An anonymous reader writes Japan has now put 100 passengers on a Maglev train doing over 500kph. That's well over twice as fast as the fastest U.S. train can manage, and that only manages 240kph on small sections of its route. The Japanese... Read More http://clicks.slashdot.org/ct.html?ufl=0&rtr=on&s=x8pb08,22wah,10sc,8uka,132e,1yh,aqzj + +Philae's Batteries Have Drained; Comet Lander Sleeps http://clicks.slashdot.org/ct.html?ufl=0&rtr=on&s=x8pb08,22wah,10sc,lzi6,uds,1yh,aqzj +From the amazing-feats-done-dirt-dirt-cheap department +astroengine (1577233) writes "In the final hours, Philae's science team hurried to squeeze as much science out of the small lander as possible. But the deep sleep was inevitable, Rosetta's lander has slipped into hibernation after running its... Read More http://clicks.slashdot.org/ct.html?ufl=0&rtr=on&s=x8pb08,22wah,10sc,iaq7,ef6w,1yh,aqzj + +Former Police Officer Indicted For Teaching How To Pass a Polygraph Test http://clicks.slashdot.org/ct.html?ufl=0&rtr=on&s=x8pb08,22wah,10sc,j6fh,abrg,1yh,aqzj +From the government-hates-competition department +George Maschke (699175) writes On Friday afternoon, the U.S. Department of Justice announced the indictment (2.6 mb PDF) of Douglas Gene Williams, a 69-year-old former Oklahoma City police polygraphist turned anti-polygraph activist for teaching... Read More http://clicks.slashdot.org/ct.html?ufl=0&rtr=on&s=x8pb08,22wah,10sc,kg1f,lf6a,1yh,aqzj + +Comcast Kisses-Up To Obama, Publicly Agrees On Net Neutrality http://clicks.slashdot.org/ct.html?ufl=0&rtr=on&s=x8pb08,22wah,10sc,cewx,cg82,1yh,aqzj +From the wormtongues-all-around department +MojoKid writes Comcast is one of two companies to have earned Consumerist's "Worst Company in America" title on more than one occasion and it looks like they're lobbying for a third title. That is, unless there's another explanation as to how the... Read More http://clicks.slashdot.org/ct.html?ufl=0&rtr=on&s=x8pb08,22wah,10sc,gakt,jpmo,1yh,aqzj + +Ask Slashdot: Programming Education Resources For a Year Offline? http://clicks.slashdot.org/ct.html?ufl=0&rtr=on&s=x8pb08,22wah,10sc,i2z2,j2ja,1yh,aqzj +From the maybe-a-local-phrasebook department +An anonymous reader writes "I will be traveling to a remote Himalayan village for year and won't have access to the internet. What offline resources would you all recommend to help me continue to develop my coding skills? I think this would be a... Read More http://clicks.slashdot.org/ct.html?ufl=0&rtr=on&s=x8pb08,22wah,10sc,dvj4,8dgh,1yh,aqzj + +Alleged Satellite Photo Says Ukraine Shootdown of MH17 http://clicks.slashdot.org/ct.html?ufl=0&rtr=on&s=x8pb08,22wah,10sc,6amg,ehfi,1yh,aqzj +From the if-the-glove-won't-fit department +theshowmecanuck (703852) writes A group calling itself the Russian Union of Engineers has published a photograph, picked up by many news organizations (just picked one, Google it yourself to find more), claiming to show that MH17 was shot down by... Read More http://clicks.slashdot.org/ct.html?ufl=0&rtr=on&s=x8pb08,22wah,10sc,kx21,15cu,1yh,aqzj + +US DOE Sets Sights On 300 Petaflop Supercomputer http://clicks.slashdot.org/ct.html?ufl=0&rtr=on&s=x8pb08,22wah,10sc,9b4x,5y5d,1yh,aqzj +From the who-is-this-we-paleface? department +dcblogs writes U.S. officials Friday announced plans to spend $325 million on two new supercomputers, one of which may eventually be built to support speeds of up to 300 petaflops. The U.S. Department of Energy, the major funder of supercomputers... Read More http://clicks.slashdot.org/ct.html?ufl=0&rtr=on&s=x8pb08,22wah,10sc,92u4,310d,1yh,aqzj + +Ask Slashdot: Who's the Doctors Without Borders of Technology? http://clicks.slashdot.org/ct.html?ufl=0&rtr=on&s=x8pb08,22wah,10sc,7gbu,25op,1yh,aqzj +From the trespassers-mostly department +danspalding writes I'm transitioning into full time tech work after 10 years in education. To that end, after years of tooling around with command line and vim, I'm starting a programming bootcamp in early December. I used to think I wanted to go... Read More http://clicks.slashdot.org/ct.html?ufl=0&rtr=on&s=x8pb08,22wah,10sc,blvv,egxy,1yh,aqzj + +Entrepreneur Injects Bitcoin Wallets Into Hands http://clicks.slashdot.org/ct.html?ufl=0&rtr=on&s=x8pb08,22wah,10sc,g9t3,i8da,1yh,aqzj +From the heirs-are-not-amused department +wiredmikey writes A Dutch entrepreneur has had two microchips containing Bitcoin injected into his hands to help him make contactless payments. The chips, enclosed in a 2mm by 12mm capsule of "biocompatible" glass, were injected using a special... Read More http://clicks.slashdot.org/ct.html?ufl=0&rtr=on&s=x8pb08,22wah,10sc,73v6,1zki,1yh,aqzj + +Microsoft Aims To Offer Windows 10 Upgrades For All Windows Phone 8 Lumias http://clicks.slashdot.org/ct.html?ufl=0&rtr=on&s=x8pb08,22wah,10sc,ad3c,5wfu,1yh,aqzj +From the number-by-any-other-number department +An anonymous reader writes News suggesting that Microsoft plans to offer Windows 10 upgrades for all its Windows Phone 8 devices broke today. "It's our intention to enable a Windows 10 upgrade for Lumia Windows Phone 8 smartphones," a Microsoft... Read More http://clicks.slashdot.org/ct.html?ufl=0&rtr=on&s=x8pb08,22wah,10sc,ebtb,le5x,1yh,aqzj + +R. A. Montgomery, Creator of the "Choose Your Own Adventure" Books, Dead At 78 http://clicks.slashdot.org/ct.html?ufl=0&rtr=on&s=x8pb08,22wah,10sc,jbbx,7g6y,1yh,aqzj +From the your-codename-is-jonah department +Dave Knott writes Raymond Almiran Montgomery, original publisher and author of the incredibly popular "Choose Your Own Adventure" book series for children, the 4th bestselling children's series of all time, has died at the age of 78. In 1975,... Read More http://clicks.slashdot.org/ct.html?ufl=0&rtr=on&s=x8pb08,22wah,10sc,5jp2,a7ls,1yh,aqzj + +Real Steampunk Computer Brought Back To Life http://clicks.slashdot.org/ct.html?ufl=0&rtr=on&s=x8pb08,22wah,10sc,g7nv,eiak,1yh,aqzj +From the malware-free department +New submitter engineerguy writes We discovered a 100 year old 19th century computer that does Fourier analysis with just gears spring and levers. It was locked in a glass case at the University of Illinois Department of Mathematics. We rebuilt a... Read More http://clicks.slashdot.org/ct.html?ufl=0&rtr=on&s=x8pb08,22wah,10sc,g887,1slz,1yh,aqzj + +AT&T Stops Using 'Super Cookies' To Track Cellphone Data http://clicks.slashdot.org/ct.html?ufl=0&rtr=on&s=x8pb08,22wah,10sc,gxkh,d3ck,1yh,aqzj +From the turns-out-people-hate-that department +jriding (1076733) writes AT&T Mobility, the nation's second-largest cellular provider, says it's no longer attaching hidden Internet tracking codes to data transmitted from its users' smartphones. The practice made it nearly impossible to... Read More http://clicks.slashdot.org/ct.html?ufl=0&rtr=on&s=x8pb08,22wah,10sc,9145,j9mq,1yh,aqzj + +New Trial Brings Skype to (Some) Browsers http://clicks.slashdot.org/ct.html?ufl=0&rtr=on&s=x8pb08,22wah,10sc,11f1,e887,1yh,aqzj +From the video-chat-not-yet-standard-browser-feature department +Ars Technica reports that Microsoft has begun giving some users a taste of a new version of Skype, with a big difference compared to previous ones: the new one (tested by users on an invitation basis) is browser based. Rather than using the... Read More http://clicks.slashdot.org/ct.html?ufl=0&rtr=on&s=x8pb08,22wah,10sc,f7r7,ffwo,1yh,aqzj + + +See all today's stories - http://clicks.slashdot.org/ct.html?ufl=0&rtr=on&s=x8pb08,22wah,10sc,lczi,3ou1,1yh,aqzj + +Submit a Story to Slashdot! http://clicks.slashdot.org/ct.html?ufl=0&rtr=on&s=x8pb08,22wah,10sc,ezuv,f7p0,1yh,aqzj + + +Follow us on: +Facebook - http://clicks.slashdot.org/ct.html?ufl=0&rtr=on&s=x8pb08,22wah,10sc,k3xq,iu97,1yh,aqzj +Twitter - http://clicks.slashdot.org/ct.html?ufl=0&rtr=on&s=x8pb08,22wah,10sc,hhbe,5104,1yh,aqzj +Google+ - http://clicks.slashdot.org/ct.html?ufl=0&rtr=on&s=x8pb08,22wah,10sc,lho1,copu,1yh,aqzj + +__________________________________________________________________________ +You are subscribed to this Resource Newsletter as habeus@stalphonsos.com . + +To change your preferences - receive this in html or text, visit the Preference Center! +http://clicks.slashdot.org/ct.html?ufl=0&rtr=on&s=x8pb08,22wah,10sc,m454,24ky,1yh,aqzj&EMAIL_ADDRESS=habeus@stalphonsos.com + +To unsubscribe, send an email to: unsubscribe-47676@elabs10.com + +Slashdot | 594 Howard Street, Suite 300 | San Francisco, CA 94105 + +To view our Privacy Policy click here: http://clicks.slashdot.org/ct.html?ufl=0&rtr=on&s=x8pb08,22wah,10sc,8pii,7uiv,1yh,aqzj