signal-styler 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +674 -0
- package/README.md +120 -0
- package/index.js +99 -0
- package/package.json +19 -0
- package/purple.css +334 -0
- package/utils.js +212 -0
package/utils.js
ADDED
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
const asar = require("@electron/asar");
|
|
2
|
+
const fs = require("fs");
|
|
3
|
+
const path = require("path");
|
|
4
|
+
const os = require("os");
|
|
5
|
+
|
|
6
|
+
const NEW_ASAR_PATH = path.join(os.homedir(), ".cache", "signal-styled.asar");
|
|
7
|
+
|
|
8
|
+
const BACKUP_ASAR_PATH = path.join(
|
|
9
|
+
os.homedir(),
|
|
10
|
+
".cache",
|
|
11
|
+
"signal-original.asar"
|
|
12
|
+
);
|
|
13
|
+
|
|
14
|
+
const MODDED_MANIFEST_HEADER = `/* SIGNAL-STYLER */ @import "custom.css"; /* SIGNAL-STYLER */`;
|
|
15
|
+
|
|
16
|
+
class Utils {
|
|
17
|
+
constructor() {
|
|
18
|
+
this.asarPath = this.assumeAsarPath();
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Validates if the given asar path is valid.
|
|
23
|
+
* @return {boolean} true if the asar path is valid, false otherwise.
|
|
24
|
+
*
|
|
25
|
+
* A valid asar path is one that is a file and ends with ".asar".
|
|
26
|
+
*/
|
|
27
|
+
validateAsarPath() {
|
|
28
|
+
if (!this.asarPath) return false;
|
|
29
|
+
|
|
30
|
+
return (
|
|
31
|
+
fs.existsSync(this.asarPath) &&
|
|
32
|
+
this.asarPath.endsWith(".asar") &&
|
|
33
|
+
fs.statSync(this.asarPath).isFile()
|
|
34
|
+
);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Checks if the asar file has root-only permissions.
|
|
39
|
+
*
|
|
40
|
+
* If the file has root-only permissions, it means that the user needs to run
|
|
41
|
+
* signal-styler with sudo.
|
|
42
|
+
*
|
|
43
|
+
* @return {boolean} true if the asar file has root-only permissions, false
|
|
44
|
+
* otherwise.
|
|
45
|
+
*/
|
|
46
|
+
checkNeedsSudo() {
|
|
47
|
+
try {
|
|
48
|
+
const stats = fs.statSync(this.asarPath);
|
|
49
|
+
return stats.uid === 0;
|
|
50
|
+
} catch (err) {
|
|
51
|
+
console.error(`Error checking file ownership: ${err.message}`);
|
|
52
|
+
return true;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
assumeAsarPath() {
|
|
57
|
+
const systemFlatpak =
|
|
58
|
+
"/var/lib/flatpak/app/org.signal.Signal/current/active/files/Signal/resources/app.asar";
|
|
59
|
+
const userFlatpak = path.join(
|
|
60
|
+
os.homedir(),
|
|
61
|
+
".var",
|
|
62
|
+
"app",
|
|
63
|
+
"org.signal.Signal",
|
|
64
|
+
"current",
|
|
65
|
+
"active",
|
|
66
|
+
"files",
|
|
67
|
+
"Signal",
|
|
68
|
+
"resources",
|
|
69
|
+
"app.asar"
|
|
70
|
+
);
|
|
71
|
+
|
|
72
|
+
const windows =
|
|
73
|
+
process.platform === "win32"
|
|
74
|
+
? path.join(
|
|
75
|
+
process.env.LOCALAPPDATA,
|
|
76
|
+
"Programs",
|
|
77
|
+
"Signal",
|
|
78
|
+
"resources",
|
|
79
|
+
"app.asar"
|
|
80
|
+
)
|
|
81
|
+
: null;
|
|
82
|
+
|
|
83
|
+
const darwin =
|
|
84
|
+
process.platform === "darwin"
|
|
85
|
+
? "/Applications/Signal.app/Contents/Resources/app.asar"
|
|
86
|
+
: null;
|
|
87
|
+
|
|
88
|
+
if (fs.existsSync(systemFlatpak)) {
|
|
89
|
+
return systemFlatpak;
|
|
90
|
+
} else if (fs.existsSync(userFlatpak)) {
|
|
91
|
+
return userFlatpak;
|
|
92
|
+
} else if (windows && fs.existsSync(windows)) {
|
|
93
|
+
return windows;
|
|
94
|
+
} else if (darwin && fs.existsSync(darwin)) {
|
|
95
|
+
return darwin;
|
|
96
|
+
} else {
|
|
97
|
+
return null;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Extracts the manifest.css file from the given asar.
|
|
103
|
+
* @return {string} the text content of the manifest.css file.
|
|
104
|
+
*/
|
|
105
|
+
getManifest() {
|
|
106
|
+
return asar.extractFile(this.asarPath, "stylesheets/manifest.css");
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Writes the given manifest to the correct location inside the `patchDir`
|
|
111
|
+
* after prepending the `MODDED_MANIFEST_HEADER`.
|
|
112
|
+
* @param {string} manifest - The text content of the manifest.css file.
|
|
113
|
+
*/
|
|
114
|
+
patchManifest(manifest) {
|
|
115
|
+
// write new manifest to patchDir
|
|
116
|
+
|
|
117
|
+
fs.writeFileSync(
|
|
118
|
+
path.join(this.patchDir, "stylesheets/manifest.css"),
|
|
119
|
+
MODDED_MANIFEST_HEADER + "\n" + manifest.toString()
|
|
120
|
+
);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* Copies the given CSS file to the correct location inside the `patchDir`.
|
|
125
|
+
* @param {string} cssPath - Path to the custom stylesheet CSS file.
|
|
126
|
+
*/
|
|
127
|
+
setStylesheet(cssPath) {
|
|
128
|
+
fs.copyFileSync(
|
|
129
|
+
cssPath,
|
|
130
|
+
path.join(this.patchDir, "stylesheets/custom.css")
|
|
131
|
+
);
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* Creates two temporary directories, `patchDir` and `buildDir`, and also
|
|
136
|
+
* creates a "stylesheets" directory inside `patchDir`.
|
|
137
|
+
*
|
|
138
|
+
* The directories are created in the system's temporary directory and are
|
|
139
|
+
* named "signal-styler-patch-{random}" and "signal-styler-build-{random}".
|
|
140
|
+
*
|
|
141
|
+
* The `patchDir` is used to store the modified manifest.css file, while the
|
|
142
|
+
* `buildDir` is used to store the full modified asar file before it is built
|
|
143
|
+
* into a new asar file.
|
|
144
|
+
*/
|
|
145
|
+
createTempDirs() {
|
|
146
|
+
this.patchDir = fs.mkdtempSync(
|
|
147
|
+
path.join(os.tmpdir(), "signal-styler-patch-")
|
|
148
|
+
);
|
|
149
|
+
this.buildDir = fs.mkdtempSync(
|
|
150
|
+
path.join(os.tmpdir(), "signal-styler-build-")
|
|
151
|
+
);
|
|
152
|
+
|
|
153
|
+
fs.mkdirSync(path.join(this.patchDir, "stylesheets"));
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
/**
|
|
157
|
+
* Returns whether the given manifest string has been modified by
|
|
158
|
+
* signal-styler. The check is done by looking for the presence of the
|
|
159
|
+
* MODDED_MANIFEST_HEADER string.
|
|
160
|
+
* @param {string} manifest - The text content of the manifest.css file.
|
|
161
|
+
* @returns {boolean} - true if signal-styler has modified this manifest, false
|
|
162
|
+
* otherwise.
|
|
163
|
+
*/
|
|
164
|
+
isManifestModified(manifest) {
|
|
165
|
+
return manifest.toString().startsWith(MODDED_MANIFEST_HEADER);
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
/**
|
|
169
|
+
* Builds a new Signal Desktop asar file by first unpacking the full asar to
|
|
170
|
+
* the `buildDir`, then copying the content of the `patchDir` over it, then
|
|
171
|
+
* backing up the original asar by copying it to `BACKUP_ASAR_PATH`, and
|
|
172
|
+
* finally building a new asar with the modified content.
|
|
173
|
+
*
|
|
174
|
+
* @returns {Promise<void>} - a Promise that resolves when the build is
|
|
175
|
+
* complete.
|
|
176
|
+
*/
|
|
177
|
+
async build() {
|
|
178
|
+
// unpack full asar to buildDir
|
|
179
|
+
asar.extractAll(this.asarPath, this.buildDir);
|
|
180
|
+
|
|
181
|
+
// copy content of patchDir to buildDir
|
|
182
|
+
fs.cpSync(this.patchDir, this.buildDir, { recursive: true });
|
|
183
|
+
|
|
184
|
+
// backup original asar
|
|
185
|
+
fs.copyFileSync(this.asarPath, BACKUP_ASAR_PATH);
|
|
186
|
+
|
|
187
|
+
// build asar
|
|
188
|
+
await asar.createPackage(this.buildDir, NEW_ASAR_PATH);
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
/**
|
|
192
|
+
* Installs the new asar file that was built by copying it to the location of
|
|
193
|
+
* the original asar file.
|
|
194
|
+
*/
|
|
195
|
+
install() {
|
|
196
|
+
// install new asar
|
|
197
|
+
fs.copyFileSync(NEW_ASAR_PATH, this.asarPath);
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
/**
|
|
201
|
+
* Removes the temporary directories and files created during the process.
|
|
202
|
+
*/
|
|
203
|
+
cleanup() {
|
|
204
|
+
// clean up
|
|
205
|
+
|
|
206
|
+
fs.rmSync(this.patchDir, { recursive: true });
|
|
207
|
+
fs.rmSync(this.buildDir, { recursive: true });
|
|
208
|
+
fs.unlinkSync(NEW_ASAR_PATH);
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
module.exports = new Utils();
|