import java.awt.*; import java.awt.event.*; import java.io.*; import java.applet.*; import java.net.*; import java.util.*; import netscape.javascript.JSObject; // A java filemanager that allows the user to manipulate files on the // Webmin server. Layout is similar to the windows explorer - directory // tree on the left, files on the right, action buttons on the top. public class FileManager extends Applet implements CbButtonCallback, HierarchyCallback, MultiColumnCallback { // top buttons CbButton ret_b, config_b, down_b, edit_b, refresh_b, props_b, copy_b, cut_b, paste_b, delete_b, new_b, upload_b, mkdir_b, makelink_b, rename_b, share_b, mount_b, search_b, acl_b, attr_b, ext_b, preview_b, extract_b, hnew_b; // Directory tree Hierarchy dirs; FileNode root; Hashtable nodemap = new Hashtable(); // File list MultiColumn files; TextField pathname; CbButton history_b; RemoteFile showing_files; RemoteFile showing_list[]; Vector history_list = new Vector(); // Copying and pasting RemoteFile cut_buffer[]; boolean cut_mode; static final String monmap[] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" }; String accroot[]; String accnoroot[]; Hashtable lang = new Hashtable(); Hashtable stab = new Hashtable(), ntab = new Hashtable(); boolean sambamode; int nfsmode; String trust; String extra; String images; int iconsize; boolean got_filesystems, acl_support, attr_support, ext_support; Hashtable mounts = new Hashtable(); Vector fslist = new Vector(); boolean read_only = false; // Standard font for all text Font fixed; // Font for button labels Font small_fixed; // Full session cookie String session; // HTTP referer String referer; // Archive parameter String archive; // Chroot directory for tree String chroot; // File attributes that can be edited boolean can_perms, can_users; // Symlimks are automatically followed boolean follow_links; // Can search file contents boolean search_contents; // Use text editor for HTML boolean force_text; // File extensions to consider as HTML String htmlexts[]; public void init() { setLayout(new BorderLayout()); // Create fonts from specified size fixed = make_font("fixed", 12); small_fixed = make_font("small_fixed", 10); Util.setFont(small_fixed); StringTokenizer tok = new StringTokenizer(getParameter("root"), " "); accroot = new String[tok.countTokens()]; for(int i=0; tok.hasMoreTokens(); i++) accroot[i] = tok.nextToken(); if (getParameter("noroot") != null) { tok = new StringTokenizer(getParameter("noroot"), " "); accnoroot = new String[tok.countTokens()]; for(int i=0; tok.hasMoreTokens(); i++) accnoroot[i] = tok.nextToken(); } else { accnoroot = new String[0]; } trust = getParameter("trust"); session = getParameter("session"); referer = getDocumentBase().toString(); extra = getParameter("extra"); if (extra == null) extra = ""; images = getParameter("images"); if (images == null) images = "images"; iconsize = Integer.parseInt(getParameter("iconsize")); archive = getParameter("doarchive"); if (archive == null) archive = "0"; chroot = getParameter("chroot"); if (chroot == null) chroot = "/"; String can_perms_str = getParameter("canperms"); can_perms = can_perms_str == null || !can_perms_str.equals("0"); String can_users_str = getParameter("canusers"); can_users = can_users_str == null || !can_users_str.equals("0"); String search_contents_str = getParameter("contents"); search_contents = search_contents_str == null || !search_contents_str.equals("0"); String force_text_str = getParameter("force_text"); if (force_text_str != null && force_text_str.equals("1")) force_text = true; String htmlexts_str = getParameter("htmlexts"); if (htmlexts_str == null || htmlexts_str.equals("")) htmlexts_str = ".htm .html"; htmlexts = DFSAdminExport.split(htmlexts_str); // download language strings String l[] = get_text("lang.cgi"); if (l.length < 1 || l[0].indexOf('=') < 0) { String err = "Failed to get language list : "+join_array(l); new ErrorWindow(err); throw new Error(err); } for(int i=0; i= 0) lang.put(l[i].substring(0, eq), l[i].substring(eq+1)); } // list samba file shares String s[] = get_text("list_shares.cgi"); if (s[0].equals("1")) { for(int i=1; i 0 || s == 0 && ss.length > 1) { // At least one non-.. file was selected boolean parentsel = false; for(int i=0; i 4) new ErrorWindow(text("edit_enormal")); else if (is_html_filename(f.path) && !force_text) { // Open HTML editor try { JSObject win = JSObject.getWindow(this); String params[] = { f.path, "" }; win.call("htmledit", params); } catch(Exception e) { new ErrorWindow(text("html_efailed", e.getMessage())); } } else { // Open text editor new EditorWindow(f, this); } } else if (b == down_b) { // Force download of the selected file if (f == null) return; download_file(f); } else if (b == preview_b) { // Open preview window for selected file if (f == null) return; if (f.type == RemoteFile.DIR) new ErrorWindow(text("preview_eimage")); else new PreviewWindow(this, f); } else if (b == refresh_b) { // Refresh the selected directory (and thus any subdirs) if (d == null) return; d.refresh(); show_files(d.file); } else if (b == props_b) { // Display the properties window if (f == null) return; new PropertiesWindow(f, this); } else if (b == acl_b) { // Display the ACL window (if filesystem supports them) if (f == null) return; FileSystem filefs = find_filesys(f); if (filefs == null) return; if (filefs.acls) new ACLWindow(this, f); else new ErrorWindow(text("eacl_efs", filefs.mount)); } else if (b == attr_b) { // Display the attributes window (if filesystem supports them) if (f == null) return; FileSystem filefs = find_filesys(f); if (filefs == null) return; if (filefs.attrs) new AttributesWindow(this, f); else new ErrorWindow(text("attr_efs", filefs.mount)); } else if (b == ext_b) { // Display EXT attributes window (if filesystem supports them) if (f == null) return; FileSystem filefs = find_filesys(f); if (filefs == null) return; if (filefs.ext) new EXTWindow(this, f); else new ErrorWindow(text("ext_efs", filefs.mount)); } else if (b == copy_b) { // Copy the selected files if (f == null) return; cut_buffer = ff; cut_mode = false; } else if (b == cut_b) { // Cut the selected file if (f == null) return; cut_buffer = ff; cut_mode = true; } else if (b == paste_b) { // Paste the copied file if (cut_buffer == null) { new ErrorWindow(text("paste_ecopy")); return; } // Check for existing file clashes // XXX // Go through all the files to paste for(int i=0; i 0) { new HistoryWindow(this); } } } boolean is_html_filename(String path) { for(int i=0; i= rl && p.substring(0, rl).equals(roots[r])) can = true; else if (l < rl && roots[r].substring(0, l).equals(p)) can = true; } return can; } // Download some file to the user's browser, if possible void download_file(RemoteFile f) { if (f.type == RemoteFile.DIR && !archive.equals("0")) new DownloadDirWindow(this, f); else if (f.type == RemoteFile.DIR || f.type > 4) new ErrorWindow(text("view_enormal2")); else open_file_window(f, true, 0); } // Returns the object for some directory, or null if not found. RemoteFile find_directory(String p, boolean fill) { boolean can = under_root_dir(p, accroot) && !under_root_dir(p, accnoroot); if (!can) { new ErrorWindow(text("find_eaccess", p)); return null; } FileNode posnode = root; RemoteFile pos = posnode.file; StringTokenizer tok = new StringTokenizer(p, "/"); while(tok.hasMoreTokens()) { String fn = tok.nextToken(); if (fn.equals("")) continue; RemoteFile fl[] = pos.list(); if (fl == null) return null; if (fill) { posnode.open = true; posnode.fill(); } boolean found = false; for(int i=0; i= l+1 && f.path.substring(0, l+1).equals(fs.mount+"/")) || fs.mount.equals("/")) { filefs = fs; } } return filefs; } public boolean action(Event e, Object o) { if (e.target == pathname) { // A new path was entered.. cd to it String p = pathname.getText().trim(); if (p.equals("")) return true; find_directory(p, true); // Add to the history if (!history_list.contains(p)) { history_list.insertElementAt(p, 0); } return true; } return false; } // singleClick // Called on a single click on a list item public void singleClick(MultiColumn list, int num) { } // doubleClick // Called upon double-clicking on a list item public void doubleClick(MultiColumn list, int num) { if (num == 0) { // Go to parent directory if (showing_files.directory != null) { ((FileNode)nodemap.get(showing_files)).open = false; show_files(showing_files.directory); dirs.select((FileNode)nodemap.get(showing_files)); dirs.redraw(); } return; } RemoteFile d = showing_list[num-1]; if (d.type == 0) { // Open this directory FileNode pn = (FileNode)nodemap.get(showing_files); pn.fill(); pn.open = true; FileNode fn = (FileNode)nodemap.get(d); if (show_files(d)) { fn.fill(); fn.open = true; dirs.select(fn); dirs.redraw(); } } else if (d.type <= 4) { // Direct the browser to this file open_file_window(d, list.last_event.shiftDown(), 0); } } // Called when the user clicks on a column heading so that it can // be sorted. public void headingClicked(MultiColumn list, int col) { if (col == 0) return; // ignore click on icon column? if (col == list.sortcol) { list.sortingArrow(col, list.sortdir == 2 ? 1 : 2); } else { list.sortingArrow(col, 1); } // Re-show the list in the new order, but with the same files selected int ss[] = files.allSelected(); RemoteFile ssf[] = new RemoteFile[ss.length]; for(int i=0; i 0) { new ErrorWindow(text("eopen", l[0])); return; } // Open for real if (download) { getAppletContext().showDocument( new URL(getDocumentBase(), urlstr)); } else { getAppletContext().showDocument( new URL(getDocumentBase(), urlstr), "show"); } } catch(Exception e) { } } static String urlize(String s) { StringBuffer rv = new StringBuffer(); for(int i=0; i= 128) rv.append("%"+Integer.toString(c, 16)); else rv.append(c); } return rv.toString(); } static String un_urlize(String s) { StringBuffer rv = new StringBuffer(); for(int i=0; i= 0) { rv = rv.substring(0, idx)+ ns+rv.substring(idx+os.length()); pos = idx+ns.length()+1; } return rv; } } // A node in the directory tree class FileNode extends HierarchyNode { FileManager parent; RemoteFile file; boolean known; FileNode(RemoteFile file) { this.file = file; parent = file.parent; setimage(); ch = new Vector(); text = file.name; parent.nodemap.put(file, this); } // Create the nodes for subdirectories void fill() { if (!known) { RemoteFile l[] = file.list(); if (l == null) return; ch.removeAllElements(); for(int i=0; i 0) { ch.insertElementAt(n, i); break; } } } void setimage() { im = parent.get_image(file.shared() && file.mounted() ? "smdir.gif" : file.shared() && file.mountpoint() ? "sudir.gif" : file.shared() ? "sdir.gif" : file.mounted() ? "mdir.gif" : file.mountpoint() ? "udir.gif" : "dir.gif"); } // Forces a re-load from the server void refresh() { known = false; file.list = null; fill(); } } class RemoteFile { static final int DIR = 0; static final int TEXT = 1; static final int IMAGE = 2; static final int BINARY = 3; static final int UNKNOWN = 4; static final int SYMLINK = 5; static final int DEVICE = 6; static final int PIPE = 7; static final String[] tmap = { "dir.gif", "text.gif", "image.gif", "binary.gif", "unknown.gif", "symlink.gif", "device.gif", "pipe.gif" }; FileManager parent; String path, name; int type; String user, group; long size; int perms; long modified; String linkto; RemoteFile list[]; RemoteFile directory; // Parse a line of text to a file object RemoteFile(FileManager parent, String line, RemoteFile d) { this.parent = parent; StringTokenizer tok = new StringTokenizer(line, "\t"); if (tok.countTokens() < 7) { String err = "Invalid file line : "+line; new ErrorWindow(err); throw new Error(err); } path = tok.nextToken(); path = parent.replace_str(path, "\\t", "\t"); path = parent.replace_str(path, "\\\\", "\\"); type = Integer.parseInt(tok.nextToken()); user = tok.nextToken(); group = tok.nextToken(); size = Long.parseLong(tok.nextToken()); perms = Integer.parseInt(tok.nextToken()); modified = Long.parseLong(tok.nextToken())*1000; if (type == 5) linkto = tok.nextToken(); directory = d; if (path.equals("/")) name = "/"; else name = path.substring(path.lastIndexOf('/')+1); } // Create a new, empty file object RemoteFile() { } // Returns a list of files in this directory RemoteFile[] list() { if (list == null) { String l[] = parent.get_text("list.cgi?dir="+ parent.urlize(path)); if (l[0].length() > 0) { //list = new RemoteFile[0]; // Error reading the remote directory! new ErrorWindow(parent.text("list_edir", path, l[0])); list = null; } else { list = new RemoteFile[l.length-3]; for(int i=3; i 0 && offset == 0) { nlist[i] = f; offset++; } nlist[i+offset] = list[i]; } if (offset == 0) nlist[list.length] = f; list = nlist; } void delete(RemoteFile f) { RemoteFile nlist[] = new RemoteFile[list.length-1]; for(int i=0,j=0; i= 0) { // Length is known buf = new byte[uc.getContentLength()]; int got = 0; while(got < buf.length) got += is.read(buf, got, buf.length-got); } else { // Length is unknown .. read till the end buf = new byte[0]; while(true) { byte data[] = new byte[16384]; int got; try { got = is.read(data); } catch(EOFException ex) { break; } if (got <= 0) break; byte nbuf[] = new byte[buf.length + got]; System.arraycopy(buf, 0, nbuf, 0, buf.length); System.arraycopy(data, 0, nbuf, buf.length, got); buf = nbuf; } } String s = charset == null ? new String(buf, 0) : new String(buf, charset); if (s.indexOf("\r\n") != -1) { dosmode.setState(true); s = FileManager.replace_str(s, "\r\n", "\n"); } edit.setText(s); is.close(); file.size = buf.length; } catch(Exception e) { e.printStackTrace(); } } // Creating a new file EditorWindow(String f, FileManager p) { super(800, 600); filemgr = p; makeUI(true); setTitle(filemgr.text("edit_title2")); name.setText(f.equals("/") ? f : f+"/"); name.select(name.getText().length(), name.getText().length()); } void makeUI(boolean add_name) { setLayout(new BorderLayout()); if (add_name) { Panel np = new Panel(); np.setLayout(new BorderLayout()); np.add("West", new Label(filemgr.text("edit_filename"))); np.add("Center", name = new TextField()); name.setFont(filemgr.fixed); add("North", np); } add("Center", edit = new TextArea(20, 80)); edit.setEditable(true); edit.setFont(filemgr.fixed); Panel bot = new Panel(); bot.setLayout(new FlowLayout(FlowLayout.RIGHT)); bot.add(dosmode = new Checkbox("Windows newlines")); bot.add(goto_b = new CbButton(filemgr.get_image("goto.gif"), filemgr.text("edit_goto"), CbButton.LEFT, this)); bot.add(find_b = new CbButton(filemgr.get_image("find.gif"), filemgr.text("edit_find"), CbButton.LEFT, this)); bot.add(new Label(" ")); bot.add(save_b = new CbButton(filemgr.get_image("save.gif"), filemgr.text("save"), CbButton.LEFT, this)); bot.add(saveclose_b = new CbButton(filemgr.get_image("save.gif"), filemgr.text("edit_saveclose"), CbButton.LEFT, this)); bot.add(cancel_b = new CbButton(filemgr.get_image("cancel.gif"), filemgr.text("close"), CbButton.LEFT, this)); add("South", bot); Util.recursiveBody(this); pack(); show(); } public void click(CbButton b) { if (b == save_b || b == saveclose_b) { RemoteFile par = null, already = null; String save_path; if (file == null) { // Locate the filemgr directory save_path = filemgr.trim_path(name.getText()); int sl = save_path.lastIndexOf('/'); par = filemgr.find_directory( save_path.substring(0, sl), false); if (par == null) return; already = par.find(save_path.substring(sl+1)); if (already != null && (already.type == 0 || already.type == 5)) { new ErrorWindow( filemgr.text("edit_eover", save_path)); return; } } else save_path = file.path; // Save the file back again String s = edit.getText(), line; s = FileManager.replace_str(s, "\r\n", "\n"); try { if (dosmode.getState()) { // Convert to DOS newlines s = FileManager.replace_str(s, "\n", "\r\n"); } else { // Remove any DOS newlines s = FileManager.replace_str(s, "\r\n", "\n"); } URL u = new URL(filemgr.getDocumentBase(), "save.cgi"+filemgr.urlize(save_path)+ "?rand="+System.currentTimeMillis()+ "&trust="+filemgr.trust+ "&length="+s.length()+ filemgr.extra); URLConnection uc = u.openConnection(); uc.setRequestProperty("Content-type", "text/plain"); filemgr.set_cookie(uc); uc.setDoOutput(true); OutputStream os = uc.getOutputStream(); byte buf[]; if (charset == null) { // Assume ascii buf = new byte[s.length()]; s.getBytes(0, buf.length, buf, 0); } else { // Convert back to original charset buf = s.getBytes(charset); } os.write(buf); os.close(); BufferedReader is = new BufferedReader(new InputStreamReader( uc.getInputStream())); String err = is.readLine(); if (err.length() > 0) { new ErrorWindow( filemgr.text("edit_esave", err)); is.close(); return; } line = is.readLine(); is.close(); } catch(Exception e) { e.printStackTrace(); return; } if (file == null) { // Create and insert or replace the file object file = new RemoteFile(filemgr, line, par); if (already != null) { // A file with this name exists already.type = file.type; already.user = file.user; already.group = file.group; already.size = file.size; already.perms = file.perms; already.modified = file.modified; } else { // Add to the list par.add(file); } } else { file.size = s.length(); file.modified = System.currentTimeMillis(); } filemgr.show_files(filemgr.showing_files); if (b == saveclose_b) dispose(); } else if (b == cancel_b) { // Just close dispose(); } else if (b == goto_b) { // Open a dialog asking which line to go to if (goto_window != null) goto_window.toFront(); else goto_window = new GotoWindow(this); } else if (b == find_b) { // Open the search (and replace) dialog if (find_window != null) find_window.toFront(); else find_window = new FindReplaceWindow(this); } } public void dispose() { super.dispose(); if (goto_window != null) goto_window.dispose(); if (find_window != null) find_window.dispose(); } } class GotoWindow extends FixedFrame implements CbButtonCallback { EditorWindow editor; FileManager filemgr; TextField line; CbButton goto_b, cancel_b; GotoWindow(EditorWindow e) { editor = e; filemgr = e.filemgr; setLayout(new BorderLayout()); add("West", new Label(filemgr.text("edit_gotoline"))); add("Center", line = new TextField(10)); line.setFont(filemgr.fixed); Panel bot = new Panel(); bot.setLayout(new FlowLayout(FlowLayout.RIGHT)); bot.add(goto_b = new CbButton(filemgr.get_image("goto.gif"), filemgr.text("edit_goto"), CbButton.LEFT, this)); bot.add(cancel_b = new CbButton(filemgr.get_image("cancel.gif"), filemgr.text("close"), CbButton.LEFT, this)); add("South", bot); Util.recursiveBody(this); pack(); show(); } public void click(CbButton b) { if (b == goto_b) { // Go to the chose line, if it exists int lnum; try { lnum = Integer.parseInt(line.getText()); } catch(Exception e) { return; } String txt = editor.edit.getText(); int c, l = 0; for(c=0; c= 0) { String sel = edittxt.substring(st, en); if (sel.equals(findtxt)) { // Replace the selected editor.edit.setText(edittxt.substring(0, st)+ replace.getText()+ edittxt.substring(en)); editor.edit.select(st, st); return; } } click(find_b); } else if (b == all_b) { // Replace all occurrences of the text in the editor int pos = 0; int len = findtxt.length(); int st = editor.edit.getSelectionStart(), en = editor.edit.getSelectionEnd(); while((pos = edittxt.indexOf(findtxt, pos)) != -1) { edittxt = edittxt.substring(0, pos)+ replace.getText()+ edittxt.substring(pos+len); pos += len; } editor.edit.setText(edittxt); editor.edit.select(st, en); // put back old selection } else if (b == cancel_b) { // Just close the window dispose(); } } public void dispose() { super.dispose(); editor.find_window = null; } } class PropertiesWindow extends FixedFrame implements CbButtonCallback { RemoteFile file; FileManager filemgr; CbButton save_b, cancel_b, size_b; TextField linkto; TextField user, group; Checkbox setuid, setgid; PermissionsPanel user_p, group_p, other_p; Checkbox sticky; Choice rec_mode; TextField octal; TextField bytes, files, dirs; PropertiesWindow(RemoteFile f, FileManager p) { file = f; filemgr = p; // Create UI setTitle(f.path); setLayout(new BorderLayout()); Panel bot = new Panel(); bot.setLayout(new FlowLayout(FlowLayout.RIGHT)); if (file.type == 0) { bot.add(size_b = new CbButton(filemgr.get_image("refresh.gif"), filemgr.text("info_getsize"), CbButton.LEFT, this)); } if (filemgr.can_perms || filemgr.can_users) { bot.add(save_b = new CbButton(filemgr.get_image("save.gif"), filemgr.text("save"), CbButton.LEFT, this)); } bot.add(cancel_b = new CbButton(filemgr.get_image("cancel.gif"), filemgr.text("cancel"), CbButton.LEFT, this)); add("South", bot); Panel mid = new Panel(); mid.setLayout(new BorderLayout()); TabbedPanel tab = null; add("Center", mid); // Create file details section Panel det = new LinedPanel(filemgr.text("info_file")), dl = new Panel(), dr = new Panel(); setup_leftright(det, dl, dr); add_item(filemgr.text("info_path"), new Label(file.path), dl, dr); add_item(filemgr.text("info_type"), new Label(filemgr.text("file_type"+file.type)), dl, dr); add_item(filemgr.text("info_size"), new Label(String.valueOf(file.size)),dl,dr); add_item(filemgr.text("info_mod"), new Label(String.valueOf(new Date(file.modified))), dl, dr); if (file.type == 5) { add_item(filemgr.text("info_link"), linkto = new TextField(file.linkto, 30), dl, dr); linkto.setFont(filemgr.fixed); } mid = add_panel(mid, det); if (filemgr.can_perms) { // Create permissions section Panel per = new LinedPanel(filemgr.text("info_perms")), pl = new Panel(), pr = new Panel(); setup_leftright(per, pl, pr); add_item(filemgr.text("info_user"), user_p = new PermissionsPanel(file, 64, filemgr), pl, pr); add_item(filemgr.text("info_group"), group_p = new PermissionsPanel(file, 8, filemgr), pl, pr); add_item(filemgr.text("info_other"), other_p = new PermissionsPanel(file, 1, filemgr), pl,pr); if (file.type == 0) { add_item(filemgr.text("info_sticky"), sticky = new Checkbox(filemgr.text("info_sticky2")), pl,pr); sticky.setState((file.perms&01000) != 0); } add_item(filemgr.text("info_octal"), octal = new TextField(4), pl, pr); octal.setFont(filemgr.fixed); octal.setEditable(false); mid = add_panel(mid, per); } if (filemgr.can_users) { // Create ownership section Panel own = new LinedPanel(filemgr.text("info_own")), ol = new Panel(), or = new Panel(); setup_leftright(own, ol, or); add_item(filemgr.text("info_user"), user = new TextField(file.user, 10), ol, or); user.setFont(filemgr.fixed); if (file.type != 0) { add_item(filemgr.text("info_setuid"), setuid = new Checkbox(filemgr.text("info_setuid2")), ol, or); setuid.setState((file.perms & 0x800) != 0); } add_item(filemgr.text("info_group"), group = new TextField(file.group, 10), ol, or); group.setFont(filemgr.fixed); if (file.type == 0) add_item(filemgr.text("info_setgid"), setgid = new Checkbox(filemgr.text("info_setgid2")), ol, or); else add_item(filemgr.text("info_setgid"), setgid = new Checkbox(filemgr.text("info_setgid3")), ol, or); setgid.setState((file.perms & 0x400) != 0); mid = add_panel(mid, own); } if (file.type == 0) { // Create directory size section, initially empty Panel szp = new LinedPanel(filemgr.text("info_sizeheader")), sl = new Panel(), sr = new Panel(); setup_leftright(szp, sl, sr); add_item(filemgr.text("info_bytes"), bytes = new TextField("", 10), sl, sr); bytes.setFont(filemgr.fixed); bytes.setEditable(false); add_item(filemgr.text("info_files"), files = new TextField("", 10), sl, sr); files.setFont(filemgr.fixed); files.setEditable(false); add_item(filemgr.text("info_dirs"), dirs = new TextField("", 10), sl, sr); dirs.setFont(filemgr.fixed); dirs.setEditable(false); mid = add_panel(mid, szp); } if (file.type == 0 && (filemgr.can_perms || filemgr.can_users)) { // Create recursion section Panel rec = new LinedPanel(filemgr.text("info_apply")); rec.setLayout(new BorderLayout()); rec_mode = new Choice(); for(int i=1; i<=5; i++) rec_mode.addItem(filemgr.text("info_apply"+i)); rec.add("Center", rec_mode); mid = add_panel(mid, rec); } set_octal(); Util.recursiveBody(this); pack(); show(); } Panel add_panel(Panel p, Component c) { p.add("North", c); Panel np = new Panel(); np.setLayout(new BorderLayout()); p.add("Center", np); return np; } public void click(CbButton b) { if (b == save_b) { // Update the file int perms = get_perms(); String user_str = user != null ? user.getText() : null; String group_str = group != null ? group.getText() : null; int rec = 0; if (file.type == 0 && rec_mode != null) rec = rec_mode.getSelectedIndex(); String rv[] = filemgr.get_text( "chmod.cgi?path="+filemgr.urlize(file.path)+ (perms < 0 ? "" : "&perms="+perms)+ (user_str == null ? "" : "&user="+filemgr.urlize(user_str))+ (group_str == null ? "" : "&group="+filemgr.urlize(group_str))+ "&rec="+rec+ (linkto==null ? "" : "&linkto="+filemgr.urlize(linkto.getText()))); if (rv[0].length() > 0) { // Something went wrong new ErrorWindow(filemgr.text("info_efailed", file.path, rv[0])); } else { // Update all changed file objects if (linkto != null) file.linkto = linkto.getText(); else if (rec == 0) { // This file or directory only update_file(file, perms, false); } else if (rec == 1) { // Update files in this directory update_file(file, perms, false); recurse_files(file, perms, false, false, true); } else if (rec == 2) { // Update files and subdirs update_file(file, perms, false); recurse_files(file, perms, true, true, true); } else if (rec == 3) { // Update files only in dir and subdirs recurse_files(file, perms, true, false, true); } else if (rec == 4) { // Update dir and subdirs but not files recurse_files(file, perms, true, true, false); } // Update directory list int os = filemgr.files.selected(); filemgr.show_files(filemgr.showing_files); filemgr.files.select(os); dispose(); } } else if (b == size_b) { // Get the size of the directory recursively String l[] = filemgr.get_text("size.cgi?dir="+ filemgr.urlize(file.path)); if (l[0].length() > 0) { new ErrorWindow(filemgr.text("info_size", l[0])); } StringTokenizer tok = new StringTokenizer(l[1], " "); String bytes_str = tok.nextToken(); files.setText(tok.nextToken()); dirs.setText(tok.nextToken()); bytes.setText(tok.nextToken()+" "+tok.nextToken()); } else { // Just close dispose(); } } void update_file(RemoteFile f, int perms, boolean perms_only) { f.user = user.getText(); f.group = group.getText(); if (perms_only) f.perms = (perms & 0777) | (f.perms & 037777777000); else f.perms = perms; } void recurse_files(RemoteFile f, int perms, boolean do_subs, boolean do_dirs, boolean do_files) { if (f.list == null) return; for(int i=0; i 1 ? "delete_mtitle" : ff[0].type == 0 ? "delete_dtitle" : "delete_ftitle")); setLayout(new BorderLayout()); if (ff.length > 1) { add("North", new Label(filemgr.text("delete_mdesc"))); Panel mp = new Panel(); mp.setLayout(new GridLayout(ff.length, 1)); for(int i=0; i 0) { new ErrorWindow(filemgr.text("delete_efailed", file.path, rv[0])); break; } else { // done the deed.. update data structures RemoteFile pf = file.directory; pf.delete(file); if (filemgr.showing_files == pf) { // Need to refresh the list as well.. need_reshow = true; } FileNode node = (FileNode)filemgr.nodemap.get( file); FileNode pnode = (FileNode)filemgr.nodemap.get( pf); if (node != null) { // Take the directory out of the tree.. pnode.ch.removeElement(node); need_redraw = true; } } } if (need_reshow) filemgr.show_files(filemgr.showing_files); if (need_redraw) filemgr.dirs.redraw(); dispose(); } else if (b == cancel_b) dispose(); } } class MkdirWindow extends FixedFrame implements CbButtonCallback { FileManager filemgr; TextField dir; CbButton create_b, cancel_b; MkdirWindow(String d, FileManager p) { filemgr = p; setTitle(filemgr.text("mkdir_title")); setLayout(new BorderLayout()); add("West", new Label(filemgr.text("mkdir_dir"))); add("Center", dir = new TextField(d.equals("/") ? "/" : d+"/", 40)); dir.setFont(filemgr.fixed); dir.select(dir.getText().length(), dir.getText().length()); Panel bot = new Panel(); bot.setLayout(new FlowLayout(FlowLayout.CENTER)); bot.add(create_b = new CbButton(filemgr.get_image("save.gif"), filemgr.text("create"), CbButton.LEFT, this)); bot.add(cancel_b = new CbButton(filemgr.get_image("cancel.gif"), filemgr.text("cancel"), CbButton.LEFT, this)); add("South", bot); Util.recursiveBody(this); pack(); show(); } public void click(CbButton b) { if (b == create_b) { // Find the filemgr directory String path = dir.getText(); path = filemgr.trim_path(path); int sl = path.lastIndexOf('/'); RemoteFile par = filemgr.find_directory( path.substring(0, sl), false); if (par.find(path.substring(sl+1)) != null) { new ErrorWindow(filemgr.text("mkdir_eexists", path)); return; } String rv[] = filemgr.get_text("mkdir.cgi?dir="+ filemgr.urlize(path)); if (rv[0].length() > 0) { new ErrorWindow(filemgr.text("mkdir_efailed", rv[0])); return; } RemoteFile file = new RemoteFile(filemgr, rv[1], par); par.add(file); FileNode parnode = (FileNode)filemgr.nodemap.get(par); if (parnode != null) { // Update the tree parnode.add(new FileNode(file)); filemgr.dirs.redraw(); } filemgr.show_files(filemgr.showing_files); dispose(); } else dispose(); } } class LinkWindow extends FixedFrame implements CbButtonCallback { FileManager filemgr; TextField from, to; CbButton create_b, cancel_b; LinkWindow(String d, FileManager p) { filemgr = p; setLayout(new BorderLayout()); setTitle(filemgr.text("link_title")); Panel l = new Panel(), r = new Panel(); l.setLayout(new GridLayout(0, 1)); l.add(new Label(filemgr.text("link_from"))); l.add(new Label(filemgr.text("link_to"))); r.setLayout(new GridLayout(0, 1)); r.add(from = new TextField(d.equals("/") ? "/" : d+"/", 40)); from.setFont(filemgr.fixed); from.select(from.getText().length(), from.getText().length()); r.add(to = new TextField()); to.setFont(filemgr.fixed); add("West", l); add("Center", r); Panel bot = new Panel(); bot.setLayout(new FlowLayout(FlowLayout.CENTER)); bot.add(create_b = new CbButton(filemgr.get_image("save.gif"), filemgr.text("create"), CbButton.LEFT, this)); bot.add(cancel_b = new CbButton(filemgr.get_image("cancel.gif"), filemgr.text("cancel"), CbButton.LEFT, this)); add("South", bot); Util.recursiveBody(this); pack(); show(); } public void click(CbButton b) { if (b == create_b) { // Check inputs String from_str = from.getText().trim(); if (!from_str.startsWith("/")) { new ErrorWindow(filemgr.text("link_efrom", from_str)); return; } int sl = from_str.lastIndexOf('/'); String par_str = from_str.substring(0, sl), file_str = from_str.substring(sl+1); RemoteFile par = filemgr.find_directory(par_str, false); if (par == null) return; if (par.find(file_str) != null) { new ErrorWindow(filemgr.text("link_eexists", from_str)); return; } // Create the actual link String rv[] = filemgr.get_text("makelink.cgi?from="+ filemgr.urlize(from_str)+"&to="+ filemgr.urlize(to.getText())); if (rv[0].length() > 0) { new ErrorWindow(filemgr.text("link_efailed", rv[0])); return; } RemoteFile file = new RemoteFile(filemgr, rv[1], par); par.add(file); filemgr.show_files(filemgr.showing_files); dispose(); } else if (b == cancel_b) dispose(); } } class RenameWindow extends FixedFrame implements CbButtonCallback { FileManager filemgr; RemoteFile file; TextField oldname, newname; CbButton rename_b, cancel_b; RenameWindow(FileManager p, RemoteFile f) { filemgr = p; file = f; setLayout(new BorderLayout()); setTitle(filemgr.text("rename_title", file.path)); Panel l = new Panel(), r = new Panel(); l.setLayout(new GridLayout(0, 1)); l.add(new Label(filemgr.text("rename_old"))); l.add(new Label(filemgr.text("rename_new"))); r.setLayout(new GridLayout(0, 1)); r.add(oldname = new TextField(file.name, 20)); oldname.setEditable(false); oldname.setFont(filemgr.fixed); r.add(newname = new TextField(file.name, 20)); newname.select(file.name.length(), file.name.length()); newname.setFont(filemgr.fixed); add("West", l); add("Center", r); Panel bot = new Panel(); bot.setLayout(new FlowLayout(FlowLayout.CENTER)); bot.add(rename_b = new CbButton(filemgr.get_image("save.gif"), filemgr.text("rename_ok"), CbButton.LEFT, this)); bot.add(cancel_b = new CbButton(filemgr.get_image("cancel.gif"), filemgr.text("cancel"), CbButton.LEFT, this)); add("South", bot); pack(); show(); Util.recursiveBody(this); } public void click(CbButton b) { if (b == rename_b) { // Work out destination file and directory String newstr = newname.getText().trim(); if (newstr.length() == 0) return; RemoteFile destdir; String newpath; if (newstr.indexOf('/') >= 0) { // Different dir if (newstr.startsWith("/")) { // Some absolute path newpath = newstr; } else { // Relative to this dir newpath = file.directory.path+"/"+newstr; } int sl = newpath.lastIndexOf('/'); String newdir = sl == 0 ? "/" : newpath.substring(0,sl); destdir = filemgr.find_directory(newdir, false); } else { // Same dir destdir = file.directory; int sl = file.path.lastIndexOf('/'); newpath = file.path.substring(0, sl)+"/"+newstr; } // Work out filename only int sl = newpath.lastIndexOf('/'); newstr = newpath.substring(sl+1); // Check for an existing file RemoteFile already = destdir.find(newstr); if (already != null) { new ErrorWindow(filemgr.text("rename_eexists", newstr)); return; } // Rename the real file String rv[] = filemgr.get_text( "rename.cgi?old="+filemgr.urlize(file.path)+ "&new="+filemgr.urlize(newpath)); if (rv[0].length() > 0) { new ErrorWindow(filemgr.text("rename_efailed", rv[0])); return; } // Update data structure file.name = newstr; file.path = newpath; file.directory.delete(file); destdir.list(); destdir.add(file); file.directory = destdir; file.list = null; FileNode parnode = (FileNode)filemgr.nodemap.get(file.directory); FileNode filenode = (FileNode)filemgr.nodemap.get(file); if (parnode != null && filenode != null) { // Need to refresh tree filenode.text = file.name; parnode.ch.removeElement(filenode); parnode.add(filenode); dispose(); filemgr.dirs.redraw(); } filemgr.show_files(filemgr.showing_files); dispose(); } else if (b == cancel_b) dispose(); } } class OverwriteWindow extends FixedFrame implements CbButtonCallback { FileManager filemgr; RemoteFile src, already; TextField newname; CbButton ok, cancel; int idx; boolean mode; OverwriteWindow(FileManager p, RemoteFile a, RemoteFile s, int i) { filemgr = p; src = s; already = a; idx = i; mode = filemgr.cut_mode; setLayout(new BorderLayout()); setTitle(filemgr.text("over_title")); add("North", new MultiLabel(filemgr.text("over_msg", already.path), 30, 0)); add("West", new Label(filemgr.text("over_new"))); add("East", newname = new TextField(a.name, 30)); newname.setFont(filemgr.fixed); Panel bot = new Panel(); bot.setLayout(new FlowLayout(FlowLayout.RIGHT)); bot.add(ok = new CbButton(filemgr.get_image("save.gif"), filemgr.text("over_ok"), CbButton.LEFT, this)); bot.add(cancel = new CbButton(filemgr.get_image("cancel.gif"), filemgr.text("cancel"), CbButton.LEFT, this)); add("South", bot); Util.recursiveBody(this); pack(); show(); } public void click(CbButton b) { if (b == cancel) dispose(); else if (b == ok && newname.getText().length() > 0) { // paste the file, but with a new name RemoteFile ap = already.directory; RemoteFile newalready = ap.find(newname.getText()); if (newalready == src) { new ErrorWindow(filemgr.text("paste_eself")); return; } if (newalready != null && (newalready.type == 0 || newalready.type == 5)) { new ErrorWindow( filemgr.text("paste_eover", newalready.path)); return; } String dpath = (ap.path.equals("/") ? "/" : ap.path+"/")+newname.getText(); RemoteFile nf = filemgr.paste_file(src, already.directory, dpath, newalready, mode); if (filemgr.cut_mode && nf != null) { // Paste from the destination path from now on filemgr.cut_buffer[idx] = nf; } dispose(); } } } class SambaShare { String path; boolean available; boolean writable; int guest; String comment; SambaShare(String l) { StringSplitter tok = new StringSplitter(l, ':'); path = tok.nextToken(); available = tok.nextToken().equals("1"); writable = tok.nextToken().equals("1"); guest = Integer.parseInt(tok.nextToken()); comment = tok.nextToken(); } SambaShare(String p, boolean a, boolean w, int g, String c) { path = p; available = a; writable = w; guest = g; comment = c; } String params() { return "path="+FileManager.urlize(path)+ "&available="+(available ? 1 : 0)+ "&writable="+(writable ? 1 : 0)+ "&guest="+guest+ "&comment="+FileManager.urlize(comment); } } class DFSAdminExport { String path; String desc; String ro, rw, root; DFSAdminExport(String l) { StringSplitter tok = new StringSplitter(l, ':'); path = tok.nextToken(); ro = tok.nextToken(); rw = tok.nextToken(); root = tok.nextToken(); desc = tok.nextToken(); } DFSAdminExport(String p, String d, String ro, String rw, String root) { path = p; desc = d; this.ro = ro; this.rw = rw; this.root = root; } static String[] split(String s) { StringTokenizer stok = new StringTokenizer(s, " "); String rv[] = new String[stok.countTokens()]; for(int i=0; i 1)); add_item(name, p, l, r); TextField t = new TextField(v.equals("-") ? "" : v, 25); t.setFont(filemgr.fixed); add_item("", t, l, r); return t; } Choice squashbox(int s) { Choice rv = new Choice(); rv.addItem(filemgr.text("share_s0")); rv.addItem(filemgr.text("share_s1")); rv.addItem(filemgr.text("share_s2")); rv.select(s); return rv; } Choice robox(boolean r) { Choice rv = new Choice(); rv.addItem(filemgr.text("share_lrw")); rv.addItem(filemgr.text("share_lro")); rv.select(r ? 1 : 0); return rv; } Panel opts_panel(Component ro, Component squash) { Panel p = new Panel(); p.setLayout(new BorderLayout()); p.add("West", ro); p.add("East", squash); return p; } void export_options(LinuxExport e) { int c = 0; for(int i=0; i 0) c++; e.host = new String[c]; e.ro = new boolean[c]; e.squash = new int[c]; for(int i=0,j=0; i 0) { e.host[j] = host[i].getText(); e.ro[j] = lro[i].getSelectedIndex() == 1; e.squash[j] = squash[i].getSelectedIndex(); j++; } } } } class SearchWindow extends FixedFrame implements CbButtonCallback,MultiColumnCallback { TabbedPanel tab; MultiColumn list; CbButton search_b, cancel_b, down_b; FileManager filemgr; TextField dir, match, user, group; Checkbox uany, usel, gany, gsel; Choice type; Checkbox sany, smore, sless; TextField more, less; Checkbox xon, xoff; String types[] = { "", "f", "d", "l", "p" }; TextField cont; RemoteFile results[]; SearchWindow(String d, FileManager p) { filemgr = p; setTitle(filemgr.text("search_title")); // setup UI setLayout(new BorderLayout()); tab = new TabbedPanel(); Panel search = new Panel(); search.setLayout(new BorderLayout()); tab.addItem(filemgr.text("search_crit"), search); Panel l = new Panel(), r = new Panel(); l.setLayout(new GridLayout(0, 1)); r.setLayout(new GridLayout(0, 1)); String cols[] = { "", filemgr.text("right_name"), filemgr.text("right_size") }; float widths[] = { .07f, .78f, .15f }; list = new MultiColumn(cols, this); list.setWidths(widths); list.setDrawLines(false); list.setFont(filemgr.fixed); tab.addItem(filemgr.text("search_list"), list); add_item(filemgr.text("search_dir"), dir = new TextField(d, 30), l, r); dir.setFont(filemgr.fixed); // Filename add_item(filemgr.text("search_match"), match = new TextField(20), l, r); match.setFont(filemgr.fixed); if (filemgr.search_contents) { // File contents add_item(filemgr.text("search_cont"), cont = new TextField(30), l, r); cont.setFont(filemgr.fixed); } // User or group owners if (filemgr.can_users) { Panel up = new Panel(); up.setLayout(new FlowLayout(FlowLayout.LEFT, 1, 1)); CheckboxGroup ug = new CheckboxGroup(); up.add(uany = new Checkbox(filemgr.text("search_any"), ug, true)); up.add(usel = new Checkbox("", ug, false)); up.add(user = new TextField(10)); user.setFont(filemgr.fixed); add_item(filemgr.text("search_user"), up, l, r); Panel gp = new Panel(); gp.setLayout(new FlowLayout(FlowLayout.LEFT, 1, 1)); CheckboxGroup gg = new CheckboxGroup(); gp.add(gany = new Checkbox(filemgr.text("search_any"), gg, true)); gp.add(gsel = new Checkbox("", gg, false)); gp.add(group = new TextField(10)); group.setFont(filemgr.fixed); add_item(filemgr.text("search_group"), gp, l, r); } // File type if (!filemgr.follow_links) { type = new Choice(); for(int i=0; i 0) url += "&type="+types[type.getSelectedIndex()]; if (usel != null && usel.getState()) { String u = user.getText().trim(); if (u.length() == 0) { new ErrorWindow(filemgr.text("search_euser")); return; } url += "&user="+filemgr.urlize(u); } if (gsel != null && gsel.getState()) { String g = group.getText().trim(); if (g.length() == 0) { new ErrorWindow(filemgr.text("search_egroup")); return; } url += "&group="+filemgr.urlize(g); } if (smore.getState()) { String m = more.getText().trim(); try { Integer.parseInt(m); } catch(Exception e) { new ErrorWindow(filemgr.text("search_esize")); return; } url += "&size=%2B"+m+"c"; } else if (sless.getState()) { String l = less.getText().trim(); try { Integer.parseInt(l); } catch(Exception e) { new ErrorWindow(filemgr.text("search_esize")); return; } url += "&size=%2D"+l+"c"; } if (xon != null && xon.getState()) url += "&xdev=1"; if (cont != null && cont.getText().trim().length() > 0) url += "&cont="+filemgr.urlize(cont.getText()); // send off the search setCursor(WAIT_CURSOR); String f[] = filemgr.get_text(url); if (f[0].length() > 0) { new ErrorWindow(f[0]); return; } Object rows[][] = new Object[f.length-1][]; results = new RemoteFile[f.length-1]; for(int i=1; i= 0) { ACLEntry e = (ACLEntry)acllist.elementAt(idx); ACLEditor ed = (ACLEditor)edmap.get(e); if (ed == null) edmap.put(e, new ACLEditor(this, e)); else { ed.toFront(); ed.requestFocus(); } } } public void singleClick(MultiColumn list, int num) { } public void headingClicked(MultiColumn list, int col) { } } class AttributesWindow extends FixedFrame implements CbButtonCallback,MultiColumnCallback { FileManager filemgr; RemoteFile file; Vector attrlist = new Vector(); Hashtable edmap = new Hashtable(); CbButton ok, cancel, add; MultiColumn attrtable; AttributesWindow(FileManager p, RemoteFile f) { super(400, 300); setTitle(p.text("attr_title", f.path)); filemgr = p; file = f; // Get the attributes String a[] = filemgr.get_text( "getattrs.cgi?file="+filemgr.urlize(file.path)); if (a[0].length() != 0) { new ErrorWindow(filemgr.text("attr_eattrs", a[0])); return; } // Create the UI setLayout(new BorderLayout()); String titles[] = { filemgr.text("attr_name"), filemgr.text("attr_value") }; attrtable = new MultiColumn(titles, this); for(int i=1; i= 0) { FileAttribute at = (FileAttribute)attrlist.elementAt(idx); AttributeEditor ed = (AttributeEditor)edmap.get(at); if (ed == null) edmap.put(at, new AttributeEditor(this, at)); else { ed.toFront(); ed.requestFocus(); } } } public void singleClick(MultiColumn list, int num) { } public void headingClicked(MultiColumn list, int col) { } } class FileAttribute { String name; String value; FileAttribute(String l, FileManager f) { int eq = l.indexOf('='); name = f.un_urlize(l.substring(0, eq)); value = f.un_urlize(l.substring(eq+1)); } FileAttribute(String n, String v) { name = n; value = v; } String[] getRow() { return new String[] { name, value }; } } class AttributeEditor extends FixedFrame implements CbButtonCallback { FileManager filemgr; AttributesWindow attrwin; FileAttribute attr; boolean creating; CbButton ok, del; TextField name; TextArea value; AttributeEditor(AttributesWindow w, FileAttribute a) { attrwin = w; attr = a; filemgr = w.filemgr; creating = false; makeUI(); } AttributeEditor(AttributesWindow w) { attrwin = w; attr = new FileAttribute("", ""); filemgr = w.filemgr; creating = true; makeUI(); } void makeUI() { setTitle(filemgr.text(creating ? "attr_create" : "attr_edit")); setLayout(new BorderLayout()); Panel top = new Panel(); top.setLayout(new GridLayout(1, 2)); top.add(new Label(filemgr.text("attr_name"))); top.add(name = new TextField(attr.name, 20)); name.setFont(filemgr.fixed); add("North", top); Panel mid = new Panel(); mid.setLayout(new GridLayout(1, 2)); mid.add(new Label(filemgr.text("attr_value"))); mid.add(value = new TextArea(attr.value, 5, 20)); add("Center", mid); Panel bot = new Panel(); bot.setLayout(new FlowLayout(FlowLayout.RIGHT)); bot.add(ok = new CbButton(filemgr.get_image("save.gif"), filemgr.text("save"), CbButton.LEFT, this)); if (!creating) bot.add(del = new CbButton(filemgr.get_image("cancel.gif"), filemgr.text("delete"), CbButton.LEFT, this)); add("South", bot); Util.recursiveBody(this); pack(); show(); } public void click(CbButton b) { if (b == ok) { // Update or add the attribute if (name.getText().length() == 0) { new ErrorWindow(filemgr.text("attr_ename")); return; } attr.name = name.getText(); attr.value = value.getText(); if (creating) { // Add to the attribs table attrwin.attrlist.addElement(attr); attrwin.attrtable.addItem(attr.getRow()); } else { // Update the table int idx = attrwin.attrlist.indexOf(attr); attrwin.attrtable.modifyItem(attr.getRow(), idx); } dispose(); } else if (b == del) { // Remove this entry int idx = attrwin.attrlist.indexOf(attr); attrwin.attrlist.removeElementAt(idx); attrwin.attrtable.deleteItem(idx); dispose(); } } public void dispose() { attrwin.edmap.remove(attr); super.dispose(); } } class EXTWindow extends FixedFrame implements CbButtonCallback { FileManager filemgr; RemoteFile file; CbButton ok, cancel; Checkbox cbs[]; String attrs[] = { "A", "a", "c", "d", "i", "s", "S", "u" }; Hashtable attrmap = new Hashtable(); EXTWindow(FileManager p, RemoteFile f) { super(); setTitle(p.text("ext_title", f.path)); filemgr = p; file = f; // Get the attributes String a[] = filemgr.get_text( "getext.cgi?file="+filemgr.urlize(file.path)); if (a[0].length() != 0) { new ErrorWindow(filemgr.text("ext_eattrs", a[0])); return; } for(int i=0; i max || !tok.hasMoreTokens()) { v.addElement(line); line = null; } } setLayout(new GridLayout(v.size(), 1, 0, 0)); for(int i=0; i