Downloading attachment from Exchange message with Perl - perl

I am automatically downloading mails from an Exchange 2010 server via perl. So far I have managed to access the message via Exchange Web Services (EWS) and parse headers. Now I wonder how I can download the attachments of a message to a local temporary folder.
I am new to Perl language and cannot find the source code or documentation for the message data structure. Any help is appreciated.
use Email::Folder::Exchange;
use Email::Simple;
# some more code here....
my $folder = Email::Folder::Exchange->new($url, $user, $pass);
for my $message ($folder->messages) {
if ($message->header('Subject') =~ /Downloadable Message/) {
// How to access message's attachments?
}
}

So basically the trick is to convert the Email::Simple to Email::MIME and use Email::MIME::Attachment::Stripper to parse through each attachment. Easy ;-)
! I only copied the relevant parts... so you might need to extend it a little for reuse.
use Email::Folder::Exchange;
use Email::Simple;
use Email::MIME::Attachment::Stripper;
# some more code here....
my $folder = Email::Folder::Exchange->new($url, $user, $pass);
for my $message ($folder->messages) {
my $tmpMsg = Email::MIME->new($message->as_string);
my $stripper = Email::MIME::Attachment::Stripper->new($tmpMsg);
for my $a ($stripper->attachments()) {
next if $a->{'filename'} !~ /csv/i; #only csv attachments
my $tempdir = "C:\\temp\\";
my $tmpPath = $tmpdir . $a->{'filename'};
# Save file to temporary path
my $f = new IO::File $tmpPath, "w" or die "Cannot create file " . $tmpPath;
print $f $a->{'payload'};
}
}

Related

perl pdf::api2 checking if a pdf file is encrypted

I have a website using a perl script for customers to upload a pdf file for me to print and post the printed pages to them.
I am using PDF::API2 to detect the page size and number of pages in order to calculate the printing costs.
However, if the pdf file is password protected this does not work and I get this error -
Software error:
Objind 9 does not exist at index 0 at /home5/smckayws/public_html/hookincrochet.com/lib//PDF/API2/Basic/PDF/File.pm line 758.
I am trying to use the isEncrypted feature in the pdf::api2 module to catch that the file is encrypted in order to direct the customer to a different page so they can enter the page size and page number manually, but it is not working for me.
I just get the same error message as above.
I have tried the following code snippets found elsewhere.
my $pdf = PDF::API2->open( "$customer_directory/$filename" );
if ( defined $pdf && $pdf->isEncrypted )
{
print "$pdf is encrypted.\n";
exit;
}
while (glob "*.pdf") {
$pdf = PDF::API2->open($_);
print "$_ is encrypted.\n" if $pdf->isEncrypted();
}
Any help would be greatly appreciated.
My guess is that the PDFs might use a feature that your version of PDF::API2 doesn't support. This is a workaround for the problem.
Wrap the call to isEncrypted in an eval, catch the error and handle it.
This will only work if the error does not occur on unencrypted files.
my $pdf = PDF::API2->open( "$customer_directory/$filename" );
if ( defined $pdf ) {
eval { $pdf->isEncrypted };
if ($#) {
# there was some kind of error opening the file
# could abort now, or look more specific, like this:
if ($# =~ m/Objind 9 does not exist at index 0/) {
print "$pdf is encrypted.\n";
exit;
}
}
# file is not encrypted, opening worked, continue reading it
}

Concrete5.7.5.2 - Where to put form file attachment headers?

I build my email headers like this:
$txt_message .= $this->txt_message;
$html_message .= $this->html_message;
$mh = Core::make('helper/mail');
$mh->to($this->email_to, $this->site_name);
$mh->from($this->email, $this->name);
$mh->replyto($this->email, $this->name);
$mh->setSubject($this->subject);
$mh->setBody($txt_message);
$mh->setBodyHtml($html_message);
#$mh->sendMail();
Some posts say an attachment can be added with
$mh->addAttachment($file);
but $file must be a file object. How can I make the uploaded file a file object?
I also found this post:http://www.adrikodde.nl/blog/2012/mail-attachments-concrete5/
But I get errors for all Zend stuff. Is Zend Mail still available in C5.7?
Where do I put headers for a file attachment? Where can I find out more about what really sends the message (is it still a Zend Mail?) and what methods are available?
Thank you.
[SOLVED]
Thanks to Nicolai, here's a working example for attaching files:
$file = $_FILES['image']['tmp_name'];
$filename = $_FILES['image']['name'];
$importer = new \Concrete\Core\File\Importer();
$file_version = $importer->import($file, $filename);
$attachment = $file_version->getFile();
$mh->addAttachment($attachment);
//Delete the file if not wanted on server
$attachment->delete();
PS. Don't forget to check the file really selected/exists/uploaded before you try to send it!
if (!empty($this->image)) {
$importer = new \Concrete\Core\File\Importer();
$image_version = $importer->import($this->image, $file_name);
if ($image_version instanceof \Concrete\Core\File\Version) {
$attachment = $image_version->getFile();
$mh->addAttachment($attachment);
}
}
#$mh->sendMail();
To add the file to your filesystem, you should take a look at this
http://concrete5.org/api/class-Concrete.Core.File.Importer.html.
On the returned object (which is a FileVersion on success), you should be able to call getFile( ) to get the actual Concrete5 File object

Perl parse email and attachments from Outlook inbox

I'm using Mail::IMAPClient to connect to our Outlook mail server. I can get the mail just fine and print the text version of that mail to a file. But I'm having trouble using MIME::Parser to parse through the email.
I've tried giving the parser a file handle to the text file that I wrote the email to. I've tried giving the parser just the text of the email but it won't work how I'm expecting it to work. The entity parts always equals 0.
When I dump the entity skeleton I get
Content-type: text/plain
Effective-type: text/plain
Body-file: NONE
--
I can see all of the parts of the email in the file. The two PDFs that are attached are there, encoded in base64, so I know that the script is actually retrieving the email and the attachments. I've also tried parse and parse_data.
my $msgCount = 0;
$msgCount = $imap->message_count();
#or abortMission("", "Could not get message count: ". $imap->LastError );
if ( $msgCount > 0 ) {
#get all the messages from the inbox folder
my #msgseqnos = $imap->messages
or abortMission("", "Could not retreive messages:". $imap->LastError);
my ($x, $bh, $attachment, $attachmentName);
foreach my $seqno ( #msgseqnos ) {
my $input_file;
my $parser = new MIME::Parser;
my $emailText = $imap->body_string($seqno) # should be the entire email as text.
or abortMission("", "Could not get message string: " . $imap->LastError);
$parser->ignore_errors(1);
$parser->output_to_core(1);
open my $emailFileHandle, ">", "invoiceText.txt";
print $emailFileHandle $emailText;
#$imap->message_to_file($emailFileHandle, $seqno);
my $entity = $parser->parse_data($emailText);
$entity->dump_skeleton;
if ( $entity->parts > 0 ) {
for ( my $i = 0; $i < $entity->parts; $i++ ) {
my $subentity = $entity->parts($i);
# grab attachment name and contents
foreach $x ( #attypes ) {
if ( $subentity->mime_type =~ m/$x/i ) {
$bh = $subentity->bodyhandle;
$attachment = $bh->as_string;
$attachmentName = $subentity->head->mime_attr('content-disposition.filename');
open FH, ">$attachmentName";
print FH $attachment;
close FH;
#push #attachment, $attachment;
#push #attname, $subentity->head->mime_attr('content-disposition.filename');
}
}
}
}
else {
stillAGo("eData VehicleInvoices problem", "Perl can't find an attachment in an email in the VehicleInvoices folder of eData email address");
}
close $emailFileHandle;
# say $emailText;
# next;
#open OUT_FILE, ">invoiceText.txt";
#print OUT_FILE $emailText;
#print OUT_FILE $imap->bodypart_string($seqno,1);
#close OUT_FILE;
#print $emailText;
}
}
I'm trying to retrieve the attachments from emails automatically and save them to disk to be processed by another job.
I'd like to include the invoiceText.txt file so people can see the actual output but it's 1200 lines long. I'm not sure where to upload a file to link in here.
The body_string method doesn't return the entire email. As the documentation describes, and the name implies, it returns the body of the message, excluding the headers. That is why dump_skeleton shows no headers apart from the defaults
What you probably want, although I haven't tried it, is message_string, which does return the entire email
I see you've used message_to_file but commented it out. That would probably have worked if you got MIME::Parse to read from the file

Perl - How to get the email address from the FROM part of header?

I am trying to set up this script for my local bands newsletter.
Currently, someone sends an email with a request to be added, we manually add it to newsletter mailer I set up.
(Which works great thanks to help I found here!)
The intent now is to have my script below log into the email account I set up for the list on our server, grab the info to add the email automatically.
I know there are a bunch of apps that do this but, I want to learn myself.
I already have the "add to list" working when there is an email address returned from the header(from) below BUT, sometimes the header(from) is a name and not the email address (eg "persons name" is returned from persons name<email#address> but, not the <email#address>.)
Now, I am not set in stone on the below method but, it works famously... to a point.
I read all the docs on these modules and there was nothing I could find to get the darn email in there all the time.
Can someone help me here? Verbose examples are greatly appreciated since I am struggling learning Perl.
#!/usr/bin/perl -w
##########
use CGI;
use Net::IMAP::Simple;
use Email::Simple;
use IO::Socket::SSL; #optional i think if no ssl is needed
use strict;
use CGI::Carp qw(fatalsToBrowser warningsToBrowser);
######################################################
# fill in your details here
my $username = '#########';
my $password = '#############';
my $mailhost = '##############';
#######################################################
print CGI::header();
# Connect
my $imap = Net::IMAP::Simple->new($mailhost, port=> 143, use_ssl => 0, ) || die "Unable to connect to IMAP: $Net::IMAP::Simple::errstr\n";
# Log in
if ( !$imap->login( $username, $password ) ) {
print STDERR "Login failed: " . $imap->errstr . "\n";
exit(64);
}
# Look in the INBOX
my $nm = $imap->select('INBOX');
# How many messages are there?
my ($unseen, $recent, $num_messages) = $imap->status();
print "unseen: $unseen, <br />recent: $recent, <br />total: $num_messages<br />\n\n";
## Iterate through unseen messages
for ( my $i = 1 ; $i <= $nm ; $i++ ) {
if ( $imap->seen($i) ) {
my $es = Email::Simple->new( join '', #{ $imap->top($i) } );
printf( "[%03d] %s\n\t%s\n", $i, $es->header('From'), $es->header('Subject'));
print "<br />";
next;
}## in the long version these are pushed into different arrays for experimenting purposes
else {
my $es = Email::Simple->new( join '', #{ $imap->top($i) } );
printf( "[%03d] %s\n\t%s\n", $i, $es->header('From'), $es->header('Subject'));
print "<br />";
}
}
# Disconnect
$imap->quit;
exit;
use Email::Address;
my #addresses = Email::Address->parse('persons name <email#address>');
print $addresses[0]->address;
The parse method returns an array, so the above way works for me.
I'm making this a separate answer because even though this information is hidden in the comments of the accepted answer, it took me all day to figure that out.
First you need to get the From header using something like Email::Simple. THEN you need to extract the address portion with Email::Address.
use Email::Simple;
use Email::Address;
my $email = Email::Simple->new($input);
my $from = $email->header('From');
my #addrs = Email::Address->parse($from);
my $from_address = $addrs[0]->address; # finally, the naked From address.
Those 4 steps in that order.
The final step is made confusing by the fact that Email::Address uses some voodoo where if you print the parts that Email::Address->parse returns, they will look like simple strings, but they are actually objects. For example if you print the result of Email::Address->parse like so,
my #addrs = Email::Address->parse($from);
foreach my $addr (#addrs) { say $addr; }
You will get the complete address as output:
"Some Name" <address#example.com>
This was highly confusing when working on this. Granted, I caused the confusion by printing the results in the first place, but I do that out of habit when debugging.

Perl ftp question, like the previous ones

I need to move or copy a simple text file from one web site to another web site.
I have administrator rights to both web sites. The first web site has a large data file (again, just a text file), certain records are selected and written to a team file (for entry into a tournament). Next I go through paypal and pay for the entries. The second site is for the the club running the tournament and I use IPN to return to a script on their site and if it verified, I add the team memebers into the master file for the tournament. I am limited to the ONE IPN script on the tournament site because I have a ton of other entries that come in from all over. The first site has the rosters for the state and no need to type all that data from each club, use the rosters like I use for all the non-paypal tounamenmts.
I can ftp the team file to the second server and place it in the folder just like it was created from scratch from that server originally and everything should go fine but I took the examples and tried them and nothing.
Here's the code section:
my $custom = $in->param('custom');
my $filename = "$ENV{DOCUMENT_ROOT}/database/$custom";
my $usjochost = '208.109.14.105';
my $okserieshost = '208.109.181.196';
my $usjocuser = 'teamentry';
my $okseriesuser = 'okwaentry';
my $usjocpw = 'Password1';
my $okseriespw = 'Password1';
my $file = $custom;
my $usjocpath ='/home/content/u/s/j/usjoc/html/database/';
my $okseriespath ='/home/content/o/k/s/okseries/html/database/';
$ftp = Net::FTP->new($okserieshost, Debug => 0) or die "Could not connect to '$okserieshost': $#";
$ftp->login($okseriesuser, $okseriespw) or die sprintf "Could not login: %s", $ftp->message;
#$ftp->cwd(/database) or die sprintf "Could not login: %s", $ftp->message;
$ftp->get($filename);
#$ftp = Net::FTP->new($usjochost, Debug => 0) or die "Could not connect to '$usjochost': $#";
$ftp->quit;
I NEED to READ the file on the first web site (okseries.com) and write the file on the second web site (usjoc.com). I have no problem reading and writing the file on the server, is sending the file to the second server. HELP! I'm not a genius at PERL.
i tested the code you made and implemented to it, the follow code will connect to your first host, okserieshost and get the $filename from database folder, verify if the file was downloaded (if not it will end operations).
#!/usr/bin/perl
use Net::FTP;
my $path = '/public_html/api';
my $filename = 'index.php';
my $host = '';
my $user = '';
my $pass = '';
print "Content-type: text/html\n\n";
$ftp = Net::FTP->new($host, Debug => 0) or die "Could not connect to $host: $#";
print "<pre>".$ftp->message ."</pre><br>\n";
$ftp->login($user,$pass) or die sprintf "Could not login: %s", $ftp->message;
print "<pre>".$ftp->message ."</pre><br>\n";
my $cur = $ftp->pwd();
my $new = $ftp->cwd($path);
if ($cur == $new) {
$ftp->quit;
print "Directory not found, exiting.\n";
} else {
if ($ftp->size($path.'/'.$filename) >= 0) {
$ftp->get($path.'/'.$filename) or die $ftp->message;
print "<pre>".$ftp->message ."</pre><br>\n";
print "File downloaded with success." if (-e $filename);
} else {
print "File not found.\n";
}
}
$ftp->quit;
i've changed the code a little you can put this file on usjoc.com and run it on the browser it wil display every step of the communication until it gets the file from okseries.com.
All you have to do is change $path to the path of where the file is but do not end the directory name with a /
filename in case on $filename
$host = ftp ip or hostname
$uesr and $pass i guess you know what goes in
response from the url you asked to be viewed:
USJOC Entry Form on
The Club file name is EdmondSkunks1T.db
/home/content/o/k/s/okseries/html/database/EdmondSkunks1T.db
Supposedly opened /home/content/o/k/s/okseries/html/database/EdmondSkunks1T.db
Back to USJOC