Handle hostnames with upper-case letters
[webmin.git] / file / FileManager.java.bak
1 import java.awt.*;
2 import java.io.*;
3 import java.applet.*;
4 import java.net.*;
5 import java.util.*;
6 import netscape.javascript.JSObject;
7
8 // A java filemanager that allows the user to manipulate files on the
9 // Webmin server. Layout is similar to the windows explorer - directory
10 // tree on the left, files on the right, action buttons on the top.
11 public class FileManager extends Applet
12         implements CbButtonCallback, HierarchyCallback, MultiColumnCallback
13 {
14         // top buttons
15         CbButton ret_b, down_b, edit_b, refresh_b, props_b,
16                  copy_b, cut_b, paste_b, delete_b, new_b, upload_b, mkdir_b,
17                  makelink_b, rename_b, share_b, search_b, acl_b, attr_b, ext_b;
18
19         // Directory tree
20         Hierarchy dirs;
21         FileNode root;
22         Hashtable nodemap = new Hashtable();
23
24         // File list
25         MultiColumn files;
26         TextField pathname;
27         RemoteFile showing_files;
28         RemoteFile showing_list[];
29
30         // Copying and pasting
31         RemoteFile cut_buffer[];
32         boolean cut_mode;
33
34         static final String monmap[] = { "Jan", "Feb", "Mar", "Apr",
35                                          "May", "Jun", "Jul", "Aug",
36                                          "Sep", "Oct", "Nov", "Dec" };
37         String accroot[];
38         Hashtable lang = new Hashtable();
39         Hashtable stab = new Hashtable(),
40                   ntab = new Hashtable();
41         boolean sambamode;
42         int nfsmode;
43         String trust;
44
45         boolean got_filesystems;
46         Vector fslist = new Vector();
47         boolean read_only = false;
48
49         public void init()
50         {
51         setLayout(new BorderLayout());
52         StringTokenizer tok = new StringTokenizer(getParameter("root"), " ");
53         accroot = new String[tok.countTokens()];
54         for(int i=0; tok.hasMoreTokens(); i++)
55                 accroot[i] = tok.nextToken();
56         trust = getParameter("trust");
57
58         // download language strings
59         String l[] = get_text("lang.cgi");
60         for(int i=0; i<l.length; i++) {
61                 int eq = l[i].indexOf('=');
62                 if (eq >= 0)
63                         lang.put(l[i].substring(0, eq), l[i].substring(eq+1));
64                 }
65
66         // list samba file shares
67         String s[] = get_text("list_shares.cgi");
68         if (s[0].equals("1")) {
69                 for(int i=1; i<s.length; i++) {
70                         SambaShare ss = new SambaShare(s[i]);
71                         stab.put(ss.path, ss);
72                         }
73                 sambamode = true;
74                 }
75
76         // list NFS exports
77         String e[] = get_text("list_exports.cgi");
78         nfsmode = e.length == 0 ? 0 : Integer.parseInt(e[0]);
79         if (nfsmode != 0) {
80                 for(int i=1; i<e.length; i++) {
81                         if (nfsmode == 1) {
82                                 // Linux export
83                                 LinuxExport le = new LinuxExport(e[i]);
84                                 ntab.put(le.path, le);
85                                 }
86                         else if (nfsmode == 2) {
87                                 // Solaris share
88                                 DFSAdminExport de = new DFSAdminExport(e[i]);
89                                 ntab.put(de.path, de);
90                                 }
91                         }
92                 }
93
94         // list filesystems
95         String f[] = get_text("filesystems.cgi");
96         got_filesystems = f[0].equals("1");
97         boolean acl_support = false,
98                 attr_support = false,
99                 ext_support = false;
100         if (got_filesystems) {
101                 for(int i=1; i<f.length; i++) {
102                         FileSystem fs = new FileSystem(f[i]);
103                         fslist.addElement(fs);
104                         if (fs.acls) acl_support = true;
105                         if (fs.attrs) attr_support = true;
106                         if (fs.ext) ext_support = true;
107                         }
108                 }
109
110         // get read-only flag
111         if (getParameter("ro").equals("1"))
112                 read_only = true;
113
114         // create button panel
115         BorderPanel top = new BorderPanel(2);
116         top.setLayout(new FlowLayout(FlowLayout.LEFT, 2, 2));
117
118         Panel top1 = new Panel();
119         top1.setLayout(new GridLayout(1, 0));
120         if (getParameter("return") != null)
121                 top1.add(ret_b = make_button("ret.gif", text("top_ret")));
122         top1.add(down_b = make_button("down.gif", text("top_down")));
123         if (!read_only)
124                 top1.add(edit_b = make_button("edit.gif", text("top_edit")));
125         top1.add(refresh_b = make_button("refresh.gif", text("top_refresh")));
126         if (!read_only)
127                 top1.add(props_b = make_button("props.gif", text("top_info")));
128         if (acl_support && !read_only)
129                 top1.add(acl_b = make_button("acl.gif", text("top_eacl")));
130         if (attr_support && !read_only)
131                 top1.add(attr_b = make_button("attr.gif", text("top_attr")));
132         if (ext_support && !read_only)
133                 top1.add(ext_b = make_button("ext.gif", text("top_ext")));
134         top1.add(search_b = make_button("search.gif", text("top_search")));
135         top.add(top1);
136         top.add(new Label(""));
137         
138         if (!read_only) {
139                 Panel top2 = new Panel();
140                 top2.setLayout(new GridLayout(1, 0));
141                 top2.add(delete_b = make_button("cancel.gif",
142                                                 text("top_delete")));
143                 top2.add(new_b = make_button("new.gif", text("top_new")));
144                 top2.add(upload_b = make_button("upload.gif",
145                                                 text("top_upload")));
146                 top2.add(mkdir_b = make_button("mkdir.gif", text("top_new")));
147                 if (!getParameter("follow").equals("1"))
148                         top2.add(makelink_b = make_button("makelink.gif",
149                                                 text("top_new")));
150                 top2.add(rename_b = make_button("rename.gif",
151                                                 text("top_rename")));
152                 if ((sambamode || nfsmode != 0) &&
153                     getParameter("sharing").equals("1"))
154                         top2.add(share_b = make_button("share.gif",
155                                                        text("top_share")));
156                 top.add(top2);
157                 top.add(new Label(""));
158
159                 Panel top3 = new Panel();
160                 top3.setLayout(new GridLayout(1, 0));
161                 top3.add(copy_b = make_button("copy.gif", text("top_copy")));
162                 top3.add(cut_b = make_button("cut.gif", text("top_cut")));
163                 top3.add(paste_b = make_button("paste.gif", text("top_paste")));
164                 top.add(top3);
165                 }
166         add("North", top);
167
168         // create directory tree
169         BorderPanel left = new BorderPanel(2);
170         left.setLayout(new BorderLayout());
171         root = new FileNode(new RemoteFile(this, get_text("root.cgi")[0],null));
172         left.add("Center", dirs = new Hierarchy(root, this));
173         root.open = true; root.fill();
174
175         // create file window
176         BorderPanel right = new BorderPanel(2);
177         right.setLayout(new BorderLayout());
178         right.add("North", pathname = new TextField());
179         //pathname.setFont(new Font("courier", Font.PLAIN, 8));
180         String cols[] = { "", text("right_name"), text("right_size"),
181                           text("right_user"), text("right_group"),
182                           text("right_date") };
183         float widths[] = { .07f, .33f, .15f, .15f, .15f, .15f };
184         right.add("Center", files = new MultiColumn(cols, this));
185         files.setWidths(widths);
186         files.setDrawLines(false);
187         files.setMultiSelect(true);
188         show_files(root.file);
189
190         ResizePanel mid = new ResizePanel(left, right, .3, false);
191         add("Center", mid);
192
193         // Go to the restricted directory
194         String home = getParameter("home");
195         String go = getParameter("goto");
196         String open = getParameter("open");
197         if (open != null) {
198                 find_directory(open, true);
199                 }
200         elsif (go != null && go.equals("1")) {
201                 if (home != null)
202                         find_directory(home, true);
203                 else if (!accroot[0].equals("/"))
204                         find_directory(accroot[0], true);
205                 }
206         }
207
208         CbButton make_button(String f, String t)
209         {
210         if (size().width < 700 && size().width > 1)
211                 return new CbButton(get_image(f), this);
212         else
213                 return new CbButton(get_image(f), t, CbButton.ABOVE, this);
214         }
215
216         // Gets an image from the images directory
217         Image get_image(String img)
218         {
219         return getImage(getDocumentBase(), "images/"+img);
220         }
221
222         String[] get_text(String url)
223         {
224         try {
225                 long now = System.currentTimeMillis();
226                 if (url.indexOf('?') > 0) url += "&rand="+now;
227                 else url += "?rand="+now;
228                 url += "&trust="+trust;
229                 URL u = new URL(getDocumentBase(), url);
230                 Vector lv = new Vector();
231                 LineInputStream is = new LineInputStream(u.openStream());
232                 while(true)
233                         try { lv.addElement(is.gets()); }
234                         catch(EOFException eof) { break; }
235                 is.close();
236                 String rv[] = new String[lv.size()];
237                 lv.copyInto(rv);
238                 return rv;
239                 }
240         catch(Exception e) {
241                 e.printStackTrace();
242                 //return null;
243                 String err[] = { e.getMessage() };
244                 return err;
245                 }
246         }
247
248         // Fill the multicolumn list with files from some directory
249         boolean show_files(RemoteFile f)
250         {
251         RemoteFile fl[] = f.list();
252         if (fl == null) return false;
253         files.clear();
254         Object rows[][] = new Object[fl.length+1][];
255         long now = System.currentTimeMillis();
256
257         // Sort listing by chosen column
258         if (f != showing_files) {
259                 // Directory has changed .. assume sort by name
260                 files.sortingArrow(1, 1);
261                 }
262         else if (files.sortdir != 0) {
263                 // Sort by chosen order
264                 RemoteFile fls[] = new RemoteFile[fl.length];
265                 System.arraycopy(fl, 0, fls, 0, fl.length);
266                 QuickSort.sort(fls, files.sortcol, files.sortdir);
267                 fl = fls;
268                 }
269
270         // Create parent directory row
271         rows[0] = new Object[6];
272         rows[0][0] = get_image("dir.gif");
273         rows[0][1] = "..";
274         rows[0][2] = rows[0][3] = rows[0][4] = rows[0][5] = "";
275
276         // Create file rows
277         Date n = new Date(now);
278         for(int i=0; i<fl.length; i++) {
279                 Object row[] = rows[i+1] = new Object[6];
280                 if (fl[i].shared())
281                         row[0] = get_image("sdir.gif");
282                 else
283                         row[0] = get_image(RemoteFile.tmap[fl[i].type]);
284                 row[1] = fl[i].name;
285                 if (fl[i].size < 1000)
286                         row[2] = spad(fl[i].size, 5)+" B";
287                 else if (fl[i].size < 1000000)
288                         row[2] = spad(fl[i].size/1000, 5)+" kB";
289                 else
290                         row[2] = spad(fl[i].size/1000000, 5)+" MB";
291                 row[3] = fl[i].user;
292                 row[4] = fl[i].group;
293                 Date d = new Date(fl[i].modified);
294                 //if (now - fl[i].modified < 24*60*60*1000) {
295                 if (n.getDate() == d.getDate() &&
296                     n.getMonth() == d.getMonth() &&
297                     n.getYear() == d.getYear()) {
298                         // show as hour:min
299                         row[5] = pad(d.getHours(),2)+":"+
300                                  pad(d.getMinutes(),2);
301                         }
302                 //else if (now - fl[i].modified < 24*60*60*365*1000) {
303                 else if (n.getYear() == d.getYear()) {
304                         // show as day/mon
305                         row[5] = pad(d.getDate(),2)+"/"+
306                                  monmap[d.getMonth()];
307                         }
308                 else {
309                         // show as mon/year
310                         row[5] = monmap[d.getMonth()]+"/"+
311                                  pad(d.getYear()%100, 2);
312                         }
313                 }
314         files.addItems(rows);
315         showing_files = f;
316         showing_list = fl;
317         pathname.setText(f.path);
318         return true;
319         }
320
321         String pad(int n, int s)
322         {
323         String rv = String.valueOf(n);
324         while(rv.length() < s)
325                 rv = "0"+rv;
326         return rv;
327         }
328
329         String spad(long n, int s)
330         {
331         String rv = String.valueOf(n);
332         while(rv.length() < s)
333                 rv = " "+rv;
334         return rv;
335         }
336
337         String trim_path(String p)
338         {
339         while(p.endsWith("/"))
340                 p = p.substring(0, p.length()-1);
341         return p;
342         }
343
344         // openNode
345         // Called when a node with children is opened
346         public void openNode(Hierarchy h, HierarchyNode n)
347         {
348         FileNode fn = (FileNode)n;
349         fn.fill();
350         }
351
352         // closeNode
353         // Called when a node is closed
354         public void closeNode(Hierarchy h, HierarchyNode n)
355         {
356         }
357
358         // clickNode
359         // Called when the user clicks on a node
360         public void clickNode(Hierarchy h, HierarchyNode n)
361         {
362         FileNode fn = (FileNode)n;
363         if (showing_files != fn.file)
364                 show_files(fn.file);
365         }
366
367         // doubleNode
368         // Called when a user double-clicks on a node
369         public void doubleNode(Hierarchy h, HierarchyNode n)
370         {
371         }
372
373         // Called when a button is clicked
374         public void click(CbButton b)
375         {
376         int s = files.selected();
377         int ss[] = files.allSelected();
378         RemoteFile f = null, ff[] = new RemoteFile[0];
379         if (s > 0 || s == 0 && ss.length > 1) {
380                 // At least one non-.. file was selected
381                 boolean parentsel = false;
382                 for(int i=0; i<ss.length; i++)
383                         if (ss[i] == 0)
384                                 parentsel = true;
385                 RemoteFile list[] = showing_list;
386                 if (parentsel) {
387                         // need to exclude .. from selected list!
388                         ff = new RemoteFile[ss.length-1];
389                         for(int i=0,j=0; i<ss.length; i++)
390                                 if (ss[i] != 0)
391                                         ff[j++] = list[ss[i]-1];
392                         f = s == 0 ? ff[0] : list[s-1];
393                         }
394                 else {
395                         // include all selected files
396                         f = list[s-1];
397                         ff = new RemoteFile[ss.length];
398                         for(int i=0; i<ss.length; i++)
399                                 ff[i] = list[ss[i]-1];
400                         }
401                 }
402         FileNode d = (FileNode)dirs.selected();
403         if (b == ret_b) {
404                 // Return to the webmin index
405                 try {
406                         URL u = new URL(getDocumentBase(),
407                                         getParameter("return"));
408                         getAppletContext().showDocument(u);
409                         }
410                 catch(Exception e) { }
411                 }
412         else if (b == edit_b) {
413                 // Open a window for editing the selected file
414                 if (f == null) return;
415                 if (f.type == 0 || f.type > 4)
416                         new ErrorWindow(text("edit_enormal"));
417                 else
418                         new EditorWindow(f, this);
419                 }
420         else if (b == down_b) {
421                 // Force download of the selected file
422                 if (f == null) return;
423                 if (f.type == 0 || f.type > 4)
424                         new ErrorWindow(text("view_enormal2"));
425                 else
426                         show_file(f, true);
427                 }
428         else if (b == refresh_b) {
429                 // Refesh the selected directory (and thus any subdirs)
430                 d.known = false;
431                 d.file.list = null;
432                 d.fill();
433                 show_files(d.file);
434                 }
435         else if (b == props_b) {
436                 // Display the properties window
437                 if (f == null) return;
438                 new PropertiesWindow(f, this);
439                 }
440         else if (b == acl_b) {
441                 // Display the ACL window (if filesystem supports them)
442                 if (f == null) return;
443                 FileSystem filefs = find_filesys(f);
444                 if (filefs == null) return;
445                 if (filefs.acls)
446                         new ACLWindow(this, f);
447                 else
448                         new ErrorWindow(text("eacl_efs", filefs.mount));
449                 }
450         else if (b == attr_b) {
451                 // Display the attributes window (if filesystem supports them)
452                 if (f == null) return;
453                 FileSystem filefs = find_filesys(f);
454                 if (filefs == null) return;
455                 if (filefs.attrs)
456                         new AttributesWindow(this, f);
457                 else
458                         new ErrorWindow(text("attr_efs", filefs.mount));
459                 }
460         else if (b == ext_b) {
461                 // Display EXT attributes window (if filesystem supports them)
462                 if (f == null) return;
463                 FileSystem filefs = find_filesys(f);
464                 if (filefs == null) return;
465                 if (filefs.ext)
466                         new EXTWindow(this, f);
467                 else
468                         new ErrorWindow(text("ext_efs", filefs.mount));
469                 }
470         else if (b == copy_b) {
471                 // Copy the selected files
472                 if (f == null) return;
473                 cut_buffer = ff;
474                 cut_mode = false;
475                 }
476         else if (b == cut_b) {
477                 // Cut the selected file
478                 if (f == null) return;
479                 cut_buffer = ff;
480                 cut_mode = true;
481                 }
482         else if (b == paste_b) {
483                 // Paste the copied file
484                 if (cut_buffer == null) {
485                         new ErrorWindow(text("paste_ecopy"));
486                         return;
487                         }
488
489                 // Check for existing file clashes
490                 // XXX
491
492                 // Go through all the files to paste
493                 for(int i=0; i<cut_buffer.length; i++) {
494                         RemoteFile cf = cut_buffer[i];
495
496                         // Check for an existing file
497                         RemoteFile already = showing_files.find(cf.name);
498                         String sp = showing_files.path;
499                         String dest_path = sp.equals("/") ? sp+cf.name
500                                                           : sp+"/"+cf.name;
501                         if (already != null) {
502                                 // File exists .. offer to rename
503                                 new OverwriteWindow(this, already, cf, i);
504                                 }
505                         else {
506                                 // do the move or copy
507                                 RemoteFile nf = paste_file(cf, showing_files,
508                                                    dest_path, null, cut_mode);
509                                 if (cut_mode && nf != null) {
510                                         // Paste from the destination path
511                                         // from now on
512                                         cut_buffer[i] = nf;
513                                         }
514                                 }
515                         }
516                 cut_mode = false;
517                 }
518         else if (b == delete_b) {
519                 // Delete the selected files
520                 if (f == null) return;
521                 new DeleteWindow(this, ff);
522                 }
523         else if (b == new_b) {
524                 // Open a window for creating a file
525                 new EditorWindow(showing_files.path, this);
526                 }
527         else if (b == upload_b) {
528                 // Call javascript to open an upload window
529                 try {
530                         JSObject win = JSObject.getWindow(this);
531                         String params[] = { showing_files.path };
532                         win.call("upload", params);
533                         }
534                 catch(Exception e) {
535                         new ErrorWindow(text("upload_efailed", e.getMessage()));
536                         }
537                 }
538         else if (b == mkdir_b) {
539                 // Prompt for new directory
540                 new MkdirWindow(showing_files.path, this);
541                 }
542         else if (b == makelink_b) {
543                 // Prompt for a new symlink
544                 new LinkWindow(showing_files.path, this);
545                 }
546         else if (b == rename_b) {
547                 // Prompt for new filename
548                 if (f == null) return;
549                 new RenameWindow(this, f);
550                 }
551         else if (b == share_b) {
552                 // Open a window for editing sharing options
553                 if (f == null || f.type != RemoteFile.DIR) return;
554                 new SharingWindow(f, this);
555                 }
556         else if (b == search_b) {
557                 // Open window for finding a file
558                 new SearchWindow(showing_files.path, this);
559                 }
560         }
561
562         // Returns the object for some directory, or null if not found.
563         RemoteFile find_directory(String p, boolean fill)
564         {
565         int l = p.length();
566         boolean can = false;
567         for(int r=0; r<accroot.length; r++) {
568                 int rl = accroot[r].length();
569                 if (accroot[r].equals("/"))
570                         can = true;
571                 else if (l >= rl && p.substring(0, rl).equals(accroot[r]))
572                         can = true;
573                 else if (l < rl && accroot[r].substring(0, l).equals(p))
574                         can = true;
575                 }
576         if (!can) {
577                 new ErrorWindow(text("find_eaccess", p));
578                 return null;
579                 }
580         FileNode posnode = root;
581         RemoteFile pos = posnode.file;
582         StringTokenizer tok = new StringTokenizer(p, "/");
583         while(tok.hasMoreTokens()) {
584                 String fn = tok.nextToken();
585                 if (fn.equals("")) continue;
586                 RemoteFile fl[] = pos.list();
587                 if (fl == null) return null;
588                 if (fill) {
589                         posnode.open = true;
590                         posnode.fill();
591                         }
592                 boolean found = false;
593                 for(int i=0; i<fl.length; i++)
594                         if (fl[i].name.equals(fn)) {
595                                 pos = fl[i];
596                                 found = true;
597                                 }
598                 if (!found) {
599                         new ErrorWindow(text("find_eexist", fn, p));
600                         return null;
601                         }
602                 if (pos.type != 0) {
603                         new ErrorWindow(text("find_edir", fn, p));
604                         return null;
605                         }
606                 if (fill)
607                         posnode = (FileNode)nodemap.get(pos);
608                 }
609         if (fill) {
610                 if (show_files(pos)) {
611                         posnode.fill();
612                         posnode.open = true;
613                         dirs.select(posnode);
614                         dirs.redraw();
615                         }
616                 }
617         return pos;
618         }
619
620         FileSystem find_filesys(RemoteFile f)
621         {
622         FileSystem filefs = null;
623         for(int i=0; i<fslist.size(); i++) {
624                 FileSystem fs = (FileSystem)fslist.elementAt(i);
625                 int l = fs.mount.length();
626                 if (fs.mount.equals(f.path) ||
627                     (f.path.length() >= l+1 &&
628                      f.path.substring(0, l+1).equals(fs.mount+"/")) ||
629                     fs.mount.equals("/")) {
630                         filefs = fs;
631                         }
632                 }
633         return filefs;
634         }
635
636         public boolean action(Event e, Object o)
637         {
638         if (e.target == pathname) {
639                 // A new path was entered.. cd to it
640                 String p = pathname.getText().trim();
641                 if (p.equals("")) return true;
642                 find_directory(p, true);
643                 return true;
644                 }
645         return false;
646         }
647
648         // singleClick
649         // Called on a single click on a list item
650         public void singleClick(MultiColumn list, int num)
651         {
652         }
653
654         // doubleClick
655         // Called upon double-clicking on a list item
656         public void doubleClick(MultiColumn list, int num)
657         {
658         if (num == 0) {
659                 // Go to parent directory
660                 if (showing_files.directory != null) {
661                         ((FileNode)nodemap.get(showing_files)).open = false;
662                         show_files(showing_files.directory);
663                         dirs.select((FileNode)nodemap.get(showing_files));
664                         dirs.redraw();
665                         }
666                 return;
667                 }
668         RemoteFile d = showing_list[num-1];
669         if (d.type == 0) {
670                 // Open this directory
671                 FileNode pn = (FileNode)nodemap.get(showing_files);
672                 pn.fill();
673                 pn.open = true;
674                 FileNode fn = (FileNode)nodemap.get(d);
675                 if (show_files(d)) {
676                         fn.fill();
677                         fn.open = true;
678                         dirs.select(fn);
679                         dirs.redraw();
680                         }
681                 }
682         else if (d.type <= 4) {
683                 // Direct the browser to this file
684                 show_file(d, list.last_event.shiftDown());
685                 }
686         }
687
688         // Called when the user clicks on a column heading so that it can
689         // be sorted.
690         public void headingClicked(MultiColumn list, int col)
691         {
692         if (col == 0)
693                 return; // ignore click on icon column?
694         if (col == list.sortcol) {
695                 list.sortingArrow(col, list.sortdir == 2 ? 1 : 2);
696                 }
697         else {
698                 list.sortingArrow(col, 1);
699                 }
700
701         // Re-show the list in the new order, but with the same files selected
702         int ss[] = files.allSelected();
703         RemoteFile ssf[] = new RemoteFile[ss.length];
704         for(int i=0; i<ss.length; i++)
705                 ssf[i] = showing_list[ss[i]-1];
706         show_files(showing_files);
707         for(int i=0; i<ss.length; i++) {
708                 for(int j=0; j<showing_list.length; j++) {
709                         if (showing_list[j] == ssf[i]) {
710                                 ss[i] = j+1;
711                                 break;
712                                 }
713                         }
714                 }
715         files.select(ss);
716         }
717
718         void show_file(RemoteFile f, boolean download)
719         {
720         try {
721                 if (download) {
722                         URL u = new URL(getDocumentBase(),
723                                         "show.cgi"+urlize(f.path)+
724                                         "?rand="+System.currentTimeMillis()+
725                                         "&type=application%2Funknown"+
726                                         "&trust="+trust);
727                         getAppletContext().showDocument(u);
728                         }
729                 else {
730                         URL u = new URL(getDocumentBase(),
731                                         "show.cgi"+urlize(f.path)+
732                                         "?rand="+System.currentTimeMillis()+
733                                         "&trust="+trust);
734                         getAppletContext().showDocument(u, "show");
735                         }
736                 }
737         catch(Exception e) { }
738         }
739
740         static String urlize(String s)
741         {
742         StringBuffer rv = new StringBuffer();
743         for(int i=0; i<s.length(); i++) {
744                 char c = s.charAt(i);
745                 if (c < 16)
746                         rv.append("%0"+Integer.toString(c, 16));
747                 else if (!Character.isLetterOrDigit(c) && c != '/' &&
748                     c != '.' && c != '_' && c != '-')
749                         rv.append("%"+Integer.toString(c, 16));
750                 else
751                         rv.append(c);
752                 }
753         return rv.toString();
754         }
755
756         static String un_urlize(String s)
757         {
758         StringBuffer rv = new StringBuffer();
759         for(int i=0; i<s.length(); i++) {
760                 char c = s.charAt(i);
761                 if (c == '%') {
762                         rv.append((char)Integer.parseInt(
763                                 s.substring(i+1, i+3), 16));
764                         i += 2;
765                         }
766                 else
767                         rv.append(c);
768                 }
769         return rv.toString();
770         }
771
772         public void upload_notify(String path_str, String info)
773         {
774         int sl = path_str.lastIndexOf('/');
775         String par_str = path_str.substring(0, sl),
776                file_str = path_str.substring(sl+1);
777         RemoteFile par = find_directory(par_str, false);
778         RemoteFile upfile = par.find(file_str);
779         if (upfile == null) {
780                 upfile = new RemoteFile(this, info, par);
781                 par.add(upfile);
782                 }
783         show_files(showing_files);
784         }
785
786         public String text(String k, String p[])
787         {
788         String rv = (String)lang.get(k);
789         if (rv == null) rv = "???";
790         for(int i=0; i<p.length; i++) {
791                 int idx = rv.indexOf("$"+(i+1));
792                 if (idx != -1)
793                         rv = rv.substring(0, idx)+p[i]+rv.substring(idx+2);
794                 }
795         return rv;
796         }
797
798         public String text(String k)
799         {
800         String p[] = { };
801         return text(k, p);
802         }
803
804         public String text(String k, String p1)
805         {
806         String p[] = { p1 };
807         return text(k, p);
808         }
809
810         public String text(String k, String p1, String p2)
811         {
812         String p[] = { p1, p2 };
813         return text(k, p);
814         }
815
816         RemoteFile paste_file(RemoteFile src, RemoteFile dir,
817                               String dest, RemoteFile already, boolean mode)
818         {
819         // Move or copy the actual file
820         String[] rv = get_text((mode ? "move.cgi" : "copy.cgi")+
821                                "?from="+urlize(src.path)+
822                                "&to="+urlize(dest));
823         if (rv[0].length() > 0) {
824                 new ErrorWindow(text(
825                         mode ? "paste_emfailed" : "paste_ecfailed", rv[0]));
826                 return null;
827                 }
828         RemoteFile file = new RemoteFile(this, rv[1], dir);
829         if (already == null) {
830                 // Add to the parent directory
831                 dir.add(file);
832                 }
833         else {
834                 // Update the existing file
835                 already.type = file.type;
836                 already.user = file.user;
837                 already.group = file.group;
838                 already.size = file.size;
839                 already.perms = file.perms;
840                 already.modified = file.modified;
841                 file = already;
842                 }
843         if (mode) {
844                 // Delete the old file
845                 src.directory.delete(src);
846                 }
847         if (src.type == 0) {
848                 // Moving or copying a directory.. update the tree
849                 FileNode dest_par_node =
850                         (FileNode)nodemap.get(showing_files);
851                 dest_par_node.add(new FileNode(file));
852                 if (mode) {
853                         FileNode cut_par_node =
854                                 (FileNode)nodemap.get(src.directory);
855                         FileNode cut_file_node =
856                                 (FileNode)nodemap.get(src);
857                         if (cut_par_node != null &&
858                             cut_file_node != null)
859                                 cut_par_node.ch.removeElement(
860                                                         cut_file_node);
861                         }
862                 dirs.redraw();
863                 }
864         show_files(showing_files);
865         return file;
866         }
867 }
868
869 // A node in the directory tree
870 class FileNode extends HierarchyNode
871 {
872         FileManager parent;
873         RemoteFile file;
874         boolean known;
875
876         FileNode(RemoteFile file)
877         {
878         this.file = file;
879         parent = file.parent;
880         im = parent.get_image(file.shared() ? "sdir.gif" : "dir.gif");
881         ch = new Vector();
882         text = file.name;
883         parent.nodemap.put(file, this);
884         }
885
886         // Create the nodes for subdirectories
887         void fill()
888         {
889         if (!known) {
890                 RemoteFile l[] = file.list();
891                 if (l == null) return;
892                 ch.removeAllElements();
893                 for(int i=0; i<l.length; i++)
894                         if (l[i].type == 0)
895                                 ch.addElement(new FileNode(l[i]));
896                 parent.dirs.redraw();
897                 known = true;
898                 }
899         }
900
901         void add(FileNode n)
902         {
903         for(int i=0; i<=ch.size(); i++) {
904                 FileNode ni = i==ch.size() ? null : (FileNode)ch.elementAt(i);
905                 if (ni == null || ni.text.compareTo(n.text) > 0) {
906                         ch.insertElementAt(n, i);
907                         break;
908                         }
909                 }
910         }
911
912 }
913
914 class RemoteFile
915 {
916         static final int DIR = 0;
917         static final int TEXT = 1;
918         static final int IMAGE = 2;
919         static final int BINARY = 3;
920         static final int UNKNOWN = 4;
921         static final int SYMLINK = 5;
922         static final int DEVICE = 6;
923         static final int PIPE = 7;
924         static final String[] tmap = { "dir.gif", "text.gif", "image.gif",
925                                        "binary.gif", "unknown.gif",
926                                        "symlink.gif", "device.gif",
927                                        "pipe.gif" };
928
929         FileManager parent;
930         String path, name;
931         int type;
932         String user, group;
933         long size;
934         int perms;
935         long modified;
936         String linkto;
937         RemoteFile list[];
938         RemoteFile directory;
939
940         // Parse a line of text to a file object
941         RemoteFile(FileManager parent, String line, RemoteFile d)
942         {
943         this.parent = parent;
944         StringTokenizer tok = new StringTokenizer(line, "\t");
945         path = tok.nextToken();
946         type = Integer.parseInt(tok.nextToken());
947         user = tok.nextToken();
948         group = tok.nextToken();
949         size = Long.parseLong(tok.nextToken());
950         perms = Integer.parseInt(tok.nextToken());
951         modified = Long.parseLong(tok.nextToken())*1000;
952         if (type == 5) linkto = tok.nextToken();
953         directory = d;
954         if (path.equals("/")) name = "/";
955         else name = path.substring(path.lastIndexOf('/')+1);
956         }
957
958         // Create a new, empty file object
959         RemoteFile() { }
960
961         // Returns a list of files in this directory
962         RemoteFile[] list()
963         {
964         if (list == null) {
965                 String l[] = parent.get_text("list.cgi?dir="+
966                                              parent.urlize(path));
967                 if (l[0].length() > 0) {
968                         //list = new RemoteFile[0];
969                         // Error reading the remote directory!
970                         new ErrorWindow(parent.text("list_edir", path, l[0]));
971                         list = null;
972                         }
973                 else {
974                         list = new RemoteFile[l.length-3];
975                         for(int i=3; i<l.length; i++)
976                                 list[i-3] = new RemoteFile(parent, l[i], this);
977                         }
978                 }
979         return list;
980         }
981
982         RemoteFile find(String n)
983         {
984         RemoteFile l[] = list();
985         if (l != null) {
986                 for(int i=0; i<l.length; i++)
987                         if (l[i].name.equals(n))
988                                 return l[i];
989                 }
990         return null;
991         }
992
993         void add(RemoteFile f)
994         {
995         RemoteFile nlist[] = new RemoteFile[list.length+1];
996         int offset = 0;
997         for(int i=0; i<list.length; i++) {
998                 if (list[i].name.compareTo(f.name) > 0 && offset == 0) {
999                         nlist[i] = f;
1000                         offset++;
1001                         }
1002                 nlist[i+offset] = list[i];
1003                 }
1004         if (offset == 0) nlist[list.length] = f;
1005         list = nlist;
1006         }
1007
1008         void delete(RemoteFile f)
1009         {
1010         RemoteFile nlist[] = new RemoteFile[list.length-1];
1011         for(int i=0,j=0; i<list.length; i++)
1012                 if (list[i] != f)
1013                         nlist[j++] = list[i];
1014         list = nlist;
1015         }
1016
1017         boolean shared()
1018         {
1019         return type == DIR &&
1020                (parent.stab.get(path) != null ||
1021                 parent.ntab.get(path) != null);
1022         }
1023 }
1024
1025 class EditorWindow extends FixedFrame implements CbButtonCallback
1026 {
1027         TextField name;
1028         TextArea edit;
1029         CbButton save_b, cancel_b, goto_b, find_b;
1030         RemoteFile file;
1031         FileManager filemgr;
1032         GotoWindow goto_window;
1033         FindReplaceWindow find_window;
1034
1035         // Editing an existing file
1036         EditorWindow(RemoteFile f, FileManager p)
1037         {
1038         super(500, 300);
1039         file = f; filemgr = p;
1040         makeUI(false);
1041         setTitle(filemgr.text("edit_title", file.path));
1042
1043         // Load the file
1044         try {
1045                 URL u = new URL(filemgr.getDocumentBase(),
1046                                 "show.cgi"+filemgr.urlize(file.path)+
1047                                 "?rand="+System.currentTimeMillis()+
1048                                 "&trust="+filemgr.trust+"&edit=1");
1049                 URLConnection uc = u.openConnection();
1050                 int len = uc.getContentLength();
1051                 InputStream is = uc.getInputStream();
1052                 byte buf[];
1053                 if (len >= 0) {
1054                         // Length is known
1055                         buf = new byte[uc.getContentLength()];
1056                         int got = 0;
1057                         while(got < buf.length)
1058                                 got += is.read(buf, got, buf.length-got);
1059                         }
1060                 else {
1061                         // Length is unknown .. read till the end
1062                         buf = new byte[0];
1063                         while(true) {
1064                             byte data[] = new byte[16384];
1065                             int got;
1066                             try { got = is.read(data); }
1067                             catch(EOFException ex) { break; }
1068                             if (got <= 0) break;
1069                             byte nbuf[] = new byte[buf.length + got];
1070                             System.arraycopy(buf, 0, nbuf, 0, buf.length);
1071                             System.arraycopy(data, 0, nbuf, buf.length, got);
1072                             buf = nbuf;
1073                             }
1074                         }
1075                 edit.setText(new String(buf, 0));
1076                 is.close();
1077                 file.size = buf.length;
1078                 }
1079         catch(Exception e) { e.printStackTrace(); }
1080         }
1081
1082         // Creating a new file
1083         EditorWindow(String f, FileManager p)
1084         {
1085         super(500, 300);
1086         filemgr = p;
1087         makeUI(true);
1088         setTitle(filemgr.text("edit_title2"));
1089         name.setText(f.equals("/") ? f : f+"/");
1090         name.select(name.getText().length(), name.getText().length());
1091         }
1092
1093         void makeUI(boolean add_name)
1094         {
1095         setLayout(new BorderLayout());
1096         if (add_name) {
1097                 Panel np = new Panel();
1098                 np.setLayout(new BorderLayout());
1099                 np.add("West", new Label(filemgr.text("edit_filename")));
1100                 np.add("Center", name = new TextField());
1101                 add("North", np);
1102                 }
1103         add("Center", edit = new TextArea(20, 80));
1104         edit.setFont(new Font("courier", Font.PLAIN, 14));
1105         Panel bot = new Panel();
1106         bot.setLayout(new FlowLayout(FlowLayout.RIGHT));
1107         bot.add(goto_b = new CbButton(filemgr.get_image("goto.gif"),
1108                                       filemgr.text("edit_goto"),
1109                                       CbButton.LEFT, this));
1110         bot.add(find_b = new CbButton(filemgr.get_image("find.gif"),
1111                                       filemgr.text("edit_find"),
1112                                       CbButton.LEFT, this));
1113         bot.add(new Label(" "));
1114         bot.add(save_b = new CbButton(filemgr.get_image("save.gif"),
1115                                       filemgr.text("save"),
1116                                       CbButton.LEFT, this));
1117         bot.add(cancel_b = new CbButton(filemgr.get_image("cancel.gif"),
1118                                         filemgr.text("cancel"),
1119                                         CbButton.LEFT, this));
1120         add("South", bot);
1121         pack();
1122         show();
1123         }
1124
1125         public void click(CbButton b)
1126         {
1127         if (b == save_b) {
1128                 RemoteFile par = null, already = null;
1129                 String save_path;
1130                 if (file == null) {
1131                         // Locate the filemgr directory
1132                         save_path = filemgr.trim_path(name.getText());
1133                         int sl = save_path.lastIndexOf('/');
1134                         par = filemgr.find_directory(
1135                                         save_path.substring(0, sl), false);
1136                         if (par == null) return;
1137                         already = par.find(save_path.substring(sl+1));
1138                         if (already != null &&
1139                             (already.type == 0 || already.type == 5)) {
1140                                 new ErrorWindow(
1141                                         filemgr.text("edit_eover", save_path));
1142                                 return;
1143                                 }
1144                         }
1145                 else save_path = file.path;
1146
1147                 // Save the file back again
1148                 String s = edit.getText(), line;
1149                 try {
1150                         URL u = new URL(filemgr.getDocumentBase(),
1151                                         "save.cgi"+filemgr.urlize(save_path)+
1152                                         "?rand="+System.currentTimeMillis()+
1153                                         "&trust="+filemgr.trust);
1154                         URLConnection uc = u.openConnection();
1155                         uc.setDoOutput(true);
1156                         OutputStream os = uc.getOutputStream();
1157                         byte buf[] = new byte[s.length()];
1158                         s.getBytes(0, buf.length, buf, 0);
1159                         os.write(buf);
1160                         os.close();
1161                         LineInputStream is = new LineInputStream(
1162                                                         uc.getInputStream());
1163                         String err = is.gets();
1164                         if (err.length() > 0) {
1165                                 new ErrorWindow(
1166                                         filemgr.text("edit_esave", err));
1167                                 is.close();
1168                                 return;
1169                                 }
1170                         line = is.gets();
1171                         is.close();
1172                         }
1173                 catch(Exception e) { e.printStackTrace(); return; }
1174
1175                 if (file == null) {
1176                         // Create and insert or replace the file object
1177                         file = new RemoteFile(filemgr, line, par);
1178                         if (already != null) {
1179                                 // A file with this name exists
1180                                 already.type = file.type;
1181                                 already.user = file.user;
1182                                 already.group = file.group;
1183                                 already.size = file.size;
1184                                 already.perms = file.perms;
1185                                 already.modified = file.modified;
1186                                 }
1187                         else {
1188                                 // Add to the list
1189                                 par.add(file);
1190                                 }
1191                         }
1192                 else {
1193                         file.size = s.length();
1194                         file.modified = System.currentTimeMillis();
1195                         }
1196                 filemgr.show_files(filemgr.showing_files);
1197                 dispose();
1198                 }
1199         else if (b == cancel_b) {
1200                 // Just close
1201                 dispose();
1202                 }
1203         else if (b == goto_b) {
1204                 // Open a dialog asking which line to go to
1205                 if (goto_window != null)
1206                         goto_window.toFront();
1207                 else
1208                         goto_window = new GotoWindow(this);
1209                 }
1210         else if (b == find_b) {
1211                 // Open the search (and replace) dialog
1212                 if (find_window != null)
1213                         find_window.toFront();
1214                 else
1215                         find_window = new FindReplaceWindow(this);
1216                 }
1217         }
1218
1219         public void dispose()
1220         {
1221         super.dispose();
1222         if (goto_window != null) goto_window.dispose();
1223         if (find_window != null) find_window.dispose();
1224         }
1225 }
1226
1227 class GotoWindow extends FixedFrame implements CbButtonCallback
1228 {
1229         EditorWindow editor;
1230         FileManager filemgr;
1231         TextField line;
1232         CbButton goto_b, cancel_b;
1233
1234         GotoWindow(EditorWindow e)
1235         {
1236         editor = e;
1237         filemgr = e.filemgr;
1238
1239         setLayout(new BorderLayout());
1240         add("West", new Label(filemgr.text("edit_gotoline")));
1241         add("Center", line = new TextField(10));
1242         Panel bot = new Panel();
1243         bot.setLayout(new FlowLayout(FlowLayout.RIGHT));
1244         bot.add(goto_b = new CbButton(filemgr.get_image("goto.gif"),
1245                                       filemgr.text("edit_goto"),
1246                                       CbButton.LEFT, this));
1247         bot.add(cancel_b = new CbButton(filemgr.get_image("cancel.gif"),
1248                                         filemgr.text("close"),
1249                                         CbButton.LEFT, this));
1250         add("South", bot);
1251         pack();
1252         show();
1253         }
1254
1255         public void click(CbButton b)
1256         {
1257         if (b == goto_b) {
1258                 // Go to the chose line, if it exists
1259                 int lnum;
1260                 try { lnum = Integer.parseInt(line.getText()); }
1261                 catch(Exception e) { return; }
1262
1263                 String txt = editor.edit.getText();
1264                 int c, l = 0;
1265                 for(c=0; c<txt.length(); c++) {
1266                         if (txt.charAt(c) == '\n') {
1267                                 l++;
1268                                 if (l == lnum) {
1269                                         // Found the line!
1270                                         editor.edit.select(c, c);
1271                                         dispose();
1272                                         return;
1273                                         }
1274                                 }
1275                         }
1276                 }
1277         else if (b == cancel_b) {
1278                 // Just close the window
1279                 dispose();
1280                 }
1281         }
1282
1283         public void dispose()
1284         {
1285         super.dispose();
1286         editor.goto_window = null;
1287         }
1288
1289         public boolean handleEvent(Event e)
1290         {
1291         if (e.target == line && e.id == Event.KEY_RELEASE && e.key == 10) {
1292                 click(goto_b);
1293                 return true;
1294                 }
1295         return false;
1296         }
1297 }
1298
1299 class FindReplaceWindow extends FixedFrame implements CbButtonCallback
1300 {
1301         EditorWindow editor;
1302         FileManager filemgr;
1303         TextField find, replace;
1304         CbButton find_b, replace_b, all_b, cancel_b;
1305
1306         FindReplaceWindow(EditorWindow e)
1307         {
1308         editor = e;
1309         filemgr = e.filemgr;
1310         setLayout(new BorderLayout());
1311
1312         Panel left = new Panel();
1313         left.setLayout(new GridLayout(2, 1));
1314         left.add(new Label(filemgr.text("edit_searchfor")));
1315         left.add(new Label(filemgr.text("edit_replaceby")));
1316         add("West", left);
1317
1318         Panel right = new Panel();
1319         right.setLayout(new GridLayout(2, 1));
1320         right.add(find = new TextField(40));
1321         right.add(replace = new TextField(40));
1322         add("Center", right);
1323
1324         Panel bot = new Panel();
1325         bot.setLayout(new FlowLayout(FlowLayout.RIGHT));
1326         bot.add(find_b = new CbButton(filemgr.get_image("find.gif"),
1327                                       filemgr.text("edit_find"),
1328                                       CbButton.LEFT, this));
1329         bot.add(replace_b = new CbButton(filemgr.get_image("replace.gif"),
1330                                       filemgr.text("edit_replace"),
1331                                       CbButton.LEFT, this));
1332         bot.add(all_b = new CbButton(filemgr.get_image("all.gif"),
1333                                       filemgr.text("edit_all"),
1334                                       CbButton.LEFT, this));
1335         bot.add(cancel_b = new CbButton(filemgr.get_image("cancel.gif"),
1336                                         filemgr.text("close"),
1337                                         CbButton.LEFT, this));
1338         add("South", bot);
1339         pack();
1340         show();
1341         }
1342
1343         public void click(CbButton b)
1344         {
1345         String findtxt = find.getText();
1346         String edittxt = editor.edit.getText();
1347         if (findtxt.length() == 0)
1348                 return;
1349         if (b == find_b) {
1350                 // Find the next occurrance of the text, starting from
1351                 // the cursor + 1, and select it
1352                 int pos = edittxt.indexOf(findtxt,
1353                                            editor.edit.getSelectionStart()+1);
1354                 if (pos < 0)
1355                         new ErrorWindow(filemgr.text("edit_notfound", findtxt));
1356                 else
1357                         editor.edit.select(pos, pos+findtxt.length());
1358                 }
1359         else if (b == replace_b) {
1360                 // If the word to search for is selected, replace it. Otherwise
1361                 // just search for the next one
1362                 int st = editor.edit.getSelectionStart(),
1363                     en = editor.edit.getSelectionEnd();
1364                 if (st >= 0) {
1365                         String sel = edittxt.substring(st, en);
1366                         if (sel.equals(findtxt)) {
1367                                 // Replace the selected
1368                                 editor.edit.setText(edittxt.substring(0, st)+
1369                                                     replace.getText()+
1370                                                     edittxt.substring(en));
1371                                 editor.edit.select(st, st);
1372                                 return;
1373                                 }
1374                         }
1375                 click(find_b);
1376                 }
1377         else if (b == all_b) {
1378                 // Replace all occurrances of the text in the editor
1379                 int pos = 0;
1380                 int len = findtxt.length();
1381                 int st = editor.edit.getSelectionStart(),
1382                     en = editor.edit.getSelectionEnd();
1383                 while((pos = edittxt.indexOf(findtxt, pos)) != -1) {
1384                         edittxt = edittxt.substring(0, pos)+
1385                                   replace.getText()+
1386                                   edittxt.substring(pos+len);
1387                         pos += len;
1388                         }
1389                 editor.edit.setText(edittxt);
1390                 editor.edit.select(st, en);     // put back old selection
1391                 }
1392         else if (b == cancel_b) {
1393                 // Just close the window
1394                 dispose();
1395                 }
1396         }
1397
1398         public void dispose()
1399         {
1400         super.dispose();
1401         editor.find_window = null;
1402         }
1403 }
1404
1405 class PropertiesWindow extends FixedFrame implements CbButtonCallback
1406 {
1407         RemoteFile file;
1408         FileManager filemgr;
1409         CbButton save_b, cancel_b;
1410
1411         TextField linkto;
1412         TextField user, group;
1413         Checkbox setuid, setgid;
1414         PermissionsPanel user_p, group_p, other_p;
1415         Checkbox sticky;
1416         Choice rec_mode;
1417         TextField octal;
1418
1419         PropertiesWindow(RemoteFile f, FileManager p)
1420         {
1421         file = f;
1422         filemgr = p;
1423
1424         // Create UI
1425         setTitle(f.path);
1426         setLayout(new BorderLayout());
1427         Panel bot = new Panel();
1428         bot.setLayout(new FlowLayout(FlowLayout.RIGHT));
1429         bot.add(save_b = new CbButton(filemgr.get_image("save.gif"),
1430                                       filemgr.text("save"),
1431                                       CbButton.LEFT, this));
1432         bot.add(cancel_b = new CbButton(filemgr.get_image("cancel.gif"),
1433                                         filemgr.text("cancel"),
1434                                         CbButton.LEFT, this));
1435         add("South", bot);
1436
1437         Panel mid = new Panel();
1438         mid.setLayout(new BorderLayout());
1439         TabbedPanel tab = null;
1440         add("Center", mid);
1441
1442         // Create file details section
1443         Panel det = new LinedPanel(filemgr.text("info_file")),
1444               dl = new Panel(), dr = new Panel();
1445         setup_leftright(det, dl, dr);
1446         add_item(filemgr.text("info_path"),
1447                 new Label(file.path), dl, dr);
1448         add_item(filemgr.text("info_type"),
1449                 new Label(filemgr.text("file_type"+file.type)), dl, dr);
1450         add_item(filemgr.text("info_size"),
1451                 new Label(String.valueOf(file.size)),dl,dr);
1452         add_item(filemgr.text("info_mod"),
1453                 new Label(String.valueOf(new Date(file.modified))), dl, dr);
1454         if (file.type == 5)
1455                 add_item(filemgr.text("info_link"),
1456                          linkto = new TextField(file.linkto, 30), dl, dr);
1457         mid = add_panel(mid, det);
1458
1459         // Create permissions section
1460         Panel per = new LinedPanel(filemgr.text("info_perms")),
1461               pl = new Panel(), pr = new Panel();
1462         setup_leftright(per, pl, pr);
1463         add_item(filemgr.text("info_user"),
1464                  user_p = new PermissionsPanel(file, 64, filemgr), pl, pr);
1465         add_item(filemgr.text("info_group"),
1466                  group_p = new PermissionsPanel(file, 8, filemgr), pl, pr);
1467         add_item(filemgr.text("info_other"),
1468                  other_p = new PermissionsPanel(file, 1, filemgr), pl,pr);
1469         if (file.type == 0) {
1470                 add_item(filemgr.text("info_sticky"), sticky = new Checkbox(
1471                                         filemgr.text("info_sticky2")), pl,pr);
1472                 sticky.setState((file.perms&01000) != 0);
1473                 }
1474         add_item(filemgr.text("info_octal"), octal = new TextField(4), pl, pr);
1475         octal.setEditable(false);
1476         mid = add_panel(mid, per);
1477
1478         // Create ownership section
1479         Panel own = new LinedPanel(filemgr.text("info_own")),
1480               ol = new Panel(), or = new Panel();
1481         setup_leftright(own, ol, or);
1482         add_item(filemgr.text("info_user"),
1483                  user = new TextField(file.user, 10), ol, or);
1484         if (file.type != 0) {
1485                 add_item(filemgr.text("info_setuid"),
1486                          setuid = new Checkbox(filemgr.text("info_setuid2")),
1487                          ol, or);
1488                 setuid.setState((file.perms & 0x800) != 0);
1489                 }
1490         add_item(filemgr.text("info_group"),
1491                  group = new TextField(file.group, 10), ol, or);
1492         if (file.type == 0)
1493                 add_item(filemgr.text("info_setgid"),
1494                   setgid = new Checkbox(filemgr.text("info_setgid2")), ol, or);
1495         else
1496                 add_item(filemgr.text("info_setgid"),
1497                   setgid = new Checkbox(filemgr.text("info_setgid3")), ol, or);
1498         setgid.setState((file.perms & 0x400) != 0);
1499         mid = add_panel(mid, own);
1500
1501         if (file.type == 0) {
1502                 // Create recursion section
1503                 Panel rec = new LinedPanel(filemgr.text("info_apply"));
1504                 rec.setLayout(new BorderLayout());
1505                 rec_mode = new Choice();
1506                 for(int i=1; i<=3; i++)
1507                         rec_mode.addItem(filemgr.text("info_apply"+i));
1508                 rec.add("Center", rec_mode);
1509                 mid = add_panel(mid, rec);
1510                 }
1511
1512         set_octal();
1513         pack();
1514         show();
1515         }
1516
1517         Panel add_panel(Panel p, Component c)
1518         {
1519         p.add("North", c);
1520         Panel np = new Panel();
1521         np.setLayout(new BorderLayout());
1522         p.add("Center", np);
1523         return np;
1524         }
1525
1526         public void click(CbButton b)
1527         {
1528         if (b == save_b) {
1529                 // Update the file
1530                 int perms = get_perms();
1531                 int rec = 0;
1532                 if (file.type == 0)
1533                         rec = rec_mode.getSelectedIndex();
1534                 String rv[] = filemgr.get_text(
1535                         "chmod.cgi?path="+filemgr.urlize(file.path)+
1536                         "&perms="+perms+"&user="+user.getText()+
1537                         "&group="+group.getText()+"&rec="+rec+
1538                         (linkto==null ? "" : "&linkto="+linkto.getText()));
1539                 if (rv[0].length() > 0) {
1540                         // Something went wrong
1541                         new ErrorWindow(filemgr.text("info_efailed",
1542                                         file.path, rv[0]));
1543                         }
1544                 else {
1545                         // Update all changed file objects
1546                         if (linkto != null)
1547                                 file.linkto = linkto.getText();
1548                         else if (rec == 0)
1549                                 update_file(file, perms, false);
1550                         else if (rec == 1) {
1551                                 // Update files in this directory
1552                                 update_file(file, perms, false);
1553                                 recurse_files(file, perms, false);
1554                                 }
1555                         else if (rec == 2) {
1556                                 // Update files and subdirs
1557                                 update_file(file, perms, false);
1558                                 recurse_files(file, perms, true);
1559                                 }
1560
1561                         // Update directory list
1562                         int os = filemgr.files.selected();
1563                         filemgr.show_files(filemgr.showing_files);
1564                         filemgr.files.select(os);
1565                         dispose();
1566                         }
1567                 }
1568         else {
1569                 // Just close
1570                 dispose();
1571                 }
1572         }
1573
1574         void update_file(RemoteFile f, int perms, boolean perms_only)
1575         {
1576         f.user = user.getText();
1577         f.group = group.getText();
1578         if (perms_only)
1579                 f.perms = (perms & 0777) | (f.perms & 037777777000);
1580         else
1581                 f.perms = perms;
1582         }
1583
1584         void recurse_files(RemoteFile f, int perms, boolean do_subs)
1585         {
1586         if (f.list == null) return;
1587         for(int i=0; i<f.list.length; i++) {
1588                 RemoteFile ff = f.list[i];
1589                 if (ff.type == 5) continue;
1590                 else if (ff.type == 0) {
1591                         if (do_subs) {
1592                                 update_file(ff, perms, false);
1593                                 recurse_files(ff, perms, true);
1594                                 }
1595                         }
1596                 else update_file(ff, perms, true);
1597                 }
1598         }
1599
1600         void setup_leftright(Panel m, Panel l, Panel r)
1601         {
1602         m.setLayout(new BorderLayout());
1603         Panel p = new Panel();
1604         p.setLayout(new BorderLayout());
1605         p.add("West", l);
1606         p.add("Center", r);
1607         l.setLayout(new GridLayout(0, 1));
1608         r.setLayout(new GridLayout(0, 1));
1609         m.add("North", p);
1610         }
1611
1612         void add_item(String t, Component c, Panel l, Panel r)
1613         {
1614         l.add(new Label(t));
1615         Panel p = new Panel();
1616         p.setLayout(new BorderLayout());
1617         p.add("West", c);
1618         r.add(p);
1619         }
1620
1621         void set_octal()
1622         {
1623         String oct = Integer.toOctalString(get_perms());
1624         while(oct.length() < 4)
1625                 oct = "0"+oct;
1626         octal.setText(oct);
1627         }
1628
1629         int get_perms()
1630         {
1631         int perms = 0;
1632         if (setuid == null)
1633                 perms |= (file.perms & 0x800);
1634         else
1635                 perms |= (setuid.getState() ? 0x800 : 0);
1636         perms |= (setgid.getState() ? 0x400 : 0);
1637         perms |= user_p.getPerms();
1638         perms |= group_p.getPerms();
1639         perms |= other_p.getPerms();
1640         if (sticky == null)
1641                 perms |= (file.perms & 01000);
1642         else
1643                 perms |= (sticky.getState() ? 01000 : 0);
1644         return perms;
1645         }
1646
1647         public boolean handleEvent(Event e)
1648         {
1649         if (e.target instanceof Checkbox) {
1650                 set_octal();
1651                 return true;
1652                 }
1653         return false;
1654         }
1655 }
1656
1657 class PermissionsPanel extends Panel
1658 {
1659         Checkbox read, write, exec;
1660         int base;
1661
1662         PermissionsPanel(RemoteFile file, int base, FileManager filemgr)
1663         {
1664         int perms = file.perms;
1665         this.base = base;
1666         setLayout(new GridLayout(1, 3));
1667         add(read = new Checkbox(filemgr.text("info_read")));
1668         read.setState((perms&(base<<2)) != 0);
1669         add(write = new Checkbox(filemgr.text("info_write")));
1670         write.setState((perms&(base<<1)) != 0);
1671         add(exec = new Checkbox(
1672                 filemgr.text(file.type==0 ? "info_list" : "info_exec")));
1673         exec.setState((perms&base) != 0);
1674         }
1675
1676         int getPerms()
1677         {
1678         int rv = 0;
1679         rv |= (read.getState() ? (base<<2) : 0);
1680         rv |= (write.getState() ? (base<<1) : 0);
1681         rv |= (exec.getState() ? base : 0);
1682         return rv;
1683         }
1684 }
1685
1686 class DeleteWindow extends FixedFrame implements CbButtonCallback
1687 {
1688         CbButton delete_b, cancel_b;
1689         FileManager filemgr;
1690         RemoteFile files[];
1691
1692         DeleteWindow(FileManager p, RemoteFile ff[])
1693         {
1694         filemgr = p;
1695         files = ff;
1696         setTitle(filemgr.text(ff.length > 1 ? "delete_mtitle" :
1697                               ff[0].type == 0 ? "delete_dtitle" :
1698                                                 "delete_ftitle"));
1699
1700         setLayout(new BorderLayout());
1701         if (ff.length > 1) {
1702                 add("North", new Label(filemgr.text("delete_mdesc")));
1703                 Panel mp = new Panel();
1704                 mp.setLayout(new GridLayout(ff.length, 1));
1705                 for(int i=0; i<ff.length; i++)
1706                         mp.add(new Label(ff[i].path));
1707                 add("Center", mp);
1708                 }
1709         else
1710                 add("Center", new MultiLabel(filemgr.text(
1711                         ff[0].type == 0 ? "delete_ddesc" : "delete_fdesc",
1712                         ff[0].path), 35));
1713         Panel bot = new Panel();
1714         bot.setLayout(new FlowLayout(FlowLayout.CENTER));
1715         bot.add(delete_b = new CbButton(filemgr.get_image("save.gif"),
1716                                         filemgr.text("delete"),
1717                                         CbButton.LEFT, this));
1718         bot.add(cancel_b = new CbButton(filemgr.get_image("cancel.gif"),
1719                                         filemgr.text("cancel"),
1720                                         CbButton.LEFT, this));
1721         add("South", bot);
1722
1723         pack();
1724         show();
1725         }
1726
1727         public void click(CbButton b)
1728         {
1729         if (b == delete_b) {
1730                 // Delete the file or directory
1731                 boolean need_redraw = false, need_reshow = false;
1732                 for(int i=0; i<files.length; i++) {
1733                         RemoteFile file = files[i];
1734                         String rv[] = filemgr.get_text("delete.cgi?file="+
1735                                                filemgr.urlize(file.path));
1736                         if (rv[0].length() > 0) {
1737                                 new ErrorWindow(filemgr.text("delete_efailed",
1738                                                 file.path, rv[0]));
1739                                 break;
1740                                 }
1741                         else {
1742                                 // done the deed.. update data structures
1743                                 RemoteFile pf = file.directory;
1744                                 pf.delete(file);
1745                                 if (filemgr.showing_files == pf) {
1746                                         // Need to refresh the list as well..
1747                                         need_reshow = true;
1748                                         }
1749
1750                                 FileNode node = (FileNode)filemgr.nodemap.get(
1751                                                         file);
1752                                 FileNode pnode = (FileNode)filemgr.nodemap.get(
1753                                                         pf);
1754                                 if (node != null) {
1755                                         // Take the directory out of the tree..
1756                                         pnode.ch.removeElement(node);
1757                                         need_redraw = true;
1758                                         }
1759                                 }
1760                         }
1761                 if (need_reshow) filemgr.show_files(filemgr.showing_files);
1762                 if (need_redraw) filemgr.dirs.redraw();
1763                 dispose();
1764                 }
1765         else if (b == cancel_b)
1766                 dispose();
1767         }
1768 }
1769
1770 class MkdirWindow extends FixedFrame implements CbButtonCallback
1771 {
1772         FileManager filemgr;
1773         TextField dir;
1774         CbButton create_b, cancel_b;
1775
1776         MkdirWindow(String d, FileManager p)
1777         {
1778         filemgr = p;
1779         setTitle(filemgr.text("mkdir_title"));
1780         setLayout(new BorderLayout());
1781         add("West", new Label(filemgr.text("mkdir_dir")));
1782         add("Center", dir = new TextField(d.equals("/") ? "/" : d+"/", 40));
1783         dir.select(dir.getText().length(), dir.getText().length());
1784         Panel bot = new Panel();
1785         bot.setLayout(new FlowLayout(FlowLayout.CENTER));
1786         bot.add(create_b = new CbButton(filemgr.get_image("save.gif"),
1787                                         filemgr.text("create"),
1788                                         CbButton.LEFT, this));
1789         bot.add(cancel_b = new CbButton(filemgr.get_image("cancel.gif"),
1790                                         filemgr.text("cancel"),
1791                                         CbButton.LEFT, this));
1792         add("South", bot);
1793         pack();
1794         show();
1795         }
1796
1797         public void click(CbButton b)
1798         {
1799         if (b == create_b) {
1800                 // Find the filemgr directory
1801                 String path = dir.getText();
1802                 path = filemgr.trim_path(path);
1803                 int sl = path.lastIndexOf('/');
1804                 RemoteFile par = filemgr.find_directory(
1805                                         path.substring(0, sl), false);
1806                 if (par.find(path.substring(sl+1)) != null) {
1807                         new ErrorWindow(filemgr.text("mkdir_eexists", path));
1808                         return;
1809                         }
1810                 String rv[] = filemgr.get_text("mkdir.cgi?dir="+
1811                                                filemgr.urlize(path));
1812                 if (rv[0].length() > 0) {
1813                         new ErrorWindow(filemgr.text("mkdir_efailed", rv[0]));
1814                         return;
1815                         }
1816                 RemoteFile file = new RemoteFile(filemgr, rv[1], par);
1817                 par.add(file);
1818                 FileNode parnode = (FileNode)filemgr.nodemap.get(par);
1819                 if (parnode != null) {
1820                         // Update the tree
1821                         parnode.add(new FileNode(file));
1822                         filemgr.dirs.redraw();
1823                         }
1824                 filemgr.show_files(filemgr.showing_files);
1825                 dispose();
1826                 }
1827         else dispose();
1828         }
1829 }
1830
1831 class LinkWindow extends FixedFrame implements CbButtonCallback
1832 {
1833         FileManager filemgr;
1834         TextField from, to;
1835         CbButton create_b, cancel_b;
1836
1837         LinkWindow(String d, FileManager p)
1838         {
1839         filemgr = p;
1840         setLayout(new BorderLayout());
1841         setTitle(filemgr.text("link_title"));
1842         Panel l = new Panel(), r = new Panel();
1843         l.setLayout(new GridLayout(0, 1));
1844         l.add(new Label(filemgr.text("link_from")));
1845         l.add(new Label(filemgr.text("link_to")));
1846         r.setLayout(new GridLayout(0, 1));
1847         r.add(from = new TextField(d.equals("/") ? "/" : d+"/", 40));
1848         from.select(from.getText().length(), from.getText().length());
1849         r.add(to = new TextField());
1850         add("West", l); add("Center", r);
1851         Panel bot = new Panel();
1852         bot.setLayout(new FlowLayout(FlowLayout.CENTER));
1853         bot.add(create_b = new CbButton(filemgr.get_image("save.gif"),
1854                                         filemgr.text("create"),
1855                                         CbButton.LEFT, this));
1856         bot.add(cancel_b = new CbButton(filemgr.get_image("cancel.gif"),
1857                                         filemgr.text("cancel"),
1858                                         CbButton.LEFT, this));
1859         add("South", bot);
1860         pack();
1861         show();
1862         }
1863
1864         public void click(CbButton b)
1865         {
1866         if (b == create_b) {
1867                 // Check inputs
1868                 String from_str = from.getText().trim();
1869                 int sl = from_str.lastIndexOf('/');
1870                 String par_str = from_str.substring(0, sl),
1871                        file_str = from_str.substring(sl+1);
1872                 RemoteFile par = filemgr.find_directory(par_str, false);
1873                 if (par == null) return;
1874                 if (par.find(file_str) != null) {
1875                         new ErrorWindow(filemgr.text("link_eexists", from_str));
1876                         return;
1877                         }
1878
1879                 // Create the actual link
1880                 String rv[] = filemgr.get_text("makelink.cgi?from="+
1881                                                filemgr.urlize(from_str)+"&to="+
1882                                                filemgr.urlize(to.getText()));
1883                 if (rv[0].length() > 0) {
1884                         new ErrorWindow(filemgr.text("link_efailed", rv[0]));
1885                         return;
1886                         }
1887                 RemoteFile file = new RemoteFile(filemgr, rv[1], par);
1888                 par.add(file);
1889                 filemgr.show_files(filemgr.showing_files);
1890                 dispose();
1891                 }
1892         else if (b == cancel_b)
1893                 dispose();
1894         }
1895 }
1896
1897 class RenameWindow extends FixedFrame implements CbButtonCallback
1898 {
1899         FileManager filemgr;
1900         RemoteFile file;
1901         TextField oldname, newname;
1902         CbButton rename_b, cancel_b;
1903
1904         RenameWindow(FileManager p, RemoteFile f)
1905         {
1906         filemgr = p; file = f;
1907         setLayout(new BorderLayout());
1908         setTitle(filemgr.text("rename_title", file.path));
1909         Panel l = new Panel(), r = new Panel();
1910         l.setLayout(new GridLayout(0, 1));
1911         l.add(new Label(filemgr.text("rename_old")));
1912         l.add(new Label(filemgr.text("rename_new")));
1913         r.setLayout(new GridLayout(0, 1));
1914         r.add(oldname = new TextField(file.name, 20));
1915         oldname.setEditable(false);
1916         r.add(newname = new TextField(file.name, 20));
1917         newname.select(file.name.length(), file.name.length());
1918         add("West", l); add("Center", r);
1919
1920         Panel bot = new Panel();
1921         bot.setLayout(new FlowLayout(FlowLayout.CENTER));
1922         bot.add(rename_b = new CbButton(filemgr.get_image("save.gif"),
1923                                         filemgr.text("rename_ok"),
1924                                         CbButton.LEFT, this));
1925         bot.add(cancel_b = new CbButton(filemgr.get_image("cancel.gif"),
1926                                         filemgr.text("cancel"),
1927                                         CbButton.LEFT, this));
1928         add("South", bot);
1929         pack();
1930         show();
1931         }
1932
1933         public void click(CbButton b)
1934         {
1935         if (b == rename_b) {
1936                 // Check for an existing file
1937                 String newstr = newname.getText().trim();
1938                 if (newstr.length() == 0) return;
1939                 RemoteFile already = file.directory.find(newstr);
1940                 if (already != null) {
1941                         new ErrorWindow(filemgr.text("rename_eexists", newstr));
1942                         return;
1943                         }
1944
1945                 // Rename the real file
1946                 int sl = file.path.lastIndexOf('/');
1947                 String newpath = file.path.substring(0, sl)+"/"+newstr;
1948                 String rv[] = filemgr.get_text(
1949                                 "rename.cgi?old="+filemgr.urlize(file.path)+
1950                                 "&new="+filemgr.urlize(newpath));
1951                 if (rv[0].length() > 0) {
1952                         new ErrorWindow(filemgr.text("rename_efailed", rv[0]));
1953                         return;
1954                         }
1955
1956                 // Update data structure
1957                 file.name = newstr;
1958                 file.path = newpath;
1959                 file.directory.delete(file);
1960                 file.directory.add(file);
1961                 file.list = null;
1962                 FileNode parnode = (FileNode)filemgr.nodemap.get(file.directory);
1963                 FileNode filenode = (FileNode)filemgr.nodemap.get(file);
1964                 if (parnode != null && filenode != null) {
1965                         filenode.text = file.name;
1966                         parnode.ch.removeElement(filenode);
1967                         parnode.add(filenode);
1968                         dispose();
1969                         filemgr.dirs.redraw();
1970                         }
1971
1972                 filemgr.show_files(filemgr.showing_files);
1973                 dispose();
1974                 }
1975         else if (b == cancel_b)
1976                 dispose();
1977         }
1978 }
1979
1980 class MultiLabel extends BorderPanel
1981 {
1982         public MultiLabel(String s, int max)
1983         {
1984         this(s, max, 1);
1985         }
1986
1987         public MultiLabel(String s, int max, int b)
1988         {
1989         super(b);
1990         Vector v = new Vector();
1991         StringTokenizer tok = new StringTokenizer(s.trim(), " \t");
1992         String line = null;
1993         while(tok.hasMoreTokens()) {
1994                 String w = tok.nextToken();
1995                 line = (line == null ? w : line+" "+w);
1996                 if (line.length() > max || !tok.hasMoreTokens()) {
1997                         v.addElement(line);
1998                         line = null;
1999                         }
2000                 }
2001         setLayout(new GridLayout(v.size(), 1, 0, 0));
2002         for(int i=0; i<v.size(); i++)
2003                 add(new Label((String)v.elementAt(i), Label.CENTER));
2004         }
2005 }
2006
2007 class OverwriteWindow extends FixedFrame implements CbButtonCallback
2008 {
2009         FileManager filemgr;
2010         RemoteFile src, already;
2011         TextField newname;
2012         CbButton ok, cancel;
2013         int idx;
2014         boolean mode;
2015
2016         OverwriteWindow(FileManager p, RemoteFile a, RemoteFile s, int i)
2017         {
2018         filemgr = p; src = s; already = a; idx = i;
2019         mode = filemgr.cut_mode;
2020         setLayout(new BorderLayout());
2021         setTitle(filemgr.text("over_title"));
2022         add("North",
2023             new MultiLabel(filemgr.text("over_msg", already.path), 30, 0));
2024         add("West", new Label(filemgr.text("over_new")));
2025         add("East", newname = new TextField(a.name, 30));
2026
2027         Panel bot = new Panel();
2028         bot.setLayout(new FlowLayout(FlowLayout.RIGHT));
2029         bot.add(ok = new CbButton(filemgr.get_image("save.gif"),
2030                                   filemgr.text("over_ok"),
2031                                   CbButton.LEFT, this));
2032         bot.add(cancel = new CbButton(filemgr.get_image("cancel.gif"),
2033                                   filemgr.text("cancel"),
2034                                   CbButton.LEFT, this));
2035         add("South", bot);
2036         pack();
2037         show();
2038         }
2039
2040         public void click(CbButton b)
2041         {
2042         if (b == cancel)
2043                 dispose();
2044         else if (b == ok && newname.getText().length() > 0) {
2045                 // paste the file, but with a new name
2046                 RemoteFile ap = already.directory;
2047                 RemoteFile newalready = ap.find(newname.getText());
2048                 if (newalready == src) {
2049                         new ErrorWindow(filemgr.text("paste_eself"));
2050                         return;
2051                         }
2052                 if (newalready != null && (newalready.type == 0 ||
2053                                            newalready.type == 5)) {
2054                         new ErrorWindow(
2055                                 filemgr.text("paste_eover", newalready.path));
2056                         return;
2057                         }
2058                 String dpath = (ap.path.equals("/") ? "/" :
2059                                 ap.path+"/")+newname.getText();
2060                 RemoteFile nf = filemgr.paste_file(src, already.directory,
2061                                                    dpath, newalready, mode);
2062                 if (filemgr.cut_mode && nf != null) {
2063                         // Paste from the destination path from now on
2064                         filemgr.cut_buffer[idx] = nf;
2065                         }
2066                 dispose();
2067                 }
2068         }
2069 }
2070
2071 class SambaShare
2072 {
2073         String path;
2074         boolean available;
2075         boolean writable;
2076         int guest;
2077         String comment;
2078
2079         SambaShare(String l)
2080         {
2081         StringSplitter tok = new StringSplitter(l, ':');
2082         path = tok.nextToken();
2083         available = tok.nextToken().equals("1");
2084         writable = tok.nextToken().equals("1");
2085         guest = Integer.parseInt(tok.nextToken());
2086         comment = tok.nextToken();
2087         }
2088
2089         SambaShare(String p, boolean a, boolean w, int g, String c)
2090         {
2091         path = p;
2092         available = a;
2093         writable = w;
2094         guest = g;
2095         comment = c;
2096         }
2097
2098         String params()
2099         {
2100         return "path="+FileManager.urlize(path)+
2101                "&available="+(available ? 1 : 0)+
2102                "&writable="+(writable ? 1 : 0)+
2103                "&guest="+guest+
2104                "&comment="+FileManager.urlize(comment);
2105         }
2106 }
2107
2108 class DFSAdminExport
2109 {
2110         String path;
2111         String desc;
2112         String ro, rw, root;
2113
2114         DFSAdminExport(String l)
2115         {
2116         StringSplitter tok = new StringSplitter(l, ':');
2117         path = tok.nextToken();
2118         ro = tok.nextToken();
2119         rw = tok.nextToken();
2120         root = tok.nextToken();
2121         desc = tok.nextToken();
2122         }
2123
2124         DFSAdminExport(String p, String d, String ro, String rw, String root)
2125         {
2126         path = p;
2127         desc = d;
2128         this.ro = ro;
2129         this.rw = rw;
2130         this.root = root;
2131         }
2132
2133         String[] split(String s)
2134         {
2135         StringTokenizer stok = new StringTokenizer(s, " ");
2136         String rv[] = new String[stok.countTokens()];
2137         for(int i=0; i<rv.length; i++)
2138                 rv[i] = stok.nextToken();
2139         return rv;
2140         }
2141
2142         String params()
2143         {
2144         return "path="+FileManager.urlize(path)+
2145                "&ro="+FileManager.urlize(ro)+
2146                "&rw="+FileManager.urlize(rw)+
2147                "&root="+FileManager.urlize(root)+
2148                "&desc="+FileManager.urlize(desc);
2149         }
2150 }
2151
2152 class LinuxExport
2153 {
2154         String path;
2155         String host[];
2156         boolean ro[];
2157         int squash[];
2158
2159         LinuxExport(String l)
2160         {
2161         StringSplitter tok = new StringSplitter(l, ':');
2162         path = tok.nextToken();
2163         int c = tok.countTokens() / 3;
2164         host = new String[c];
2165         ro = new boolean[c];
2166         squash = new int[c];
2167         for(int i=0; tok.hasMoreTokens(); i++) {
2168                 host[i] = tok.nextToken();
2169                 ro[i] = tok.nextToken().equals("1");
2170                 squash[i] = Integer.parseInt(tok.nextToken());
2171                 }
2172         }
2173
2174         LinuxExport(String p, String h[], String r[], String s[])
2175         {
2176         path = p;
2177         }
2178
2179         String params()
2180         {
2181         String rv = "path="+FileManager.urlize(path)+
2182                     "&count="+host.length;
2183         for(int i=0; i<host.length; i++) {
2184                 rv += "&host"+i+"="+FileManager.urlize(host[i]);
2185                 rv += "&ro"+i+"="+(ro[i] ? 1 : 0);
2186                 rv += "&squash"+i+"="+squash[i];
2187                 }
2188         return rv;
2189         }
2190 }
2191
2192 class SharingWindow extends FixedFrame implements CbButtonCallback
2193 {
2194         CbButton save_b, cancel_b;
2195         RemoteFile file;
2196         FileManager filemgr;
2197         SambaShare sshare;
2198         DFSAdminExport dexport;
2199         LinuxExport lexport;
2200         Checkbox samba_on, samba_off;
2201         Checkbox writable_on, writable_off;
2202         Checkbox available_on, available_off;
2203         Checkbox guest_on, guest_off, guest_only;
2204         TextField comment;
2205
2206         TextField desc;
2207         Checkbox nfs_on, nfs_off;
2208         TextField rwhosts, rohosts, roothosts;
2209         Checkbox rw[] = new Checkbox[3], ro[] = new Checkbox[3],
2210                  root[] = new Checkbox[3];
2211
2212         TextField host[];
2213         Choice lro[], squash[];
2214
2215         SharingWindow(RemoteFile f, FileManager p)
2216         {
2217         file = f; filemgr = p;
2218         setTitle(filemgr.text("share_title", file.path));
2219         sshare = (SambaShare)filemgr.stab.get(file.path);
2220         Object nshare = filemgr.ntab.get(file.path);
2221         if (filemgr.nfsmode == 1)
2222                 lexport = (LinuxExport)nshare;
2223         else if (filemgr.nfsmode == 2)
2224                 dexport = (DFSAdminExport)nshare;
2225
2226         // setup UI
2227         setLayout(new BorderLayout());
2228         Panel samba = new Panel(), sl = new Panel(), sr = new Panel();
2229         samba.setLayout(new BorderLayout());
2230         Panel st = new Panel();
2231         st.setLayout(new GridLayout(2, 1));
2232         CheckboxGroup sg = new CheckboxGroup();
2233         st.add(samba_off = new Checkbox(filemgr.text("share_soff"), sg, 
2234                                        sshare == null));
2235         st.add(samba_on = new Checkbox(filemgr.text("share_son"), sg,
2236                                         sshare != null));
2237         samba.add("North", st);
2238
2239         Panel stop = new LinedPanel(filemgr.text("share_sheader"));
2240         setup_leftright(stop, sl, sr);
2241
2242         comment = new TextField(sshare == null ? "" : sshare.comment, 25);
2243         add_item(filemgr.text("share_comment"), comment, sl, sr);
2244
2245         Panel ap = new Panel();
2246         ap.setLayout(new GridLayout(1, 0));
2247         CheckboxGroup ag = new CheckboxGroup();
2248         ap.add(available_on = new Checkbox(filemgr.text("yes"), ag,
2249                                           sshare == null || sshare.available));
2250         ap.add(available_off = new Checkbox(filemgr.text("no"), ag,
2251                                           sshare != null && !sshare.available));
2252         add_item(filemgr.text("share_available"), ap, sl, sr);
2253
2254         Panel wp = new Panel();
2255         wp.setLayout(new GridLayout(1, 0));
2256         CheckboxGroup wg = new CheckboxGroup();
2257         wp.add(writable_on = new Checkbox(filemgr.text("yes"), wg,
2258                                           sshare == null || sshare.writable));
2259         wp.add(writable_off = new Checkbox(filemgr.text("no"), wg,
2260                                            sshare != null && !sshare.writable));
2261         add_item(filemgr.text("share_writable"), wp, sl, sr);
2262
2263         Panel gp = new Panel();
2264         gp.setLayout(new GridLayout(1, 0));
2265         CheckboxGroup gg = new CheckboxGroup();
2266         gp.add(guest_only = new Checkbox(filemgr.text("share_only"), gg,
2267                                 sshare != null && sshare.guest == 2));
2268         gp.add(guest_on = new Checkbox(filemgr.text("yes"), gg,
2269                                 sshare == null || sshare.guest == 1));
2270         gp.add(guest_off = new Checkbox(filemgr.text("no"), gg,
2271                                 sshare != null && sshare.guest == 0));
2272         add_item(filemgr.text("share_guest"), gp, sl, sr);
2273
2274         samba.add("Center", stop);
2275
2276         // Setup NFS UI
2277         Panel nfs = new Panel(), nl = new Panel(), nr = new Panel();
2278         nfs.setLayout(new BorderLayout());
2279         Panel nt = new Panel();
2280         nt.setLayout(new GridLayout(2, 1));
2281         CheckboxGroup ng = new CheckboxGroup();
2282         nt.add(nfs_off = new Checkbox(filemgr.text("share_noff"), ng, 
2283                                       nshare == null));
2284         nt.add(nfs_on = new Checkbox(filemgr.text("share_non"), ng,
2285                                      nshare != null));
2286         nfs.add("North", nt);
2287
2288         Panel ntop = new LinedPanel(filemgr.text("share_nheader"));
2289         setup_leftright(ntop, nl, nr);
2290         if (filemgr.nfsmode == 1) {
2291                 // Linux export mode
2292                 nl.setLayout(new GridLayout(0, 1, 2, 2));
2293                 nr.setLayout(new GridLayout(0, 1, 2, 2));
2294                 nl.add(new Label(filemgr.text("share_host")));
2295                 nr.add(new Label(filemgr.text("share_opts")));
2296                 int c = lexport==null ? 0 : lexport.host.length;
2297                 host = new TextField[c+1];
2298                 lro = new Choice[c+1];
2299                 squash = new Choice[c+1];
2300                 for(int i=0; i<c; i++) {
2301                         host[i] = new TextField(lexport.host[i], 20);
2302                         lro[i] = robox(lexport.ro[i]);
2303                         squash[i] = squashbox(lexport.squash[i]);
2304                         nl.add(host[i]);
2305                         nr.add(opts_panel(lro[i], squash[i]));
2306                         }
2307                 host[c] = new TextField("", 20);
2308                 lro[c] = robox(false);
2309                 squash[c] = squashbox(1);
2310                 nl.add(host[c]);
2311                 nr.add(opts_panel(lro[c], squash[c]));
2312                 }
2313         else if (filemgr.nfsmode == 2) {
2314                 // Solaris share mode
2315                 desc = new TextField(dexport == null ? "" : dexport.desc, 25);
2316                 add_item(filemgr.text("share_desc"), desc, nl, nr);
2317
2318                 rohosts = add_hosts(filemgr.text("share_ro"),
2319                                     dexport == null ? "-" : dexport.ro,
2320                                     ro, nl, nr);
2321                 rwhosts = add_hosts(filemgr.text("share_rw"),
2322                                     dexport == null ? "-" : dexport.rw,
2323                                     rw, nl, nr);
2324                 roothosts = add_hosts(filemgr.text("share_root"),
2325                                     dexport == null ? "-" : dexport.root,
2326                                     root, nl, nr);
2327                 root[1].getParent().remove(root[1]);
2328                 }
2329         else if (filemgr.nfsmode == 3) {
2330                 }
2331         nfs.add("Center", ntop);
2332
2333         // Add the appropriate tabs
2334         if (filemgr.sambamode && filemgr.nfsmode != 0) {
2335                 TabbedPanel tab = new TabbedPanel();
2336                 tab.addItem(filemgr.text("share_samba"), samba);
2337                 tab.addItem(filemgr.text("share_nfs"), nfs);
2338                 add("Center", tab);
2339                 }
2340         else if (filemgr.sambamode)
2341                 add("Center", samba);
2342         else if (filemgr.nfsmode != 0)
2343                 add("Center", nfs);
2344
2345         // Create save and cancel buttons
2346         Panel bot = new Panel();
2347         bot.setLayout(new FlowLayout(FlowLayout.RIGHT));
2348         bot.add(save_b = new CbButton(filemgr.get_image("save.gif"),
2349                                       filemgr.text("save"),
2350                                       CbButton.LEFT, this));
2351         bot.add(cancel_b = new CbButton(filemgr.get_image("cancel.gif"),
2352                                         filemgr.text("cancel"),
2353                                         CbButton.LEFT, this));
2354         add("South", bot);
2355         pack();
2356         show();
2357         }
2358
2359         public void click(CbButton b)
2360         {
2361         if (b == save_b) {
2362                 // Update samba settings on server
2363                 if (sshare != null && samba_on.getState()) {
2364                         // Updating share
2365                         sshare.available = available_on.getState();
2366                         sshare.writable = writable_on.getState();
2367                         sshare.guest = guest_only.getState() ? 2 :
2368                                        guest_on.getState() ? 1 : 0;
2369                         sshare.comment = comment.getText();
2370                         String rv[] = filemgr.get_text(
2371                                 "save_share.cgi?"+sshare.params());
2372                         }
2373                 else if (sshare != null) {
2374                         // Deleting share
2375                         String rv[] = filemgr.get_text(
2376                                 "save_share.cgi?delete=1&"+sshare.params());
2377                         filemgr.stab.remove(sshare.path);
2378                         }
2379                 else if (samba_on.getState()) {
2380                         // Creating share
2381                         sshare = new SambaShare(file.path,
2382                                                 available_on.getState(),
2383                                                 writable_on.getState(),
2384                                                 guest_only.getState() ? 2 :
2385                                                 guest_on.getState() ? 1 : 0,
2386                                                 comment.getText());
2387                         filemgr.stab.put(sshare.path, sshare);
2388                         String rv[] = filemgr.get_text(
2389                                 "save_share.cgi?new=1&"+sshare.params());
2390                         }
2391
2392                 // Update NFS settings on server
2393                 if (filemgr.nfsmode == 1) {
2394                         if (lexport != null && nfs_on.getState()) {
2395                                 // Updating export
2396                                 export_options(lexport);
2397                                 String rv[] = filemgr.get_text(
2398                                         "save_export.cgi?"+lexport.params());
2399                                 }
2400                         else if (lexport != null) {
2401                                 // Deleting export
2402                                 String rv[] = filemgr.get_text(
2403                                   "save_export.cgi?delete=1&"+lexport.params());
2404                                 filemgr.ntab.remove(lexport.path);
2405                                 }
2406                         else if (nfs_on.getState()) {
2407                                 // Creating export
2408                                 lexport = new LinuxExport(file.path, null,
2409                                                           null, null);
2410                                 export_options(lexport);
2411                                 String rv[] = filemgr.get_text(
2412                                   "save_export.cgi?new=1&"+lexport.params());
2413                                 filemgr.ntab.put(lexport.path, lexport);
2414                                 }
2415                         }
2416                 else if (filemgr.nfsmode == 2) {
2417                         if (dexport != null && nfs_on.getState()) {
2418                                 // Updating share
2419                                 dexport.desc = desc.getText();
2420                                 dexport.ro = ro[0].getState() ? "-" :
2421                                              ro[1].getState() ? "" :
2422                                              rohosts.getText();
2423                                 dexport.rw = rw[0].getState() ? "-" :
2424                                              rw[1].getState() ? "" :
2425                                              rwhosts.getText();
2426                                 dexport.root = root[0].getState() ? "-" :
2427                                                roothosts.getText();
2428                                 String rv[] = filemgr.get_text(
2429                                         "save_export.cgi?"+dexport.params());
2430                                 }
2431                         else if (dexport != null) {
2432                                 // Deleting share
2433                                 String rv[] = filemgr.get_text(
2434                                   "save_export.cgi?delete=1&"+dexport.params());
2435                                 filemgr.ntab.remove(dexport.path);
2436                                 }
2437                         else if (nfs_on.getState()) {
2438                                 // Creating new share
2439                                 dexport = new DFSAdminExport(file.path,
2440                                         desc.getText(),
2441                                         ro[0].getState() ? "-" :
2442                                         ro[1].getState() ? "" :
2443                                         rohosts.getText(),
2444                                         rw[0].getState() ? "-" :
2445                                         rw[1].getState() ? "" :
2446                                         rwhosts.getText(),
2447                                         root[0].getState() ? "-" :
2448                                         roothosts.getText());
2449                                 String rv[] = filemgr.get_text(
2450                                     "save_export.cgi?new=1&"+dexport.params());
2451                                 filemgr.ntab.put(dexport.path, dexport);
2452                                 }
2453                         }
2454                 else if (filemgr.nfsmode == 3) {
2455                         }
2456
2457                 filemgr.show_files(filemgr.showing_files);
2458                 dispose();
2459                 }
2460         else if (b == cancel_b)
2461                 dispose();
2462         }
2463
2464         void setup_leftright(Panel m, Panel l, Panel r)
2465         {
2466         m.setLayout(new BorderLayout());
2467         Panel p = new Panel();
2468         p.setLayout(new BorderLayout());
2469         p.add("West", l);
2470         p.add("Center", r);
2471         l.setLayout(new GridLayout(0, 1));
2472         r.setLayout(new GridLayout(0, 1));
2473         m.add("North", p);
2474         }
2475
2476         void add_item(String t, Component c, Panel l, Panel r)
2477         {
2478         l.add(new Label(t));
2479         Panel p = new Panel();
2480         p.setLayout(new BorderLayout());
2481         p.add("West", c);
2482         r.add(p);
2483         }
2484
2485         TextField add_hosts(String name, String v, Checkbox cb[],
2486                             Panel l, Panel r)
2487         {
2488         Panel p = new Panel();
2489         p.setLayout(new GridLayout(1, 3));
2490         CheckboxGroup g = new CheckboxGroup();
2491         p.add(cb[0] = new Checkbox(filemgr.text("share_none"), g,
2492                                    v.equals("-")));
2493         p.add(cb[1] = new Checkbox(filemgr.text("share_all"), g,
2494                                    v.length() == 0));
2495         p.add(cb[2] = new Checkbox(filemgr.text("share_listed"), g,
2496                                    v.length() > 1));
2497         add_item(name, p, l, r);
2498         TextField t = new TextField(v.equals("-") ? "" : v, 25);
2499         add_item("", t, l, r);
2500         return t;
2501         }
2502
2503         Choice squashbox(int s)
2504         {
2505         Choice rv = new Choice();
2506         rv.addItem(filemgr.text("share_s0"));
2507         rv.addItem(filemgr.text("share_s1"));
2508         rv.addItem(filemgr.text("share_s2"));
2509         rv.select(s);
2510         return rv;
2511         }
2512
2513         Choice robox(boolean r)
2514         {
2515         Choice rv = new Choice();
2516         rv.addItem(filemgr.text("share_lrw"));
2517         rv.addItem(filemgr.text("share_lro"));
2518         rv.select(r ? 1 : 0);
2519         return rv;
2520         }
2521
2522         Panel opts_panel(Component ro, Component squash)
2523         {
2524         Panel p = new Panel();
2525         p.setLayout(new BorderLayout());
2526         p.add("West", ro);
2527         p.add("East", squash);
2528         return p;
2529         }
2530
2531         void export_options(LinuxExport e)
2532         {
2533         int c = 0;
2534         for(int i=0; i<host.length; i++)
2535                 if (host[i].getText().length() > 0)
2536                         c++;
2537         e.host = new String[c];
2538         e.ro = new boolean[c];
2539         e.squash = new int[c];
2540         for(int i=0,j=0; i<host.length; i++) {
2541                 if (host[i].getText().trim().length() > 0) {
2542                         e.host[j] = host[i].getText();
2543                         e.ro[j] = lro[i].getSelectedIndex() == 1;
2544                         e.squash[j] = squash[i].getSelectedIndex();
2545                         j++;
2546                         }
2547                 }
2548         }
2549
2550 }
2551
2552 class SearchWindow extends FixedFrame
2553         implements CbButtonCallback,MultiColumnCallback
2554 {
2555         TabbedPanel tab;
2556         MultiColumn list;
2557         CbButton search_b, cancel_b;
2558         FileManager filemgr;
2559         TextField dir, match, user, group;
2560         Checkbox uany, usel, gany, gsel;
2561         Choice type;
2562         Checkbox sany, smore, sless;
2563         TextField more, less;
2564         Checkbox xon, xoff;
2565         String types[] = { "", "f", "d", "l", "p" };
2566         RemoteFile results[];
2567
2568         SearchWindow(String d, FileManager p)
2569         {
2570         filemgr = p;
2571         setTitle(filemgr.text("search_title"));
2572
2573         // setup UI
2574         setLayout(new BorderLayout());
2575         tab = new TabbedPanel();
2576         Panel search = new Panel();
2577         search.setLayout(new BorderLayout());
2578         tab.addItem(filemgr.text("search_crit"), search);
2579         Panel l = new Panel(), r = new Panel();
2580         l.setLayout(new GridLayout(0, 1));
2581         r.setLayout(new GridLayout(0, 1));
2582
2583         String cols[] = { "", filemgr.text("right_name"),
2584                           filemgr.text("right_size") };
2585         float widths[] = { .07f, .78f, .15f };
2586         list = new MultiColumn(cols, this);
2587         list.setWidths(widths);
2588         list.setDrawLines(false);
2589         tab.addItem(filemgr.text("search_list"), list);
2590
2591         add_item(filemgr.text("search_dir"), dir = new TextField(d, 30), l, r);
2592
2593         add_item(filemgr.text("search_match"), match = new TextField(20), l, r);
2594
2595         Panel up = new Panel();
2596         up.setLayout(new FlowLayout(FlowLayout.LEFT, 1, 1));
2597         CheckboxGroup ug = new CheckboxGroup();
2598         up.add(uany = new Checkbox(filemgr.text("search_any"), ug, true));
2599         up.add(usel = new Checkbox("", ug, false));
2600         up.add(user = new TextField(10));
2601         add_item(filemgr.text("search_user"), up, l, r);
2602
2603         Panel gp = new Panel();
2604         gp.setLayout(new FlowLayout(FlowLayout.LEFT, 1, 1));
2605         CheckboxGroup gg = new CheckboxGroup();
2606         gp.add(gany = new Checkbox(filemgr.text("search_any"), gg, true));
2607         gp.add(gsel = new Checkbox("", gg, false));
2608         gp.add(group = new TextField(10));
2609         add_item(filemgr.text("search_group"), gp, l, r);
2610
2611         type = new Choice();
2612         for(int i=0; i<types.length; i++)
2613                 type.addItem(filemgr.text("search_types_"+types[i]));
2614         add_item(filemgr.text("search_type"), type, l, r);
2615
2616         CheckboxGroup sg = new CheckboxGroup();
2617         add_item(filemgr.text("search_size"),
2618                  sany = new Checkbox(filemgr.text("search_any"), sg, true),
2619                  l, r);
2620         Panel mp = new Panel();
2621         mp.setLayout(new FlowLayout(FlowLayout.LEFT, 1, 1));
2622         mp.add(smore = new Checkbox(filemgr.text("search_more"), sg, false));
2623         mp.add(more = new TextField(10));
2624         add_item("", mp, l, r);
2625         Panel lp = new Panel();
2626         lp.setLayout(new FlowLayout(FlowLayout.LEFT, 1, 1));
2627         lp.add(sless = new Checkbox(filemgr.text("search_less"), sg, false));
2628         lp.add(less = new TextField(10));
2629         add_item("", lp, l, r);
2630
2631         CheckboxGroup xg = new CheckboxGroup();
2632         Panel xp = new Panel();
2633         xp.setLayout(new FlowLayout(FlowLayout.LEFT, 1, 1));
2634         xp.add(xoff = new Checkbox(filemgr.text("yes"), xg, true));
2635         xp.add(xon = new Checkbox(filemgr.text("no"), xg, false));
2636         add_item(filemgr.text("search_xdev"), xp, l, r);
2637
2638         search.add("West", l); search.add("East", r);
2639         add("Center", tab);
2640
2641         // Create search and cancel buttons
2642         Panel bot = new Panel();
2643         bot.setLayout(new FlowLayout(FlowLayout.RIGHT));
2644         bot.add(search_b = new CbButton(filemgr.get_image("save.gif"),
2645                                       filemgr.text("search_ok"),
2646                                       CbButton.LEFT, this));
2647         bot.add(cancel_b = new CbButton(filemgr.get_image("cancel.gif"),
2648                                         filemgr.text("cancel"),
2649                                         CbButton.LEFT, this));
2650         add("South", bot);
2651         pack();
2652         show();
2653         }
2654
2655         void add_item(String t, Component c, Panel l, Panel r)
2656         {
2657         l.add(new Label(t));
2658         Panel p = new Panel();
2659         p.setLayout(new BorderLayout());
2660         p.add("West", c);
2661         r.add(p);
2662         }
2663
2664         public void click(CbButton b)
2665         {
2666         if (b == cancel_b)
2667                 dispose();
2668         else if (b == search_b) {
2669                 // validate inputs and build search URL
2670                 String url = "search.cgi";
2671                 String d = dir.getText().trim();
2672                 if (d.length() == 0 || d.charAt(0) != '/') {
2673                         new ErrorWindow(filemgr.text("search_edir"));
2674                         return;
2675                         }
2676                 url += "?dir="+filemgr.urlize(d);
2677                 String mt = match.getText().trim();
2678                 if (mt.length() == 0) {
2679                         new ErrorWindow(filemgr.text("search_ematch"));
2680                         return;
2681                         }
2682                 url += "&match="+filemgr.urlize(mt);
2683                 if (type.getSelectedIndex() > 0)
2684                         url += "&type="+types[type.getSelectedIndex()];
2685                 if (usel.getState()) {
2686                         String u = user.getText().trim();
2687                         if (u.length() == 0) {
2688                                 new ErrorWindow(filemgr.text("search_euser"));
2689                                 return;
2690                                 }
2691                         url += "&user="+filemgr.urlize(u);
2692                         }
2693                 if (gsel.getState()) {
2694                         String g = group.getText().trim();
2695                         if (g.length() == 0) {
2696                                 new ErrorWindow(filemgr.text("search_egroup"));
2697                                 return;
2698                                 }
2699                         url += "&group="+filemgr.urlize(g);
2700                         }
2701                 if (smore.getState()) {
2702                         String m = more.getText().trim();
2703                         try { Integer.parseInt(m); }
2704                         catch(Exception e) {
2705                                 new ErrorWindow(filemgr.text("search_esize"));
2706                                 return;
2707                                 }
2708                         url += "&size=%2B"+m+"c";
2709                         }
2710                 else if (sless.getState()) {
2711                         String l = less.getText().trim();
2712                         try { Integer.parseInt(l); }
2713                         catch(Exception e) {
2714                                 new ErrorWindow(filemgr.text("search_esize"));
2715                                 return;
2716                                 }
2717                         url += "&size=%2D"+l+"c";
2718                         }
2719                 if (xon.getState())
2720                         url += "&xdev=1";
2721
2722                 // send off the search
2723                 setCursor(WAIT_CURSOR);
2724                 String f[] = filemgr.get_text(url);
2725                 if (f[0].length() > 0) {
2726                         new ErrorWindow(f[0]);
2727                         return;
2728                         }
2729                 Object rows[][] = new Object[f.length-1][];
2730                 results = new RemoteFile[f.length-1];
2731                 for(int i=1; i<f.length; i++) {
2732                         RemoteFile r = new RemoteFile(filemgr, f[i], null);
2733                         results[i-1] = r;
2734                         Object row[] = rows[i-1] = new Object[3];
2735                         row[0] = filemgr.get_image(RemoteFile.tmap[r.type]);
2736                         row[1] = r.path;
2737                         if (r.size < 1000)
2738                                 row[2] = filemgr.spad(r.size, 5)+" B";
2739                         else if (r.size < 1000000)
2740                                 row[2] = filemgr.spad(r.size/1000, 5)+" kB";
2741                         else
2742                                 row[2] = filemgr.spad(r.size/1000000, 5)+" MB";
2743                         }
2744                 list.clear();
2745                 list.addItems(rows);
2746                 tab.select(filemgr.text("search_list"));
2747                 setCursor(DEFAULT_CURSOR);
2748                 }
2749         }
2750
2751         public void singleClick(MultiColumn list, int num)
2752         {
2753         }
2754
2755         // go to the directory of the double-clicked file
2756         public void doubleClick(MultiColumn list, int num)
2757         {
2758         RemoteFile f = results[num];
2759         int sl = f.path.lastIndexOf('/');
2760         String dir = sl == 0 ? "/" : f.path.substring(0, sl);
2761         filemgr.find_directory(dir, true);
2762         RemoteFile l[] = filemgr.showing_list;
2763         for(int i=0; i<l.length; i++) {
2764                 if (l[i].name.equals(f.name)) {
2765                         // select the file in the list
2766                         filemgr.files.select(i+1);
2767                         filemgr.files.scrollto(i+1);
2768                         break;
2769                         }
2770                 }
2771         dispose();
2772         }
2773
2774         public void headingClicked(MultiColumn list, int col)
2775         {
2776         }
2777 }
2778
2779 class FileSystem
2780 {
2781         String mount;
2782         String dev;
2783         String type;
2784         String opts[];
2785         boolean acls;
2786         boolean attrs;
2787         boolean ext;
2788
2789         FileSystem(String l)
2790         {
2791         StringSplitter tok = new StringSplitter(l, ' ');
2792         mount = tok.nextToken();
2793         dev = tok.nextToken();
2794         type = tok.nextToken();
2795         String optstr = tok.nextToken();
2796         acls = tok.nextToken().equals("1");
2797         attrs = tok.nextToken().equals("1");
2798         ext = tok.nextToken().equals("1");
2799
2800         StringTokenizer tok2 = new StringTokenizer(optstr, ",");
2801         opts = new String[tok2.countTokens()];
2802         for(int i=0; i<opts.length; i++)
2803                 opts[i] = tok2.nextToken();
2804         }
2805 }
2806
2807 class ACLEntry
2808 {
2809         FileManager filemgr;
2810         RemoteFile file;
2811         boolean def;
2812         String type;
2813         String owner;
2814         boolean read, write, exec;
2815
2816         ACLEntry(String l, ACLWindow w)
2817         {
2818         filemgr = w.filemgr;
2819         file = w.file;
2820         StringSplitter tok = new StringSplitter(l, ':');
2821         type = tok.nextToken();
2822         if (type.equals("default")) {
2823                 def = true;
2824                 type = tok.nextToken();
2825                 }
2826         if (!type.equals("mask") && !type.equals("other")) {
2827                 owner = tok.nextToken();
2828                 if (owner.length() == 0)
2829                         owner = null;
2830                 }
2831         String rwx = tok.nextToken();
2832         if (rwx.length() == 0)
2833                 rwx = tok.nextToken();  // getfacl outputs a blank owner for mask
2834                                         // and other on some systems
2835         read = (rwx.charAt(0) == 'r');
2836         write = (rwx.charAt(1) == 'w');
2837         exec = (rwx.charAt(2) == 'x');
2838         }
2839
2840         ACLEntry(ACLWindow w)
2841         {
2842         filemgr = w.filemgr;
2843         file = w.file;
2844         }
2845
2846         String[] getRow()
2847         {
2848         String rv[] = new String[3];
2849         String t = def ? "acltype_default_"+type : "acltype_"+type;
2850         rv[0] = filemgr.text(t);
2851         if (type.equals("mask") || type.equals("other") ||
2852             (def && owner == null))
2853                 rv[1] = "";
2854         else if (owner != null)
2855                 rv[1] = owner;
2856         else if (type.equals("user"))
2857                 rv[1] = filemgr.text("eacl_user", file.user);
2858         else
2859                 rv[1] = filemgr.text("eacl_group", file.group);
2860         rv[2] = "";
2861         if (read) rv[2] += filemgr.text("info_read")+" ";
2862         if (write) rv[2] += filemgr.text("info_write")+" ";
2863         if (exec) rv[2] += filemgr.text("info_exec")+" ";
2864         return rv;
2865         }
2866
2867         public String toString()
2868         {
2869         String rv = def ? "default:" : "";
2870         rv += type+":";
2871         if (!type.equals("mask") && !type.equals("other"))
2872                 rv += (owner == null ? "" : owner)+":";
2873         rv += (read ? 'r' : '-');
2874         rv += (write ? 'w' : '-');
2875         rv += (exec ? 'x' : '-');
2876         return rv;
2877         }
2878 }
2879
2880 class ACLEditor extends FixedFrame implements CbButtonCallback
2881 {
2882         FileManager filemgr;
2883         ACLWindow aclwin;
2884         ACLEntry acl;
2885         boolean creating;
2886         CbButton ok, del;
2887         Checkbox read, write, exec, owner1, owner2;
2888         TextField owner;
2889
2890         // Editing an existing ACL entry
2891         ACLEditor(ACLWindow w, ACLEntry a)
2892         {
2893         aclwin = w;
2894         filemgr = aclwin.filemgr;
2895         acl = a;
2896         creating = false;
2897         makeUI();
2898         }
2899
2900         // Creating a new ACL entry
2901         ACLEditor(ACLWindow w, String type, boolean def)
2902         {
2903         aclwin = w;
2904         filemgr = aclwin.filemgr;
2905         acl = new ACLEntry(aclwin);
2906         acl.def = def;
2907         acl.type = type;
2908         creating = true;
2909         makeUI();
2910         }
2911
2912         void makeUI()
2913         {
2914         setTitle(filemgr.text(creating ? "eacl_create" : "eacl_edit"));
2915         setLayout(new BorderLayout());
2916         Panel left = new Panel();
2917         left.setLayout(new GridLayout(0, 1));
2918         add("West", left);
2919         Panel right = new Panel();
2920         right.setLayout(new GridLayout(0, 1));
2921         add("East", right);
2922
2923         left.add(new Label(filemgr.text("eacl_acltype")));
2924         TextField type;
2925         right.add(type = new TextField(
2926                                 (acl.def ? "default " : "")+acl.type, 20));
2927         type.setEditable(false);
2928
2929         if (!acl.type.equals("mask") && !acl.type.equals("other")) {
2930                 left.add(new Label(filemgr.text("eacl_aclname")));
2931                 if (acl.def) {
2932                         // A default user or group ACL .. can be for
2933                         // a specific user, or for the file owner
2934                         Panel op = new Panel();
2935                         op.setLayout(new FlowLayout(FlowLayout.LEFT, 0, 0));
2936                         CheckboxGroup gr = new CheckboxGroup();
2937                         op.add(owner1 = new Checkbox(filemgr.text("eacl_owner"),
2938                                             gr, acl.owner == null));
2939                         op.add(owner2 = new Checkbox("",
2940                                             gr, acl.owner != null));
2941                         op.add(owner = new TextField(
2942                                 acl.owner == null ? "" : acl.owner, 20));
2943                         right.add(op);
2944                         }
2945                 else if (creating || acl.owner != null) {
2946                         // A user or group ACL for a specific user
2947                         owner = new TextField(
2948                                         acl.owner == null ? "" : acl.owner, 20);
2949                         right.add(owner);
2950                         }
2951                 else {
2952                         // A user or group ACL for the file owner
2953                         String str;
2954                         if (acl.type.equals("user"))
2955                             str = filemgr.text("eacl_user", aclwin.file.user);
2956                         else
2957                             str = filemgr.text("eacl_group", aclwin.file.group);
2958                         TextField o = new TextField(str);
2959                         o.setEditable(false);
2960                         right.add(o);
2961                         }
2962                 }
2963
2964         left.add(new Label(filemgr.text("eacl_aclperms")));
2965         Panel pp = new Panel();
2966         pp.setLayout(new FlowLayout(FlowLayout.RIGHT));
2967         pp.add(read = new Checkbox(filemgr.text("info_read"), null, acl.read));
2968         pp.add(write = new Checkbox(filemgr.text("info_write"), null, acl.write));
2969         pp.add(exec = new Checkbox(filemgr.text("info_exec"), null, acl.exec));
2970         right.add(pp);
2971
2972         Panel bot = new Panel();
2973         bot.setLayout(new FlowLayout(FlowLayout.RIGHT));
2974         bot.add(ok = new CbButton(filemgr.get_image("save.gif"),
2975                                   filemgr.text("save"),
2976                                   CbButton.LEFT, this));
2977         if (!creating && (acl.owner != null || acl.def))
2978                 bot.add(del = new CbButton(filemgr.get_image("cancel.gif"),
2979                                            filemgr.text("delete"),
2980                                            CbButton.LEFT, this));
2981         add("South", bot);
2982
2983         pack();
2984         show();
2985         }
2986
2987         public void click(CbButton b)
2988         {
2989         if (b == ok) {
2990                 // Update or add the ACL entry
2991                 if (owner1 != null && owner1.getState()) {
2992                         acl.owner = null;
2993                         }
2994                 else if (owner != null) {
2995                         String o = owner.getText().trim();
2996                         if (o.length() == 0 && !acl.def) {
2997                                 new ErrorWindow(filemgr.text("eacl_eowner"));
2998                                 return;
2999                                 }
3000                         acl.owner = owner.getText();
3001                         if (acl.owner.length() == 0)
3002                                 acl.owner = null;
3003                         }
3004                 acl.read = read.getState();
3005                 acl.write = write.getState();
3006                 acl.exec = exec.getState();
3007                 if (creating) {
3008                         // Add to the ACL table
3009                         aclwin.acllist.addElement(acl);
3010                         aclwin.acltable.addItem(acl.getRow());
3011                         }
3012                 else {
3013                         // Update the table
3014                         int idx = aclwin.acllist.indexOf(acl);
3015                         aclwin.acltable.modifyItem(acl.getRow(), idx);
3016                         }
3017                 dispose();
3018                 }
3019         else if (b == del) {
3020                 // Remove this entry
3021                 int idx = aclwin.acllist.indexOf(acl);
3022                 aclwin.acllist.removeElementAt(idx);
3023                 aclwin.acltable.deleteItem(idx);
3024                 dispose();
3025                 }
3026         }
3027
3028         public void dispose()
3029         {
3030         aclwin.edmap.remove(acl);
3031         super.dispose();
3032         }
3033 }
3034
3035 class ACLWindow extends FixedFrame implements CbButtonCallback,MultiColumnCallback
3036 {
3037         FileManager filemgr;
3038         RemoteFile file;
3039         Vector acllist = new Vector();
3040         Hashtable edmap = new Hashtable();
3041
3042         CbButton ok, cancel, add;
3043         Choice addtype;
3044         MultiColumn acltable;
3045
3046         String acltypes[] = { "user", "group", "mask",
3047                               "default user", "default group", "default other",
3048                               "default mask" };
3049
3050         ACLWindow(FileManager p, RemoteFile f)
3051         {
3052         super(400, 300);
3053         setTitle(p.text("eacl_title", f.path));
3054         filemgr = p;
3055         file = f;
3056
3057         // Get the ACLs
3058         String a[] = filemgr.get_text(
3059                         "getfacl.cgi?file="+filemgr.urlize(file.path));
3060         if (a[0].length() != 0) {
3061                 new ErrorWindow(filemgr.text("eacl_eacls", a[0]));
3062                 return;
3063                 }
3064
3065         // Create the UI
3066         setLayout(new BorderLayout());
3067         String titles[] = { filemgr.text("eacl_acltype"),
3068                             filemgr.text("eacl_aclname"),
3069                             filemgr.text("eacl_aclperms") };
3070         acltable = new MultiColumn(titles, this);
3071         for(int i=1; i<a.length; i++) {
3072                 ACLEntry acl = new ACLEntry(a[i], this);
3073                 acllist.addElement(acl);
3074                 acltable.addItem(acl.getRow());
3075                 }
3076         add("Center", acltable);
3077         Panel abot = new Panel();
3078         abot.setLayout(new FlowLayout(FlowLayout.RIGHT));
3079         abot.add(add = new CbButton(filemgr.get_image("add.gif"),
3080                                    filemgr.text("eacl_add"),
3081                                    CbButton.LEFT, this));
3082         int len = file.type == RemoteFile.DIR ? acltypes.length : 3;
3083         abot.add(addtype = new Choice());
3084         for(int i=0; i<len; i++) {
3085                 String t = "acltype_"+acltypes[i].replace(' ', '_');
3086                 addtype.addItem(filemgr.text(t));
3087                 }
3088         abot.add(new Label(" "));
3089         abot.add(ok = new CbButton(filemgr.get_image("save.gif"),
3090                                    filemgr.text("save"),
3091                                    CbButton.LEFT, this));
3092         abot.add(cancel = new CbButton(filemgr.get_image("cancel.gif"),
3093                                        filemgr.text("cancel"),
3094                                        CbButton.LEFT, this));
3095         add("South", abot);
3096
3097         pack();
3098         show();
3099         }
3100
3101         public void click(CbButton b)
3102         {
3103         if (b == ok) {
3104                 // Check if there are any defaults, and if so there must
3105                 // be default user, group and other
3106                 boolean anydef = false, defuser = false,
3107                         defgroup = false, defother = false;
3108                 for(int i=0; i<acllist.size(); i++) {
3109                         ACLEntry e = (ACLEntry)acllist.elementAt(i);
3110                         if (e.def) anydef = true;
3111                         if (e.def && e.owner == null) {
3112                                 if (e.type.equals("user")) defuser = true;
3113                                 if (e.type.equals("group")) defgroup = true;
3114                                 if (e.type.equals("other")) defother = true;
3115                                 }
3116                         }
3117                 if (anydef && (!defuser || !defgroup || !defother)) {
3118                         new ErrorWindow(filemgr.text("eacl_edefaults"));
3119                         return;
3120                         }
3121
3122                 // Save the ACLs
3123                 String aclstr = "";
3124                 for(int i=0; i<acllist.size(); i++)
3125                         aclstr += (ACLEntry)acllist.elementAt(i)+"\n";
3126                 String rv[] = filemgr.get_text("setfacl.cgi?file="+
3127                                                 filemgr.urlize(file.path)+
3128                                                 "&acl="+filemgr.urlize(aclstr));
3129                 if (rv[0].length() > 0)
3130                         new ErrorWindow(filemgr.text("eacl_efailed",
3131                                 file.path, rv[0]));
3132                 else
3133                         dispose();
3134                 }
3135         else if (b == add) {
3136                 // Open a window for a new ACL entry
3137                 String t = acltypes[addtype.getSelectedIndex()];
3138                 String d = "default ";
3139                 boolean def = t.startsWith(d);
3140                 if (def)
3141                         t = t.substring(d.length());
3142                 if (t.equals("mask")) {
3143                         // Only allow one mask
3144                         for(int i=0; i<acllist.size(); i++) {
3145                                 ACLEntry a = (ACLEntry)acllist.elementAt(i);
3146                                 if (a.type.equals(t) && a.def == def) {
3147                                         new ErrorWindow(filemgr.text(def ?
3148                                             "eacl_edefmask" : "eacl_emask"));
3149                                         return;
3150                                         }
3151                                 }
3152                         }
3153                 new ACLEditor(this, t, def);
3154                 }
3155         else if (b == cancel) {
3156                 // Don't save
3157                 dispose();
3158                 }
3159         }
3160
3161         // Bring up an editor for an ACL
3162         public void doubleClick(MultiColumn list, int num)
3163         {
3164         int idx = list.selected();
3165         if (idx >= 0) {
3166                 ACLEntry e = (ACLEntry)acllist.elementAt(idx);
3167                 ACLEditor ed = (ACLEditor)edmap.get(e);
3168                 if (ed == null)
3169                         edmap.put(e, new ACLEditor(this, e));
3170                 else {
3171                         ed.toFront();
3172                         ed.requestFocus();
3173                         }
3174                 }
3175         }
3176
3177         public void singleClick(MultiColumn list, int num)
3178         {
3179         }
3180
3181         public void headingClicked(MultiColumn list, int col)
3182         {
3183         }
3184 }
3185
3186 class AttributesWindow extends FixedFrame
3187         implements CbButtonCallback,MultiColumnCallback
3188 {
3189         FileManager filemgr;
3190         RemoteFile file;
3191         Vector attrlist = new Vector();
3192         Hashtable edmap = new Hashtable();
3193
3194         CbButton ok, cancel, add;
3195         MultiColumn attrtable;
3196
3197         AttributesWindow(FileManager p, RemoteFile f)
3198         {
3199         super(400, 300);
3200         setTitle(p.text("attr_title", f.path));
3201         filemgr = p;
3202         file = f;
3203
3204         // Get the attributes
3205         String a[] = filemgr.get_text(
3206                         "getattrs.cgi?file="+filemgr.urlize(file.path));
3207         if (a[0].length() != 0) {
3208                 new ErrorWindow(filemgr.text("attr_eattrs", a[0]));
3209                 return;
3210                 }
3211
3212         // Create the UI
3213         setLayout(new BorderLayout());
3214         String titles[] = { filemgr.text("attr_name"),
3215                             filemgr.text("attr_value") };
3216         attrtable = new MultiColumn(titles, this);
3217         for(int i=1; i<a.length; i++) {
3218                 FileAttribute at = new FileAttribute(a[i], filemgr);
3219                 attrlist.addElement(at);
3220                 attrtable.addItem(at.getRow());
3221                 }
3222         add("Center", attrtable);
3223         Panel abot = new Panel();
3224         abot.setLayout(new FlowLayout(FlowLayout.RIGHT));
3225         abot.add(add = new CbButton(filemgr.get_image("add.gif"),
3226                                    filemgr.text("attr_add"),
3227                                    CbButton.LEFT, this));
3228         abot.add(new Label(" "));
3229         abot.add(ok = new CbButton(filemgr.get_image("save.gif"),
3230                                    filemgr.text("save"),
3231                                    CbButton.LEFT, this));
3232         abot.add(cancel = new CbButton(filemgr.get_image("cancel.gif"),
3233                                        filemgr.text("cancel"),
3234                                        CbButton.LEFT, this));
3235         add("South", abot);
3236
3237         pack();
3238         show();
3239         }
3240
3241         public void click(CbButton b)
3242         {
3243         if (b == ok) {
3244                 // Save the attributes
3245                 String pstr = "";
3246                 for(int i=0; i<attrlist.size(); i++) {
3247                         FileAttribute at = (FileAttribute)attrlist.elementAt(i);
3248                         pstr += "&name"+i+"="+filemgr.urlize(at.name)+
3249                                 "&value"+i+"="+filemgr.urlize(at.value);
3250                         }
3251                 String rv[] = filemgr.get_text("setattrs.cgi?file="+
3252                                                 filemgr.urlize(file.path)+pstr);
3253                 if (rv[0].length() > 0)
3254                         new ErrorWindow(filemgr.text("attr_efailed",
3255                                 file.path, rv[0]));
3256                 else
3257                         dispose();
3258                 }
3259         else if (b == add) {
3260                 // Open a window for a new ACL entry
3261                 new AttributeEditor(this);
3262                 }
3263         else if (b == cancel) {
3264                 // Don't save
3265                 dispose();
3266                 }
3267         }
3268
3269         // Bring up an editor for an ACL
3270         public void doubleClick(MultiColumn list, int num)
3271         {
3272         int idx = list.selected();
3273         if (idx >= 0) {
3274                 FileAttribute at = (FileAttribute)attrlist.elementAt(idx);
3275                 AttributeEditor ed = (AttributeEditor)edmap.get(at);
3276                 if (ed == null)
3277                         edmap.put(at, new AttributeEditor(this, at));
3278                 else {
3279                         ed.toFront();
3280                         ed.requestFocus();
3281                         }
3282                 }
3283         }
3284
3285         public void singleClick(MultiColumn list, int num)
3286         {
3287         }
3288
3289         public void headingClicked(MultiColumn list, int col)
3290         {
3291         }
3292 }
3293
3294 class FileAttribute
3295 {
3296         String name;
3297         String value;
3298
3299         FileAttribute(String l, FileManager f)
3300         {
3301         int eq = l.indexOf('=');
3302         name = f.un_urlize(l.substring(0, eq));
3303         value = f.un_urlize(l.substring(eq+1));
3304         }
3305
3306         FileAttribute(String n, String v)
3307         {
3308         name = n;
3309         value = v;
3310         }
3311
3312         String[] getRow()
3313         {
3314         return new String[] { name, value };
3315         }
3316 }
3317
3318 class AttributeEditor extends FixedFrame implements CbButtonCallback
3319 {
3320         FileManager filemgr;
3321         AttributesWindow attrwin;
3322         FileAttribute attr;
3323         boolean creating;
3324         CbButton ok, del;
3325         TextField name;
3326         TextArea value;
3327
3328         AttributeEditor(AttributesWindow w, FileAttribute a)
3329         {
3330         attrwin = w;
3331         attr = a;
3332         filemgr = w.filemgr;
3333         creating = false;
3334         makeUI();
3335         }
3336
3337         AttributeEditor(AttributesWindow w)
3338         {
3339         attrwin = w;
3340         attr = new FileAttribute("", "");
3341         filemgr = w.filemgr;
3342         creating = true;
3343         makeUI();
3344         }
3345
3346         void makeUI()
3347         {
3348         setTitle(filemgr.text(creating ? "attr_create" : "attr_edit"));
3349         setLayout(new BorderLayout());
3350
3351         Panel top = new Panel();
3352         top.setLayout(new GridLayout(1, 2));
3353         top.add(new Label(filemgr.text("attr_name")));
3354         top.add(name = new TextField(attr.name, 20));
3355         add("North", top);
3356
3357         Panel mid = new Panel();
3358         mid.setLayout(new GridLayout(1, 2));
3359         mid.add(new Label(filemgr.text("attr_value")));
3360         mid.add(value = new TextArea(attr.value, 5, 20));
3361         add("Center", mid);
3362
3363         Panel bot = new Panel();
3364         bot.setLayout(new FlowLayout(FlowLayout.RIGHT));
3365         bot.add(ok = new CbButton(filemgr.get_image("save.gif"),
3366                                   filemgr.text("save"),
3367                                   CbButton.LEFT, this));
3368         if (!creating)
3369                 bot.add(del = new CbButton(filemgr.get_image("cancel.gif"),
3370                                            filemgr.text("delete"),
3371                                            CbButton.LEFT, this));
3372         add("South", bot);
3373
3374         pack();
3375         show();
3376         }
3377
3378         public void click(CbButton b)
3379         {
3380         if (b == ok) {
3381                 // Update or add the attribute
3382                 if (name.getText().length() == 0) {
3383                         new ErrorWindow(filemgr.text("attr_ename"));
3384                         return;
3385                         }
3386                 attr.name = name.getText();
3387                 attr.value = value.getText();
3388                 if (creating) {
3389                         // Add to the attribs table
3390                         attrwin.attrlist.addElement(attr);
3391                         attrwin.attrtable.addItem(attr.getRow());
3392                         }
3393                 else {
3394                         // Update the table
3395                         int idx = attrwin.attrlist.indexOf(attr);
3396                         attrwin.attrtable.modifyItem(attr.getRow(), idx);
3397                         }
3398                 dispose();
3399                 }
3400         else if (b == del) {
3401                 // Remove this entry
3402                 int idx = attrwin.attrlist.indexOf(attr);
3403                 attrwin.attrlist.removeElementAt(idx);
3404                 attrwin.attrtable.deleteItem(idx);
3405                 dispose();
3406                 }
3407         }
3408
3409         public void dispose()
3410         {
3411         attrwin.edmap.remove(attr);
3412         super.dispose();
3413         }
3414 }
3415
3416 class EXTWindow extends FixedFrame implements CbButtonCallback
3417 {
3418         FileManager filemgr;
3419         RemoteFile file;
3420
3421         CbButton ok, cancel;
3422         Checkbox cbs[];
3423
3424         String attrs[] = { "A", "a", "c", "d", "i", "s", "S", "u" };
3425         Hashtable attrmap = new Hashtable();
3426
3427         EXTWindow(FileManager p, RemoteFile f)
3428         {
3429         super();
3430         setTitle(p.text("ext_title", f.path));
3431         filemgr = p;
3432         file = f;
3433
3434         // Get the attributes
3435         String a[] = filemgr.get_text(
3436                         "getext.cgi?file="+filemgr.urlize(file.path));
3437         if (a[0].length() != 0) {
3438                 new ErrorWindow(filemgr.text("ext_eattrs", a[0]));
3439                 return;
3440                 }
3441         for(int i=0; i<a[1].length(); i++)
3442                 attrmap.put(a[1].substring(i, i+1), "");
3443
3444         // Create the UI
3445         setLayout(new BorderLayout());
3446         Panel top = new LinedPanel(filemgr.text("ext_header"));
3447         top.setLayout(new GridLayout(0, 1));
3448         cbs = new Checkbox[attrs.length];
3449         for(int i=0; i<attrs.length; i++) {
3450                 cbs[i] = new Checkbox(filemgr.text("eattr_"+attrs[i]));
3451                 cbs[i].setState(attrmap.get(attrs[i]) != null);
3452                 top.add(cbs[i]);
3453                 }
3454         add("Center", top);
3455
3456         Panel bot = new Panel();
3457         bot.setLayout(new FlowLayout(FlowLayout.RIGHT));
3458         bot.add(ok = new CbButton(filemgr.get_image("save.gif"),
3459                                   filemgr.text("save"),
3460                                   CbButton.LEFT, this));
3461         bot.add(cancel = new CbButton(filemgr.get_image("cancel.gif"),
3462                                       filemgr.text("cancel"),
3463                                       CbButton.LEFT, this));
3464         add("South", bot);
3465
3466         pack();
3467         show();
3468         }
3469
3470         public void click(CbButton b)
3471         {
3472         if (b == ok) {
3473                 // Save the attributes (including unknown ones)
3474                 String astr = "";
3475                 for(int i=0; i<cbs.length; i++) {
3476                         if (cbs[i].getState())
3477                                 astr += attrs[i];
3478                         attrmap.remove(attrs[i]);
3479                         }
3480                 for(Enumeration e = attrmap.keys(); e.hasMoreElements(); )
3481                         astr += e.nextElement();
3482
3483                 // Try to set on the server
3484                 String rv[] = filemgr.get_text("setext.cgi?file="+
3485                                 filemgr.urlize(file.path)+"&attrs="+astr);
3486                 if (rv[0].length() > 0)
3487                         new ErrorWindow(filemgr.text("ext_efailed",
3488                                 file.path, rv[0]));
3489                 else
3490                         dispose();
3491                 }
3492         else if (b == cancel) {
3493                 dispose();
3494                 }
3495         }
3496 }
3497