Decode the utf8 to ISO-8859-1 mail subject to text in .procmailrc file - perl

Set out to write a simple procmail recipie that would forward the mail if it found the text "ABC Store: New Order" in the subject.
:0
* ^(To|From).*abc#cdefgh.com
* ^Subject:.*ABC Store: New Order*
{
Unfortunately the subject field in the mail message coming from the mail server was in MIME encoded-word syntax.
Subject: =?UTF-8?B?QUJDIFN0b3JlOiBOZXcgT3JkZXI=?=
The above subject is utf-8 ISO-8859-1 charset, So was wondering if there are any mechanisms/scripts/utilities to parse this and convert to string format so that I could apply my procmail filter.

You may use perl one liner to decode Subject: before assigment to procmail variable.
# Store "may be encoded" Subject: into $SUBJECT after conversion to ISO-8859-1
:0 h
* ^Subject:.*=\?
SUBJECT=| formail -cXSubject: | perl -MEncode=from_to -pe 'from_to $_, "MIME-Header", "iso-8859-1"'
# Store all remaining cases of Subject: into $SUBJECT
:0 hE
SUBJECT=| formail -cXSubject:
# trigger recipe based also on $SUBJECT content
:0
* ^(To|From).*abc#cdefgh.com
* SUBJECT ?? ^Subject:.*ABC Store: New Order
{
....
}
Comment (2020-03-07): It may be better to convert to UTF-8 charset instead of ISO-8859-*.

You should use MIME::EncWords.
Like this
use strict;
use warnings;
use 5.010;
use MIME::EncWords 'decode_mimewords';
my $subject = '=?UTF-8?B?QUJDIFN0b3JlOiBOZXcgT3JkZXI=?=';
my $decoded = decode_mimewords($subject);
say $decoded;
output
ABC Store: New Order

Related

Coldfusion Encryption and Perl Decryption

I have a situation where I need to encrypt content in Coldfusion and then decrypt in Perl. Here's a sample Coldfusion code:
<cfscript>
input = "Amidst the roar of liberated Rome, Of nations freed, and the world overjoy'd";
encryptionKey = "8kbD1Cf8TIMvm8SRxNNfaQ==";
encryptedInput = encrypt( input, encryptionKey, "AES/ECB/PKCS5Padding", "hex" );
writeOutput( "Encrypted Input: #encryptedInput# <br />" );
</cfscript>
This produces:
27B0F3EB1286FFB462BDD3F14F5A41724DF1ED888F1BEFA7174CA981C7898ED2EF841A15CDE4332D030818B9923A2DBA0C68C8352E128A0744DF5F9FA955D3C72469FEFDAE2120DE5D74319ED666DDD0
And the Perl:
use 5.24.1;
use Crypt::ECB qw(encrypt_hex);
my $input = "Amidst the roar of liberated Rome, Of nations freed, and the world overjoy'd";
my $encryption_key = "8kbD1Cf8TIMvm8SRxNNfaQ==";
my $encrypted_input = encrypt_hex($encryption_key, 'Rijndael', $input);
say $encrypted_input;
This produces:
e220ff2efe5d41e92237622ba969f35158d20e2c9c44995d44136d928d517462980321d4d6193fe62dc942fd717128442972524207777366954e5ceb2d1812ac997e06767a27d6a0145176d717c3836b
Why is the encrypted content different? Does anyone have any insights into this?
Your encryption key is base64 encoded, but Crypt::ECB expects a raw byte string (this isn't clear from the docs, though).
use Convert::Base64;
...
my $encryption_key = decode_base64("8kbD1Cf8TIMvm8SRxNNfaQ==");
...
New output:
27b0f3eb1286ffb462bdd3f14f5a41724df1ed888f1befa7174ca981c7898ed2ef841a15cde4332d030818b9923a2dba0c68c8352e128a0744df5f9fa955d3c72469fefdae2120de5d74319ed666ddd0

Perl sendmail to list is truncated

I am working with a perl based cgi pages for a webproject. I am encountering a strange problem with sendmail module which happens randomly.
Problem:
Sendmail would truncate the emails of the users appended at last. But not always, it happens randomly. I log the email list right before sending email and I don't see anything wrong.
Example Image (See Karl's last name is truncated at '.' after his first name.)
Headers for the email.
Message-ID: <201305221503.r4MF3dYf022792#pazmo.internal.company.com>
Subject: < ...>
MIME-Version: 1.0
Content-Type: text/plain
To: <biradavolu.ln#company.com>, <dessimira.ln#company.com>,
<yun.ln#company.com>, karl.
Date: Wed, 22 May 2013 10:03:39 -0500
From: <tool#company.com>
Return-Path: tool#company.com
X-MS-Exchange-Organization-AuthSource: eusaamw0712.domain.company.com
X-MS-Exchange-Organization-AuthAs: Internal
X-MS-Exchange-Organization-AuthMechanism: 10
X-MS-Exchange-Organization-AVStamp-Mailbox: MSFTFF;1;0;0 0 0
The logged input before sending email: ( I don't see anything wrong with the format)
biradavolu.ln#company.com;dessimira.ln#company.com;yun.lastName#company.com;karl.LastName#company.com;
use Mail::Sendmail;
# Step 1: Declare the mail variable
%mail = (
from => 'test#company.com',
to => 'user1FN.user1LN#company.com;user2FN.user2LN#company.com;user3FN.user3LN#company.com;' . "$requester_email; $responsible_email",
subject => ... ,
'content-type' => "multipart/alternative; "
);
my $toList='user1FN.user1LN#company.com;user2FN.user2LN#company.com;user3FN.user3LN#company.com;' . "$requester_email;";
# Step 2: Add members to toList based on different conditions
if(condition1)
$toList= $toList.'user4FN.user4LN#company.com;';
if(condition2)
$toList= $toList.'user5FN.user5LN#company.com;';
... # few other similar condition statement
...
# Step 3: Assign toList based on different conditions
$mail{ 'to' } = $toList;
# Step 4: Set Body of the $mail
if(sendmail(%mail)){
print LOGFILE "Mail send successfully to $mail{\"to\"}: ";
}else{
print LOGFILE "Mail was not send : Mail list was $mail{\"to\"} : ";
}
Wild guess here. You're hiding the actual lastnames of your users (which is fine), but it could be possible that the "random" truncating is always happening on a user with a space in the last name? Like "St. Pierre". Your string might get truncated right at the space.
Let me know if that's possible!

Perl to parse email, change "From:" header, send onwards

I wish I was lying, but I've spent several months trying to get this to work and I have to admit defeat on my perl scripting skills. I'm at a loss to make this work and need help (for which I wil be very grateful).
The background:
I am running a discussion email list using a third party Listserv. I want to change the "From" header on incoming emails to an address at my domain, by doing a database lookup for the email address, and then adding the users name and company code to the From header, and sending it on.
For example, Super Dave , is changed to David Smith (ABC - LON) , and then the list members will see that header instead of whatever he has chosen as his "From free text".
The script I have developed works very well ... except that more complex emails seem to stun it. Right now the script takes a text version of the email, strips out all the MIME parts and html bits, and changes the header. If it encounters an email format thats new to it (and I havent written a code line to handle), it stops. I could continue fixing each type of email coming in, but I think thats overkill - I need to get back to the KISS method.
Note: the database lookup is without issue. The problem is in the way the email body finally arrives at the listserver.
Instead of this, I want to leave the original email untouched, but just change the From header. Nothing else. Is there any way to do that? Here is (the salient part of) the script.
What Im after is a much simpler method to search the email for the from Header, change it to another value, and then send it on.
Thoughts?
$connect = DBI->connect($dsn, $user, $pw);
open FH, ">mail.txt" or die "can't open mail.txt: $!";
while ( $_ = <STDIN>) {
print FH "$_";
}
close(FH);
$file_content = `cat 'mail.txt' | grep -m1 From |tail -n+1`;
chomp($file_content);
$from = `echo "$file_content"| sed -e "s/.*<//;s/>.*//"`;
chomp($from);
$subject=`cat mail.txt |grep -m1 Subject| sed -e "s/.*Subject: //"`;
chomp($subject);
system('./body.sh');
$encoded=`cat body.txt`;
#Decode the mail and save output to dbody.txt. Still have header+body at this stage.
$body=decode_qp($encoded);
open FF, ">dbody.txt" or die $!;
print FF $body;
close FF;
#If body still has headers, Look for first blank line, and delete all before - this is the body
$bodycheck =`cat dbody.txt`;
if ($bodycheck =~ /Message-Id/ ){
$bodyfinal= `sed '0,/^\$/d' dbody.txt`;
} else {
$bodyfinal =$bodycheck
}
#Save the output to bodyfinal.txt
open FF, ">bodyfinal.txt" or die $!;
print FF $bodyfinal;
close FF;
#THIS SECTION contains code to query the database with the original FROM email address
#get username and domain and then change to lower case for the query
$case_username = substr($from, 0, index($from, '#'));
$m_username = lc($case_username);
$case_domain = substr($from, index($from, '#')+1);
$m_domain = lc($case_domain);
#print "\n##############$m_username\#$m_domain#################\n";
$query = "select user_real_name, company_code, location_code from user where user_email='$m_username\#$m_domain'";
$query_handle = $connect->prepare($query);
$query_handle->execute() or die $DBI::errstr;
#result=$query_handle->fetchrow_array();
print "\n#result\n";
##Forward the mail
sub sendEmail
{
my ($to, $from_sub, $subject, $message) = #_;
my $sendmail = '/usr/sbin/sendmail';
open(MAIL, "|$sendmail -oi -t");
print MAIL "From: $from_sub\n";
print MAIL "To: $to\n";
print MAIL "Subject: $subject\n\n";
print MAIL "$message\n";
close(MAIL);
}
{my $msg = MIME::Lite->new
(
Subject => "$subject",
From => "$result[0] ($result[1]/$codes[0]-$result[2])<listmail#>",
To => 'opg#maillist.com',
Type => 'text/plain',
Encoding => '7bit',
Data => "From: $result[0]/$result[1]-$codes[0]/$result[2] \n________________________________________________ \n \n$bodyfinal \n"
);
$msg->send();
}
To only answer "what is a simple method to search some file for a From: header,
change it to another value, and send it on?": use Tie::File;
Given a file named 'email' that contains the example headers from this page,
#! /usr/bin/env perl
use common::sense;
use Tie::File;
tie my #f, 'Tie::File', 'email' or die $!;
for (#f) {
if (/^From:/) {
say "old: $_";
s/(?<=^From:).*$/ A New Sender <anewsender\#ans.com>/;
say "new: $_";
last
}
}
untie #f;
Output:
$ perl tie-ex
old: From: Taylor Evans <example_from#dc.edu>
new: From: A New Sender <anewsender#ans.com>
$ grep ^From email
From: A New Sender <anewsender#ans.com>
Mind, there's all kinds of wrong with this. Headers don't need to be neatly on one line; there can be more than one From: header (by someone else's scripting error, for instance); there can even be no From: header in the headers and then a From: randomly in the body. Spammers do strange things. But if your original code already contains these limitations and you're happy enough with them, try this.
Meanwhile, there are already great Perl modules that handle mail. Take a look through the Email:: modules listed here.

Sending email to multiple recipients

I've moved some old code from an old unix box to our new unix box, and I'm having some difficulty with a perl script sending email to multiple recipients. It works on the old box.
Old box perl: version 5.004_04 built for PA-RISC2.0
New box perl: v5.8.8 built for IA64.ARCHREV_0-thread-multi-LP64
Here's the basics of the script (stripped-down):
use Net::SMTP::Multipart;
$to = "sam\#bogus.com tom\#foo.com";
$smtp = Net::SMTP::Multipart->new($smtpserver);
$smtp->Header(To => $to,
From => "junk\#junk.com",
Subj => "This is a test.");
$smtp->Text("Hello, world!\n");
$smtp->End();
This works if I change it to $to = "justOneEmail\#address.com", but if I have two or more email addresses (separated by spaces), it no longer works. I don't get an error message, but no message shows up.
Any ideas why?
Do it like this:
use Net::SMTP::Multipart;
$to1 = "sam\#bogus.com";
$to2 = 'tom#foo.com';
$smtp = Net::SMTP::Multipart->new($smtpserver);
$smtp->Header(To => [ $to1, $to2, 'another_email#server.com' ],
From => "junk\#junk.com",
Subj => "This is a test.");
$smtp->Text("Hello, world!\n");
$smtp->End();
Notice that if you use double-quotes, you should escape the # in the email addresses, or perl may try to interpret it as an array interpolation.
Instead of separating the email addresses with spaces, use a comma with no intervening spaces. This works for me..
Declare an array and put all the email id's like
#MailTo = ('mail1#demomail.com', 'mail2#demomail.com', ...., 'mailn#demomail.com')
Now use the Net::SMTP module to send out the emails
$smtp->to(#MailTo);

base64-Encoding breaks smime-encrypted emaildata

I'm using Mime::Lite to create and send E-Mails. Now I need to add support for S/Mime-encryption and finally could encrypt my E-Mail (the only Perllib I could install seems broken, so I'm using a systemcall and openssl smime), but when I try to create a mime-object with it, the E-Mail will be broken as soon as I set the Content-Transfer-Encoding to base64. To make it even more curious, it happens only if I set it via $myMessage->attr. If I'm using the constructor ->new everything is fine, besides a little warning which I suppress by using MIME::Lite->quiet(1);
Is it a bug or my fault? Here are the two ways how I create the mime-object.
Setting the Content-Transfer-Encoding via construtor and suppress the warning:
MIME::Lite->quiet(1);
my $msgEncr = MIME::Lite->new(From =>'me#myhost.com',
To => 'you#yourhost.com',
Subject => 'SMIME Test',
Data => $myEncryptedMessage,
'Content-Transfer-Encoding' => 'base64');
$msgEncr->attr('Content-Disposition' => 'attachment');
$msgEncr->attr('Content-Disposition.filename' => 'smime.p7m');
$msgEncr->attr('Content-Type' => 'application/x-pkcs7-mime');
$msgEncr->attr('Content-Type.smime-type' => 'enveloped-data');
$msgEncr->attr('Content-Type.name' => 'smime.p7m');
$msgEncr->send;
MIME::Lite->quiet(0);
Setting the Content-Transfer-Encoding via $myMessage->attr which breaks the encrypted Data, but won't cause a warning:
my $msgEncr = MIME::Lite->new(From => 'me#myhost.com',
To => 'you#yourhost.com',
Subject => 'SMIME Test',
Data => $myEncryptedMessage);
$msgEncr->attr('Content-Disposition' => 'attachment');
$msgEncr->attr('Content-Disposition.filename' => 'smime.p7m');
$msgEncr->attr('Content-Type' => 'application/x-pkcs7-mime');
$msgEncr->attr('Content-Type.smime-type' => 'enveloped-data');
$msgEncr->attr('Content-Type.name' => 'smime.p7m');
$msgEncr->attr('Content-Transfer-Encoding' => 'base64');
$msgEncr->send;
I just don't get why my message is broken when I'm using the attribute-setter. Thanks in advance for your help!
Besides that i'm unable to attach any file to this E-Mail without breaking the encrypted message again.
To debug this
Make a script call showmail.pl
#!/usr/bin/perl
while (<STDIN>) { print $_; }
Test it like
use MIME::Lite;
use Net::SMTP;
use MIME::Base64;
$myEncryptedMessage = encode_base64("This is not valid encrypted message\n");
MIME::Lite->send('sendmail', "./showmail.pl"); ## Add this for debugging.
MIME::Lite->quiet(1); my $msgEncr = MIME::Lite->new(From =>'me#localhost',
To => 'you#localhost',
Subject => 'SMIME Test',
Data => $myEncryptedMessage,
'Content-Transfer-Encoding' => 'base64');
$msgEncr->attr('Content-Disposition' => 'attachment');
$msgEncr->attr('Content-Disposition.filename' => 'smime.p7m');
$msgEncr->attr('Content-Type' => 'application/x-pkcs7-mime');
$msgEncr->attr('Content-Type.smime-type' => 'enveloped-data');
$msgEncr->attr('Content-Type.name' => 'smime.p7m');
$msgEncr->send();
you should see something like.
MIME-Version: 1.0
Content-Disposition: attachment; filename="smime.p7m"
Content-Length: 49
Content-Type: application/x-pkcs7-mime; name="smime.p7m"; smime-type="enveloped-data"
X-Mailer: MIME::Lite 3.028 (F2.74; B3.07; Q3.07)
Date: Mon, 23 Mar 2012 10:40:51 -0400
From: me#localhost
To: you#localhost
Subject: SMIME Test
Content-Transfer-Encoding: base64
VGhpcyBpcyBub3QgdmFsaWQgZW5jcnlwdGVkIG1lc3NhZ2UK
The message is encoded base64, but the real message still needs to be correctly
encypted. You need to make sure that is the case since $myEncryptedMessage is
passed in. With the debug output, you can compare with a known good encrypted mail
and see if the headers are good, as far as I can see the headers are fine, it is probably
the data that is not valid.
I am not able to test this with a real mail client, but this is what I think may work for multi-parts.
use MIME::Lite;
use Net::SMTP;
use MIME::Base64;
MIME::Lite->send('sendmail', "./showmail.pl"); ## <---- for testing only
my $from_address = "nobody#localhost";
my $to_address = "somebody#localhost";
my $mail_host = "localhost";
my $subject = "Subject list";
my $message_body = "Attachment list";
my #files = ("crypt.data1","crypt.data2");
$msg = MIME::Lite->new (
From => $from_address,
To => $to_address,
Subject => $subject,
Type =>'multipart/mixed'
) or die "Error creating multipart container: $!\n";
foreach $c(#files) {
$msg->attach (
Disposition => 'attachment',
Type => "application/x-pkcs7-mime; name=smime.p7m; smime-type=enveloped-data",
Path => $c,
) or die "Error adding $c: $!\n";
}
$msg->send;
As I said in one comment the difference in setting the encoding in the construtor of the mimeobject or with the ->attr-Setter is, that the construtor just sets the encoding in the mimeheader. By using the ->attr-Setter mime encodes the data with base64.
So in my case, my previously generated mimeobject - which is base64-encoded and with s/mime encrypted - read from a file needs to set the encoding in the construtor (and suppress the warning) so no more encoding will be done by mime. Otherwise mime will encode the data again and therefore break the encryption and the email itself.
I finally got attachments to work. To achieve this I create a normal multipart/mixed mimeobject, print this object into a normal file, encrypt this file with openssl smime, read this whole file (except the 6 headerlines) into a variable and use this as the datainput. Additionally I set the Content-Transfer-Encoding to base64 using the construtor (so no encoding is done to my data).
I hope this will help someone else then me ;)
Replace $myEncryptedMessage with encode_base64($myEncryptedMessage)
and use MIME::Base64;