[Mimedefang] proposing a patch for URL listing in boilerplate and more

Vieri Di Paola vieridipaola at yahoo.com
Mon Dec 24 06:41:35 EST 2007


Hi,

I would like to suggest a patch for mimedefang.pl and
custom example for /etc/mail/mimedefang-filter.

I'm suggesting this because:

1) I found that when using action_replace_with_url,
there's no way that hotmail webmail users will see the
URL link "inline" so that they can simply click on it.
I needed a "clickable" solution for lazy users who
don't want to open the warning.txt plain text
attachment in a text editor then copy and paste the
long URL into a web browser. With this patch, all
major clients including M$ Lookout seem to display an
"immediately clickable link" to the end user.

2) I needed a way to replace an attachment with a
compressed version of it. This avoids having to force
lazy users to actively compress their attachments
before sending.

Note that I'm using mimedefang for outgoing messages
only.

Please let me know if you can include this in the code
base or if I should just stick to reimplementing the
functions in /etc/mail/mimedefang-filter.

Cheers,

Vieri



      ____________________________________________________________________________________
Looking for last minute shopping deals?  
Find them fast with Yahoo! Search.  http://tools.search.yahoo.com/newsearch/category.php?category=shopping
-------------- next part --------------
diff -NuarwbB mimedefang-2.63-original/mimedefang.pl.in mimedefang-2.63/mimedefang.pl.in
--- mimedefang-2.63-original/mimedefang.pl.in	2007-08-13 15:50:18.000000000 +0200
+++ mimedefang-2.63/mimedefang.pl.in	2007-12-24 11:52:24.000000000 +0100
@@ -38,7 +38,7 @@
 	    $MessageID $Rebuild $QuarantineCount
 	    $QuarantineSubdir $QueueID $MsgID
 	    $RelayAddr $WasResent $RelayHostname
-	    $RealRelayAddr $RealRelayHostname
+	    $RealRelayAddr $RealRelayHostname @attachURLlist
 	    $ReplacementEntity $Sender $ServerMode $Subject $SubjectCount
 	    $ClamdSock $SophieSock $TrophieSock
 	    $SuspiciousCharsInHeaders
@@ -1084,23 +1084,60 @@
 # %PROCEDURE: action_replace_with_warning
 # %ARGUMENTS:
 #  msg -- warning message
+#  mtype -- mime type
+#  mext -- extension
+#  mname -- file name
 # %RETURNS:
 #  Nothing
 # %DESCRIPTION:
 #  Makes a note to drop the current part and replace it with a warning
 #***********************************************************************
-sub action_replace_with_warning ($) {
-    my($msg) = @_;
+sub action_replace_with_warning ($;$$$) {
+    my($msg, $mtype, $mext, $mname) = @_;
     return 0 if (!in_filter_context("action_replace_with_warning"));
     $Actions{'replace_with_warning'}++;
     $Action = "replace";
-    $ReplacementEntity = MIME::Entity->build(Type => "text/plain",
+    $mtype = "text/plain" unless defined($mtype);
+    $mext = "txt" unless defined($mext);
+    $mname = "warning" unless defined($mname);
+    $ReplacementEntity = MIME::Entity->build(Type => $mtype,
 					     Encoding => "-suggest",
 					     Data => [ "$msg\n" ]);
     $WarningCounter++;
-    $ReplacementEntity->head->mime_attr("Content-Type.name" => "warning$WarningCounter.txt");
+    $ReplacementEntity->head->mime_attr("Content-Type.name" => "$mname$WarningCounter.$mext");
     $ReplacementEntity->head->mime_attr("Content-Disposition" => "inline");
-    $ReplacementEntity->head->mime_attr("Content-Disposition.filename" => "warning$WarningCounter.txt");
+    $ReplacementEntity->head->mime_attr("Content-Disposition.filename" => "$mname$WarningCounter.$mext");
+    return 1;
+}
+
+#***********************************************************************
+# %PROCEDURE: action_replace_with_file
+# %ARGUMENTS:
+#  entity -- mime entity
+#  nfname -- new file path
+#  nfdesc -- description
+#  nftype -- mime type
+#  nfencode -- encoding
+# %RETURNS:
+#  Nothing
+# %DESCRIPTION:
+#  Makes a note to drop the current part and replace it with a file
+#***********************************************************************
+sub action_replace_with_file ($$;$$$$$) {
+    my($entity, $nfpath, $nfname, $nftype, $nfencode, $nfdispo) = @_;
+    return 0 if (!in_filter_context("action_replace_with_file"));
+    $Actions{'replace_with_file'}++;
+    $Action = "replace";
+    $nftype = "application/octet-stream" unless defined($nftype);
+    $nfname = "" unless defined($nfname);
+    $nfencode = "base64" unless defined($nfencode);
+    $nfdispo = "attachment" unless defined($nfdispo);
+    $ReplacementEntity = MIME::Entity->build(Type => $nftype,
+					     Encoding => $nfencode,
+					     Path => $nfpath,
+					     Filename => $nfname,
+					     Disposition => $nfdispo);
+    copy_or_link($nfpath, $entity->bodyhandle->path) or return 0;
     return 1;
 }
 
@@ -5282,6 +5319,7 @@
     undef @StatusTags;
     undef @ESMTPArgs;
     undef @SenderESMTPArgs;
+    undef @attachURLlist;
 }
 
 sub builtin_create_parser () {
@@ -6040,6 +6078,9 @@
 #  base_url -- base URL for retrieving document
 #  msg -- message to replace document with.  The string "_URL_" is
 #         replaced with the actual URL of the part.
+#  mtype -- message mime type (for the warning msg)
+#  mext -- message extension (for the warning msg)
+#  mname -- message name (for the warning msg)
 #  cd_data -- optional Content-Disposition filename data to save
 #  salt    -- optional salt to add to SHA1 hash.
 # %RETURNS:
@@ -6048,8 +6089,8 @@
 #  Places the part in doc_root/{sha1_of_part}.ext and replaces it with
 #  a text/plain part giving the URL for pickup.
 #***********************************************************************
-sub action_replace_with_url ($$$$;$$) {
-    my($entity, $doc_root, $base_url, $msg, $cd_data, $salt) = @_;
+sub action_replace_with_url ($$$$;$$$$$) {
+    my($entity, $doc_root, $base_url, $msg, $mtype, $mext, $mname, $cd_data, $salt) = @_;
     my($ctx);
     my($path);
     my($fname, $ext, $name, $url);
@@ -6099,8 +6140,16 @@
 	}
     }
 
+    push(@attachURLlist, $cd_data."\n".$url."\n");
+
     $msg =~ s/_URL_/$url/g;
+    if (defined($mtype) and ($mtype ne "") and defined($mext) and ($mext ne "") and defined($mname) and ($mname ne "")) {
+	action_replace_with_warning($msg, $mtype, $mext, $mname);
+	}
+    else {
     action_replace_with_warning($msg);
+	}
+
     return 1;
 }
 
-------------- next part --------------
use Archive::Zip qw( :ERROR_CODES :CONSTANTS );
$AddWarningsInline = 1;
$Stupidity{"NoMultipleInlines"} = 1;

#***********************************************************************
# %PROCEDURE: custom_list_replacement_urls
# %ARGUMENTS:
#  entity
# %RETURNS:
#  nothing
# %DESCRIPTION:
#  Lists replacement URLs at end of body message for all removed attachments.
#  Must be called from filter_end.
#***********************************************************************
sub custom_list_replacement_urls ($;$$) {
    my($entity, $header, $footer) = @_;
    my $plain = $header.join("\n", @attachURLlist).$footer;
    my $html = $plain;
    $html =~ s|(http://\S*)|<a href="$1">$1</a>|g;
    $html =~ s/\n/<br>\n/g;
    append_text_boilerplate($entity, $plain, 0);
    append_html_boilerplate($entity, $html, 0);
}

#***********************************************************************
# %PROCEDURE: filter
#***********************************************************************
sub filter {
    my($entity, $fname, $ext, $type) = @_;

    return if message_rejected(); # Avoid unnecessary work

(...)

    # Some senders do not get their mail filtered
    if ( ($ext ne '') && ($Sender ne '<me at domain1.org>') && ($Sender ne '<me at domain2.org>') ) {

	# If it's not a compressed file then zip it
	my($comp_exts, $re);
	$comp_exts = '(zip|gz|tgz|bz2|Z|\{[^\}]+\})';
	$re = '\.' . $comp_exts . '\.*$';
	my $zipped;

	if (!re_match($entity, $re)) {
	    md_graphdefang_log('mail', 'CUSTOM_COMPRESS', $fname);
	    my $zip = Archive::Zip->new();
	    my $member;
	    $member = $zip->addFile($entity->bodyhandle->path, $fname);
	    # compress (DEFLATED) or not (STORED)
	    $member->desiredCompressionMethod( COMPRESSION_DEFLATED );
	    $member->desiredCompressionLevel( COMPRESSION_LEVEL_BEST_COMPRESSION );
	    $fname = "$fname.zip" unless $fname =~ s/\.[^.]*$/\.zip/;
	    $zip->writeToFileNamed("./Work/CUSTOM_$fname");
	    action_replace_with_file($entity, "./Work/CUSTOM_$fname", $fname);
	    $zipped = 1;
	}

	my($custom_size, $custom_bandwidth);
	$custom_bandwidth = (stat("./INPUTMSG"))[7]*scalar(@Recipients);
	md_graphdefang_log('mail', 'CUSTOM_BANDWIDTH', $custom_bandwidth);
	$custom_size = (stat($entity->bodyhandle->path))[7];
	md_graphdefang_log('mail', 'CUSTOM_MSG_PART_SIZE', $custom_size);
	if ( ($custom_size > 800*1024) || ($custom_bandwidth > 1300*1024) ) {
    	    action_notify_sender("You e-mail has been filtered and sent. No further action required.\n");
    	    return action_replace_with_url($entity,"/var/www/localhost/htdocs/dload","http://download.domain3.org/dload","You can download the attachment from:<br>\n<a href='_URL_'>_URL_</a><br>\nNOTICE: the attached file ($fname - $custom_size bytes) is available for about 1 week.\n","text/html","html","attachment",$fname);
	}

	if ($zipped) {
	    return 1;
	}

    }
    
    return action_accept();
}

(...)

sub filter_end {
    my($entity) = @_;

    return if message_rejected();

    if (@attachURLlist > 0) {
	&custom_list_replacement_urls($entity,"The following is a list links to download attached files.\n","\nThe data is available for about 1 week.")
    }



More information about the MIMEDefang mailing list