File Coverage

blib/lib/WebService/MobileMe.pm
Criterion Covered Total %
statement 18 79 22.7
branch 0 18 0.0
condition 0 9 0.0
subroutine 6 14 42.8
pod 0 6 0.0
total 24 126 19.0


line stmt bran cond sub pod time code
1             package WebService::MobileMe;
2              
3             # ABSTRACT: access MobileMe iPhone stuffs from Perl
4              
5 1     1   21949 use strict;
  1         2  
  1         40  
6 1     1   6 use warnings;
  1         1  
  1         31  
7              
8 1     1   1158 use JSON 2.00;
  1         18579  
  1         8  
9 1     1   1481 use LWP::UserAgent;
  1         53402  
  1         33  
10 1     1   934 use MIME::Base64;
  1         757  
  1         108  
11 1     1   1030 use Data::Dumper;
  1         8216  
  1         1117  
12              
13             our $VERSION = '0.007';
14              
15             my %headers = (
16             'X-Apple-Find-Api-Ver' => '2.0',
17             'X-Apple-Authscheme' => 'UserIdGuest',
18             'X-Apple-Realm-Support' => '1.0',
19             'Content-Type' => 'application/json; charset=utf-8',
20             'Accept-Language' => 'en-us',
21             'Pragma' => 'no-cache',
22             'Connection' => 'keep-alive',
23             );
24              
25             my $default_uuid = '0000000000000000000000000000000000000000';
26             my $default_name = 'My iPhone';
27             my $base_url = 'https://fmipmobile.icloud.com/fmipservice/device/';
28             my $fmi_app_version = '1.2.1';
29             my $fmi_build_version = '145';
30             my $fmi_os_version = '4.2.1';
31              
32             sub new {
33 0     0 0   my ( $class, %args ) = @_;
34 0           my $self = {};
35 0           bless $self, $class;
36              
37 0   0       $self->{debug} = $args{debug} || 0;
38              
39 0           $self->{ua} = LWP::UserAgent->new(
40             agent => 'Find iPhone/1.1 MeKit (iPhone: iPhone OS/4.2.1)',
41             autocheck => 0,
42             );
43              
44 0           $self->{ua}->default_header( 'Authorization' => 'Basic '
45             . encode_base64( $args{username} . ':' . $args{password} ) );
46              
47 0           while (my ($header, $value) = each %headers) {
48 0           $self->{ua}->default_header( $header => $value);
49             }
50              
51 0 0 0       if ( defined( $args{uuid} && $args{device_name} ) ) {
52 0           $self->{uuid} = $args{uuid};
53 0           $self->{device_name} = $args{device_name};
54             }
55             else {
56 0           $self->{uuid} = $default_uuid;
57 0           $self->{device_name} = $default_name;
58             }
59              
60 0           $self->{ua}->default_header( 'X-Client-Uuid' => $self->{uuid} );
61 0           $self->{ua}->default_header( 'X-Client-Name' => $self->{device_name} );
62              
63 0           $self->{base_url} = $base_url . $args{username};
64              
65 0           $self->update();
66              
67 0           return $self;
68              
69             }
70              
71             sub locate {
72 0     0 0   my $self = shift;
73 0           $self->update();
74 0           my $device = $self->device(shift);
75 0 0         die "Don't have location for device" unless exists $device->{location};
76 0           return $device->{location}
77             }
78              
79             sub device {
80 0     0 0   my $self = shift;
81 0   0       my $device_number = shift || 0;
82 0           my $device = $self->{devices}[$device_number];
83 0 0         die "Didn't find specified device number ( $device_number )" unless $device;
84 0           return $device
85              
86             }
87              
88             sub sendMessage {
89 0     0 0   my ($self, %args) = @_;
90 0   0       $args{subject} ||= 'Important Message';
91 0 0         $args{alarm} = $args{alarm} ? 'true' : 'false';
92 0 0         die "Must specify message." unless $args{message};
93 0           my $device = $self->device( $args{device} );
94 0           my $post_content = sprintf(qq|{"clientContext":{"appName":"FindMyiPhone","appVersion":"$fmi_app_version","buildVersion":"$fmi_build_version","deviceUDID":"0000000000000000000000000000000000000000","inactiveTime":5911,"osVersion":"$fmi_os_version","productType":"iPad1,1","selectedDevice":"%s","shouldLocate":false},"device":"%s","serverContext":{"callbackIntervalInMS":3000,"clientId":"0000000000000000000000000000000000000000","deviceLoadStatus":"203","hasDevices":true,"lastSessionExtensionTime":null,"maxDeviceLoadTime":60000,"maxLocatingTime":90000,"preferredLanguage":"en","prefsUpdateTime":1276872996660,"sessionLifespan":900000,"timezone":{"currentOffset":-25200000,"previousOffset":-28800000,"previousTransition":1268560799999,"tzCurrentName":"Pacific Daylight Time","tzName":"America/Los_Angeles"},"validRegion":true},"sound":%s,"subject":"%s","text":"%s"}|,
95             $device->{id}, $device->{id},
96             $args{alarm}, $args{subject}, $args{message}
97             );
98 0           return from_json( $self->_post( '/sendMessage', $post_content )->content )->{msg};
99             }
100              
101             sub remoteLock {
102 0     0 0   my ($self, $passcode, $devicenum) = @_;
103 0 0         die "Must specify passcode." unless $passcode;
104 0           my $device = $self->device( $devicenum );
105 0           my $post_content = sprintf(qq|{"clientContext":{"appName":"FindMyiPhone","appVersion":"$fmi_app_version","buildVersion":$fmi_build_version","deviceUDID":"0000000000000000000000000000000000000000","inactiveTime":5911,"osVersion":"$fmi_os_version","productType":"iPad1,1","selectedDevice":"%s","shouldLocate":false},"device":"%s","oldPasscode":"","passcode":"%s","serverContext":{"callbackIntervalInMS":3000,"clientId":"0000000000000000000000000000000000000000","deviceLoadStatus":"203","hasDevices":true,"lastSessionExtensionTime":null,"maxDeviceLoadTime":60000,"maxLocatingTime":90000,"preferredLanguage":"en","prefsUpdateTime":1276872996660,"sessionLifespan":900000,"timezone":{"currentOffset":-25200000,"previousOffset":-28800000,"previousTransition":1268560799999,"tzCurrentName":"Pacific Daylight Time","tzName":"America/Los_Angeles"},"validRegion":true}}|,
106             $device->{id}, $device->{id}, $passcode
107             );
108 0           return from_json( $self->_post( '/remoteLock', $post_content )->content )->{remoteLock};
109             }
110              
111             sub update {
112 0     0 0   my $self = shift;
113 0           my $response;
114              
115 0           my $post_content =
116             qq|{"clientContext":{"appName":"FindMyiPhone","appVersion":"$fmi_app_version","buildVersion":"$fmi_build_version","deviceUDID":"|
117             . $self->{uuid}
118             . qq|","inactiveTime":2147483647,"osVersion":"$fmi_os_version","personID":0,"productType":"iPhone3,1"}}|;
119 0           my $retry = 1;
120 0           while ($retry) {
121 0           $response = $self->_post( '/initClient', $post_content );
122 0 0         if ($response->code == 330) {
123 0           my $host = $response->headers->header('X-Apple-MME-Host');
124 0           $self->_debug("Updating url to point to $host");
125 0           $self->{base_url} =~ s|https://fmipmobile.icloud.com|https://$host|;
126             }
127             else {
128 0           $retry = 0;
129             }
130             }
131 0 0         if ($response->code != 200) {
132 0           die "Failed to init, got " . $response->status_line;
133             }
134              
135 0           my $data = from_json( $response->content );
136              
137 0           $self->{devices} = $data->{content};
138 0           $self->_debug("In update, found " . scalar (@{$self->{devices}}) . " device(s)");
  0            
139              
140 0           return 1;
141             }
142              
143             sub _debug {
144 0 0   0     print STDERR $_[1] . "\n" if $_[0]->{debug};
145             }
146              
147             sub _post {
148 0     0     my $self = shift;
149 0           return $self->{ua}->post( $self->{base_url} . $_[0], Content => $_[1] );
150             }
151              
152             1;
153              
154             __END__