File Coverage

blib/lib/WebService/FindMyiPhone.pm
Criterion Covered Total %
statement 21 68 30.8
branch 0 18 0.0
condition n/a
subroutine 7 14 50.0
pod 4 4 100.0
total 32 104 30.7


line stmt bran cond sub pod time code
1             package WebService::FindMyiPhone;
2              
3 1     1   28308 use strict;
  1         2  
  1         37  
4 1     1   5 use warnings;
  1         2  
  1         28  
5              
6 1     1   76 use 5.010_001;
  1         8  
  1         53  
7             our $VERSION = '0.02';
8              
9 1     1   6 use Carp;
  1         1  
  1         99  
10 1     1   12 use List::Util 'first';
  1         2  
  1         128  
11 1     1   1071 use Mojolicious;
  1         622965  
  1         10  
12 1     1   850 use WebService::FindMyiPhone::Device;
  1         5  
  1         1015  
13              
14             my $post_data
15             = '{"clientContext":{"appName":"FindMyiPhone","appVersion":"1.4","buildVersion":"145","deviceUDID":"0000000000000000000000000000000000000000","inactiveTime":2147483647,"osVersion":"4.2.1","personID":0,"productType":"iPad1,1"}}';
16              
17             sub new {
18 0     0 1   my ( $class, %args ) = @_;
19              
20 0           for my $required_arg (qw(username password)) {
21 0 0         croak "Required argument $required_arg not specifided."
22             unless $args{$required_arg};
23             }
24              
25 0 0         $ENV{MOJO_USERAGENT_DEBUG} = 1 if $args{debug};
26              
27 0           my $self = {
28             username => $args{username},
29             password => $args{password},
30             debug => $args{debug},
31             devices => [],
32             hostname => 'fmipmobile.icloud.com',
33             ua => Mojo::UserAgent->new(),
34             };
35 0           bless $self, $class;
36              
37 0           $self->{ua}->transactor->name('Find iPhone/1.4 MeKit (iPad: iPhone OS/4.2.1)');
38 0           $self->_get_shard();
39 0           $self->update_devices();
40              
41 0           return $self;
42             }
43              
44             sub _get_shard {
45 0     0     my ($self) = @_;
46 0           my $response = $self->_post( '/initClient', $post_data );
47 0           $self->{hostname} = $response->headers->header('X-Apple-MMe-Host');
48 0 0         warn "User is on shard $self->{hostname}" if $self->{debug};
49             }
50              
51             sub update_devices {
52 0     0 1   my ($self) = @_;
53 0 0         if ( @{ $self->{devices} } ) {
  0            
54 0           my $new_device_data
55             = $self->_post( '/initClient', $post_data )->json->{content};
56 0           for my $device ( @{$new_device_data} ) {
  0            
57 0           my $device_object = $self->get_device_by( id => $device->{id} );
58 0           $device_object->_update_self($device);
59             }
60             }
61             else {
62 0           $self->{devices}
63             = $self->_post( '/initClient', $post_data )->json->{content};
64 0           $_ = WebService::FindMyiPhone::Device->new( $self, $_ )
65 0           for @{ $self->{devices} };
66             }
67 0           return $self->{devices};
68             }
69              
70             sub get_devices_field {
71 0     0 1   my ( $self, $field ) = @_;
72 0 0         return [ map { $_->{$field} } @{ $self->{devices} } ] unless ref $field;
  0            
  0            
73 0           return [ map { [ @$_{@$field} ] } @{ $self->{devices} } ];
  0            
  0            
74             }
75              
76             sub get_device_by {
77 0     0 1   my ( $self, $field, $query ) = @_;
78 0     0     return first { $_->{$field} eq $query } @{ $self->{devices} };
  0            
  0            
79             }
80              
81             sub _post {
82 0     0     my ( $self, $path, $data ) = @_;
83              
84 0           state $headers = {
85             'Content-Type' => ' application/json; charset=utf-8',
86             'X-Apple-Find-Api-Ver' => ' 2.0',
87             'X-Apple-Authscheme' => ' UserIdGuest',
88             'X-Apple-Realm-Support' => ' 1.0',
89             'X-Client-Name' => ' iPad',
90             'X-Client-UUID' => ' 0000000000000000000000000000000000000000',
91             'Accept-Language' => ' en-us',
92             };
93              
94 0           my $url = Mojo::URL->new( join '', 'https://', $self->{hostname},
95             '/fmipservice/device/', $self->{username}, $path );
96 0 0         warn "Posting to $url\n" if $self->{debug};
97 0           $url->userinfo( $self->{username} . ':' . $self->{password} );
98              
99 0 0         my $transaction = $self->{ua}
100             ->post( $url, $headers, ref $data ? ( json => $data ) : $data );
101              
102 0 0         if ( my $response = $transaction->success ) {
103 0           return $response;
104             }
105             else {
106 0           my ( $err, $code ) = $transaction->error;
107 0 0         confess $code ? "$code response: $err" : "Connection error: $err";
108             }
109             }
110              
111             1;
112             __END__