-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathtickfix.js
254 lines (238 loc) · 7.81 KB
/
tickfix.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
// $Id: tickfix.js,v 1.6 2020/04/28 23:42:10 rswindell Exp $
// TickFix: A remote Area Manager (ala FileFix) for TickIT
// Requires SBBSecho v3.11 or later
// Install using "jsexec tickfix -install"
const REVISION = "$Revision: 1.6 $".split(' ')[1];
require('smbdefs.js', 'NET_FIDO');
var fidoaddr = load({}, 'fidoaddr.js');
// Parse tickfix.ini
var f = new File(file_cfgname(system.ctrl_dir, "tickfix.ini"));
f.open("r");
var AreaMgr = f.iniGetValue(null, "AreaMgr", ["FileFix"]);
var backup_level = f.iniGetValue(null, "Backups", 10);
f.close();
for(var i in AreaMgr)
AreaMgr[i] = AreaMgr[i].toLowerCase();
const help_text =
"TickFix " + REVISION + " Help\r\n" +
"\r\n" +
"In the body of the message, one or more:\r\n" +
"\r\n" +
"+<tag> Connect an area\n" +
"+ALL Connect all areas\n" +
"-<tag> Disconnect an area\n" +
"-ALL Disconnect all areas\n" +
"%HELP This message\r\n" +
"%LIST List of all available areas (tags)\r\n" +
"%QUERY List of linked / connected areas\r\n" +
"%UNLINKED List of unlinked areas\r\n" +
"[---] Everything below the tear line is ignored"
;
if(argv.indexOf("-install") >= 0)
{
print("Installing TickFix");
var cnflib = load({}, "cnflib.js");
var xtrn_cnf = cnflib.read("xtrn.cnf");
if(!xtrn_cnf)
js.report_error("Failed to read xtrn.cnf", /* fatal */true);
var changed = false;
if(!xtrn_area.event["tickfix"]) {
print("Adding timed event: TICKFIX");
xtrn_cnf.event.push( {
"code": "TICKFIX",
"cmd": "?tickfix",
"days": 0,
"time": 0,
"node_num": 1,
"settings": 0,
"startup_dir": "",
"freq": 30, // interval
"mdays": 0,
"months": 0
});
changed = true;
}
if(changed && !cnflib.write("xtrn.cnf", undefined, xtrn_cnf))
js.report_error("Failed to write xtrn.cnf", /* fatal */true);
var f = new File(file_cfgname(system.ctrl_dir, "sbbsecho.ini"));
file_backup(f.name, backup_level);
if(!f.open(f.exists ? 'r+':'w+'))
js.report_error(f.error + " opening " + f.name, /* fatal */true);
var section = "robot:filefix";
if(!f.iniGetObject(section)) {
log("Adding [" + section + "] in " + f.name);
f.iniSetValue(section, "SemFile", "../data/tickfix.now");
f.iniSetValue(section, "attr", "0x0020");
}
f.close();
exit(0);
}
function delete_msg(hdr)
{
hdr.attr |= MSG_DELETE;
if(!msgbase.put_msg_header(true, i, hdr))
js.report_error(msgbase.last_error + " deleting msg");
}
function reply_to_msg(re, text)
{
log(LOG_INFO, "Replying to " + re.from_net_addr);
var hdr = {
from: "TickFix",
from_net_type: re.to_net_type,
from_net_addr: re.to_net_addr,
to: re.from,
to_net_type: re.from_net_type,
to_net_addr: re.from_net_addr,
subject: "Area Manager Response",
ftn_reply: re.ftn_msgid,
ftn_pid: "TickFix " + REVISION,
};
text += "\r\n--- " + hdr.ftn_pid;
if(!msgbase.save_msg(hdr, text))
js.report_error(msgbase.last_error + " saving response msg to " + msgbase.file);
log("Response to " + hdr.to_net_addr + " saved successfully");
if(argv.indexOf("-debug") < 0)
delete_msg(re);
}
function handle_command(hdr, cmd)
{
var me = hdr.to_net_addr;
var you = hdr.from_net_addr;
switch(cmd.toLowerCase()) {
case "%help":
return help_text;
case "%list":
return format("%u file areas are available from %s to %s:\r\n\r\n"
,area_list.length, me, you) + area_list.join("\r\n");
case "%query":
var response = [];
for(var a in area_map) {
if(area_map[a].indexOf(you) >= 0)
response.push(a.toUpperCase());
}
return format("%u file areas are connected from %s to %s:\r\n\r\n"
,response.length, me, you) + response.join("\r\n");
case "%unlinked":
var response = [];
for(var a in area_map) {
if(area_map[a].indexOf(you) < 0)
response.push(a.toUpperCase());
}
return format("%u available file areas on %s are not connected to %s:\r\n\r\n"
,response.length, me, you) + response.join("\r\n");
default:
var tag = cmd.toLowerCase();
if(tag[0] == '-') { // Remove
tag = tag.substring(1);
if(tag == "all") {
for(var a in area_map) {
var i = area_map[a].indexOf(you);
if(i >= 0)
area_map[a].splice(i, 1);
}
return you + " is now disconnected from all file areas on " + me;
}
if(!area_map[tag])
return "Unrecognized area tag: " + tag.toUpperCase();
var i = area_map[tag].indexOf(you);
if(i < 0)
return you + " is not connected to area: " + tag.toUpperCase();
area_map[tag].splice(i, 1);
return you + " is now disconnected from area: " + tag.toUpperCase();
}
if(tag[0] == '+')
tag = tag.substring(1);
if(tag == "all") {
for(var a in area_map) {
if(area_map[a].indexOf(you) < 0)
area_map[a].push(you);
}
return you + " is now connected to all available file areas on " + me;
}
if(area_map[tag]) { // Add
if(area_map[tag].indexOf(you) >= 0)
return you + " is already connected to area: " + tag.toUpperCase();
area_map[tag].push(you);
return you + " is now connected to area: " + tag.toUpperCase();
}
return "!Unrecognized area-tag or command on " + me + ": '" + cmd + "'";
}
}
var tickit = new File(file_cfgname(system.ctrl_dir, "tickit.ini"));
if(!tickit.open(tickit.exists ? 'r+':'w+'))
js.report_error(tickit.error + " opening " + tickit.name, /* fatal */true);
var area_list = tickit.iniGetSections();
var area_map = {};
for(var i = 0; i < area_list.length; i++) {
var tag = area_list[i].toLowerCase();
area_map[tag] = tickit.iniGetValue(tag, "links", []);
}
var orig_map = JSON.stringify(area_map);
var f = new File(file_cfgname(system.ctrl_dir, "sbbsecho.ini"));
if(!f.open("r"))
js.report_error(f.error + " opening " + f.name, /* fatal */true);
var node_map = {};
var node_list = f.iniGetAllObjects("addr", "node:", /* lowercase: */true);
for(var i = 0 ; i < node_list.length; i++) {
// Normalize node address (e.g. strip .0 and @domain) here
node_map[fidoaddr.to_str(fidoaddr.parse(node_list[i].addr))] = node_list[i];
}
f.close();
var msgbase = new MsgBase("mail");
if(!msgbase.open())
js.report_error(msgbase.last_error + " opening " + msgbase.file, /* fatal */true);
var idx_list = msgbase.get_index();
for(var i = 0; i < idx_list.length; i++) {
var idx = idx_list[i];
if((idx.attr&MSG_DELETE) || idx.to != 0)
continue;
var hdr = msgbase.get_msg_header(true, i, /* expand_fields: */false);
if(AreaMgr.indexOf[hdr.to.toLowerCase()] < 0)
continue;
if(hdr.from_net_type != NET_FIDO || hdr.to_net_type != NET_FIDO)
continue;
if(system.fido_addr_list.indexOf(hdr.to_net_addr) < 0)
continue;
if(!node_map[hdr.from_net_addr]) {
reply_to_msg(hdr, "Unknown node address: " + hdr.from_net_addr);
continue;
}
if(!node_map[hdr.from_net_addr].areafix) {
reply_to_msg(hdr, "AreaFix not enabled for node: " + hdr.from_net_addr);
continue;
}
if(!node_map[hdr.from_net_addr].areafixpwd) {
reply_to_msg(hdr, "No AreaFix password for node: " + hdr.from_net_addr);
continue;
}
if(hdr.subject.toLowerCase() != String(node_map[hdr.from_net_addr].areafixpwd).toLowerCase()) {
reply_to_msg(hdr, "Wrong AreaFix password for node: " + hdr.from_net_addr);
continue;
}
log("Successful authentication of: " + hdr.from_net_addr);
var text = msgbase.get_msg_body(true, i
, /* strip ctrl-a */true
, /* dot-stuffing */false
, /* include-tails */false);
if(!text) {
reply_to_msg(hdr, "No body text for Area Management Request from node: " + hdr.from_net_addr);
continue;
}
var response = [];
var line = text.split('\r\n');
for(var l = 0; l < line.length; l++) {
if(line[l])
response.push(handle_command(hdr, line[l]));
}
reply_to_msg(hdr, response.join('\r\n'));
}
msgbase.close();
if(JSON.stringify(area_map) != orig_map) {
log(LOG_INFO, tickit.name + " modified");
file_backup(tickit.name, backup_level);
for(var i = 0; i < area_list.length; i++) {
var tag = area_list[i].toLowerCase();
tickit.iniSetValue(tag, "links", area_map[tag]);
}
}
tickit.close();