I currently have a working Perl code that fires off an email to the correct email address with my generic subject and content.
However, currently whenever a new user starts, we fire off a premade .oft template that says everything that they need to know. The .oft is stored on our server. I was wondering is there a way to alter the perl code to make it so it takes the to: but uses the .oft template to make the rest of the email??
so basically
$smtp = Net::SMTP->new('smtp.blah');
$smtp->('no-replay#blah.com');
$smtp->to('$personEmail');
$smtp->data();
$smtp->datasend($locationOfOftTemplate);
$smtp->dataend();
$smtp->quit;
AFAIK there is no module for handling this proprietary format. Instead, translate it to a standard templating system:
my $template = <<'TEMPLATE';
Good morning, [% name %]!
Today is your first day at work. You have already received your universal login
credentials:
[% ldap_user %]
[% ldap_pass %]
It works for authenticating at the Web proxy, mail system, Jabber and internal
services. Change the password ASAP: <[% ldap_web %]>
--
Yours sincerely, the greeting daemon
TEMPLATE
use Text::Xslate qw();
my $text = Text::Xslate->new(syntax => 'TTerse')->render_string($template, {
name => 'New Bee',
ldap_user => 'nbee',
ldap_pass => 'CON-GLOM-O',
ldap_web => 'http://192.168.0.1:8080/',
});
Use a MIME toolkit to create emails. HTML/multipart/attachments are easy with
Courriel::Builder:
use Courriel::Builder;
my $email = build_email(
subject('Welcome'),
from('no-reply#example.com'),
to('…'),
plain_body($text),
);
Finally, send it using the high-level library Email::Sender that gives you nice error checking and allows you to easily switch out transports - run local delivery for testing.
use Email::Sender::Simple qw(sendmail);
use Email::Sender::Transport::SMTP qw();
use Try::Tiny;
try {
sendmail(
$email,
{
transport => Email::Sender::Transport::SMTP->new({
host => 'smtp.example.invalid',
})
}
);
} catch {
warn "sending failed: $_";
};
Related
I want to extract the body of an email from a gmail url: https://mail.google.com/mail/u/0/#inbox/FMfBlahwDrHlsBlahlzHWzQXHFKhjpTp1
I am using the Perl module: Net::IMAP::Simple::Gmail which gets me sequential access to the email inboxes and their imap ids. It also has a search method for searching emails. It also provides imap thread ids of each email.
But I haven't found an easy way to link the thread's URL in the browser to a particular imap email id so I can extract the body.
Here is a solution, though it's a bit convoluted:
Navigate to the gmail message thread of interest in your browser
Run the following JS in the developer console (or better, programmatically with Applescript, if using Safari, or some other browser automation program like WWW::Mechanize::Chrome) to get the "legacy" thread id:
document.querySelector('[data-legacy-thread-id]').getAttribute('data-legacy-thread-id')
Once you get the thread id, you then need to convert it to a decimal number. In Perl:
my $dec_num = sprintf("%d", hex($thread_id));
Now that you have obtained the thread id, you can use the Perl Mail::IMAPClient cpan module to obtain the messages in the thread by searching on the thread id:
use Mail::IMAPClient;
my $imap = Mail::IMAPClient->new(
Server => 'imap.gmail.com',
User => 'me',
Password => 'blah',
Ssl => 1,
Uid => 1,
);
my $folders = $imap->select('INBOX');
my $msgs = $imap->search("X-GM-THRID", $decimal_thread_id);
foreach my $msg (#$msgs) {
my $msg_st = $imap->message_string($msg);
# slice and dice messages with modules listed below
}
Now you can use cpan modules like Email::MIME and Email::MIME::Attachment::Stripper and Email::MIME::Encodings to parse the emails in $msgs and decode them as necessary.
this question is related to HTML image not showing in Gmail , but the answers there do not (or no longer) work.
the problem is that my perl program (below) fails when my html-formatted messages that I want to send off as soon as an img tag is included. it does not matter whether the image itself is served from the google drive or not. I am guessing that I am running into a novel gmail restriction (my script used to work), but I am not sure.
(of course, this problem is not that my recipients do not see the image; it is that the sending perl script aborts with an error---unfortunately, not with more information explaining to me why it is an error. of course, I understand that my recipients need to agree to view images to prevent tracking.)
so here are my questions:
is this a gmail or a perl module problem?
is it possible to send images, so that if my recipients want to see
images from my website (preferably not just images from my google drive as in my example below), they can agree to see this?
is it possible to get a better error message from google about why it fails?
here is the [almost] working code:
#!/usr/bin/perl -w
use strict;
use warnings;
use Email::MIME::CreateHTML;
use Email::Send;
use Email::Send::Gmail;
my $toemail = 'ivo.welch#gmail.com';
my $subject = 'testing image mailing';
my $bodytext= '<html> <body> fails: <img src="https://drive.google.com/open?id=1K4psrWWolTSqx_f6MQP-T1-FMFpegT1Trg" alt="photo" /> </body> </html>\n';
use readcredentials;
my $gmailaccount= readcredentials( 'account' );
my $gmailuserlogin= readcredentials( 'userlogin');
my $gmailpasswd= readcredentials( 'gmailpassword');
eval {
my $message = Email::MIME->create_html(
header => [
From => $gmailaccount,
To => $toemail,
Subject => $subject,
],
body => $bodytext,
);
my $sender = Email::Send->new(
{ mailer => 'Gmail',
mailer_args => [
username => $gmailuserlogin,
password => $gmailpasswd,
]
}
);
$sender->send($message);
};
warn "Error sending email: $#" if $#;
print STDERR "emailed!\n";
I ran your script, with some modifications to remove the dependency on readcredentials to make it run in my environment, and the email was delivered with no problem. Problems could be:
You could have some problems with your gmail credentials.
The script downloads and attaches the image, so perhaps your local environment is preventing the image from being downloaded.
But without your specific error message, it's hard to diagnose any further.
I am getting error as "Internal Server Error.The server encountered an internal error or misconfiguration and was unable to complete your request."
I am submitting a form in html and get its values.
HTML Code (index.cgi)
#!c:/perl/bin/perl.exe
print "Content-type: text/html; charset=iso-8859-1\n\n";
print "<html>";
print "<body>";
print "<form name = 'login' method = 'get' action = '/cgi-bin/login.pl'> <input type = 'text' name = 'uid'><br /><input type = 'text' name = 'pass'><br /><input type = 'submit'>";
print "</body>";
print "</html>";
Perl Code to fetch data (login.pl)
#!c:/perl/bin/perl.exe
use CGI::Carp qw(fatalsToBrowser);
my(%frmfields);
getdata(\%frmfields);
sub getdata {
my ($buffer) = "";
if (($ENV{'REQUEST_METHOD'} eq 'GET')) {
my (%hashref) = shift;
$buffer = $ENV{'QUERY_STRING'};
foreach (split(/&/,$buffer)) {
my ($key, $value) = split(/=/, $_);
$key = decodeURL($key);
$value= decodeURL($value);
$hashref{$key} = $value;
}
}
else{
read(STDIN,$buffer,$ENV{'CONTENT_LENGTH'})
}
}
sub decodeURL{
$_=shift;
tr/+/ /;
s/%(..)/pack('c', hex($1))/eg;
return($_);
}
The HTML page opens correctly but when i submit the form, i get internal server error.
Please help.
What does the web server's error log say?
Independent of what it says, you must stop parsing the form data yourself. There are modules for that, specifically CGI.pm. Using that, you can do this instead:
use CGI;
my $CGI = CGI->new();
my $uid = $CGI->param( 'uid' );
my $pass = $CGI->param( 'pass' );
# rest of your script
Much cleaner and much safer.
I agree with Tore that you must not parse this yourself. Your code has multiple errors. You don't allow multiple parameter values, you don't allow the ; alternate separator, you don't handle POST with a query string in the URL, and so on.
I don't know how long it will be online for free, but chapter 15 of my new "Beginning Perl" book covers Web programming. That should get you started on some decent basics. Note that the online version is an early, rough draft. The actual book also includes Chapter 19 which has a complete Web app example.
could it be this line that's the problem?
my (%hashref) = shift;
You're initialising a proper hash, but shift will give you a hash reference, since you did getdata(\%frmfields);. You probably want this, instead:
my $hashref = shift;
"500 Internal Server Error" just means that something didn't work the way the web server expected. Maybe you don't have CGI enabled. Maybe the script isn't executable. Maybe it's in a directory the web server isn't allowed to access. It's even possible that maybe the web server ran the script successfully and it worked perfectly, but didn't start its output with a valid set of HTTP headers. You need to look in the web server's error log to find out what it didn't like, which may or may not be a Perl issue.
Like everyone else has said, though, don't try to parse query strings and grovel though %ENV yourself. Use one of the many fine modules or frameworks which are available and already known to work correctly. CGI.pm is the granddaddy of them all and works well for smaller projects, but I'd recommend looking into a proper web application framework such as Dancer, Mojolicious, or Catalyst (there are many others, but those are the big three) if you're planning to build anything with more than a handful of relatively simple pages and forms.
I’m writing a small Perl page that receives a POST method submit. I want to be able to prevent from a single person/computer to submit the form multiple times (to avoid flooding with repetitive submits). But I can’t find any examples or explanations on how to do this in Perl CGI. Could you advise or direct me to some examples?
I understand I can use some data from the HTTP header (token?) and/or plant a cookie after the first submit, but I’m not sure how.
Any help will be appreciated.
Best Regards,
-Arseny
The most simple way of avoiding users clicking the button several times would be to add some Javascript to your page. That would ofc not work for scripts or for i.e. pressing F5.
<input type="submit" name="go" id="go" value="go" onclick="this.disabled='disabled'"/>
You could also write a log file/database on the server that holds the IP address of the user and the timestamp, and check whether he as already submitted. Doing that in addition to setting and checking a cookie is probably the way to go.
For cookies, see cookies in the CGI doc. Simple example:
use strict; use warnings;
use CGI;
my $q = new CGI;
my $submitted = 0;
if ($q->cookie('submitted ')) {
$submitted = 1;
}
# Here you could place the file/db check to also set $voted
if ($submitted) {
print $q->header('text/plain');
print "You have already submitted!";
} else {
# Do stuff with the form, like $q->param('foo')...
# Once you're done, place the cookie
print $q->header(
-type => 'text/plain',
-cookie => $q->cookie(
-name => 'submitted',
-value => 1,
-expires => '+1y',
));
}
I'm trying to send a list of IPs from a Postgres query via e-mail.
I found MIME::Lite::TT and now have that installed and working. I'd like to pass 20-30 IPs out in each e-mail in a line feed delimited list. I could do it by generating a new .txt template for each e-mail I need to send, but that seems inefficient. I found Template on cpan, and I think the #list part is what I need, but I don't have any idea how to implement it.
Passing $params{ips} = "1.2.3.4\n2.3.4.5\n3.4.5.6\n" didn't work either.
Thanks for your thoughts.
This code sends a single IP successfully:
#!/usr/bin/perl -w
use MIME::Lite::TT;
# SendTo email id
my $email = 'tester#mydomain.org';
my %params;
$params{ips} = "1.2.3.4";
# create a new MIME Lite based email
my $msg = MIME::Lite::TT->new
(
Subject => "HTML email test",
From => 'admin#mydomain.org',
To => $email,
Type => 'text/html',
Template => 'test.txt',
TmplParams => \%params
);
$msg->send();
Ah. Yeah.
Without knowing what your template looks like, it's difficult to come up with a specific suggestion. However, if you set this up as:
$params{ips} = [ qw{ 1.2.3.4 2.3.4.5 3.4.5.6 } ];
and your template had a region that looked like:
[% FOREACH address IN ips %]
[% address %]
[% END %]
your problem would be solved, I think. I leave the splitting up of the array into 20–30 element lists as an exercise. :)