File Coverage

blib/lib/SMS/Send/DE/MeinBMW.pm
Criterion Covered Total %
statement 34 108 31.4
branch 0 40 0.0
condition 0 9 0.0
subroutine 12 20 60.0
pod 2 2 100.0
total 48 179 26.8


line stmt bran cond sub pod time code
1             package SMS::Send::DE::MeinBMW;
2              
3             BEGIN {
4 1     1   24649 $VERSION = '0.06';
5             }
6              
7 1     1   8 use base 'SMS::Send::Driver';
  1         2  
  1         947  
8 1     1   446 use warnings;
  1         2  
  1         28  
9 1     1   10 use strict;
  1         1  
  1         49  
10 1     1   1045 use LWP::UserAgent;
  1         69126  
  1         36  
11 1     1   14 use HTTP::Response;
  1         2  
  1         18  
12 1     1   4937 use HTTP::Request::Common;
  1         5931  
  1         192  
13 1     1   1193 use HTTP::Cookies;
  1         11177  
  1         111  
14 1     1   1063 use HTML::Form;
  1         26033  
  1         114  
15 1     1   13 use Carp;
  1         3  
  1         673  
16              
17             my $RE_BADLOGIN = qr/Sie konnten nicht authentifiziert werden/;
18             my $root_page = 'https://www.meinbmw.de';
19             my $login_page = 'https://www.meinbmw.de/Home/tabid/36/ctl/Login/Default.aspx';
20             my $sms_page =
21             'https://www.meinbmw.de/DownloadsServices/Services/SMSService/tabid/80/Default.aspx';
22              
23             sub new {
24 0     0 1   my $class = shift;
25 0           my %params = @_;
26              
27             # Get the login
28 0           my $login = $class->_LOGIN( delete $params{_login} );
29 0           my $password = $class->_PASSWORD( delete $params{_password} );
30              
31 0           my $ua = LWP::UserAgent->new;
32              
33             # follow posts
34 0           push @{ $ua->requests_redirectable }, 'POST';
  0            
35              
36             # lie about the agent
37 0           $ua->agent('Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1)');
38 0           $ua->cookie_jar( HTTP::Cookies->new );
39              
40             # Create the object, saving any private params for later
41 0           my $self = bless {
42             ua => $ua,
43             login => $login,
44             password => $password,
45             private => \%params,
46              
47             # State variables
48             logged_in => '',
49             }, $class;
50              
51 0           $self;
52             }
53              
54             sub _get_login {
55 0     0     my $self = shift;
56              
57 0           my $ua = $self->{ua};
58              
59             #get session code and cookies!
60 0           my $res = $ua->request( POST $login_page);
61              
62 0 0         $res->is_success || Carp::croak("HTTP Error: $login_page\n$res->status_line");
63              
64 0           my $html = $res->content;
65 0           my $f = HTML::Form->parse( $html, $login_page );
66              
67 0           for ( $f->param ) {
68 0 0         $f->find_input($_)->value( $self->{login} ) if /username$/i;
69 0 0         $f->find_input($_)->value( $self->{password} ) if /password$/i;
70             }
71              
72 0           $res = $ua->request( $f->click );
73              
74 0 0 0       if ( $res->is_success && $res->content =~ /logout/i ) {
75 0           return 1;
76             }
77 0           else { Carp::croak( "Couldn't log in: ", $res->status_line ); }
78              
79 0           return 0;
80             }
81              
82             sub _send_login {
83 0     0     my $self = shift;
84              
85             # Shortcut if logged in
86 0 0         return 1 if $self->{logged_in};
87              
88             # Get to the login page
89 0           $self->_get_login;
90              
91             # Submit the login form
92 0           my $res = $self->{ua}->request( GET $login_page );
93              
94 0 0         $res->is_success || Carp::croak("HTTP Error: $login_page");
95              
96 0 0         if ( $res->content =~ $RE_BADLOGIN ) {
97 0           Carp::croak('Invalid login and/or password');
98             }
99              
100 0           $self->{logged_in} = 1;
101 0           return 1;
102             }
103              
104             ##
105             # send_sms
106              
107             sub send_sms {
108 0     0 1   my $self = shift;
109 0           my %params = @_;
110              
111             # Get the message and destination
112 0           my $message = $self->_MESSAGE( delete $params{text} );
113 0           my $recipient = $self->_TO( delete $params{to} );
114              
115             # Make sure we are logged in
116 0           $self->_send_login;
117              
118 1     1   7 my $free_chars = do { use bytes; 160 - length($message) };
  1         2  
  1         16  
  0            
  0            
119              
120 0           my $res = $self->{ua}->request( GET $sms_page);
121              
122 0 0         $res->is_success || Carp::croak("HTTP Error: $sms_page\n$res->status_line");
123              
124 0           my $html = $res->content;
125              
126 0           my $f = HTML::Form->parse( $html, $sms_page );
127              
128 0           for ( $f->param ) {
129 0 0         $f->find_input($_)->value($recipient) if /phone$/i;
130 0 0         $f->find_input($_)->value($message) if /subject$/i;
131             }
132              
133 0           $res = $self->{ua}->request( $f->click );
134            
135 0 0         unless ( $res->is_success ) {
136 0           Carp::croak("HTTP request returned failure when sending SMS request");
137             }
138              
139             # Check if the SMS limit isn't reached
140 0 0         return 0 unless $res->{_content} =~ /Ihre SMS wurde an .+ versendet/i;
141              
142             # Fire-and-forget, we don't know for sure.
143 0           return 1;
144             }
145              
146             ###############################################
147             # Internal
148              
149             sub _LOGIN {
150 0 0   0     my $class = ref $_[0] ? ref shift : shift;
151 0           my $email = shift;
152 0 0         unless ( defined $email ) {
153 0           Carp::croak("Did not provide a login emailaddress");
154             }
155 0 0         unless ( $email =~ /^.+\@\w+\.\w+$/ ) {
156 0           Carp::croak("Login does nnot look like a emailaddress");
157             }
158 0           return $email;
159             }
160              
161             sub _PASSWORD {
162 0 0   0     my $class = ref $_[0] ? ref shift : shift;
163 0           my $password = shift;
164 0 0 0       unless ( defined $password and !ref $password and length $password ) {
      0        
165 0           Carp::croak("Did not provide a password");
166             }
167 0           return $password;
168             }
169              
170             sub _MESSAGE {
171 1     1   449 use bytes;
  1         2  
  1         3  
172 0 0   0     my $class = ref $_[0] ? ref shift : shift;
173 0           my $message = shift;
174 0 0         unless ( length($message) <= 160 ) {
175 0           Carp::croak("Message length limit is 160 characters");
176             }
177 0           return $message;
178             }
179              
180             sub _TO {
181 0 0   0     my $class = ref $_[0] ? ref shift : shift;
182 0           my $to = shift;
183              
184             # International numbers need their + removed
185 0           $to =~ y/0123456789//cd;
186              
187 0           return $to;
188             }
189              
190             1;
191             __END__