Handle hostnames with upper-case letters
[webmin.git] / file / FileManager.java
1 import java.awt.*;
2 import java.awt.event.*;
3 import java.io.*;
4 import java.applet.*;
5 import java.net.*;
6 import java.util.*;
7 import netscape.javascript.JSObject;
8
9 // A java filemanager that allows the user to manipulate files on the
10 // Webmin server. Layout is similar to the windows explorer - directory
11 // tree on the left, files on the right, action buttons on the top.
12 public class FileManager extends Applet
13         implements CbButtonCallback, HierarchyCallback, MultiColumnCallback
14 {
15         // top buttons
16         CbButton ret_b, config_b, down_b, edit_b, refresh_b, props_b,
17                  copy_b, cut_b, paste_b, delete_b, new_b, upload_b, mkdir_b,
18                  makelink_b, rename_b, share_b, mount_b, search_b, acl_b,
19                  attr_b, ext_b, preview_b, extract_b, hnew_b;
20
21         // Directory tree
22         Hierarchy dirs;
23         FileNode root;
24         Hashtable nodemap = new Hashtable();
25
26         // File list
27         MultiColumn files;
28         TextField pathname;
29         CbButton history_b;
30         RemoteFile showing_files;
31         RemoteFile showing_list[];
32         Vector history_list = new Vector();
33
34         // Copying and pasting
35         RemoteFile cut_buffer[];
36         boolean cut_mode;
37
38         static final String monmap[] = { "Jan", "Feb", "Mar", "Apr",
39                                          "May", "Jun", "Jul", "Aug",
40                                          "Sep", "Oct", "Nov", "Dec" };
41         String accroot[];
42         String accnoroot[];
43         Hashtable lang = new Hashtable();
44         Hashtable stab = new Hashtable(),
45                   ntab = new Hashtable();
46         boolean sambamode;
47         int nfsmode;
48         String trust;
49         String extra;
50         String images;
51         int iconsize;
52
53         boolean got_filesystems,
54                 acl_support, attr_support, ext_support;
55         Hashtable mounts = new Hashtable();
56         Vector fslist = new Vector();
57         boolean read_only = false;
58
59         // Standard font for all text
60         Font fixed;
61
62         // Font for button labels
63         Font small_fixed;
64
65         // Full session cookie
66         String session;
67
68         // HTTP referer
69         String referer;
70
71         // Archive parameter
72         String archive;
73
74         // Chroot directory for tree
75         String chroot;
76
77         // File attributes that can be edited
78         boolean can_perms, can_users;
79
80         // Symlimks are automatically followed
81         boolean follow_links;
82
83         // Can search file contents
84         boolean search_contents;
85
86         // Use text editor for HTML
87         boolean force_text;
88
89         // File extensions to consider as HTML
90         String htmlexts[];
91
92         public void init()
93         {
94         setLayout(new BorderLayout());
95
96         // Create fonts from specified size
97         fixed = make_font("fixed", 12);
98         small_fixed = make_font("small_fixed", 10);
99
100         Util.setFont(small_fixed);
101         StringTokenizer tok = new StringTokenizer(getParameter("root"), " ");
102         accroot = new String[tok.countTokens()];
103         for(int i=0; tok.hasMoreTokens(); i++)
104                 accroot[i] = tok.nextToken();
105         if (getParameter("noroot") != null) {
106                 tok = new StringTokenizer(getParameter("noroot"), " ");
107                 accnoroot = new String[tok.countTokens()];
108                 for(int i=0; tok.hasMoreTokens(); i++)
109                         accnoroot[i] = tok.nextToken();
110                 }
111         else {
112                 accnoroot = new String[0];
113                 }
114         trust = getParameter("trust");
115         session = getParameter("session");
116         referer = getDocumentBase().toString();
117         extra = getParameter("extra");
118         if (extra == null) extra = "";
119         images = getParameter("images");
120         if (images == null) images = "images";
121         iconsize = Integer.parseInt(getParameter("iconsize"));
122         archive = getParameter("doarchive");
123         if (archive == null) archive = "0";
124         chroot = getParameter("chroot");
125         if (chroot == null) chroot = "/";
126         String can_perms_str = getParameter("canperms");
127         can_perms = can_perms_str == null || !can_perms_str.equals("0");
128         String can_users_str = getParameter("canusers");
129         can_users = can_users_str == null || !can_users_str.equals("0");
130         String search_contents_str = getParameter("contents");
131         search_contents = search_contents_str == null ||
132                           !search_contents_str.equals("0");
133         String force_text_str = getParameter("force_text");
134         if (force_text_str != null && force_text_str.equals("1"))
135                 force_text = true;
136         String htmlexts_str = getParameter("htmlexts");
137         if (htmlexts_str == null || htmlexts_str.equals(""))
138                 htmlexts_str = ".htm .html";
139         htmlexts = DFSAdminExport.split(htmlexts_str);
140
141         // download language strings
142         String l[] = get_text("lang.cgi");
143         if (l.length < 1 || l[0].indexOf('=') < 0) {
144                 String err = "Failed to get language list : "+join_array(l);
145                 new ErrorWindow(err);
146                 throw new Error(err);
147                 }
148         for(int i=0; i<l.length; i++) {
149                 int eq = l[i].indexOf('=');
150                 if (eq >= 0)
151                         lang.put(l[i].substring(0, eq), l[i].substring(eq+1));
152                 }
153
154         // list samba file shares
155         String s[] = get_text("list_shares.cgi");
156         if (s[0].equals("1")) {
157                 for(int i=1; i<s.length; i++) {
158                         SambaShare ss = new SambaShare(s[i]);
159                         stab.put(ss.path, ss);
160                         }
161                 sambamode = true;
162                 }
163
164         // list NFS exports
165         String e[] = get_text("list_exports.cgi");
166         nfsmode = e.length == 0 ? 0 : Integer.parseInt(e[0]);
167         if (nfsmode != 0) {
168                 for(int i=1; i<e.length; i++) {
169                         if (nfsmode == 1) {
170                                 // Linux export
171                                 LinuxExport le = new LinuxExport(e[i]);
172                                 ntab.put(le.path, le);
173                                 }
174                         else if (nfsmode == 2) {
175                                 // Solaris share
176                                 DFSAdminExport de = new DFSAdminExport(e[i]);
177                                 ntab.put(de.path, de);
178                                 }
179                         }
180                 }
181
182         // list filesystems
183         get_filesystems();
184
185         // get read-only flag
186         if (getParameter("ro").equals("1"))
187                 read_only = true;
188
189         // get custom colours
190         Util.light_edge = get_colour("light_edge", Util.light_edge);
191         Util.dark_edge = get_colour("dark_edge", Util.dark_edge);
192         Util.body = get_colour("body", Util.body);
193         Util.body_hi = get_colour("body_hi", Util.body_hi);
194         Util.light_edge_hi = get_colour("light_edge_hi", Util.light_edge_hi);
195         Util.dark_edge_hi = get_colour("dark_edge_hi", Util.dark_edge_hi);
196         Util.dark_bg = get_colour("dark_bg", Util.dark_bg);
197         Util.text = get_colour("text", Util.text);
198         Util.light_bg = get_colour("light_bg", Util.light_bg);
199
200         // create button panel
201         BorderPanel top = new BorderPanel(2, Util.body);
202         top.setLayout(new ToolbarLayout(ToolbarLayout.LEFT, 5, 2));
203
204         Panel top1 = new Panel();
205         top1.setLayout(new GridLayout(1, 0));
206         if (getParameter("return") != null && can_button("return"))
207                 top1.add(ret_b = make_button("ret.gif", text("top_ret")));
208         if (getParameter("config") != null && can_button("config"))
209                 top1.add(config_b = make_button("config.gif",
210                                                 text("top_config")));
211         if (can_button("save"))
212                 top1.add(down_b = make_button("down.gif", text("top_down")));
213         if (can_button("preview"))
214                 top1.add(preview_b = make_button("preview.gif", text("top_preview")));
215         if (!read_only && can_button("edit")) {
216                 top1.add(edit_b = make_button("edit.gif", text("top_edit")));
217                 }
218         if (can_button("refresh"))
219                 top1.add(refresh_b = make_button("refresh.gif", text("top_refresh")));
220         if (!read_only && can_button("info"))
221                 top1.add(props_b = make_button("props.gif", text("top_info")));
222         if (acl_support && !read_only && can_button("acl"))
223                 top1.add(acl_b = make_button("acl.gif", text("top_eacl")));
224         if (attr_support && !read_only && can_button("attr"))
225                 top1.add(attr_b = make_button("attr.gif", text("top_attr")));
226         if (ext_support && !read_only && can_button("ext"))
227                 top1.add(ext_b = make_button("ext.gif", text("top_ext")));
228         if (can_button("search"))
229                 top1.add(search_b = make_button("search.gif", text("top_search")));
230         top.add(top1);
231         
232         if (!read_only) {
233                 Panel top2 = new Panel();
234                 top2.setLayout(new GridLayout(1, 0));
235                 if (can_button("delete"))
236                         top2.add(delete_b = make_button("delete.gif",
237                                                         text("top_delete")));
238                 if (can_button("new")) {
239                         top2.add(new_b = make_button("new.gif",
240                                                      text("top_new")));
241                         top2.add(hnew_b = make_button("html.gif",
242                                                      text("top_new")));
243                         }
244                 if (can_button("upload"))
245                         top2.add(upload_b = make_button("upload.gif",
246                                                         text("top_upload")));
247                         top2.add(extract_b = make_button("extract.gif",
248                                                          text("top_extract")));
249                 if (can_button("mkdir"))
250                         top2.add(mkdir_b = make_button("mkdir.gif",
251                                                        text("top_new")));
252                 if (getParameter("follow").equals("0") &&
253                     can_button("makelink"))
254                         top2.add(makelink_b = make_button("makelink.gif",
255                                                 text("top_new")));
256                 if (can_button("rename"))
257                         top2.add(rename_b = make_button("rename.gif",
258                                                         text("top_rename")));
259                 if ((sambamode || nfsmode != 0) &&
260                     getParameter("sharing").equals("1") &&
261                     can_button("sharing"))
262                         top2.add(share_b = make_button("share.gif",
263                                                        text("top_share")));
264                 if (getParameter("mounting").equals("1") &&
265                     can_button("mount"))
266                         top2.add(mount_b = make_button("mount.gif",
267                                                        text("top_mount")));
268                 top.add(top2);
269
270                 if (can_button("copy")) {
271                         Panel top3 = new Panel();
272                         top3.setLayout(new GridLayout(1, 0));
273                         top3.add(copy_b = make_button("copy.gif",
274                                                       text("top_copy")));
275                         top3.add(cut_b = make_button("cut.gif",
276                                                      text("top_cut")));
277                         top3.add(paste_b = make_button("paste.gif",
278                                                        text("top_paste")));
279                         top.add(top3);
280                         }
281                 }
282         add("North", top);
283         follow_links = getParameter("follow").equals("1");
284
285         // create directory tree
286         BorderPanel left = new BorderPanel(2, Util.body);
287         left.setLayout(new BorderLayout());
288         root = new FileNode(new RemoteFile(this, get_text("root.cgi")[0],null));
289         left.add("Center", dirs = new Hierarchy(root, this));
290         dirs.setFont(fixed);
291         root.open = true; root.fill();
292
293         // create file list window
294         BorderPanel right = new BorderPanel(2, Util.body);
295         right.setLayout(new BorderLayout());
296         Panel rtop = new Panel();
297         rtop.setLayout(new BorderLayout());
298         rtop.add("Center", pathname = new TextField());
299         rtop.add("East", history_b = new CbButton(text("history_button"),this));
300         right.add("North", rtop);
301         pathname.setFont(fixed);
302         String cols[] = { "", text("right_name"), text("right_size"),
303                           text("right_user"), text("right_group"),
304                           text("right_date") };
305         float widths[] = { .07f, .33f, .15f, .15f, .15f, .15f };
306         right.add("Center", files = new MultiColumn(cols, this));
307         files.setWidths(widths);
308         files.setDrawLines(false);
309         files.setMultiSelect(true);
310         files.setFont(fixed);
311         show_files(root.file);
312
313         ResizePanel mid = new ResizePanel(left, right, .3, false);
314         add("Center", mid);
315
316         // Go to the restricted directory
317         String home = getParameter("home");
318         String go = getParameter("goto");
319         String open = getParameter("open");
320         if (open != null) {
321                 find_directory(open, true);
322                 }
323         else if (go != null && go.equals("1")) {
324                 if (home != null)
325                         find_directory(home, true);
326                 else if (!accroot[0].equals("/"))
327                         find_directory(accroot[0], true);
328                 }
329         }
330
331         Font make_font(String name, int defsize)
332         {
333         String str = getParameter(name);
334         int size = str == null || str.equals("") ? defsize :
335                    Integer.parseInt(str);
336         return new Font("courier", Font.PLAIN, size);
337         }
338
339         // Looks up an applet parameter for a colour, and returns it if
340         // defined, otherwise the default. MUST be in RRGGBB hex format
341         Color get_colour(String name, Color def)
342         {
343         String str = getParameter("applet_"+name);
344         if (str == null) {
345                 return def;
346                 }
347         else {
348                 return new Color(get_hex(str, 0),
349                                  get_hex(str, 2),
350                                  get_hex(str, 4));
351                 }
352         }
353
354         int get_hex(String str, int pos)
355         {
356         str = str.toUpperCase();
357         char c1 = str.charAt(pos), c2 = str.charAt(pos+1);
358         int b1 = Character.isDigit(c1) ? c1-48 : c1-65+10;
359         int b2 = Character.isDigit(c2) ? c2-48 : c2-65+10;
360         return (b1<<4) + (b2);
361         }
362
363         boolean can_button(String name)
364         {
365         return getParameter("no_"+name) == null;
366         }
367
368         CbButton make_button(String f, String t)
369         {
370         if (iconsize == 1)
371                 return new CbButton(get_image(f), this);
372         else
373                 return new CbButton(get_image(f), t, CbButton.ABOVE, this);
374         }
375
376         // Gets an image from the images directory
377         Image get_image(String img)
378         {
379         return getImage(getDocumentBase(), images+"/"+img);
380         }
381
382         // Gets charset parameter from Content-Type: header
383         String get_charset(String ct)
384         {
385         if (ct == null)
386                 return null;
387         StringTokenizer st = new StringTokenizer(ct, ";");
388         while (st.hasMoreTokens()) {
389                 String l = st.nextToken().trim();
390                 if (l.startsWith("charset=")) {
391                         // get the value of charset= param.
392                         return l.substring(8);
393                         }
394                 }
395         return null;
396         }
397
398         String[] get_text(String url)
399         {
400         try {
401                 long now = System.currentTimeMillis();
402                 if (url.indexOf('?') > 0) url += "&rand="+now;
403                 else url += "?rand="+now;
404                 url += "&trust="+trust;
405                 url += extra;
406                 URL u = new URL(getDocumentBase(), url);
407                 URLConnection uc = u.openConnection();
408                 set_cookie(uc);
409                 String charset = get_charset(uc.getContentType());
410                 InputStream ris = uc.getInputStream();
411                 BufferedReader is = null;
412                 if (charset == null) {
413                         is = new BufferedReader(new InputStreamReader(ris));
414                         }
415                 else {
416                         // Try to use a character set, and handle failure
417                         try {
418                                 is = new BufferedReader(
419                                         new InputStreamReader(ris, charset));
420                                 }
421                         catch(Exception e) {
422                                 e.printStackTrace();
423                                 is = new BufferedReader(
424                                         new InputStreamReader(ris));
425                                 }
426                         }
427                 Vector lv = new Vector();
428                 while(true) {
429                         String l = is.readLine();
430                         if (l == null) { break; }
431                         lv.addElement(l);
432                         }
433                 is.close();
434                 String rv[] = new String[lv.size()];
435                 lv.copyInto(rv);
436                 return rv;
437                 }
438         catch(Exception e) {
439                 e.printStackTrace();
440                 //return null;
441                 String err[] = { e.getClass().getName()+" : "+e.getMessage() };
442                 return err;
443                 }
444         }
445
446         void set_cookie(URLConnection conn)
447         {
448         if (session != null)
449                 conn.setRequestProperty("Cookie", session);
450         conn.setRequestProperty("Referer", referer);
451         }
452
453         // Fill the multicolumn list with files from some directory
454         boolean show_files(RemoteFile f)
455         {
456         RemoteFile fl[] = f.list();
457         if (fl == null) return false;
458         files.clear();
459         Object rows[][] = new Object[fl.length+1][];
460         long now = System.currentTimeMillis();
461
462         // Sort listing by chosen column
463         if (f != showing_files) {
464                 // Directory has changed .. assume sort by name
465                 files.sortingArrow(1, 1);
466                 }
467         else if (files.sortdir != 0) {
468                 // Sort by chosen order
469                 RemoteFile fls[] = new RemoteFile[fl.length];
470                 System.arraycopy(fl, 0, fls, 0, fl.length);
471                 QuickSort.sort(fls, files.sortcol, files.sortdir);
472                 fl = fls;
473                 }
474
475         // Create parent directory row
476         rows[0] = new Object[6];
477         rows[0][0] = get_image("dir.gif");
478         rows[0][1] = "..";
479         rows[0][2] = rows[0][3] = rows[0][4] = rows[0][5] = "";
480
481         // Create file rows
482         Date n = new Date(now);
483         for(int i=0; i<fl.length; i++) {
484                 Object row[] = rows[i+1] = new Object[6];
485                 if (fl[i].shared() && fl[i].mounted())
486                         row[0] = get_image("smdir.gif");
487                 else if (fl[i].shared() && fl[i].mountpoint())
488                         row[0] = get_image("sudir.gif");
489                 else if (fl[i].shared())
490                         row[0] = get_image("sdir.gif");
491                 else if (fl[i].mounted())
492                         row[0] = get_image("mdir.gif");
493                 else if (fl[i].mountpoint())
494                         row[0] = get_image("udir.gif");
495                 else
496                         row[0] = get_image(RemoteFile.tmap[fl[i].type]);
497                 row[1] = fl[i].name;
498                 if (fl[i].size < 1000)
499                         row[2] = spad(fl[i].size, 5)+" B";
500                 else if (fl[i].size < 1000000)
501                         row[2] = spad(fl[i].size/1000, 5)+" kB";
502                 else
503                         row[2] = spad(fl[i].size/1000000, 5)+" MB";
504                 row[3] = fl[i].user;
505                 row[4] = fl[i].group;
506                 Date d = new Date(fl[i].modified);
507                 //if (now - fl[i].modified < 24*60*60*1000) {
508                 if (n.getDate() == d.getDate() &&
509                     n.getMonth() == d.getMonth() &&
510                     n.getYear() == d.getYear()) {
511                         // show as hour:min
512                         row[5] = pad(d.getHours(),2)+":"+
513                                  pad(d.getMinutes(),2);
514                         }
515                 //else if (now - fl[i].modified < 24*60*60*365*1000) {
516                 else if (n.getYear() == d.getYear()) {
517                         // show as day/mon
518                         row[5] = pad(d.getDate(),2)+"/"+
519                                  monmap[d.getMonth()];
520                         }
521                 else {
522                         // show as mon/year
523                         row[5] = monmap[d.getMonth()]+"/"+
524                                  pad(d.getYear()%100, 2);
525                         }
526                 }
527         files.addItems(rows);
528         showing_files = f;
529         showing_list = fl;
530         pathname.setText(f.path);
531         return true;
532         }
533
534         String pad(int n, int s)
535         {
536         String rv = String.valueOf(n);
537         while(rv.length() < s)
538                 rv = "0"+rv;
539         return rv;
540         }
541
542         String spad(long n, int s)
543         {
544         String rv = String.valueOf(n);
545         while(rv.length() < s)
546                 rv = " "+rv;
547         return rv;
548         }
549
550         String trim_path(String p)
551         {
552         while(p.endsWith("/"))
553                 p = p.substring(0, p.length()-1);
554         return p;
555         }
556
557         // openNode
558         // Called when a node with children is opened
559         public void openNode(Hierarchy h, HierarchyNode n)
560         {
561         FileNode fn = (FileNode)n;
562         fn.fill();
563         }
564
565         // closeNode
566         // Called when a node is closed
567         public void closeNode(Hierarchy h, HierarchyNode n)
568         {
569         }
570
571         // clickNode
572         // Called when the user clicks on a node
573         public void clickNode(Hierarchy h, HierarchyNode n)
574         {
575         FileNode fn = (FileNode)n;
576         if (showing_files != fn.file)
577                 show_files(fn.file);
578         }
579
580         // doubleNode
581         // Called when a user double-clicks on a node
582         public void doubleNode(Hierarchy h, HierarchyNode n)
583         {
584         }
585
586         // Called when a button is clicked
587         public void click(CbButton b)
588         {
589         int s = files.selected();
590         int ss[] = files.allSelected();
591         RemoteFile f = null, ff[] = new RemoteFile[0];
592         if (s > 0 || s == 0 && ss.length > 1) {
593                 // At least one non-.. file was selected
594                 boolean parentsel = false;
595                 for(int i=0; i<ss.length; i++)
596                         if (ss[i] == 0)
597                                 parentsel = true;
598                 RemoteFile list[] = showing_list;
599                 if (parentsel) {
600                         // need to exclude .. from selected list!
601                         ff = new RemoteFile[ss.length-1];
602                         for(int i=0,j=0; i<ss.length; i++)
603                                 if (ss[i] != 0)
604                                         ff[j++] = list[ss[i]-1];
605                         f = s == 0 ? ff[0] : list[s-1];
606                         }
607                 else {
608                         // include all selected files
609                         f = list[s-1];
610                         ff = new RemoteFile[ss.length];
611                         for(int i=0; i<ss.length; i++)
612                                 ff[i] = list[ss[i]-1];
613                         }
614                 }
615         FileNode d = (FileNode)dirs.selected();
616         if (b == ret_b) {
617                 // Return to the webmin index
618                 try {
619                         URL u = new URL(getDocumentBase(),
620                                         getParameter("return"));
621                         getAppletContext().showDocument(u);
622                         }
623                 catch(Exception e) { }
624                 }
625         else if (b == config_b) {
626                 // Open the module config window
627                 try {
628                         URL u = new URL(getDocumentBase(),
629                                         getParameter("config"));
630                         getAppletContext().showDocument(u, "_self");
631                         }
632                 catch(Exception e) { }
633                 }
634         else if (b == edit_b) {
635                 // Open a window for editing the selected file
636                 if (f == null)
637                         new ErrorWindow(text("top_efile"));
638                 else if (f.type == 0 || f.type > 4)
639                         new ErrorWindow(text("edit_enormal"));
640                 else if (is_html_filename(f.path) && !force_text) {
641                         // Open HTML editor
642                         try {
643                                 JSObject win = JSObject.getWindow(this);
644                                 String params[] = { f.path, "" };
645                                 win.call("htmledit", params);
646                                 }
647                         catch(Exception e) {
648                                 new ErrorWindow(text("html_efailed",
649                                                      e.getMessage()));
650                                 }
651                         }
652                 else {
653                         // Open text editor
654                         new EditorWindow(f, this);
655                         }
656                 }
657         else if (b == down_b) {
658                 // Force download of the selected file
659                 if (f == null) return;
660                 download_file(f);
661                 }
662         else if (b == preview_b) {
663                 // Open preview window for selected file
664                 if (f == null) return;
665                 if (f.type == RemoteFile.DIR)
666                         new ErrorWindow(text("preview_eimage"));
667                 else
668                         new PreviewWindow(this, f);
669                 }
670         else if (b == refresh_b) {
671                 // Refesh the selected directory (and thus any subdirs)
672                 if (d == null) return;
673                 d.refresh();
674                 show_files(d.file);
675                 }
676         else if (b == props_b) {
677                 // Display the properties window
678                 if (f == null) return;
679                 new PropertiesWindow(f, this);
680                 }
681         else if (b == acl_b) {
682                 // Display the ACL window (if filesystem supports them)
683                 if (f == null) return;
684                 FileSystem filefs = find_filesys(f);
685                 if (filefs == null) return;
686                 if (filefs.acls)
687                         new ACLWindow(this, f);
688                 else
689                         new ErrorWindow(text("eacl_efs", filefs.mount));
690                 }
691         else if (b == attr_b) {
692                 // Display the attributes window (if filesystem supports them)
693                 if (f == null) return;
694                 FileSystem filefs = find_filesys(f);
695                 if (filefs == null) return;
696                 if (filefs.attrs)
697                         new AttributesWindow(this, f);
698                 else
699                         new ErrorWindow(text("attr_efs", filefs.mount));
700                 }
701         else if (b == ext_b) {
702                 // Display EXT attributes window (if filesystem supports them)
703                 if (f == null) return;
704                 FileSystem filefs = find_filesys(f);
705                 if (filefs == null) return;
706                 if (filefs.ext)
707                         new EXTWindow(this, f);
708                 else
709                         new ErrorWindow(text("ext_efs", filefs.mount));
710                 }
711         else if (b == copy_b) {
712                 // Copy the selected files
713                 if (f == null) return;
714                 cut_buffer = ff;
715                 cut_mode = false;
716                 }
717         else if (b == cut_b) {
718                 // Cut the selected file
719                 if (f == null) return;
720                 cut_buffer = ff;
721                 cut_mode = true;
722                 }
723         else if (b == paste_b) {
724                 // Paste the copied file
725                 if (cut_buffer == null) {
726                         new ErrorWindow(text("paste_ecopy"));
727                         return;
728                         }
729
730                 // Check for existing file clashes
731                 // XXX
732
733                 // Go through all the files to paste
734                 for(int i=0; i<cut_buffer.length; i++) {
735                         RemoteFile cf = cut_buffer[i];
736
737                         // Check for an existing file
738                         RemoteFile already = showing_files.find(cf.name);
739                         String sp = showing_files.path;
740                         String dest_path = sp.equals("/") ? sp+cf.name
741                                                           : sp+"/"+cf.name;
742                         if (already != null) {
743                                 // File exists .. offer to rename
744                                 new OverwriteWindow(this, already, cf, i);
745                                 }
746                         else {
747                                 // do the move or copy
748                                 RemoteFile nf = paste_file(cf, showing_files,
749                                                    dest_path, null, cut_mode);
750                                 if (cut_mode && nf != null) {
751                                         // Paste from the destination path
752                                         // from now on
753                                         cut_buffer[i] = nf;
754                                         }
755                                 }
756                         }
757                 cut_mode = false;
758                 }
759         else if (b == delete_b) {
760                 // Delete the selected files
761                 if (f == null) return;
762                 new DeleteWindow(this, ff);
763                 }
764         else if (b == new_b) {
765                 // Open a window for creating a text file
766                 new EditorWindow(showing_files.path, this);
767                 }
768         else if (b == hnew_b) {
769                 // Open a window for creating an HTML file
770                 try {
771                         JSObject win = JSObject.getWindow(this);
772                         String params[] = { "", showing_files.path };
773                         win.call("htmledit", params);
774                         }
775                 catch(Exception e) {
776                         new ErrorWindow(text("html_efailed",
777                                              e.getMessage()));
778                         }
779                 }
780         else if (b == upload_b) {
781                 // Call javascript to open an upload window
782                 try {
783                         JSObject win = JSObject.getWindow(this);
784                         String params[] = { showing_files.path };
785                         win.call("upload", params);
786                         }
787                 catch(Exception e) {
788                         new ErrorWindow(text("upload_efailed", e.getMessage()));
789                         }
790                 }
791         else if (b == extract_b) {
792                 // Ask for confirmation, then extract file
793                 if (f == null) return;
794                 if (f.type == 0 || f.type == 6 || f.type == 7)
795                         new ErrorWindow(text("extract_etype", f.path));
796                 else
797                         new ExtractWindow(this, f);
798                 }
799         else if (b == mkdir_b) {
800                 // Prompt for new directory
801                 new MkdirWindow(showing_files.path, this);
802                 }
803         else if (b == makelink_b) {
804                 // Prompt for a new symlink
805                 new LinkWindow(showing_files.path, this);
806                 }
807         else if (b == rename_b) {
808                 // Prompt for new filename
809                 if (f == null) return;
810                 new RenameWindow(this, f);
811                 }
812         else if (b == share_b) {
813                 // Open a window for editing sharing options
814                 if (f == null || f.type != RemoteFile.DIR) return;
815                 new SharingWindow(f, this);
816                 }
817         else if (b == mount_b) {
818                 // Check if the selected directory is a mount point
819                 if (f == null || f.type != RemoteFile.DIR) return;
820                 FileSystem fs = f.fs();
821                 if (fs == null)
822                         new ErrorWindow(text("mount_epoint", f.path));
823                 else
824                         new MountWindow(this, fs, f);
825                 }
826         else if (b == search_b) {
827                 // Open window for finding a file
828                 new SearchWindow(showing_files.path, this);
829                 }
830         else if (b == history_b) {
831                 // Open entered file history window
832                 if (history_list.size() > 0) {
833                         new HistoryWindow(this);
834                         }
835                 }
836         }
837
838         boolean is_html_filename(String path)
839         {
840         for(int i=0; i<htmlexts.length; i++)
841                 if (path.toLowerCase().endsWith(htmlexts[i]))
842                         return true;
843         return false;
844         }
845
846         boolean under_root_dir(String p, String roots[])
847         {
848         boolean can = false;
849         int l = p.length();
850         for(int r=0; r<roots.length; r++) {
851                 int rl = roots[r].length();
852                 if (roots[r].equals("/"))
853                         can = true;
854                 else if (l >= rl && p.substring(0, rl).equals(roots[r]))
855                         can = true;
856                 else if (l < rl && roots[r].substring(0, l).equals(p))
857                         can = true;
858                 }
859         return can;
860         }
861
862         // Download some file to the user's browser, if possible
863         void download_file(RemoteFile f)
864         {
865         if (f.type == RemoteFile.DIR && !archive.equals("0"))
866                 new DownloadDirWindow(this, f);
867         else if (f.type == RemoteFile.DIR || f.type > 4)
868                 new ErrorWindow(text("view_enormal2"));
869         else
870                 open_file_window(f, true, 0);
871         }
872
873         // Returns the object for some directory, or null if not found.
874         RemoteFile find_directory(String p, boolean fill)
875         {
876         boolean can = under_root_dir(p, accroot) &&
877                       !under_root_dir(p, accnoroot);
878         if (!can) {
879                 new ErrorWindow(text("find_eaccess", p));
880                 return null;
881                 }
882         FileNode posnode = root;
883         RemoteFile pos = posnode.file;
884         StringTokenizer tok = new StringTokenizer(p, "/");
885         while(tok.hasMoreTokens()) {
886                 String fn = tok.nextToken();
887                 if (fn.equals("")) continue;
888                 RemoteFile fl[] = pos.list();
889                 if (fl == null) return null;
890                 if (fill) {
891                         posnode.open = true;
892                         posnode.fill();
893                         }
894                 boolean found = false;
895                 for(int i=0; i<fl.length; i++)
896                         if (fl[i].name.equals(fn)) {
897                                 pos = fl[i];
898                                 found = true;
899                                 }
900                 if (!found) {
901                         new ErrorWindow(text("find_eexist", fn, p));
902                         return null;
903                         }
904                 if (pos.type != 0) {
905                         new ErrorWindow(text("find_edir", fn, p));
906                         return null;
907                         }
908                 if (fill)
909                         posnode = (FileNode)nodemap.get(pos);
910                 }
911         if (fill) {
912                 if (show_files(pos)) {
913                         posnode.fill();
914                         posnode.open = true;
915                         dirs.select(posnode);
916                         dirs.redraw();
917                         }
918                 }
919         return pos;
920         }
921
922         FileSystem find_filesys(RemoteFile f)
923         {
924         FileSystem filefs = null;
925         for(int i=0; i<fslist.size(); i++) {
926                 FileSystem fs = (FileSystem)fslist.elementAt(i);
927                 int l = fs.mount.length();
928                 if (fs.mount.equals(f.path) ||
929                     (f.path.length() >= l+1 &&
930                      f.path.substring(0, l+1).equals(fs.mount+"/")) ||
931                     fs.mount.equals("/")) {
932                         filefs = fs;
933                         }
934                 }
935         return filefs;
936         }
937
938         public boolean action(Event e, Object o)
939         {
940         if (e.target == pathname) {
941                 // A new path was entered.. cd to it
942                 String p = pathname.getText().trim();
943                 if (p.equals("")) return true;
944                 find_directory(p, true);
945
946                 // Add to the history
947                 if (!history_list.contains(p)) {
948                         history_list.insertElementAt(p, 0);
949                         }
950                 return true;
951                 }
952         return false;
953         }
954
955         // singleClick
956         // Called on a single click on a list item
957         public void singleClick(MultiColumn list, int num)
958         {
959         }
960
961         // doubleClick
962         // Called upon double-clicking on a list item
963         public void doubleClick(MultiColumn list, int num)
964         {
965         if (num == 0) {
966                 // Go to parent directory
967                 if (showing_files.directory != null) {
968                         ((FileNode)nodemap.get(showing_files)).open = false;
969                         show_files(showing_files.directory);
970                         dirs.select((FileNode)nodemap.get(showing_files));
971                         dirs.redraw();
972                         }
973                 return;
974                 }
975         RemoteFile d = showing_list[num-1];
976         if (d.type == 0) {
977                 // Open this directory
978                 FileNode pn = (FileNode)nodemap.get(showing_files);
979                 pn.fill();
980                 pn.open = true;
981                 FileNode fn = (FileNode)nodemap.get(d);
982                 if (show_files(d)) {
983                         fn.fill();
984                         fn.open = true;
985                         dirs.select(fn);
986                         dirs.redraw();
987                         }
988                 }
989         else if (d.type <= 4) {
990                 // Direct the browser to this file
991                 open_file_window(d, list.last_event.shiftDown(), 0);
992                 }
993         }
994
995         // Called when the user clicks on a column heading so that it can
996         // be sorted.
997         public void headingClicked(MultiColumn list, int col)
998         {
999         if (col == 0)
1000                 return; // ignore click on icon column?
1001         if (col == list.sortcol) {
1002                 list.sortingArrow(col, list.sortdir == 2 ? 1 : 2);
1003                 }
1004         else {
1005                 list.sortingArrow(col, 1);
1006                 }
1007
1008         // Re-show the list in the new order, but with the same files selected
1009         int ss[] = files.allSelected();
1010         RemoteFile ssf[] = new RemoteFile[ss.length];
1011         for(int i=0; i<ss.length; i++)
1012                 ssf[i] = showing_list[ss[i]-1];
1013         show_files(showing_files);
1014         for(int i=0; i<ss.length; i++) {
1015                 for(int j=0; j<showing_list.length; j++) {
1016                         if (showing_list[j] == ssf[i]) {
1017                                 ss[i] = j+1;
1018                                 break;
1019                                 }
1020                         }
1021                 }
1022         files.select(ss);
1023         }
1024
1025         void open_file_window(RemoteFile f, boolean download, int format)
1026         {
1027         try {
1028                 String ext = format == 1 ? ".zip" :
1029                              format == 2 ? ".tgz" :
1030                              format == 3 ? ".tar" : "";
1031                 String urlstr;
1032                 if (download) {
1033                         urlstr = "show.cgi"+urlize(f.path)+ext+
1034                                  "?rand="+System.currentTimeMillis()+
1035                                  "&type=application%2Funknown"+
1036                                  "&trust="+trust+
1037                                  "&format="+format+
1038                                  extra;
1039                         }
1040                 else {
1041                         urlstr = "show.cgi"+urlize(f.path)+ext+
1042                                  "?rand="+System.currentTimeMillis()+
1043                                  "&trust="+trust+
1044                                  "&format="+format+
1045                                  extra;
1046                         }
1047
1048                 // Do a test fetch
1049                 String l[] = get_text(urlstr+"&test=1");
1050                 if (l[0].length() > 0) {
1051                         new ErrorWindow(text("eopen", l[0]));
1052                         return;
1053                         }
1054
1055                 // Open for real
1056                 if (download) {
1057                         getAppletContext().showDocument(
1058                                 new URL(getDocumentBase(), urlstr));
1059                         }
1060                 else {
1061                         getAppletContext().showDocument(
1062                                 new URL(getDocumentBase(), urlstr), "show");
1063                         }
1064                 }
1065         catch(Exception e) { }
1066         }
1067
1068         static String urlize(String s)
1069         {
1070         StringBuffer rv = new StringBuffer();
1071         for(int i=0; i<s.length(); i++) {
1072                 char c = s.charAt(i);
1073                 if (c < 16)
1074                         rv.append("%0"+Integer.toString(c, 16));
1075                 else if ((!Character.isLetterOrDigit(c) && c != '/' &&
1076                     c != '.' && c != '_' && c != '-') || c >= 128)
1077                         rv.append("%"+Integer.toString(c, 16));
1078                 else
1079                         rv.append(c);
1080                 }
1081         return rv.toString();
1082         }
1083
1084         static String un_urlize(String s)
1085         {
1086         StringBuffer rv = new StringBuffer();
1087         for(int i=0; i<s.length(); i++) {
1088                 char c = s.charAt(i);
1089                 if (c == '%') {
1090                         rv.append((char)Integer.parseInt(
1091                                 s.substring(i+1, i+3), 16));
1092                         i += 2;
1093                         }
1094                 else
1095                         rv.append(c);
1096                 }
1097         return rv.toString();
1098         }
1099
1100         // Called back by Javascript when a file or directory has been modified
1101         public void upload_notify(String path_str, String info)
1102         {
1103         int sl = path_str.lastIndexOf('/');
1104         String par_str = path_str.substring(0, sl),
1105                file_str = path_str.substring(sl+1);
1106         RemoteFile par = find_directory(par_str, false);
1107         RemoteFile upfile = par.find(file_str);
1108         try {
1109                 if (upfile == null) {
1110                         // Need to add this file/directory
1111                         upfile = new RemoteFile(this, info, par);
1112                         par.add(upfile);
1113                         }
1114                 else if (upfile.type == RemoteFile.DIR) {
1115                         // Is a directory .. refresh from server
1116                         FileNode upnode = (FileNode)nodemap.get(upfile);
1117                         if (upnode != null)
1118                                 upnode.refresh();
1119                         }
1120                 show_files(showing_files);
1121                 }
1122         catch(Exception e) {
1123                 // In some cases, any attempt to make an HTTP request to
1124                 // refresh the directory may fail because Java apparently has
1125                 // some security rules that limit what a function called from
1126                 // JavaScript is allowed to do. All we can do is ignore the
1127                 // exception :-(
1128                 e.printStackTrace();
1129                 }
1130         }
1131
1132         // Called back by Javascript to show an upload-related error
1133         public void upload_error(String err)
1134         {
1135         new ErrorWindow(err);
1136         }
1137
1138         public String text(String k, String p[])
1139         {
1140         String rv = (String)lang.get(k);
1141         if (rv == null) rv = "???";
1142         for(int i=0; i<p.length; i++) {
1143                 int idx = rv.indexOf("$"+(i+1));
1144                 if (idx != -1)
1145                         rv = rv.substring(0, idx)+p[i]+rv.substring(idx+2);
1146                 }
1147         return rv;
1148         }
1149
1150         public String text(String k)
1151         {
1152         String p[] = { };
1153         return text(k, p);
1154         }
1155
1156         public String text(String k, String p1)
1157         {
1158         String p[] = { p1 };
1159         return text(k, p);
1160         }
1161
1162         public String text(String k, String p1, String p2)
1163         {
1164         String p[] = { p1, p2 };
1165         return text(k, p);
1166         }
1167
1168         RemoteFile paste_file(RemoteFile src, RemoteFile dir,
1169                               String dest, RemoteFile already, boolean mode)
1170         {
1171         // Move or copy the actual file
1172         String[] rv = get_text((mode ? "move.cgi" : "copy.cgi")+
1173                                "?from="+urlize(src.path)+
1174                                "&to="+urlize(dest));
1175         if (rv[0].length() > 0) {
1176                 new ErrorWindow(text(
1177                         mode ? "paste_emfailed" : "paste_ecfailed", rv[0]));
1178                 return null;
1179                 }
1180         RemoteFile file = new RemoteFile(this, rv[1], dir);
1181         if (already == null) {
1182                 // Add to the parent directory
1183                 dir.add(file);
1184                 }
1185         else {
1186                 // Update the existing file
1187                 already.type = file.type;
1188                 already.user = file.user;
1189                 already.group = file.group;
1190                 already.size = file.size;
1191                 already.perms = file.perms;
1192                 already.modified = file.modified;
1193                 file = already;
1194                 }
1195         if (mode) {
1196                 // Delete the old file
1197                 src.directory.delete(src);
1198                 }
1199         if (src.type == 0) {
1200                 // Moving or copying a directory.. update the tree
1201                 FileNode dest_par_node =
1202                         (FileNode)nodemap.get(showing_files);
1203                 dest_par_node.add(new FileNode(file));
1204                 if (mode) {
1205                         FileNode cut_par_node =
1206                                 (FileNode)nodemap.get(src.directory);
1207                         FileNode cut_file_node =
1208                                 (FileNode)nodemap.get(src);
1209                         if (cut_par_node != null &&
1210                             cut_file_node != null)
1211                                 cut_par_node.ch.removeElement(
1212                                                         cut_file_node);
1213                         }
1214                 dirs.redraw();
1215                 }
1216         show_files(showing_files);
1217         return file;
1218         }
1219
1220         // Loads the list of filesystems from the server, and refreshes all
1221         // caches
1222         void get_filesystems()
1223         {
1224         String f[] = get_text("filesystems.cgi");
1225         got_filesystems = f[0].equals("1");
1226         acl_support = false;
1227         attr_support = false;
1228         ext_support = false;
1229         mounts.clear();
1230         fslist.removeAllElements();
1231         if (got_filesystems) {
1232                 for(int i=1; i<f.length; i++) {
1233                         FileSystem fs = new FileSystem(f[i]);
1234                         fslist.addElement(fs);
1235                         if (fs.acls) acl_support = true;
1236                         if (fs.attrs) attr_support = true;
1237                         if (fs.ext) ext_support = true;
1238                         mounts.put(fs.mount, fs);
1239                         }
1240                 }
1241         }
1242
1243         String join_array(String l[])
1244         {
1245         String rv = "";
1246         for(int i=0; i<l.length; i++)
1247                 rv += l[i]+"\n";
1248         return rv;
1249         }
1250
1251         static String replace_str(String str, String os, String ns)
1252         {
1253         String rv;
1254         int idx;
1255         int pos = 0;
1256         rv = str;
1257         while((idx = rv.indexOf(os, pos)) >= 0) {
1258                 rv = rv.substring(0, idx)+
1259                       ns+rv.substring(idx+os.length());
1260                 pos = idx+ns.length()+1;
1261                 }
1262         return rv;
1263         }
1264 }
1265
1266 // A node in the directory tree
1267 class FileNode extends HierarchyNode
1268 {
1269         FileManager parent;
1270         RemoteFile file;
1271         boolean known;
1272
1273         FileNode(RemoteFile file)
1274         {
1275         this.file = file;
1276         parent = file.parent;
1277         setimage();
1278         ch = new Vector();
1279         text = file.name;
1280         parent.nodemap.put(file, this);
1281         }
1282
1283         // Create the nodes for subdirectories
1284         void fill()
1285         {
1286         if (!known) {
1287                 RemoteFile l[] = file.list();
1288                 if (l == null) return;
1289                 ch.removeAllElements();
1290                 for(int i=0; i<l.length; i++)
1291                         if (l[i].type == 0)
1292                                 ch.addElement(new FileNode(l[i]));
1293                 parent.dirs.redraw();
1294                 known = true;
1295                 }
1296         }
1297
1298         void add(FileNode n)
1299         {
1300         for(int i=0; i<=ch.size(); i++) {
1301                 FileNode ni = i==ch.size() ? null : (FileNode)ch.elementAt(i);
1302                 if (ni == null || ni.text.compareTo(n.text) > 0) {
1303                         ch.insertElementAt(n, i);
1304                         break;
1305                         }
1306                 }
1307         }
1308
1309         void setimage()
1310         {
1311         im = parent.get_image(file.shared() && file.mounted() ? "smdir.gif" :
1312                               file.shared() && file.mountpoint() ? "sudir.gif" :
1313                               file.shared() ? "sdir.gif" :
1314                               file.mounted() ? "mdir.gif" :
1315                               file.mountpoint() ? "udir.gif" :
1316                                                   "dir.gif");
1317         }
1318
1319         // Forces a re-load from the server
1320         void refresh()
1321         {
1322         known = false;
1323         file.list = null;
1324         fill();
1325         }
1326 }
1327
1328 class RemoteFile
1329 {
1330         static final int DIR = 0;
1331         static final int TEXT = 1;
1332         static final int IMAGE = 2;
1333         static final int BINARY = 3;
1334         static final int UNKNOWN = 4;
1335         static final int SYMLINK = 5;
1336         static final int DEVICE = 6;
1337         static final int PIPE = 7;
1338         static final String[] tmap = { "dir.gif", "text.gif", "image.gif",
1339                                        "binary.gif", "unknown.gif",
1340                                        "symlink.gif", "device.gif",
1341                                        "pipe.gif" };
1342
1343         FileManager parent;
1344         String path, name;
1345         int type;
1346         String user, group;
1347         long size;
1348         int perms;
1349         long modified;
1350         String linkto;
1351         RemoteFile list[];
1352         RemoteFile directory;
1353
1354         // Parse a line of text to a file object
1355         RemoteFile(FileManager parent, String line, RemoteFile d)
1356         {
1357         this.parent = parent;
1358         StringTokenizer tok = new StringTokenizer(line, "\t");
1359         if (tok.countTokens() < 7) {
1360                 String err = "Invalid file line : "+line;
1361                 new ErrorWindow(err);
1362                 throw new Error(err);
1363                 }
1364         path = tok.nextToken();
1365         path = parent.replace_str(path, "\\t", "\t");
1366         path = parent.replace_str(path, "\\\\", "\\");
1367         type = Integer.parseInt(tok.nextToken());
1368         user = tok.nextToken();
1369         group = tok.nextToken();
1370         size = Long.parseLong(tok.nextToken());
1371         perms = Integer.parseInt(tok.nextToken());
1372         modified = Long.parseLong(tok.nextToken())*1000;
1373         if (type == 5) linkto = tok.nextToken();
1374         directory = d;
1375         if (path.equals("/")) name = "/";
1376         else name = path.substring(path.lastIndexOf('/')+1);
1377         }
1378
1379         // Create a new, empty file object
1380         RemoteFile() { }
1381
1382         // Returns a list of files in this directory
1383         RemoteFile[] list()
1384         {
1385         if (list == null) {
1386                 String l[] = parent.get_text("list.cgi?dir="+
1387                                              parent.urlize(path));
1388                 if (l[0].length() > 0) {
1389                         //list = new RemoteFile[0];
1390                         // Error reading the remote directory!
1391                         new ErrorWindow(parent.text("list_edir", path, l[0]));
1392                         list = null;
1393                         }
1394                 else {
1395                         list = new RemoteFile[l.length-3];
1396                         for(int i=3; i<l.length; i++)
1397                                 list[i-3] = new RemoteFile(parent, l[i], this);
1398                         }
1399                 }
1400         return list;
1401         }
1402
1403         RemoteFile find(String n)
1404         {
1405         RemoteFile l[] = list();
1406         if (l != null) {
1407                 for(int i=0; i<l.length; i++)
1408                         if (l[i].name.equals(n))
1409                                 return l[i];
1410                 }
1411         return null;
1412         }
1413
1414         void add(RemoteFile f)
1415         {
1416         RemoteFile nlist[] = new RemoteFile[list.length+1];
1417         int offset = 0;
1418         for(int i=0; i<list.length; i++) {
1419                 if (list[i].name.compareTo(f.name) > 0 && offset == 0) {
1420                         nlist[i] = f;
1421                         offset++;
1422                         }
1423                 nlist[i+offset] = list[i];
1424                 }
1425         if (offset == 0) nlist[list.length] = f;
1426         list = nlist;
1427         }
1428
1429         void delete(RemoteFile f)
1430         {
1431         RemoteFile nlist[] = new RemoteFile[list.length-1];
1432         for(int i=0,j=0; i<list.length; i++)
1433                 if (list[i] != f)
1434                         nlist[j++] = list[i];
1435         list = nlist;
1436         }
1437
1438         boolean shared()
1439         {
1440         return type == DIR &&
1441                (parent.stab.get(path) != null ||
1442                 parent.ntab.get(path) != null);
1443         }
1444
1445         boolean mountpoint()
1446         {
1447         return type == DIR && fs() != null;
1448         }
1449
1450         boolean mounted()
1451         {
1452         FileSystem fs = fs();
1453         return type == DIR && fs != null && fs.mtab;
1454         }
1455
1456         FileSystem fs()
1457         {
1458         return (FileSystem)parent.mounts.get(path);
1459         }
1460
1461 }
1462
1463 class EditorWindow extends FixedFrame implements CbButtonCallback
1464 {
1465         TextField name;
1466         TextArea edit;
1467         CbButton save_b, saveclose_b, cancel_b, goto_b, find_b;
1468         Checkbox dosmode;
1469         RemoteFile file;
1470         FileManager filemgr;
1471         GotoWindow goto_window;
1472         FindReplaceWindow find_window;
1473
1474         // Editing an existing file
1475         EditorWindow(RemoteFile f, FileManager p)
1476         {
1477         super(800, 600);
1478         file = f; filemgr = p;
1479         makeUI(false);
1480         setTitle(filemgr.text("edit_title", file.path));
1481
1482         // Load the file
1483         try {
1484                 URL u = new URL(filemgr.getDocumentBase(),
1485                                 "show.cgi"+filemgr.urlize(file.path)+
1486                                 "?rand="+System.currentTimeMillis()+
1487                                 "&trust="+filemgr.trust+"&edit=1"+
1488                                 filemgr.extra);
1489                 URLConnection uc = u.openConnection();
1490                 filemgr.set_cookie(uc);
1491                 int len = uc.getContentLength();
1492                 InputStream is = uc.getInputStream();
1493                 byte buf[];
1494                 if (len >= 0) {
1495                         // Length is known
1496                         buf = new byte[uc.getContentLength()];
1497                         int got = 0;
1498                         while(got < buf.length)
1499                                 got += is.read(buf, got, buf.length-got);
1500                         }
1501                 else {
1502                         // Length is unknown .. read till the end
1503                         buf = new byte[0];
1504                         while(true) {
1505                             byte data[] = new byte[16384];
1506                             int got;
1507                             try { got = is.read(data); }
1508                             catch(EOFException ex) { break; }
1509                             if (got <= 0) break;
1510                             byte nbuf[] = new byte[buf.length + got];
1511                             System.arraycopy(buf, 0, nbuf, 0, buf.length);
1512                             System.arraycopy(data, 0, nbuf, buf.length, got);
1513                             buf = nbuf;
1514                             }
1515                         }
1516                 String s = new String(buf, 0);
1517                 if (s.indexOf("\r\n") != -1) {
1518                         dosmode.setState(true);
1519                         s = FileManager.replace_str(s, "\r\n", "\n");
1520                         }
1521                 edit.setText(s);
1522                 is.close();
1523                 file.size = buf.length;
1524                 }
1525         catch(Exception e) { e.printStackTrace(); }
1526         }
1527
1528         // Creating a new file
1529         EditorWindow(String f, FileManager p)
1530         {
1531         super(800, 600);
1532         filemgr = p;
1533         makeUI(true);
1534         setTitle(filemgr.text("edit_title2"));
1535         name.setText(f.equals("/") ? f : f+"/");
1536         name.select(name.getText().length(), name.getText().length());
1537         }
1538
1539         void makeUI(boolean add_name)
1540         {
1541         setLayout(new BorderLayout());
1542         if (add_name) {
1543                 Panel np = new Panel();
1544                 np.setLayout(new BorderLayout());
1545                 np.add("West", new Label(filemgr.text("edit_filename")));
1546                 np.add("Center", name = new TextField());
1547                 name.setFont(filemgr.fixed);
1548                 add("North", np);
1549                 }
1550         add("Center", edit = new TextArea(20, 80));
1551         edit.setFont(filemgr.fixed);
1552         Panel bot = new Panel();
1553         bot.setLayout(new FlowLayout(FlowLayout.RIGHT));
1554         bot.add(dosmode = new Checkbox("Windows newlines"));
1555         bot.add(goto_b = new CbButton(filemgr.get_image("goto.gif"),
1556                                       filemgr.text("edit_goto"),
1557                                       CbButton.LEFT, this));
1558         bot.add(find_b = new CbButton(filemgr.get_image("find.gif"),
1559                                       filemgr.text("edit_find"),
1560                                       CbButton.LEFT, this));
1561         bot.add(new Label(" "));
1562         bot.add(save_b = new CbButton(filemgr.get_image("save.gif"),
1563                                       filemgr.text("save"),
1564                                       CbButton.LEFT, this));
1565         bot.add(saveclose_b = new CbButton(filemgr.get_image("save.gif"),
1566                                       filemgr.text("edit_saveclose"),
1567                                       CbButton.LEFT, this));
1568         bot.add(cancel_b = new CbButton(filemgr.get_image("cancel.gif"),
1569                                         filemgr.text("close"),
1570                                         CbButton.LEFT, this));
1571         add("South", bot);
1572         Util.recursiveBody(this);
1573         pack();
1574         show();
1575         }
1576
1577         public void click(CbButton b)
1578         {
1579         if (b == save_b || b == saveclose_b) {
1580                 RemoteFile par = null, already = null;
1581                 String save_path;
1582                 if (file == null) {
1583                         // Locate the filemgr directory
1584                         save_path = filemgr.trim_path(name.getText());
1585                         int sl = save_path.lastIndexOf('/');
1586                         par = filemgr.find_directory(
1587                                         save_path.substring(0, sl), false);
1588                         if (par == null) return;
1589                         already = par.find(save_path.substring(sl+1));
1590                         if (already != null &&
1591                             (already.type == 0 || already.type == 5)) {
1592                                 new ErrorWindow(
1593                                         filemgr.text("edit_eover", save_path));
1594                                 return;
1595                                 }
1596                         }
1597                 else save_path = file.path;
1598
1599                 // Save the file back again
1600                 String s = edit.getText(), line;
1601                 s = FileManager.replace_str(s, "\r\n", "\n");
1602                 try {
1603                         if (dosmode.getState()) {
1604                                 // Convert to DOS newlines
1605                                 s = FileManager.replace_str(s, "\n", "\r\n");
1606                                 }
1607                         else {
1608                                 // Remove any DOS newlines
1609                                 s = FileManager.replace_str(s, "\r\n", "\n");
1610                                 }
1611                         URL u = new URL(filemgr.getDocumentBase(),
1612                                         "save.cgi"+filemgr.urlize(save_path)+
1613                                         "?rand="+System.currentTimeMillis()+
1614                                         "&trust="+filemgr.trust+
1615                                         "&length="+s.length()+
1616                                         filemgr.extra);
1617                         URLConnection uc = u.openConnection();
1618                         uc.setRequestProperty("Content-type", "text/plain");
1619                         filemgr.set_cookie(uc);
1620                         uc.setDoOutput(true);
1621                         OutputStream os = uc.getOutputStream();
1622                         byte buf[] = new byte[s.length()];
1623                         s.getBytes(0, buf.length, buf, 0);
1624                         os.write(buf);
1625                         os.close();
1626                         BufferedReader is =
1627                             new BufferedReader(new InputStreamReader(
1628                                 uc.getInputStream()));
1629                         String err = is.readLine();
1630                         if (err.length() > 0) {
1631                                 new ErrorWindow(
1632                                         filemgr.text("edit_esave", err));
1633                                 is.close();
1634                                 return;
1635                                 }
1636                         line = is.readLine();
1637                         is.close();
1638                         }
1639                 catch(Exception e) { e.printStackTrace(); return; }
1640
1641                 if (file == null) {
1642                         // Create and insert or replace the file object
1643                         file = new RemoteFile(filemgr, line, par);
1644                         if (already != null) {
1645                                 // A file with this name exists
1646                                 already.type = file.type;
1647                                 already.user = file.user;
1648                                 already.group = file.group;
1649                                 already.size = file.size;
1650                                 already.perms = file.perms;
1651                                 already.modified = file.modified;
1652                                 }
1653                         else {
1654                                 // Add to the list
1655                                 par.add(file);
1656                                 }
1657                         }
1658                 else {
1659                         file.size = s.length();
1660                         file.modified = System.currentTimeMillis();
1661                         }
1662                 filemgr.show_files(filemgr.showing_files);
1663                 if (b == saveclose_b)
1664                         dispose();
1665                 }
1666         else if (b == cancel_b) {
1667                 // Just close
1668                 dispose();
1669                 }
1670         else if (b == goto_b) {
1671                 // Open a dialog asking which line to go to
1672                 if (goto_window != null)
1673                         goto_window.toFront();
1674                 else
1675                         goto_window = new GotoWindow(this);
1676                 }
1677         else if (b == find_b) {
1678                 // Open the search (and replace) dialog
1679                 if (find_window != null)
1680                         find_window.toFront();
1681                 else
1682                         find_window = new FindReplaceWindow(this);
1683                 }
1684         }
1685
1686         public void dispose()
1687         {
1688         super.dispose();
1689         if (goto_window != null) goto_window.dispose();
1690         if (find_window != null) find_window.dispose();
1691         }
1692 }
1693
1694 class GotoWindow extends FixedFrame implements CbButtonCallback
1695 {
1696         EditorWindow editor;
1697         FileManager filemgr;
1698         TextField line;
1699         CbButton goto_b, cancel_b;
1700
1701         GotoWindow(EditorWindow e)
1702         {
1703         editor = e;
1704         filemgr = e.filemgr;
1705
1706         setLayout(new BorderLayout());
1707         add("West", new Label(filemgr.text("edit_gotoline")));
1708         add("Center", line = new TextField(10));
1709         line.setFont(filemgr.fixed);
1710         Panel bot = new Panel();
1711         bot.setLayout(new FlowLayout(FlowLayout.RIGHT));
1712         bot.add(goto_b = new CbButton(filemgr.get_image("goto.gif"),
1713                                       filemgr.text("edit_goto"),
1714                                       CbButton.LEFT, this));
1715         bot.add(cancel_b = new CbButton(filemgr.get_image("cancel.gif"),
1716                                         filemgr.text("close"),
1717                                         CbButton.LEFT, this));
1718         add("South", bot);
1719         Util.recursiveBody(this);
1720         pack();
1721         show();
1722         }
1723
1724         public void click(CbButton b)
1725         {
1726         if (b == goto_b) {
1727                 // Go to the chose line, if it exists
1728                 int lnum;
1729                 try { lnum = Integer.parseInt(line.getText()); }
1730                 catch(Exception e) { return; }
1731
1732                 String txt = editor.edit.getText();
1733                 int c, l = 0;
1734                 for(c=0; c<txt.length(); c++) {
1735                         if (txt.charAt(c) == '\n') {
1736                                 l++;
1737                                 if (l == lnum) {
1738                                         // Found the line!
1739                                         editor.edit.select(c, c);
1740                                         dispose();
1741                                         editor.edit.requestFocus();
1742                                         return;
1743                                         }
1744                                 }
1745                         }
1746                 }
1747         else if (b == cancel_b) {
1748                 // Just close the window
1749                 dispose();
1750                 }
1751         }
1752
1753         public void dispose()
1754         {
1755         super.dispose();
1756         editor.goto_window = null;
1757         }
1758
1759         public boolean handleEvent(Event e)
1760         {
1761         if (e.target == line && e.id == Event.KEY_RELEASE && e.key == 10) {
1762                 click(goto_b);
1763                 return true;
1764                 }
1765         return false;
1766         }
1767 }
1768
1769 class FindReplaceWindow extends FixedFrame implements CbButtonCallback
1770 {
1771         EditorWindow editor;
1772         FileManager filemgr;
1773         TextField find, replace;
1774         CbButton find_b, replace_b, all_b, cancel_b;
1775
1776         FindReplaceWindow(EditorWindow e)
1777         {
1778         editor = e;
1779         filemgr = e.filemgr;
1780         setLayout(new BorderLayout());
1781
1782         Panel left = new Panel();
1783         left.setLayout(new GridLayout(2, 1));
1784         left.add(new Label(filemgr.text("edit_searchfor")));
1785         left.add(new Label(filemgr.text("edit_replaceby")));
1786         add("West", left);
1787
1788         Panel right = new Panel();
1789         right.setLayout(new GridLayout(2, 1));
1790         right.add(find = new TextField(40));
1791         find.setFont(filemgr.fixed);
1792         right.add(replace = new TextField(40));
1793         replace.setFont(filemgr.fixed);
1794         add("Center", right);
1795
1796         Panel bot = new Panel();
1797         bot.setLayout(new FlowLayout(FlowLayout.RIGHT));
1798         bot.add(find_b = new CbButton(filemgr.get_image("find.gif"),
1799                                       filemgr.text("edit_find"),
1800                                       CbButton.LEFT, this));
1801         bot.add(replace_b = new CbButton(filemgr.get_image("replace.gif"),
1802                                       filemgr.text("edit_replace"),
1803                                       CbButton.LEFT, this));
1804         bot.add(all_b = new CbButton(filemgr.get_image("all.gif"),
1805                                       filemgr.text("edit_all"),
1806                                       CbButton.LEFT, this));
1807         bot.add(cancel_b = new CbButton(filemgr.get_image("cancel.gif"),
1808                                         filemgr.text("close"),
1809                                         CbButton.LEFT, this));
1810         add("South", bot);
1811         Util.recursiveBody(this);
1812         pack();
1813         show();
1814         }
1815
1816         public void click(CbButton b)
1817         {
1818         String findtxt = find.getText();
1819         String edittxt = editor.edit.getText();
1820         if (findtxt.length() == 0)
1821                 return;
1822         if (b == find_b) {
1823                 // Find the next occurrance of the text, starting from
1824                 // the cursor + 1, and select it
1825                 int pos = edittxt.indexOf(findtxt,
1826                                            editor.edit.getSelectionStart()+1);
1827                 if (pos < 0) {
1828                         // Not found .. but try wrap-around
1829                         pos = edittxt.indexOf(findtxt, 0);
1830                         }
1831                 if (pos < 0)
1832                         new ErrorWindow(filemgr.text("edit_notfound", findtxt));
1833                 else {
1834                         editor.edit.select(pos, pos+findtxt.length());
1835                         editor.edit.requestFocus();
1836                         }
1837                 }
1838         else if (b == replace_b) {
1839                 // If the word to search for is selected, replace it. Otherwise
1840                 // just search for the next one
1841                 int st = editor.edit.getSelectionStart(),
1842                     en = editor.edit.getSelectionEnd();
1843                 if (st >= 0) {
1844                         String sel = edittxt.substring(st, en);
1845                         if (sel.equals(findtxt)) {
1846                                 // Replace the selected
1847                                 editor.edit.setText(edittxt.substring(0, st)+
1848                                                     replace.getText()+
1849                                                     edittxt.substring(en));
1850                                 editor.edit.select(st, st);
1851                                 return;
1852                                 }
1853                         }
1854                 click(find_b);
1855                 }
1856         else if (b == all_b) {
1857                 // Replace all occurrances of the text in the editor
1858                 int pos = 0;
1859                 int len = findtxt.length();
1860                 int st = editor.edit.getSelectionStart(),
1861                     en = editor.edit.getSelectionEnd();
1862                 while((pos = edittxt.indexOf(findtxt, pos)) != -1) {
1863                         edittxt = edittxt.substring(0, pos)+
1864                                   replace.getText()+
1865                                   edittxt.substring(pos+len);
1866                         pos += len;
1867                         }
1868                 editor.edit.setText(edittxt);
1869                 editor.edit.select(st, en);     // put back old selection
1870                 }
1871         else if (b == cancel_b) {
1872                 // Just close the window
1873                 dispose();
1874                 }
1875         }
1876
1877         public void dispose()
1878         {
1879         super.dispose();
1880         editor.find_window = null;
1881         }
1882 }
1883
1884 class PropertiesWindow extends FixedFrame implements CbButtonCallback
1885 {
1886         RemoteFile file;
1887         FileManager filemgr;
1888         CbButton save_b, cancel_b, size_b;
1889
1890         TextField linkto;
1891         TextField user, group;
1892         Checkbox setuid, setgid;
1893         PermissionsPanel user_p, group_p, other_p;
1894         Checkbox sticky;
1895         Choice rec_mode;
1896         TextField octal;
1897
1898         TextField bytes, files, dirs;
1899
1900         PropertiesWindow(RemoteFile f, FileManager p)
1901         {
1902         file = f;
1903         filemgr = p;
1904
1905         // Create UI
1906         setTitle(f.path);
1907         setLayout(new BorderLayout());
1908         Panel bot = new Panel();
1909         bot.setLayout(new FlowLayout(FlowLayout.RIGHT));
1910         if (file.type == 0) {
1911                 bot.add(size_b = new CbButton(filemgr.get_image("refresh.gif"),
1912                                               filemgr.text("info_getsize"),
1913                                               CbButton.LEFT, this));
1914                 }
1915         if (filemgr.can_perms || filemgr.can_users) {
1916                 bot.add(save_b = new CbButton(filemgr.get_image("save.gif"),
1917                                               filemgr.text("save"),
1918                                               CbButton.LEFT, this));
1919                 }
1920         bot.add(cancel_b = new CbButton(filemgr.get_image("cancel.gif"),
1921                                         filemgr.text("cancel"),
1922                                         CbButton.LEFT, this));
1923         add("South", bot);
1924
1925         Panel mid = new Panel();
1926         mid.setLayout(new BorderLayout());
1927         TabbedPanel tab = null;
1928         add("Center", mid);
1929
1930         // Create file details section
1931         Panel det = new LinedPanel(filemgr.text("info_file")),
1932               dl = new Panel(), dr = new Panel();
1933         setup_leftright(det, dl, dr);
1934         add_item(filemgr.text("info_path"),
1935                 new Label(file.path), dl, dr);
1936         add_item(filemgr.text("info_type"),
1937                 new Label(filemgr.text("file_type"+file.type)), dl, dr);
1938         add_item(filemgr.text("info_size"),
1939                 new Label(String.valueOf(file.size)),dl,dr);
1940         add_item(filemgr.text("info_mod"),
1941                 new Label(String.valueOf(new Date(file.modified))), dl, dr);
1942         if (file.type == 5) {
1943                 add_item(filemgr.text("info_link"),
1944                          linkto = new TextField(file.linkto, 30), dl, dr);
1945                 linkto.setFont(filemgr.fixed);
1946                 }
1947         mid = add_panel(mid, det);
1948
1949         if (filemgr.can_perms) {
1950                 // Create permissions section
1951                 Panel per = new LinedPanel(filemgr.text("info_perms")),
1952                       pl = new Panel(), pr = new Panel();
1953                 setup_leftright(per, pl, pr);
1954                 add_item(filemgr.text("info_user"),
1955                     user_p = new PermissionsPanel(file, 64, filemgr), pl, pr);
1956                 add_item(filemgr.text("info_group"),
1957                     group_p = new PermissionsPanel(file, 8, filemgr), pl, pr);
1958                 add_item(filemgr.text("info_other"),
1959                     other_p = new PermissionsPanel(file, 1, filemgr), pl,pr);
1960                 if (file.type == 0) {
1961                         add_item(filemgr.text("info_sticky"),
1962                             sticky = new Checkbox(filemgr.text("info_sticky2")),
1963                             pl,pr);
1964                         sticky.setState((file.perms&01000) != 0);
1965                         }
1966                 add_item(filemgr.text("info_octal"),
1967                          octal = new TextField(4), pl, pr);
1968                 octal.setFont(filemgr.fixed);
1969                 octal.setEditable(false);
1970                 mid = add_panel(mid, per);
1971                 }
1972
1973         if (filemgr.can_users) {
1974                 // Create ownership section
1975                 Panel own = new LinedPanel(filemgr.text("info_own")),
1976                       ol = new Panel(), or = new Panel();
1977                 setup_leftright(own, ol, or);
1978                 add_item(filemgr.text("info_user"),
1979                          user = new TextField(file.user, 10), ol, or);
1980                 user.setFont(filemgr.fixed);
1981                 if (file.type != 0) {
1982                         add_item(filemgr.text("info_setuid"),
1983                             setuid = new Checkbox(filemgr.text("info_setuid2")),
1984                             ol, or);
1985                         setuid.setState((file.perms & 0x800) != 0);
1986                         }
1987                 add_item(filemgr.text("info_group"),
1988                          group = new TextField(file.group, 10), ol, or);
1989                 group.setFont(filemgr.fixed);
1990                 if (file.type == 0)
1991                         add_item(filemgr.text("info_setgid"),
1992                           setgid = new Checkbox(filemgr.text("info_setgid2")),
1993                           ol, or);
1994                 else
1995                         add_item(filemgr.text("info_setgid"),
1996                           setgid = new Checkbox(filemgr.text("info_setgid3")),
1997                           ol, or);
1998                 setgid.setState((file.perms & 0x400) != 0);
1999                 mid = add_panel(mid, own);
2000                 }
2001
2002         if (file.type == 0) {
2003                 // Create directory size section, initially empty
2004                 Panel szp = new LinedPanel(filemgr.text("info_sizeheader")),
2005                       sl = new Panel(), sr = new Panel();
2006                 setup_leftright(szp, sl, sr);
2007                 add_item(filemgr.text("info_bytes"),
2008                          bytes = new TextField("", 10), sl, sr);
2009                 bytes.setFont(filemgr.fixed);
2010                 bytes.setEditable(false);
2011                 add_item(filemgr.text("info_files"),
2012                          files = new TextField("", 10), sl, sr);
2013                 files.setFont(filemgr.fixed);
2014                 files.setEditable(false);
2015                 add_item(filemgr.text("info_dirs"),
2016                          dirs = new TextField("", 10), sl, sr);
2017                 dirs.setFont(filemgr.fixed);
2018                 dirs.setEditable(false);
2019                 mid = add_panel(mid, szp);
2020                 }
2021
2022         if (file.type == 0 && (filemgr.can_perms || filemgr.can_users)) {
2023                 // Create recursion section
2024                 Panel rec = new LinedPanel(filemgr.text("info_apply"));
2025                 rec.setLayout(new BorderLayout());
2026                 rec_mode = new Choice();
2027                 for(int i=1; i<=3; i++)
2028                         rec_mode.addItem(filemgr.text("info_apply"+i));
2029                 rec.add("Center", rec_mode);
2030                 mid = add_panel(mid, rec);
2031                 }
2032
2033         set_octal();
2034         Util.recursiveBody(this);
2035         pack();
2036         show();
2037         }
2038
2039         Panel add_panel(Panel p, Component c)
2040         {
2041         p.add("North", c);
2042         Panel np = new Panel();
2043         np.setLayout(new BorderLayout());
2044         p.add("Center", np);
2045         return np;
2046         }
2047
2048         public void click(CbButton b)
2049         {
2050         if (b == save_b) {
2051                 // Update the file
2052                 int perms = get_perms();
2053                 String user_str = user != null ? user.getText() : null;
2054                 String group_str = group != null ? group.getText() : null;
2055                 int rec = 0;
2056                 if (file.type == 0 && rec_mode != null)
2057                         rec = rec_mode.getSelectedIndex();
2058                 String rv[] = filemgr.get_text(
2059                         "chmod.cgi?path="+filemgr.urlize(file.path)+
2060                         (perms < 0 ? "" : "&perms="+perms)+
2061                         (user_str == null ? "" :
2062                                 "&user="+filemgr.urlize(user_str))+
2063                         (group_str == null ? "" :
2064                                 "&group="+filemgr.urlize(group_str))+
2065                         "&rec="+rec+
2066                         (linkto==null ? "" :
2067                                 "&linkto="+filemgr.urlize(linkto.getText())));
2068                 if (rv[0].length() > 0) {
2069                         // Something went wrong
2070                         new ErrorWindow(filemgr.text("info_efailed",
2071                                         file.path, rv[0]));
2072                         }
2073                 else {
2074                         // Update all changed file objects
2075                         if (linkto != null)
2076                                 file.linkto = linkto.getText();
2077                         else if (rec == 0)
2078                                 update_file(file, perms, false);
2079                         else if (rec == 1) {
2080                                 // Update files in this directory
2081                                 update_file(file, perms, false);
2082                                 recurse_files(file, perms, false);
2083                                 }
2084                         else if (rec == 2) {
2085                                 // Update files and subdirs
2086                                 update_file(file, perms, false);
2087                                 recurse_files(file, perms, true);
2088                                 }
2089
2090                         // Update directory list
2091                         int os = filemgr.files.selected();
2092                         filemgr.show_files(filemgr.showing_files);
2093                         filemgr.files.select(os);
2094                         dispose();
2095                         }
2096                 }
2097         else if (b == size_b) {
2098                 // Get the size of the directory recursively
2099                 String l[] = filemgr.get_text("size.cgi?dir="+
2100                                               filemgr.urlize(file.path));
2101                 if (l[0].length() > 0) {
2102                         new ErrorWindow(filemgr.text("info_size", l[0]));
2103                         }
2104                 StringTokenizer tok = new StringTokenizer(l[1], " ");
2105                 String bytes_str = tok.nextToken();
2106                 files.setText(tok.nextToken());
2107                 dirs.setText(tok.nextToken());
2108                 bytes.setText(tok.nextToken()+" "+tok.nextToken());
2109                 }
2110         else {
2111                 // Just close
2112                 dispose();
2113                 }
2114         }
2115
2116         void update_file(RemoteFile f, int perms, boolean perms_only)
2117         {
2118         f.user = user.getText();
2119         f.group = group.getText();
2120         if (perms_only)
2121                 f.perms = (perms & 0777) | (f.perms & 037777777000);
2122         else
2123                 f.perms = perms;
2124         }
2125
2126         void recurse_files(RemoteFile f, int perms, boolean do_subs)
2127         {
2128         if (f.list == null) return;
2129         for(int i=0; i<f.list.length; i++) {
2130                 RemoteFile ff = f.list[i];
2131                 if (ff.type == 5) continue;
2132                 else if (ff.type == 0) {
2133                         if (do_subs) {
2134                                 update_file(ff, perms, false);
2135                                 recurse_files(ff, perms, true);
2136                                 }
2137                         }
2138                 else update_file(ff, perms, true);
2139                 }
2140         }
2141
2142         void setup_leftright(Panel m, Panel l, Panel r)
2143         {
2144         m.setLayout(new BorderLayout());
2145         Panel p = new Panel();
2146         p.setLayout(new BorderLayout());
2147         p.add("West", l);
2148         p.add("Center", r);
2149         l.setLayout(new GridLayout(0, 1));
2150         r.setLayout(new GridLayout(0, 1));
2151         m.add("North", p);
2152         }
2153
2154         void add_item(String t, Component c, Panel l, Panel r)
2155         {
2156         l.add(new Label(t));
2157         Panel p = new Panel();
2158         p.setLayout(new BorderLayout());
2159         p.add("West", c);
2160         r.add(p);
2161         }
2162
2163         void set_octal()
2164         {
2165         if (octal != null) {
2166                 String oct = Integer.toOctalString(get_perms());
2167                 while(oct.length() < 4)
2168                         oct = "0"+oct;
2169                 octal.setText(oct);
2170                 }
2171         }
2172
2173         int get_perms()
2174         {
2175         if (user_p == null)
2176                 return -1;              // Cannot edit
2177         int perms = 0;
2178         if (setuid == null)
2179                 perms |= (file.perms & 0x800);
2180         else
2181                 perms |= (setuid.getState() ? 0x800 : 0);
2182         perms |= (setgid.getState() ? 0x400 : 0);
2183         perms |= user_p.getPerms();
2184         perms |= group_p.getPerms();
2185         perms |= other_p.getPerms();
2186         if (sticky == null)
2187                 perms |= (file.perms & 01000);
2188         else
2189                 perms |= (sticky.getState() ? 01000 : 0);
2190         return perms;
2191         }
2192
2193         public boolean handleEvent(Event e)
2194         {
2195         if (e.target instanceof Checkbox) {
2196                 set_octal();
2197                 return true;
2198                 }
2199         return super.handleEvent(e);
2200         }
2201 }
2202
2203 class PermissionsPanel extends Panel
2204 {
2205         Checkbox read, write, exec;
2206         int base;
2207
2208         PermissionsPanel(RemoteFile file, int base, FileManager filemgr)
2209         {
2210         int perms = file.perms;
2211         this.base = base;
2212         setLayout(new GridLayout(1, 3));
2213         add(read = new Checkbox(filemgr.text("info_read")));
2214         read.setState((perms&(base<<2)) != 0);
2215         add(write = new Checkbox(filemgr.text("info_write")));
2216         write.setState((perms&(base<<1)) != 0);
2217         add(exec = new Checkbox(
2218                 filemgr.text(file.type == RemoteFile.DIR ? "info_list"
2219                                                          : "info_exec")));
2220         exec.setState((perms&base) != 0);
2221         }
2222
2223         int getPerms()
2224         {
2225         int rv = 0;
2226         rv |= (read.getState() ? (base<<2) : 0);
2227         rv |= (write.getState() ? (base<<1) : 0);
2228         rv |= (exec.getState() ? base : 0);
2229         return rv;
2230         }
2231 }
2232
2233 class DeleteWindow extends FixedFrame implements CbButtonCallback
2234 {
2235         CbButton delete_b, cancel_b;
2236         FileManager filemgr;
2237         RemoteFile files[];
2238
2239         DeleteWindow(FileManager p, RemoteFile ff[])
2240         {
2241         filemgr = p;
2242         files = ff;
2243         setTitle(filemgr.text(ff.length > 1 ? "delete_mtitle" :
2244                               ff[0].type == 0 ? "delete_dtitle" :
2245                                                 "delete_ftitle"));
2246
2247         setLayout(new BorderLayout());
2248         if (ff.length > 1) {
2249                 add("North", new Label(filemgr.text("delete_mdesc")));
2250                 Panel mp = new Panel();
2251                 mp.setLayout(new GridLayout(ff.length, 1));
2252                 for(int i=0; i<ff.length; i++)
2253                         mp.add(new Label(ff[i].path));
2254                 add("Center", mp);
2255                 }
2256         else
2257                 add("Center", new MultiLabel(filemgr.text(
2258                         ff[0].type == 0 ? "delete_ddesc" : "delete_fdesc",
2259                         ff[0].path), 35));
2260         Panel bot = new Panel();
2261         bot.setLayout(new FlowLayout(FlowLayout.CENTER));
2262         bot.add(delete_b = new CbButton(filemgr.get_image("save.gif"),
2263                                         filemgr.text("delete"),
2264                                         CbButton.LEFT, this));
2265         bot.add(cancel_b = new CbButton(filemgr.get_image("cancel.gif"),
2266                                         filemgr.text("cancel"),
2267                                         CbButton.LEFT, this));
2268         add("South", bot);
2269         Util.recursiveBody(this);
2270         pack();
2271         show();
2272         }
2273
2274         public void click(CbButton b)
2275         {
2276         if (b == delete_b) {
2277                 // Delete the file or directory
2278                 boolean need_redraw = false, need_reshow = false;
2279                 for(int i=0; i<files.length; i++) {
2280                         RemoteFile file = files[i];
2281                         String rv[] = filemgr.get_text("delete.cgi?file="+
2282                                                filemgr.urlize(file.path));
2283                         if (rv[0].length() > 0) {
2284                                 new ErrorWindow(filemgr.text("delete_efailed",
2285                                                 file.path, rv[0]));
2286                                 break;
2287                                 }
2288                         else {
2289                                 // done the deed.. update data structures
2290                                 RemoteFile pf = file.directory;
2291                                 pf.delete(file);
2292                                 if (filemgr.showing_files == pf) {
2293                                         // Need to refresh the list as well..
2294                                         need_reshow = true;
2295                                         }
2296
2297                                 FileNode node = (FileNode)filemgr.nodemap.get(
2298                                                         file);
2299                                 FileNode pnode = (FileNode)filemgr.nodemap.get(
2300                                                         pf);
2301                                 if (node != null) {
2302                                         // Take the directory out of the tree..
2303                                         pnode.ch.removeElement(node);
2304                                         need_redraw = true;
2305                                         }
2306                                 }
2307                         }
2308                 if (need_reshow) filemgr.show_files(filemgr.showing_files);
2309                 if (need_redraw) filemgr.dirs.redraw();
2310                 dispose();
2311                 }
2312         else if (b == cancel_b)
2313                 dispose();
2314         }
2315 }
2316
2317 class MkdirWindow extends FixedFrame implements CbButtonCallback
2318 {
2319         FileManager filemgr;
2320         TextField dir;
2321         CbButton create_b, cancel_b;
2322
2323         MkdirWindow(String d, FileManager p)
2324         {
2325         filemgr = p;
2326         setTitle(filemgr.text("mkdir_title"));
2327         setLayout(new BorderLayout());
2328         add("West", new Label(filemgr.text("mkdir_dir")));
2329         add("Center", dir = new TextField(d.equals("/") ? "/" : d+"/", 40));
2330         dir.setFont(filemgr.fixed);
2331         dir.select(dir.getText().length(), dir.getText().length());
2332         Panel bot = new Panel();
2333         bot.setLayout(new FlowLayout(FlowLayout.CENTER));
2334         bot.add(create_b = new CbButton(filemgr.get_image("save.gif"),
2335                                         filemgr.text("create"),
2336                                         CbButton.LEFT, this));
2337         bot.add(cancel_b = new CbButton(filemgr.get_image("cancel.gif"),
2338                                         filemgr.text("cancel"),
2339                                         CbButton.LEFT, this));
2340         add("South", bot);
2341         Util.recursiveBody(this);
2342         pack();
2343         show();
2344         }
2345
2346         public void click(CbButton b)
2347         {
2348         if (b == create_b) {
2349                 // Find the filemgr directory
2350                 String path = dir.getText();
2351                 path = filemgr.trim_path(path);
2352                 int sl = path.lastIndexOf('/');
2353                 RemoteFile par = filemgr.find_directory(
2354                                         path.substring(0, sl), false);
2355                 if (par.find(path.substring(sl+1)) != null) {
2356                         new ErrorWindow(filemgr.text("mkdir_eexists", path));
2357                         return;
2358                         }
2359                 String rv[] = filemgr.get_text("mkdir.cgi?dir="+
2360                                                filemgr.urlize(path));
2361                 if (rv[0].length() > 0) {
2362                         new ErrorWindow(filemgr.text("mkdir_efailed", rv[0]));
2363                         return;
2364                         }
2365                 RemoteFile file = new RemoteFile(filemgr, rv[1], par);
2366                 par.add(file);
2367                 FileNode parnode = (FileNode)filemgr.nodemap.get(par);
2368                 if (parnode != null) {
2369                         // Update the tree
2370                         parnode.add(new FileNode(file));
2371                         filemgr.dirs.redraw();
2372                         }
2373                 filemgr.show_files(filemgr.showing_files);
2374                 dispose();
2375                 }
2376         else dispose();
2377         }
2378 }
2379
2380 class LinkWindow extends FixedFrame implements CbButtonCallback
2381 {
2382         FileManager filemgr;
2383         TextField from, to;
2384         CbButton create_b, cancel_b;
2385
2386         LinkWindow(String d, FileManager p)
2387         {
2388         filemgr = p;
2389         setLayout(new BorderLayout());
2390         setTitle(filemgr.text("link_title"));
2391         Panel l = new Panel(), r = new Panel();
2392         l.setLayout(new GridLayout(0, 1));
2393         l.add(new Label(filemgr.text("link_from")));
2394         l.add(new Label(filemgr.text("link_to")));
2395         r.setLayout(new GridLayout(0, 1));
2396         r.add(from = new TextField(d.equals("/") ? "/" : d+"/", 40));
2397         from.setFont(filemgr.fixed);
2398         from.select(from.getText().length(), from.getText().length());
2399         r.add(to = new TextField());
2400         to.setFont(filemgr.fixed);
2401         add("West", l); add("Center", r);
2402         Panel bot = new Panel();
2403         bot.setLayout(new FlowLayout(FlowLayout.CENTER));
2404         bot.add(create_b = new CbButton(filemgr.get_image("save.gif"),
2405                                         filemgr.text("create"),
2406                                         CbButton.LEFT, this));
2407         bot.add(cancel_b = new CbButton(filemgr.get_image("cancel.gif"),
2408                                         filemgr.text("cancel"),
2409                                         CbButton.LEFT, this));
2410         add("South", bot);
2411         Util.recursiveBody(this);
2412         pack();
2413         show();
2414         }
2415
2416         public void click(CbButton b)
2417         {
2418         if (b == create_b) {
2419                 // Check inputs
2420                 String from_str = from.getText().trim();
2421                 if (!from_str.startsWith("/")) {
2422                         new ErrorWindow(filemgr.text("link_efrom", from_str));
2423                         return;
2424                         }
2425                 int sl = from_str.lastIndexOf('/');
2426                 String par_str = from_str.substring(0, sl),
2427                        file_str = from_str.substring(sl+1);
2428                 RemoteFile par = filemgr.find_directory(par_str, false);
2429                 if (par == null) return;
2430                 if (par.find(file_str) != null) {
2431                         new ErrorWindow(filemgr.text("link_eexists", from_str));
2432                         return;
2433                         }
2434
2435                 // Create the actual link
2436                 String rv[] = filemgr.get_text("makelink.cgi?from="+
2437                                                filemgr.urlize(from_str)+"&to="+
2438                                                filemgr.urlize(to.getText()));
2439                 if (rv[0].length() > 0) {
2440                         new ErrorWindow(filemgr.text("link_efailed", rv[0]));
2441                         return;
2442                         }
2443                 RemoteFile file = new RemoteFile(filemgr, rv[1], par);
2444                 par.add(file);
2445                 filemgr.show_files(filemgr.showing_files);
2446                 dispose();
2447                 }
2448         else if (b == cancel_b)
2449                 dispose();
2450         }
2451 }
2452
2453 class RenameWindow extends FixedFrame implements CbButtonCallback
2454 {
2455         FileManager filemgr;
2456         RemoteFile file;
2457         TextField oldname, newname;
2458         CbButton rename_b, cancel_b;
2459
2460         RenameWindow(FileManager p, RemoteFile f)
2461         {
2462         filemgr = p; file = f;
2463         setLayout(new BorderLayout());
2464         setTitle(filemgr.text("rename_title", file.path));
2465         Panel l = new Panel(), r = new Panel();
2466         l.setLayout(new GridLayout(0, 1));
2467         l.add(new Label(filemgr.text("rename_old")));
2468         l.add(new Label(filemgr.text("rename_new")));
2469         r.setLayout(new GridLayout(0, 1));
2470         r.add(oldname = new TextField(file.name, 20));
2471         oldname.setEditable(false);
2472         oldname.setFont(filemgr.fixed);
2473         r.add(newname = new TextField(file.name, 20));
2474         newname.select(file.name.length(), file.name.length());
2475         newname.setFont(filemgr.fixed);
2476         add("West", l); add("Center", r);
2477
2478         Panel bot = new Panel();
2479         bot.setLayout(new FlowLayout(FlowLayout.CENTER));
2480         bot.add(rename_b = new CbButton(filemgr.get_image("save.gif"),
2481                                         filemgr.text("rename_ok"),
2482                                         CbButton.LEFT, this));
2483         bot.add(cancel_b = new CbButton(filemgr.get_image("cancel.gif"),
2484                                         filemgr.text("cancel"),
2485                                         CbButton.LEFT, this));
2486         add("South", bot);
2487         pack();
2488         show();
2489         Util.recursiveBody(this);
2490         }
2491
2492         public void click(CbButton b)
2493         {
2494         if (b == rename_b) {
2495                 // Work out destination file and directory
2496                 String newstr = newname.getText().trim();
2497                 if (newstr.length() == 0) return;
2498                 RemoteFile destdir;
2499                 String newpath;
2500                 if (newstr.indexOf('/') >= 0) {
2501                         // Different dir
2502                         if (newstr.startsWith("/")) {
2503                                 // Some absolute path
2504                                 newpath = newstr;
2505                                 }
2506                         else {
2507                                 // Relative to this dir
2508                                 newpath = file.directory.path+"/"+newstr;
2509                                 }
2510                         int sl = newpath.lastIndexOf('/');
2511                         String newdir = sl == 0 ? "/" : newpath.substring(0,sl);
2512                         destdir = filemgr.find_directory(newdir, false);
2513                         }
2514                 else {
2515                         // Same dir
2516                         destdir = file.directory;
2517                         int sl = file.path.lastIndexOf('/');
2518                         newpath = file.path.substring(0, sl)+"/"+newstr;
2519                         }
2520
2521                 // Work out filename only
2522                 int sl = newpath.lastIndexOf('/');
2523                 newstr = newpath.substring(sl+1);
2524
2525                 // Check for an existing file
2526                 RemoteFile already = destdir.find(newstr);
2527                 if (already != null) {
2528                         new ErrorWindow(filemgr.text("rename_eexists", newstr));
2529                         return;
2530                         }
2531
2532                 // Rename the real file
2533                 String rv[] = filemgr.get_text(
2534                                 "rename.cgi?old="+filemgr.urlize(file.path)+
2535                                 "&new="+filemgr.urlize(newpath));
2536                 if (rv[0].length() > 0) {
2537                         new ErrorWindow(filemgr.text("rename_efailed", rv[0]));
2538                         return;
2539                         }
2540
2541                 // Update data structure
2542                 file.name = newstr;
2543                 file.path = newpath;
2544                 file.directory.delete(file);
2545                 destdir.list();
2546                 destdir.add(file);
2547                 file.directory = destdir;
2548                 file.list = null;
2549                 FileNode parnode = (FileNode)filemgr.nodemap.get(file.directory);
2550                 FileNode filenode = (FileNode)filemgr.nodemap.get(file);
2551                 if (parnode != null && filenode != null) {
2552                         // Need to refresh tree
2553                         filenode.text = file.name;
2554                         parnode.ch.removeElement(filenode);
2555                         parnode.add(filenode);
2556                         dispose();
2557                         filemgr.dirs.redraw();
2558                         }
2559
2560                 filemgr.show_files(filemgr.showing_files);
2561                 dispose();
2562                 }
2563         else if (b == cancel_b)
2564                 dispose();
2565         }
2566 }
2567
2568 class OverwriteWindow extends FixedFrame implements CbButtonCallback
2569 {
2570         FileManager filemgr;
2571         RemoteFile src, already;
2572         TextField newname;
2573         CbButton ok, cancel;
2574         int idx;
2575         boolean mode;
2576
2577         OverwriteWindow(FileManager p, RemoteFile a, RemoteFile s, int i)
2578         {
2579         filemgr = p; src = s; already = a; idx = i;
2580         mode = filemgr.cut_mode;
2581         setLayout(new BorderLayout());
2582         setTitle(filemgr.text("over_title"));
2583         add("North",
2584             new MultiLabel(filemgr.text("over_msg", already.path), 30, 0));
2585         add("West", new Label(filemgr.text("over_new")));
2586         add("East", newname = new TextField(a.name, 30));
2587         newname.setFont(filemgr.fixed);
2588
2589         Panel bot = new Panel();
2590         bot.setLayout(new FlowLayout(FlowLayout.RIGHT));
2591         bot.add(ok = new CbButton(filemgr.get_image("save.gif"),
2592                                   filemgr.text("over_ok"),
2593                                   CbButton.LEFT, this));
2594         bot.add(cancel = new CbButton(filemgr.get_image("cancel.gif"),
2595                                   filemgr.text("cancel"),
2596                                   CbButton.LEFT, this));
2597         add("South", bot);
2598         Util.recursiveBody(this);
2599         pack();
2600         show();
2601         }
2602
2603         public void click(CbButton b)
2604         {
2605         if (b == cancel)
2606                 dispose();
2607         else if (b == ok && newname.getText().length() > 0) {
2608                 // paste the file, but with a new name
2609                 RemoteFile ap = already.directory;
2610                 RemoteFile newalready = ap.find(newname.getText());
2611                 if (newalready == src) {
2612                         new ErrorWindow(filemgr.text("paste_eself"));
2613                         return;
2614                         }
2615                 if (newalready != null && (newalready.type == 0 ||
2616                                            newalready.type == 5)) {
2617                         new ErrorWindow(
2618                                 filemgr.text("paste_eover", newalready.path));
2619                         return;
2620                         }
2621                 String dpath = (ap.path.equals("/") ? "/" :
2622                                 ap.path+"/")+newname.getText();
2623                 RemoteFile nf = filemgr.paste_file(src, already.directory,
2624                                                    dpath, newalready, mode);
2625                 if (filemgr.cut_mode && nf != null) {
2626                         // Paste from the destination path from now on
2627                         filemgr.cut_buffer[idx] = nf;
2628                         }
2629                 dispose();
2630                 }
2631         }
2632 }
2633
2634 class SambaShare
2635 {
2636         String path;
2637         boolean available;
2638         boolean writable;
2639         int guest;
2640         String comment;
2641
2642         SambaShare(String l)
2643         {
2644         StringSplitter tok = new StringSplitter(l, ':');
2645         path = tok.nextToken();
2646         available = tok.nextToken().equals("1");
2647         writable = tok.nextToken().equals("1");
2648         guest = Integer.parseInt(tok.nextToken());
2649         comment = tok.nextToken();
2650         }
2651
2652         SambaShare(String p, boolean a, boolean w, int g, String c)
2653         {
2654         path = p;
2655         available = a;
2656         writable = w;
2657         guest = g;
2658         comment = c;
2659         }
2660
2661         String params()
2662         {
2663         return "path="+FileManager.urlize(path)+
2664                "&available="+(available ? 1 : 0)+
2665                "&writable="+(writable ? 1 : 0)+
2666                "&guest="+guest+
2667                "&comment="+FileManager.urlize(comment);
2668         }
2669 }
2670
2671 class DFSAdminExport
2672 {
2673         String path;
2674         String desc;
2675         String ro, rw, root;
2676
2677         DFSAdminExport(String l)
2678         {
2679         StringSplitter tok = new StringSplitter(l, ':');
2680         path = tok.nextToken();
2681         ro = tok.nextToken();
2682         rw = tok.nextToken();
2683         root = tok.nextToken();
2684         desc = tok.nextToken();
2685         }
2686
2687         DFSAdminExport(String p, String d, String ro, String rw, String root)
2688         {
2689         path = p;
2690         desc = d;
2691         this.ro = ro;
2692         this.rw = rw;
2693         this.root = root;
2694         }
2695
2696         static String[] split(String s)
2697         {
2698         StringTokenizer stok = new StringTokenizer(s, " ");
2699         String rv[] = new String[stok.countTokens()];
2700         for(int i=0; i<rv.length; i++)
2701                 rv[i] = stok.nextToken();
2702         return rv;
2703         }
2704
2705         String params()
2706         {
2707         return "path="+FileManager.urlize(path)+
2708                "&ro="+FileManager.urlize(ro)+
2709                "&rw="+FileManager.urlize(rw)+
2710                "&root="+FileManager.urlize(root)+
2711                "&desc="+FileManager.urlize(desc);
2712         }
2713 }
2714
2715 class LinuxExport
2716 {
2717         String path;
2718         String host[];
2719         boolean ro[];
2720         int squash[];
2721
2722         LinuxExport(String l)
2723         {
2724         StringSplitter tok = new StringSplitter(l, ':');
2725         path = tok.nextToken();
2726         int c = tok.countTokens() / 3;
2727         host = new String[c];
2728         ro = new boolean[c];
2729         squash = new int[c];
2730         for(int i=0; tok.hasMoreTokens(); i++) {
2731                 host[i] = tok.nextToken();
2732                 ro[i] = tok.nextToken().equals("1");
2733                 squash[i] = Integer.parseInt(tok.nextToken());
2734                 }
2735         }
2736
2737         LinuxExport(String p, String h[], String r[], String s[])
2738         {
2739         path = p;
2740         }
2741
2742         String params()
2743         {
2744         String rv = "path="+FileManager.urlize(path)+
2745                     "&count="+host.length;
2746         for(int i=0; i<host.length; i++) {
2747                 rv += "&host"+i+"="+FileManager.urlize(host[i]);
2748                 rv += "&ro"+i+"="+(ro[i] ? 1 : 0);
2749                 rv += "&squash"+i+"="+squash[i];
2750                 }
2751         return rv;
2752         }
2753 }
2754
2755 class SharingWindow extends FixedFrame implements CbButtonCallback
2756 {
2757         CbButton save_b, cancel_b;
2758         RemoteFile file;
2759         FileManager filemgr;
2760         SambaShare sshare;
2761         DFSAdminExport dexport;
2762         LinuxExport lexport;
2763         Checkbox samba_on, samba_off;
2764         Checkbox writable_on, writable_off;
2765         Checkbox available_on, available_off;
2766         Checkbox guest_on, guest_off, guest_only;
2767         TextField comment;
2768
2769         TextField desc;
2770         Checkbox nfs_on, nfs_off;
2771         TextField rwhosts, rohosts, roothosts;
2772         Checkbox rw[] = new Checkbox[3], ro[] = new Checkbox[3],
2773                  root[] = new Checkbox[3];
2774
2775         TextField host[];
2776         Choice lro[], squash[];
2777
2778         SharingWindow(RemoteFile f, FileManager p)
2779         {
2780         file = f; filemgr = p;
2781         setTitle(filemgr.text("share_title", file.path));
2782         sshare = (SambaShare)filemgr.stab.get(file.path);
2783         Object nshare = filemgr.ntab.get(file.path);
2784         if (filemgr.nfsmode == 1)
2785                 lexport = (LinuxExport)nshare;
2786         else if (filemgr.nfsmode == 2)
2787                 dexport = (DFSAdminExport)nshare;
2788
2789         // setup UI
2790         setLayout(new BorderLayout());
2791         Panel samba = new Panel(), sl = new Panel(), sr = new Panel();
2792         samba.setLayout(new BorderLayout());
2793         Panel st = new Panel();
2794         st.setLayout(new GridLayout(2, 1));
2795         CheckboxGroup sg = new CheckboxGroup();
2796         st.add(samba_off = new Checkbox(filemgr.text("share_soff"), sg, 
2797                                        sshare == null));
2798         st.add(samba_on = new Checkbox(filemgr.text("share_son"), sg,
2799                                         sshare != null));
2800         samba.add("North", st);
2801
2802         Panel stop = new LinedPanel(filemgr.text("share_sheader"));
2803         setup_leftright(stop, sl, sr);
2804
2805         comment = new TextField(sshare == null ? "" : sshare.comment, 25);
2806         comment.setFont(filemgr.fixed);
2807         add_item(filemgr.text("share_comment"), comment, sl, sr);
2808
2809         Panel ap = new Panel();
2810         ap.setLayout(new GridLayout(1, 0));
2811         CheckboxGroup ag = new CheckboxGroup();
2812         ap.add(available_on = new Checkbox(filemgr.text("yes"), ag,
2813                                           sshare == null || sshare.available));
2814         ap.add(available_off = new Checkbox(filemgr.text("no"), ag,
2815                                           sshare != null && !sshare.available));
2816         add_item(filemgr.text("share_available"), ap, sl, sr);
2817
2818         Panel wp = new Panel();
2819         wp.setLayout(new GridLayout(1, 0));
2820         CheckboxGroup wg = new CheckboxGroup();
2821         wp.add(writable_on = new Checkbox(filemgr.text("yes"), wg,
2822                                           sshare == null || sshare.writable));
2823         wp.add(writable_off = new Checkbox(filemgr.text("no"), wg,
2824                                            sshare != null && !sshare.writable));
2825         add_item(filemgr.text("share_writable"), wp, sl, sr);
2826
2827         Panel gp = new Panel();
2828         gp.setLayout(new GridLayout(1, 0));
2829         CheckboxGroup gg = new CheckboxGroup();
2830         gp.add(guest_only = new Checkbox(filemgr.text("share_only"), gg,
2831                                 sshare != null && sshare.guest == 2));
2832         gp.add(guest_on = new Checkbox(filemgr.text("yes"), gg,
2833                                 sshare == null || sshare.guest == 1));
2834         gp.add(guest_off = new Checkbox(filemgr.text("no"), gg,
2835                                 sshare != null && sshare.guest == 0));
2836         add_item(filemgr.text("share_guest"), gp, sl, sr);
2837
2838         samba.add("Center", stop);
2839
2840         // Setup NFS UI
2841         Panel nfs = new Panel(), nl = new Panel(), nr = new Panel();
2842         nfs.setLayout(new BorderLayout());
2843         Panel nt = new Panel();
2844         nt.setLayout(new GridLayout(2, 1));
2845         CheckboxGroup ng = new CheckboxGroup();
2846         nt.add(nfs_off = new Checkbox(filemgr.text("share_noff"), ng, 
2847                                       nshare == null));
2848         nt.add(nfs_on = new Checkbox(filemgr.text("share_non"), ng,
2849                                      nshare != null));
2850         nfs.add("North", nt);
2851
2852         Panel ntop = new LinedPanel(filemgr.text("share_nheader"));
2853         setup_leftright(ntop, nl, nr);
2854         if (filemgr.nfsmode == 1) {
2855                 // Linux export mode
2856                 nl.setLayout(new GridLayout(0, 1, 2, 2));
2857                 nr.setLayout(new GridLayout(0, 1, 2, 2));
2858                 nl.add(new Label(filemgr.text("share_host")));
2859                 nr.add(new Label(filemgr.text("share_opts")));
2860                 int c = lexport==null ? 0 : lexport.host.length;
2861                 host = new TextField[c+1];
2862                 lro = new Choice[c+1];
2863                 squash = new Choice[c+1];
2864                 for(int i=0; i<c; i++) {
2865                         host[i] = new TextField(lexport.host[i], 20);
2866                         host[i].setFont(filemgr.fixed);
2867                         lro[i] = robox(lexport.ro[i]);
2868                         squash[i] = squashbox(lexport.squash[i]);
2869                         nl.add(host[i]);
2870                         nr.add(opts_panel(lro[i], squash[i]));
2871                         }
2872                 host[c] = new TextField("", 20);
2873                 host[c].setFont(filemgr.fixed);
2874                 lro[c] = robox(false);
2875                 squash[c] = squashbox(1);
2876                 nl.add(host[c]);
2877                 nr.add(opts_panel(lro[c], squash[c]));
2878                 }
2879         else if (filemgr.nfsmode == 2) {
2880                 // Solaris share mode
2881                 desc = new TextField(dexport == null ? "" : dexport.desc, 25);
2882                 desc.setFont(filemgr.fixed);
2883                 add_item(filemgr.text("share_desc"), desc, nl, nr);
2884
2885                 rohosts = add_hosts(filemgr.text("share_ro"),
2886                                     dexport == null ? "-" : dexport.ro,
2887                                     ro, nl, nr);
2888                 rwhosts = add_hosts(filemgr.text("share_rw"),
2889                                     dexport == null ? "-" : dexport.rw,
2890                                     rw, nl, nr);
2891                 roothosts = add_hosts(filemgr.text("share_root"),
2892                                     dexport == null ? "-" : dexport.root,
2893                                     root, nl, nr);
2894                 root[1].getParent().remove(root[1]);
2895                 }
2896         else if (filemgr.nfsmode == 3) {
2897                 }
2898         nfs.add("Center", ntop);
2899
2900         // Add the appropriate tabs
2901         if (filemgr.sambamode && filemgr.nfsmode != 0) {
2902                 TabbedPanel tab = new TabbedPanel();
2903                 tab.addItem(filemgr.text("share_samba"), samba);
2904                 tab.addItem(filemgr.text("share_nfs"), nfs);
2905                 add("Center", tab);
2906                 }
2907         else if (filemgr.sambamode)
2908                 add("Center", samba);
2909         else if (filemgr.nfsmode != 0)
2910                 add("Center", nfs);
2911
2912         // Create save and cancel buttons
2913         Panel bot = new Panel();
2914         bot.setLayout(new FlowLayout(FlowLayout.RIGHT));
2915         bot.add(save_b = new CbButton(filemgr.get_image("save.gif"),
2916                                       filemgr.text("save"),
2917                                       CbButton.LEFT, this));
2918         bot.add(cancel_b = new CbButton(filemgr.get_image("cancel.gif"),
2919                                         filemgr.text("cancel"),
2920                                         CbButton.LEFT, this));
2921         add("South", bot);
2922         Util.recursiveBody(this);
2923         pack();
2924         show();
2925         }
2926
2927         public void click(CbButton b)
2928         {
2929         if (b == save_b) {
2930                 // Update samba settings on server
2931                 if (sshare != null && samba_on.getState()) {
2932                         // Updating share
2933                         sshare.available = available_on.getState();
2934                         sshare.writable = writable_on.getState();
2935                         sshare.guest = guest_only.getState() ? 2 :
2936                                        guest_on.getState() ? 1 : 0;
2937                         sshare.comment = comment.getText();
2938                         String rv[] = filemgr.get_text(
2939                                 "save_share.cgi?"+sshare.params());
2940                         }
2941                 else if (sshare != null) {
2942                         // Deleting share
2943                         String rv[] = filemgr.get_text(
2944                                 "save_share.cgi?delete=1&"+sshare.params());
2945                         filemgr.stab.remove(sshare.path);
2946                         }
2947                 else if (samba_on.getState()) {
2948                         // Creating share
2949                         sshare = new SambaShare(file.path,
2950                                                 available_on.getState(),
2951                                                 writable_on.getState(),
2952                                                 guest_only.getState() ? 2 :
2953                                                 guest_on.getState() ? 1 : 0,
2954                                                 comment.getText());
2955                         filemgr.stab.put(sshare.path, sshare);
2956                         String rv[] = filemgr.get_text(
2957                                 "save_share.cgi?new=1&"+sshare.params());
2958                         }
2959
2960                 // Update NFS settings on server
2961                 if (filemgr.nfsmode == 1) {
2962                         if (lexport != null && nfs_on.getState()) {
2963                                 // Updating export
2964                                 export_options(lexport);
2965                                 String rv[] = filemgr.get_text(
2966                                         "save_export.cgi?"+lexport.params());
2967                                 }
2968                         else if (lexport != null) {
2969                                 // Deleting export
2970                                 String rv[] = filemgr.get_text(
2971                                   "save_export.cgi?delete=1&"+lexport.params());
2972                                 filemgr.ntab.remove(lexport.path);
2973                                 }
2974                         else if (nfs_on.getState()) {
2975                                 // Creating export
2976                                 lexport = new LinuxExport(file.path, null,
2977                                                           null, null);
2978                                 export_options(lexport);
2979                                 String rv[] = filemgr.get_text(
2980                                   "save_export.cgi?new=1&"+lexport.params());
2981                                 filemgr.ntab.put(lexport.path, lexport);
2982                                 }
2983                         }
2984                 else if (filemgr.nfsmode == 2) {
2985                         if (dexport != null && nfs_on.getState()) {
2986                                 // Updating share
2987                                 dexport.desc = desc.getText();
2988                                 dexport.ro = ro[0].getState() ? "-" :
2989                                              ro[1].getState() ? "" :
2990                                              rohosts.getText();
2991                                 dexport.rw = rw[0].getState() ? "-" :
2992                                              rw[1].getState() ? "" :
2993                                              rwhosts.getText();
2994                                 dexport.root = root[0].getState() ? "-" :
2995                                                roothosts.getText();
2996                                 String rv[] = filemgr.get_text(
2997                                         "save_export.cgi?"+dexport.params());
2998                                 }
2999                         else if (dexport != null) {
3000                                 // Deleting share
3001                                 String rv[] = filemgr.get_text(
3002                                   "save_export.cgi?delete=1&"+dexport.params());
3003                                 filemgr.ntab.remove(dexport.path);
3004                                 }
3005                         else if (nfs_on.getState()) {
3006                                 // Creating new share
3007                                 dexport = new DFSAdminExport(file.path,
3008                                         desc.getText(),
3009                                         ro[0].getState() ? "-" :
3010                                         ro[1].getState() ? "" :
3011                                         rohosts.getText(),
3012                                         rw[0].getState() ? "-" :
3013                                         rw[1].getState() ? "" :
3014                                         rwhosts.getText(),
3015                                         root[0].getState() ? "-" :
3016                                         roothosts.getText());
3017                                 String rv[] = filemgr.get_text(
3018                                     "save_export.cgi?new=1&"+dexport.params());
3019                                 filemgr.ntab.put(dexport.path, dexport);
3020                                 }
3021                         }
3022                 else if (filemgr.nfsmode == 3) {
3023                         }
3024
3025                 filemgr.show_files(filemgr.showing_files);
3026                 dispose();
3027                 }
3028         else if (b == cancel_b)
3029                 dispose();
3030         }
3031
3032         void setup_leftright(Panel m, Panel l, Panel r)
3033         {
3034         m.setLayout(new BorderLayout());
3035         Panel p = new Panel();
3036         p.setLayout(new BorderLayout());
3037         p.add("West", l);
3038         p.add("Center", r);
3039         l.setLayout(new GridLayout(0, 1));
3040         r.setLayout(new GridLayout(0, 1));
3041         m.add("North", p);
3042         }
3043
3044         void add_item(String t, Component c, Panel l, Panel r)
3045         {
3046         l.add(new Label(t));
3047         Panel p = new Panel();
3048         p.setLayout(new BorderLayout());
3049         p.add("West", c);
3050         r.add(p);
3051         }
3052
3053         TextField add_hosts(String name, String v, Checkbox cb[],
3054                             Panel l, Panel r)
3055         {
3056         Panel p = new Panel();
3057         p.setLayout(new GridLayout(1, 3));
3058         CheckboxGroup g = new CheckboxGroup();
3059         p.add(cb[0] = new Checkbox(filemgr.text("share_none"), g,
3060                                    v.equals("-")));
3061         p.add(cb[1] = new Checkbox(filemgr.text("share_all"), g,
3062                                    v.length() == 0));
3063         p.add(cb[2] = new Checkbox(filemgr.text("share_listed"), g,
3064                                    v.length() > 1));
3065         add_item(name, p, l, r);
3066         TextField t = new TextField(v.equals("-") ? "" : v, 25);
3067         t.setFont(filemgr.fixed);
3068         add_item("", t, l, r);
3069         return t;
3070         }
3071
3072         Choice squashbox(int s)
3073         {
3074         Choice rv = new Choice();
3075         rv.addItem(filemgr.text("share_s0"));
3076         rv.addItem(filemgr.text("share_s1"));
3077         rv.addItem(filemgr.text("share_s2"));
3078         rv.select(s);
3079         return rv;
3080         }
3081
3082         Choice robox(boolean r)
3083         {
3084         Choice rv = new Choice();
3085         rv.addItem(filemgr.text("share_lrw"));
3086         rv.addItem(filemgr.text("share_lro"));
3087         rv.select(r ? 1 : 0);
3088         return rv;
3089         }
3090
3091         Panel opts_panel(Component ro, Component squash)
3092         {
3093         Panel p = new Panel();
3094         p.setLayout(new BorderLayout());
3095         p.add("West", ro);
3096         p.add("East", squash);
3097         return p;
3098         }
3099
3100         void export_options(LinuxExport e)
3101         {
3102         int c = 0;
3103         for(int i=0; i<host.length; i++)
3104                 if (host[i].getText().length() > 0)
3105                         c++;
3106         e.host = new String[c];
3107         e.ro = new boolean[c];
3108         e.squash = new int[c];
3109         for(int i=0,j=0; i<host.length; i++) {
3110                 if (host[i].getText().trim().length() > 0) {
3111                         e.host[j] = host[i].getText();
3112                         e.ro[j] = lro[i].getSelectedIndex() == 1;
3113                         e.squash[j] = squash[i].getSelectedIndex();
3114                         j++;
3115                         }
3116                 }
3117         }
3118
3119 }
3120
3121 class SearchWindow extends FixedFrame
3122         implements CbButtonCallback,MultiColumnCallback
3123 {
3124         TabbedPanel tab;
3125         MultiColumn list;
3126         CbButton search_b, cancel_b, down_b;
3127         FileManager filemgr;
3128         TextField dir, match, user, group;
3129         Checkbox uany, usel, gany, gsel;
3130         Choice type;
3131         Checkbox sany, smore, sless;
3132         TextField more, less;
3133         Checkbox xon, xoff;
3134         String types[] = { "", "f", "d", "l", "p" };
3135         TextField cont;
3136         RemoteFile results[];
3137
3138         SearchWindow(String d, FileManager p)
3139         {
3140         filemgr = p;
3141         setTitle(filemgr.text("search_title"));
3142
3143         // setup UI
3144         setLayout(new BorderLayout());
3145         tab = new TabbedPanel();
3146         Panel search = new Panel();
3147         search.setLayout(new BorderLayout());
3148         tab.addItem(filemgr.text("search_crit"), search);
3149         Panel l = new Panel(), r = new Panel();
3150         l.setLayout(new GridLayout(0, 1));
3151         r.setLayout(new GridLayout(0, 1));
3152
3153         String cols[] = { "", filemgr.text("right_name"),
3154                           filemgr.text("right_size") };
3155         float widths[] = { .07f, .78f, .15f };
3156         list = new MultiColumn(cols, this);
3157         list.setWidths(widths);
3158         list.setDrawLines(false);
3159         list.setFont(filemgr.fixed);
3160         tab.addItem(filemgr.text("search_list"), list);
3161
3162         add_item(filemgr.text("search_dir"), dir = new TextField(d, 30), l, r);
3163         dir.setFont(filemgr.fixed);
3164
3165         // Filename
3166         add_item(filemgr.text("search_match"), match = new TextField(20), l, r);
3167         match.setFont(filemgr.fixed);
3168
3169         if (filemgr.search_contents) {
3170                 // File contents
3171                 add_item(filemgr.text("search_cont"),
3172                          cont = new TextField(30), l, r);
3173                 cont.setFont(filemgr.fixed);
3174                 }
3175
3176         // User or group owners
3177         if (filemgr.can_users) {
3178                 Panel up = new Panel();
3179                 up.setLayout(new FlowLayout(FlowLayout.LEFT, 1, 1));
3180                 CheckboxGroup ug = new CheckboxGroup();
3181                 up.add(uany = new Checkbox(filemgr.text("search_any"), ug, true));
3182                 up.add(usel = new Checkbox("", ug, false));
3183                 up.add(user = new TextField(10));
3184                 user.setFont(filemgr.fixed);
3185                 add_item(filemgr.text("search_user"), up, l, r);
3186
3187                 Panel gp = new Panel();
3188                 gp.setLayout(new FlowLayout(FlowLayout.LEFT, 1, 1));
3189                 CheckboxGroup gg = new CheckboxGroup();
3190                 gp.add(gany = new Checkbox(filemgr.text("search_any"), gg, true));
3191                 gp.add(gsel = new Checkbox("", gg, false));
3192                 gp.add(group = new TextField(10));
3193                 group.setFont(filemgr.fixed);
3194                 add_item(filemgr.text("search_group"), gp, l, r);
3195                 }
3196
3197         // File type
3198         if (!filemgr.follow_links) {
3199                 type = new Choice();
3200                 for(int i=0; i<types.length; i++)
3201                         type.addItem(filemgr.text("search_types_"+types[i]));
3202                 add_item(filemgr.text("search_type"), type, l, r);
3203                 }
3204
3205         // File size
3206         CheckboxGroup sg = new CheckboxGroup();
3207         add_item(filemgr.text("search_size"),
3208                  sany = new Checkbox(filemgr.text("search_any"), sg, true),
3209                  l, r);
3210         Panel mp = new Panel();
3211         mp.setLayout(new FlowLayout(FlowLayout.LEFT, 1, 1));
3212         mp.add(smore = new Checkbox(filemgr.text("search_more"), sg, false));
3213         mp.add(more = new TextField(10));
3214         more.setFont(filemgr.fixed);
3215         add_item("", mp, l, r);
3216         Panel lp = new Panel();
3217         lp.setLayout(new FlowLayout(FlowLayout.LEFT, 1, 1));
3218         lp.add(sless = new Checkbox(filemgr.text("search_less"), sg, false));
3219         lp.add(less = new TextField(10));
3220         less.setFont(filemgr.fixed);
3221         add_item("", lp, l, r);
3222
3223         if (filemgr.got_filesystems) {
3224                 // Search past mounts
3225                 CheckboxGroup xg = new CheckboxGroup();
3226                 Panel xp = new Panel();
3227                 xp.setLayout(new FlowLayout(FlowLayout.LEFT, 1, 1));
3228                 xp.add(xoff = new Checkbox(filemgr.text("yes"), xg, true));
3229                 xp.add(xon = new Checkbox(filemgr.text("no"), xg, false));
3230                 add_item(filemgr.text("search_xdev"), xp, l, r);
3231                 }
3232
3233         search.add("West", l); search.add("East", r);
3234         add("Center", tab);
3235
3236         // Create search and cancel buttons
3237         Panel bot = new Panel();
3238         bot.setLayout(new FlowLayout(FlowLayout.RIGHT));
3239         bot.add(down_b = new CbButton(filemgr.get_image("down.gif"),
3240                                       filemgr.text("search_down"),
3241                                       CbButton.LEFT, this));
3242         bot.add(search_b = new CbButton(filemgr.get_image("save.gif"),
3243                                       filemgr.text("search_ok"),
3244                                       CbButton.LEFT, this));
3245         bot.add(cancel_b = new CbButton(filemgr.get_image("cancel.gif"),
3246                                         filemgr.text("cancel"),
3247                                         CbButton.LEFT, this));
3248         add("South", bot);
3249         Util.recursiveBody(this);
3250         pack();
3251         show();
3252         }
3253
3254         void add_item(String t, Component c, Panel l, Panel r)
3255         {
3256         l.add(new Label(t));
3257         Panel p = new Panel();
3258         p.setLayout(new BorderLayout());
3259         p.add("West", c);
3260         r.add(p);
3261         }
3262
3263         public void click(CbButton b)
3264         {
3265         if (b == cancel_b)
3266                 dispose();
3267         else if (b == search_b) {
3268                 // validate inputs and build search URL
3269                 String url = "search.cgi";
3270                 String d = dir.getText().trim();
3271                 if (d.length() == 0 || d.charAt(0) != '/') {
3272                         new ErrorWindow(filemgr.text("search_edir"));
3273                         return;
3274                         }
3275                 url += "?dir="+filemgr.urlize(d);
3276                 String mt = match.getText().trim();
3277                 if (mt.length() == 0) {
3278                         mt = "*";
3279                         //new ErrorWindow(filemgr.text("search_ematch"));
3280                         //return;
3281                         }
3282                 url += "&match="+filemgr.urlize(mt);
3283                 if (type != null && type.getSelectedIndex() > 0)
3284                         url += "&type="+types[type.getSelectedIndex()];
3285                 if (usel != null && usel.getState()) {
3286                         String u = user.getText().trim();
3287                         if (u.length() == 0) {
3288                                 new ErrorWindow(filemgr.text("search_euser"));
3289                                 return;
3290                                 }
3291                         url += "&user="+filemgr.urlize(u);
3292                         }
3293                 if (gsel != null && gsel.getState()) {
3294                         String g = group.getText().trim();
3295                         if (g.length() == 0) {
3296                                 new ErrorWindow(filemgr.text("search_egroup"));
3297                                 return;
3298                                 }
3299                         url += "&group="+filemgr.urlize(g);
3300                         }
3301                 if (smore.getState()) {
3302                         String m = more.getText().trim();
3303                         try { Integer.parseInt(m); }
3304                         catch(Exception e) {
3305                                 new ErrorWindow(filemgr.text("search_esize"));
3306                                 return;
3307                                 }
3308                         url += "&size=%2B"+m+"c";
3309                         }
3310                 else if (sless.getState()) {
3311                         String l = less.getText().trim();
3312                         try { Integer.parseInt(l); }
3313                         catch(Exception e) {
3314                                 new ErrorWindow(filemgr.text("search_esize"));
3315                                 return;
3316                                 }
3317                         url += "&size=%2D"+l+"c";
3318                         }
3319                 if (xon != null && xon.getState())
3320                         url += "&xdev=1";
3321                 if (cont != null && cont.getText().trim().length() > 0)
3322                         url += "&cont="+filemgr.urlize(cont.getText());
3323
3324                 // send off the search
3325                 setCursor(WAIT_CURSOR);
3326                 String f[] = filemgr.get_text(url);
3327                 if (f[0].length() > 0) {
3328                         new ErrorWindow(f[0]);
3329                         return;
3330                         }
3331                 Object rows[][] = new Object[f.length-1][];
3332                 results = new RemoteFile[f.length-1];
3333                 for(int i=1; i<f.length; i++) {
3334                         RemoteFile r = new RemoteFile(filemgr, f[i], null);
3335                         results[i-1] = r;
3336                         Object row[] = rows[i-1] = new Object[3];
3337                         row[0] = filemgr.get_image(RemoteFile.tmap[r.type]);
3338                         row[1] = r.path;
3339                         if (r.size < 1000)
3340                                 row[2] = filemgr.spad(r.size, 5)+" B";
3341                         else if (r.size < 1000000)
3342                                 row[2] = filemgr.spad(r.size/1000, 5)+" kB";
3343                         else
3344                                 row[2] = filemgr.spad(r.size/1000000, 5)+" MB";
3345                         }
3346                 list.clear();
3347                 list.addItems(rows);
3348                 tab.select(filemgr.text("search_list"));
3349                 setCursor(DEFAULT_CURSOR);
3350                 }
3351         else if (b == down_b) {
3352                 // Download selected file (if any)
3353                 int num = list.selected();
3354                 if (num < 0 || results.length == 0) {
3355                         new ErrorWindow(filemgr.text("search_edown"));
3356                         return;
3357                         }
3358                 filemgr.download_file(results[num]);
3359                 }
3360         }
3361
3362         public void singleClick(MultiColumn list, int num)
3363         {
3364         }
3365
3366         // go to the directory of the double-clicked file
3367         public void doubleClick(MultiColumn list, int num)
3368         {
3369         RemoteFile f = results[num];
3370         int sl = f.path.lastIndexOf('/');
3371         String dir = sl == 0 ? "/" : f.path.substring(0, sl);
3372         filemgr.find_directory(dir, true);
3373         RemoteFile l[] = filemgr.showing_list;
3374         for(int i=0; i<l.length; i++) {
3375                 if (l[i].name.equals(f.name)) {
3376                         // select the file in the list
3377                         filemgr.files.select(i+1);
3378                         filemgr.files.scrollto(i+1);
3379                         break;
3380                         }
3381                 }
3382         dispose();
3383         }
3384
3385         public void headingClicked(MultiColumn list, int col)
3386         {
3387         }
3388 }
3389
3390 // A popup window showing previously entered paths
3391 class HistoryWindow extends FixedFrame
3392         implements CbButtonCallback,ActionListener
3393 {
3394
3395         java.awt.List hlist;
3396         CbButton ok_b, cancel_b;
3397         FileManager filemgr;
3398
3399         HistoryWindow(FileManager p)
3400         {
3401         filemgr = p;
3402         setTitle(filemgr.text("history_title"));
3403
3404         // Setup UI
3405         hlist = new java.awt.List();
3406         for(int i=0; i<filemgr.history_list.size(); i++) {
3407                 hlist.add((String)filemgr.history_list.elementAt(i));
3408                 }
3409         hlist.addActionListener(this);
3410         setLayout(new BorderLayout());
3411         add("Center", hlist);
3412         Panel bot = new Panel();
3413         bot.setLayout(new FlowLayout(FlowLayout.RIGHT));
3414         bot.add(ok_b = new CbButton(filemgr.get_image("save.gif"),
3415                                     filemgr.text("history_ok"),
3416                                     CbButton.LEFT, this));
3417         bot.add(cancel_b = new CbButton(filemgr.get_image("cancel.gif"),
3418                                         filemgr.text("cancel"),
3419                                         CbButton.LEFT, this));
3420         add("South", bot);
3421         Util.recursiveBody(this);
3422         pack();
3423         show();
3424         }
3425
3426         public void click(CbButton b)
3427         {
3428         if (b == cancel_b)
3429                 dispose();
3430         else if (b == ok_b) {
3431                 // Go to the selected directory
3432                 String p = hlist.getSelectedItem();
3433                 if (p != null) {
3434                         filemgr.find_directory(p, true);
3435                         dispose();
3436                         }
3437                 }
3438         }
3439
3440         public void actionPerformed(ActionEvent e)
3441         {
3442         // List entry double-clicked .. go to it
3443         String p = hlist.getSelectedItem();
3444         filemgr.find_directory(p, true);
3445         dispose();
3446         }
3447
3448         public Dimension minimumSize()
3449         {
3450         return new Dimension(300, 300);
3451         }
3452 }
3453
3454 class FileSystem
3455 {
3456         String mount;
3457         String dev;
3458         String type;
3459         String opts[];
3460         boolean acls;
3461         boolean attrs;
3462         boolean ext;
3463         boolean mtab, fstab;
3464
3465         FileSystem(String l)
3466         {
3467         StringSplitter tok = new StringSplitter(l, ' ');
3468         mount = tok.nextToken();
3469         dev = tok.nextToken();
3470         type = tok.nextToken();
3471         String optstr = tok.nextToken();
3472         acls = tok.nextToken().equals("1");
3473         attrs = tok.nextToken().equals("1");
3474         ext = tok.nextToken().equals("1");
3475         mtab = tok.nextToken().equals("1");
3476         fstab = tok.nextToken().equals("1");
3477
3478         StringTokenizer tok2 = new StringTokenizer(optstr, ",");
3479         opts = new String[tok2.countTokens()];
3480         for(int i=0; i<opts.length; i++)
3481                 opts[i] = tok2.nextToken();
3482         }
3483 }
3484
3485 class ACLEntry
3486 {
3487         FileManager filemgr;
3488         RemoteFile file;
3489         boolean def;
3490         String type;
3491         String owner;
3492         boolean read, write, exec;
3493         boolean empty_owner = false;
3494
3495         ACLEntry(String l, ACLWindow w)
3496         {
3497         filemgr = w.filemgr;
3498         file = w.file;
3499         StringSplitter tok = new StringSplitter(l, ':');
3500         type = tok.nextToken();
3501         if (type.equals("default")) {
3502                 def = true;
3503                 type = tok.nextToken();
3504                 }
3505         if (!type.equals("mask") && !type.equals("other")) {
3506                 owner = tok.nextToken();
3507                 if (owner.length() == 0)
3508                         owner = null;
3509                 }
3510         String rwx = tok.nextToken();
3511         if (rwx.length() == 0) {
3512                 rwx = tok.nextToken();  // getfacl outputs a blank owner for
3513                                         // mask and other on some systems
3514                 empty_owner = true;
3515                 }
3516         read = (rwx.charAt(0) == 'r');
3517         write = (rwx.charAt(1) == 'w');
3518         exec = (rwx.charAt(2) == 'x');
3519         }
3520
3521         ACLEntry(ACLWindow w)
3522         {
3523         filemgr = w.filemgr;
3524         file = w.file;
3525         }
3526
3527         String[] getRow()
3528         {
3529         String rv[] = new String[3];
3530         String t = def ? "acltype_default_"+type : "acltype_"+type;
3531         rv[0] = filemgr.text(t);
3532         if (type.equals("mask") || type.equals("other") ||
3533             (def && owner == null))
3534                 rv[1] = "";
3535         else if (owner != null)
3536                 rv[1] = owner;
3537         else if (type.equals("user"))
3538                 rv[1] = filemgr.text("eacl_user", file.user);
3539         else
3540                 rv[1] = filemgr.text("eacl_group", file.group);
3541         rv[2] = "";
3542         if (read) rv[2] += filemgr.text("info_read")+" ";
3543         if (write) rv[2] += filemgr.text("info_write")+" ";
3544         if (exec) rv[2] += filemgr.text("info_exec")+" ";
3545         return rv;
3546         }
3547
3548         public String toString()
3549         {
3550         String rv = def ? "default:" : "";
3551         rv += type+":";
3552         if (!type.equals("mask") && !type.equals("other") || empty_owner)
3553                 // mask and other types have no owner field at all, except
3554                 // on some operating systems like FreeBSD where it is empty
3555                 rv += (owner == null ? "" : owner)+":";
3556         rv += (read ? 'r' : '-');
3557         rv += (write ? 'w' : '-');
3558         rv += (exec ? 'x' : '-');
3559         return rv;
3560         }
3561 }
3562
3563 class ACLEditor extends FixedFrame implements CbButtonCallback
3564 {
3565         FileManager filemgr;
3566         ACLWindow aclwin;
3567         ACLEntry acl;
3568         boolean creating;
3569         CbButton ok, del;
3570         Checkbox read, write, exec, owner1, owner2;
3571         TextField owner;
3572
3573         // Editing an existing ACL entry
3574         ACLEditor(ACLWindow w, ACLEntry a)
3575         {
3576         aclwin = w;
3577         filemgr = aclwin.filemgr;
3578         acl = a;
3579         creating = false;
3580         makeUI();
3581         }
3582
3583         // Creating a new ACL entry
3584         ACLEditor(ACLWindow w, String type, boolean def, boolean empty_owner)
3585         {
3586         aclwin = w;
3587         filemgr = aclwin.filemgr;
3588         acl = new ACLEntry(aclwin);
3589         acl.def = def;
3590         acl.type = type;
3591         acl.empty_owner = empty_owner;
3592         creating = true;
3593         makeUI();
3594         }
3595
3596         void makeUI()
3597         {
3598         setTitle(filemgr.text(creating ? "eacl_create" : "eacl_edit"));
3599         setLayout(new BorderLayout());
3600         Panel left = new Panel();
3601         left.setLayout(new GridLayout(0, 1));
3602         add("West", left);
3603         Panel right = new Panel();
3604         right.setLayout(new GridLayout(0, 1));
3605         add("East", right);
3606
3607         left.add(new Label(filemgr.text("eacl_acltype")));
3608         TextField type;
3609         right.add(type = new TextField(
3610                                 (acl.def ? "default " : "")+acl.type, 20));
3611         type.setEditable(false);
3612         type.setFont(filemgr.fixed);
3613
3614         if (!acl.type.equals("mask") && !acl.type.equals("other")) {
3615                 left.add(new Label(filemgr.text("eacl_aclname")));
3616                 if (acl.def) {
3617                         // A default user or group ACL .. can be for
3618                         // a specific user, or for the file owner
3619                         Panel op = new Panel();
3620                         op.setLayout(new FlowLayout(FlowLayout.LEFT, 0, 0));
3621                         CheckboxGroup gr = new CheckboxGroup();
3622                         op.add(owner1 = new Checkbox(filemgr.text("eacl_owner"),
3623                                             gr, acl.owner == null));
3624                         op.add(owner2 = new Checkbox("",
3625                                             gr, acl.owner != null));
3626                         op.add(owner = new TextField(
3627                                 acl.owner == null ? "" : acl.owner, 20));
3628                         owner.setFont(filemgr.fixed);
3629                         right.add(op);
3630                         }
3631                 else if (creating || acl.owner != null) {
3632                         // A user or group ACL for a specific user
3633                         owner = new TextField(
3634                                         acl.owner == null ? "" : acl.owner, 20);
3635                         owner.setFont(filemgr.fixed);
3636                         right.add(owner);
3637                         }
3638                 else {
3639                         // A user or group ACL for the file owner
3640                         String str;
3641                         if (acl.type.equals("user"))
3642                             str = filemgr.text("eacl_user", aclwin.file.user);
3643                         else
3644                             str = filemgr.text("eacl_group", aclwin.file.group);
3645                         TextField o = new TextField(str);
3646                         o.setEditable(false);
3647                         o.setFont(filemgr.fixed);
3648                         right.add(o);
3649                         }
3650                 }
3651
3652         left.add(new Label(filemgr.text("eacl_aclperms")));
3653         Panel pp = new Panel();
3654         pp.setLayout(new FlowLayout(FlowLayout.RIGHT));
3655         pp.add(read = new Checkbox(filemgr.text("info_read"), null, acl.read));
3656         pp.add(write = new Checkbox(filemgr.text("info_write"), null, acl.write));
3657         pp.add(exec = new Checkbox(filemgr.text("info_exec"), null, acl.exec));
3658         right.add(pp);
3659
3660         Panel bot = new Panel();
3661         bot.setLayout(new FlowLayout(FlowLayout.RIGHT));
3662         bot.add(ok = new CbButton(filemgr.get_image("save.gif"),
3663                                   filemgr.text("save"),
3664                                   CbButton.LEFT, this));
3665         if (!creating && (acl.owner != null || acl.def))
3666                 bot.add(del = new CbButton(filemgr.get_image("cancel.gif"),
3667                                            filemgr.text("delete"),
3668                                            CbButton.LEFT, this));
3669         add("South", bot);
3670
3671         Util.recursiveBody(this);
3672         pack();
3673         show();
3674         }
3675
3676         public void click(CbButton b)
3677         {
3678         if (b == ok) {
3679                 // Update or add the ACL entry
3680                 if (owner1 != null && owner1.getState()) {
3681                         acl.owner = null;
3682                         }
3683                 else if (owner != null) {
3684                         String o = owner.getText().trim();
3685                         if (o.length() == 0 && !acl.def) {
3686                                 new ErrorWindow(filemgr.text("eacl_eowner"));
3687                                 return;
3688                                 }
3689                         acl.owner = owner.getText();
3690                         if (acl.owner.length() == 0)
3691                                 acl.owner = null;
3692                         }
3693                 acl.read = read.getState();
3694                 acl.write = write.getState();
3695                 acl.exec = exec.getState();
3696                 if (creating) {
3697                         // Add to the ACL table
3698                         aclwin.acllist.addElement(acl);
3699                         aclwin.acltable.addItem(acl.getRow());
3700                         }
3701                 else {
3702                         // Update the table
3703                         int idx = aclwin.acllist.indexOf(acl);
3704                         aclwin.acltable.modifyItem(acl.getRow(), idx);
3705                         }
3706                 dispose();
3707                 }
3708         else if (b == del) {
3709                 // Remove this entry
3710                 int idx = aclwin.acllist.indexOf(acl);
3711                 aclwin.acllist.removeElementAt(idx);
3712                 aclwin.acltable.deleteItem(idx);
3713                 dispose();
3714                 }
3715         }
3716
3717         public void dispose()
3718         {
3719         aclwin.edmap.remove(acl);
3720         super.dispose();
3721         }
3722 }
3723
3724 class ACLWindow extends FixedFrame implements CbButtonCallback,MultiColumnCallback
3725 {
3726         FileManager filemgr;
3727         RemoteFile file;
3728         Vector acllist = new Vector();
3729         Hashtable edmap = new Hashtable();
3730
3731         CbButton ok, cancel, add;
3732         Choice addtype;
3733         MultiColumn acltable;
3734
3735         String acltypes[] = { "user", "group", "mask",
3736                               "default user", "default group", "default other",
3737                               "default mask" };
3738
3739         ACLWindow(FileManager p, RemoteFile f)
3740         {
3741         super(400, 300);
3742         setTitle(p.text("eacl_title", f.path));
3743         filemgr = p;
3744         file = f;
3745
3746         // Get the ACLs
3747         String a[] = filemgr.get_text(
3748                         "getfacl.cgi?file="+filemgr.urlize(file.path));
3749         if (a[0].length() != 0) {
3750                 new ErrorWindow(filemgr.text("eacl_eacls", a[0]));
3751                 return;
3752                 }
3753
3754         // Create the UI
3755         setLayout(new BorderLayout());
3756         String titles[] = { filemgr.text("eacl_acltype"),
3757                             filemgr.text("eacl_aclname"),
3758                             filemgr.text("eacl_aclperms") };
3759         acltable = new MultiColumn(titles, this);
3760         for(int i=1; i<a.length; i++) {
3761                 ACLEntry acl = new ACLEntry(a[i], this);
3762                 acllist.addElement(acl);
3763                 acltable.addItem(acl.getRow());
3764                 }
3765         add("Center", acltable);
3766         Panel abot = new Panel();
3767         abot.setLayout(new FlowLayout(FlowLayout.RIGHT));
3768         abot.add(add = new CbButton(filemgr.get_image("add.gif"),
3769                                    filemgr.text("eacl_add"),
3770                                    CbButton.LEFT, this));
3771         int len = file.type == RemoteFile.DIR ? acltypes.length : 3;
3772         abot.add(addtype = new Choice());
3773         for(int i=0; i<len; i++) {
3774                 String t = "acltype_"+acltypes[i].replace(' ', '_');
3775                 addtype.addItem(filemgr.text(t));
3776                 }
3777         abot.add(new Label(" "));
3778         abot.add(ok = new CbButton(filemgr.get_image("save.gif"),
3779                                    filemgr.text("save"),
3780                                    CbButton.LEFT, this));
3781         abot.add(cancel = new CbButton(filemgr.get_image("cancel.gif"),
3782                                        filemgr.text("cancel"),
3783                                        CbButton.LEFT, this));
3784         add("South", abot);
3785
3786         Util.recursiveBody(this);
3787         pack();
3788         show();
3789         }
3790
3791         public void click(CbButton b)
3792         {
3793         if (b == ok) {
3794                 // Check if there are any defaults, and if so there must
3795                 // be default user, group and other
3796                 boolean anydef = false, defuser = false,
3797                         defgroup = false, defother = false;
3798                 for(int i=0; i<acllist.size(); i++) {
3799                         ACLEntry e = (ACLEntry)acllist.elementAt(i);
3800                         if (e.def) anydef = true;
3801                         if (e.def && e.owner == null) {
3802                                 if (e.type.equals("user")) defuser = true;
3803                                 if (e.type.equals("group")) defgroup = true;
3804                                 if (e.type.equals("other")) defother = true;
3805                                 }
3806                         }
3807                 if (anydef && (!defuser || !defgroup || !defother)) {
3808                         new ErrorWindow(filemgr.text("eacl_edefaults"));
3809                         return;
3810                         }
3811
3812                 // Save the ACLs
3813                 String aclstr = "";
3814                 for(int i=0; i<acllist.size(); i++)
3815                         aclstr += (ACLEntry)acllist.elementAt(i)+"\n";
3816                 String rv[] = filemgr.get_text("setfacl.cgi?file="+
3817                                                 filemgr.urlize(file.path)+
3818                                                 "&acl="+filemgr.urlize(aclstr));
3819                 if (rv[0].length() > 0)
3820                         new ErrorWindow(filemgr.text("eacl_efailed",
3821                                 file.path, rv[0]));
3822                 else
3823                         dispose();
3824                 }
3825         else if (b == add) {
3826                 // Open a window for a new ACL entry
3827                 String t = acltypes[addtype.getSelectedIndex()];
3828                 String d = "default ";
3829                 boolean def = t.startsWith(d);
3830                 if (def)
3831                         t = t.substring(d.length());
3832                 if (t.equals("mask")) {
3833                         // Only allow one mask
3834                         for(int i=0; i<acllist.size(); i++) {
3835                                 ACLEntry a = (ACLEntry)acllist.elementAt(i);
3836                                 if (a.type.equals(t) && a.def == def) {
3837                                         new ErrorWindow(filemgr.text(def ?
3838                                             "eacl_edefmask" : "eacl_emask"));
3839                                         return;
3840                                         }
3841                                 }
3842                         }
3843                 // Check if owner field exists and is empty for existing
3844                 // mask or other fields
3845                 boolean new_empty_owner = false;
3846                 for(int i=0; i<acllist.size(); i++) {
3847                         ACLEntry a = (ACLEntry)acllist.elementAt(i);
3848                         if ((a.type.equals("mask") || a.type.equals("other")) &&
3849                            a.empty_owner) {
3850                                 new_empty_owner = true;
3851                                 }
3852                         }
3853                 new ACLEditor(this, t, def, new_empty_owner);
3854                 }
3855         else if (b == cancel) {
3856                 // Don't save
3857                 dispose();
3858                 }
3859         }
3860
3861         // Bring up an editor for an ACL
3862         public void doubleClick(MultiColumn list, int num)
3863         {
3864         int idx = list.selected();
3865         if (idx >= 0) {
3866                 ACLEntry e = (ACLEntry)acllist.elementAt(idx);
3867                 ACLEditor ed = (ACLEditor)edmap.get(e);
3868                 if (ed == null)
3869                         edmap.put(e, new ACLEditor(this, e));
3870                 else {
3871                         ed.toFront();
3872                         ed.requestFocus();
3873                         }
3874                 }
3875         }
3876
3877         public void singleClick(MultiColumn list, int num)
3878         {
3879         }
3880
3881         public void headingClicked(MultiColumn list, int col)
3882         {
3883         }
3884 }
3885
3886 class AttributesWindow extends FixedFrame
3887         implements CbButtonCallback,MultiColumnCallback
3888 {
3889         FileManager filemgr;
3890         RemoteFile file;
3891         Vector attrlist = new Vector();
3892         Hashtable edmap = new Hashtable();
3893
3894         CbButton ok, cancel, add;
3895         MultiColumn attrtable;
3896
3897         AttributesWindow(FileManager p, RemoteFile f)
3898         {
3899         super(400, 300);
3900         setTitle(p.text("attr_title", f.path));
3901         filemgr = p;
3902         file = f;
3903
3904         // Get the attributes
3905         String a[] = filemgr.get_text(
3906                         "getattrs.cgi?file="+filemgr.urlize(file.path));
3907         if (a[0].length() != 0) {
3908                 new ErrorWindow(filemgr.text("attr_eattrs", a[0]));
3909                 return;
3910                 }
3911
3912         // Create the UI
3913         setLayout(new BorderLayout());
3914         String titles[] = { filemgr.text("attr_name"),
3915                             filemgr.text("attr_value") };
3916         attrtable = new MultiColumn(titles, this);
3917         for(int i=1; i<a.length; i++) {
3918                 FileAttribute at = new FileAttribute(a[i], filemgr);
3919                 attrlist.addElement(at);
3920                 attrtable.addItem(at.getRow());
3921                 }
3922         add("Center", attrtable);
3923         Panel abot = new Panel();
3924         abot.setLayout(new FlowLayout(FlowLayout.RIGHT));
3925         abot.add(add = new CbButton(filemgr.get_image("add.gif"),
3926                                    filemgr.text("attr_add"),
3927                                    CbButton.LEFT, this));
3928         abot.add(new Label(" "));
3929         abot.add(ok = new CbButton(filemgr.get_image("save.gif"),
3930                                    filemgr.text("save"),
3931                                    CbButton.LEFT, this));
3932         abot.add(cancel = new CbButton(filemgr.get_image("cancel.gif"),
3933                                        filemgr.text("cancel"),
3934                                        CbButton.LEFT, this));
3935         add("South", abot);
3936
3937         Util.recursiveBody(this);
3938         pack();
3939         show();
3940         }
3941
3942         public void click(CbButton b)
3943         {
3944         if (b == ok) {
3945                 // Save the attributes
3946                 String pstr = "";
3947                 for(int i=0; i<attrlist.size(); i++) {
3948                         FileAttribute at = (FileAttribute)attrlist.elementAt(i);
3949                         pstr += "&name"+i+"="+filemgr.urlize(at.name)+
3950                                 "&value"+i+"="+filemgr.urlize(at.value);
3951                         }
3952                 String rv[] = filemgr.get_text("setattrs.cgi?file="+
3953                                                 filemgr.urlize(file.path)+pstr);
3954                 if (rv[0].length() > 0)
3955                         new ErrorWindow(filemgr.text("attr_efailed",
3956                                 file.path, rv[0]));
3957                 else
3958                         dispose();
3959                 }
3960         else if (b == add) {
3961                 // Open a window for a new ACL entry
3962                 new AttributeEditor(this);
3963                 }
3964         else if (b == cancel) {
3965                 // Don't save
3966                 dispose();
3967                 }
3968         }
3969
3970         // Bring up an editor for an ACL
3971         public void doubleClick(MultiColumn list, int num)
3972         {
3973         int idx = list.selected();
3974         if (idx >= 0) {
3975                 FileAttribute at = (FileAttribute)attrlist.elementAt(idx);
3976                 AttributeEditor ed = (AttributeEditor)edmap.get(at);
3977                 if (ed == null)
3978                         edmap.put(at, new AttributeEditor(this, at));
3979                 else {
3980                         ed.toFront();
3981                         ed.requestFocus();
3982                         }
3983                 }
3984         }
3985
3986         public void singleClick(MultiColumn list, int num)
3987         {
3988         }
3989
3990         public void headingClicked(MultiColumn list, int col)
3991         {
3992         }
3993 }
3994
3995 class FileAttribute
3996 {
3997         String name;
3998         String value;
3999
4000         FileAttribute(String l, FileManager f)
4001         {
4002         int eq = l.indexOf('=');
4003         name = f.un_urlize(l.substring(0, eq));
4004         value = f.un_urlize(l.substring(eq+1));
4005         }
4006
4007         FileAttribute(String n, String v)
4008         {
4009         name = n;
4010         value = v;
4011         }
4012
4013         String[] getRow()
4014         {
4015         return new String[] { name, value };
4016         }
4017 }
4018
4019 class AttributeEditor extends FixedFrame implements CbButtonCallback
4020 {
4021         FileManager filemgr;
4022         AttributesWindow attrwin;
4023         FileAttribute attr;
4024         boolean creating;
4025         CbButton ok, del;
4026         TextField name;
4027         TextArea value;
4028
4029         AttributeEditor(AttributesWindow w, FileAttribute a)
4030         {
4031         attrwin = w;
4032         attr = a;
4033         filemgr = w.filemgr;
4034         creating = false;
4035         makeUI();
4036         }
4037
4038         AttributeEditor(AttributesWindow w)
4039         {
4040         attrwin = w;
4041         attr = new FileAttribute("", "");
4042         filemgr = w.filemgr;
4043         creating = true;
4044         makeUI();
4045         }
4046
4047         void makeUI()
4048         {
4049         setTitle(filemgr.text(creating ? "attr_create" : "attr_edit"));
4050         setLayout(new BorderLayout());
4051
4052         Panel top = new Panel();
4053         top.setLayout(new GridLayout(1, 2));
4054         top.add(new Label(filemgr.text("attr_name")));
4055         top.add(name = new TextField(attr.name, 20));
4056         name.setFont(filemgr.fixed);
4057         add("North", top);
4058
4059         Panel mid = new Panel();
4060         mid.setLayout(new GridLayout(1, 2));
4061         mid.add(new Label(filemgr.text("attr_value")));
4062         mid.add(value = new TextArea(attr.value, 5, 20));
4063         add("Center", mid);
4064
4065         Panel bot = new Panel();
4066         bot.setLayout(new FlowLayout(FlowLayout.RIGHT));
4067         bot.add(ok = new CbButton(filemgr.get_image("save.gif"),
4068                                   filemgr.text("save"),
4069                                   CbButton.LEFT, this));
4070         if (!creating)
4071                 bot.add(del = new CbButton(filemgr.get_image("cancel.gif"),
4072                                            filemgr.text("delete"),
4073                                            CbButton.LEFT, this));
4074         add("South", bot);
4075
4076         Util.recursiveBody(this);
4077         pack();
4078         show();
4079         }
4080
4081         public void click(CbButton b)
4082         {
4083         if (b == ok) {
4084                 // Update or add the attribute
4085                 if (name.getText().length() == 0) {
4086                         new ErrorWindow(filemgr.text("attr_ename"));
4087                         return;
4088                         }
4089                 attr.name = name.getText();
4090                 attr.value = value.getText();
4091                 if (creating) {
4092                         // Add to the attribs table
4093                         attrwin.attrlist.addElement(attr);
4094                         attrwin.attrtable.addItem(attr.getRow());
4095                         }
4096                 else {
4097                         // Update the table
4098                         int idx = attrwin.attrlist.indexOf(attr);
4099                         attrwin.attrtable.modifyItem(attr.getRow(), idx);
4100                         }
4101                 dispose();
4102                 }
4103         else if (b == del) {
4104                 // Remove this entry
4105                 int idx = attrwin.attrlist.indexOf(attr);
4106                 attrwin.attrlist.removeElementAt(idx);
4107                 attrwin.attrtable.deleteItem(idx);
4108                 dispose();
4109                 }
4110         }
4111
4112         public void dispose()
4113         {
4114         attrwin.edmap.remove(attr);
4115         super.dispose();
4116         }
4117 }
4118
4119 class EXTWindow extends FixedFrame implements CbButtonCallback
4120 {
4121         FileManager filemgr;
4122         RemoteFile file;
4123
4124         CbButton ok, cancel;
4125         Checkbox cbs[];
4126
4127         String attrs[] = { "A", "a", "c", "d", "i", "s", "S", "u" };
4128         Hashtable attrmap = new Hashtable();
4129
4130         EXTWindow(FileManager p, RemoteFile f)
4131         {
4132         super();
4133         setTitle(p.text("ext_title", f.path));
4134         filemgr = p;
4135         file = f;
4136
4137         // Get the attributes
4138         String a[] = filemgr.get_text(
4139                         "getext.cgi?file="+filemgr.urlize(file.path));
4140         if (a[0].length() != 0) {
4141                 new ErrorWindow(filemgr.text("ext_eattrs", a[0]));
4142                 return;
4143                 }
4144         for(int i=0; i<a[1].length(); i++)
4145                 attrmap.put(a[1].substring(i, i+1), "");
4146
4147         // Create the UI
4148         setLayout(new BorderLayout());
4149         Panel top = new LinedPanel(filemgr.text("ext_header"));
4150         top.setLayout(new GridLayout(0, 1));
4151         cbs = new Checkbox[attrs.length];
4152         for(int i=0; i<attrs.length; i++) {
4153                 cbs[i] = new Checkbox(filemgr.text("eattr_"+attrs[i]));
4154                 cbs[i].setState(attrmap.get(attrs[i]) != null);
4155                 top.add(cbs[i]);
4156                 }
4157         add("Center", top);
4158
4159         Panel bot = new Panel();
4160         bot.setLayout(new FlowLayout(FlowLayout.RIGHT));
4161         bot.add(ok = new CbButton(filemgr.get_image("save.gif"),
4162                                   filemgr.text("save"),
4163                                   CbButton.LEFT, this));
4164         bot.add(cancel = new CbButton(filemgr.get_image("cancel.gif"),
4165                                       filemgr.text("cancel"),
4166                                       CbButton.LEFT, this));
4167         add("South", bot);
4168
4169         Util.recursiveBody(this);
4170         pack();
4171         show();
4172         }
4173
4174         public void click(CbButton b)
4175         {
4176         if (b == ok) {
4177                 // Save the attributes (including unknown ones)
4178                 String astr = "";
4179                 for(int i=0; i<cbs.length; i++) {
4180                         if (cbs[i].getState())
4181                                 astr += attrs[i];
4182                         attrmap.remove(attrs[i]);
4183                         }
4184                 for(Enumeration e = attrmap.keys(); e.hasMoreElements(); )
4185                         astr += e.nextElement();
4186
4187                 // Try to set on the server
4188                 String rv[] = filemgr.get_text("setext.cgi?file="+
4189                                 filemgr.urlize(file.path)+"&attrs="+astr);
4190                 if (rv[0].length() > 0)
4191                         new ErrorWindow(filemgr.text("ext_efailed",
4192                                 file.path, rv[0]));
4193                 else
4194                         dispose();
4195                 }
4196         else if (b == cancel) {
4197                 dispose();
4198                 }
4199         }
4200 }
4201
4202 class MountWindow extends FixedFrame implements CbButtonCallback
4203 {
4204         CbButton yes, no;
4205         FileManager filemgr;
4206         FileSystem fs;
4207         RemoteFile file;
4208
4209         MountWindow(FileManager filemgr, FileSystem fs, RemoteFile file)
4210         {
4211         super();
4212         setTitle(filemgr.text(fs.mtab ? "mount_title2" : "mount_title1"));
4213         this.filemgr = filemgr;
4214         this.fs = fs;
4215         this.file = file;
4216
4217         // Create the UI
4218         setLayout(new BorderLayout());
4219         Panel cen = new BorderPanel(1, Util.body);
4220         cen.setLayout(new GridLayout(1, 1));
4221         String rusure = fs.mtab ? "mount_rusure2" : "mount_rusure1";
4222         cen.add(new Label(filemgr.text(rusure, fs.mount, fs.dev)));
4223         add("Center", cen);
4224         Panel bot = new Panel();
4225         bot.setLayout(new FlowLayout(FlowLayout.CENTER));
4226         bot.add(yes = new CbButton(filemgr.text("yes"), this));
4227         bot.add(no = new CbButton(filemgr.text("no"), this));
4228         add("South", bot);
4229         pack();
4230         show();
4231         Util.recursiveBody(this);
4232         }
4233
4234         public void click(CbButton b)
4235         {
4236         if (b == yes) {
4237                 // Go ahread and do it!
4238                 String rv[] = filemgr.get_text("mount.cgi?dir="+
4239                                                filemgr.urlize(fs.mount));
4240                 dispose();
4241                 if (rv[0].equals("")) {
4242                         // It worked - refresh this directory and the mount list
4243                         filemgr.get_filesystems();
4244                         FileNode d = (FileNode)filemgr.nodemap.get(file);
4245                         if (d != null) {
4246                                 d.setimage();
4247                                 d.known = false;
4248                                 d.file.list = null;
4249                                 d.fill();
4250                                 }
4251                         if (fs.mtab)
4252                                 filemgr.show_files(file.directory);
4253                         else
4254                                 filemgr.show_files(filemgr.showing_files);
4255                         }
4256                 else {
4257                         // Failed - show the error
4258                         new ErrorWindow(filemgr.text(
4259                                 fs.mtab ? "mount_err2" : "mount_err1",
4260                                 fs.mount, rv[0]));
4261                         }
4262                 }
4263         else {
4264                 // Just close the window
4265                 dispose();
4266                 }
4267         }
4268 }
4269
4270 // A label that is limited to a maximum number of characters wide
4271 class MultiLabel extends BorderPanel
4272 {
4273         public MultiLabel(String s, int max)
4274         {
4275         this(s, max, 1);
4276         }
4277
4278         public MultiLabel(String s, int max, int b)
4279         {
4280         this(s, max, b, Label.CENTER);
4281         }
4282
4283         
4284         public MultiLabel(String s, int max, int b, int align)
4285         {
4286         super(b, Util.body);
4287         Vector v = new Vector();
4288         StringTokenizer tok = new StringTokenizer(s.trim(), " \t");
4289         String line = null;
4290         while(tok.hasMoreTokens()) {
4291                 String w = tok.nextToken();
4292                 line = (line == null ? w : line+" "+w);
4293                 if (line.length() > max || !tok.hasMoreTokens()) {
4294                         v.addElement(line);
4295                         line = null;
4296                         }
4297                 }
4298         setLayout(new GridLayout(v.size(), 1, 0, 0));
4299         for(int i=0; i<v.size(); i++) {
4300                 Label l = new Label((String)v.elementAt(i), Label.CENTER);
4301                 add(l);
4302                 }
4303         }
4304 }
4305
4306 // A window for choosing the format in which a directory will be downloaded
4307 class DownloadDirWindow extends FixedFrame implements CbButtonCallback
4308 {
4309         CbButton zip, tgz, tar, cancel;
4310         FileManager filemgr;
4311         RemoteFile file;
4312
4313         DownloadDirWindow(FileManager filemgr, RemoteFile file)
4314         {
4315         super();
4316         setTitle(filemgr.text("ddir_title"));
4317         this.filemgr = filemgr;
4318         this.file = file;
4319
4320         // Create the UI
4321         setLayout(new BorderLayout());
4322         Panel cen = new BorderPanel(1, Util.body);
4323         cen.setLayout(new GridLayout(1, 1));
4324         cen.add(new Label(filemgr.text("ddir_rusure", file.path)));
4325         add("Center", cen);
4326
4327         Panel bot = new Panel();
4328         bot.setLayout(new FlowLayout(FlowLayout.CENTER));
4329         bot.add(zip = new CbButton(filemgr.text("ddir_zip"), this));
4330         bot.add(tgz = new CbButton(filemgr.text("ddir_tgz"), this));
4331         bot.add(tar = new CbButton(filemgr.text("ddir_tar"), this));
4332         bot.add(cancel = new CbButton(filemgr.text("cancel"), this));
4333         add("South", bot);
4334         pack();
4335         show();
4336         Util.recursiveBody(this);
4337         }
4338
4339         public void click(CbButton b)
4340         {
4341         if (b == cancel) {
4342                 // just close the window
4343                 dispose();
4344                 }
4345         else {
4346                 // open the download window
4347                 int format = b == zip ? 1 :
4348                              b == tgz ? 2 : 3;
4349                 dispose();
4350                 filemgr.open_file_window(file, true, format);
4351                 }
4352         }
4353 }
4354
4355 class PreviewWindow extends Frame implements CbButtonCallback
4356 {
4357         CbButton close_b;
4358         RemoteFile file;
4359         FileManager filemgr;
4360         ImagePanel ip;
4361
4362         // Previewing a file
4363         public PreviewWindow(FileManager p, RemoteFile f)
4364         {
4365         //super(350, 350);
4366         file = f; filemgr = p;
4367         makeUI();
4368         setTitle(filemgr.text("preview_title", file.path));
4369
4370         // Load the file
4371         try {
4372                 URL u = new URL(filemgr.getDocumentBase(),
4373                                 "preview.cgi"+filemgr.urlize(file.path)+
4374                                 "?rand="+System.currentTimeMillis()+
4375                                 "&trust="+filemgr.trust+
4376                                 filemgr.extra);
4377                 URLConnection uc = u.openConnection();
4378                 filemgr.set_cookie(uc);
4379                 int len = uc.getContentLength();
4380                 InputStream is = uc.getInputStream();
4381                 byte buf[];
4382                 if (len >= 0) {
4383                         // Length is known
4384                         buf = new byte[uc.getContentLength()];
4385                         int got = 0;
4386                         while(got < buf.length)
4387                                 got += is.read(buf, got, buf.length-got);
4388                         }
4389                 else {
4390                         // Length is unknown .. read till the end
4391                         buf = new byte[0];
4392                         while(true) {
4393                             byte data[] = new byte[16384];
4394                             int got;
4395                             try { got = is.read(data); }
4396                             catch(EOFException ex) { break; }
4397                             if (got <= 0) break;
4398                             byte nbuf[] = new byte[buf.length + got];
4399                             System.arraycopy(buf, 0, nbuf, 0, buf.length);
4400                             System.arraycopy(data, 0, nbuf, buf.length, got);
4401                             buf = nbuf;
4402                             }
4403                         }
4404
4405                 // Check if this is really an error
4406                 if (uc.getContentType().equals("text/plain")) {
4407                         String s = new String(buf, 0);
4408                         new ErrorWindow(s);
4409                         dispose();
4410                         return;
4411                         }
4412
4413                 // Show the image
4414                 Image img = Toolkit.getDefaultToolkit().createImage(buf);
4415                 MediaTracker waiter = new MediaTracker(this);
4416                 waiter.addImage(img, 666);
4417                 try { waiter.waitForAll(); }
4418                 catch(InterruptedException e) { }
4419                 if (img.getWidth(this) <= 0) {
4420                         new ErrorWindow(filemgr.text("preview_bad"));
4421                         dispose();
4422                         return;
4423                         }
4424                 ip.setImage(img);
4425
4426                 pack();
4427                 show();
4428                 }
4429         catch(Exception e) { e.printStackTrace(); }
4430         }
4431
4432         void makeUI()
4433         {
4434         setLayout(new BorderLayout());
4435
4436         // Image viewing area
4437         BorderPanel mid = new BorderPanel(2, Util.body);
4438         mid.setLayout(new BorderLayout());
4439         ip = new ImagePanel(null);
4440         mid.add("Center", ip);
4441         add("Center", mid);
4442
4443         // Button panel
4444         Panel bot = new Panel();
4445         bot.setLayout(new FlowLayout(FlowLayout.RIGHT));
4446         bot.add(close_b = new CbButton(filemgr.get_image("cancel.gif"),
4447                                         filemgr.text("close"),
4448                                         CbButton.LEFT, this));
4449         add("South", bot);
4450         Util.recursiveBody(this);
4451         }
4452
4453         public void click(CbButton b)
4454         {
4455         if (b == close_b) {
4456                 // Just close
4457                 dispose();
4458                 }
4459         }
4460 }
4461
4462 class ImagePanel extends Panel
4463 {
4464         Image img;
4465
4466         public ImagePanel(Image img)
4467         {
4468         this.img = img;
4469         }
4470
4471         public void paint(Graphics g)
4472         {
4473         if (img != null) {
4474                 g.drawImage(img, 0, 0, this);
4475                 }
4476         }
4477
4478         public void setImage(Image img)
4479         {
4480         this.img = img;
4481         repaint();
4482         }
4483
4484         public Dimension minimumSize()
4485         {
4486         return new Dimension(img.getWidth(this), img.getHeight(this));
4487         }
4488
4489         public Dimension preferredSize()
4490         {
4491         return minimumSize();
4492         }
4493 }
4494
4495 class ExtractWindow extends FixedFrame implements CbButtonCallback
4496 {
4497         CbButton yes, yesdelete, no, show;
4498         FileManager filemgr;
4499         RemoteFile file;
4500
4501         ExtractWindow(FileManager filemgr, RemoteFile file)
4502         {
4503         super();
4504         setTitle(filemgr.text("extract_title"));
4505         this.filemgr = filemgr;
4506         this.file = file;
4507
4508         // Create the UI
4509         setLayout(new BorderLayout());
4510         Panel cen = new BorderPanel(1, Util.body);
4511         cen.setLayout(new GridLayout(3, 1));
4512         cen.add(new Label(filemgr.text("extract_rusure")));
4513         cen.add(new Label(file.path));
4514         cen.add(new Label(filemgr.text("extract_rusure2")));
4515         add("Center", cen);
4516         Panel bot = new Panel();
4517         bot.setLayout(new FlowLayout(FlowLayout.CENTER));
4518         bot.add(yes = new CbButton(filemgr.text("yes"), this));
4519         bot.add(yesdelete = new CbButton(filemgr.text("extract_yes"), this));
4520         bot.add(no = new CbButton(filemgr.text("no"), this));
4521         bot.add(show = new CbButton(filemgr.text("extract_show"), this));
4522         add("South", bot);
4523         pack();
4524         show();
4525         Util.recursiveBody(this);
4526         }
4527
4528         public void click(CbButton b)
4529         {
4530         if (b == yes || b == yesdelete) {
4531                 // Go ahread and do it!
4532                 String rv[] = filemgr.get_text("extract.cgi?file="+
4533                                        filemgr.urlize(file.path)+
4534                                        "&delete="+(b == yesdelete ? 1 : 0));
4535                 dispose();
4536                 if (rv[0].equals("")) {
4537                         // It worked - refresh the directory
4538                         RemoteFile par = file.directory;
4539                         FileNode d = (FileNode)filemgr.nodemap.get(par);
4540                         if (d != null) {
4541                                 d.setimage();
4542                                 d.known = false;
4543                                 d.file.list = null;
4544                                 d.fill();
4545                                 }
4546                         filemgr.show_files(filemgr.showing_files);
4547                         }
4548                 else {
4549                         // Failed - show the error
4550                         new ErrorWindow(filemgr.text("extract_err", rv[0]));
4551                         }
4552                 }
4553         else if (b == show) {
4554                 // Open window just showing contents
4555                 String rv[] = filemgr.get_text("contents.cgi?file="+
4556                                                filemgr.urlize(file.path));
4557                 dispose();
4558                 if (rv[0].equals("")) {
4559                         // Worked - show the files
4560                         new ContentsWindow(file, filemgr, rv);
4561                         }
4562                 else {
4563                         // Failed - show the error
4564                         new ErrorWindow(filemgr.text("extract_err2", rv[0]));
4565                         }
4566                 }
4567         else {
4568                 // Just close the window
4569                 dispose();
4570                 }
4571         }
4572 }
4573
4574 class ContentsWindow extends FixedFrame implements CbButtonCallback
4575 {
4576         RemoteFile file;
4577         FileManager filemgr;
4578         CbButton close_b;
4579
4580         ContentsWindow(RemoteFile f, FileManager p, String rv[])
4581         {
4582         file = f;
4583         filemgr = p;
4584
4585         // Create UI
4586         setTitle(f.path);
4587         setLayout(new BorderLayout());
4588         Panel bot = new Panel();
4589         bot.setLayout(new FlowLayout(FlowLayout.RIGHT));
4590         bot.add(close_b = new CbButton(filemgr.get_image("cancel.gif"),
4591                                         filemgr.text("close"),
4592                                         CbButton.LEFT, this));
4593         add("South", bot);
4594
4595         // Create text area showing contents
4596         String lines = "";
4597         for(int i=1; i<rv.length; i++) {
4598                 lines = lines + rv[i] + "\n";
4599                 }
4600         TextArea contents = new TextArea(lines, 30, 60);
4601         contents.setEditable(false);
4602         add("Center", contents);
4603         add("North", new Label(filemgr.text("extract_shown")));
4604
4605         Util.recursiveBody(this);
4606         pack();
4607         show();
4608         }
4609
4610         public void click(CbButton b)
4611         {
4612         if (b == close_b) {
4613                 // Just close
4614                 dispose();
4615                 }
4616         }
4617 }
4618