Synchronet now requires the libarchive development package (e.g. libarchive-dev on Debian-based Linux distros, libarchive.org for more info) to build successfully.

Commits (4)
// Delete files from Synchronet v3.19 file bases
require("sbbsdefs.js", "DIR_SINCEDL");
"use strict";
var dir_list = [];
var exclude = [];
var options = {};
for(var i = 0; i < argc; i++) {
var arg = argv[i];
if(arg[0] == '-') {
var opt = arg;
while(opt[0] == '-')
opt = opt.slice(1);
if(opt == "help" || opt == "?") {
writeln("usage: [-options] [[dir_code] [...]]");
writeln("options:");
writeln(" -lib=<name> search for duplicates in specified library only");
writeln(" -ex=<filename> add to excluded file name list (case-insensitive)");
writeln(" -offline remove files that are offline (don't exist on disk)");
writeln(" -test don't actually remove files, just report findings");
exit(0);
}
if(opt.indexOf("ex=") == 0) {
exclude.push(opt.slice(3).toUpperCase());
continue;
}
if(opt.indexOf("lib=") == 0) {
var lib = opt.slice(4);
if(!file_area.lib[lib]) {
alert("Library not found: " + lib);
exit(1);
}
for(var j = 0; j < file_area.lib[lib].dir_list.length; j++)
dir_list.push(file_area.lib[lib].dir_list[j].code);
continue;
}
options[opt] = true;
continue;
}
dir_list.push(arg);
}
if(dir_list.length < 1)
for(var dir in file_area.dir)
dir_list.push(dir);
var now = time();
for(var i in dir_list) {
var dir_code = dir_list[i];
var dir = file_area.dir[dir_code];
var base = new FileBase(dir_code);
if(!base.open())
throw new Error(base.last_error);
if(options.offline) {
log("Purging offline files");
var list = base.get_names(/* sort: */false);
var removed = 0;
for(var j = 0; j < list.length; j++) {
var file = list[j];
if(exclude.indexOf(file.toUpperCase()) >= 0)
continue;
if(base.get_size(file) < 0) {
log("Removing offline file: " + base.get_path(file));
if(options.test)
removed++;
else {
if(!base.remove(file, /* delete: */true))
alert(base.error);
else
removed++;
}
}
}
log("Removed " + removed + " offline files");
}
if(base.max_age) {
log("Purging old files, imposing max age of " + base.max_age + " days");
var list = base.get_list(FileBase.DETAIL.NORM, /* sort: */false);
var removed = 0;
for(var j = 0; j < list.length; j++) {
var file = list[j];
if(exclude.indexOf(file.name.toUpperCase()) >= 0)
continue;
var t = file.added;
var age_desc = "uploaded";
if(file.last_downloaded
&& (file_area.dir[dir_code].settings & DIR_SINCEDL)) {
t = file.last_downloaded;
age_desc = "last downloaded";
}
var file_age = Math.floor((now - t) / (24 * 60 * 60));
if(file_age > base.max_age) {
log("Removing " + base.get_path(file.name) + " " + age_desc + " " + file_age + " days ago");
if(options.test)
removed++;
else {
if(!base.remove(file.name, /* delete: */true))
alert(base.error);
else
removed++;
}
}
}
log("Removed " + removed + " of " + list.length + " files due to age of " + base.max_age + " days");
}
if(base.max_files) {
log("Purging excess files, imposing max files limit of " + base.max_files);
var list = base.get_list(FileBase.DETAIL.MIN, /* sort: */false);
var removed = 0;
var excess = list.length - base.max_files;
for(var j = 0; j < list.length && removed < excess; j++) {
var file = list[j];
if(exclude.indexOf(file.name.toUpperCase()) >= 0)
continue;
log("Removing " + file.name);
if(options.test)
removed++;
else {
if(!base.remove(file.name, /* delete: */true))
alert(base.error);
else
removed++;
}
}
log("Removed " + removed + " of " + list.length + " files due to max file limit of " + base.max_files);
}
base.close();
}
......@@ -7,10 +7,11 @@ load("file_size.js");
"use strict";
var detail = 0;
var detail = FileBase.DETAIL.NORM;
var min_size = 1024;
var dir_list = [];
var exclude = [];
var json_space = 4;
var hash_type;
var options = {};
for(var i = 0; i < argc; i++) {
......@@ -29,8 +30,9 @@ for(var i = 0; i < argc; i++) {
writeln(" -sha1 search for duplicate SHA-1 sums (the default)");
writeln(" -crc32 search for duplicate CRC-32 sums");
writeln(" -md5 search for duplicate MD5 sums");
writeln(" -json use JSON formatted output");
writeln(" -v increase verbosity of output");
writeln(" -json[=space] create JSON formatted output");
writeln(" -v increase verbosity of JSON output");
writeln(" -dedupe remove/delete duplicate files");
exit(0);
}
if(opt.indexOf("ex=") == 0) {
......@@ -57,6 +59,11 @@ for(var i = 0; i < argc; i++) {
detail++;
continue;
}
if(opt.indexOf("json=") == 0) {
options.json = true;
json_space = parseInt(opt.slice(5), 10);
continue;
}
switch(opt) {
case 'crc16':
case 'crc32':
......@@ -93,6 +100,7 @@ for(var i in dir_list) {
if(exclude.indexOf(file.name.toUpperCase()) >= 0)
continue;
file.dir = dir_code;
file.path = base.get_path(file.name);
if(options.names) {
var fname = file.name.toLowerCase();
if(!name[fname])
......@@ -135,16 +143,20 @@ for(var n in hash) {
if(options.names) {
log(dupe.name.length + " duplicate file names (" + file_size_str(name_bytes,1 , 1) + " bytes)");
if(options.json)
writeln(JSON.stringify(dupe.name, null, 4));
if(options.dedupe)
writeln(remove_list(dupe.name, "name") + " files removed");
else if(options.json)
writeln(JSON.stringify(dupe.name, null, json_space));
else
print_list(dupe.name, "name");
}
if(hash_type) {
log(dupe.hash.length + " duplicate file " + hash_type.toUpperCase() + " sums of at least "
+ min_size + " bytes (" + file_size_str(hash_bytes, 1, 1) + " bytes)");
if(options.json)
writeln(JSON.stringify(dupe.hash, null, 4));
if(options.dedupe)
writeln(remove_list(dupe.hash, hash_type) + " files removed");
else if(options.json)
writeln(JSON.stringify(dupe.hash, null, json_space));
else
print_list(dupe.hash, hash_type);
}
......@@ -152,10 +164,40 @@ if(hash_type) {
function print_list(list, key)
{
for(var i = 0; i < list.length; i++) {
writeln("Duplicate file " + key + " #" + (i + 1) + ": " + list[i][0][key]);
var value = list[i][0][key];
if(key == 'crc32')
value = format("%08X", value);
writeln("Duplicate file " + key + " #" + (i + 1) + ": " + value);
for(var j = 0; j < list[i].length; j++) {
var f = list[i][j];
writeln(format(" %s%s", file_area.dir[f.dir].path, f.name));
var file = list[i][j];
writeln(" " + file.path);
}
}
}
function remove_list(list, key)
{
var removed = 0;
for(var i = 0; i < list.length; i++) {
var value = list[i][0][key];
if(key == 'crc32')
value = format("%08X", value);
writeln("Duplicates of file " + key + " #" + (i + 1) + ": " + value);
writeln(" Keeping " + list[i][0].path);
for(var j = 1; j < list[i].length; j++) {
var file = list[i][j];
var base = new FileBase(file.dir);
if(!base.open()) {
alert(base.errror);
break;
}
writeln(" Removing " + file.path);
if(base.remove(file.name, /* delete: */true))
removed++;
else
alert(base.error);
base.close();
}
}
return removed;
}
......@@ -333,7 +333,7 @@ var FiDateDled=323;
var FiTimesDled=324;
var FiTransferTime=325;
var FiTags=326;
var Unused327=327;
var FiUploadedTo=327;
var FiChecksum=328;
var HappyBirthday=329;
var TimeToChangePw=330;
......
......@@ -571,53 +571,6 @@ function str_cmds(str)
return;
}
if(str=="HELP") {
writeln("ALTUL [path]");
writeln("\tSets the ALT upload path to <path>. If path is omitted, turns off the");
writeln("\talt upload path.");
}
if(word=="ALTUL") {
str=str.substr(6);
bbs.alt_ul_dir=(str+0);
printf(bbs.text(text.AltULPathIsNow),bbs.alt_ul_dir?bbs.alt_ul_dir:bbs.text(text.OFF));
return;
}
if(str=="HELP") {
writeln("RESORT [ALL|LIB|blank]");
writeln("\tResorts the specified file areas.");
}
if(word=="RESORT") {
for(i=0;i<system.nodes;i++) {
if(i!=bbs.node_num-1) {
if(system.node_list[i].stats==NODE_INUSE
|| system.node_list[i].stats==NODE_QUIET)
break;
}
}
if(i<system.nodes) {
write(bbs.text(text.ResortWarning));
return;
}
if(str.search(/^ALL$/i)!=-1) {
for(i=0;i<file_area.lib_list.length;i++) {
for(j=0;j<file_area.lib_list[i].dir_list.length;j++) {
bbs.resort_dir(file_area.lib_list[i].dir_list[j].number);
}
}
}
else if(str.search(/^LIB$/i)!=-1) {
for(j=0;j<file_area.lib_list[bbs.curlib].dir_list.length;j++) {
bbs.resort_dir(file_area.lib_list[bbs.curlib].dir_list[j].number);
}
}
else {
bbs.resort_dir(undefined);
}
str=str.substr(7);
return;
}
if(str=="HELP") {
writeln("OLDUL [ALL|LIB|blank]");
writeln("\tLists all files uploaded before your last scan time.");
......@@ -625,10 +578,8 @@ function str_cmds(str)
writeln("\tLists all files not downloaded since your last scan time.");
writeln("OFFLINE [ALL|LIB|blank]");
writeln("\tLists all offline files.");
writeln("CLOSE [ALL|LIB|blank]");
writeln("\tLists all files currently open.");
}
if(word=="OLDUL" || word=="OLD" || word=="OFFLINE" || word=="CLOSE") {
if(word=="OLDUL" || word=="OLD" || word=="OFFLINE") {
str=str.replace(/^[A-Z]*\s/,"");
if(file_area.lib_list.length<1)
return;
......@@ -655,10 +606,6 @@ function str_cmds(str)
write("not online...\r\n");
m=FI_OFFLINE;
}
else {
write("currently open...\r\n");
m=FI_CLOSE;
}
k=0;
if(str.toUpperCase()=="ALL") {
for(i=0;i<file_area.lib_list.length;i++) {
......