File Coverage

blib/lib/WWW/GoogleAnalytics/Mobile/PSGI.pm
Criterion Covered Total %
statement 22 24 91.6
branch n/a
condition n/a
subroutine 8 8 100.0
pod n/a
total 30 32 93.7


line stmt bran cond sub pod time code
1             package WWW::GoogleAnalytics::Mobile::PSGI;
2              
3 2     2   157243 use strict;
  2         6  
  2         66  
4 2     2   9 use warnings;
  2         3  
  2         53  
5 2     2   699 use parent qw/Plack::Component/;
  2         268  
  2         14  
6 2     2   13469 use Carp;
  2         4  
  2         125  
7 2     2   1090 use URI;
  2         5365  
  2         56  
8 2     2   1045 use URI::QueryParam;
  2         845  
  2         53  
9 2     2   12 use List::Util qw/first/;
  2         4  
  2         235  
10 2     2   2280 use Furl;
  0            
  0            
11             use Net::DNS::Lite;
12             use Digest::SHA qw/hmac_sha1_hex sha1_hex/;
13             use Plack::Request;
14             use Plack::Response;
15              
16             use Plack::Util::Accessor qw/secret timeout/;
17              
18             our $GAM_VERSION = '4.4sp';
19             our $GAM_COOKIE_NAME = '__utmmobile';
20             our $GAM_COOKIE_PATH = '/';
21             our $GAM_COOKIE_USER_PERSISTENCE = '+2y';
22             our $GAM_UTM_GIF_LOCATION = "http://www.google-analytics.com/__utm.gif";
23              
24             my $GIF_DATA = pack "C43", (
25             0x47, 0x49, 0x46, 0x38, 0x39, 0x61,
26             0x01, 0x00, 0x01, 0x00, 0x80, 0xff,
27             0x00, 0xc0, 0xc0, 0xc0, 0x00, 0x00,
28             0x00, 0x21, 0xf9, 0x04, 0x01, 0x00,
29             0x00, 0x00, 0x00, 0x2c, 0x00, 0x00,
30             0x00, 0x00, 0x01, 0x00, 0x01, 0x00,
31             0x00, 0x02, 0x02, 0x44, 0x01, 0x00,
32             0x3b );
33              
34             our $DEBUG = 0;
35              
36             sub prepare_app {
37             my $self = shift;
38             Carp::croak "secret key must be defined" if ! $self->secret;
39             $self->timeout(5) unless defined $self->timeout;
40             }
41              
42             sub param_or {
43             my ($req, $key, $val) = @_;
44             return $req->param($key) if defined $req->param($key);
45             return $val;
46             }
47              
48             sub call {
49             my $self = shift;
50             my $env = shift;
51             my $req = Plack::Request->new($env);
52              
53             my $domain_name = param_or($req, 'utmhn', "");
54             my $document_referer = param_or($req, 'utmr', "-");
55             my $document_path = param_or($req, 'utmp', "");
56             my $account = param_or($req, 'utmac', "");
57             my $utmn = param_or($req, 'utmn', "");
58              
59             if ( ! $req->param('cs') ) {
60             return ['403', ['Content-Type' => 'text/plain'], ['no checksum'] ];
61             }
62             if ( $req->param('cs') ne
63             substr( hmac_sha1_hex($utmn . $domain_name . $document_path, $self->secret), 16, 6 ) ) {
64             return ['403', ['Content-Type' => 'text/plain'], ['checksum not match'] ];
65             }
66              
67             my $user_agent = "";
68             if ( defined $req->user_agent ) {
69             $user_agent = $req->user_agent;
70             }
71              
72             my $remote_address = "";
73             if ( defined $req->address ) {
74             $remote_address = $req->address;
75             if ($remote_address =~ /^((\d{1,3}\.){3})\d{1,3}$/) {
76             $remote_address = $1 . "0";
77             } else {
78             $remote_address = "";
79             }
80             }
81              
82             srand(); #for preforking
83             my $visitor_id = $self->get_visitor_id($req, $user_agent, $account);
84            
85             my $utm_url = URI->new($GAM_UTM_GIF_LOCATION);
86             $utm_url->query_form_hash({
87             utmwv => $GAM_VERSION,
88             utmn => int(rand 0x7fffffff),
89             utmhn => $domain_name,
90             utmr => $document_referer,
91             utmp => $document_path,
92             utmac => $account,
93             utmcc => '__utma=999.999.999.999.999.1;',
94             utmvid => $visitor_id,
95             utmip => $remote_address
96             });
97              
98             my @headers;
99             if (defined $req->header("Accept-Language") ) {
100             push @headers, "Accept-Language", $req->header("Accept-Language");
101             }
102            
103             my $furl = Furl::HTTP->new(
104             inet_aton => \&Net::DNS::Lite::inet_aton,
105             timeout => $self->timeout,
106             agent => $user_agent,
107             headers => \@headers,
108             );
109             $furl->env_proxy;
110             my ($minor_version, $status, $message, $headers, $content) = $furl->get("$utm_url");
111              
112             if ( substr( $status, 0, 1 ) ne '2' ) {
113             Carp::carp "Failed request to '$GAM_UTM_GIF_LOCATION': $message";
114             }
115              
116             my $res = Plack::Response->new(200);
117             $res->content_type('image/gif');
118             $res->header('Cache-Control', 'private, no-cache, no-cache=Set-Cookie, proxy-revalidate');
119             $res->header('Pragma', 'no-cahce');
120             $res->cookies->{$GAM_COOKIE_NAME} = {
121             value => $visitor_id,
122             path => $GAM_COOKIE_PATH,
123             expires => $GAM_COOKIE_USER_PERSISTENCE,
124             };
125             $res->body($GIF_DATA);
126             if ( $DEBUG ) {
127             $res->header('X-GAM-Code', $status);
128             $res->header('X-GAM-URI', $utm_url);
129             }
130             return $res->finalize;
131             }
132              
133             sub get_visitor_id {
134             my ($self, $req, $user_agent, $account) = @_;
135              
136             my $guid = first { defined $_ } map { $req->env->{$_} } (
137             "HTTP_X_DCMGUID",
138             "HTTP_X_UP_SUBNO",
139             "HTTP_X_JPHONE_UID",
140             "HTTP_X_EM_UID"
141             );
142             $guid = "" if ! defined $guid;
143              
144             my $cookie = "";
145             if ( defined $req->cookies->{$GAM_COOKIE_NAME} ) {
146             $cookie = $req->cookies->{$GAM_COOKIE_NAME};
147             }
148              
149             return $cookie if ($cookie ne "");
150              
151             my $message = "";
152             if ($guid ne "") {
153             $message = $guid . $account;
154             } else {
155             $message = $user_agent . int(rand 0x7fffffff );
156             }
157              
158             return "0x" . substr(sha1_hex($message), 0, 16);
159             }
160              
161              
162             1;
163             __END__