From 142c85ea522eed01577a7f1dbe3c5f89263f96ec Mon Sep 17 00:00:00 2001 From: CrawfordCurrie Date: Tue, 3 Feb 2009 13:05:52 +0000 Subject: [PATCH] Foswikitask:Item456: ported to Foswiki Foswikitask:Item4955: config options moved to 'Mail and Proxies' Foswikitask:Item5829: force new revision on each submission Foswikitask:Item5843: extract target topic from CC: field - general improvement to rules for extracting the target topic git-svn-id: http://svn.foswiki.org/trunk/MailInContrib@2328 0b4bb1d4-4e5a-0410-9cc4-b2b747904278 --- data/System/MailInContrib.txt | 30 ++++++--- data/System/MailInContribTemplate.txt | 6 +- lib/Foswiki/Contrib/MailInContrib.pm | 66 +++++++++++++------ lib/Foswiki/Contrib/MailInContrib/Config.spec | 2 +- test/unit/MailInContrib/MailInContribSuite.pm | 38 ++++++++--- 5 files changed, 100 insertions(+), 42 deletions(-) diff --git a/data/System/MailInContrib.txt b/data/System/MailInContrib.txt index 69b5165..cbe438d 100644 --- a/data/System/MailInContrib.txt +++ b/data/System/MailInContrib.txt @@ -40,10 +40,21 @@ Attachments to the mail get treated as attachments by Foswiki, and attached to t Note that =mailincron= will only process messages that have arrived since the last time it ran. So if there was an error adding a mail, it won't attempt to add it again even if it is still in the inbox. ---+ Where the mail gets added -The target topic for a mail is normally specified in the =Subject:= of the mail. You just put Web.Topic as the first thing on the subject line, optionally followed by a colon and other subject text. Alternatively you can configure the module to examine the =To:= field in the mail and get the topic name from there instead. - * If a topic is specified, but doesn't exist, it will be created. - * If there is no web name specified, then you can configure a default web to accept those mails. - * If a non-existant web is specified, then it is an error. +By default the target topic for mail is specified in the =Subject:= of the mail. You just put Web.Topic as the first thing on the subject line, optionally followed by a colon and other subject text. If there is no web name specified, then you can configure a default web to accept those mails. + * A mail with a subject of =Schedule.MyClass= will be saved to Schedule.MyClass + * =Subject: Schedule.MyClass: timetable= will be saved to Schedule.MyClass + * =Subject: MyClass= will be saved to <default web>.MyClass. + * =Subject: MyClass: timetable= will be saved to <default web>.MyClass, if the default web is set as =Schedule=. + * If a topic specified on the =Subject:= line doesn't exist, it will be created. + +You can also configure the module to examine the =To:= and =CC:= fields in the mail, and get the name of an existing topic from there instead. + * If a non-existant topic is specified, then it is an error. + * The first valid, existing topic found in the =To:= or =CC:= fields is taken, and overrides the =Subject:=. + * A mail addressed to =<MyTopic@example.com>= will be saved in <default web>.MyTopic + * A mail addressed to =<Schedule.MyTopic@example.com>= will be saved in Schedule.MyTopic + * A mail addressed to ="Mylene Classe" <Schedule.MyClass@example.com>= will be saved in Schedule.MyTopic + * A mail addressed to ="Mylene Classe" <MyClass@example.com>= will be saved in <default web>.MyTopic + You can also define a 'spambox' for each mail folder. A spambox is a topic that will take all messages that do _not_ have a valid topic. The module can use special HTML comments in the topic to decide where to insert new emails within the text. @@ -86,20 +97,21 @@ Make sure that the cron is run by a user with the permissions needed to read and ---++ Contrib Info -Another great Foswiki extension from the *WikiRing* - working together to improve your wiki experience! +Another great Foswiki extension from the *WikiRing* - *working together to improve your wiki experience!* Many thanks to the following sponsors for supporting this work: * [[http://www.evolvedmedianetwork.com][Evolved Media Network]] | Author: | Foswiki:Main.CrawfordCurrie (http://c-dot.co.uk) | -| Copyright ©: | 2005, TWiki Contributors; 2008 Foswiki Contributors | +| Copyright ©: | 2005, TWiki Contributors
2008-2009 Foswiki Contributors | | License: | GPL ([[http://www.gnu.org/copyleft/gpl.html][GNU General Public License]]) | | Dependencies: | %$DEPENDENCIES% | | Version: | %$VERSION% | | Change History: | | -| 1 Aug 2008 | Bugs:Item5450: minor doc fix | -| 8 Sep 2007 | Bugs:Item4575 various ideas from Dev topic, tested against 4.2.0 | -| 12412 | Fixed Bugs:Item3334 by removing dependency on !FuncUsersContrib | +| 3 Feb 2009 | Foswikitask:Item456: ported to Foswiki Foswikitask:Item4955: config options moved to 'Mail and Proxies' Foswikitask:Item5829: force new revision on each submission Foswikitask:Item5843: extract target topic from CC: field - general improvement to rules for extracting the target topic | +| 1 Aug 2008 | TWikibug:Item5450: minor doc fix | +| 8 Sep 2007 | TWikibug:Item4575 various ideas from Dev topic, tested against 4.2.0 | +| 12412 | Fixed TWikibug:Item3334 by removing dependency on !FuncUsersContrib | | 10239 | Added support for listing attachments in-line with the mail | | 10182 | Fixed parser failures on multipart MIME messages | | 10111 | Fixed problem with spaces in attachment names | diff --git a/data/System/MailInContribTemplate.txt b/data/System/MailInContribTemplate.txt index 89071fd..8b5943e 100644 --- a/data/System/MailInContribTemplate.txt +++ b/data/System/MailInContribTemplate.txt @@ -3,9 +3,9 @@ *DO NOT MODIFY THIS TOPIC* -This is a normal TWiki topic, but it is also a template file, as described in %SYSTEMWEB%.SkinTemplates. It contains the template definitions used by the MailInContrib to insert mail messages in topics. +This is a normal topic, but it is also a template file, as described in %SYSTEMWEB%.SkinTemplates. It contains the template definitions used by the MailInContrib to insert mail messages in topics. -Each template is specified in the form of a pair of TWiki template definitions, (surrounded by verbatim tags below for readability). The template to use when inserting incoming mail in a topic is decided from the parameters in the =<--MAIL--> comment in the topic. The default template is =normal=. +Each template is specified in the form of a pair of template definitions, (surrounded by verbatim tags below for readability). The template to use when inserting incoming mail in a topic is decided from the parameters in the =<--MAIL--> comment in the topic. The default template is =normal=. %TMPL:DEF{MAILIN:normal}% * *%SUBJECT%*: %TEXT% -- %WIKIUSERNAME% - %SERVERTIME%%ATTACHMENTS% @@ -20,7 +20,7 @@ Each template is specified in the form of a pair of TWiki template definitions, *DO NOT MODIFY THIS TOPIC* -If you want to modify or add any templates, create the TWiki topic %SYSTEMWEB%.MailInContribUserTemplate. This topic is automatically included here if it exists, but it is *not* shipped with the MailInContrib. Any templates you define in %SYSTEMWEB%.MailInContribUserTemplate will override those defined here. +If you want to modify or add any templates, create the topic %SYSTEMWEB%.MailInContribUserTemplate. This topic is automatically included here if it exists, but it is *not* shipped with the MailInContrib. You can create this topic in individual webs, as described in %SYSTEMWEB%.SkinTemplates. In this case the template will only apply in that specific web. %TMPL:INCLUDE{"MailInContribUser"}% diff --git a/lib/Foswiki/Contrib/MailInContrib.pm b/lib/Foswiki/Contrib/MailInContrib.pm index 1a54e4d..953c427 100644 --- a/lib/Foswiki/Contrib/MailInContrib.pm +++ b/lib/Foswiki/Contrib/MailInContrib.pm @@ -1,11 +1,13 @@ # # Foswiki - The Free and Open Source Wiki, http://foswiki.org/ # -# Copyright (C) 2005 TWiki Contributors. All Rights Reserved. -# Copyright (C) 2008 Foswiki Contributors. All Rights Reserved. +# Copyright (C) 2009 Foswiki Contributors. All Rights Reserved. # Foswiki Contributors are listed in the AUTHORS file in the root -# of this distribution. -# NOTE: Please extend that file, not this notice. +# of this distribution. NOTE: Please extend that file, not this notice. +# +# Additional copyrights apply to some or all of the code in this module +# as follows: +# Copyright (C) 2005 TWiki Contributors. All Rights Reserved. # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -22,6 +24,7 @@ package Foswiki::Contrib::MailInContrib; use strict; use Foswiki; +use Assert; use Email::Folder; use Email::FolderType::Net; @@ -163,17 +166,18 @@ sub processInbox { $received = $receipt if $receipt > $received; } } - unless ($received) { + if (!$received && $mail->header('Date')) { # Use the send date $received = Time::ParseDate::parsedate($mail->header('Date')); } $received ||= time(); # Try to get the target topic by - # 1. examining the "To" address to see if it is a valid web.wikiname (if - # enabled in config) - # 2. if the subject line starts with a valid Foswiki Web.WikiName (if optionally - # followed by a colon, the rest of the subject line will be ignored) + # 1. examining the "To" and "cc" addresses to see if either has + # a valid web.wikiname (if enabled in config) + # 2. if the subject line starts with a valid Foswiki Web.WikiName + # (if optionally followed by a colon, the rest of the subject + # line will be ignored) # 3. Routing the comment to the spambox if it is enabled # 4. Otherwise replying to the user to say "no thanks" if replyonnotopic my( $web, $topic, $user ); @@ -190,8 +194,15 @@ sub processInbox { if( $targets && scalar(@$targets)) { $user = $targets->[0]; } - my $to = $mail->header('To'); - $to =~ s/^.*<(.*)>.*$/$1/; + + my @to = split(/,\s*/, $mail->header('To') || ''); + if (defined $mail->header('CC')) { + push(@to, split(/,\s*/, $mail->header('CC'))); + } + # Use the address in the <> if there is one + @to = map { /^.*<(.*)>.*$/ ? $1 : $_; } @to; + print STDERR "Targets: ", join(' ', @to),"\n" if $this->{debug}; + print STDERR "Subject: $subject\n" if $this->{debug}; unless( $user ) { unless( $box->{user} && ($user = _getUser( $box->{user} ))) { @@ -203,16 +214,32 @@ sub processInbox { } } - print STDERR "User ",($user||'undefined'),"\n" if( $this->{debug} ); - - if( $box->{topicPath} =~ /\bto\b/ && - $to =~ /^(?:($Foswiki::regex{webNameRegex})\.)($Foswiki::regex{wikiWordRegex})@/i) { - ( $web, $topic ) = ( $1, $2 ); + print STDERR "User is '",($user||'undefined'),"'\n" + if( $this->{debug} ); + + # See if we can get a valid web.topic out of to: or cc: + if( $box->{topicPath} =~ /\bto\b/) { + foreach my $target (@to) { + next unless $target =~ /^(?:($Foswiki::regex{webNameRegex})\.)($Foswiki::regex{topicNameRegex})\@/i; + my ($guessweb, $guesstopic) = + Foswiki::Func::normalizeWebTopicName( + ($1 || $box->{defaultWeb}), $2); + if (Foswiki::Func::topicExists($guessweb, $guesstopic)) { + # Found an existing topic + ($web, $topic) = ($guessweb, $guesstopic); + last; + } + } } + + # If we didn't get the name of an existing topic from the + # To: or CC:, use the Subject: if( !$topic && $box->{topicPath} =~ /\bsubject\b/ && $subject =~ - s/^\s*(?:($Foswiki::regex{webNameRegex})\.)?($Foswiki::regex{wikiWordRegex})(:\s*|\s*$)// ) { - ( $web, $topic ) = ( $1, $2 ); + /^\s*(?:($Foswiki::regex{webNameRegex})\.)?($Foswiki::regex{topicNameRegex})(:\s*|\s*$)/ ) { + ($web, $topic) = Foswiki::Func::normalizeWebTopicName( + ($1 || $box->{defaultWeb}), $2); + # This time the topic doesn't have to exist } $web ||= $box->{defaultWeb}; @@ -401,8 +428,9 @@ sub _saveTopic { print STDERR "Save topic $web.$topic:\n$text\n" if( $this->{debug} ); + ASSERT(!$meta || $meta->isa('Foswiki::Meta')) if DEBUG; Foswiki::Func::saveTopic( - $web, $topic, $text, $meta, + $web, $topic, $meta, $text, { comment => "Submitted by e-mail", forcenewrevision => 1} ); diff --git a/lib/Foswiki/Contrib/MailInContrib/Config.spec b/lib/Foswiki/Contrib/MailInContrib/Config.spec index e805a22..741c33e 100644 --- a/lib/Foswiki/Contrib/MailInContrib/Config.spec +++ b/lib/Foswiki/Contrib/MailInContrib/Config.spec @@ -63,7 +63,7 @@ # specify a full web.topicname # # -$TWiki::cfg{MailInContrib} = [ +$Foswiki::cfg{MailInContrib} = [ { folder => 'pop://example_user:password@example.com/Inbox', onError => 'log', diff --git a/test/unit/MailInContrib/MailInContribSuite.pm b/test/unit/MailInContrib/MailInContribSuite.pm index 38b40cc..26ae973 100644 --- a/test/unit/MailInContrib/MailInContribSuite.pm +++ b/test/unit/MailInContrib/MailInContribSuite.pm @@ -155,8 +155,18 @@ HERE my( $m, $t ) = Foswiki::Func::readTopic($this->{test_web},$this->{test_topic}); - $this->assert($t =~ s/^ *\* \*$this->{test_web}\.NotHere\*: Message 1 text here\s*-- $this->{users_web}\.MoleInnaHole -\s+\d+\s+\w+\s+\d+\s+-\s+\d+:\d+\n//m, $t); - $this->assert($t =~ s/^ *\* \*$this->{test_web}\.IgnoreThis\*: Message 2 text here\s*-- $this->{users_web}\.AllyGator -\s+\d+\s+\w+\s+\d+\s+-\s+\d+:\d+\n//m, $t); + $this->assert(0, $t) unless + $t =~ s/^\s*\*\s+\*$this->{test_web}\.NotHere\*:\s*//s; + $this->assert(0, $t) unless + $t =~ s/^Message 1 text here\s*//s; + $this->assert(0, $t) unless + $t =~ s/^_$this->{users_web}\.MoleInnaHole\s*\@\s*\d+\s+\w+\s+\d+\s+-\s+\d+:\d+_\s*//s; + $this->assert(0, $t) unless + $t =~ s/^ *\* \*$this->{test_web}\.IgnoreThis\*: //s; + $this->assert(0, $t) unless + $t =~ s/^Message 2 text here\s*//s; + $this->assert(0, $t) unless + $t =~ s/^_$this->{users_web}\.AllyGator\s*\@\s*\d+\s+\w+\s+\d+\s+-\s+\d+:\d+_//s; $this->assert_matches(qr/^\s*$/, $t); $this->assert_equals(0, scalar(@mails)); @@ -187,13 +197,22 @@ HERE $this->sendTestMail($mail); $box->{topicPath} = 'subject'; my $c = $this->cron(); - $this->assert_null($c->{error}); - - my( $m, $t ) = Foswiki::Func::readTopic($this->{test_web},$this->{test_topic}); - - $this->assert($t =~ s/^\s*\* \*\*: Message 1 text here\s* -- $this->{users_web}\.MoleInnaHole -\s+\d+\s+\w+\s+\d+\s+-\s+\d+:\d+$//m, $t); - $this->assert($t =~ s/^ *\* \*SPAM\*: Message 2 text here\s*-- $this->{users_web}\.AllyGator -\s+\d+\s+\w+\s+\d+\s+-\s+\d+:\d+$//m); + if ($c->{error}) { + print STDERR $c->{error},"\n"; + $this->assert_null($c->{error}); + } + my( $m, $t ) = Foswiki::Func::readTopic( + $this->{test_web}, $this->{test_topic}); + + $this->assert(0, $t) unless + $t =~ s/^\s*\* \*$this->{test_web}.$this->{test_topic}\*: Message 1 text here\s*//s; + $this->assert(0, $t) unless + $t =~ s/^_$this->{users_web}\.MoleInnaHole\s*\@\s*\d+\s+\w+\s+\d+\s+-\s+\d+:\d+_//s; + $this->assert(0, $t) unless + $t =~ s/^\s*\*\s*\*$this->{test_web}.$this->{test_topic}: SPAM\*: Message 2 text here\s*//s; + $this->assert(0, $t) unless + $t =~ s/^_$this->{users_web}\.AllyGator\s*\@\s*\d+\s+\w+\s+\d+\s+-\s+\d+:\d+_//s; $this->assert_matches(qr/^\s*$/, $t); $this->assert_equals(0, scalar(@mails)); } @@ -296,7 +315,7 @@ Date: Mon, 27 Feb 2006 00:33:58 -0800 From: "Ally Gator" To: "Dick Head" Subject: $this->{test_web}.AnotherTopic: attachment test -Cc: another.idiot@twiki.com> +Cc: another.idiot@twiki.com MIME-Version: 1.0 Content-Type: multipart/mixed; boundary="----=_Part_21658_5579231.1141029238540" @@ -366,7 +385,6 @@ HERE $box->{onSuccess} = 'reply'; my $c = $this->cron(); -print STDERR "VBLO\n"; $this->assert_equals(1, scalar(@mails)); $this->assert_matches(qr/Thank you for your successful/, $mails[0]);