-
Notifications
You must be signed in to change notification settings - Fork 5
/
util.go
345 lines (306 loc) · 9.03 KB
/
util.go
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
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
package util
import (
"encoding/json"
"fmt"
"io"
"io/ioutil"
"log"
"net"
"net/http"
"net/url"
"os"
"os/exec"
"path"
"strings"
"time"
)
// Unit for maps in data.go
type Unit struct{}
var unit Unit
// App Struct for holding of information extracted from the APK
type App struct {
DBID int64
ID, Store, Region, Ver string
Path, UnpackDir string
Perms []Permission
Hosts []string
Packages []string
Icon string
UsesReflect bool
APKLocationUUID string
APKLocationPath string
APKLocationRoot string
}
// Permission Struct represents the permission information found
// in an APK
type Permission struct {
ID string `xml:"name,attr"`
MaxSdkVer string `xml:"maxSdkVersion,attr"`
}
// NewApp Constructs a new app. initialising values based on
// the parameters passed.
func NewApp(dbID int64, id, store, region, ver, apkLocationPath, apkLocationRoot, apkLocationUUID string) *App {
return &App{
DBID: dbID,
ID: id,
Store: store,
Region: region,
Ver: ver,
APKLocationPath: apkLocationPath,
APKLocationRoot: apkLocationRoot,
APKLocationUUID: apkLocationUUID}
}
// AppByPath returns an App object with the Path value initialised.
func AppByPath(path string) *App {
return &App{Path: path}
}
// AppDir returns the directory of the apk and other misc files.
func (app *App) AppDir() string {
if app.Path != "" {
return path.Dir(app.Path)
}
return path.Dir(app.ApkPath())
}
// getUUIDMountPath uses linux 'findmnt' program to try and find the mount point of the provided uuid.
func getUUIDMountPath(UUID string) string {
out, err := exec.Command("findmnt", "-rn", "-S", "UUID="+UUID, "-o", "TARGET").Output()
if err != nil {
log.Fatal(err)
}
return string(out[:])
}
// ApkPath will try and find the APK on any mounted disks in potential locations
// starting with the location specified in the DB, falling down to checking
// locations with the root of the path specidied in the DB substituted,
// follewed by checking each location in the config forming a path from the
// app version details.
func (app *App) ApkPath() string {
fmt.Println("Getting APK Path for App:", app.ID)
apkLocation := path.Join(app.APKLocationPath, app.ID+".apk")
fmt.Println("Checking if APK is at: ", apkLocation)
if _, err := os.Stat(apkLocation); err == nil {
fmt.Println("App Found in DB specified location: ", apkLocation)
return path.Join(path.Clean(app.APKLocationPath), app.ID+".apk")
}
// Check if the Root is wrong, get the filesystem mount path
// and replace the APKLocationRoot of APKLocationPath with the new root.
uuidMount := getUUIDMountPath(app.APKLocationUUID)
apkLocation = path.Join(strings.Replace(app.APKLocationPath, app.APKLocationRoot, uuidMount, 1), app.ID+".apk")
fmt.Println("Checking if APK is at: ", apkLocation)
if _, err := os.Stat(apkLocation); err == nil {
fmt.Println("App Found on DB specified Device. UUID: ", app.APKLocationUUID, "APK Path:", apkLocation)
return path.Join(path.Clean(apkLocation), app.ID+".apk")
}
// if the app cannot be found in the new mount location for whatever UUID, go through
// each storage location in the config and check there.
for _, location := range Cfg.StorageConfig.APKDownloadDirectories {
apkLocation = path.Join(strings.Replace(app.APKLocationPath, app.APKLocationRoot, location.Path, 1), app.ID+".apk")
fmt.Println("Checking if APK is at: ", apkLocation)
if _, err := os.Stat(apkLocation); err == nil {
fmt.Println("Searched for APK and found it in: ", apkLocation)
return path.Join(path.Clean(apkLocation), app.ID+".apk")
}
}
if app.Path != "" {
return app.Path
}
return path.Join(app.AppDir(), app.ID+".apk")
}
// OutDir specifies where Apps should be unpacked to. it also creates
// the directory structure for that path and returns the path as a
// string.
func (app *App) OutDir() string {
if app.UnpackDir == "" {
if app.Path != "" {
var err error
app.UnpackDir, err = ioutil.TempDir(Cfg.StorageConfig.APKUnpackDirectory, path.Base(app.Path))
if err != nil {
// maybe do something else?
log.Fatal("Failed to create temp dir in ", Cfg.StorageConfig.APKUnpackDirectory, ": ", err)
}
} else {
app.UnpackDir = path.Join(Cfg.StorageConfig.APKUnpackDirectory, app.ID, app.Store, app.Region, app.Ver)
if err := os.MkdirAll(app.UnpackDir, 0755); err != nil {
log.Fatalf("Failed to create temp dir in %s: %s", app.UnpackDir, err.Error())
}
}
}
return app.UnpackDir
}
// Unpack passes an app to apktool to disassemble an APK. the contents are
// stored in the path specified by OutDir.
func (app *App) Unpack() error {
apkPath, outDir := app.ApkPath(), app.OutDir()
if _, err := os.Stat(apkPath); err != nil {
if os.IsNotExist(err) {
return err
}
return fmt.Errorf("couldn't open apk %s: %s", apkPath, err.Error())
}
if err := os.MkdirAll(path.Dir(outDir), 0755); err != nil {
return os.ErrPermission
}
now := time.Now()
if err := os.Chtimes(path.Dir(outDir), now, now); err != nil {
return os.ErrPermission
}
cmd := exec.Command("apktool", "d", "-s", apkPath, "-o", outDir, "-f")
out, err := cmd.CombinedOutput()
if err != nil {
return fmt.Errorf("%s unpacking apk; output below:\n%s",
err.Error(), string(out))
}
return nil
}
// Cleanup removes all directories specifed in an app object's OutDir.
func (app *App) Cleanup() error {
return os.RemoveAll(app.OutDir())
}
// CheckDir verifies that a Dir is a Dir and exists.
func CheckDir(dir, name string) {
fif, err := os.Stat(dir)
if err != nil {
if os.IsNotExist(err) {
err = os.MkdirAll(dir, 0644)
if err != nil {
//TODO: something else
panic(fmt.Sprintf("Couldn't create %s: %s", name, err.Error()))
}
} else {
//TODO: something else
panic(err)
}
} else if !fif.IsDir() {
panic(fmt.Sprintf("%s isn't a directory!", name))
}
}
// UniqAppend takes the contents of one array and adds any content
// not present in another array.
func UniqAppend(a []string, b []string) []string {
ret := make([]string, 0, len(a)+len(b))
for _, e := range a {
ret = append(ret, e)
}
// set ret = a
for _, be := range b {
add := true
for _, ae := range a {
if ae == be {
add = false
break
}
}
if add {
ret = append(ret, be)
}
}
return ret
}
/*
func uniqAppend(a []interface{}, b []interface{}) []interface{} {
eMap = map[interface{}]Unit
for _, e := range a {
eMap[e] := unit
}
for _, e := range b {
eMap[e] := unit
}
ret := make([]interface{}, 0, len(eMap))
for e, _ := range eMap {
ret := append(ret, e)
}
return ret
}
*/
// Dedup deduplicates a slice
func Dedup(a []string) []string {
length := len(a) - 1
for i := 0; i < length; i++ {
for j := i + 1; j <= length; j++ {
if a[i] == a[j] {
a[j] = a[length]
a = a[:length]
length--
j--
}
}
}
return a
}
// Combine puts together two maps of string keys and unit values.
func Combine(a, b map[string]Unit) map[string]Unit {
ret := a
for e := range b {
ret[e] = unit
}
return ret
}
// StrMap creates a map of strings and units.
func StrMap(args ...string) map[string]Unit {
ret := make(map[string]Unit)
for _, e := range args {
ret[e] = unit
}
return ret
}
// WriteJSON writes and encodes json dat.
func WriteJSON(w io.Writer, data interface{}) error {
enc := json.NewEncoder(w)
enc.SetEscapeHTML(false)
enc.SetIndent("", "")
return enc.Encode(data)
}
// WriteDEAN Writes and Encodes a 'Nah Mate'.
func WriteDEAN(w io.Writer, data interface{}) error {
w.Write([]byte("Nah\n"))
WriteJSON(w, data)
w.Write([]byte("mate."))
return nil
}
// GetJSON from valid url string gets json
func GetJSON(url string, target interface{}) error {
client := &http.Client{Timeout: 10 * time.Second}
r, err := client.Get(url)
if err != nil {
return err
}
if r.StatusCode != http.StatusOK {
return fmt.Errorf("Got status %d while attempting to get GeoIP data", r.StatusCode)
}
defer r.Body.Close()
return json.NewDecoder(r.Body).Decode(target)
}
// GeoIPInfo stores apphosts data for geolocation
type GeoIPInfo struct {
IP string `json:"ip"`
CountryCode string `json:"country_code"`
CountryName string `json:"country_name"`
RegionCode string `json:"region_code"`
RegionName string `json:"region_name"`
City string `json:"city"`
ZipCode string `json:"zip_code"`
TimeZone string `json:"time_zone"`
Latitude float64 `json:"latitude"`
Longitude float64 `json:"longitude"`
MetroCode int `json:"metro_code"`
}
// GetHostGeoIP grabs geo location information from hostname
func GetHostGeoIP(geoipHost, host string) ([]GeoIPInfo, error) {
hosts, err := net.LookupHost(host)
if err != nil {
return nil, err
}
ret := make([]GeoIPInfo, 0, len(hosts))
for _, host := range hosts {
var inf GeoIPInfo
//TODO: fix?
err = GetJSON(geoipHost+"/"+url.PathEscape(host), &inf)
if err != nil {
//TODO: better handling?
fmt.Printf("Couldn't lookup geoip info for %s: %s \n", host, err.Error())
} else {
ret = append(ret, inf)
}
}
return ret, nil
}