File Coverage

blib/lib/Net/SMS/O2_DE.pm
Criterion Covered Total %
statement 30 168 17.8
branch 0 66 0.0
condition 0 15 0.0
subroutine 10 20 50.0
pod 7 7 100.0
total 47 276 17.0


})
line stmt bran cond sub pod time code
1             package Net::SMS::O2_DE;
2              
3 1     1   25220 use 5.006;
  1         4  
  1         51  
4 1     1   7 use strict;
  1         2  
  1         49  
5 1     1   7 use warnings;
  1         7  
  1         50  
6              
7 1     1   7 use Carp;
  1         1  
  1         109  
8 1     1   770 use Net::SMS::Web;
  1         110065  
  1         44  
9 1     1   10 use Time::Local;
  1         2  
  1         86  
10 1     1   743 use Date::Format;
  1         6378  
  1         107  
11 1     1   831 use POSIX qw(ceil);
  1         8058  
  1         9  
12              
13             =head1 NAME
14              
15             Net::SMS::O2_DE - a module to send SMS messages using the O2 Germany web2sms!
16             It's working for Internet-Pack users. They have normally 50 free sms each month.
17             Maybe it is working also for other users. Please tell me if it's working with you
18             and you aren't a Internet-Pack user.
19              
20             =head1 VERSION
21              
22             Version: 0.07
23             Date: 24.07.2011
24              
25             =cut
26              
27             our $VERSION = '0.07';
28              
29              
30             =head1 SYNOPSIS
31              
32             use strict;
33             use Net::SMS::O2_DE;
34            
35             my $sms = Net::SMS::O2_DE->new(
36             autotruncate => 1,
37             username => '01701234567',
38             password => 'SECRET',
39             sender => 'YourMother',
40             recipient => '+4917676543210',
41             message => 'a test message',
42             );
43            
44             $sms->verbose( 1 );
45             $sms->login();
46             print "Quota: " , $sms->quota(). "\n";
47             $sms->message( 'a different message' );
48             print "sending message to mobile number ", $sms->recipient();
49             $sms->send_sms();
50             $sms->logout();
51              
52             =head1 WARNING
53              
54             If you don't have any free sms left, sending SMS may cost you money!!
55              
56             So check, if your quota is high enough to send the desired sms.
57             Use the function C to determine how much sms will be sent
58             with the current settings.
59              
60             Keep in mind that if you have sheduled sms, they arent yet included in the C.
61             Eg: If you send now, with quota=10 a scheduled sms which will be sent each hour for
62             the next 3 hours, quota will be decreased to 9 after the first sms is sent.
63             An hour later to 8, another hour later to 7. So the quota will not be immideately 7.
64            
65             If you have scheduled a sms with C and C but no end date is set
66             there will be send an infinite amount of sms. This this is in most cases undesired.
67             So remind to set C AND C
68              
69             Use C to check, if you send an infinite number of sms.
70              
71            
72             =head1 DESCRIPTION
73              
74              
75              
76             A perl module to send SMS messages, using the O2 web2sms gateway. This
77             module will only work with mobile phone numbers that have been registered with
78             O2 (L) and uses form submission to a URL that may be
79             subject to change. The O2 service is currently only available to german
80             phone users with internet pack.
81              
82             There is a maximum length for SMS message (1800 for O2). If the sum
83             of message length exceed this, the behaviour of the
84             Net::SMS::O2_DE objects depends on the value of the 'autotruncate' argument to
85             the constructor. If this is a true value, then the subject / message will be
86             truncated to 1800 characters. If false, the object will throw an exception
87             (die). If you set notruncate to 1, then the module won't check the message
88             length, and you are on your own!
89              
90             This implementation is based on the module L.
91              
92             The HTTP requests are sent using L which uses L module. If you are using a
93             proxy, you may need to set the HTTP_PROXY environment variable for this to
94             work (see L).
95              
96             =head1 TODO
97              
98             There is no check if you entered a valid tel number or frequency or other fields.
99              
100             =cut
101              
102             #------------------------------------------------------------------------------
103             #
104             # Package globals
105             #
106             #------------------------------------------------------------------------------
107              
108 1         402 use vars qw(
109             @ISA
110             $URL_PRELOGIN
111             $URL_LOGIN
112             $URL_SMSCENTER
113             $URL_PRESEND
114             $URL_SEND
115             $URL_SCHEDULE
116             $URL_LOGOUT
117             %REQUIRED_KEYS
118             %LEGAL_KEYS
119             $MAX_CHARS
120             $SINGLE_CHARS
121 1     1   1605 );
  1         3  
122              
123             @ISA = qw( Net::SMS::Web );
124              
125             $URL_PRELOGIN = 'https://login.o2online.de/loginRegistration/loginAction.do?_flowId=login&o2_type=asp&o2_label=login/comcenter-login&scheme=http&port=80&server=email.o2online.de&url=%2Fssomanager.osp%3FAPIID%3DAUTH-WEBSSO%26TargetApp%3D%2Fsmscenter_new.osp%253f%26o2_type%3Durl%26o2_label%3Dweb2sms-o2online';
126             $URL_LOGIN = 'https://login.o2online.de/loginRegistration/loginAction.do';
127             $URL_SMSCENTER = 'http://email.o2online.de:80/ssomanager.osp?APIID=AUTH-WEBSSO&TargetApp=/smscenter_new.osp?&o2_type=url&o2_label=web2sms-o2online';
128             $URL_PRESEND = 'https://email.o2online.de/smscenter_new.osp?Autocompletion=1&MsgContentID=-1';
129             $URL_SEND = 'https://email.o2online.de/smscenter_send.osp';
130             $URL_SCHEDULE = 'https://email.o2online.de/smscenter_schedule.osp';
131             $URL_LOGOUT = 'https://login.o2online.de/loginRegistration/loginAction.do?_flowId=logout';
132              
133              
134             %REQUIRED_KEYS = (
135             username => 1,
136             password => 1,
137             );
138              
139             %LEGAL_KEYS = (
140             username => 1,
141             password => 1,
142             sender => 1,
143             anonymous => 1,
144             recipient => 1,
145             message => 1,
146             verbose => 1,
147             audit_trail => 1,
148             flash_sms => 1,
149             frequency => 1,
150             schedule_start => 1,
151             schedule_end => 1,
152             );
153              
154             $MAX_CHARS = 1800;
155             $SINGLE_CHARS = 160;
156              
157              
158              
159              
160             =head1 SUBROUTINES/METHODS
161              
162             =cut
163              
164             =head2 CONSTRUCTOR Parameters
165              
166             The constructor for Net::SMS::O2_DE takes the following arguments as hash
167             values (see L):
168              
169             =head3 autotruncate (OPTIONAL)
170              
171             O2 has a upper limit on the length of the message (1800). If
172             autotruncate is true, message is truncated to 1800 if the sum of
173             the length exceeds 1800. The default for this is false.
174              
175             =head3 notruncate (OPTIONAL)
176              
177             Of course, if you don't believe the O2 web interface about maximum character
178             length, then you can set this option.
179              
180             =head3 username (REQUIRED)
181              
182             The O2 username for the user (assuming that the user is already registered
183             at L. Normally your phone number (eg. 017801234567)
184              
185             =head3 password (REQUIRED)
186              
187             The O2 password for the user (assuming that the user is already registered
188             at L.
189              
190             =head3 sender (OPTIONAL)
191              
192             The sender of the sms. You can set a string value which the recipient sees as sender.
193             Defaults to undefined which means your number is set as sender or if anonymous is set
194             the message is sent by anonymous.
195              
196             =head3 anonymous (OPTIONAL)
197              
198             If anonymous is set and sender is undef the sms will be sent as anonymous
199              
200             =head3 recipient (REQUIRED)
201              
202             Mobile number for the intended SMS recipient. Format must be international (eg. +4917801234567)
203              
204             =head3 message (REQUIRED)
205              
206             SMS message body.
207              
208             =head3 verbose (OPTIONAL)
209              
210             If true, various soothing messages are sent to STDERR. Defaults to false.
211              
212             =head3 flash_sms (OPTIONAL)
213              
214             If true uses FlashSMS. Defaults to undef which means FlashSMS is off.
215              
216             =head3 schedule_start (OPTIONAL)
217              
218             If you want to schedule the sms set the parameter C to desired value.
219             This is the start time using epoch time of the scheduling. The value is given
220             in seconds from epoch (eg. use time function).
221             ATTENTION: Must be multiple of 900 sekonds (=15 minutes). if not the value will
222             be round up internally to the next quarter of the hour.
223              
224             Example:
225             If C is set to the time value which represents in localtime:
226              
227             20.07.2011 20:05:12 it will be round up to 20.07.2011 20:15:00
228              
229             So the first sms is sent at 20:15:00
230              
231             =head3 schedule_end (OPTIONAL)
232              
233             If you want to schedule the sms set the parameter C to desired value.
234             This is the end time using epoch time of the scheduling. The value is given
235             in seconds from epoch (eg. use time function).
236             ATTENTION: Must be multiple of 900 sekonds (=15 minutes). if not the value will
237             be round up internally to the next quarter of the hour.
238              
239             Example:
240             If C is set to the time value which represents in localtime:
241              
242             20.07.2011 21:05:12 it will be round up to 20.07.2011 21:15:00
243              
244             So the last sms is sent at exactly 21:15:00
245              
246             =head3 frequency (OPTIONAL)
247              
248             Frequency for scheduled sms. Use one of the following values (default is 5):
249              
250             5 : only once
251             6 : hourly
252             1 : dayly
253             2 : weekly
254             3 : monthly
255             4 : each year
256              
257             Don't forget to set schedule_end otherwise there you may not be able to stop the
258             sms sending.
259              
260              
261             =cut
262              
263             =head2 new
264             The constructor. For parameter explanation see CONSTRUCTOR parameters
265             =cut
266              
267             sub new
268             {
269 0     0 1   my $class = shift;
270 0           my $self = $class->SUPER::new( @_ );
271 0           $self->_init( @_ );
272 0           return $self;
273             }
274              
275              
276             #------------------------------------------------------------------------------
277             #
278             # AUTOLOAD - to set / get object attributes
279             #
280             #------------------------------------------------------------------------------
281              
282             =head2 AUTOLOAD
283              
284             All of the constructor arguments can be got / set using accessor methods. E.g.:
285              
286             $old_message = $self->message;
287             $self->message( $new_message );
288              
289             =cut
290              
291             sub AUTOLOAD
292             {
293 0     0     my $self = shift;
294 0           my $value = shift;
295              
296 1     1   7 use vars qw( $AUTOLOAD );
  1         2  
  1         2982  
297 0           my $key = $AUTOLOAD;
298 0           $key =~ s/.*:://;
299 0 0         return if $key eq 'DESTROY';
300 0 0         croak ref($self), ": unknown method $AUTOLOAD\n"
301             unless $LEGAL_KEYS{ $key }
302             ;
303 0 0         if ( defined( $value ) )
304             {
305 0           $self->{$key} = $value;
306             }
307 0           return $self->{$key};
308             }
309              
310             =head2 get_flow_execution_key
311              
312             Calls the page to get the FlowExecutionKey for handling login and sending sms.
313             Called by login.
314              
315             =cut
316              
317             sub get_flow_execution_key
318             {
319 0     0 1   my($self) = @_;
320              
321 0           $self->action( Net::SMS::Web::Action->new(
322             url => $URL_PRELOGIN,
323             method => 'POST',
324             params => {
325             username => $self->{username},
326             password => $self->{password},
327             }
328             ) );
329            
330 0 0         if ( $self->response() =~ m{} )
331             {
332 0           return $1;
333             }
334 0           croak "Can't load FlowExecutionKey";
335             }
336              
337             =head2 login
338              
339             Logs in with specified username and password. Calls get_flow_execution_key
340             which is required for the whole communication.
341             After login changes from the login page to the SMS-Center page to set cookies properly.
342              
343             If already login was already called and not yet logout, this function returns
344             immediatly because assumes that you are already logged in.
345             If you want to force a new login, call logout first.
346              
347             =cut
348              
349             sub login
350             {
351 0     0 1   my($self) = @_;
352              
353 0 0         return if $self->{is_logged_in};
354            
355 0           $self->action( Net::SMS::Web::Action->new(
356             url => $URL_LOGIN,
357             method => 'POST',
358             params => {
359             '_flowExecutionKey' => $self->get_flow_execution_key(),
360             'loginName' => $self->{username},
361             'password' => $self->{password},
362             '_eventId' => 'login'
363             }
364             ) );
365              
366 0 0         if ($self->response() =~ m{
  • (.*)
367             {
368 0           croak "Login ERROR: " .$1;
369             }
370            
371             #Change to sms center to initialize server communication
372            
373 0           $self->action( Net::SMS::Web::Action->new(
374             url => $URL_SMSCENTER,
375             method => 'POST'
376             ) );
377              
378 0           $self->{is_logged_in} = 1;
379             }
380              
381              
382              
383             =head2 logout
384              
385             Logs you out from the current session. Use login to relogon.
386             There can be a parameter with '1' to force logging out
387              
388             =cut
389             sub logout
390             {
391 0     0 1   my($self) = shift;
392 0           my $force = shift;
393              
394 0 0         unless ($force)
395             {
396 0 0         return if (!$self->{is_logged_in});
397             }
398            
399 0           $self->action( Net::SMS::Web::Action->new(
400             url => $URL_LOGOUT,
401             method => 'POST'
402             ) );
403              
404 0           $self->{is_logged_in} = 0;
405            
406             #if ($self->response =~ m/Logout erfolgreich!/)
407             #{
408             # $self->{is_logged_in} = 0;
409             # return 1;
410             #}
411             #croak "Logout wasn't successful. Maybe the HTML-Code has changed. Please report a bug.";
412            
413             }
414              
415             =head2 sms_count
416              
417             Returns the number of needed SMS to send with current settings.
418             Eg: If your message contains 200 characters, it will you cost
419             2 sms because 1 sms can hold only 160 chars. So this function will return 2.
420              
421             If you scheduled the sms for eg. each hour in the next three hours,
422             this function will return 3.
423              
424             Combining of long messages and scheduling will also be calculated correctly.
425              
426             If you have scheduled a sms but no end date there will be send an infinite mount
427             of sms so this function returns -1 for infinite
428              
429             =cut
430              
431             sub sms_count
432             {
433 0     0 1   my($self) = @_;
434 0           my $count_by_text = ceil(length($self->{message})/$SINGLE_CHARS);
435 0 0         if ($count_by_text <=0)
436             {
437 0           $count_by_text = 1;
438             }
439            
440 0           my $count_by_schedule = 1;
441 0 0 0       if ($self->{schedule_start} || $self->{frequency} || $self->{schedule_end})
      0        
442             {
443             #if one of these is set, all must be set, otherwise ther is an infinite number of sms
444 0 0 0       unless ($self->{schedule_start} && $self->{frequency} && $self->{schedule_end})
      0        
445             {
446 0           return -1; #infinite
447             }
448 0 0         if ($self->{frequency} != 5) #more than once
449             {
450            
451 0           my $sched_start = $self->{schedule_start};
452 0           my $sched_end = $self->{schedule_end};
453             #round up to next full quarter hour
454 0           $sched_start += (15*60)-($sched_start%(15*60));
455 0           $sched_end += (15*60)-($sched_end%(15*60));
456 0           my $schedule_duration = $sched_end - $sched_start; #in seconds
457 0 0         if ($self->{frequency} == 6)
    0          
    0          
    0          
    0          
458             { #hourly
459 0           $count_by_schedule = int($schedule_duration / (60*60))+1;
460             }
461             elsif ($self->{frequency} == 1)
462             { #dayly
463 0           $count_by_schedule = int($schedule_duration / (24*60*60))+1;
464             }
465             elsif ($self->{frequency} == 2)
466             { #weekly
467 0           $count_by_schedule = int($schedule_duration / (7*24*60*60))+1;
468             }
469             elsif ($self->{frequency} == 3)
470             { #monthly
471 0           $count_by_schedule = int($schedule_duration / (31*24*60*60))+1;
472             }
473             elsif ($self->{frequency} == 4)
474             { #each year
475 0           $count_by_schedule = int($schedule_duration / (365*24*60*60))+1;
476             }
477 0 0         if ($count_by_schedule < 1) #if division floor to null
478             {
479 0           $count_by_schedule = 1;
480             }
481             }
482             }
483            
484 0           return $count_by_text*$count_by_schedule;
485             }
486              
487             =head2 quota
488              
489             Returns the current available free sms.
490              
491             =cut
492              
493             sub quota
494             {
495 0     0 1   my($self) = @_;
496 0           $self->login( );
497 0           $self->action( Net::SMS::Web::Action->new(
498             url => $URL_PRESEND,
499             method => 'POST',
500             ) );
501              
502             #Get all hidden form fields needed to send them back to the server
503 0 0         if ($self->response() =~ m{"frmSMS"(.*?)tr>}ms)
504             {
505 0           my @hiddenFieldsArea = $1;
506            
507 0           my %hash = ($hiddenFieldsArea[0] =~ m{}ig);
508 0 0         if (%hash)
509             {
510 0           $self->{hiddenFields} = \%hash;
511             }
512             else
513             {
514 0           croak "Can't parse hidden fields. Maybe the HTML-Code has changed. Please report a bug.";
515             }
516             } else {
517 0           croak "Can't parse hidden fields area. Maybe the HTML-Code has changed. Please report a bug.";
518             }
519            
520 0 0         if ( $self->response() =~ m{Frei-SMS: ([\d]+) Web2SMS noch in diesem Monat mit Ihrem Internet-Pack inklusive!} )
521             {
522 0           return $1;
523             }
524 0           croak "Can't determine quota. Maybe the HTML-Code has changed. Please report a bug.";
525             }
526              
527              
528              
529             =head2 send_sms
530              
531             This method is invoked to actually send the SMS message that corresponds to the
532             constructor arguments an set member variables.
533             Returns 1 on success. Otherwise croak will be called with the error message.
534             Login will be automatically performed if not yet called.
535             You have to call logout manually if you want to close the current session.
536              
537             =cut
538              
539             sub send_sms
540             {
541 0     0 1   my $self = shift;
542              
543 0           $self->login( );
544            
545             #Needed to load hidden form fields
546 0           $self->quota( );
547            
548            
549             #TODO: Add check for valid SMSto, smsfrom, frequency, end date if frequency set
550            
551 0           my $params = {
552             'SMSTo' => $self->{recipient},
553             'SMSText' => $self->{message},
554             };
555            
556             #Add hidden fields to params
557 0           my %hidden = %{$self->{hiddenFields}};
  0            
558 0           while ( my ($key, $value) = each(%hidden) ) {
559 0           $params->{$key} = $value;
560             }
561            
562 0 0         if ($self->{sender})
563             {
564 0           $params->{'SMSFrom'} = $self->{sender};
565 0           $params->{'FlagAnonymous'} = '0';
566 0           $params->{'FlagDefSender'} = '1';
567             } else {
568 0 0         if ($self->{anonymous})
569             {
570 0           print "\n --anon-- \n";
571             #Anonymous sender
572 0           $params->{'FlagAnonymous'} = '1';
573             } else {
574 0           print "\n --NUMBER-- \n";
575             #Use login number as sender (default)
576 0           $params->{'SMSFrom'} = '';
577 0           $params->{'FlagAnonymous'} = '0';
578 0           $params->{'FlagDefSender'} = '1';
579             }
580             }
581            
582 0 0         if ($self->{flash_sms})
583             {
584 0           $params->{'FlagFlash'} = '1';
585             } else {
586 0           $params->{'FlagFlash'} = '0';
587             }
588            
589 0 0         if ($self->{frequency})
590             {
591 0           $params->{'Frequency'} = $self->{frequency};
592             } else {
593 0           $params->{'Frequency'} = '5';
594             }
595            
596 0           my $url = $URL_SEND;
597            
598             #If no schedule time set, set it to now because it is needed
599 0           my $sched_start = $self->{schedule_start};
600 0           my $sched_end = $self->{schedule_end};
601            
602 0 0         if ($sched_start)
603             {
604 0           $sched_start += (15*60)-($sched_start%(15*60));
605             #sched end should be set
606 0 0         unless ($sched_end)
607             {
608 0           $sched_end = $sched_start;
609             }
610 0           $url = $URL_SCHEDULE;
611 0           my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime($sched_start);
612 0           $params->{'StartDateDay'} = $mday;
613 0           $params->{'StartDateMonth'} = $mon+1;
614 0           $params->{'StartDateYear'} = $year+1900;
615 0           $params->{'StartDateHour'} = $hour;
616 0           $params->{'StartDateMin'} = $min;
617 0           $params->{'RepeatStartDate'} = time2str("%Y,%m,%d,%H,%M,00",$sched_start);
618 0 0         if ($sched_end)
619             {
620 0           $sched_end += (15*60)-($sched_end%(15*60));
621 0           my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime($sched_end);
622 0           $params->{'EndDateDay'} = $mday;
623 0           $params->{'EndDateMonth'} = $mon+1;
624 0           $params->{'EndDateYear'} = $year+1900;
625 0           $params->{'EndDateHour'} = $hour;
626 0           $params->{'EndDateMin'} = $min;
627 0           $params->{'RepeatEndDate'} = time2str("%Y,%m,%d,%H,%M,00",$sched_end);
628 0           $params->{'RepeatEndType'} = '1';
629             } else {
630 0           $params->{'RepeatEndType'} = '0';
631             }
632 0           $params->{'RepeatType'} = $params->{'Frequency'};
633             }
634            
635             $self->action(
636 0           Net::SMS::Web::Action->new(
637             url => $url,
638             method => 'POST',
639             params => $params
640             )
641             );
642            
643 0 0 0       if ( $self->response =~ m/Ihre SMS wurde erfolgreich versendet./ or $self->response =~ m/Ihre Web2SMS ist geplant/)
644             {
645 0           return 1;
646             }
647            
648 0           croak "Coudln't send sms.";
649             }
650              
651             sub _check_length
652             {
653 0     0     my $self = shift;
654 0           $self->{message_length} = 0;
655 0 0         if ( $self->{autotruncate} )
    0          
656             {
657             # Chop the message down the the correct length.
658 0           $self->{message} = substr $self->{message}, 0, $MAX_CHARS;
659 0           $self->{message_length} += length $self->{$_} for qw/message/;
660             }
661             elsif ( ! $self->{notruncate} )
662             {
663 0           $self->{message_length} = length( $self->{message} );
664 0 0         if ( $self->{message_length} > $MAX_CHARS )
665             {
666 0           croak ref($self),
667             ": total message length is too long ",
668             "(> $MAX_CHARS)\n"
669             ;
670             }
671             }
672             }
673              
674             sub _init
675             {
676 0     0     my $self = shift;
677 0           my %keys = @_;
678              
679 0           for ( keys %REQUIRED_KEYS )
680             {
681 0 0         croak ref($self), ": $_ field is required\n" unless $keys{$_};
682             }
683 0           for ( keys %keys )
684             {
685 0           $self->{$_} = $keys{$_};
686             }
687 0           $self->_check_length();
688             }
689              
690             =head1 SEE ALSO
691              
692             L.
693             L.
694              
695             =head1 AUTHOR
696              
697             Stefan Profanter, C<< >>
698              
699             =head1 BUGS
700              
701             Please report any bugs or feature requests to C, or through
702             the web interface at L. I will be notified, and then you'll
703             automatically be notified of progress on your bug as I make changes.
704              
705              
706              
707              
708             =head1 SUPPORT
709              
710             You can find documentation for this module with the perldoc command.
711              
712             perldoc Net::SMS::O2_DE
713              
714              
715             You can also look for information at:
716              
717             =over 4
718              
719             =item * RT: CPAN's request tracker (report bugs here)
720              
721             L
722              
723             =item * AnnoCPAN: Annotated CPAN documentation
724              
725             L
726              
727             =item * CPAN Ratings
728              
729             L
730              
731             =item * Search CPAN
732              
733             L
734              
735             =back
736              
737              
738             =head1 ACKNOWLEDGEMENTS
739              
740              
741             =head1 LICENSE AND COPYRIGHT
742              
743             Copyright 2011 Stefan Profanter.
744              
745             This program is free software; you can redistribute it and/or modify it
746             under the terms of either: the GNU General Public License as published
747             by the Free Software Foundation; or the Artistic License.
748              
749             See http://dev.perl.org/licenses/ for more information.
750              
751              
752             =cut
753              
754             1; # End of Net::SMS::O2_DE