-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathGen.js
325 lines (300 loc) · 8.79 KB
/
Gen.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
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
#!/usr/bin/env node
const crypto = require('crypto');
const fs = require('fs');
const path = require('path');
const marked = require('./Libs/marked.min.js');
const md5 = require('./Libs/md5.min.js');
const FancyEncrypt = require('./Libs/FancyEncrypt.js');
/* TODO:
- Hide some items
- Rich preview
- Autoredirects
- Embeds
*/
const HashSize = 6;
const NoScriptNotice = `
<p><b>This page requires JavaScript</b> to work for security reasons.</p>
<p>If you see this message:</p>
<ul>
<li>You may have disabled JavaScript in your browser. If that's the case, you should <b>re-enable it</b>.</li>
<li>Your browser may be outdated and unable to run modern code.</li>
</ul>
<p>Don't worry, as all the code is:</p>
<ul>
<li>Open source: You can review it, by using "View Page Source" or visiting <a href="https://gitlab.com/octospacc/links" target="_blank" rel="noopener">the Git repo</a>.</li>
<li>Free (libre): You don't have to give away your freedom.</li>
</ul>
`;
const CaptchaHtml = `<div style="border: double; display: inline-block; padding: 8px;"><label>
<button style="width: 2em; height: 2em;"></button> <span style="vertical-align: sub;">I am not a robot</span>
</label></div>`;
const protocolPrefixes = {
bittorrent: "magnet:?xt=urn:btih:",
drivehlb0: "https://hlb0it.blogspot.com/?path=/Drive/",
};
var BaseHTML = '',
Items = {},
OldItems = {};
// https://stackoverflow.com/a/73594511
const walk = (dir, files = []) => {
const dirFiles = fs.readdirSync(dir);
for (const f of dirFiles) {
const stat = fs.lstatSync(dir + path.sep + f)
if (stat.isDirectory()) {
walk(dir + path.sep + f, files);
} else {
files.push(dir + path.sep + f);
};
};
return files;
};
const TryMkdirSync = Dir => {
if (!fs.existsSync(Dir)) {
return fs.mkdirSync(Dir, {recursive:true});
};
};
const InitItem = _ => {
let Item = {};
Item["Alias"] = '';
Item["Content"] = '';
return Item;
};
const MakeStrAltern = Str => {
let New = '';
for (let i = 0; i < Str.length; i++) {
if (i % 2 == 0) New += Str[i];
};
return New;
};
const FlattenStr = Str => {
let Flat = '';
let Lines = Str.trim().split('\n');
for (let i = 0; i < Lines.length; i++) {
Flat += Lines[i].trim() + '\n';
};
return Flat;
};
const GetTitle = Item => {
if ("Title" in Item) {
if (Item["Title"] != '') {
return '# ' + Item["Title"] + '\n\n';
} else {
return '';
};
};
};
const GetMatchableContent = Item => {
if (!"Content" in Item) return;
let Content = Item["Content"];
if (Content == '') return;
if ("Title" in Item) {
if (Item["Title"] != '') {
Content = GetTitle(Item) + Content;
};
};
return Content;
};
const GetItemId = Item => {
if (Item["Id"]) {
return Item["Id"];
} else {
let OldId = GetContentItemId(Item["Content"], OldItems);
if (OldId) {
return OldId;
} else {
return crypto.randomBytes(HashSize).toString('hex');
};
};
};
const GetContentItemId = (Content, From) => {
if (!From) From = Items;
let Values = Object.values(From);
for (let i = 0; i < Values.length; i++) {
let Item = Values[i];
if (Content.trim() == Item["Content"].trim()) return Item["Id"];
};
};
const GetContentHash = (Content, Pad) => {
let Hash = MakeStrAltern(md5(FlattenStr(Content) + " ".repeat(Pad)).substring(0,HashSize*4));
//let HashB64 = Buffer.from(Hash, 'hex').toString('base64');
return Hash;
};
const StoreItem = Item => {
if (Item["Content"] != '') {
Item["Alias"] = Item["Alias"].trim();
Item["Content"] = Item["Content"].trim();
let Id = GetItemId(Item);
while (Id in Items) { // If item with same id is already present, retry
Id = GetItemId(Item);
};
//let Pad = 0;
let Hash = GetContentHash(Item["Content"], 0);
//while (Hash in Items) { // If item with same hash is already present, retry with pad
// Pad++;
// Hash = GetContentHash(Item["Content"], Pad);
//}
Items[Id] = {
"Id": Id,
"Hash": Hash,
"Visibility": (Item["Visibility"] == 'false' ? false : true),
"Obfuscation": (Item["Obfuscation"] == 'false' ? false : true),
"NetProtection": (Item["NetProtection"] == 'true' ? true : false),
"Alias": (Item["Alias"] != '' ? Item["Alias"].split(' ') : []),
"Title": Item["Title"],
"Content": Item["Content"],
};
};
};
const DoParseFile = Data => {
let Item = InitItem();
let ParsedMeta = false;
let Lines = Data.trim().split('\n');
for (let i = 0; i < Lines.length; i++) {
let l = Lines[i];
let lt = l.trim().replace('\t', ' ');
if (lt.startsWith('# ')) { // Title of new item
StoreItem(Item); // Store previous item (if exists)
Item = InitItem();
ParsedMeta = false;
Item["Title"] = lt.substring(2);
} else if (lt.startsWith('// ') && !ParsedMeta) { // Meta line
let MetaLine = lt.substring(3).toLowerCase();
['Id', 'Alias', 'Visibility', 'Obfuscation', 'NetProtection'].forEach(function(i) {
if (MetaLine.startsWith(i.toLowerCase() + ' ')) {
Item[i] = MetaLine.substring(i.length+1);
}
});
if (!Lines[i+1].trim().startsWith('// ')) ParsedMeta = true;
} else {
Item["Content"] += l + '\n';
};
};
StoreItem(Item); // Store last item
};
const DoHandleFiles = Mode => {
let Files = walk('Data');
for (let i = 0; i < Files.length; i++) {
let File = Files[i].toLowerCase();
if (File.endsWith('.md') || File.endsWith('.markdown')) {
let Data = fs.readFileSync(Files[i], 'utf8');
if (Mode == 'Parse') {
DoParseFile(Data);
} else if (Mode == 'Patch') {
fs.writeFileSync(Files[i], DoPatchFile(Data));
};
};
};
};
const Init = _ => {
if (fs.existsSync('Data.json')) {
OldItems = JSON.parse(fs.readFileSync('Data.json', 'utf8'));
}
if (fs.existsSync('Base.html')) {
BaseHTML = fs.readFileSync('Base.html', 'utf8');
};
};
const MakeHTMLPage = Item => {
let content = Item.Content;
for (const protocol in protocolPrefixes) {
content = content.replaceAll(`<${protocol}://`, `<${protocolPrefixes[protocol]}`);
}
let html = marked.parse(GetTitle(Item) + content);
return BaseHTML
.replace('{{NOSCRIPT}}', (Item.Obfuscation ? NoScriptNotice : ''))
.replace('{{TITLE}}', (Item.Title || '[?]'))
.replace('{{CONTENT}}', (Item.Obfuscation ? '' : html))
.replace('{{CONTENTCRYPT}}', (Item.Obfuscation && !Item.NetProtection ? FancyEncrypt(html) : ''))
.replace('{{NETPROTECTION}}', (Item.NetProtection ? true : ''));
};
const WriteItem = Item => {
let Id = Item["Id"];
let Hash = Item["Hash"];
let HTML = MakeHTMLPage(Item);
Item["Html"] = marked.parse(GetTitle(Item) + Item["Content"]);
let Raw = JSON.stringify(Item, null, '\t');
TryMkdirSync('public/$'+Id);
fs.writeFileSync('public/$'+Id+'/index.html', HTML);
fs.writeFileSync('public/$'+Id+'/Data.json', Raw);
TryMkdirSync('public/!'+Hash);
fs.writeFileSync('public/!'+Hash+'/index.html', HTML);
fs.writeFileSync('public/!'+Hash+'/Data.json', Raw);
Item["Alias"].forEach(function(Alias) {
TryMkdirSync('public/'+Alias);
fs.writeFileSync('public/'+Alias+'/index.html', HTML);
fs.writeFileSync('public/'+Alias+'/Data.json', Raw);
});
};
const WritePages = _ => {
Object.values(Items).forEach(function(Item) {
WriteItem(Item);
});
};
const DoPatchFile = Data => {
let IdLine = -1,
Content = '',
MetaPresent = false,
IdPresent = false,
ParsedMeta = false;
let Lines = Data.trim().split('\n');
for (let i = 0; i < Lines.length; i++) {
let l = Lines[i];
let lt = l.trim();
if (lt.startsWith('# ')) { // New item
if (IdLine != -1) Lines.splice(IdLine, 0, '// Id ' + GetContentItemId(Content) + '\n');
IdLine = -1,
Content = '',
MetaPresent = false,
IdPresent = false,
ParsedMeta = false;
} else if (lt.startsWith('// ') && !ParsedMeta) { // Meta line
MetaPresent = true;
let MetaLine = lt.substring(3).toLowerCase();
if (MetaLine.startsWith('id ')) IdPresent = true;
if (!Lines[i+1].trim().startsWith('// ')) ParsedMeta = true;
} else {
if (!IdPresent && lt != '') {
if (MetaPresent) {
for (let j = i; j > 0; j--) {
if (Lines[j-1].startsWith('// ')) {
IdLine = j;
IdPresent = true;
break;
};
};
} else {
IdLine = i;
IdPresent = true;
};
};
Content += l + '\n';
};
};
if (IdLine != -1) Lines.splice(IdLine, 0, '// Id ' + GetContentItemId(Content) + '\n');
return Lines.join('\n') + '\n'
};
const MakeItemsList = _ => {
let list = {};
Object.values(Items).forEach(function(Item) {
let id = Item.Id;
list[id] = {
Id: id,
Hash: Item["Hash"],
Alias: (Item["Alias"] != '' ? Item["Alias"].split(' ') : []),
Title: Item["Title"],
};
});
return list;
};
const Main = _ => {
Init();
DoHandleFiles('Parse');
fs.writeFileSync('Data.json', JSON.stringify(Items, null, '\t'));
WritePages();
fs.writeFileSync('public/List.json', JSON.stringify(MakeItemsList(), null, '\t'));
fs.writeFileSync('public/Gdo.js', `Gdo=${JSON.stringify({
k: 'org.eu.octt.go',
cpt: FancyEncrypt(CaptchaHtml),
})}`);
DoHandleFiles('Patch');
};
Main();