+<!DOCTYPE html>
+<html>
+ <head>
+ <script>
+function hex(str) {
+ var res = "";
+ var achar = 'a'.charCodeAt(0)
+ for (var i=0; i<str.length; i++) {
+ var c = str.charCodeAt(i)
+ res += String.fromCharCode((c>>4)+achar) + String.fromCharCode((c&0xf)+achar)
+ }
+ return res
+}
+
+function dehex(str) {
+ var res = ""
+ var achar = 'a'.charCodeAt(0)
+ for (var i=0; i<str.length; i+=2) {
+ var c1 = str.charCodeAt(i)-achar, c2 = str.charCodeAt(i+1)-achar
+ res += String.fromCharCode((c1<<4)+c2)
+ }
+ return res
+}
+
+function cgireq(script, qs, body, cb) {
+ var req = new XMLHttpRequest()
+ req.open((body!=null)?'POST':'GET', '/cgi-bin/cwebfiles/'+script+(qs?'?'+qs:''), !!cb)
+ if (cb) {
+ req.onloadend = function() {
+ cb(req.status, req.responseText)
+ }
+ }
+ req.send(body)
+ if (!cb) {
+ return {status: req.status, data: req.responseText}
+ }
+}
+
+function login(user, pass, cb) {
+ return cgireq('login', null, user+':'+pass, cb)
+}
+
+function listdir(path, cb) {
+ cgireq('listdir', hex(path), null, function(status, data) {
+ if (status != 200) return cb(status, data, null)
+ var listing = data.split('\n').filter(function(line) {
+ return line.trim().length > 0
+ }).map(function(line) {
+ var parts = line.split(' ')
+ return {name: dehex(parts[0]), size: parseInt(parts[1]), mode: parseInt(parts[2])}
+ })
+ cb(status, data, listing)
+ })
+}
+
+var S_IFMT = parseInt("0170000",8) // bit mask for the file type bit fields
+var S_IFSOCK = parseInt("0140000",8) // socket
+var S_IFLNK = parseInt("0120000",8) // symbolic link
+var S_IFREG = parseInt("0100000",8) // regular file
+var S_IFBLK = parseInt("0060000",8) // block device
+var S_IFDIR = parseInt("0040000",8) // directory
+var S_IFCHR = parseInt("0020000",8) // character device
+var S_IFIFO = parseInt("0010000",8) // FIFO
+
+var S_ISUID = parseInt("0004000",8) // set UID bit
+var S_ISGID = parseInt("0002000",8) // set-group-ID bit (see below)
+var S_ISVTX = parseInt("0001000",8) // sticky bit (see below)
+
+var S_IRWXU = parseInt("00700",8) // mask for file owner permissions
+var S_IRUSR = parseInt("00400",8) // owner has read permission
+var S_IWUSR = parseInt("00200",8) // owner has write permission
+var S_IXUSR = parseInt("00100",8) // owner has execute permission
+var S_IRWXG = parseInt("00070",8) // mask for group permissions
+var S_IRGRP = parseInt("00040",8) // group has read permission
+var S_IWGRP = parseInt("00020",8) // group has write permission
+var S_IXGRP = parseInt("00010",8) // group has execute permission
+var S_IRWXO = parseInt("00007",8) // mask for permissions for others (not in group)
+var S_IROTH = parseInt("00004",8) // others have read permission
+var S_IWOTH = parseInt("00002",8) // others have write permission
+var S_IXOTH = parseInt("00001",8) // others have execute permission
+
+
+var typechars = {}
+typechars[S_IFSOCK] = 's'
+typechars[S_IFLNK] = 'l'
+typechars[S_IFREG] = '-'
+typechars[S_IFBLK] = 'b'
+typechars[S_IFDIR] = 'd'
+typechars[S_IFCHR] = 'c'
+typechars[S_IFIFO] = 'p'
+
+function escapehtml(text) {
+ return text.replace(/&/g, '&')
+ .replace(/</g, '<')
+ .replace(/>/g, '>')
+ .replace(/"/g, '"')
+}
+
+var loc = '/'
+
+function dive(hexedname) { __listdir(loc + dehex(hexedname) + '/'); }
+
+function format_entry(entry) {
+ var modeline = ""
+ modeline += typechars[entry.mode&S_IFMT]
+ modeline += (entry.mode&S_IRUSR) ? 'r' : '-'
+ modeline += (entry.mode&S_IWUSR) ? 'w' : '-'
+ modeline += (entry.mode&S_ISUID) ? ((entry.mode&S_IXUSR) ? 's' : 'S') : ((entry.mode&S_IXUSR) ? 'x' : '-')
+ modeline += (entry.mode&S_IRGRP) ? 'r' : '-'
+ modeline += (entry.mode&S_IWGRP) ? 'w' : '-'
+ modeline += (entry.mode&S_ISGID) ? ((entry.mode&S_IXGRP) ? 's' : 'S') : ((entry.mode&S_IXGRP) ? 'x' : '-')
+ modeline += (entry.mode&S_IROTH) ? 'r' : '-'
+ modeline += (entry.mode&S_IWOTH) ? 'w' : '-'
+ modeline += (entry.mode&S_ISVTX) ? ((entry.mode&S_IXOTH) ? 's' : 'S') : ((entry.mode&S_IXOTH) ? 'x' : '-')
+
+ var markred = (entry.mode&(S_ISUID|S_ISGID))
+ if (markred) {
+ modeline = '<span style="color: red">'+modeline+'</span>'
+ }
+
+ var showsize = (entry.mode&S_IFMT) == S_IFREG
+
+ var escaped_name = escapehtml(entry.name)
+
+ var clickcode = ''
+ if ((entry.mode&S_IFMT) == S_IFDIR) clickcode = "dive('"+hex(entry.name)+"')"
+
+ return '<tr><td>'+modeline+'</td><td '+(clickcode?'class="clickme" ':'')+'onclick="'+clickcode+'">'+escaped_name+'</td><td>'+(showsize?entry.size:'')+'</td></tr>'
+}
+
+function renderloc(path) {
+ var parts = path.split('/')
+ var curpath = '/';
+ var linkparts = parts.map(function(dir, i) {
+ var esc = (i==0) ? 'main directory' : escapehtml(dir)
+ if (i != 0) curpath += dir + '/'
+ var hexpath = hex(curpath)
+ var clickcode = "__listdir(dehex('"+hexpath+"'))"
+ return '<span class="clickme" onclick="'+clickcode+'">'+esc+'</span>'
+ })
+ return '<div id="loc">'+linkparts.join(' / ')+'</div>'
+}
+
+function __listdir(path) {
+ listdir(path, function(status, err, listing) {
+ if (status != 200) {
+ alert(err)
+ return
+ }
+ loc = path
+ var body = document.getElementsByTagName('body')[0]
+ body.innerHTML = renderloc(path)
+ + '<table><thead><tr><th>mode</th><th>name</th><th>size</th></tr></thead><tbody>'+listing.filter(function(e) {
+ return e.name != '.' && e.name != '..'
+ }).map(function(e) {
+ return format_entry(e)
+ }).join('')+'</tbody></table>'
+ /*listing.forEach(function(e) {
+ //e.mode = e.mode.toString(8)
+ console.log(Object.keys(e).map(function(k) {
+ return k+':'+e[k]
+ }).join(', '))
+ })*/
+ })
+}
+
+
+ </script>
+ <style>
+ .clickme {
+ text-decoration: underline;
+ color: blue;
+ cursor: pointer;
+ }
+ </style>
+ </head>
+ <body onload="__listdir('/')">
+ </body>
+</html>