File Coverage

blib/lib/FWS/V2/SocketLabs.pm
Criterion Covered Total %
statement 22 151 14.5
branch 0 50 0.0
condition 0 6 0.0
subroutine 8 13 61.5
pod 3 3 100.0
total 33 223 14.8


line stmt bran cond sub pod time code
1             package FWS::V2::SocketLabs;
2              
3 1     1   18786 use 5.006;
  1         3  
  1         33  
4 1     1   6 use strict;
  1         1  
  1         29  
5 1     1   5 use warnings;
  1         13  
  1         27  
6 1     1   1668 use MIME::Lite;
  1         38889  
  1         31  
7 1     1   11 use MIME::Base64;
  1         2  
  1         43  
8 1     1   763 use Authen::SASL;
  1         1017  
  1         6  
9              
10             #
11             # not everything will be defined by nature
12             #
13 1     1   26 no warnings 'uninitialized';
  1         2  
  1         55  
14              
15             #
16             # Merge this module with the FWS V2
17             #
18 1     1   1450 BEGIN { push @FWS::V2::ISA, 'FWS::V2::SocketLabs'; }
19              
20             =head1 NAME
21              
22             FWS::V2::SocketLabs - FrameWork Sites version 2 socketlabs.com SMTP integration
23              
24             =head1 VERSION
25              
26             Version 0.02
27              
28             =cut
29              
30             our $VERSION = '0.02';
31              
32              
33             =head1 SYNOPSIS
34              
35             This module will process all outgoing mail from FWS 2.0 though a socketlabs.com SMTP account. Add the following to your FWS go.pl FWS parameter:
36              
37            
38             my $fws = FWS::V2->new( %yourConfiguration,
39             sendMethod => 'socketlabs');
40              
41             Here is an example FWS independent process you can use as a starter to make your own customized FWS socketlabs process. This will be appropriate to be added to your CRONTAB to run 'socketLabs.pl send' every minute and run 'socketLabs.pl audit' every hour. This will work fine if your sending less than 500 email an hour. If you are sending more than that you should create a custom optimized script for your application based on what your trying to accomplish.
42              
43             Crontab entry:
44              
45             * * * * * /wherever/it/is/socketLabs.pl send >/dev/null 2>&1
46             0 * * * * /wherever/it/is/socketLabs.pl audit >/dev/null 2>&1
47              
48             socketLabs.pl:
49              
50             #!/usr/bin/perl
51             use strict;
52              
53             #
54             # setup your FWS
55             #
56             use FWS::V2;
57              
58             my $fws = FWS::V2->new(%yourConfiguration,
59            
60             SocketLabs=>{ mailingId => 'unique', # up to 8 characters of unique string
61             port => '2525',
62             host => 'smtp.socketlabs.com',
63             username => 'user name for SMTP auth',
64             password => 'password for SMTP auth',
65             queueFailLimit => 5,
66             apiURL => 'https://api.socketlabs.com/v1',
67             apiAccountId => 'from socket labs account',
68             apiPassword => 'from socket labs account',
69             apiUsername => 'from socket labs account'}
70             );
71              
72             #
73             # add SocketLabs
74             #
75             $fws->registerPlugin('FWS::V2::SocketLabs');
76              
77             #
78             # Add your site values
79             #
80             $fws->setSiteValues('site');
81              
82              
83             #
84             # Usage String
85             #
86             my $usageString = "\nUsage: socketlabs.pl [send|audit]\n\n\tsend: send the current queue\n\taudit: sync the socketlabs data with FWS\n\n";
87             if ($#ARGV != 0) { print $usageString }
88              
89             #
90             # we have an argument lets do it!
91             #
92             else {
93            
94             my $arg = $ARGV[0];
95             my $email = $ARGV[1];
96            
97              
98             #
99             # send anything in the queue
100             #
101             if ($arg eq 'send') {
102             print "Runnning Process: ".$arg."\n\n";
103             $fws->SocketLabs_processQueue();
104             }
105              
106             #
107             # audit anything that was sent and update FWS if there is something not synced
108             #
109             elsif ($arg eq 'audit') {
110             print "Runnning Process: ".$arg."\n\n";
111             my @historyArray = $fws->queueHistoryArray(synced=>'0');
112             if ($#historyArray > -1 ) { $fws->SocketLabs_processAudit() }
113             else { print "No sync required\n\n" }
114             }
115             }
116             1;
117              
118              
119             =head1 PLUGIN INITIALIZATION
120              
121             =head2 pluginInit
122              
123             Create a socketLabs object with the configuration parameters.
124              
125             =over 4
126              
127             =item * fws
128              
129             Pass what FWS object you want it to use for its lookups
130              
131             =item * mailingId
132              
133             Make sure this is Less than 8 characters. If you use your socketLabs account for more than one account make sure this is unique.
134              
135             =item * port
136              
137             Port 2525 should be good. If not 25 would be another appropriate port.
138              
139             =item * host
140              
141             Default is: smtp.socketlabs.com
142              
143             =item * username
144              
145             This is the username for the SMTP auth.
146              
147             =item * password
148              
149             This is the password for the SMTP auth.
150              
151             =item * queueFailLimit
152              
153             How many times it will try to audit before it gives up on the sync. Make sure this is at least 5 is you are syncing every minute.
154              
155             =item * apiURL
156              
157             Deault is: https://api.socketlabs.com/v1
158              
159             =item * apiAccountId
160              
161             Consult the socketlabs API documentation to know what this is.
162              
163             =item * apiUsername
164              
165             Consult the socketlabs API documentation to know what this is.
166              
167             =item * apiPassword
168              
169             Consult the socketlabs API documentation to know what this is.
170              
171             =back
172              
173             =cut
174              
175             sub pluginInit {
176 0     0 1   my ($self,$fws) = @_;
177              
178             #
179             # set defaults
180             #
181 0 0         if ($fws->{"SocketLabs"}->{"port"} eq '') { $fws->{"SocketLabs"}->{"port"} = 2525 }
  0            
182 0 0         if ($fws->{"SocketLabs"}->{"host"} eq '') { $fws->{"SocketLabs"}->{"host"} = 'smtp.socketlabs.com' }
  0            
183 0 0         if ($fws->{"SocketLabs"}->{"apiURL"} eq '') { $fws->{"SocketLabs"}->{"apiURL"} = 'https://api.socketlabs.com/v1' }
  0            
184 0 0         if ($fws->{"SocketLabs"}->{"queueFailLimit"} eq '') { $fws->{"SocketLabs"}->{"queueFailLimit"} = 5 }
  0            
185            
186             #
187             # update any $fws data
188             #
189            
190             #
191             # pass back our extended class
192             #
193 0           return $fws;
194             }
195              
196             =head1 EXTENDED METHODS
197              
198             =head2 SocketLabs_processQueue
199              
200             Move through the FWS queue and send all email in the queue with the socketlabs type.
201              
202             =cut
203              
204             sub SocketLabs_processQueue {
205 0     0 1   my ($self) = @_;
206              
207             #
208             # Get Items
209             #
210 0           my @queueArray = $self->queueArray();
211            
212             #
213             # send each one via SocketLabs_sendEmail
214             #
215 0           for my $i (0 .. $#queueArray) { $self->_SocketLabs_sendEmail(%{$queueArray[$i]}) }
  0            
  0            
216             }
217              
218             =head2 SocketLabs_processAudit
219              
220             Audit all the socket labs success and fail messages and update FWS with the response.
221              
222             =cut
223              
224             sub SocketLabs_processAudit {
225 0     0 1   my ($self) = @_;
226              
227             #
228             # Request Processed Messages from SocketLabs
229             #
230 0           my @SLArray = $self->_SocketLabs_post( url => $self->{'SocketLabs'}->{'apiURL'},
231             method => "messagesProcessed",
232             account_id => $self->{'SocketLabs'}->{'apiAccountId'},
233             mailingId => $self->{'SocketLabs'}->{'mailingId'},
234             user => $self->{'SocketLabs'}->{'apiUsername'},
235             password => $self->{'SocketLabs'}->{'apiPassword'});
236              
237 0           for my $i (0 .. $#SLArray) {
238 0           my %queueHash = $self->queueHistoryHash(queueGUID=>$SLArray[$i]{'MessageId'});
239              
240 0 0 0       if ($queueHash{'guid'} ne '' && $queueHash{'response'} eq '') {
241 0           $queueHash{'response'} = $SLArray[$i]{"Response"} . $SLArray[$i]{"Reason"};
242 0 0         if ($SLArray[$i]{"Reason"} eq '') { $queueHash{'success'} = 1 }
  0            
243 0           print $queueHash{'guid'}.": Synced!\n";
244 0           $queueHash{'synced'} = 1;
245 0           $queueHash{"response"} =~ s/\{CRLF\}/
/sg;
246 0           $self->saveQueueHistory(%queueHash);
247             }
248             }
249              
250 0           my @historyArray = $self->queueHistoryArray(synced=>'0');
251 0           for my $i (0 .. $#historyArray) {
252 0           $historyArray[$i]{'failureCode'}++;
253 0           print $historyArray[$i]{'guid'}.': Not Synced Try # '.$historyArray[$i]{'failureCode'}."\n";
254              
255             #
256             # if this is tried to many times, just mark it as synced
257             #
258 0 0         if ($historyArray[$i]{'failureCode'} gt $self->{'SocketLabs'}->{'queueFailLimit'}) {
259 0           print $historyArray[$i]{'guid'}.": Giving up, to many tries\n";
260 0           $historyArray[$i]{'synced'} = 1;
261 0           $historyArray[$i]{'response'} = 'Audit not available';
262             }
263 0           $self->saveQueueHistory(%{$historyArray[$i]});
  0            
264             }
265              
266             #
267             # Request Failed Messages from SocketLabs
268             #
269 0           @SLArray = $self->_SocketLabs_post( url => $self->{'SocketLabs'}->{'apiURL'},
270             method => "messagesFailed",
271             account_id => $self->{'SocketLabs'}->{'apiAccountId'},
272             mailingId => $self->{'SocketLabs'}->{'mailingId'},
273             user => $self->{'SocketLabs'}->{'apiUsername'},
274             password => $self->{'SocketLabs'}->{'apiPassword'});
275              
276 0           for my $i (0 .. $#SLArray) {
277 0           my %queueHash = $self->queueHistoryHash(queueGUID=>$SLArray[$i]{'MessageId'});
278 0 0 0       if ($queueHash{'guid'} ne '' && $queueHash{'response'} eq '') {
279 0           $queueHash{'response'} = $SLArray[$i]{"Response"} . $SLArray[$i]{"Reason"};
280 0 0         if ($SLArray[$i]{"Reason"} eq '') { $queueHash{'success'} = 1 }
  0            
281 0           print $queueHash{'guid'}.": Synced!\n";
282 0           $queueHash{'synced'} = 1;
283 0           $queueHash{"response"} =~ s/\{CRLF\}/
/sg;
284 0           $self->saveQueueHistory(%queueHash);
285             }
286             }
287             }
288              
289              
290              
291              
292              
293             ##########################################################
294             # Net: do the actual send via SocketLabs
295             ##########################################################
296             sub _SocketLabs_sendEmail {
297 0     0     my ($self,%paramHash) = @_;
298              
299             #
300             # create email sending params
301             #
302 0           my $msg = MIME::Lite->new(
303             From => $paramHash{'fromName'}." <".$paramHash{'from'}.">",
304             To => $paramHash{'to'},
305             Subject => $paramHash{'subject'},
306             Type => $paramHash{'mimeType'},
307             Data => $paramHash{'body'});
308              
309             #
310             # add guid references
311             # We loose some uniqueness - but we need to make these short so they will work with
312             # all email systems. The combined size of message and mailing id cannot be
313             # greater than 30 chars
314             #
315             # we will truncate the guids to 20 so they don't bust over. In the context of this
316             # limit the replication rate should never happen because we will only have a few in the
317             # queue at any given time. And the context of this id, will only last a couple minutes
318             #
319 0           my $messageId = substr($paramHash{'guid'},0,20);
320 0           $msg->add('X-xsMailingId' => $self->{'SocketLabs'}->{'mailingId'});
321 0           $msg->add('X-xsMessageId' => $messageId);
322              
323             #
324             # send email
325             #
326 0           eval { $msg->send('smtp', $self->{'SocketLabs'}->{'host'},
  0            
327             Port => $self->{'SocketLabs'}->{'port'},
328             AuthUser => $self->{'SocketLabs'}->{'username'},
329             AuthPass => $self->{'SocketLabs'}->{'password'});
330             };
331              
332 0           my $errorCode = $@;
333 0 0         if ($errorCode eq '') {
334 0           print "\nMESSAGE SENT TO: ".$paramHash{'to'} ."\n";
335 0           print "SUBJECT: ".$paramHash{'subject'} ."\n";
336 0           print "-----------------------------------------\n";
337             }
338             else {
339 0           print "ERROR: ". $errorCode."\n\n";
340 0           $paramHash{'response'} = $errorCode;
341             }
342              
343             #
344             # kill the guid so we make a new record and save it to the history
345             #
346 0           my %historyHash = %paramHash;
347 0           $historyHash{'queueGUID'} = $messageId;
348 0           $historyHash{'guid'} = '';
349 0           $self->saveQueueHistory(%historyHash);
350              
351             #
352             # Remove this item from the Queue
353             #
354 0           $self->deleteQueue(%paramHash);
355             }
356              
357             sub _SocketLabs_post {
358 0     0     my ($self,%paramHash) = @_;
359              
360             # Connection
361 0           my $URL = $paramHash{'url'};
362 0           my $method = $paramHash{'method'};
363              
364             # Authentication
365 0           my $account_id = $paramHash{'account_id'};
366 0           my $user = $paramHash{'user'};
367 0           my $password = $paramHash{'password'};
368              
369             # Query Params
370 0           my $serverId = $paramHash{'serverId'};
371 0           my $startDate = $paramHash{'startDate'};
372 0           my $endDate = $paramHash{'endDate'};
373 0           my $timeZone = $paramHash{'timeZone'};
374 0           my $mailingId = $paramHash{'mailingId'};
375 0           my $messageId = $paramHash{'messageId'};
376 0           my $index = $paramHash{'index'};
377 0           my $count = $paramHash{'count'};
378 0           my $type = $paramHash{'type'};
379              
380             #
381             # Failure codes
382             #
383 0           my %failCode = (
384             1001 => "Spam complaint",
385             1002 => "Blacklist",
386             1003 => "ISP block",
387             1004 => "Content block",
388             1005 => "URL block",
389             1006 => "Excess traffic",
390             1007 => "Security violation or virus",
391             1008 => "Open relay",
392             1009 => "Namespace mining detection",
393             1010 => "Authentication",
394             1999 => "Other",
395             2001 => "Unknown user",
396             2002 => "Bad domain",
397             2003 => "Address error",
398             2004 => "Closed account",
399             2999 => "Other",
400             3001 => "Recipient mailbox full",
401             3002 => "Recipient email account is inactive or disabled",
402             3003 => "Greylist",
403             3999 => "Other",
404             4001 => "Recipient server too busy",
405             4002 => "Recipient server returned a data format error",
406             4003 => "Network error",
407             4004 => "Recipient server rejected message as too old",
408             4006 => "Recipient network or configuration error normally a relay denied",
409             4999 => "Other",
410             5001 => "Auto Reply",
411             5999 => "Other",
412             9999 => "Unknown"
413             );
414              
415             #
416             # Check for Important Variables
417             #
418 0 0         if ($account_id eq '') { warn("Your account number has not been set"); }
  0            
419 0 0         if ($user eq '') { warn("Your authentication Username has not been set"); }
  0            
420 0 0         if ($password eq '') { warn("Your authentication Password has not been set"); }
  0            
421              
422              
423             # Check if URL and method are set
424 0 0         if ($URL eq '') { $URL = "https://api.socketlabs.com/v1"; }
  0            
425 0 0         if ($method eq '') { $method = "messagesQueued"; }
  0            
426              
427             #
428             # Trim Ending Backslash from URL and Method
429             # so we can handle it without worrying how
430             # it was passed to the sub routine
431             #
432 0           $URL =~ s/\/$//sg;
433 0           $method =~ s/\/$//sg;
434              
435             #
436             # BUILD URL
437             #
438 0           $URL .= "/" . $method . "/?accountId=" . $account_id;
439              
440             # Check if serverId is set
441 0 0         if ($serverId ne '') { $URL .= "&serverId=" . $serverId; }
  0            
442              
443             # Check if startDate is set
444 0 0         if ($startDate ne '') { $URL .= "&startDate=" . $startDate; }
  0            
445              
446             # Check if endDate is set
447 0 0         if ($endDate ne '') { $URL .= "&endDate=" . $endDate; }
  0            
448              
449             # Check if timeZone is set
450 0 0         if ($timeZone ne '') { $URL .= "&timeZone=" . $timeZone; }
  0            
451              
452             # Check if timeZone is set
453 0 0         if ($mailingId ne '') { $URL .= "&mailingId=" . $mailingId; }
  0            
454              
455             # Check if timeZone is set
456 0 0         if ($messageId ne '') { $URL .= "&messageId=" . $messageId; }
  0            
457              
458             # Check if timeZone is set
459 0 0         if ($index ne '') { $URL .= "&index=" . $index; }
  0            
460              
461             # Check if timeZone is set
462 0 0         if ($count ne '') { $URL .= "&count=" . $count; }
  0            
463              
464             # Check if type is set
465 0 0         if ($type ne '') { $URL .= "&type=" . $type; }
  0            
466 0           else { $URL .= "&type=xml"; }
467              
468             #
469             # Connect to SocketLabs
470             #
471 0           my $responseRef = $self->HTTPRequest(
472             url => $URL,
473             user => $user,
474             password => $password);
475 0           my $httpReturn = $responseRef->{'content'};
476              
477             #
478             # XML to Hash
479             #
480 0           my @itemArray;
481 0           while ($httpReturn =~ /(.*?)<\/item>/g) {
482 0           my %itemHash;
483              
484 0           my $itemNode = $1;
485              
486 0           while ($itemNode =~ /<(.*?)>(.*?)<\//g) {
487 0           my $key = $1;
488 0           my $value = $2;
489 0           $itemHash{$key} = $value;
490 0 0         if ($key eq 'FailureCode') { $itemHash{$key} = $failCode{$value} }
  0            
491             }
492 0           push (@itemArray,{%itemHash});
493             }
494              
495 0           return @itemArray;
496             }
497              
498             =head1 AUTHOR
499              
500             Nate Lewis, C<< >>
501              
502             =head1 BUGS
503              
504             Please report any bugs or feature requests to C, or through
505             the web interface at L. I will be notified, and then you'll
506             automatically be notified of progress on your bug as I make changes.
507              
508              
509              
510              
511             =head1 SUPPORT
512              
513             You can find documentation for this module with the perldoc command.
514              
515             perldoc FWS::V2::SocketLabs
516              
517              
518             You can also look for information at:
519              
520             =over 4
521              
522             =item * RT: CPAN's request tracker (report bugs here)
523              
524             L
525              
526             =item * AnnoCPAN: Annotated CPAN documentation
527              
528             L
529              
530             =item * CPAN Ratings
531              
532             L
533              
534             =item * Search CPAN
535              
536             L
537              
538             =back
539              
540              
541             =head1 ACKNOWLEDGEMENTS
542              
543              
544             =head1 LICENSE AND COPYRIGHT
545              
546             Copyright 2012 Nate Lewis.
547              
548             This program is free software; you can redistribute it and/or modify it
549             under the terms of either: the GNU General Public License as published
550             by the Free Software Foundation; or the Artistic License.
551              
552             See http://dev.perl.org/licenses/ for more information.
553              
554              
555             =cut
556              
557             1; # End of FWS::V2::SocketLabs