/
firefox_extension.go
292 lines (197 loc) · 7.14 KB
/
firefox_extension.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
package webdriver
import (
"archive/zip"
"encoding/xml"
"errors"
"fmt"
"log"
"io"
"io/ioutil"
"os"
"path/filepath"
"strings"
)
type (
Extension struct {
BaseDir string
ConfigList []string
ConfigPolicy string
Path string
Name string
Policy string
TargetPath string
}
InstallManifest struct {
Description InstallManifestDescription
}
InstallManifestDescription struct {
ID string `xml:"id"`
}
)
// Installs the webdriver.xpi extension into the current firefox profile directory.
func (s *Firefox) installExtension() error {
var err error
log.Println("installExtension()...")
// verifies the profile directory exists. if not, creates it
// also, verifies profiledir/extensions exists. if not, creates it
// then, if the policy is to remove the extension; it is removed.
if err = s.verifyExtensionsPath(); err == nil && s.Extension.Policy == "remove" {
// directories exist and the policy is to remove and install the extension
if s.Extension.Name, err = s.extractExtensionName(); err == nil {
if err = s.prepareExtensionTarget(); err == nil {
// just return the error. no need to if logic, etc.
err = s.extractExtensionContents()
}
}
}
return err
}
func (s *Firefox) verifyExtensionsPath() (err error) {
// The target ProfileDir and the extensions subdirectory need to exist
// in order to install and use the webdriver plugin. So, verifyExtensionsPath
// will test for the existence of the full path to the extensions directory
// and attempt to create it if it does not exist. That would create the ProfileDir
// as well during the process thus killing two birds with one stone.
//
// Returns and error if there is a problem, otherwise, returns nil
log.Println("verifyExtensionsPath() verifying profile and extensions directory exists: ", s.Extension.BaseDir)
// check if the full path to the extensions directory exists
if _, err = os.Stat(s.Extension.BaseDir); err != nil {
log.Println("profile / extensions directory DOES NOT exist. attempting to created it")
// the full path to the extensions directory DOES NOT exist
// attempt to create it with MkdirAll. That way, the ProfileDir
// will be created as well if it does not exist (two birds one stone)
err = os.MkdirAll(s.Extension.BaseDir, s.DirPermissions)
if err != nil {
buffer := fmt.Sprintf("verifyExtensionsPath() => %v DOES NOT exist and unable to create\n", s.Extension.BaseDir)
buffer = fmt.Sprint("%v%v", buffer, err.Error())
err = errors.New(buffer)
log.Println(err)
} else {
log.Println("Directory created! ", s.Extension.BaseDir)
}
} else {
log.Println("verifyExtensionsPath() profile directory ALREADY exists", s.Extension.BaseDir)
}
return err
}
// Extracts the entire contents of webdriver.xpi
func (s *Firefox) extractExtensionContents() (err error) {
log.Println("Installing the extension at: ", s.Extension.TargetPath)
var zipFile *zip.ReadCloser
total := 0
if _, err = os.Stat(s.Extension.Path); err == nil {
if zipFile, err = zip.OpenReader(s.Extension.Path); err == nil {
defer zipFile.Close()
for _, f := range zipFile.File {
if err = s.writeExtensionFile(f); err != nil {
break
}
total += 1
}
}
}
log.Println("Total files installed: ", total)
return err
}
// Writes the contents of a single file contained in webdriver.xpi
func (s *Firefox) writeExtensionFile(f *zip.File) (err error) {
// seperated this into a function so I could use defer, etc.
var file io.ReadCloser
if file, err = f.Open(); err == nil {
defer file.Close()
targetFileSpec := filepath.Join(s.Extension.TargetPath, f.Name)
if f.FileInfo().IsDir() {
err = os.MkdirAll(targetFileSpec, s.DirPermissions)
} else {
var targetFile *os.File
if targetFile, err = os.OpenFile(targetFileSpec,
os.O_WRONLY | os.O_CREATE,
s.FilePermissions); err == nil {
defer targetFile.Close()
_, err = io.Copy(targetFile, file)
}
}
}
return err
}
// Extracts the name of the extension from within install.rdf
// which is a file contained in webdriver.xpi
func (s *Firefox) extractExtensionName() (extensionName string, err error) {
var zipFile *zip.ReadCloser
var file io.ReadCloser
if _, err = os.Stat(s.Extension.Path); err == nil {
// there is a file named install.rdf within the webdriver extension
// the contents of that file contain the plugin name
// open the zip archive (webdriver extension) and
// extract the contents of of install.rdf to obtain the file name
if zipFile, err = zip.OpenReader(s.Extension.Path); err == nil {
defer zipFile.Close()
for _, f := range zipFile.File {
if extensionName == "" {
if strings.ToLower(f.Name) == "install.rdf" {
if file, err = f.Open(); err == nil {
var buffer []byte
if buffer, err = ioutil.ReadAll(file); err == nil {
manifest := InstallManifest{}
if err = xml.Unmarshal(buffer, &manifest); err == nil {
extensionName = manifest.Description.ID
}
}
file.Close()
}
}
} else {
break
}
}
}
}
return extensionName, err
}
// Prepares the extension target installation directory. if it exists, then,
// it is removed and created again.
func (s *Firefox) prepareExtensionTarget() (err error) {
s.Extension.TargetPath = filepath.Join(s.Extension.BaseDir, s.Extension.Name)
log.Println("Preparing the extension target installation directory: ", s.Extension.TargetPath)
if _, err2 := os.Stat(s.Extension.TargetPath); err2 == nil {
err = os.RemoveAll(s.Extension.TargetPath)
}
if err == nil {
err = os.MkdirAll(s.Extension.TargetPath, s.DirPermissions)
if err != nil {
buffer := fmt.Sprintf("prepareExtensionTarget() => %v unable to create extension target directory\n", s.Extension.TargetPath)
buffer = fmt.Sprint("%v%v", buffer, err.Error())
err = errors.New(buffer)
}
}
return err
}
// The spec recommends removing the following files:
// compatibility.ini
// extensions.cache
// extensions.ini
// extensions.json
// extensions.rdf
// extensions.sqlite
// extensions.sqlite-journal
//
// By default, removeExtensionConfig() will remove the above list. You can add / remove
// files to / from the list, however, it will ONLY remove files in the root of the extensions
// directory.
//
// You have the option of bypassing by setting Extension.ConfigPolicy = ""
func (s *Firefox) removeExtensionConfig() (err error) {
if s.Extension.ConfigPolicy == "remove" {
for _, v := range s.Extension.ConfigList {
if err == nil {
targetFileSpec := filepath.Join(s.ProfileDir, v)
// nil means the file/directory exists
if _, err2 := os.Stat(targetFileSpec); err2 == nil {
err = os.Remove(targetFileSpec)
}
}
}
}
return err
}