/
ipsw.go
146 lines (114 loc) · 3.32 KB
/
ipsw.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
// ipsw is for doing things with IPSWs
package fwutils
import (
"archive/zip"
"bytes"
"errors"
"github.com/DHowett/ranger"
"howett.net/plist"
"net/url"
"net/http"
"io"
"strconv"
"strings"
"time"
//"io/ioutil"
//"github.com/davecgh/go-spew/spew"
)
type IPSW struct {
DownloadURL string
Properties *IPSWProperties
}
type IndividualDevice struct {
Identifier string
BDID int
BoardConfig string
CPID int
Platform string
SCEP int
}
type Restore struct {
DeviceClass string
Devices []*IndividualDevice `plist:"DeviceMap"`
ProductBuildVersion string
ProductType string
ProductVersion string
}
func ReadFile(reader *zip.Reader, file string) (result []byte, err error) {
for _, f := range reader.File {
//fmt.Println(f)
if f.Name == file {
data := make([]byte, f.UncompressedSize64)
rc, err := f.Open()
if err != nil {
return nil, err
}
io.ReadFull(rc, data)
rc.Close()
return data, nil
}
}
return nil, errors.New("Unable to find file")
}
// Get the restore Plist from the IPSW
func (ip *IPSW) GetRestorePlist() (parsed Restore, err error) {
url, err := url.Parse(ip.DownloadURL)
// retrieve the Restore.plist from the IPSW, possibly need the BuildManifest too?
reader, err := ranger.NewReader(&ranger.HTTPRanger{URL: url})
zipreader, err := zip.NewReader(reader, reader.Length())
restorePlist, err := ReadFile(zipreader, "Restore.plist")
// decode the plist
dec := plist.NewDecoder(bytes.NewReader(restorePlist))
dec.Decode(&parsed)
return parsed, err
}
type IPSWProperties struct {
Identifier string
Version string
BuildID string
Filename string
Size int64
MD5sum string
SHA1sum string
UploadDate string
ReleaseDate string
AppleTVSoftwareVersion string
// ... and probably some more that i've not thought of yet
}
// given a longForm to parse
const longForm = "Mon, 2 Jan 2006 15:04:05 MST"
// PopulateInfo gets the information about the IPSW from the path
func (ip *IPSW) PopulateInfo() (err error) {
restorePlist, err := ip.GetRestorePlist()
// get the headers from the URL
res, err := http.Get(ip.DownloadURL)
// get the size as an int
size, err := strconv.ParseInt(res.Header["Content-Length"][0], 10, 64)
// the md5sum is the string before the `:` after the `"` has been removed
md5sum := strings.Split(strings.Replace(res.Header["Etag"][0], `"`, "", -1), ":")[0]
uploadDate, _ := time.Parse(longForm, res.Header["Last-Modified"][0])
ip.Properties = &IPSWProperties{
Identifier: restorePlist.ProductType,
Version: restorePlist.ProductVersion,
BuildID: restorePlist.ProductBuildVersion,
Filename: restorePlist.ProductType + "_" + restorePlist.ProductVersion + "_" + restorePlist.ProductBuildVersion + "_Restore.ipsw",
Size: size,
MD5sum: md5sum,
UploadDate: uploadDate.Format(longForm),
}
return err
}
func (ip *IPSW) PopulateInfoGivenBuild(build *IndividualBuild) {
ip.Properties.SHA1sum = build.FirmwareSHA1
t := time.Now()
ip.Properties.ReleaseDate = t.Format(longForm)
if ip.Properties.Version != build.ProductVersion {
// this is most likely an AppleTV, which incorrectly reports versions
ip.Properties.AppleTVSoftwareVersion = build.ProductVersion
}
return
}
// creates a new IPSW given an URL
func NewIPSW(url string) *IPSW {
return &IPSW{DownloadURL: url}
}