-
Notifications
You must be signed in to change notification settings - Fork 86
/
protocol.js
173 lines (170 loc) · 6.73 KB
/
protocol.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
/**
* Protocol-related functions. For a PHP implementation, see
* http://thomascannon.net/blog/2011/06/downloading-apks-from-android-market/
*
* Authors:
* redphoenix89 <http://codekiem.com/>
* Stephan Schmitz <eyecatchup@gmail.com>
* Peter Wu <peter@lekensteyn.nl>
*/
'use strict';
/* jshint browser:true, devel:true */
/* globals openTab, processAsset */
/* exported MarketSession */
/**
* Serialize Javascript types in a special format used by MarketSession.
*/
var Utils = {
stringToByteArray: function(str) {
var b = [];
for (var pos = 0; pos < str.length; ++pos) {
b.push(str.charCodeAt(pos));
}
return b;
},
serializeInt32: function(num) {
var data = [];
for (var times = 0; times < 5; times++) {
var elm = num % 128;
if ((num >>>= 7)) {
elm += 128;
}
data.push(elm);
if (num === 0) {
break;
}
}
return data;
},
serializeData: function(arr, value, data_type) {
var new_data = [];
switch (data_type) {
case "string":
new_data = new_data.concat(this.serializeInt32(value.length));
new_data = new_data.concat(this.stringToByteArray(value));
break;
case "int32":
new_data = new_data.concat(this.serializeInt32(value));
break;
case "bool":
new_data.push(value ? 1 : 0);
break;
}
return arr.concat(new_data);
}
};
/* Starts an APK download attempt */
var MarketSession = {
/**
* Called when pressing the APK Downloader icon in the location bar.
*/
download: function(packageName, tabId) {
if (!localStorage.getItem("authToken")) {
alert("Please login at the Options page");
openTab("options.html");
} else if (!localStorage.getItem("simCountry") || !localStorage.getItem("simOperator") || !localStorage.getItem("simOperatorCode")) {
alert("Please set Sim Operator in the Options page first");
openTab("options.html");
} else {
var options = {};
options.authToken = localStorage.authToken;
options.isSecure = true;
options.sdkVersion = 2009011;
options.deviceId = localStorage.deviceId;
options.deviceAndSdkVersion = "passion:15";
options.locale = "en";
options.country = "us";
options.operatorAlpha = localStorage.simOperator;
options.simOperatorAlpha = localStorage.simOperator;
options.operatorNumeric = localStorage.simOperatorCode;
options.simOperatorNumeric = localStorage.simOperatorCode;
options.packageName = packageName;
var asset_query_base64 = this.generateAssetRequest(options);
processAsset(asset_query_base64, packageName, tabId);
}
},
/**
* @returns base64 encoded binary data that can be passed to Google Play API.
*/
generateAssetRequest: function(options, tabUrl) {
/* some constants to avoid magic numbers */
var FIELD_AUTHTOKEN = 0;
var FIELD_ISSECURE = 2;
var FIELD_SDKVERSION = 4;
var FIELD_DEVICEID = 6;
var FIELD_DEVICEANDSDKVERSION = 8;
var FIELD_LOCALE = 10;
var FIELD_COUNTRY = 12;
var FIELD_OPERATORALPHA = 14;
var FIELD_SIMOPERATORALPHA = 16;
var FIELD_OPERATORNUMERIC = 18;
var FIELD_SIMOPERATORNUMERIC = 20;
var FIELD_PACKAGENAME_LENGTH = 22;
var FIELD_PACKAGENAME = 24;
/* describes format of request, numbers will be filled in, arrays of
* numbers will be appended as-is */
var desc = [FIELD_AUTHTOKEN, [16], FIELD_ISSECURE, [24],
FIELD_SDKVERSION, [34], FIELD_DEVICEID, [42],
FIELD_DEVICEANDSDKVERSION, [50], FIELD_LOCALE, [58],
FIELD_COUNTRY, [66], FIELD_OPERATORALPHA, [74],
FIELD_SIMOPERATORALPHA, [82], FIELD_OPERATORNUMERIC, [90],
FIELD_SIMOPERATORNUMERIC, [19, 82],
FIELD_PACKAGENAME_LENGTH, [10], FIELD_PACKAGENAME, [20]];
var out = [];
var simOperatorLength = 0;
for (var i = 0; i<desc.length; i++) {
if ("object" == typeof desc[i]) {
/* array, just append it as raw numbers to the output */
out = out.concat(desc[i]);
continue;
}
switch (desc[i]) {
case FIELD_AUTHTOKEN:
out = Utils.serializeData(out, options.authToken, "string");
break;
case FIELD_ISSECURE:
out = Utils.serializeData(out, options.isSecure, "bool");
break;
case FIELD_SDKVERSION:
out = Utils.serializeData(out, options.sdkVersion, "int32");
break;
case FIELD_DEVICEID:
out = Utils.serializeData(out, options.deviceId, "string");
break;
case FIELD_DEVICEANDSDKVERSION:
out = Utils.serializeData(out, options.deviceAndSdkVersion, "string");
break;
case FIELD_LOCALE:
out = Utils.serializeData(out, options.locale, "string");
break;
case FIELD_COUNTRY:
out = Utils.serializeData(out, options.country, "string");
break;
case FIELD_OPERATORALPHA:
out = Utils.serializeData(out, options.operatorAlpha, "string");
break;
case FIELD_SIMOPERATORALPHA:
out = Utils.serializeData(out, options.simOperatorAlpha, "string");
break;
case FIELD_OPERATORNUMERIC:
out = Utils.serializeData(out, options.operatorNumeric, "string");
break;
case FIELD_SIMOPERATORNUMERIC:
out = Utils.serializeData(out, options.simOperatorNumeric, "string");
simOperatorLength = out.length + 1;
break;
case FIELD_PACKAGENAME_LENGTH:
out = out.concat(Utils.serializeInt32(options.packageName.length + 2));
break;
case FIELD_PACKAGENAME:
out = Utils.serializeData(out, options.packageName, "string");
break;
}
}
out = [10].concat(Utils.serializeInt32(simOperatorLength)).concat([10]).concat(out);
var binary = out.map(function (c) {
return String.fromCharCode(c);
}).join("");
return btoa(binary);
}
};