Refactored CLI #10
							
								
								
									
										166
									
								
								bin/cfb.njs
									
									
									
									
									
								
							
							
								
								
								
								
								
									
									
								
							
						
						
									
										166
									
								
								bin/cfb.njs
									
									
									
									
									
								
							@ -1,166 +0,0 @@
 | 
			
		||||
#!/usr/bin/env node
 | 
			
		||||
/* cfb.js (C) 2013-present  SheetJS -- http://sheetjs.com */
 | 
			
		||||
/* eslint-env node */
 | 
			
		||||
/* vim: set ts=2 ft=javascript: */
 | 
			
		||||
var n = "cfb";
 | 
			
		||||
var X = require('../');
 | 
			
		||||
var fs = require('fs');
 | 
			
		||||
var program = require('commander');
 | 
			
		||||
var PRINTJ = require("printj");
 | 
			
		||||
var sprintf = PRINTJ.sprintf;
 | 
			
		||||
program
 | 
			
		||||
	.version(X.version)
 | 
			
		||||
	.usage('[options] <file> [subfiles...]')
 | 
			
		||||
	.option('-l, --list-files', 'list files')
 | 
			
		||||
	.option('-r, --repair', 'attempt to repair and garbage-collect archive')
 | 
			
		||||
	.option('-c, --create', 'create file')
 | 
			
		||||
	.option('-a, --append', 'add files to CFB (overwrite existing data)')
 | 
			
		||||
	.option('-d, --delete', 'delete files from CFB')
 | 
			
		||||
	.option('-O, --to-stdout', 'extract raw contents to stdout')
 | 
			
		||||
	.option('-z, --dump', 'dump internal representation but do not extract')
 | 
			
		||||
	.option('-q, --quiet', 'process but do not report')
 | 
			
		||||
	.option('--here', 'skip the CFB root name when extracting')
 | 
			
		||||
	.option('--osx', 'use OSX-style unzip listing')
 | 
			
		||||
	.option('--zlib', 'use native zlib')
 | 
			
		||||
	.option('--local', 'print times in local timezone')
 | 
			
		||||
	.option('--dev', 'development mode')
 | 
			
		||||
	.option('--read', 'read but do not print out contents');
 | 
			
		||||
program.parse(process.argv);
 | 
			
		||||
 | 
			
		||||
if(program.zlib) X.utils.use_zlib(require('zlib'));
 | 
			
		||||
 | 
			
		||||
var exit = process.exit;
 | 
			
		||||
var die = function(errno/*:number*/, msg/*:string*/) { console.error(n + ": " + msg); exit(errno); };
 | 
			
		||||
var logit = function(cmd/*:string*/, f/*:string*/) { console.error(sprintf("%-6s %s", cmd, f)); };
 | 
			
		||||
 | 
			
		||||
if(program.args.length === 0) die(1, "must specify a filename");
 | 
			
		||||
 | 
			
		||||
if(program.create) {
 | 
			
		||||
	logit("create", program.args[0]);
 | 
			
		||||
	var newcfb = X.utils.cfb_new();
 | 
			
		||||
	X.writeFile(newcfb, program.args[0]);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
if(!fs.existsSync(program.args[0])) die(1, "must specify a filename");
 | 
			
		||||
 | 
			
		||||
var opts = ({type:'file'}/*:any*/);
 | 
			
		||||
if(program.dev) opts.WTF = true;
 | 
			
		||||
 | 
			
		||||
var cfb = X.read(program.args[0], opts);
 | 
			
		||||
if(program.quiet) exit(0);
 | 
			
		||||
 | 
			
		||||
if(program.dump) {
 | 
			
		||||
	console.log("Full Paths:");
 | 
			
		||||
	console.log(cfb.FullPaths.map(function(x/*:string*/) { return "  " + x; }).join("\n"));
 | 
			
		||||
	console.log("File Index:");
 | 
			
		||||
	console.log(cfb.FileIndex);
 | 
			
		||||
	exit(0);
 | 
			
		||||
}
 | 
			
		||||
if(program.repair) { X.writeFile(cfb, program.args[0]); exit(0); }
 | 
			
		||||
 | 
			
		||||
var rlen = cfb.FullPaths[0].length;
 | 
			
		||||
 | 
			
		||||
function fix_string(x/*:string*/)/*:string*/ { return x.replace(/[\u0000-\u001f]/g, function($$) { return sprintf("\\u%04X", $$.charCodeAt(0)); }); }
 | 
			
		||||
var format_date = function(date/*:Date*/, osx/*:?any*/)/*:string*/ {
 | 
			
		||||
	var datefmt = osx ? "%02u-%02u-%04u %02u:%02u": "%02u-%02u-%02u %02u:%02u";
 | 
			
		||||
	var MM = program.local ? date.getMonth() + 1 : date.getUTCMonth() + 1;
 | 
			
		||||
	var DD = program.local ? date.getDate() : date.getUTCDate();
 | 
			
		||||
	var YY = (program.local ? date.getFullYear() : date.getUTCFullYear())%(osx ? 10000 : 100);
 | 
			
		||||
	var hh = program.local ? date.getHours() : date.getUTCHours();
 | 
			
		||||
	var mm = program.local ? date.getMinutes() : date.getUTCMinutes();
 | 
			
		||||
	return sprintf(datefmt, MM, DD, YY, hh, mm);
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
if(program.listFiles) {
 | 
			
		||||
	var basetime = new Date(Date.UTC(1980,0,1));
 | 
			
		||||
	var cnt = 0, rootsize = 0, filesize = 0;
 | 
			
		||||
	var fmtstr = "%9lu  %s   %s";
 | 
			
		||||
	if(program.osx) {
 | 
			
		||||
		console.log("Archive:  " + program.args[0]);
 | 
			
		||||
		console.log("  Length      Date    Time    Name");
 | 
			
		||||
		console.log("---------  ---------- -----   ----");
 | 
			
		||||
		fmtstr = "%9lu  %s   %s";
 | 
			
		||||
	} else {
 | 
			
		||||
		console.log("  Length     Date   Time    Name");
 | 
			
		||||
		console.log(" --------    ----   ----    ----");
 | 
			
		||||
	}
 | 
			
		||||
	cfb.FileIndex.forEach(function(file/*:CFBEntry*/, i/*:number*/) {
 | 
			
		||||
		switch(file.type) {
 | 
			
		||||
			case 5:
 | 
			
		||||
				basetime = file.ct || file.mt || basetime;
 | 
			
		||||
				rootsize = file.size;
 | 
			
		||||
				break;
 | 
			
		||||
			case 2:
 | 
			
		||||
				var fixname = fix_string(cfb.FullPaths[i]);
 | 
			
		||||
				if(program.osx && fixname.match(/\\u0001Sh33tJ5/)) return;
 | 
			
		||||
				if(program.here) fixname = fixname.slice(rlen);
 | 
			
		||||
				console.log(sprintf(fmtstr, file.size, format_date(file.mt || basetime, program.osx), fixname));
 | 
			
		||||
				filesize += file.size;
 | 
			
		||||
				++cnt;
 | 
			
		||||
		}
 | 
			
		||||
	});
 | 
			
		||||
	var outfmt = "%9lu                   %lu file%s";
 | 
			
		||||
	if(program.osx) {
 | 
			
		||||
		console.log("---------                     -------");
 | 
			
		||||
		outfmt = "%9lu                     %lu file%s";
 | 
			
		||||
	} else {
 | 
			
		||||
		console.log(" --------                   -------");
 | 
			
		||||
	}
 | 
			
		||||
	console.log(sprintf(outfmt, rootsize || filesize, cnt, (cnt !== 1 ? "s" : "")));
 | 
			
		||||
 | 
			
		||||
	exit(0);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function mkdirp(path/*:string*/) { path.split("/").reduce(function(acc/*:string*/, p/*:string*/) {
 | 
			
		||||
	acc += p + "/";
 | 
			
		||||
	if(!fs.existsSync(acc)) { logit("mkdir", acc); fs.mkdirSync(acc); }
 | 
			
		||||
	return acc;
 | 
			
		||||
}, ""); }
 | 
			
		||||
 | 
			
		||||
function write(path/*:string*/, data/*:CFBEntry*/) {
 | 
			
		||||
	logit("write", fix_string(path));
 | 
			
		||||
	fs.writeFileSync(path, /*::new Buffer((*/data.content/*:: :any))*/ || new Buffer(0));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
if(program.create || program.append) {
 | 
			
		||||
	program.args.slice(1).forEach(function(x/*:string*/) {
 | 
			
		||||
		logit("append", x);
 | 
			
		||||
		X.utils.cfb_add(cfb, "/" + x, fs.readFileSync(x));
 | 
			
		||||
	});
 | 
			
		||||
	X.writeFile(cfb, program.args[0]);
 | 
			
		||||
	exit(0);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
if(program.delete) {
 | 
			
		||||
	program.args.slice(1).forEach(function(x/*:string*/) {
 | 
			
		||||
		logit("delete", x);
 | 
			
		||||
		X.utils.cfb_del(cfb, "/" + x);
 | 
			
		||||
	});
 | 
			
		||||
	X.writeFile(cfb, program.args[0]);
 | 
			
		||||
	exit(0);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
if(program.args.length > 1) {
 | 
			
		||||
	program.args.slice(1).forEach(function(x/*:string*/) {
 | 
			
		||||
		var data/*:?CFBEntry*/ = X.find(cfb, x.replace(/\\u000\d/g,"!"));
 | 
			
		||||
		if(!data) { console.error(x + ": file not found"); return; }
 | 
			
		||||
		if(data.type !== 2) { console.error(x + ": not a file"); return; }
 | 
			
		||||
		var idx = cfb.FileIndex.indexOf(data), path = cfb.FullPaths[idx];
 | 
			
		||||
		if(program.toStdout) return process.stdout.write(/*::new Buffer((*/data.content/*:: :any))*/);
 | 
			
		||||
		mkdirp(path.slice(0, path.lastIndexOf("/")));
 | 
			
		||||
		write(path, data);
 | 
			
		||||
	});
 | 
			
		||||
	exit(0);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
if(program.toStdout) exit(0);
 | 
			
		||||
for(var i=program.here ? 1 : 0; i!==cfb.FullPaths.length; ++i) {
 | 
			
		||||
	if(!cfb.FileIndex[i].name) continue;
 | 
			
		||||
	var fp = cfb.FullPaths[i];
 | 
			
		||||
	if(program.here) fp = fp.slice(rlen);
 | 
			
		||||
	if(fp.slice(-1) === "/") mkdirp(fp);
 | 
			
		||||
	else {
 | 
			
		||||
		if(fp.indexOf("/") > -1) mkdirp(fp.slice(0, fp.lastIndexOf("/")));
 | 
			
		||||
		write(fp, cfb.FileIndex[i]);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										7
									
								
								packages/cfb-cli/bin/cfb.njs
									
									
									
									
									
										Executable file
									
								
							
							
								
								
								
								
								
									
									
								
							
						
						
									
										7
									
								
								packages/cfb-cli/bin/cfb.njs
									
									
									
									
									
										Executable file
									
								
							@ -0,0 +1,7 @@
 | 
			
		||||
#!/usr/bin/env node
 | 
			
		||||
/* cfb.js (C) 2013-present  SheetJS -- http://sheetjs.com */
 | 
			
		||||
/* eslint-env node */
 | 
			
		||||
/* vim: set ts=2 ft=javascript: */
 | 
			
		||||
var cli = require('../');
 | 
			
		||||
 | 
			
		||||
cli();
 | 
			
		||||
							
								
								
									
										173
									
								
								packages/cfb-cli/index.js
									
									
									
									
									
										Normal file
									
								
							
							
								
								
								
								
								
									
									
								
							
						
						
									
										173
									
								
								packages/cfb-cli/index.js
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,173 @@
 | 
			
		||||
#!/usr/bin/env node
 | 
			
		||||
/* index.js (C) 2020-present  SheetJS -- http://sheetjs.com */
 | 
			
		||||
/* eslint-env node */
 | 
			
		||||
/* vim: set ts=2 ft=javascript: */
 | 
			
		||||
 | 
			
		||||
var n = "cfb-cli";
 | 
			
		||||
var X = require('cfb');
 | 
			
		||||
var fs = require('fs');
 | 
			
		||||
var program = require('commander');
 | 
			
		||||
var PRINTJ = require("printj");
 | 
			
		||||
function run() {
 | 
			
		||||
  var sprintf = PRINTJ.sprintf;
 | 
			
		||||
  program
 | 
			
		||||
    .version(X.version)
 | 
			
		||||
    .usage('[options] <file> [subfiles...]')
 | 
			
		||||
    .option('-l, --list-files', 'list files')
 | 
			
		||||
    .option('-r, --repair', 'attempt to repair and garbage-collect archive')
 | 
			
		||||
    .option('-c, --create', 'create file')
 | 
			
		||||
    .option('-a, --append', 'add files to CFB (overwrite existing data)')
 | 
			
		||||
    .option('-d, --delete', 'delete files from CFB')
 | 
			
		||||
    .option('-O, --to-stdout', 'extract raw contents to stdout')
 | 
			
		||||
    .option('-z, --dump', 'dump internal representation but do not extract')
 | 
			
		||||
    .option('-q, --quiet', 'process but do not report')
 | 
			
		||||
    .option('--here', 'skip the CFB root name when extracting')
 | 
			
		||||
    .option('--osx', 'use OSX-style unzip listing')
 | 
			
		||||
    .option('--zlib', 'use native zlib')
 | 
			
		||||
    .option('--local', 'print times in local timezone')
 | 
			
		||||
    .option('--dev', 'development mode')
 | 
			
		||||
    .option('--read', 'read but do not print out contents');
 | 
			
		||||
  program.parse(process.argv);
 | 
			
		||||
 | 
			
		||||
  if (program.zlib) X.utils.use_zlib(require('zlib'));
 | 
			
		||||
 | 
			
		||||
  var exit = process.exit;
 | 
			
		||||
  var die = function (errno/*:number*/, msg/*:string*/) { console.error(n + ": " + msg); exit(errno); };
 | 
			
		||||
  var logit = function (cmd/*:string*/, f/*:string*/) { console.error(sprintf("%-6s %s", cmd, f)); };
 | 
			
		||||
 | 
			
		||||
  if (program.args.length === 0) die(1, "must specify a filename");
 | 
			
		||||
 | 
			
		||||
  if (program.create) {
 | 
			
		||||
    logit("create", program.args[0]);
 | 
			
		||||
    var newcfb = X.utils.cfb_new();
 | 
			
		||||
    X.writeFile(newcfb, program.args[0]);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (!fs.existsSync(program.args[0])) die(1, "must specify a filename");
 | 
			
		||||
 | 
			
		||||
  var opts = ({ type: 'file' }/*:any*/);
 | 
			
		||||
  if (program.dev) opts.WTF = true;
 | 
			
		||||
 | 
			
		||||
  var cfb = X.read(program.args[0], opts);
 | 
			
		||||
  if (program.quiet) exit(0);
 | 
			
		||||
 | 
			
		||||
  if (program.dump) {
 | 
			
		||||
    console.log("Full Paths:");
 | 
			
		||||
    console.log(cfb.FullPaths.map(function (x/*:string*/) { return "  " + x; }).join("\n"));
 | 
			
		||||
    console.log("File Index:");
 | 
			
		||||
    console.log(cfb.FileIndex);
 | 
			
		||||
    exit(0);
 | 
			
		||||
  }
 | 
			
		||||
  if (program.repair) { X.writeFile(cfb, program.args[0]); exit(0); }
 | 
			
		||||
 | 
			
		||||
  var rlen = cfb.FullPaths[0].length;
 | 
			
		||||
 | 
			
		||||
  function fix_string(x/*:string*/)/*:string*/ { return x.replace(/[\u0000-\u001f]/g, function ($$) { return sprintf("\\u%04X", $$.charCodeAt(0)); }); }
 | 
			
		||||
  var format_date = function (date/*:Date*/, osx/*:?any*/)/*:string*/ {
 | 
			
		||||
    var datefmt = osx ? "%02u-%02u-%04u %02u:%02u" : "%02u-%02u-%02u %02u:%02u";
 | 
			
		||||
    var MM = program.local ? date.getMonth() + 1 : date.getUTCMonth() + 1;
 | 
			
		||||
    var DD = program.local ? date.getDate() : date.getUTCDate();
 | 
			
		||||
    var YY = (program.local ? date.getFullYear() : date.getUTCFullYear()) % (osx ? 10000 : 100);
 | 
			
		||||
    var hh = program.local ? date.getHours() : date.getUTCHours();
 | 
			
		||||
    var mm = program.local ? date.getMinutes() : date.getUTCMinutes();
 | 
			
		||||
    return sprintf(datefmt, MM, DD, YY, hh, mm);
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  if (program.listFiles) {
 | 
			
		||||
    var basetime = new Date(Date.UTC(1980, 0, 1));
 | 
			
		||||
    var cnt = 0, rootsize = 0, filesize = 0;
 | 
			
		||||
    var fmtstr = "%9lu  %s   %s";
 | 
			
		||||
    if (program.osx) {
 | 
			
		||||
      console.log("Archive:  " + program.args[0]);
 | 
			
		||||
      console.log("  Length      Date    Time    Name");
 | 
			
		||||
      console.log("---------  ---------- -----   ----");
 | 
			
		||||
      fmtstr = "%9lu  %s   %s";
 | 
			
		||||
    } else {
 | 
			
		||||
      console.log("  Length     Date   Time    Name");
 | 
			
		||||
      console.log(" --------    ----   ----    ----");
 | 
			
		||||
    }
 | 
			
		||||
    cfb.FileIndex.forEach(function (file/*:CFBEntry*/, i/*:number*/) {
 | 
			
		||||
      switch (file.type) {
 | 
			
		||||
        case 5:
 | 
			
		||||
          basetime = file.ct || file.mt || basetime;
 | 
			
		||||
          rootsize = file.size;
 | 
			
		||||
          break;
 | 
			
		||||
        case 2:
 | 
			
		||||
          var fixname = fix_string(cfb.FullPaths[i]);
 | 
			
		||||
          if (program.osx && fixname.match(/\\u0001Sh33tJ5/)) return;
 | 
			
		||||
          if (program.here) fixname = fixname.slice(rlen);
 | 
			
		||||
          console.log(sprintf(fmtstr, file.size, format_date(file.mt || basetime, program.osx), fixname));
 | 
			
		||||
          filesize += file.size;
 | 
			
		||||
          ++cnt;
 | 
			
		||||
      }
 | 
			
		||||
    });
 | 
			
		||||
    var outfmt = "%9lu                   %lu file%s";
 | 
			
		||||
    if (program.osx) {
 | 
			
		||||
      console.log("---------                     -------");
 | 
			
		||||
      outfmt = "%9lu                     %lu file%s";
 | 
			
		||||
    } else {
 | 
			
		||||
      console.log(" --------                   -------");
 | 
			
		||||
    }
 | 
			
		||||
    console.log(sprintf(outfmt, rootsize || filesize, cnt, (cnt !== 1 ? "s" : "")));
 | 
			
		||||
 | 
			
		||||
    exit(0);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  function mkdirp(path/*:string*/) {
 | 
			
		||||
    path.split("/").reduce(function (acc/*:string*/, p/*:string*/) {
 | 
			
		||||
      acc += p + "/";
 | 
			
		||||
      if (!fs.existsSync(acc)) { logit("mkdir", acc); fs.mkdirSync(acc); }
 | 
			
		||||
      return acc;
 | 
			
		||||
    }, "");
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  function write(path/*:string*/, data/*:CFBEntry*/) {
 | 
			
		||||
    logit("write", fix_string(path));
 | 
			
		||||
    fs.writeFileSync(path, /*::new Buffer((*/data.content/*:: :any))*/ || new Buffer(0));
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (program.create || program.append) {
 | 
			
		||||
    program.args.slice(1).forEach(function (x/*:string*/) {
 | 
			
		||||
      logit("append", x);
 | 
			
		||||
      X.utils.cfb_add(cfb, "/" + x, fs.readFileSync(x));
 | 
			
		||||
    });
 | 
			
		||||
    X.writeFile(cfb, program.args[0]);
 | 
			
		||||
    exit(0);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (program.delete) {
 | 
			
		||||
    program.args.slice(1).forEach(function (x/*:string*/) {
 | 
			
		||||
      logit("delete", x);
 | 
			
		||||
      X.utils.cfb_del(cfb, "/" + x);
 | 
			
		||||
    });
 | 
			
		||||
    X.writeFile(cfb, program.args[0]);
 | 
			
		||||
    exit(0);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (program.args.length > 1) {
 | 
			
		||||
    program.args.slice(1).forEach(function (x/*:string*/) {
 | 
			
		||||
      var data/*:?CFBEntry*/ = X.find(cfb, x.replace(/\\u000\d/g, "!"));
 | 
			
		||||
      if (!data) { console.error(x + ": file not found"); return; }
 | 
			
		||||
      if (data.type !== 2) { console.error(x + ": not a file"); return; }
 | 
			
		||||
      var idx = cfb.FileIndex.indexOf(data), path = cfb.FullPaths[idx];
 | 
			
		||||
      if (program.toStdout) return process.stdout.write(/*::new Buffer((*/data.content/*:: :any))*/);
 | 
			
		||||
      mkdirp(path.slice(0, path.lastIndexOf("/")));
 | 
			
		||||
      write(path, data);
 | 
			
		||||
    });
 | 
			
		||||
    exit(0);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (program.toStdout) exit(0);
 | 
			
		||||
  for (var i = program.here ? 1 : 0; i !== cfb.FullPaths.length; ++i) {
 | 
			
		||||
    if (!cfb.FileIndex[i].name) continue;
 | 
			
		||||
    var fp = cfb.FullPaths[i];
 | 
			
		||||
    if (program.here) fp = fp.slice(rlen);
 | 
			
		||||
    if (fp.slice(-1) === "/") mkdirp(fp);
 | 
			
		||||
    else {
 | 
			
		||||
      if (fp.indexOf("/") > -1) mkdirp(fp.slice(0, fp.lastIndexOf("/")));
 | 
			
		||||
      write(fp, cfb.FileIndex[i]);
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
module.exports = run;
 | 
			
		||||
							
								
								
									
										17
									
								
								packages/cfb-cli/package.json
									
									
									
									
									
										Normal file
									
								
							
							
								
								
								
								
								
									
									
								
							
						
						
									
										17
									
								
								packages/cfb-cli/package.json
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,17 @@
 | 
			
		||||
{
 | 
			
		||||
  "name": "cfb-cli",
 | 
			
		||||
  "version": "1.0.0",
 | 
			
		||||
  "description": "Command-line interface for cfb",
 | 
			
		||||
  "bin": {
 | 
			
		||||
    "cfb-cli": "./bin/cfb.njs"
 | 
			
		||||
  },
 | 
			
		||||
  "main": "index.js",
 | 
			
		||||
  "author": "Garrett Luu",
 | 
			
		||||
  "license": "Apache-2.0",
 | 
			
		||||
  "dependencies": {
 | 
			
		||||
    "cfb": "^1.1.4",
 | 
			
		||||
    "commander": "^5.1.0",
 | 
			
		||||
    "fs": "0.0.1-security",
 | 
			
		||||
    "printj": "^1.2.2"
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
		Loading…
	
		Reference in New Issue
	
	Block a user