Test checkin of some modules
authorJamie Cameron <jcameron@webmin.com>
Thu, 12 Apr 2007 19:28:29 +0000 (19:28 +0000)
committerJamie Cameron <jcameron@webmin.com>
Thu, 12 Apr 2007 19:28:29 +0000 (19:28 +0000)
358 files changed:
file/BorderPanel.java [new file with mode: 0755]
file/CHANGELOG [new file with mode: 0644]
file/CbButton.java [new file with mode: 0644]
file/CbColorButton.java [new file with mode: 0644]
file/CbColorWindow.java [new file with mode: 0644]
file/CbImageChooser.java [new file with mode: 0644]
file/CbScrollbar.java [new file with mode: 0644]
file/CbSlider.java [new file with mode: 0644]
file/ErrorWindow.java [new file with mode: 0644]
file/FileManager.java [new file with mode: 0644]
file/FileManager.java.bak [new file with mode: 0644]
file/FixedFrame.java [new file with mode: 0644]
file/GrayPanel.java [new file with mode: 0644]
file/Hierarchy.java [new file with mode: 0644]
file/Hierarchy.java.bak [new file with mode: 0644]
file/LinedPanel.java [new file with mode: 0644]
file/Makefile [new file with mode: 0644]
file/MultiColumn.java [new file with mode: 0644]
file/MultiColumn.java.bak [new file with mode: 0644]
file/QuickSort.java [new file with mode: 0644]
file/ResizePanel.java [new file with mode: 0644]
file/StaticTextField.java [new file with mode: 0644]
file/StringSplitter.java [new file with mode: 0644]
file/TabbedPanel.java [new file with mode: 0644]
file/ToolbarLayout.java [new file with mode: 0644]
file/Util.java [new file with mode: 0644]
file/acl_security.pl [new file with mode: 0644]
file/chmod.cgi [new file with mode: 0755]
file/config [new file with mode: 0644]
file/config-*-linux [new file with mode: 0644]
file/config-irix [new file with mode: 0644]
file/config-solaris [new file with mode: 0644]
file/config.info [new file with mode: 0644]
file/config.info.ca [new file with mode: 0755]
file/config.info.de [new file with mode: 0644]
file/config.info.es [new file with mode: 0644]
file/config.info.fa [new file with mode: 0644]
file/config.info.it [new file with mode: 0755]
file/config.info.tr [new file with mode: 0644]
file/copy.cgi [new file with mode: 0755]
file/defaultacl [new file with mode: 0644]
file/delete.cgi [new file with mode: 0755]
file/edit_html.cgi [new file with mode: 0755]
file/extract.cgi [new file with mode: 0755]
file/file-lib.pl [new file with mode: 0644]
file/filesystems.cgi [new file with mode: 0755]
file/getattrs.cgi [new file with mode: 0755]
file/getext.cgi [new file with mode: 0755]
file/getext.cgi.bak [new file with mode: 0755]
file/getfacl.cgi [new file with mode: 0755]
file/images/.xvpics/preview.gif [new file with mode: 0644]
file/images/Thumbs.db [new file with mode: 0755]
file/images/acl.gif [new file with mode: 0644]
file/images/add.gif [new file with mode: 0644]
file/images/all.gif [new file with mode: 0644]
file/images/attr.gif [new file with mode: 0644]
file/images/binary.gif [new file with mode: 0644]
file/images/cancel.gif [new file with mode: 0644]
file/images/config.gif [new file with mode: 0644]
file/images/copy.gif [new file with mode: 0644]
file/images/cut.gif [new file with mode: 0644]
file/images/delete.gif [new file with mode: 0644]
file/images/device.gif [new file with mode: 0644]
file/images/dir.gif [new file with mode: 0644]
file/images/down.gif [new file with mode: 0644]
file/images/edit.gif [new file with mode: 0644]
file/images/ext.gif [new file with mode: 0644]
file/images/extract.gif [new file with mode: 0644]
file/images/file.gif [new file with mode: 0644]
file/images/find.gif [new file with mode: 0644]
file/images/goto.gif [new file with mode: 0644]
file/images/html.gif [new file with mode: 0755]
file/images/icon.gif [new file with mode: 0644]
file/images/image.gif [new file with mode: 0644]
file/images/makelink.gif [new file with mode: 0644]
file/images/mdir.gif [new file with mode: 0644]
file/images/mkdir.gif [new file with mode: 0644]
file/images/mount.gif [new file with mode: 0644]
file/images/new.gif [new file with mode: 0644]
file/images/open.gif [new file with mode: 0644]
file/images/paste.gif [new file with mode: 0644]
file/images/pipe.gif [new file with mode: 0644]
file/images/preview.gif [new file with mode: 0644]
file/images/props.gif [new file with mode: 0644]
file/images/refresh.gif [new file with mode: 0644]
file/images/rename.gif [new file with mode: 0644]
file/images/replace.gif [new file with mode: 0644]
file/images/ret.gif [new file with mode: 0644]
file/images/run.gif [new file with mode: 0644]
file/images/save.gif [new file with mode: 0644]
file/images/sdir.gif [new file with mode: 0644]
file/images/search.gif [new file with mode: 0644]
file/images/share.gif [new file with mode: 0644]
file/images/smallicon.gif [new file with mode: 0644]
file/images/smdir.gif [new file with mode: 0644]
file/images/sub.gif [new file with mode: 0644]
file/images/sudir.gif [new file with mode: 0644]
file/images/symlink.gif [new file with mode: 0644]
file/images/text.gif [new file with mode: 0644]
file/images/udir.gif [new file with mode: 0644]
file/images/unknown.gif [new file with mode: 0644]
file/images/upload.gif [new file with mode: 0644]
file/images/view.gif [new file with mode: 0644]
file/index.cgi [new file with mode: 0755]
file/index.cgi.bak [new file with mode: 0755]
file/irix-getfacl.pl [new file with mode: 0755]
file/irix-setfacl.pl [new file with mode: 0755]
file/lang.cgi [new file with mode: 0755]
file/lang/bg [new file with mode: 0644]
file/lang/ca [new file with mode: 0644]
file/lang/de [new file with mode: 0644]
file/lang/en [new file with mode: 0644]
file/lang/es [new file with mode: 0644]
file/lang/fa [new file with mode: 0644]
file/lang/fr [new file with mode: 0644]
file/lang/it [new file with mode: 0644]
file/lang/ja_JP.UTF-8 [new file with mode: 0644]
file/lang/ja_JP.euc [new file with mode: 0644]
file/lang/ko_KR.UTF-8 [new file with mode: 0644]
file/lang/ko_KR.euc [new file with mode: 0644]
file/lang/pl [new file with mode: 0644]
file/lang/pt [new file with mode: 0644]
file/lang/pt_BR [new file with mode: 0644]
file/lang/ru_RU [new file with mode: 0644]
file/lang/ru_SU [new file with mode: 0644]
file/lang/sk [new file with mode: 0644]
file/lang/sv [new file with mode: 0644]
file/lang/tr [new file with mode: 0644]
file/lang/tr.bak [new file with mode: 0644]
file/lang/uk_UA [new file with mode: 0644]
file/lang/zh_CN [new file with mode: 0644]
file/lang/zh_CN.UTF-8 [new file with mode: 0644]
file/lang/zh_TW.Big5 [new file with mode: 0644]
file/lang/zh_TW.UTF-8 [new file with mode: 0644]
file/list.cgi [new file with mode: 0755]
file/list_exports.cgi [new file with mode: 0755]
file/list_shares.cgi [new file with mode: 0755]
file/log_parser.pl [new file with mode: 0644]
file/makelink.cgi [new file with mode: 0755]
file/mkdir.cgi [new file with mode: 0755]
file/module.info [new file with mode: 0644]
file/mount.cgi [new file with mode: 0755]
file/move.cgi [new file with mode: 0755]
file/preview.cgi [new file with mode: 0755]
file/rename.cgi [new file with mode: 0755]
file/root.cgi [new file with mode: 0755]
file/save.cgi [new file with mode: 0755]
file/save_export.cgi [new file with mode: 0755]
file/save_html.cgi [new file with mode: 0755]
file/save_share.cgi [new file with mode: 0755]
file/search.cgi [new file with mode: 0755]
file/setattrs.cgi [new file with mode: 0755]
file/setext.cgi [new file with mode: 0755]
file/setfacl.cgi [new file with mode: 0755]
file/show.cgi [new file with mode: 0755]
file/size.cgi [new file with mode: 0755]
file/unicode.pl [new file with mode: 0755]
file/unicode/zh_TW.Big5 [new file with mode: 0644]
file/upform.cgi [new file with mode: 0755]
file/upload.cgi [new file with mode: 0755]
file/upload2.cgi [new file with mode: 0755]
file/xinha [new symlink]
mailboxes/CHANGELOG [new file with mode: 0644]
mailboxes/Makefile [new file with mode: 0644]
mailboxes/acl_security.pl [new file with mode: 0644]
mailboxes/boxes-lib.pl [new file with mode: 0644]
mailboxes/config [new file with mode: 0644]
mailboxes/config.info [new file with mode: 0644]
mailboxes/config.info.ca [new file with mode: 0644]
mailboxes/config.info.de [new file with mode: 0644]
mailboxes/config_info.pl [new file with mode: 0644]
mailboxes/defaultacl [new file with mode: 0644]
mailboxes/delete_all.cgi [new file with mode: 0755]
mailboxes/delete_mail.cgi [new file with mode: 0755]
mailboxes/detach.cgi [new file with mode: 0755]
mailboxes/find.cgi [new file with mode: 0755]
mailboxes/folders-lib.pl [new file with mode: 0644]
mailboxes/images/attach.gif [new file with mode: 0644]
mailboxes/images/boxes.gif [new file with mode: 0644]
mailboxes/images/error.gif [new file with mode: 0644]
mailboxes/images/icon.gif [new file with mode: 0644]
mailboxes/images/p1.gif [new file with mode: 0644]
mailboxes/images/p2.gif [new file with mode: 0644]
mailboxes/images/read.gif [new file with mode: 0644]
mailboxes/images/smallicon.gif [new file with mode: 0644]
mailboxes/images/special.gif [new file with mode: 0644]
mailboxes/index.cgi [new file with mode: 0755]
mailboxes/lang/ca [new file with mode: 0644]
mailboxes/lang/de [new file with mode: 0644]
mailboxes/lang/en [new file with mode: 0644]
mailboxes/lang/es [new file with mode: 0644]
mailboxes/lang/fr [new file with mode: 0644]
mailboxes/lang/it [new file with mode: 0755]
mailboxes/lang/ja_JP.UTF-8 [new file with mode: 0644]
mailboxes/lang/ja_JP.euc [new file with mode: 0644]
mailboxes/lang/ja_JP.jis [new file with mode: 0644]
mailboxes/lang/ko_KR.UTF-8 [new file with mode: 0644]
mailboxes/lang/ko_KR.euc [new file with mode: 0644]
mailboxes/lang/pl [new file with mode: 0644]
mailboxes/lang/pt [new file with mode: 0644]
mailboxes/lang/sv [new file with mode: 0644]
mailboxes/lang/tr [new file with mode: 0644]
mailboxes/lang/zh_CN [new file with mode: 0644]
mailboxes/lang/zh_CN.UTF-8 [new file with mode: 0644]
mailboxes/lang/zh_TW.Big5 [new file with mode: 0644]
mailboxes/lang/zh_TW.UTF-8 [new file with mode: 0644]
mailboxes/list_mail.cgi [new file with mode: 0755]
mailboxes/log_parser.pl [new file with mode: 0644]
mailboxes/mail_search.cgi [new file with mode: 0755]
mailboxes/mailboxes-lib.pl [new file with mode: 0644]
mailboxes/makelang.pl [new file with mode: 0755]
mailboxes/module.info [new file with mode: 0644]
mailboxes/reply_mail.cgi [new file with mode: 0755]
mailboxes/search_form.cgi [new file with mode: 0755]
mailboxes/send_mail.cgi [new file with mode: 0755]
mailboxes/useradmin_update.pl [new file with mode: 0644]
mailboxes/view_mail.cgi [new file with mode: 0755]
mailboxes/xinha/Xinha.css [new file with mode: 0644]
mailboxes/xinha/XinhaCore.js [new file with mode: 0644]
mailboxes/xinha/contrib/lc_parse_strings.php [new file with mode: 0644]
mailboxes/xinha/contrib/php-xinha.php [new file with mode: 0644]
mailboxes/xinha/examples/Extended.html [new file with mode: 0644]
mailboxes/xinha/examples/custom.css [new file with mode: 0644]
mailboxes/xinha/examples/dynamic.css [new file with mode: 0644]
mailboxes/xinha/examples/ext_example-body.html [new file with mode: 0644]
mailboxes/xinha/examples/ext_example-dest.php [new file with mode: 0644]
mailboxes/xinha/examples/ext_example-menu.php [new file with mode: 0644]
mailboxes/xinha/examples/ext_example.html [new file with mode: 0644]
mailboxes/xinha/examples/full_example.css [new file with mode: 0644]
mailboxes/xinha/examples/full_example.js [new file with mode: 0644]
mailboxes/xinha/examples/simple_example.html [new file with mode: 0644]
mailboxes/xinha/examples/stylist.css [new file with mode: 0644]
mailboxes/xinha/examples/testbed.html [new file with mode: 0644]
mailboxes/xinha/htmlarea.js [new file with mode: 0644]
mailboxes/xinha/images/de/bold.gif [new file with mode: 0644]
mailboxes/xinha/images/de/italic.gif [new file with mode: 0644]
mailboxes/xinha/images/de/underline.gif [new file with mode: 0644]
mailboxes/xinha/images/ed_about.gif [new file with mode: 0644]
mailboxes/xinha/images/ed_align.gif [new file with mode: 0644]
mailboxes/xinha/images/ed_align_center.gif [new file with mode: 0644]
mailboxes/xinha/images/ed_align_justify.gif [new file with mode: 0644]
mailboxes/xinha/images/ed_align_left.gif [new file with mode: 0644]
mailboxes/xinha/images/ed_align_right.gif [new file with mode: 0644]
mailboxes/xinha/images/ed_blank.gif [new file with mode: 0644]
mailboxes/xinha/images/ed_buttons_main.gif [new file with mode: 0644]
mailboxes/xinha/images/ed_charmap.gif [new file with mode: 0644]
mailboxes/xinha/images/ed_clearfonts.gif [new file with mode: 0644]
mailboxes/xinha/images/ed_color_bg.gif [new file with mode: 0644]
mailboxes/xinha/images/ed_color_fg.gif [new file with mode: 0644]
mailboxes/xinha/images/ed_copy.gif [new file with mode: 0644]
mailboxes/xinha/images/ed_custom.gif [new file with mode: 0644]
mailboxes/xinha/images/ed_cut.gif [new file with mode: 0644]
mailboxes/xinha/images/ed_delete.gif [new file with mode: 0644]
mailboxes/xinha/images/ed_format_bold.gif [new file with mode: 0644]
mailboxes/xinha/images/ed_format_italic.gif [new file with mode: 0644]
mailboxes/xinha/images/ed_format_strike.gif [new file with mode: 0644]
mailboxes/xinha/images/ed_format_sub.gif [new file with mode: 0644]
mailboxes/xinha/images/ed_format_sup.gif [new file with mode: 0644]
mailboxes/xinha/images/ed_format_underline.gif [new file with mode: 0644]
mailboxes/xinha/images/ed_help.gif [new file with mode: 0644]
mailboxes/xinha/images/ed_hr.gif [new file with mode: 0644]
mailboxes/xinha/images/ed_html.gif [new file with mode: 0644]
mailboxes/xinha/images/ed_image.gif [new file with mode: 0644]
mailboxes/xinha/images/ed_indent_less.gif [new file with mode: 0644]
mailboxes/xinha/images/ed_indent_more.gif [new file with mode: 0644]
mailboxes/xinha/images/ed_killword.gif [new file with mode: 0644]
mailboxes/xinha/images/ed_left_to_right.gif [new file with mode: 0644]
mailboxes/xinha/images/ed_link.gif [new file with mode: 0644]
mailboxes/xinha/images/ed_list_bullet.gif [new file with mode: 0644]
mailboxes/xinha/images/ed_list_num.gif [new file with mode: 0644]
mailboxes/xinha/images/ed_overwrite.gif [new file with mode: 0644]
mailboxes/xinha/images/ed_paste.gif [new file with mode: 0644]
mailboxes/xinha/images/ed_print.gif [new file with mode: 0644]
mailboxes/xinha/images/ed_redo.gif [new file with mode: 0644]
mailboxes/xinha/images/ed_right_to_left.gif [new file with mode: 0644]
mailboxes/xinha/images/ed_rmformat.gif [new file with mode: 0644]
mailboxes/xinha/images/ed_save.gif [new file with mode: 0644]
mailboxes/xinha/images/ed_save.png [new file with mode: 0644]
mailboxes/xinha/images/ed_saveas.gif [new file with mode: 0644]
mailboxes/xinha/images/ed_selectall.gif [new file with mode: 0644]
mailboxes/xinha/images/ed_show_border.gif [new file with mode: 0644]
mailboxes/xinha/images/ed_splitblock.gif [new file with mode: 0644]
mailboxes/xinha/images/ed_splitcel.gif [new file with mode: 0644]
mailboxes/xinha/images/ed_undo.gif [new file with mode: 0644]
mailboxes/xinha/images/ed_word_cleaner.gif [new file with mode: 0644]
mailboxes/xinha/images/fr/bold.gif [new file with mode: 0644]
mailboxes/xinha/images/fr/strikethrough.gif [new file with mode: 0644]
mailboxes/xinha/images/fr/underline.gif [new file with mode: 0644]
mailboxes/xinha/images/fullscreen_maximize.gif [new file with mode: 0644]
mailboxes/xinha/images/fullscreen_minimize.gif [new file with mode: 0644]
mailboxes/xinha/images/insert_table.gif [new file with mode: 0644]
mailboxes/xinha/images/insertfilelink.gif [new file with mode: 0644]
mailboxes/xinha/images/insertmacro.png [new file with mode: 0644]
mailboxes/xinha/images/tidy.gif [new file with mode: 0644]
mailboxes/xinha/images/toggle_borders.gif [new file with mode: 0644]
mailboxes/xinha/images/xinha_logo.gif [new file with mode: 0644]
mailboxes/xinha/lang/b5.js [new file with mode: 0644]
mailboxes/xinha/lang/ch.js [new file with mode: 0644]
mailboxes/xinha/lang/cz.js [new file with mode: 0644]
mailboxes/xinha/lang/da.js [new file with mode: 0644]
mailboxes/xinha/lang/de.js [new file with mode: 0644]
mailboxes/xinha/lang/ee.js [new file with mode: 0644]
mailboxes/xinha/lang/el.js [new file with mode: 0644]
mailboxes/xinha/lang/es.js [new file with mode: 0644]
mailboxes/xinha/lang/fa.js [new file with mode: 0644]
mailboxes/xinha/lang/fi.js [new file with mode: 0644]
mailboxes/xinha/lang/fr.js [new file with mode: 0644]
mailboxes/xinha/lang/gb.js [new file with mode: 0644]
mailboxes/xinha/lang/he.js [new file with mode: 0644]
mailboxes/xinha/lang/hu.js [new file with mode: 0644]
mailboxes/xinha/lang/it.js [new file with mode: 0644]
mailboxes/xinha/lang/ja.js [new file with mode: 0644]
mailboxes/xinha/lang/lt.js [new file with mode: 0644]
mailboxes/xinha/lang/lv.js [new file with mode: 0644]
mailboxes/xinha/lang/nb.js [new file with mode: 0644]
mailboxes/xinha/lang/nl.js [new file with mode: 0644]
mailboxes/xinha/lang/pl.js [new file with mode: 0644]
mailboxes/xinha/lang/pt_br.js [new file with mode: 0644]
mailboxes/xinha/lang/ro.js [new file with mode: 0644]
mailboxes/xinha/lang/ru.js [new file with mode: 0644]
mailboxes/xinha/lang/sh.js [new file with mode: 0644]
mailboxes/xinha/lang/si.js [new file with mode: 0644]
mailboxes/xinha/lang/sr.js [new file with mode: 0644]
mailboxes/xinha/lang/sv.js [new file with mode: 0644]
mailboxes/xinha/lang/vn.js [new file with mode: 0644]
mailboxes/xinha/license.txt [new file with mode: 0644]
mailboxes/xinha/modules/ColorPicker/ColorPicker.js [new file with mode: 0644]
mailboxes/xinha/modules/CreateLink/link.html [new file with mode: 0644]
mailboxes/xinha/modules/CreateLink/link.js [new file with mode: 0644]
mailboxes/xinha/modules/Dialogs/dialog.js [new file with mode: 0644]
mailboxes/xinha/modules/Dialogs/inline-dialog.js [new file with mode: 0644]
mailboxes/xinha/modules/Dialogs/panel-dialog.js [new file with mode: 0644]
mailboxes/xinha/modules/Dialogs/popupwin.js [new file with mode: 0644]
mailboxes/xinha/modules/FullScreen/full-screen.js [new file with mode: 0644]
mailboxes/xinha/modules/FullScreen/lang/de.js [new file with mode: 0644]
mailboxes/xinha/modules/FullScreen/lang/fr.js [new file with mode: 0644]
mailboxes/xinha/modules/FullScreen/lang/ja.js [new file with mode: 0644]
mailboxes/xinha/modules/FullScreen/lang/nb.js [new file with mode: 0644]
mailboxes/xinha/modules/FullScreen/lang/pl.js [new file with mode: 0644]
mailboxes/xinha/modules/FullScreen/lang/ru.js [new file with mode: 0644]
mailboxes/xinha/modules/FullScreen/lang/sv.js [new file with mode: 0644]
mailboxes/xinha/modules/Gecko/Gecko.js [new file with mode: 0644]
mailboxes/xinha/modules/Gecko/paraHandlerBest.js [new file with mode: 0644]
mailboxes/xinha/modules/Gecko/paraHandlerDirty.js [new file with mode: 0644]
mailboxes/xinha/modules/GetHtml/DOMwalk.js [new file with mode: 0644]
mailboxes/xinha/modules/GetHtml/TransformInnerHTML.js [new file with mode: 0644]
mailboxes/xinha/modules/InsertImage/insert_image.html [new file with mode: 0644]
mailboxes/xinha/modules/InsertImage/insert_image.js [new file with mode: 0644]
mailboxes/xinha/modules/InsertTable/insert_table.html [new file with mode: 0644]
mailboxes/xinha/modules/InsertTable/insert_table.js [new file with mode: 0644]
mailboxes/xinha/modules/InternetExplorer/InternetExplorer.js [new file with mode: 0644]
mailboxes/xinha/popups/about.html [new file with mode: 0644]
mailboxes/xinha/popups/blank.html [new file with mode: 0644]
mailboxes/xinha/popups/editor_help.html [new file with mode: 0644]
mailboxes/xinha/popups/popup.css [new file with mode: 0644]
mailboxes/xinha/popups/popup.js [new file with mode: 0644]
mailboxes/xinha/popups/select_color.html [new file with mode: 0644]
mailboxes/xinha/release-notes.txt [new file with mode: 0644]

diff --git a/file/BorderPanel.java b/file/BorderPanel.java
new file mode 100755 (executable)
index 0000000..67306e5
--- /dev/null
@@ -0,0 +1,67 @@
+import java.awt.*;
+
+class BorderPanel extends Panel
+{
+       int border = 5; // size of border
+       Color col1 = Util.light_edge;
+       Color col2 = Util.dark_edge;
+       Color body;
+
+       BorderPanel()
+       {
+       }
+
+       BorderPanel(int w)
+       {
+       border = w;
+       }
+
+       BorderPanel(int w, Color cb)
+       {
+       border = w;
+       body = cb;
+       }
+
+       BorderPanel(int w, Color c1, Color c2)
+       {       
+       border = w;
+       col1 = c1; col2 = c2;
+       }
+
+       BorderPanel(int w, Color c1, Color c2, Color cb)
+       {
+       border = w;
+       col1 = c1; col2 = c2; body = cb;
+       }
+
+       BorderPanel(Color c1, Color c2)
+       {
+       col1 = c1; col2 = c2;
+       }
+
+       public Insets insets()
+       {
+       return new Insets(border+2, border+2, border+2, border+2);
+       }
+
+       public void paint(Graphics g)
+       {
+       if (body != null) {
+               g.setColor(body);
+               g.fillRect(0, 0, size().width, size().height);
+               }
+       super.paint(g);
+       int w = size().width-1, h = size().height-1;
+       g.setColor(col1);
+       for(int i=0; i<border; i++) {
+               g.drawLine(i,i,w-i,i);
+               g.drawLine(i,i,i,h-i);
+               }
+       g.setColor(col2);
+       for(int i=0; i<border; i++) {
+               g.drawLine(w-i,h-i, w-i,i);
+               g.drawLine(w-i,h-i, i,h-i);
+               }
+       }
+}
+
diff --git a/file/CHANGELOG b/file/CHANGELOG
new file mode 100644 (file)
index 0000000..9d5bfb5
--- /dev/null
@@ -0,0 +1,33 @@
+---- Changes since 1.130 ----
+An uploaded zip, tar or tar.gz file can be extracted in the directory it was uploaded to.
+Directories can now be downloaded as zip, tar or tar.gz files
+Added improved access control options to hide buttons.
+Added a chroot access control option to hide all directories above it.
+---- Changes since 1.190 ----
+Added checkbox for saving text files in DOS mode.
+Users can now be prevented from accessing certain directories by a new option on the access control page.
+---- Changes since 1.240 ----
+When searching for files, you can now search by their contents too.
+Added a Preview button, for viewing a scaled-down version of a GIF, JPEG or PNG image.
+---- Changes since 1.250 ----
+Absolute and relative paths like /tmp/foo and bar/foo can be used when renaming a file.
+The GD Perl module will be used for scaling preview images, if installed.
+---- Changes since 1.260 ----
+Added a button for editing HTML files, using a rich-text editor.
+---- Changes since 1.270 ----
+Added Module Config options for changing the font size for buttons and regular text.
+---- Changes since 1.290 ----
+Added the ability to extract tar.bz2 files.
+Added a History button next to the field for entering a directory to show, for quickly navigating to recently entered paths.
+Added text fields to the file info window showing the total size, number of sub-files and number of sub-directories in a directory. These are only populated when a new 'Get Size' button is clicked.
+---- Changes since 1.300 ----
+Added ACL options to prevent users from editing file permissions or ownership in the Info window, to stop filesystem mount points from being shown, and to restrict file contents searches.
+Added Extract button for un-compressing tar, tgz, zip and gz archives on the server.
+Added a Download button to the search results window, for downloading a selected matching file.
+---- Changes since 1.310 ----
+Added a popup progress window to track large uploads.
+---- Changes since 1.320 ----
+Removed the HTML editing button, and changed the Edit button to detect HTML files and launch the HTML editor instead (optional on the Module Config page).
+Added a button for creating a new HTML file.
+---- Changes since 1.330 ----
+Replaced the HTMLarea widget for editing .html files with Xinha.
diff --git a/file/CbButton.java b/file/CbButton.java
new file mode 100644 (file)
index 0000000..9770751
--- /dev/null
@@ -0,0 +1,264 @@
+import java.awt.*;
+import java.util.*;
+
+public class CbButton extends Canvas
+{
+       public static final int LEFT = 0;
+       public static final int RIGHT = 1;
+       public static final int ABOVE = 2;
+       public static final int BELOW = 3;
+
+       Image image;
+       String string;
+       CbButtonCallback callback;
+       int imode;
+       int iwidth, iheight, pwidth, pheight, twidth, theight;
+       boolean inside, indent;
+
+       CbButtonGroup group;
+       boolean selected;
+
+       Color lc1 = Util.light_edge, lc2 = Util.body, lc3 = Util.dark_edge;
+       Color hc1 = Util.light_edge_hi, hc2 = Util.body_hi, hc3 = Util.dark_edge_hi;
+
+       public CbButton(Image i, CbButtonCallback cb)
+       {
+       this(i, null, LEFT, cb);
+       }
+
+       public CbButton(String s, CbButtonCallback cb)
+       {
+       this(null, s, LEFT, cb);
+       }
+
+       public CbButton(Image i, String s, int im, CbButtonCallback cb)
+       {
+       image = i;
+       string = s;
+       imode = im;
+       callback = cb;
+       if (image != null) {
+               iwidth = Util.getWidth(image);
+               iheight = Util.getHeight(image);
+               }
+       if (string != null) {
+               twidth = Util.fnm.stringWidth(string);
+               theight = Util.fnm.getHeight();
+               }
+       if (image != null && string != null) {
+               switch(imode) {
+               case LEFT:
+               case RIGHT:
+                       pwidth = iwidth + twidth + 6;
+                       pheight = Math.max(iheight , theight) + 4;
+                       break;
+               case ABOVE:
+               case BELOW:
+                       pwidth = Math.max(iwidth, twidth) + 4;
+                       pheight = iheight + theight + 6;
+                       break;
+                       }
+               }
+       else if (image != null) {
+               pwidth = iwidth + 4;
+               pheight = iheight + 4;
+               }
+       else if (string != null) {
+               pwidth = twidth + 8;
+               pheight = theight + 8;
+               }
+       }
+
+       /**Make this button part of a mutual-exclusion group. Only one such
+        * button can be indented at a time
+        */
+       public void setGroup(CbButtonGroup g)
+       {
+       group = g;
+       group.add(this);
+       }
+
+       /**Make this button the selected one in it's group
+        */
+       public void select()
+       {
+       if (group != null)
+               group.select(this);
+       }
+
+       /**Display the given string
+        */
+       public void setText(String s)
+       {
+       string = s;
+       image = null;
+       twidth = Util.fnm.stringWidth(string);
+       theight = Util.fnm.getHeight();
+       repaint();
+       }
+
+       /**Display the given image
+        */
+       public void setImage(Image i)
+       {
+       string = null;
+       image = i;
+       iwidth = Util.getWidth(image);
+       iheight = Util.getHeight(image);
+       repaint();
+       }
+
+       /**Display the given image and text, with the given alignment mode
+        */
+       public void setImageText(Image i, String s, int m)
+       {
+       image = i;
+       string = s;
+       imode = m;
+       twidth = Util.fnm.stringWidth(string);
+       theight = Util.fnm.getHeight();
+       iwidth = Util.getWidth(image);
+       iheight = Util.getHeight(image);
+       repaint();
+       }
+
+       public void paint(Graphics g)
+       {
+       Color c1 = inside ? hc1 : lc1,
+             c2 = inside ? hc2 : lc2,
+             c3 = inside ? hc3 : lc3;
+       int w = size().width, h = size().height;
+       Color hi = indent||selected ? c3 : c1,
+             lo = indent||selected ? c1 : c3;
+       g.setColor(c2);
+       g.fillRect(0, 0, w-1, h-1);
+       g.setColor(hi);
+       g.drawLine(0, 0, w-2, 0);
+       g.drawLine(0, 0, 0, h-2);
+       g.setColor(lo);
+       g.drawLine(w-1, h-1, w-1, 1);
+       g.drawLine(w-1, h-1, 1, h-1);
+       if (inside) {
+               /* g.setColor(hi);
+               g.drawLine(1, 1, w-3, 1);
+               g.drawLine(1, 1, 1, h-3); */
+               g.setColor(lo);
+               g.drawLine(w-2, h-2, w-2, 2);
+               g.drawLine(w-2, h-2, 2, h-2);
+               }
+
+       g.setColor(c3);
+       g.setFont(Util.f);
+       if (image != null && string != null) {
+               if (imode == LEFT) {
+                       Dimension is = imgSize(w-twidth-6, h-4);
+                       g.drawImage(image, (w - is.width - twidth - 2)/2,
+                                   (h-is.height)/2, is.width, is.height, this);
+                       g.drawString(string,
+                                    (w - is.width - twidth - 2)/2 +is.width +2,
+                                    (h + theight - Util.fnm.getDescent())/2);
+                       }
+               else if (imode == RIGHT) {
+                       }
+               else if (imode == ABOVE) {
+                       //Dimension is = imgSize(w-4, h-theight-6);
+                       g.drawImage(image, (w - iwidth)/2, 
+                                   (h - iheight - theight - 2)/2,
+                                   iwidth, iheight, this);
+                       g.drawString(string, (w - twidth)/2, iheight+Util.fnm.getHeight()+2);
+                       }
+               else if (imode == BELOW) {
+                       }
+               }
+       else if (image != null) {
+               Dimension is = imgSize(w-4, h-4);
+               g.drawImage(image, (w - is.width)/2, (h-is.height)/2,
+                           is.width, is.height, this);
+               }
+       else if (string != null) {
+               g.drawString(string, (w - twidth)/2,
+                                    (h+theight-Util.fnm.getDescent())/2);
+               }
+       }
+
+       public void update(Graphics g) { paint(g); }
+
+       public boolean mouseEnter(Event e, int x, int y)
+       {
+       inside = true;
+       repaint();
+       return true;
+       }
+
+       public boolean mouseExit(Event e, int x, int y)
+       {
+       inside = false;
+       repaint();
+       return true;
+       }
+
+       public boolean mouseDown(Event e, int x, int y)
+       {
+       indent = true;
+       repaint();
+       return true;
+       }
+
+       public boolean mouseUp(Event e, int x, int y)
+       {
+       if (x >= 0 && y >= 0 && x < size().width && y < size().height) {
+               if (callback != null)
+                       callback.click(this);
+               select();
+               }
+       indent = false;
+       repaint();
+       return true;
+       }
+
+       public Dimension preferredSize()
+       {
+       return new Dimension(pwidth, pheight);
+       }
+
+       public Dimension minimumSize()
+       {
+       return preferredSize();
+       }
+
+       private Dimension imgSize(int mw, int mh)
+       {
+       float ws = (float)mw/(float)iwidth,
+             hs = (float)mh/(float)iheight;
+       float s = ws < hs ? ws : hs;
+       if (s > 1) s = 1;
+       return new Dimension((int)(iwidth*s), (int)(iheight*s));
+       }
+}
+
+
+interface CbButtonCallback
+{
+       void click(CbButton b);
+}
+
+
+class CbButtonGroup
+{
+       Vector buttons = new Vector();
+
+       void add(CbButton b)
+       {
+       buttons.addElement(b);
+       }
+
+       void select(CbButton b)
+       {
+       for(int i=0; i<buttons.size(); i++) {
+               CbButton but = (CbButton)buttons.elementAt(i);
+               but.selected = (b == but);
+               but.repaint();
+               }
+       }
+}
+
diff --git a/file/CbColorButton.java b/file/CbColorButton.java
new file mode 100644 (file)
index 0000000..f6fc10f
--- /dev/null
@@ -0,0 +1,51 @@
+import java.awt.*;
+import java.util.*;
+
+/**A component for choosing a color
+ */
+public class CbColorButton extends Panel implements CbButtonCallback,
+                                                    CbColorWindowCallback
+{
+       Color col;
+       CbButton but;
+       Vector pal;
+       Image swatch = Util.createImage(32, 16);
+       Graphics g = swatch.getGraphics();
+       CbColorWindow win;
+
+       CbColorButton(Color c)
+       {
+       this(c, new Vector());
+       }
+
+       CbColorButton(Color c, Vector p)
+       {
+       if (c == null) c = Color.black;
+       col = c;
+       g.setColor(col); g.fillRect(0, 0, 32, 16);
+       setLayout(new BorderLayout());
+       add("Center", but = new CbButton(swatch, this));
+       }
+
+       public void click(CbButton b)
+       {
+       if (win == null)
+               win = new CbColorWindow(col, this);
+       }
+
+       public void chosen(CbColorWindow w, Color c)
+       {
+       if (c != null) {
+               col = c;
+               g.setColor(col); g.fillRect(0, 0, 32, 16);
+               but.repaint();
+               }
+       win = null;
+       }
+
+       public Vector palette(CbColorWindow w)
+       {
+       return pal;
+       }
+}
+
diff --git a/file/CbColorWindow.java b/file/CbColorWindow.java
new file mode 100644 (file)
index 0000000..4e4dedf
--- /dev/null
@@ -0,0 +1,226 @@
+import java.awt.*;
+import java.util.*;
+
+/**A window for choosing a colour, either from a pre-set palette
+ * or from a color cube
+ */
+class CbColorWindow extends FixedFrame implements CbButtonCallback
+{
+       CbColorWindowCallback callback;
+       Color col;
+       Vector pal;
+       static Vector defpal = new Vector();
+       Image palimg[] = new Image[12];
+       CbButton palbut[] = new CbButton[12];
+       int curpal = -1;
+       CbButton ok, cancel;
+       CbColorWindowCube ccube;
+
+       static
+       {
+       defpal.addElement(Color.black);
+       defpal.addElement(Color.blue);
+       defpal.addElement(Color.cyan);
+       defpal.addElement(Color.gray);
+       defpal.addElement(Color.green);
+       defpal.addElement(Color.darkGray);
+       defpal.addElement(Color.magenta);
+       defpal.addElement(Color.orange);
+       defpal.addElement(Color.pink);
+       defpal.addElement(Color.red);
+       defpal.addElement(Color.white);
+       defpal.addElement(Color.yellow);
+       }
+
+       CbColorWindow(Color c, CbColorWindowCallback cb)
+       {
+       col = c;
+       callback = cb;
+
+       // Setup color vector
+       pal = callback.palette(this);
+       if (pal == null)
+               pal = defpal;
+       else if (pal.size() == 0)
+               for(int i=0; i<12; i++)
+                       pal.addElement(defpal.elementAt(i));
+
+       // Create palette images
+       for(int i=0; i<12; i++) {
+               palimg[i] = Util.createImage(16, 16);
+               updatePal(i);
+               }
+
+       // create UI
+       setLayout(new BorderLayout());
+       Panel bot = new GrayPanel();
+       bot.setLayout(new FlowLayout(FlowLayout.RIGHT));
+       bot.add(ok = new CbButton("Ok", this));
+       bot.add(cancel = new CbButton("Cancel", this));
+       add("South", bot);
+       Panel mid = new BorderPanel(1);
+       mid.setLayout(new BorderLayout());
+       Panel midbot = new GrayPanel();
+       midbot.setLayout(new GridLayout(2, 6, 4, 4));
+       CbButtonGroup g = new CbButtonGroup();
+       for(int i=0; i<12; i++) {
+               midbot.add(palbut[i] = new CbButton(palimg[i], this));
+               palbut[i].setGroup(g);
+               }
+       for(int i=0; i<12; i++)
+               if (c.equals(pal.elementAt(i))) {
+                       curpal = i;
+                       palbut[i].select();
+                       break;
+                       }
+       mid.add("South", midbot);
+       mid.add("North", ccube = new CbColorWindowCube(this));
+       add("Center", mid);
+
+       pack();
+       show();
+       setTitle("Choose Color...");
+       }
+
+       void updatePal(int i)
+       {
+       Graphics g = palimg[i].getGraphics();
+       g.setColor((Color)pal.elementAt(i));
+       g.fillRect(0, 0, 16, 16);
+       if (palbut[i] != null) palbut[i].repaint();
+       }
+
+       public void click(CbButton b)
+       {
+       if (b == ok) {
+               callback.chosen(this, col);
+               super.dispose();
+               }
+       else if (b == cancel)
+               dispose();
+       else {
+               for(int i=0; i<12; i++)
+                       if (b == palbut[i]) {
+                               curpal = i;
+                               col = (Color)pal.elementAt(i);
+                               ccube.red.setPosition(col.getRed());
+                               ccube.blue.setPosition(col.getBlue());
+                               ccube.green.setPosition(col.getGreen());
+                               ccube.swatch.setColor(col);
+                               }
+               }
+       }
+
+       public void dispose()
+       {
+       super.dispose();
+       callback.chosen(this, null);
+       }
+
+       public boolean isResizable() { return false; }
+}
+
+/**Displays 3 sliders, for red green and blue plus a block to show the
+ * current color
+ */
+class CbColorWindowCube extends BorderPanel implements CbSliderCallback
+{
+       CbColorWindow parent;
+       CbSlider red, green, blue;
+       CbColorWindowSwatch swatch;
+
+       CbColorWindowCube(CbColorWindow p)
+       {
+       super(1, Util.body, Util.body);
+       parent = p;
+       setLayout(new BorderLayout());
+       Panel sl = new GrayPanel();
+       sl.setLayout(new GridLayout(3, 1));
+       sl.add(red = new CbSlider(0, 0, 255, p.col.getRed(), this));
+       sl.add(green = new CbSlider(0, 0, 255, p.col.getBlue(), this));
+       sl.add(blue = new CbSlider(0, 0, 255, p.col.getGreen(), this));
+       add("Center", sl);
+       add("East", swatch = new CbColorWindowSwatch(p.col));
+       }
+
+       public void moved(CbSlider s, int p)
+       {
+       moving(s, p);
+       }
+
+       public void moving(CbSlider s, int p)
+       {
+       parent.col = new Color(red.getPosition(), green.getPosition(),
+                              blue.getPosition());
+       swatch.setColor(parent.col);
+       if (parent.curpal != -1) {
+               parent.pal.setElementAt(parent.col, parent.curpal);
+               parent.updatePal(parent.curpal);
+               }
+       }
+}
+
+
+interface CbColorWindowCallback
+{
+       /**This method will be called when the user chooses a colour. If
+        * the user cancels the dialog, then this method will also be chosen
+        * but with null for the color.
+        */
+       public void chosen(CbColorWindow w, Color c);
+
+       /**The chooser keeps a palette of colors that the user can modify,
+        * stored in a vector. The callback class should provide this vector
+        * so as to maintain the palette between color window calls.
+        * If an empty vector is returned, it will be filled with the default
+        * color table (which can be then modified).
+        * If null is returned, the chooser will use it's own internal
+        * vector.
+        */
+       public Vector palette(CbColorWindow w);
+}
+
+
+class CbColorWindowSwatch extends BorderPanel
+{
+       Color col = Color.black;
+       String txt;
+
+       CbColorWindowSwatch(Color c)
+       {
+       super(1);
+       setColor(c);
+       }
+
+       void setColor(Color c)
+       {
+       col = c;
+       txt = col.getRed()+","+col.getGreen()+","+col.getBlue();
+       repaint();
+       }
+
+       public void paint(Graphics g)
+       {
+       super.paint(g);
+       g.setColor(col);
+       g.fillRect(1, 1, size().width-2, size().height-2);
+       g.setColor(Color.white);
+       g.setXORMode(Color.black);
+       g.setFont(Util.f);
+       g.drawString(txt, 3, Util.fnm.getHeight()+1);
+       g.setPaintMode();
+       }
+
+       public void upate(Graphics g) { paint(g); }
+
+       public Dimension preferredSize()
+       {
+       return new Dimension(60, 60);
+       }
+
+       public Dimension minimumSize()
+       {
+       return preferredSize();
+       }
+}
+
diff --git a/file/CbImageChooser.java b/file/CbImageChooser.java
new file mode 100644 (file)
index 0000000..e5cc142
--- /dev/null
@@ -0,0 +1,233 @@
+import java.awt.*;
+import java.net.*;
+
+class CbImageChooser extends Panel implements CbButtonCallback
+{
+       Image img;
+       String imgsrc;
+       int imgw, imgh;
+       CbButton but;
+       CbImageFileWindow filewin;
+       //CbImageChooserCallback callback;
+
+       CbImageChooser(Image i)
+       {
+       this(i, null);
+       }
+
+       CbImageChooser(Image i, String s)
+       {
+       setLayout(new BorderLayout());
+       add("Center", but = new CbButton("Choose..", this));
+       setImage(i, s==null ? "" : s);
+       }
+
+       void setImage(Image i, String s)
+       {
+       img = i;
+       imgsrc = s;
+       if (img != null) but.setImage(img);
+       else but.setText("Choose..");
+       }
+
+       public void click(CbButton b)
+       {
+       if (b == but && filewin == null)
+               new CbImageFileWindow(this);
+       }
+}
+
+
+class CbImageFileWindow extends FixedFrame implements CbButtonCallback
+{
+       CbImageChooser parent;
+       ScrollImage imgp;
+       TextField url;
+       CbButton browse, ok, cancel;
+       FileDialog filedlog;
+       String lastfile = "";
+
+       CbImageFileWindow(CbImageChooser p)
+       {
+       parent = p;
+       parent.filewin = this;
+       setLayout(new BorderLayout());
+       add("Center", imgp = new ScrollImage(parent.img, 200, 200));
+       Panel bot = new GrayPanel();
+       bot.setLayout(new FlowLayout(FlowLayout.LEFT));
+       bot.add(new Label("URL:"));
+       bot.add(url = new TextField(parent.imgsrc, 20));
+       bot.add(browse = new CbButton("Browse..", this));
+       bot.add(new Label("  "));
+       bot.add(ok = new CbButton("Ok", this));
+       bot.add(cancel = new CbButton("Cancel", this));
+       add("South", bot);
+
+       pack();
+       show();
+       setTitle("Choose Image..");
+       Util.recursiveBackground(this, Util.body);
+       }
+
+       public void click(CbButton b)
+       {
+       if (b == ok)
+               parent.setImage(imgp.img, lastfile);
+       if (b == ok || b == cancel)
+               dispose();
+       else if (b == browse) {
+               // Open file chooser here!
+               FileDialog filedlog =
+                 new FileDialog(this, "Choose Image",FileDialog.LOAD);
+               filedlog.show();
+               if (filedlog.getFile() != null) {
+                       // file chosen.. load it in
+                       String fn = filedlog.getDirectory()+filedlog.getFile();
+                       url.setText(fn);
+                       loadFile(fn);
+                       }
+               }
+       }
+
+       public void dispose()
+       {
+       super.dispose();
+       parent.filewin = null;
+       }
+
+       public boolean action(Event evt, Object obj)
+       {
+       if (evt.target == url) {
+               String ut = url.getText();
+               if (ut.startsWith("http:") || ut.startsWith("ftp:"))
+                       loadURL(ut);
+               else
+                       loadFile(ut);
+               return true;
+               }
+       return false;
+       }
+
+       private void loadFile(String f)
+       {
+       Image i = Util.tk.getImage(f);
+       if (i == null || !Util.waitForImage(i))
+               new ErrorWindow("Failed to load image "+f);
+       else {
+               imgp.setImage(i);
+               lastfile = f;
+               }
+       }
+
+       private void loadURL(String u)
+       {
+       try {
+               Image i = Util.tk.getImage(new URL(u));
+               if (i == null || !Util.waitForImage(i))
+                       new ErrorWindow("Failed to load image from "+u);
+               else {
+                       imgp.setImage(i);
+                       lastfile = u;
+                       }
+               }
+       catch(MalformedURLException e) {
+               new ErrorWindow(u+" is not a valid URL");
+               }
+       }
+}
+
+
+class ScrollImage extends Panel implements CbScrollbarCallback
+{
+       Image img;
+       int imgw, imgh;
+       int pw, ph;
+       CbScrollbar vsc, hsc;
+       boolean compute_scrollbars = true;
+
+       ScrollImage(Image i)
+       {
+       this(i, Util.getWidth(i), Util.getHeight(i));
+       }
+
+       ScrollImage(Image i, int w, int h)
+       {
+       pw = w; ph = h;
+       setLayout(new BorderLayout());
+       add("East", vsc = new CbScrollbar(CbScrollbar.VERTICAL, this));
+       add("South", hsc = new CbScrollbar(CbScrollbar.HORIZONTAL, this));
+       setImage(i);
+       }
+
+       void setImage(Image i)
+       {
+       img = i;
+       if (img != null) {
+               imgw = Util.getWidth(img);
+               imgh = Util.getHeight(img);
+               }
+       compute_scrollbars = true;
+       repaint();
+       }
+
+       public void paint(Graphics g)
+       {
+       int w = size().width-vsc.size().width,
+           h = size().height-hsc.size().height;
+       if (compute_scrollbars) {
+               if (img == null) {
+                       hsc.setValues(0, 1, 1);
+                       vsc.setValues(0, 1, 1);
+                       }
+               else {
+                       if (imgw < w) hsc.setValues(0, 1, 1);
+                       else hsc.setValues(0, w, imgw);
+                       if (imgh < h) vsc.setValues(0, 1, 1);
+                       else vsc.setValues(0, h, imgh);
+                       }
+               compute_scrollbars = false;
+               }
+
+       g.setColor(Util.body);
+       g.fillRect(0, 0, w, h);
+       if (img != null) {
+               if (imgw < w && imgh < h)
+                       g.drawImage(img, (w-imgw)/2, (h-imgh)/2, this);
+               else
+                       g.drawImage(img, -hsc.getValue(), -vsc.getValue(),this);
+               }
+       else {
+               g.setFont(Util.f);
+               g.setColor(Util.text);
+               String s = "<None>";
+               g.drawString(s, (w-Util.fnm.stringWidth(s))/2,
+                               (h-Util.fnm.getHeight())/2);
+               }
+       }
+
+       public void update(Graphics g) { paint(g); }
+
+       public void reshape(int nx, int ny, int nw, int nh)
+       {
+       super.reshape(nx, ny, nw, nh);
+       compute_scrollbars = true;
+       repaint();
+       }
+
+       public void moved(CbScrollbar s, int p)
+       {
+       repaint();
+       }
+
+       public void moving(CbScrollbar s, int p) { }
+
+       public Dimension minimumSize()
+       {
+       return new Dimension(pw, ph);
+       }
+
+       public Dimension preferredSize()
+       {
+       return minimumSize();
+       }
+}
diff --git a/file/CbScrollbar.java b/file/CbScrollbar.java
new file mode 100644 (file)
index 0000000..57bc886
--- /dev/null
@@ -0,0 +1,345 @@
+// CbScrollbar.java
+// A drop-in replacement for the AWT scrollbar class, with callbacks
+// and a nicer look. This scrollbar is typically used to display some
+// fraction of a list of items, with values ranging from min to max.
+// The lvisible parameter determines how many of the list are lvisible
+// at any one time. The value of the scrollbar ranges from min to
+// max-lvisible+1 (the highest position in the list to start displaying)
+import java.awt.*;
+
+public class CbScrollbar extends Panel
+{
+       static final int VERTICAL = 0;
+       static final int HORIZONTAL = 1;
+       CbScrollbarCallback callback;           // who to call back to
+       boolean inside, indent;
+       int orient;                     // horizontal or vertical?
+       int value;                      // position
+       int lvisible;                   // the number of lines lvisible
+       int num;                        // total number of lines
+       int lineinc = 1;                // how much the arrow buttons move by
+       Color lc1 = Util.light_edge, lc2 = Util.body, lc3 = Util.dark_edge;
+       Color hc1 = Util.light_edge_hi, hc2 = Util.body_hi, hc3 = Util.dark_edge_hi;
+       Color bc = Util.dark_bg;
+       int y1, y2, x1, x2, drag;
+
+       CbScrollbarArrow arrow1, arrow2;
+
+       CbScrollbar(int o, CbScrollbarCallback cb)
+       {
+       this(o, 0, 1, 1, cb);
+       }
+
+       /**Create a new scrollbar
+        */
+       CbScrollbar(int o, int v, int vis, int n, CbScrollbarCallback cb)
+       {
+       setValues(v, vis, n);
+       orient = o;
+       callback = cb;
+       setLayout(null);
+       if (orient == VERTICAL) {
+               add(arrow1 = new CbScrollbarArrow(this, 0));
+               add(arrow2 = new CbScrollbarArrow(this, 1));
+               }
+       else {
+               add(arrow1 = new CbScrollbarArrow(this, 2));
+               add(arrow2 = new CbScrollbarArrow(this, 3));
+               }
+       }
+
+       /**Set the current scrollbar parameters
+        * @param v             Current position
+        * @param vis           Number of lines lvisible
+        * @param n             Total number of lines
+        */
+       public void setValues(int v, int vis, int n)
+       {
+       value = v;
+       lvisible = vis;
+       num = n;
+       if (lvisible > num) lvisible = num;
+       checkValue();
+       repaint();
+       }
+
+       public int getValue() { return value; }
+
+       public void setValue(int v)
+       {
+       value = v;
+       checkValue();
+       repaint();
+       }
+
+       private void checkValue()
+       {
+       if (value < 0) value = 0;
+       else if (value > num-lvisible) value = num-lvisible;
+       }
+
+       public void paint(Graphics g)
+       {
+       if (num == 0) return;
+       int w = size().width, h = size().height;
+       boolean ins = inside && !(arrow1.inside || arrow2.inside);
+       Color c1 = ins ? hc1 : lc1, c2 = ins ? hc2 : lc2,
+             c3 = ins ? hc3 : lc3;
+       g.setColor(bc);
+       g.fillRect(0, 0, w, h);
+       g.setColor(c3);
+       g.drawLine(0, 0, w-1, 0); g.drawLine(0, 0, 0, h-1);
+       g.setColor(c1);
+       g.drawLine(w-1, h-1, w-1, 0); g.drawLine(w-1, h-1, 0, h-1);
+
+       if (orient == VERTICAL) {
+               int va = h-w*2;
+               y1 = w+va*value/num;
+               y2 = w+va*(value+lvisible)/num-1;
+               g.setColor(c2);
+               g.fillRect(1, y1, w-2, y2-y1);
+               g.setColor(indent ? c3 : c1);
+               g.drawLine(1, y1, w-2, y1);
+               g.drawLine(1, y1, 1, y2-1);
+               g.setColor(indent ? c1 : c3);
+               g.drawLine(w-2, y2-1, w-2, y1);
+               g.drawLine(w-2, y2-1, 1, y2-1);
+               if (ins) {
+                       g.drawLine(w-3, y2-2, w-3, y1+1);
+                       g.drawLine(w-3, y2-2, 2, y2-2);
+                       }
+               }
+       else if (orient == HORIZONTAL) {
+               int va = w-h*2;
+               x1 = h+va*value/num;
+               x2 = h+va*(value+lvisible)/num-1;
+               g.setColor(c2);
+               g.fillRect(x1, 1, x2-x1, h-2);
+               g.setColor(indent ? c3 : c1);
+               g.drawLine(x1, 1, x1, h-2);
+               g.drawLine(x1, 1, x2-1, 1);
+               g.setColor(indent ? c1 : c3);
+               g.drawLine(x2-1, h-2, x1, h-2);
+               g.drawLine(x2-1, h-2, x2-1, 1);
+               if (ins) {
+                       g.drawLine(x2-2, h-3, x1+1, h-3);
+                       g.drawLine(x2-2, h-3, x2-2, 2);
+                       }
+               }
+       }
+
+       /**Called by arrows to move the slider
+        */
+       void arrowClick(int d)
+       {
+       int oldvalue = value;
+       value += d;
+       checkValue();
+       if (value != oldvalue) {
+               callback.moved(this, value);
+               repaint();
+               }
+       }
+
+       public void reshape(int nx, int ny, int nw, int nh)
+       {
+       super.reshape(nx, ny, nw, nh);
+       if (orient == VERTICAL) {
+               arrow1.reshape(1, 1, nw-2, nw-1);
+               arrow2.reshape(1, nh-nw-1, nw-2, nw-1);
+               }
+       else {
+               arrow1.reshape(1, 1, nh-1, nh-2);
+               arrow2.reshape(nw-nh-1, 1, nh-1, nh-2);
+               }
+       repaint();
+       }
+
+       public Dimension preferredSize()
+       {
+       return orient==VERTICAL ? new Dimension(16, 100)
+                               : new Dimension(100, 16);
+       }
+
+       public Dimension minimumSize()
+       {
+       return preferredSize();
+       }
+
+       public boolean mouseDown(Event e, int mx, int my)
+       {
+       if (orient == VERTICAL) {
+               // move up/down one page, or start dragging
+               if (my < y1) arrowClick(-lvisible);
+               else if (my > y2) arrowClick(lvisible);
+               else {
+                       indent = true;
+                       drag = my-y1;
+                       repaint();
+                       }
+               }
+       else {
+               // move left/right one page, or start dragging
+               if (mx < x1) arrowClick(-lvisible);
+               else if (mx > x2) arrowClick(lvisible);
+               else {
+                       indent = true;
+                       drag = mx-x1;
+                       repaint();
+                       }
+               }
+       return true;
+       }
+
+       public boolean mouseDrag(Event e, int mx, int my)
+       {
+       if (indent) {
+               int w = size().width, h = size().height;
+               int oldvalue = value;
+               if (orient == VERTICAL) {
+                       int va = h-w*2, ny = my-drag-w;
+                       value = ny*num/va;
+                       }
+               else {
+                       int va = w-h*2, nx = mx-drag-h;
+                       value = nx*num/va;
+                       }
+               checkValue();
+               if (value != oldvalue) {
+                       callback.moving(this, value);
+                       repaint();
+                       }
+               }
+       return indent;
+       }
+
+       public boolean mouseUp(Event e, int mx, int my)
+       {
+       if (indent) {
+               indent = false;
+               repaint();
+               callback.moved(this, value);
+               return true;
+               }
+       return false;
+       }
+
+/*
+       public boolean mouseEnter(Event e, int mx, int my)
+       {
+       inside = true;
+       repaint();
+       return true;
+       }
+
+       public boolean mouseExit(Event e, int mx, int my)
+       {
+       inside = false;
+       repaint();
+       return true;
+       }
+*/
+}
+
+class CbScrollbarArrow extends Canvas implements Runnable
+{
+       int mode;
+       CbScrollbar scrollbar;
+       boolean inside, indent;
+       Thread th;
+
+       CbScrollbarArrow(CbScrollbar p, int m)
+       {
+       scrollbar = p;
+       mode = m;
+       }
+
+       public void paint(Graphics g)
+       {
+       int w = size().width, h = size().height;
+       Color c1 = inside ? scrollbar.hc1 : scrollbar.lc1,
+             c2 = inside ? scrollbar.hc2 : scrollbar.lc2,
+             c3 = inside ? scrollbar.hc3 : scrollbar.lc3;
+       g.setColor(scrollbar.bc);
+       g.fillRect(0, 0, w, h);
+       int xp[] = new int[3], yp[] = new int[3];
+       // blank, dark, light
+       if (mode == 0) {
+               // up arrow
+               xp[0] = w/2; xp[1] = w-1; xp[2] = 0;
+               yp[0] = 0;   yp[1] = h-1; yp[2] = h-1;
+               }
+       else if (mode == 1) {
+               // down arrow
+               xp[0] = 0;   xp[1] = w/2; xp[2] = w-1;
+               yp[0] = 0;   yp[1] = h-1; yp[2] = 0;
+               }
+       else if (mode == 2) {
+               // left arrow
+               xp[0] = 0;   xp[1] = w-1; xp[2] = w-1; 
+               yp[0] = h/2; yp[1] = h-1; yp[2] = 0;
+               }
+       else if (mode == 3) {
+               // right arrow
+               xp[0] = 0;   xp[1] = w-1; xp[2] = 0;
+               yp[0] = 0;   yp[1] = h/2; yp[2] = h-1;
+               }
+       g.setColor(c2);
+       g.fillPolygon(xp, yp, 3);
+       g.setColor(indent ? c1 : c3);
+       g.drawLine(xp[1], yp[1], xp[2], yp[2]);
+       g.setColor(indent ? c3 : c1);
+       g.drawLine(xp[0], yp[0], xp[2], yp[2]);
+       }
+
+       public boolean mouseDown(Event e, int mx, int my)
+       {
+       indent = true;
+       repaint();
+       (th = new Thread(this)).start();
+       return true;
+       }
+
+       public boolean mouseUp(Event e, int mx, int my)
+       {
+       indent = false;
+       repaint();
+       if (th != null) th.stop();
+       return true;
+       }
+
+       /**Thread for doing repeated scrolling
+        */
+       public void run()
+       {
+       int stime = 500;
+       while(true) {
+               scrollbar.arrowClick(mode%2 == 0 ? -1 : 1);
+               try { Thread.sleep(stime); } catch(Exception e) { }
+               stime = 100;
+               }
+       }
+}
+
+
+// CbScrollbarCallback
+// Methods for reporting the movement of the scrollbar to another object
+interface CbScrollbarCallback
+{
+       /**Called when the scrollbar stops moving. This happens when an
+        * arrow is clicked, the scrollbar is moved by a page, or the user
+        * lets go of the scrollbar after dragging it.
+        * @param sb    The scrollar that has been moved
+        * @param v     The new value
+        */
+       void moved(CbScrollbar sb, int v);
+
+       /**Called upon every pixel movement of the scrollbar when it is
+        * being dragged, but NOT when moved() is called.
+        * @param sb    The scrollar that has been moved
+        * @param v     The new value
+        */
+       void moving(CbScrollbar sb, int v);
+}
+
+
diff --git a/file/CbSlider.java b/file/CbSlider.java
new file mode 100644 (file)
index 0000000..52a37d5
--- /dev/null
@@ -0,0 +1,233 @@
+import java.awt.*; 
+
+class CbSlider extends Canvas
+{
+       int dir, min, max, pos;
+       CbSliderCallback callback;
+       int px, py;
+       Color lc1 = Util.light_edge, lc2 = Util.body, lc3 = Util.dark_edge;
+       Color hc1 = Util.light_edge_hi, hc2 = Util.body_hi, hc3 = Util.dark_edge_hi;
+       int ticks = 0;
+       boolean inside = false, dragging = false;
+       int dragx;
+
+       /**Create a new slider
+        * @param d             0=horizontal, 1=vertical
+        * @param mi    Minimum value
+        * @param ma    Maximum value
+        * @param p             Current value
+        */
+       public CbSlider(int d, int mi, int ma, int p)
+       {
+       this(d, mi, ma, p, null);
+       }
+
+       /**Create a new slider
+        * @param d             0=horizontal, 1=vertical
+        * @param mi    Minimum value
+        * @param ma    Maximum value
+        * @param p             Current value
+        * @param cb    Object to call back to
+        */
+       public CbSlider(int d, int mi, int ma, int p, CbSliderCallback cb)
+       {
+       dir = d; min = mi; max = ma;
+       pos = p;
+       callback = cb;
+       }
+
+       /**Toggle drawing of tick-marks on the slider track
+        * @param t             The number of units/tick, or 0 to disable
+        */
+       public void setTicks(int t)
+       {
+       ticks = t;
+       repaint();
+       }
+
+       /**Returns the current slider position
+        */
+       public int getPosition() { return pos; }
+
+       /**Sets the current slider position
+        */
+       public void setPosition(int p)
+       {
+       if (pos != p) {
+               pos = p;
+               repaint();
+               }
+       }
+
+       /**Returns the current minimum slider value
+        */
+       public int getMinimum() { return min; }
+
+       /**Sets the minimum slider value
+        * @param mi    The new minimum
+        */
+       public void setMinimum(int mi)
+       {
+       min = mi;
+       checkPos();
+       repaint();
+       }
+
+       /**Returns the current maximum slider value
+        */
+       public int getMaximum() { return max; }
+
+       /**Sets the maximum slider value
+        * @param mx    The new maximum
+        */
+       public void setMaximum(int mx)
+       {
+       max = mx;
+       checkPos();
+       repaint();
+       }
+
+       public void paint(Graphics g)
+       {
+       Color c1 = inside ? hc1 : lc1,
+             c2 = inside ? hc2 : lc2,
+             c3 = inside ? hc3 : lc3;
+
+       // draw slider track
+       int w = size().width, h = size().height;
+       g.setColor(c2);
+       g.fillRect(0, 0, w, h);
+       g.setColor(c3);
+       g.drawLine(8, h/2, w-8, h/2);
+       g.setColor(c1);
+       g.drawLine(8, h/2+1, w-8, h/2+1);
+
+       // draw border
+       g.setColor(c1);
+       g.drawLine(0, 0, w-1, 0);
+       g.drawLine(0, 0, 0, h-1);
+       g.setColor(c3);
+       g.drawLine(w-1, h-1, w-1, 0);
+       g.drawLine(w-1, h-1, 0, h-1);
+       if (inside) {
+               g.drawLine(w-2, h-2, w-2, 0);
+               g.drawLine(w-2, h-2, 0, h-2);
+               }
+
+       // draw tick marks
+       if (ticks != 0) {
+               int mm = max-min;
+               for(int i=0; i<=mm; i+=ticks) {
+                       int tx = ((w-16)*i / mm) + 8;
+                       g.setColor(c3);
+                       g.drawLine(tx, h/2, tx, h/2-6);
+                       }
+               }
+
+       // draw slider
+       px = ((w-16)*pos / (max - min)) + 8;
+       py = h/2;
+       g.setColor(c2);
+       int xpt[] = { px-3, px-3, px, px+3, px+3 };
+       int ypt[] = { py+5, py-4, py-6, py-4, py+5 };
+       g.fillPolygon(xpt, ypt, 5);
+       g.setColor(dragging ? c3 : c1);
+       g.drawLine(px-3, py+5, px-3, py-4);
+       g.drawLine(px-3, py-4, px, py-6);
+       g.setColor(dragging ? c1 : c3);
+       g.drawLine(px-3, py+5, px+3, py+5);
+       g.drawLine(px+3, py+5, px+3, py-4);
+       }
+
+       public void update(Graphics g) { paint(g); }
+
+       public boolean mouseEnter(Event e, int x, int y)
+       {
+       inside = true;
+       repaint();
+       return true;
+       }
+
+       public boolean mouseDown(Event e, int x, int y)
+       {
+       int step = ticks==0 ? (max-min)/10 : ticks;
+       if (x < px-3) {
+               // move one tick to the left
+               pos -= step;
+               }
+       else if (x > px+3) {
+               // move one tick to the right
+               pos += step;
+               }
+       else {
+               // start dragging
+               dragging = true;
+               dragx = x-px;
+               }
+       checkPos();
+       if (callback != null)
+               callback.moved(this, pos);
+       repaint();
+       return true;
+       }
+
+       public boolean mouseDrag(Event e, int x, int y)
+       {
+       if (dragging) {
+               px = x-dragx;
+               pos = (px-8)*(max - min) / (size().width-16);
+               checkPos();
+               if (callback != null)
+                       callback.moving(this, pos);
+               repaint();
+               }
+       return dragging;
+       }
+
+       public boolean mouseUp(Event e, int x, int y)
+       {
+       if (dragging) {
+               dragging = false;
+               if (callback != null)
+                       callback.moved(this, pos);
+               repaint();
+               return true;
+               }
+       return false;
+       }
+
+       public boolean mouseExit(Event e, int x, int y)
+       {
+       inside = false;
+       repaint();
+       return true;
+       }
+
+       protected void checkPos()
+       {
+       if (pos < min) pos = min;
+       else if (pos > max) pos = max;
+       }
+
+       public Dimension preferredSize()
+       {
+       return new Dimension(100, 20);
+       }
+
+       public Dimension minimumSize() { return preferredSize(); }
+}
+
+interface CbSliderCallback
+{
+       /**Callled back when the slider stops at a new position
+        * @param s             The slider being moved
+        * @param p             New position
+        */
+       public void moved(CbSlider s, int p);
+
+       /**Callled back whenever the slider is being dragged
+        * @param s             The slider being moved
+        * @param p             New position
+        */
+       public void moving(CbSlider s, int p);
+}
diff --git a/file/ErrorWindow.java b/file/ErrorWindow.java
new file mode 100644 (file)
index 0000000..dd65546
--- /dev/null
@@ -0,0 +1,34 @@
+import java.awt.*;
+import java.util.*;
+
+class ErrorWindow extends FixedFrame implements CbButtonCallback
+{
+       CbButton ok;
+
+       ErrorWindow(String m)
+       {
+       setLayout(new BorderLayout());
+       Panel cen = new BorderPanel(1);
+       StringTokenizer tok = new StringTokenizer(m, "\r\n");
+       cen.setLayout(new GridLayout(tok.countTokens(), 1));
+       while(tok.hasMoreTokens()) {
+               cen.add(new Label(tok.nextToken()));
+               }
+       add("Center", cen);
+       Panel bot = new GrayPanel();
+       bot.setLayout(new FlowLayout(FlowLayout.CENTER));
+       bot.add(new CbButton("Ok", this));
+       add("South", bot);
+       pack();
+       show();
+       setTitle("Error");
+       Util.recursiveBackground(this, Util.body);
+       }
+
+       public void click(CbButton b)
+       {
+       dispose();
+       }
+}
+
+
diff --git a/file/FileManager.java b/file/FileManager.java
new file mode 100644 (file)
index 0000000..fcbc2b4
--- /dev/null
@@ -0,0 +1,4504 @@
+import java.awt.*;
+import java.awt.event.*;
+import java.io.*;
+import java.applet.*;
+import java.net.*;
+import java.util.*;
+import netscape.javascript.JSObject;
+
+// A java filemanager that allows the user to manipulate files on the
+// Webmin server. Layout is similar to the windows explorer - directory
+// tree on the left, files on the right, action buttons on the top.
+public class FileManager extends Applet
+       implements CbButtonCallback, HierarchyCallback, MultiColumnCallback
+{
+       // top buttons
+       CbButton ret_b, config_b, down_b, edit_b, refresh_b, props_b,
+                copy_b, cut_b, paste_b, delete_b, new_b, upload_b, mkdir_b,
+                makelink_b, rename_b, share_b, mount_b, search_b, acl_b,
+                attr_b, ext_b, preview_b, extract_b, hnew_b;
+
+       // Directory tree
+       Hierarchy dirs;
+       FileNode root;
+       Hashtable nodemap = new Hashtable();
+
+       // File list
+       MultiColumn files;
+       TextField pathname;
+       CbButton history_b;
+       RemoteFile showing_files;
+       RemoteFile showing_list[];
+       Vector history_list = new Vector();
+
+       // Copying and pasting
+       RemoteFile cut_buffer[];
+       boolean cut_mode;
+
+       static final String monmap[] = { "Jan", "Feb", "Mar", "Apr",
+                                        "May", "Jun", "Jul", "Aug",
+                                        "Sep", "Oct", "Nov", "Dec" };
+       String accroot[];
+       String accnoroot[];
+       Hashtable lang = new Hashtable();
+       Hashtable stab = new Hashtable(),
+                 ntab = new Hashtable();
+       boolean sambamode;
+       int nfsmode;
+       String trust;
+       String extra;
+       String images;
+       int iconsize;
+
+       boolean got_filesystems,
+               acl_support, attr_support, ext_support;
+       Hashtable mounts = new Hashtable();
+       Vector fslist = new Vector();
+       boolean read_only = false;
+
+       // Standard font for all text
+       Font fixed;
+
+       // Font for button labels
+       Font small_fixed;
+
+       // Full session cookie
+       String session;
+
+       // Archive parameter
+       String archive;
+
+       // Chroot directory for tree
+       String chroot;
+
+       // File attributes that can be edited
+       boolean can_perms, can_users;
+
+       // Symlimks are automatically followed
+       boolean follow_links;
+
+       // Can search file contents
+       boolean search_contents;
+
+       // Use text editor for HTML
+       boolean force_text;
+
+       public void init()
+       {
+       setLayout(new BorderLayout());
+
+       // Create fonts from specified size
+       fixed = make_font("fixed", 12);
+       small_fixed = make_font("small_fixed", 10);
+
+       Util.setFont(small_fixed);
+       StringTokenizer tok = new StringTokenizer(getParameter("root"), " ");
+       accroot = new String[tok.countTokens()];
+       for(int i=0; tok.hasMoreTokens(); i++)
+               accroot[i] = tok.nextToken();
+       if (getParameter("noroot") != null) {
+               tok = new StringTokenizer(getParameter("noroot"), " ");
+               accnoroot = new String[tok.countTokens()];
+               for(int i=0; tok.hasMoreTokens(); i++)
+                       accnoroot[i] = tok.nextToken();
+               }
+       else {
+               accnoroot = new String[0];
+               }
+       trust = getParameter("trust");
+       session = getParameter("session");
+       extra = getParameter("extra");
+       if (extra == null) extra = "";
+       images = getParameter("images");
+       if (images == null) images = "images";
+       iconsize = Integer.parseInt(getParameter("iconsize"));
+       archive = getParameter("doarchive");
+       if (archive == null) archive = "0";
+       chroot = getParameter("chroot");
+       if (chroot == null) chroot = "/";
+       String can_perms_str = getParameter("canperms");
+       can_perms = can_perms_str == null || !can_perms_str.equals("0");
+       String can_users_str = getParameter("canusers");
+       can_users = can_users_str == null || !can_users_str.equals("0");
+       String search_contents_str = getParameter("contents");
+       search_contents = search_contents_str == null ||
+                         !search_contents_str.equals("0");
+       String force_text_str = getParameter("force_text");
+       if (force_text_str != null && force_text_str.equals("1"))
+               force_text = true;
+
+       // download language strings
+       String l[] = get_text("lang.cgi");
+       if (l.length < 1 || l[0].indexOf('=') < 0) {
+               String err = "Failed to get language list : "+join_array(l);
+               new ErrorWindow(err);
+               throw new Error(err);
+               }
+       for(int i=0; i<l.length; i++) {
+               int eq = l[i].indexOf('=');
+               if (eq >= 0)
+                       lang.put(l[i].substring(0, eq), l[i].substring(eq+1));
+               }
+
+       // list samba file shares
+       String s[] = get_text("list_shares.cgi");
+       if (s[0].equals("1")) {
+               for(int i=1; i<s.length; i++) {
+                       SambaShare ss = new SambaShare(s[i]);
+                       stab.put(ss.path, ss);
+                       }
+               sambamode = true;
+               }
+
+       // list NFS exports
+       String e[] = get_text("list_exports.cgi");
+       nfsmode = e.length == 0 ? 0 : Integer.parseInt(e[0]);
+       if (nfsmode != 0) {
+               for(int i=1; i<e.length; i++) {
+                       if (nfsmode == 1) {
+                               // Linux export
+                               LinuxExport le = new LinuxExport(e[i]);
+                               ntab.put(le.path, le);
+                               }
+                       else if (nfsmode == 2) {
+                               // Solaris share
+                               DFSAdminExport de = new DFSAdminExport(e[i]);
+                               ntab.put(de.path, de);
+                               }
+                       }
+               }
+
+       // list filesystems
+       get_filesystems();
+
+       // get read-only flag
+       if (getParameter("ro").equals("1"))
+               read_only = true;
+
+       // get custom colours
+       Util.light_edge = get_colour("light_edge", Util.light_edge);
+       Util.dark_edge = get_colour("dark_edge", Util.dark_edge);
+       Util.body = get_colour("body", Util.body);
+       Util.body_hi = get_colour("body_hi", Util.body_hi);
+       Util.light_edge_hi = get_colour("light_edge_hi", Util.light_edge_hi);
+       Util.dark_edge_hi = get_colour("dark_edge_hi", Util.dark_edge_hi);
+       Util.dark_bg = get_colour("dark_bg", Util.dark_bg);
+       Util.text = get_colour("text", Util.text);
+       Util.light_bg = get_colour("light_bg", Util.light_bg);
+
+       // create button panel
+       BorderPanel top = new BorderPanel(2, Util.body);
+       top.setLayout(new ToolbarLayout(ToolbarLayout.LEFT, 5, 2));
+
+       Panel top1 = new Panel();
+       top1.setLayout(new GridLayout(1, 0));
+       if (getParameter("return") != null && can_button("return"))
+               top1.add(ret_b = make_button("ret.gif", text("top_ret")));
+       if (getParameter("config") != null && can_button("config"))
+               top1.add(config_b = make_button("config.gif",
+                                               text("top_config")));
+       if (can_button("save"))
+               top1.add(down_b = make_button("down.gif", text("top_down")));
+       if (can_button("preview"))
+               top1.add(preview_b = make_button("preview.gif", text("top_preview")));
+       if (!read_only && can_button("edit")) {
+               top1.add(edit_b = make_button("edit.gif", text("top_edit")));
+               }
+       if (can_button("refresh"))
+               top1.add(refresh_b = make_button("refresh.gif", text("top_refresh")));
+       if (!read_only && can_button("info"))
+               top1.add(props_b = make_button("props.gif", text("top_info")));
+       if (acl_support && !read_only && can_button("acl"))
+               top1.add(acl_b = make_button("acl.gif", text("top_eacl")));
+       if (attr_support && !read_only && can_button("attr"))
+               top1.add(attr_b = make_button("attr.gif", text("top_attr")));
+       if (ext_support && !read_only && can_button("ext"))
+               top1.add(ext_b = make_button("ext.gif", text("top_ext")));
+       if (can_button("search"))
+               top1.add(search_b = make_button("search.gif", text("top_search")));
+       top.add(top1);
+       
+       if (!read_only) {
+               Panel top2 = new Panel();
+               top2.setLayout(new GridLayout(1, 0));
+               if (can_button("delete"))
+                       top2.add(delete_b = make_button("delete.gif",
+                                                       text("top_delete")));
+               if (can_button("new")) {
+                       top2.add(new_b = make_button("new.gif",
+                                                    text("top_new")));
+                       top2.add(hnew_b = make_button("html.gif",
+                                                    text("top_new")));
+                       }
+               if (can_button("upload"))
+                       top2.add(upload_b = make_button("upload.gif",
+                                                       text("top_upload")));
+                       top2.add(extract_b = make_button("extract.gif",
+                                                        text("top_extract")));
+               if (can_button("mkdir"))
+                       top2.add(mkdir_b = make_button("mkdir.gif",
+                                                      text("top_new")));
+               if (getParameter("follow").equals("0") &&
+                   can_button("makelink"))
+                       top2.add(makelink_b = make_button("makelink.gif",
+                                               text("top_new")));
+               if (can_button("rename"))
+                       top2.add(rename_b = make_button("rename.gif",
+                                                       text("top_rename")));
+               if ((sambamode || nfsmode != 0) &&
+                   getParameter("sharing").equals("1") &&
+                   can_button("sharing"))
+                       top2.add(share_b = make_button("share.gif",
+                                                      text("top_share")));
+               if (getParameter("mounting").equals("1") &&
+                   can_button("mount"))
+                       top2.add(mount_b = make_button("mount.gif",
+                                                      text("top_mount")));
+               top.add(top2);
+
+               if (can_button("copy")) {
+                       Panel top3 = new Panel();
+                       top3.setLayout(new GridLayout(1, 0));
+                       top3.add(copy_b = make_button("copy.gif",
+                                                     text("top_copy")));
+                       top3.add(cut_b = make_button("cut.gif",
+                                                    text("top_cut")));
+                       top3.add(paste_b = make_button("paste.gif",
+                                                      text("top_paste")));
+                       top.add(top3);
+                       }
+               }
+       add("North", top);
+       follow_links = getParameter("follow").equals("1");
+
+       // create directory tree
+       BorderPanel left = new BorderPanel(2, Util.body);
+       left.setLayout(new BorderLayout());
+       root = new FileNode(new RemoteFile(this, get_text("root.cgi")[0],null));
+       left.add("Center", dirs = new Hierarchy(root, this));
+       dirs.setFont(fixed);
+       root.open = true; root.fill();
+
+       // create file list window
+       BorderPanel right = new BorderPanel(2, Util.body);
+       right.setLayout(new BorderLayout());
+       Panel rtop = new Panel();
+       rtop.setLayout(new BorderLayout());
+       rtop.add("Center", pathname = new TextField());
+       rtop.add("East", history_b = new CbButton(text("history_button"),this));
+       right.add("North", rtop);
+       pathname.setFont(fixed);
+       String cols[] = { "", text("right_name"), text("right_size"),
+                         text("right_user"), text("right_group"),
+                         text("right_date") };
+       float widths[] = { .07f, .33f, .15f, .15f, .15f, .15f };
+       right.add("Center", files = new MultiColumn(cols, this));
+       files.setWidths(widths);
+       files.setDrawLines(false);
+       files.setMultiSelect(true);
+       files.setFont(fixed);
+       show_files(root.file);
+
+       ResizePanel mid = new ResizePanel(left, right, .3, false);
+       add("Center", mid);
+
+       // Go to the restricted directory
+       String home = getParameter("home");
+       String go = getParameter("goto");
+       String open = getParameter("open");
+       if (open != null) {
+               find_directory(open, true);
+               }
+       else if (go != null && go.equals("1")) {
+               if (home != null)
+                       find_directory(home, true);
+               else if (!accroot[0].equals("/"))
+                       find_directory(accroot[0], true);
+               }
+       }
+
+       Font make_font(String name, int defsize)
+       {
+       String str = getParameter(name);
+       int size = str == null || str.equals("") ? defsize :
+                  Integer.parseInt(str);
+       return new Font("courier", Font.PLAIN, size);
+       }
+
+       // Looks up an applet parameter for a colour, and returns it if
+       // defined, otherwise the default. MUST be in RRGGBB hex format
+       Color get_colour(String name, Color def)
+       {
+       String str = getParameter("applet_"+name);
+       if (str == null) {
+               return def;
+               }
+       else {
+               return new Color(get_hex(str, 0),
+                                get_hex(str, 2),
+                                get_hex(str, 4));
+               }
+       }
+
+       int get_hex(String str, int pos)
+       {
+       str = str.toUpperCase();
+       char c1 = str.charAt(pos), c2 = str.charAt(pos+1);
+       int b1 = Character.isDigit(c1) ? c1-48 : c1-65+10;
+       int b2 = Character.isDigit(c2) ? c2-48 : c2-65+10;
+       return (b1<<4) + (b2);
+       }
+
+       boolean can_button(String name)
+       {
+       return getParameter("no_"+name) == null;
+       }
+
+       CbButton make_button(String f, String t)
+       {
+       if (iconsize == 1)
+               return new CbButton(get_image(f), this);
+       else
+               return new CbButton(get_image(f), t, CbButton.ABOVE, this);
+       }
+
+       // Gets an image from the images directory
+       Image get_image(String img)
+       {
+       return getImage(getDocumentBase(), images+"/"+img);
+       }
+
+       // Gets charset parameter from Content-Type: header
+       String get_charset(String ct)
+       {
+       if (ct == null)
+               return null;
+       StringTokenizer st = new StringTokenizer(ct, ";");
+       while (st.hasMoreTokens()) {
+               String l = st.nextToken().trim();
+               if (l.startsWith("charset=")) {
+                       // get the value of charset= param.
+                       return l.substring(8);
+                       }
+               }
+       return null;
+       }
+
+       String[] get_text(String url)
+       {
+       try {
+               long now = System.currentTimeMillis();
+               if (url.indexOf('?') > 0) url += "&rand="+now;
+               else url += "?rand="+now;
+               url += "&trust="+trust;
+               url += extra;
+               URL u = new URL(getDocumentBase(), url);
+               URLConnection uc = u.openConnection();
+               set_cookie(uc);
+               String charset = get_charset(uc.getContentType());
+               InputStream ris = uc.getInputStream();
+               BufferedReader is = null;
+               if (charset == null) {
+                       is = new BufferedReader(new InputStreamReader(ris));
+                       }
+               else {
+                       // Try to use a character set, and handle failure
+                       try {
+                               is = new BufferedReader(
+                                       new InputStreamReader(ris, charset));
+                               }
+                       catch(Exception e) {
+                               e.printStackTrace();
+                               is = new BufferedReader(
+                                       new InputStreamReader(ris));
+                               }
+                       }
+               Vector lv = new Vector();
+               while(true) {
+                       String l = is.readLine();
+                       if (l == null) { break; }
+                       lv.addElement(l);
+                       }
+               is.close();
+               String rv[] = new String[lv.size()];
+               lv.copyInto(rv);
+               return rv;
+               }
+       catch(Exception e) {
+               e.printStackTrace();
+               //return null;
+               String err[] = { e.getClass().getName()+" : "+e.getMessage() };
+               return err;
+               }
+       }
+
+       void set_cookie(URLConnection conn)
+       {
+       if (session != null)
+               conn.setRequestProperty("Cookie", session);
+       }
+
+       // Fill the multicolumn list with files from some directory
+       boolean show_files(RemoteFile f)
+       {
+       RemoteFile fl[] = f.list();
+       if (fl == null) return false;
+       files.clear();
+       Object rows[][] = new Object[fl.length+1][];
+       long now = System.currentTimeMillis();
+
+       // Sort listing by chosen column
+       if (f != showing_files) {
+               // Directory has changed .. assume sort by name
+               files.sortingArrow(1, 1);
+               }
+       else if (files.sortdir != 0) {
+               // Sort by chosen order
+               RemoteFile fls[] = new RemoteFile[fl.length];
+               System.arraycopy(fl, 0, fls, 0, fl.length);
+               QuickSort.sort(fls, files.sortcol, files.sortdir);
+               fl = fls;
+               }
+
+       // Create parent directory row
+       rows[0] = new Object[6];
+       rows[0][0] = get_image("dir.gif");
+       rows[0][1] = "..";
+       rows[0][2] = rows[0][3] = rows[0][4] = rows[0][5] = "";
+
+       // Create file rows
+       Date n = new Date(now);
+       for(int i=0; i<fl.length; i++) {
+               Object row[] = rows[i+1] = new Object[6];
+               if (fl[i].shared() && fl[i].mounted())
+                       row[0] = get_image("smdir.gif");
+               else if (fl[i].shared() && fl[i].mountpoint())
+                       row[0] = get_image("sudir.gif");
+               else if (fl[i].shared())
+                       row[0] = get_image("sdir.gif");
+               else if (fl[i].mounted())
+                       row[0] = get_image("mdir.gif");
+               else if (fl[i].mountpoint())
+                       row[0] = get_image("udir.gif");
+               else
+                       row[0] = get_image(RemoteFile.tmap[fl[i].type]);
+               row[1] = fl[i].name;
+               if (fl[i].size < 1000)
+                       row[2] = spad(fl[i].size, 5)+" B";
+               else if (fl[i].size < 1000000)
+                       row[2] = spad(fl[i].size/1000, 5)+" kB";
+               else
+                       row[2] = spad(fl[i].size/1000000, 5)+" MB";
+               row[3] = fl[i].user;
+               row[4] = fl[i].group;
+               Date d = new Date(fl[i].modified);
+               //if (now - fl[i].modified < 24*60*60*1000) {
+               if (n.getDate() == d.getDate() &&
+                   n.getMonth() == d.getMonth() &&
+                   n.getYear() == d.getYear()) {
+                       // show as hour:min
+                       row[5] = pad(d.getHours(),2)+":"+
+                                pad(d.getMinutes(),2);
+                       }
+               //else if (now - fl[i].modified < 24*60*60*365*1000) {
+               else if (n.getYear() == d.getYear()) {
+                       // show as day/mon
+                       row[5] = pad(d.getDate(),2)+"/"+
+                                monmap[d.getMonth()];
+                       }
+               else {
+                       // show as mon/year
+                       row[5] = monmap[d.getMonth()]+"/"+
+                                pad(d.getYear()%100, 2);
+                       }
+               }
+       files.addItems(rows);
+       showing_files = f;
+       showing_list = fl;
+       pathname.setText(f.path);
+       return true;
+       }
+
+       String pad(int n, int s)
+       {
+       String rv = String.valueOf(n);
+       while(rv.length() < s)
+               rv = "0"+rv;
+       return rv;
+       }
+
+       String spad(long n, int s)
+       {
+       String rv = String.valueOf(n);
+       while(rv.length() < s)
+               rv = " "+rv;
+       return rv;
+       }
+
+       String trim_path(String p)
+       {
+       while(p.endsWith("/"))
+               p = p.substring(0, p.length()-1);
+       return p;
+       }
+
+       // openNode
+       // Called when a node with children is opened
+       public void openNode(Hierarchy h, HierarchyNode n)
+       {
+       FileNode fn = (FileNode)n;
+       fn.fill();
+       }
+
+       // closeNode
+       // Called when a node is closed
+       public void closeNode(Hierarchy h, HierarchyNode n)
+       {
+       }
+
+       // clickNode
+       // Called when the user clicks on a node
+       public void clickNode(Hierarchy h, HierarchyNode n)
+       {
+       FileNode fn = (FileNode)n;
+       if (showing_files != fn.file)
+               show_files(fn.file);
+       }
+
+       // doubleNode
+       // Called when a user double-clicks on a node
+       public void doubleNode(Hierarchy h, HierarchyNode n)
+       {
+       }
+
+       // Called when a button is clicked
+       public void click(CbButton b)
+       {
+       int s = files.selected();
+       int ss[] = files.allSelected();
+       RemoteFile f = null, ff[] = new RemoteFile[0];
+       if (s > 0 || s == 0 && ss.length > 1) {
+               // At least one non-.. file was selected
+               boolean parentsel = false;
+               for(int i=0; i<ss.length; i++)
+                       if (ss[i] == 0)
+                               parentsel = true;
+               RemoteFile list[] = showing_list;
+               if (parentsel) {
+                       // need to exclude .. from selected list!
+                       ff = new RemoteFile[ss.length-1];
+                       for(int i=0,j=0; i<ss.length; i++)
+                               if (ss[i] != 0)
+                                       ff[j++] = list[ss[i]-1];
+                       f = s == 0 ? ff[0] : list[s-1];
+                       }
+               else {
+                       // include all selected files
+                       f = list[s-1];
+                       ff = new RemoteFile[ss.length];
+                       for(int i=0; i<ss.length; i++)
+                               ff[i] = list[ss[i]-1];
+                       }
+               }
+       FileNode d = (FileNode)dirs.selected();
+       if (b == ret_b) {
+               // Return to the webmin index
+               try {
+                       URL u = new URL(getDocumentBase(),
+                                       getParameter("return"));
+                       getAppletContext().showDocument(u);
+                       }
+               catch(Exception e) { }
+               }
+       else if (b == config_b) {
+               // Open the module config window
+               try {
+                       URL u = new URL(getDocumentBase(),
+                                       getParameter("config"));
+                       getAppletContext().showDocument(u, "_self");
+                       }
+               catch(Exception e) { }
+               }
+       else if (b == edit_b) {
+               // Open a window for editing the selected file
+               if (f == null)
+                       new ErrorWindow(text("top_efile"));
+               else if (f.type == 0 || f.type > 4)
+                       new ErrorWindow(text("edit_enormal"));
+               else if ((f.path.toLowerCase().endsWith(".htm") ||
+                         f.path.toLowerCase().endsWith(".html")) &&
+                        !force_text) {
+                       // Open HTML editor
+                       try {
+                               JSObject win = JSObject.getWindow(this);
+                               String params[] = { f.path, "" };
+                               win.call("htmledit", params);
+                               }
+                       catch(Exception e) {
+                               new ErrorWindow(text("html_efailed",
+                                                    e.getMessage()));
+                               }
+                       }
+               else {
+                       // Open text editor
+                       new EditorWindow(f, this);
+                       }
+               }
+       else if (b == down_b) {
+               // Force download of the selected file
+               if (f == null) return;
+               download_file(f);
+               }
+       else if (b == preview_b) {
+               // Open preview window for selected file
+               if (f == null) return;
+               if (f.type == RemoteFile.DIR)
+                       new ErrorWindow(text("preview_eimage"));
+               else
+                       new PreviewWindow(this, f);
+               }
+       else if (b == refresh_b) {
+               // Refesh the selected directory (and thus any subdirs)
+               if (d == null) return;
+               d.refresh();
+               show_files(d.file);
+               }
+       else if (b == props_b) {
+               // Display the properties window
+               if (f == null) return;
+               new PropertiesWindow(f, this);
+               }
+       else if (b == acl_b) {
+               // Display the ACL window (if filesystem supports them)
+               if (f == null) return;
+               FileSystem filefs = find_filesys(f);
+               if (filefs == null) return;
+               if (filefs.acls)
+                       new ACLWindow(this, f);
+               else
+                       new ErrorWindow(text("eacl_efs", filefs.mount));
+               }
+       else if (b == attr_b) {
+               // Display the attributes window (if filesystem supports them)
+               if (f == null) return;
+               FileSystem filefs = find_filesys(f);
+               if (filefs == null) return;
+               if (filefs.attrs)
+                       new AttributesWindow(this, f);
+               else
+                       new ErrorWindow(text("attr_efs", filefs.mount));
+               }
+       else if (b == ext_b) {
+               // Display EXT attributes window (if filesystem supports them)
+               if (f == null) return;
+               FileSystem filefs = find_filesys(f);
+               if (filefs == null) return;
+               if (filefs.ext)
+                       new EXTWindow(this, f);
+               else
+                       new ErrorWindow(text("ext_efs", filefs.mount));
+               }
+       else if (b == copy_b) {
+               // Copy the selected files
+               if (f == null) return;
+               cut_buffer = ff;
+               cut_mode = false;
+               }
+       else if (b == cut_b) {
+               // Cut the selected file
+               if (f == null) return;
+               cut_buffer = ff;
+               cut_mode = true;
+               }
+       else if (b == paste_b) {
+               // Paste the copied file
+               if (cut_buffer == null) {
+                       new ErrorWindow(text("paste_ecopy"));
+                       return;
+                       }
+
+               // Check for existing file clashes
+               // XXX
+
+               // Go through all the files to paste
+               for(int i=0; i<cut_buffer.length; i++) {
+                       RemoteFile cf = cut_buffer[i];
+
+                       // Check for an existing file
+                       RemoteFile already = showing_files.find(cf.name);
+                       String sp = showing_files.path;
+                       String dest_path = sp.equals("/") ? sp+cf.name
+                                                         : sp+"/"+cf.name;
+                       if (already != null) {
+                               // File exists .. offer to rename
+                               new OverwriteWindow(this, already, cf, i);
+                               }
+                       else {
+                               // do the move or copy
+                               RemoteFile nf = paste_file(cf, showing_files,
+                                                  dest_path, null, cut_mode);
+                               if (cut_mode && nf != null) {
+                                       // Paste from the destination path
+                                       // from now on
+                                       cut_buffer[i] = nf;
+                                       }
+                               }
+                       }
+               cut_mode = false;
+               }
+       else if (b == delete_b) {
+               // Delete the selected files
+               if (f == null) return;
+               new DeleteWindow(this, ff);
+               }
+       else if (b == new_b) {
+               // Open a window for creating a text file
+               new EditorWindow(showing_files.path, this);
+               }
+       else if (b == hnew_b) {
+               // Open a window for creating an HTML file
+               try {
+                       JSObject win = JSObject.getWindow(this);
+                       String params[] = { "", showing_files.path };
+                       win.call("htmledit", params);
+                       }
+               catch(Exception e) {
+                       new ErrorWindow(text("html_efailed",
+                                            e.getMessage()));
+                       }
+               }
+       else if (b == upload_b) {
+               // Call javascript to open an upload window
+               try {
+                       JSObject win = JSObject.getWindow(this);
+                       String params[] = { showing_files.path };
+                       win.call("upload", params);
+                       }
+               catch(Exception e) {
+                       new ErrorWindow(text("upload_efailed", e.getMessage()));
+                       }
+               }
+       else if (b == extract_b) {
+               // Ask for confirmation, then extract file
+               if (f == null) return;
+               if (f.type == 0 || f.type == 6 || f.type == 7)
+                       new ErrorWindow(text("extract_etype", f.path));
+               else
+                       new ExtractWindow(this, f);
+               }
+       else if (b == mkdir_b) {
+               // Prompt for new directory
+               new MkdirWindow(showing_files.path, this);
+               }
+       else if (b == makelink_b) {
+               // Prompt for a new symlink
+               new LinkWindow(showing_files.path, this);
+               }
+       else if (b == rename_b) {
+               // Prompt for new filename
+               if (f == null) return;
+               new RenameWindow(this, f);
+               }
+       else if (b == share_b) {
+               // Open a window for editing sharing options
+               if (f == null || f.type != RemoteFile.DIR) return;
+               new SharingWindow(f, this);
+               }
+       else if (b == mount_b) {
+               // Check if the selected directory is a mount point
+               if (f == null || f.type != RemoteFile.DIR) return;
+               FileSystem fs = f.fs();
+               if (fs == null)
+                       new ErrorWindow(text("mount_epoint", f.path));
+               else
+                       new MountWindow(this, fs, f);
+               }
+       else if (b == search_b) {
+               // Open window for finding a file
+               new SearchWindow(showing_files.path, this);
+               }
+       else if (b == history_b) {
+               // Open entered file history window
+               if (history_list.size() > 0) {
+                       new HistoryWindow(this);
+                       }
+               }
+       }
+
+       boolean under_root_dir(String p, String roots[])
+       {
+       boolean can = false;
+       int l = p.length();
+       for(int r=0; r<roots.length; r++) {
+               int rl = roots[r].length();
+               if (roots[r].equals("/"))
+                       can = true;
+               else if (l >= rl && p.substring(0, rl).equals(roots[r]))
+                       can = true;
+               else if (l < rl && roots[r].substring(0, l).equals(p))
+                       can = true;
+               }
+       return can;
+       }
+
+       // Download some file to the user's browser, if possible
+       void download_file(RemoteFile f)
+       {
+       if (f.type == RemoteFile.DIR && !archive.equals("0"))
+               new DownloadDirWindow(this, f);
+       else if (f.type == RemoteFile.DIR || f.type > 4)
+               new ErrorWindow(text("view_enormal2"));
+       else
+               open_file_window(f, true, 0);
+       }
+
+       // Returns the object for some directory, or null if not found.
+       RemoteFile find_directory(String p, boolean fill)
+       {
+       boolean can = under_root_dir(p, accroot) &&
+                     !under_root_dir(p, accnoroot);
+       if (!can) {
+               new ErrorWindow(text("find_eaccess", p));
+               return null;
+               }
+       FileNode posnode = root;
+       RemoteFile pos = posnode.file;
+       StringTokenizer tok = new StringTokenizer(p, "/");
+       while(tok.hasMoreTokens()) {
+               String fn = tok.nextToken();
+               if (fn.equals("")) continue;
+               RemoteFile fl[] = pos.list();
+               if (fl == null) return null;
+               if (fill) {
+                       posnode.open = true;
+                       posnode.fill();
+                       }
+               boolean found = false;
+               for(int i=0; i<fl.length; i++)
+                       if (fl[i].name.equals(fn)) {
+                               pos = fl[i];
+                               found = true;
+                               }
+               if (!found) {
+                       new ErrorWindow(text("find_eexist", fn, p));
+                       return null;
+                       }
+               if (pos.type != 0) {
+                       new ErrorWindow(text("find_edir", fn, p));
+                       return null;
+                       }
+               if (fill)
+                       posnode = (FileNode)nodemap.get(pos);
+               }
+       if (fill) {
+               if (show_files(pos)) {
+                       posnode.fill();
+                       posnode.open = true;
+                       dirs.select(posnode);
+                       dirs.redraw();
+                       }
+               }
+       return pos;
+       }
+
+       FileSystem find_filesys(RemoteFile f)
+       {
+       FileSystem filefs = null;
+       for(int i=0; i<fslist.size(); i++) {
+               FileSystem fs = (FileSystem)fslist.elementAt(i);
+               int l = fs.mount.length();
+               if (fs.mount.equals(f.path) ||
+                   (f.path.length() >= l+1 &&
+                    f.path.substring(0, l+1).equals(fs.mount+"/")) ||
+                   fs.mount.equals("/")) {
+                       filefs = fs;
+                       }
+               }
+       return filefs;
+       }
+
+       public boolean action(Event e, Object o)
+       {
+       if (e.target == pathname) {
+               // A new path was entered.. cd to it
+               String p = pathname.getText().trim();
+               if (p.equals("")) return true;
+               find_directory(p, true);
+
+               // Add to the history
+               if (!history_list.contains(p)) {
+                       history_list.insertElementAt(p, 0);
+                       }
+               return true;
+               }
+       return false;
+       }
+
+        // singleClick
+        // Called on a single click on a list item
+        public void singleClick(MultiColumn list, int num)
+       {
+       }
+
+        // doubleClick
+        // Called upon double-clicking on a list item
+        public void doubleClick(MultiColumn list, int num)
+       {
+       if (num == 0) {
+               // Go to parent directory
+               if (showing_files.directory != null) {
+                       ((FileNode)nodemap.get(showing_files)).open = false;
+                       show_files(showing_files.directory);
+                       dirs.select((FileNode)nodemap.get(showing_files));
+                       dirs.redraw();
+                       }
+               return;
+               }
+       RemoteFile d = showing_list[num-1];
+       if (d.type == 0) {
+               // Open this directory
+               FileNode pn = (FileNode)nodemap.get(showing_files);
+               pn.fill();
+               pn.open = true;
+               FileNode fn = (FileNode)nodemap.get(d);
+               if (show_files(d)) {
+                       fn.fill();
+                       fn.open = true;
+                       dirs.select(fn);
+                       dirs.redraw();
+                       }
+               }
+       else if (d.type <= 4) {
+               // Direct the browser to this file
+               open_file_window(d, list.last_event.shiftDown(), 0);
+               }
+       }
+
+       // Called when the user clicks on a column heading so that it can
+       // be sorted.
+       public void headingClicked(MultiColumn list, int col)
+       {
+       if (col == 0)
+               return; // ignore click on icon column?
+       if (col == list.sortcol) {
+               list.sortingArrow(col, list.sortdir == 2 ? 1 : 2);
+               }
+       else {
+               list.sortingArrow(col, 1);
+               }
+
+       // Re-show the list in the new order, but with the same files selected
+       int ss[] = files.allSelected();
+       RemoteFile ssf[] = new RemoteFile[ss.length];
+       for(int i=0; i<ss.length; i++)
+               ssf[i] = showing_list[ss[i]-1];
+       show_files(showing_files);
+       for(int i=0; i<ss.length; i++) {
+               for(int j=0; j<showing_list.length; j++) {
+                       if (showing_list[j] == ssf[i]) {
+                               ss[i] = j+1;
+                               break;
+                               }
+                       }
+               }
+       files.select(ss);
+       }
+
+       void open_file_window(RemoteFile f, boolean download, int format)
+       {
+       try {
+               String ext = format == 1 ? ".zip" :
+                            format == 2 ? ".tgz" :
+                            format == 3 ? ".tar" : "";
+               String urlstr;
+               if (download) {
+                       urlstr = "show.cgi"+urlize(f.path)+ext+
+                                "?rand="+System.currentTimeMillis()+
+                                "&type=application%2Funknown"+
+                                "&trust="+trust+
+                                "&format="+format+
+                                extra;
+                       }
+               else {
+                       urlstr = "show.cgi"+urlize(f.path)+ext+
+                                "?rand="+System.currentTimeMillis()+
+                                "&trust="+trust+
+                                "&format="+format+
+                                extra;
+                       }
+
+               // Do a test fetch
+               String l[] = get_text(urlstr+"&test=1");
+               if (l[0].length() > 0) {
+                       new ErrorWindow(text("eopen", l[0]));
+                       return;
+                       }
+
+               // Open for real
+               if (download) {
+                       getAppletContext().showDocument(
+                               new URL(getDocumentBase(), urlstr));
+                       }
+               else {
+                       getAppletContext().showDocument(
+                               new URL(getDocumentBase(), urlstr), "show");
+                       }
+               }
+       catch(Exception e) { }
+       }
+
+       static String urlize(String s)
+       {
+       StringBuffer rv = new StringBuffer();
+       for(int i=0; i<s.length(); i++) {
+               char c = s.charAt(i);
+               if (c < 16)
+                       rv.append("%0"+Integer.toString(c, 16));
+               else if ((!Character.isLetterOrDigit(c) && c != '/' &&
+                   c != '.' && c != '_' && c != '-') || c >= 128)
+                       rv.append("%"+Integer.toString(c, 16));
+               else
+                       rv.append(c);
+               }
+       return rv.toString();
+       }
+
+       static String un_urlize(String s)
+       {
+       StringBuffer rv = new StringBuffer();
+       for(int i=0; i<s.length(); i++) {
+               char c = s.charAt(i);
+               if (c == '%') {
+                       rv.append((char)Integer.parseInt(
+                               s.substring(i+1, i+3), 16));
+                       i += 2;
+                       }
+               else
+                       rv.append(c);
+               }
+       return rv.toString();
+       }
+
+       // Called back by Javascript when a file or directory has been modified
+       public void upload_notify(String path_str, String info)
+       {
+       int sl = path_str.lastIndexOf('/');
+       String par_str = path_str.substring(0, sl),
+              file_str = path_str.substring(sl+1);
+       RemoteFile par = find_directory(par_str, false);
+       RemoteFile upfile = par.find(file_str);
+       if (upfile == null) {
+               // Need to add this file/directory
+               upfile = new RemoteFile(this, info, par);
+               par.add(upfile);
+               }
+       else if (upfile.type == RemoteFile.DIR) {
+               // Is a directory .. refresh from server
+               FileNode upnode = (FileNode)nodemap.get(upfile);
+               if (upnode != null)
+                       upnode.refresh();
+               }
+       show_files(showing_files);
+       }
+
+       // Called back by Javascript to show an upload-related error
+       public void upload_error(String err)
+       {
+       new ErrorWindow(err);
+       }
+
+       public String text(String k, String p[])
+       {
+       String rv = (String)lang.get(k);
+       if (rv == null) rv = "???";
+       for(int i=0; i<p.length; i++) {
+               int idx = rv.indexOf("$"+(i+1));
+               if (idx != -1)
+                       rv = rv.substring(0, idx)+p[i]+rv.substring(idx+2);
+               }
+       return rv;
+       }
+
+       public String text(String k)
+       {
+       String p[] = { };
+       return text(k, p);
+       }
+
+       public String text(String k, String p1)
+       {
+       String p[] = { p1 };
+       return text(k, p);
+       }
+
+       public String text(String k, String p1, String p2)
+       {
+       String p[] = { p1, p2 };
+       return text(k, p);
+       }
+
+       RemoteFile paste_file(RemoteFile src, RemoteFile dir,
+                             String dest, RemoteFile already, boolean mode)
+       {
+       // Move or copy the actual file
+       String[] rv = get_text((mode ? "move.cgi" : "copy.cgi")+
+                              "?from="+urlize(src.path)+
+                              "&to="+urlize(dest));
+       if (rv[0].length() > 0) {
+               new ErrorWindow(text(
+                       mode ? "paste_emfailed" : "paste_ecfailed", rv[0]));
+               return null;
+               }
+       RemoteFile file = new RemoteFile(this, rv[1], dir);
+       if (already == null) {
+               // Add to the parent directory
+               dir.add(file);
+               }
+       else {
+               // Update the existing file
+               already.type = file.type;
+               already.user = file.user;
+               already.group = file.group;
+               already.size = file.size;
+               already.perms = file.perms;
+               already.modified = file.modified;
+               file = already;
+               }
+       if (mode) {
+               // Delete the old file
+               src.directory.delete(src);
+               }
+       if (src.type == 0) {
+               // Moving or copying a directory.. update the tree
+               FileNode dest_par_node =
+                       (FileNode)nodemap.get(showing_files);
+               dest_par_node.add(new FileNode(file));
+               if (mode) {
+                       FileNode cut_par_node =
+                               (FileNode)nodemap.get(src.directory);
+                       FileNode cut_file_node =
+                               (FileNode)nodemap.get(src);
+                       if (cut_par_node != null &&
+                           cut_file_node != null)
+                               cut_par_node.ch.removeElement(
+                                                       cut_file_node);
+                       }
+               dirs.redraw();
+               }
+       show_files(showing_files);
+       return file;
+       }
+
+       // Loads the list of filesystems from the server, and refreshes all
+       // caches
+       void get_filesystems()
+       {
+       String f[] = get_text("filesystems.cgi");
+       got_filesystems = f[0].equals("1");
+       acl_support = false;
+       attr_support = false;
+       ext_support = false;
+       mounts.clear();
+       fslist.removeAllElements();
+       if (got_filesystems) {
+               for(int i=1; i<f.length; i++) {
+                       FileSystem fs = new FileSystem(f[i]);
+                       fslist.addElement(fs);
+                       if (fs.acls) acl_support = true;
+                       if (fs.attrs) attr_support = true;
+                       if (fs.ext) ext_support = true;
+                       mounts.put(fs.mount, fs);
+                       }
+               }
+       }
+
+       String join_array(String l[])
+       {
+       String rv = "";
+       for(int i=0; i<l.length; i++)
+               rv += l[i]+"\n";
+       return rv;
+       }
+
+       static String replace_str(String str, String os, String ns)
+       {
+       String rv;
+       int idx;
+       int pos = 0;
+       rv = str;
+       while((idx = rv.indexOf(os, pos)) >= 0) {
+               rv = rv.substring(0, idx)+
+                     ns+rv.substring(idx+os.length());
+               pos = idx+ns.length()+1;
+               }
+       return rv;
+       }
+}
+
+// A node in the directory tree
+class FileNode extends HierarchyNode
+{
+       FileManager parent;
+       RemoteFile file;
+       boolean known;
+
+       FileNode(RemoteFile file)
+       {
+       this.file = file;
+       parent = file.parent;
+       setimage();
+       ch = new Vector();
+       text = file.name;
+       parent.nodemap.put(file, this);
+       }
+
+       // Create the nodes for subdirectories
+       void fill()
+       {
+       if (!known) {
+               RemoteFile l[] = file.list();
+               if (l == null) return;
+               ch.removeAllElements();
+               for(int i=0; i<l.length; i++)
+                       if (l[i].type == 0)
+                               ch.addElement(new FileNode(l[i]));
+               parent.dirs.redraw();
+               known = true;
+               }
+       }
+
+       void add(FileNode n)
+       {
+       for(int i=0; i<=ch.size(); i++) {
+               FileNode ni = i==ch.size() ? null : (FileNode)ch.elementAt(i);
+               if (ni == null || ni.text.compareTo(n.text) > 0) {
+                       ch.insertElementAt(n, i);
+                       break;
+                       }
+               }
+       }
+
+       void setimage()
+       {
+       im = parent.get_image(file.shared() && file.mounted() ? "smdir.gif" :
+                             file.shared() && file.mountpoint() ? "sudir.gif" :
+                             file.shared() ? "sdir.gif" :
+                             file.mounted() ? "mdir.gif" :
+                             file.mountpoint() ? "udir.gif" :
+                                                 "dir.gif");
+       }
+
+       // Forces a re-load from the server
+       void refresh()
+       {
+       known = false;
+       file.list = null;
+       fill();
+       }
+}
+
+class RemoteFile
+{
+       static final int DIR = 0;
+       static final int TEXT = 1;
+       static final int IMAGE = 2;
+       static final int BINARY = 3;
+       static final int UNKNOWN = 4;
+       static final int SYMLINK = 5;
+       static final int DEVICE = 6;
+       static final int PIPE = 7;
+       static final String[] tmap = { "dir.gif", "text.gif", "image.gif",
+                                      "binary.gif", "unknown.gif",
+                                      "symlink.gif", "device.gif",
+                                      "pipe.gif" };
+
+       FileManager parent;
+       String path, name;
+       int type;
+       String user, group;
+       long size;
+       int perms;
+       long modified;
+       String linkto;
+       RemoteFile list[];
+       RemoteFile directory;
+
+       // Parse a line of text to a file object
+       RemoteFile(FileManager parent, String line, RemoteFile d)
+       {
+       this.parent = parent;
+       StringTokenizer tok = new StringTokenizer(line, "\t");
+       if (tok.countTokens() < 7) {
+               String err = "Invalid file line : "+line;
+               new ErrorWindow(err);
+               throw new Error(err);
+               }
+       path = tok.nextToken();
+       path = parent.replace_str(path, "\\t", "\t");
+       path = parent.replace_str(path, "\\\\", "\\");
+       type = Integer.parseInt(tok.nextToken());
+       user = tok.nextToken();
+       group = tok.nextToken();
+       size = Long.parseLong(tok.nextToken());
+       perms = Integer.parseInt(tok.nextToken());
+       modified = Long.parseLong(tok.nextToken())*1000;
+       if (type == 5) linkto = tok.nextToken();
+       directory = d;
+       if (path.equals("/")) name = "/";
+       else name = path.substring(path.lastIndexOf('/')+1);
+       }
+
+       // Create a new, empty file object
+       RemoteFile() { }
+
+       // Returns a list of files in this directory
+       RemoteFile[] list()
+       {
+       if (list == null) {
+               String l[] = parent.get_text("list.cgi?dir="+
+                                            parent.urlize(path));
+               if (l[0].length() > 0) {
+                       //list = new RemoteFile[0];
+                       // Error reading the remote directory!
+                       new ErrorWindow(parent.text("list_edir", path, l[0]));
+                       list = null;
+                       }
+               else {
+                       list = new RemoteFile[l.length-3];
+                       for(int i=3; i<l.length; i++)
+                               list[i-3] = new RemoteFile(parent, l[i], this);
+                       }
+               }
+       return list;
+       }
+
+       RemoteFile find(String n)
+       {
+       RemoteFile l[] = list();
+       if (l != null) {
+               for(int i=0; i<l.length; i++)
+                       if (l[i].name.equals(n))
+                               return l[i];
+               }
+       return null;
+       }
+
+       void add(RemoteFile f)
+       {
+       RemoteFile nlist[] = new RemoteFile[list.length+1];
+       int offset = 0;
+       for(int i=0; i<list.length; i++) {
+               if (list[i].name.compareTo(f.name) > 0 && offset == 0) {
+                       nlist[i] = f;
+                       offset++;
+                       }
+               nlist[i+offset] = list[i];
+               }
+       if (offset == 0) nlist[list.length] = f;
+       list = nlist;
+       }
+
+       void delete(RemoteFile f)
+       {
+       RemoteFile nlist[] = new RemoteFile[list.length-1];
+       for(int i=0,j=0; i<list.length; i++)
+               if (list[i] != f)
+                       nlist[j++] = list[i];
+       list = nlist;
+       }
+
+       boolean shared()
+       {
+       return type == DIR &&
+              (parent.stab.get(path) != null ||
+               parent.ntab.get(path) != null);
+       }
+
+       boolean mountpoint()
+       {
+       return type == DIR && fs() != null;
+       }
+
+       boolean mounted()
+       {
+       FileSystem fs = fs();
+       return type == DIR && fs != null && fs.mtab;
+       }
+
+       FileSystem fs()
+       {
+       return (FileSystem)parent.mounts.get(path);
+       }
+
+}
+
+class EditorWindow extends FixedFrame implements CbButtonCallback
+{
+       TextField name;
+       TextArea edit;
+       CbButton save_b, saveclose_b, cancel_b, goto_b, find_b;
+       Checkbox dosmode;
+       RemoteFile file;
+       FileManager filemgr;
+       GotoWindow goto_window;
+       FindReplaceWindow find_window;
+
+       // Editing an existing file
+       EditorWindow(RemoteFile f, FileManager p)
+       {
+       super(500, 300);
+       file = f; filemgr = p;
+       makeUI(false);
+       setTitle(filemgr.text("edit_title", file.path));
+
+       // Load the file
+       try {
+               URL u = new URL(filemgr.getDocumentBase(),
+                               "show.cgi"+filemgr.urlize(file.path)+
+                               "?rand="+System.currentTimeMillis()+
+                               "&trust="+filemgr.trust+"&edit=1"+
+                               filemgr.extra);
+               URLConnection uc = u.openConnection();
+               filemgr.set_cookie(uc);
+               int len = uc.getContentLength();
+               InputStream is = uc.getInputStream();
+               byte buf[];
+               if (len >= 0) {
+                       // Length is known
+                       buf = new byte[uc.getContentLength()];
+                       int got = 0;
+                       while(got < buf.length)
+                               got += is.read(buf, got, buf.length-got);
+                       }
+               else {
+                       // Length is unknown .. read till the end
+                       buf = new byte[0];
+                       while(true) {
+                           byte data[] = new byte[16384];
+                           int got;
+                           try { got = is.read(data); }
+                           catch(EOFException ex) { break; }
+                           if (got <= 0) break;
+                           byte nbuf[] = new byte[buf.length + got];
+                           System.arraycopy(buf, 0, nbuf, 0, buf.length);
+                           System.arraycopy(data, 0, nbuf, buf.length, got);
+                           buf = nbuf;
+                           }
+                       }
+               String s = new String(buf, 0);
+               if (s.indexOf("\r\n") != -1) {
+                       dosmode.setState(true);
+                       s = FileManager.replace_str(s, "\r\n", "\n");
+                       }
+               edit.setText(s);
+               is.close();
+               file.size = buf.length;
+               }
+       catch(Exception e) { e.printStackTrace(); }
+       }
+
+       // Creating a new file
+       EditorWindow(String f, FileManager p)
+       {
+       super(500, 300);
+       filemgr = p;
+       makeUI(true);
+       setTitle(filemgr.text("edit_title2"));
+       name.setText(f.equals("/") ? f : f+"/");
+       name.select(name.getText().length(), name.getText().length());
+       }
+
+       void makeUI(boolean add_name)
+       {
+       setLayout(new BorderLayout());
+       if (add_name) {
+               Panel np = new Panel();
+               np.setLayout(new BorderLayout());
+               np.add("West", new Label(filemgr.text("edit_filename")));
+               np.add("Center", name = new TextField());
+               name.setFont(filemgr.fixed);
+               add("North", np);
+               }
+       add("Center", edit = new TextArea(20, 80));
+       edit.setFont(filemgr.fixed);
+       Panel bot = new Panel();
+       bot.setLayout(new FlowLayout(FlowLayout.RIGHT));
+       bot.add(dosmode = new Checkbox("DOS mode"));
+       bot.add(goto_b = new CbButton(filemgr.get_image("goto.gif"),
+                                     filemgr.text("edit_goto"),
+                                     CbButton.LEFT, this));
+       bot.add(find_b = new CbButton(filemgr.get_image("find.gif"),
+                                     filemgr.text("edit_find"),
+                                     CbButton.LEFT, this));
+       bot.add(new Label(" "));
+       bot.add(save_b = new CbButton(filemgr.get_image("save.gif"),
+                                     filemgr.text("save"),
+                                     CbButton.LEFT, this));
+       bot.add(saveclose_b = new CbButton(filemgr.get_image("save.gif"),
+                                     filemgr.text("edit_saveclose"),
+                                     CbButton.LEFT, this));
+       bot.add(cancel_b = new CbButton(filemgr.get_image("cancel.gif"),
+                                       filemgr.text("close"),
+                                       CbButton.LEFT, this));
+       add("South", bot);
+       Util.recursiveBody(this);
+       pack();
+       show();
+       }
+
+       public void click(CbButton b)
+       {
+       if (b == save_b || b == saveclose_b) {
+               RemoteFile par = null, already = null;
+               String save_path;
+               if (file == null) {
+                       // Locate the filemgr directory
+                       save_path = filemgr.trim_path(name.getText());
+                       int sl = save_path.lastIndexOf('/');
+                       par = filemgr.find_directory(
+                                       save_path.substring(0, sl), false);
+                       if (par == null) return;
+                       already = par.find(save_path.substring(sl+1));
+                       if (already != null &&
+                           (already.type == 0 || already.type == 5)) {
+                               new ErrorWindow(
+                                       filemgr.text("edit_eover", save_path));
+                               return;
+                               }
+                       }
+               else save_path = file.path;
+
+               // Save the file back again
+               String s = edit.getText(), line;
+               s = FileManager.replace_str(s, "\r\n", "\n");
+               try {
+                       if (dosmode.getState()) {
+                               // Convert to DOS newlines
+                               s = FileManager.replace_str(s, "\n", "\r\n");
+                               }
+                       URL u = new URL(filemgr.getDocumentBase(),
+                                       "save.cgi"+filemgr.urlize(save_path)+
+                                       "?rand="+System.currentTimeMillis()+
+                                       "&trust="+filemgr.trust+
+                                       "&length="+s.length()+
+                                       filemgr.extra);
+                       URLConnection uc = u.openConnection();
+                       uc.setRequestProperty("Content-type", "text/plain");
+                       filemgr.set_cookie(uc);
+                       uc.setDoOutput(true);
+                       OutputStream os = uc.getOutputStream();
+                       byte buf[] = new byte[s.length()];
+                       s.getBytes(0, buf.length, buf, 0);
+                       os.write(buf);
+                       os.close();
+                       BufferedReader is =
+                           new BufferedReader(new InputStreamReader(
+                               uc.getInputStream()));
+                       String err = is.readLine();
+                       if (err.length() > 0) {
+                               new ErrorWindow(
+                                       filemgr.text("edit_esave", err));
+                               is.close();
+                               return;
+                               }
+                       line = is.readLine();
+                       is.close();
+                       }
+               catch(Exception e) { e.printStackTrace(); return; }
+
+               if (file == null) {
+                       // Create and insert or replace the file object
+                       file = new RemoteFile(filemgr, line, par);
+                       if (already != null) {
+                               // A file with this name exists
+                               already.type = file.type;
+                               already.user = file.user;
+                               already.group = file.group;
+                               already.size = file.size;
+                               already.perms = file.perms;
+                               already.modified = file.modified;
+                               }
+                       else {
+                               // Add to the list
+                               par.add(file);
+                               }
+                       }
+               else {
+                       file.size = s.length();
+                       file.modified = System.currentTimeMillis();
+                       }
+               filemgr.show_files(filemgr.showing_files);
+               if (b == saveclose_b)
+                       dispose();
+               }
+       else if (b == cancel_b) {
+               // Just close
+               dispose();
+               }
+       else if (b == goto_b) {
+               // Open a dialog asking which line to go to
+               if (goto_window != null)
+                       goto_window.toFront();
+               else
+                       goto_window = new GotoWindow(this);
+               }
+       else if (b == find_b) {
+               // Open the search (and replace) dialog
+               if (find_window != null)
+                       find_window.toFront();
+               else
+                       find_window = new FindReplaceWindow(this);
+               }
+       }
+
+       public void dispose()
+       {
+       super.dispose();
+       if (goto_window != null) goto_window.dispose();
+       if (find_window != null) find_window.dispose();
+       }
+}
+
+class GotoWindow extends FixedFrame implements CbButtonCallback
+{
+       EditorWindow editor;
+       FileManager filemgr;
+       TextField line;
+       CbButton goto_b, cancel_b;
+
+       GotoWindow(EditorWindow e)
+       {
+       editor = e;
+       filemgr = e.filemgr;
+
+       setLayout(new BorderLayout());
+       add("West", new Label(filemgr.text("edit_gotoline")));
+       add("Center", line = new TextField(10));
+       line.setFont(filemgr.fixed);
+       Panel bot = new Panel();
+       bot.setLayout(new FlowLayout(FlowLayout.RIGHT));
+       bot.add(goto_b = new CbButton(filemgr.get_image("goto.gif"),
+                                     filemgr.text("edit_goto"),
+                                     CbButton.LEFT, this));
+       bot.add(cancel_b = new CbButton(filemgr.get_image("cancel.gif"),
+                                       filemgr.text("close"),
+                                       CbButton.LEFT, this));
+       add("South", bot);
+       Util.recursiveBody(this);
+       pack();
+       show();
+       }
+
+       public void click(CbButton b)
+       {
+       if (b == goto_b) {
+               // Go to the chose line, if it exists
+               int lnum;
+               try { lnum = Integer.parseInt(line.getText()); }
+               catch(Exception e) { return; }
+
+               String txt = editor.edit.getText();
+               int c, l = 0;
+               for(c=0; c<txt.length(); c++) {
+                       if (txt.charAt(c) == '\n') {
+                               l++;
+                               if (l == lnum) {
+                                       // Found the line!
+                                       editor.edit.select(c, c);
+                                       dispose();
+                                       return;
+                                       }
+                               }
+                       }
+               }
+       else if (b == cancel_b) {
+               // Just close the window
+               dispose();
+               }
+       }
+
+       public void dispose()
+       {
+       super.dispose();
+       editor.goto_window = null;
+       }
+
+       public boolean handleEvent(Event e)
+       {
+       if (e.target == line && e.id == Event.KEY_RELEASE && e.key == 10) {
+               click(goto_b);
+               return true;
+               }
+       return false;
+       }
+}
+
+class FindReplaceWindow extends FixedFrame implements CbButtonCallback
+{
+       EditorWindow editor;
+       FileManager filemgr;
+       TextField find, replace;
+       CbButton find_b, replace_b, all_b, cancel_b;
+
+       FindReplaceWindow(EditorWindow e)
+       {
+       editor = e;
+       filemgr = e.filemgr;
+       setLayout(new BorderLayout());
+
+       Panel left = new Panel();
+       left.setLayout(new GridLayout(2, 1));
+       left.add(new Label(filemgr.text("edit_searchfor")));
+       left.add(new Label(filemgr.text("edit_replaceby")));
+       add("West", left);
+
+       Panel right = new Panel();
+       right.setLayout(new GridLayout(2, 1));
+       right.add(find = new TextField(40));
+       find.setFont(filemgr.fixed);
+       right.add(replace = new TextField(40));
+       replace.setFont(filemgr.fixed);
+       add("Center", right);
+
+       Panel bot = new Panel();
+       bot.setLayout(new FlowLayout(FlowLayout.RIGHT));
+       bot.add(find_b = new CbButton(filemgr.get_image("find.gif"),
+                                     filemgr.text("edit_find"),
+                                     CbButton.LEFT, this));
+       bot.add(replace_b = new CbButton(filemgr.get_image("replace.gif"),
+                                     filemgr.text("edit_replace"),
+                                     CbButton.LEFT, this));
+       bot.add(all_b = new CbButton(filemgr.get_image("all.gif"),
+                                     filemgr.text("edit_all"),
+                                     CbButton.LEFT, this));
+       bot.add(cancel_b = new CbButton(filemgr.get_image("cancel.gif"),
+                                       filemgr.text("close"),
+                                       CbButton.LEFT, this));
+       add("South", bot);
+       Util.recursiveBody(this);
+       pack();
+       show();
+       }
+
+       public void click(CbButton b)
+       {
+       String findtxt = find.getText();
+       String edittxt = editor.edit.getText();
+       if (findtxt.length() == 0)
+               return;
+       if (b == find_b) {
+               // Find the next occurrance of the text, starting from
+               // the cursor + 1, and select it
+               int pos = edittxt.indexOf(findtxt,
+                                          editor.edit.getSelectionStart()+1);
+               if (pos < 0)
+                       new ErrorWindow(filemgr.text("edit_notfound", findtxt));
+               else
+                       editor.edit.select(pos, pos+findtxt.length());
+               }
+       else if (b == replace_b) {
+               // If the word to search for is selected, replace it. Otherwise
+               // just search for the next one
+               int st = editor.edit.getSelectionStart(),
+                   en = editor.edit.getSelectionEnd();
+               if (st >= 0) {
+                       String sel = edittxt.substring(st, en);
+                       if (sel.equals(findtxt)) {
+                               // Replace the selected
+                               editor.edit.setText(edittxt.substring(0, st)+
+                                                   replace.getText()+
+                                                   edittxt.substring(en));
+                               editor.edit.select(st, st);
+                               return;
+                               }
+                       }
+               click(find_b);
+               }
+       else if (b == all_b) {
+               // Replace all occurrances of the text in the editor
+               int pos = 0;
+               int len = findtxt.length();
+               int st = editor.edit.getSelectionStart(),
+                   en = editor.edit.getSelectionEnd();
+               while((pos = edittxt.indexOf(findtxt, pos)) != -1) {
+                       edittxt = edittxt.substring(0, pos)+
+                                 replace.getText()+
+                                 edittxt.substring(pos+len);
+                       pos += len;
+                       }
+               editor.edit.setText(edittxt);
+               editor.edit.select(st, en);     // put back old selection
+               }
+       else if (b == cancel_b) {
+               // Just close the window
+               dispose();
+               }
+       }
+
+       public void dispose()
+       {
+       super.dispose();
+       editor.find_window = null;
+       }
+}
+
+class PropertiesWindow extends FixedFrame implements CbButtonCallback
+{
+       RemoteFile file;
+       FileManager filemgr;
+       CbButton save_b, cancel_b, size_b;
+
+       TextField linkto;
+       TextField user, group;
+       Checkbox setuid, setgid;
+       PermissionsPanel user_p, group_p, other_p;
+       Checkbox sticky;
+       Choice rec_mode;
+       TextField octal;
+
+       TextField bytes, files, dirs;
+
+       PropertiesWindow(RemoteFile f, FileManager p)
+       {
+       file = f;
+       filemgr = p;
+
+       // Create UI
+       setTitle(f.path);
+       setLayout(new BorderLayout());
+       Panel bot = new Panel();
+       bot.setLayout(new FlowLayout(FlowLayout.RIGHT));
+       if (file.type == 0) {
+               bot.add(size_b = new CbButton(filemgr.get_image("refresh.gif"),
+                                             filemgr.text("info_getsize"),
+                                             CbButton.LEFT, this));
+               }
+       if (filemgr.can_perms || filemgr.can_users) {
+               bot.add(save_b = new CbButton(filemgr.get_image("save.gif"),
+                                             filemgr.text("save"),
+                                             CbButton.LEFT, this));
+               }
+       bot.add(cancel_b = new CbButton(filemgr.get_image("cancel.gif"),
+                                       filemgr.text("cancel"),
+                                       CbButton.LEFT, this));
+       add("South", bot);
+
+       Panel mid = new Panel();
+       mid.setLayout(new BorderLayout());
+       TabbedPanel tab = null;
+       add("Center", mid);
+
+       // Create file details section
+       Panel det = new LinedPanel(filemgr.text("info_file")),
+             dl = new Panel(), dr = new Panel();
+       setup_leftright(det, dl, dr);
+       add_item(filemgr.text("info_path"),
+               new Label(file.path), dl, dr);
+       add_item(filemgr.text("info_type"),
+               new Label(filemgr.text("file_type"+file.type)), dl, dr);
+       add_item(filemgr.text("info_size"),
+               new Label(String.valueOf(file.size)),dl,dr);
+       add_item(filemgr.text("info_mod"),
+               new Label(String.valueOf(new Date(file.modified))), dl, dr);
+       if (file.type == 5) {
+               add_item(filemgr.text("info_link"),
+                        linkto = new TextField(file.linkto, 30), dl, dr);
+               linkto.setFont(filemgr.fixed);
+               }
+       mid = add_panel(mid, det);
+
+       if (filemgr.can_perms) {
+               // Create permissions section
+               Panel per = new LinedPanel(filemgr.text("info_perms")),
+                     pl = new Panel(), pr = new Panel();
+               setup_leftright(per, pl, pr);
+               add_item(filemgr.text("info_user"),
+                   user_p = new PermissionsPanel(file, 64, filemgr), pl, pr);
+               add_item(filemgr.text("info_group"),
+                   group_p = new PermissionsPanel(file, 8, filemgr), pl, pr);
+               add_item(filemgr.text("info_other"),
+                   other_p = new PermissionsPanel(file, 1, filemgr), pl,pr);
+               if (file.type == 0) {
+                       add_item(filemgr.text("info_sticky"),
+                           sticky = new Checkbox(filemgr.text("info_sticky2")),
+                           pl,pr);
+                       sticky.setState((file.perms&01000) != 0);
+                       }
+               add_item(filemgr.text("info_octal"),
+                        octal = new TextField(4), pl, pr);
+               octal.setFont(filemgr.fixed);
+               octal.setEditable(false);
+               mid = add_panel(mid, per);
+               }
+
+       if (filemgr.can_users) {
+               // Create ownership section
+               Panel own = new LinedPanel(filemgr.text("info_own")),
+                     ol = new Panel(), or = new Panel();
+               setup_leftright(own, ol, or);
+               add_item(filemgr.text("info_user"),
+                        user = new TextField(file.user, 10), ol, or);
+               user.setFont(filemgr.fixed);
+               if (file.type != 0) {
+                       add_item(filemgr.text("info_setuid"),
+                           setuid = new Checkbox(filemgr.text("info_setuid2")),
+                           ol, or);
+                       setuid.setState((file.perms & 0x800) != 0);
+                       }
+               add_item(filemgr.text("info_group"),
+                        group = new TextField(file.group, 10), ol, or);
+               group.setFont(filemgr.fixed);
+               if (file.type == 0)
+                       add_item(filemgr.text("info_setgid"),
+                         setgid = new Checkbox(filemgr.text("info_setgid2")),
+                         ol, or);
+               else
+                       add_item(filemgr.text("info_setgid"),
+                         setgid = new Checkbox(filemgr.text("info_setgid3")),
+                         ol, or);
+               setgid.setState((file.perms & 0x400) != 0);
+               mid = add_panel(mid, own);
+               }
+
+       if (file.type == 0) {
+               // Create directory size section, initially empty
+               Panel szp = new LinedPanel(filemgr.text("info_sizeheader")),
+                     sl = new Panel(), sr = new Panel();
+               setup_leftright(szp, sl, sr);
+               add_item(filemgr.text("info_bytes"),
+                        bytes = new TextField("", 10), sl, sr);
+               bytes.setFont(filemgr.fixed);
+               bytes.setEditable(false);
+               add_item(filemgr.text("info_files"),
+                        files = new TextField("", 10), sl, sr);
+               files.setFont(filemgr.fixed);
+               files.setEditable(false);
+               add_item(filemgr.text("info_dirs"),
+                        dirs = new TextField("", 10), sl, sr);
+               dirs.setFont(filemgr.fixed);
+               dirs.setEditable(false);
+               mid = add_panel(mid, szp);
+               }
+
+       if (file.type == 0 && (filemgr.can_perms || filemgr.can_users)) {
+               // Create recursion section
+               Panel rec = new LinedPanel(filemgr.text("info_apply"));
+               rec.setLayout(new BorderLayout());
+               rec_mode = new Choice();
+               for(int i=1; i<=3; i++)
+                       rec_mode.addItem(filemgr.text("info_apply"+i));
+               rec.add("Center", rec_mode);
+               mid = add_panel(mid, rec);
+               }
+
+       set_octal();
+       Util.recursiveBody(this);
+       pack();
+       show();
+       }
+
+       Panel add_panel(Panel p, Component c)
+       {
+       p.add("North", c);
+       Panel np = new Panel();
+       np.setLayout(new BorderLayout());
+       p.add("Center", np);
+       return np;
+       }
+
+       public void click(CbButton b)
+       {
+       if (b == save_b) {
+               // Update the file
+               int perms = get_perms();
+               String user_str = user != null ? user.getText() : null;
+               String group_str = group != null ? group.getText() : null;
+               int rec = 0;
+               if (file.type == 0 && rec_mode != null)
+                       rec = rec_mode.getSelectedIndex();
+               String rv[] = filemgr.get_text(
+                       "chmod.cgi?path="+filemgr.urlize(file.path)+
+                       (perms < 0 ? "" : "&perms="+perms)+
+                       (user_str == null ? "" :
+                               "&user="+filemgr.urlize(user_str))+
+                       (group_str == null ? "" :
+                               "&group="+filemgr.urlize(group_str))+
+                       "&rec="+rec+
+                       (linkto==null ? "" :
+                               "&linkto="+filemgr.urlize(linkto.getText())));
+               if (rv[0].length() > 0) {
+                       // Something went wrong
+                       new ErrorWindow(filemgr.text("info_efailed",
+                                       file.path, rv[0]));
+                       }
+               else {
+                       // Update all changed file objects
+                       if (linkto != null)
+                               file.linkto = linkto.getText();
+                       else if (rec == 0)
+                               update_file(file, perms, false);
+                       else if (rec == 1) {
+                               // Update files in this directory
+                               update_file(file, perms, false);
+                               recurse_files(file, perms, false);
+                               }
+                       else if (rec == 2) {
+                               // Update files and subdirs
+                                update_file(file, perms, false);
+                               recurse_files(file, perms, true);
+                               }
+
+                       // Update directory list
+                       int os = filemgr.files.selected();
+                       filemgr.show_files(filemgr.showing_files);
+                       filemgr.files.select(os);
+                       dispose();
+                       }
+               }
+       else if (b == size_b) {
+               // Get the size of the directory recursively
+               String l[] = filemgr.get_text("size.cgi?dir="+
+                                             filemgr.urlize(file.path));
+               if (l[0].length() > 0) {
+                       new ErrorWindow(filemgr.text("info_size", l[0]));
+                       }
+               StringTokenizer tok = new StringTokenizer(l[1], " ");
+               String bytes_str = tok.nextToken();
+               files.setText(tok.nextToken());
+               dirs.setText(tok.nextToken());
+               bytes.setText(tok.nextToken()+" "+tok.nextToken());
+               }
+       else {
+               // Just close
+               dispose();
+               }
+       }
+
+       void update_file(RemoteFile f, int perms, boolean perms_only)
+       {
+       f.user = user.getText();
+       f.group = group.getText();
+       if (perms_only)
+               f.perms = (perms & 0777) | (f.perms & 037777777000);
+       else
+               f.perms = perms;
+       }
+
+       void recurse_files(RemoteFile f, int perms, boolean do_subs)
+       {
+       if (f.list == null) return;
+       for(int i=0; i<f.list.length; i++) {
+               RemoteFile ff = f.list[i];
+               if (ff.type == 5) continue;
+               else if (ff.type == 0) {
+                       if (do_subs) {
+                               update_file(ff, perms, false);
+                               recurse_files(ff, perms, true);
+                               }
+                       }
+               else update_file(ff, perms, true);
+               }
+       }
+
+       void setup_leftright(Panel m, Panel l, Panel r)
+       {
+       m.setLayout(new BorderLayout());
+       Panel p = new Panel();
+       p.setLayout(new BorderLayout());
+       p.add("West", l);
+       p.add("Center", r);
+       l.setLayout(new GridLayout(0, 1));
+       r.setLayout(new GridLayout(0, 1));
+       m.add("North", p);
+       }
+
+       void add_item(String t, Component c, Panel l, Panel r)
+       {
+       l.add(new Label(t));
+       Panel p = new Panel();
+       p.setLayout(new BorderLayout());
+       p.add("West", c);
+       r.add(p);
+       }
+
+       void set_octal()
+       {
+       if (octal != null) {
+               String oct = Integer.toOctalString(get_perms());
+               while(oct.length() < 4)
+                       oct = "0"+oct;
+               octal.setText(oct);
+               }
+       }
+
+       int get_perms()
+       {
+       if (user_p == null)
+               return -1;              // Cannot edit
+       int perms = 0;
+       if (setuid == null)
+               perms |= (file.perms & 0x800);
+       else
+               perms |= (setuid.getState() ? 0x800 : 0);
+       perms |= (setgid.getState() ? 0x400 : 0);
+       perms |= user_p.getPerms();
+       perms |= group_p.getPerms();
+       perms |= other_p.getPerms();
+       if (sticky == null)
+               perms |= (file.perms & 01000);
+       else
+               perms |= (sticky.getState() ? 01000 : 0);
+       return perms;
+       }
+
+       public boolean handleEvent(Event e)
+       {
+       if (e.target instanceof Checkbox) {
+               set_octal();
+               return true;
+               }
+       return super.handleEvent(e);
+       }
+}
+
+class PermissionsPanel extends Panel
+{
+       Checkbox read, write, exec;
+       int base;
+
+       PermissionsPanel(RemoteFile file, int base, FileManager filemgr)
+       {
+       int perms = file.perms;
+       this.base = base;
+       setLayout(new GridLayout(1, 3));
+       add(read = new Checkbox(filemgr.text("info_read")));
+       read.setState((perms&(base<<2)) != 0);
+       add(write = new Checkbox(filemgr.text("info_write")));
+       write.setState((perms&(base<<1)) != 0);
+       add(exec = new Checkbox(
+               filemgr.text(file.type == RemoteFile.DIR ? "info_list"
+                                                        : "info_exec")));
+       exec.setState((perms&base) != 0);
+       }
+
+       int getPerms()
+       {
+       int rv = 0;
+       rv |= (read.getState() ? (base<<2) : 0);
+       rv |= (write.getState() ? (base<<1) : 0);
+       rv |= (exec.getState() ? base : 0);
+       return rv;
+       }
+}
+
+class DeleteWindow extends FixedFrame implements CbButtonCallback
+{
+       CbButton delete_b, cancel_b;
+       FileManager filemgr;
+       RemoteFile files[];
+
+       DeleteWindow(FileManager p, RemoteFile ff[])
+       {
+       filemgr = p;
+       files = ff;
+       setTitle(filemgr.text(ff.length > 1 ? "delete_mtitle" :
+                             ff[0].type == 0 ? "delete_dtitle" :
+                                               "delete_ftitle"));
+
+       setLayout(new BorderLayout());
+       if (ff.length > 1) {
+               add("North", new Label(filemgr.text("delete_mdesc")));
+               Panel mp = new Panel();
+               mp.setLayout(new GridLayout(ff.length, 1));
+               for(int i=0; i<ff.length; i++)
+                       mp.add(new Label(ff[i].path));
+               add("Center", mp);
+               }
+       else
+               add("Center", new MultiLabel(filemgr.text(
+                       ff[0].type == 0 ? "delete_ddesc" : "delete_fdesc",
+                       ff[0].path), 35));
+       Panel bot = new Panel();
+       bot.setLayout(new FlowLayout(FlowLayout.CENTER));
+       bot.add(delete_b = new CbButton(filemgr.get_image("save.gif"),
+                                       filemgr.text("delete"),
+                                       CbButton.LEFT, this));
+       bot.add(cancel_b = new CbButton(filemgr.get_image("cancel.gif"),
+                                       filemgr.text("cancel"),
+                                       CbButton.LEFT, this));
+       add("South", bot);
+       Util.recursiveBody(this);
+       pack();
+       show();
+       }
+
+       public void click(CbButton b)
+       {
+       if (b == delete_b) {
+               // Delete the file or directory
+               boolean need_redraw = false, need_reshow = false;
+               for(int i=0; i<files.length; i++) {
+                       RemoteFile file = files[i];
+                       String rv[] = filemgr.get_text("delete.cgi?file="+
+                                              filemgr.urlize(file.path));
+                       if (rv[0].length() > 0) {
+                               new ErrorWindow(filemgr.text("delete_efailed",
+                                               file.path, rv[0]));
+                               break;
+                               }
+                       else {
+                               // done the deed.. update data structures
+                               RemoteFile pf = file.directory;
+                               pf.delete(file);
+                               if (filemgr.showing_files == pf) {
+                                       // Need to refresh the list as well..
+                                       need_reshow = true;
+                                       }
+
+                               FileNode node = (FileNode)filemgr.nodemap.get(
+                                                       file);
+                               FileNode pnode = (FileNode)filemgr.nodemap.get(
+                                                       pf);
+                               if (node != null) {
+                                       // Take the directory out of the tree..
+                                       pnode.ch.removeElement(node);
+                                       need_redraw = true;
+                                       }
+                               }
+                       }
+               if (need_reshow) filemgr.show_files(filemgr.showing_files);
+               if (need_redraw) filemgr.dirs.redraw();
+               dispose();
+               }
+       else if (b == cancel_b)
+               dispose();
+       }
+}
+
+class MkdirWindow extends FixedFrame implements CbButtonCallback
+{
+       FileManager filemgr;
+       TextField dir;
+       CbButton create_b, cancel_b;
+
+       MkdirWindow(String d, FileManager p)
+       {
+       filemgr = p;
+       setTitle(filemgr.text("mkdir_title"));
+       setLayout(new BorderLayout());
+       add("West", new Label(filemgr.text("mkdir_dir")));
+       add("Center", dir = new TextField(d.equals("/") ? "/" : d+"/", 40));
+       dir.setFont(filemgr.fixed);
+       dir.select(dir.getText().length(), dir.getText().length());
+       Panel bot = new Panel();
+       bot.setLayout(new FlowLayout(FlowLayout.CENTER));
+       bot.add(create_b = new CbButton(filemgr.get_image("save.gif"),
+                                       filemgr.text("create"),
+                                       CbButton.LEFT, this));
+       bot.add(cancel_b = new CbButton(filemgr.get_image("cancel.gif"),
+                                       filemgr.text("cancel"),
+                                       CbButton.LEFT, this));
+       add("South", bot);
+       Util.recursiveBody(this);
+       pack();
+       show();
+       }
+
+       public void click(CbButton b)
+       {
+       if (b == create_b) {
+               // Find the filemgr directory
+               String path = dir.getText();
+               path = filemgr.trim_path(path);
+               int sl = path.lastIndexOf('/');
+               RemoteFile par = filemgr.find_directory(
+                                       path.substring(0, sl), false);
+               if (par.find(path.substring(sl+1)) != null) {
+                       new ErrorWindow(filemgr.text("mkdir_eexists", path));
+                       return;
+                       }
+               String rv[] = filemgr.get_text("mkdir.cgi?dir="+
+                                              filemgr.urlize(path));
+               if (rv[0].length() > 0) {
+                       new ErrorWindow(filemgr.text("mkdir_efailed", rv[0]));
+                       return;
+                       }
+               RemoteFile file = new RemoteFile(filemgr, rv[1], par);
+               par.add(file);
+               FileNode parnode = (FileNode)filemgr.nodemap.get(par);
+               if (parnode != null) {
+                       // Update the tree
+                       parnode.add(new FileNode(file));
+                       filemgr.dirs.redraw();
+                       }
+               filemgr.show_files(filemgr.showing_files);
+               dispose();
+               }
+       else dispose();
+       }
+}
+
+class LinkWindow extends FixedFrame implements CbButtonCallback
+{
+       FileManager filemgr;
+       TextField from, to;
+       CbButton create_b, cancel_b;
+
+       LinkWindow(String d, FileManager p)
+       {
+       filemgr = p;
+       setLayout(new BorderLayout());
+       setTitle(filemgr.text("link_title"));
+       Panel l = new Panel(), r = new Panel();
+       l.setLayout(new GridLayout(0, 1));
+       l.add(new Label(filemgr.text("link_from")));
+       l.add(new Label(filemgr.text("link_to")));
+       r.setLayout(new GridLayout(0, 1));
+       r.add(from = new TextField(d.equals("/") ? "/" : d+"/", 40));
+       from.setFont(filemgr.fixed);
+       from.select(from.getText().length(), from.getText().length());
+       r.add(to = new TextField());
+       to.setFont(filemgr.fixed);
+       add("West", l); add("Center", r);
+       Panel bot = new Panel();
+       bot.setLayout(new FlowLayout(FlowLayout.CENTER));
+       bot.add(create_b = new CbButton(filemgr.get_image("save.gif"),
+                                       filemgr.text("create"),
+                                       CbButton.LEFT, this));
+       bot.add(cancel_b = new CbButton(filemgr.get_image("cancel.gif"),
+                                       filemgr.text("cancel"),
+                                       CbButton.LEFT, this));
+       add("South", bot);
+       Util.recursiveBody(this);
+       pack();
+       show();
+       }
+
+       public void click(CbButton b)
+       {
+       if (b == create_b) {
+               // Check inputs
+               String from_str = from.getText().trim();
+               if (!from_str.startsWith("/")) {
+                       new ErrorWindow(filemgr.text("link_efrom", from_str));
+                       return;
+                       }
+               int sl = from_str.lastIndexOf('/');
+               String par_str = from_str.substring(0, sl),
+                      file_str = from_str.substring(sl+1);
+               RemoteFile par = filemgr.find_directory(par_str, false);
+               if (par == null) return;
+               if (par.find(file_str) != null) {
+                       new ErrorWindow(filemgr.text("link_eexists", from_str));
+                       return;
+                       }
+
+               // Create the actual link
+               String rv[] = filemgr.get_text("makelink.cgi?from="+
+                                              filemgr.urlize(from_str)+"&to="+
+                                              filemgr.urlize(to.getText()));
+               if (rv[0].length() > 0) {
+                       new ErrorWindow(filemgr.text("link_efailed", rv[0]));
+                       return;
+                       }
+               RemoteFile file = new RemoteFile(filemgr, rv[1], par);
+               par.add(file);
+               filemgr.show_files(filemgr.showing_files);
+               dispose();
+               }
+       else if (b == cancel_b)
+               dispose();
+       }
+}
+
+class RenameWindow extends FixedFrame implements CbButtonCallback
+{
+       FileManager filemgr;
+       RemoteFile file;
+       TextField oldname, newname;
+       CbButton rename_b, cancel_b;
+
+       RenameWindow(FileManager p, RemoteFile f)
+       {
+       filemgr = p; file = f;
+       setLayout(new BorderLayout());
+       setTitle(filemgr.text("rename_title", file.path));
+       Panel l = new Panel(), r = new Panel();
+       l.setLayout(new GridLayout(0, 1));
+       l.add(new Label(filemgr.text("rename_old")));
+       l.add(new Label(filemgr.text("rename_new")));
+       r.setLayout(new GridLayout(0, 1));
+       r.add(oldname = new TextField(file.name, 20));
+       oldname.setEditable(false);
+       oldname.setFont(filemgr.fixed);
+       r.add(newname = new TextField(file.name, 20));
+       newname.select(file.name.length(), file.name.length());
+       newname.setFont(filemgr.fixed);
+       add("West", l); add("Center", r);
+
+       Panel bot = new Panel();
+       bot.setLayout(new FlowLayout(FlowLayout.CENTER));
+       bot.add(rename_b = new CbButton(filemgr.get_image("save.gif"),
+                                       filemgr.text("rename_ok"),
+                                       CbButton.LEFT, this));
+       bot.add(cancel_b = new CbButton(filemgr.get_image("cancel.gif"),
+                                       filemgr.text("cancel"),
+                                       CbButton.LEFT, this));
+       add("South", bot);
+       pack();
+       show();
+       Util.recursiveBody(this);
+       }
+
+       public void click(CbButton b)
+       {
+       if (b == rename_b) {
+               // Work out destination file and directory
+               String newstr = newname.getText().trim();
+               if (newstr.length() == 0) return;
+               RemoteFile destdir;
+               String newpath;
+               if (newstr.indexOf('/') >= 0) {
+                       // Different dir
+                       if (newstr.startsWith("/")) {
+                               // Some absolute path
+                               newpath = newstr;
+                               }
+                       else {
+                               // Relative to this dir
+                               newpath = file.directory.path+"/"+newstr;
+                               }
+                       int sl = newpath.lastIndexOf('/');
+                       String newdir = sl == 0 ? "/" : newpath.substring(0,sl);
+                       destdir = filemgr.find_directory(newdir, false);
+                       }
+               else {
+                       // Same dir
+                       destdir = file.directory;
+                       int sl = file.path.lastIndexOf('/');
+                       newpath = file.path.substring(0, sl)+"/"+newstr;
+                       }
+
+               // Work out filename only
+               int sl = newpath.lastIndexOf('/');
+               newstr = newpath.substring(sl+1);
+
+               // Check for an existing file
+               RemoteFile already = destdir.find(newstr);
+               if (already != null) {
+                       new ErrorWindow(filemgr.text("rename_eexists", newstr));
+                       return;
+                       }
+
+               // Rename the real file
+               String rv[] = filemgr.get_text(
+                               "rename.cgi?old="+filemgr.urlize(file.path)+
+                               "&new="+filemgr.urlize(newpath));
+               if (rv[0].length() > 0) {
+                       new ErrorWindow(filemgr.text("rename_efailed", rv[0]));
+                       return;
+                       }
+
+               // Update data structure
+               file.name = newstr;
+               file.path = newpath;
+               file.directory.delete(file);
+               destdir.list();
+               destdir.add(file);
+               file.directory = destdir;
+               file.list = null;
+               FileNode parnode = (FileNode)filemgr.nodemap.get(file.directory);
+               FileNode filenode = (FileNode)filemgr.nodemap.get(file);
+               if (parnode != null && filenode != null) {
+                       // Need to refresh tree
+                       filenode.text = file.name;
+                       parnode.ch.removeElement(filenode);
+                       parnode.add(filenode);
+                       dispose();
+                       filemgr.dirs.redraw();
+                       }
+
+               filemgr.show_files(filemgr.showing_files);
+               dispose();
+               }
+       else if (b == cancel_b)
+               dispose();
+       }
+}
+
+class OverwriteWindow extends FixedFrame implements CbButtonCallback
+{
+       FileManager filemgr;
+       RemoteFile src, already;
+       TextField newname;
+       CbButton ok, cancel;
+       int idx;
+       boolean mode;
+
+       OverwriteWindow(FileManager p, RemoteFile a, RemoteFile s, int i)
+       {
+       filemgr = p; src = s; already = a; idx = i;
+       mode = filemgr.cut_mode;
+       setLayout(new BorderLayout());
+       setTitle(filemgr.text("over_title"));
+       add("North",
+           new MultiLabel(filemgr.text("over_msg", already.path), 30, 0));
+       add("West", new Label(filemgr.text("over_new")));
+       add("East", newname = new TextField(a.name, 30));
+       newname.setFont(filemgr.fixed);
+
+       Panel bot = new Panel();
+       bot.setLayout(new FlowLayout(FlowLayout.RIGHT));
+       bot.add(ok = new CbButton(filemgr.get_image("save.gif"),
+                                 filemgr.text("over_ok"),
+                                 CbButton.LEFT, this));
+       bot.add(cancel = new CbButton(filemgr.get_image("cancel.gif"),
+                                 filemgr.text("cancel"),
+                                 CbButton.LEFT, this));
+       add("South", bot);
+       Util.recursiveBody(this);
+       pack();
+       show();
+       }
+
+       public void click(CbButton b)
+       {
+       if (b == cancel)
+               dispose();
+       else if (b == ok && newname.getText().length() > 0) {
+               // paste the file, but with a new name
+               RemoteFile ap = already.directory;
+               RemoteFile newalready = ap.find(newname.getText());
+               if (newalready == src) {
+                       new ErrorWindow(filemgr.text("paste_eself"));
+                       return;
+                       }
+               if (newalready != null && (newalready.type == 0 ||
+                                          newalready.type == 5)) {
+                       new ErrorWindow(
+                               filemgr.text("paste_eover", newalready.path));
+                       return;
+                       }
+               String dpath = (ap.path.equals("/") ? "/" :
+                               ap.path+"/")+newname.getText();
+               RemoteFile nf = filemgr.paste_file(src, already.directory,
+                                                  dpath, newalready, mode);
+               if (filemgr.cut_mode && nf != null) {
+                       // Paste from the destination path from now on
+                       filemgr.cut_buffer[idx] = nf;
+                       }
+               dispose();
+               }
+       }
+}
+
+class SambaShare
+{
+       String path;
+       boolean available;
+       boolean writable;
+       int guest;
+       String comment;
+
+       SambaShare(String l)
+       {
+       StringSplitter tok = new StringSplitter(l, ':');
+       path = tok.nextToken();
+       available = tok.nextToken().equals("1");
+       writable = tok.nextToken().equals("1");
+       guest = Integer.parseInt(tok.nextToken());
+       comment = tok.nextToken();
+       }
+
+       SambaShare(String p, boolean a, boolean w, int g, String c)
+       {
+       path = p;
+       available = a;
+       writable = w;
+       guest = g;
+       comment = c;
+       }
+
+       String params()
+       {
+       return "path="+FileManager.urlize(path)+
+              "&available="+(available ? 1 : 0)+
+              "&writable="+(writable ? 1 : 0)+
+              "&guest="+guest+
+              "&comment="+FileManager.urlize(comment);
+       }
+}
+
+class DFSAdminExport
+{
+       String path;
+       String desc;
+       String ro, rw, root;
+
+       DFSAdminExport(String l)
+       {
+       StringSplitter tok = new StringSplitter(l, ':');
+       path = tok.nextToken();
+       ro = tok.nextToken();
+       rw = tok.nextToken();
+       root = tok.nextToken();
+       desc = tok.nextToken();
+       }
+
+       DFSAdminExport(String p, String d, String ro, String rw, String root)
+       {
+       path = p;
+       desc = d;
+       this.ro = ro;
+       this.rw = rw;
+       this.root = root;
+       }
+
+       String[] split(String s)
+       {
+       StringTokenizer stok = new StringTokenizer(s, " ");
+       String rv[] = new String[stok.countTokens()];
+       for(int i=0; i<rv.length; i++)
+               rv[i] = stok.nextToken();
+       return rv;
+       }
+
+       String params()
+       {
+       return "path="+FileManager.urlize(path)+
+              "&ro="+FileManager.urlize(ro)+
+              "&rw="+FileManager.urlize(rw)+
+              "&root="+FileManager.urlize(root)+
+              "&desc="+FileManager.urlize(desc);
+       }
+}
+
+class LinuxExport
+{
+       String path;
+       String host[];
+       boolean ro[];
+       int squash[];
+
+       LinuxExport(String l)
+       {
+       StringSplitter tok = new StringSplitter(l, ':');
+       path = tok.nextToken();
+       int c = tok.countTokens() / 3;
+       host = new String[c];
+       ro = new boolean[c];
+       squash = new int[c];
+       for(int i=0; tok.hasMoreTokens(); i++) {
+               host[i] = tok.nextToken();
+               ro[i] = tok.nextToken().equals("1");
+               squash[i] = Integer.parseInt(tok.nextToken());
+               }
+       }
+
+       LinuxExport(String p, String h[], String r[], String s[])
+       {
+       path = p;
+       }
+
+       String params()
+       {
+       String rv = "path="+FileManager.urlize(path)+
+                   "&count="+host.length;
+       for(int i=0; i<host.length; i++) {
+               rv += "&host"+i+"="+FileManager.urlize(host[i]);
+               rv += "&ro"+i+"="+(ro[i] ? 1 : 0);
+               rv += "&squash"+i+"="+squash[i];
+               }
+       return rv;
+       }
+}
+
+class SharingWindow extends FixedFrame implements CbButtonCallback
+{
+       CbButton save_b, cancel_b;
+       RemoteFile file;
+       FileManager filemgr;
+       SambaShare sshare;
+       DFSAdminExport dexport;
+       LinuxExport lexport;
+       Checkbox samba_on, samba_off;
+       Checkbox writable_on, writable_off;
+       Checkbox available_on, available_off;
+       Checkbox guest_on, guest_off, guest_only;
+       TextField comment;
+
+       TextField desc;
+       Checkbox nfs_on, nfs_off;
+       TextField rwhosts, rohosts, roothosts;
+       Checkbox rw[] = new Checkbox[3], ro[] = new Checkbox[3],
+                root[] = new Checkbox[3];
+
+       TextField host[];
+       Choice lro[], squash[];
+
+       SharingWindow(RemoteFile f, FileManager p)
+       {
+       file = f; filemgr = p;
+       setTitle(filemgr.text("share_title", file.path));
+       sshare = (SambaShare)filemgr.stab.get(file.path);
+       Object nshare = filemgr.ntab.get(file.path);
+       if (filemgr.nfsmode == 1)
+               lexport = (LinuxExport)nshare;
+       else if (filemgr.nfsmode == 2)
+               dexport = (DFSAdminExport)nshare;
+
+       // setup UI
+       setLayout(new BorderLayout());
+       Panel samba = new Panel(), sl = new Panel(), sr = new Panel();
+       samba.setLayout(new BorderLayout());
+       Panel st = new Panel();
+       st.setLayout(new GridLayout(2, 1));
+       CheckboxGroup sg = new CheckboxGroup();
+       st.add(samba_off = new Checkbox(filemgr.text("share_soff"), sg, 
+                                      sshare == null));
+       st.add(samba_on = new Checkbox(filemgr.text("share_son"), sg,
+                                       sshare != null));
+       samba.add("North", st);
+
+       Panel stop = new LinedPanel(filemgr.text("share_sheader"));
+       setup_leftright(stop, sl, sr);
+
+       comment = new TextField(sshare == null ? "" : sshare.comment, 25);
+       comment.setFont(filemgr.fixed);
+       add_item(filemgr.text("share_comment"), comment, sl, sr);
+
+       Panel ap = new Panel();
+       ap.setLayout(new GridLayout(1, 0));
+       CheckboxGroup ag = new CheckboxGroup();
+       ap.add(available_on = new Checkbox(filemgr.text("yes"), ag,
+                                         sshare == null || sshare.available));
+       ap.add(available_off = new Checkbox(filemgr.text("no"), ag,
+                                         sshare != null && !sshare.available));
+       add_item(filemgr.text("share_available"), ap, sl, sr);
+
+       Panel wp = new Panel();
+       wp.setLayout(new GridLayout(1, 0));
+       CheckboxGroup wg = new CheckboxGroup();
+       wp.add(writable_on = new Checkbox(filemgr.text("yes"), wg,
+                                         sshare == null || sshare.writable));
+       wp.add(writable_off = new Checkbox(filemgr.text("no"), wg,
+                                          sshare != null && !sshare.writable));
+       add_item(filemgr.text("share_writable"), wp, sl, sr);
+
+       Panel gp = new Panel();
+       gp.setLayout(new GridLayout(1, 0));
+       CheckboxGroup gg = new CheckboxGroup();
+       gp.add(guest_only = new Checkbox(filemgr.text("share_only"), gg,
+                               sshare != null && sshare.guest == 2));
+       gp.add(guest_on = new Checkbox(filemgr.text("yes"), gg,
+                               sshare == null || sshare.guest == 1));
+       gp.add(guest_off = new Checkbox(filemgr.text("no"), gg,
+                               sshare != null && sshare.guest == 0));
+       add_item(filemgr.text("share_guest"), gp, sl, sr);
+
+       samba.add("Center", stop);
+
+       // Setup NFS UI
+       Panel nfs = new Panel(), nl = new Panel(), nr = new Panel();
+       nfs.setLayout(new BorderLayout());
+       Panel nt = new Panel();
+       nt.setLayout(new GridLayout(2, 1));
+       CheckboxGroup ng = new CheckboxGroup();
+       nt.add(nfs_off = new Checkbox(filemgr.text("share_noff"), ng, 
+                                     nshare == null));
+       nt.add(nfs_on = new Checkbox(filemgr.text("share_non"), ng,
+                                    nshare != null));
+       nfs.add("North", nt);
+
+       Panel ntop = new LinedPanel(filemgr.text("share_nheader"));
+       setup_leftright(ntop, nl, nr);
+       if (filemgr.nfsmode == 1) {
+               // Linux export mode
+               nl.setLayout(new GridLayout(0, 1, 2, 2));
+               nr.setLayout(new GridLayout(0, 1, 2, 2));
+               nl.add(new Label(filemgr.text("share_host")));
+               nr.add(new Label(filemgr.text("share_opts")));
+               int c = lexport==null ? 0 : lexport.host.length;
+               host = new TextField[c+1];
+               lro = new Choice[c+1];
+               squash = new Choice[c+1];
+               for(int i=0; i<c; i++) {
+                       host[i] = new TextField(lexport.host[i], 20);
+                       host[i].setFont(filemgr.fixed);
+                       lro[i] = robox(lexport.ro[i]);
+                       squash[i] = squashbox(lexport.squash[i]);
+                       nl.add(host[i]);
+                       nr.add(opts_panel(lro[i], squash[i]));
+                       }
+               host[c] = new TextField("", 20);
+               host[c].setFont(filemgr.fixed);
+               lro[c] = robox(false);
+               squash[c] = squashbox(1);
+               nl.add(host[c]);
+               nr.add(opts_panel(lro[c], squash[c]));
+               }
+       else if (filemgr.nfsmode == 2) {
+               // Solaris share mode
+               desc = new TextField(dexport == null ? "" : dexport.desc, 25);
+               desc.setFont(filemgr.fixed);
+               add_item(filemgr.text("share_desc"), desc, nl, nr);
+
+               rohosts = add_hosts(filemgr.text("share_ro"),
+                                   dexport == null ? "-" : dexport.ro,
+                                   ro, nl, nr);
+               rwhosts = add_hosts(filemgr.text("share_rw"),
+                                   dexport == null ? "-" : dexport.rw,
+                                   rw, nl, nr);
+               roothosts = add_hosts(filemgr.text("share_root"),
+                                   dexport == null ? "-" : dexport.root,
+                                   root, nl, nr);
+               root[1].getParent().remove(root[1]);
+               }
+       else if (filemgr.nfsmode == 3) {
+               }
+       nfs.add("Center", ntop);
+
+       // Add the appropriate tabs
+       if (filemgr.sambamode && filemgr.nfsmode != 0) {
+               TabbedPanel tab = new TabbedPanel();
+               tab.addItem(filemgr.text("share_samba"), samba);
+               tab.addItem(filemgr.text("share_nfs"), nfs);
+               add("Center", tab);
+               }
+       else if (filemgr.sambamode)
+               add("Center", samba);
+       else if (filemgr.nfsmode != 0)
+               add("Center", nfs);
+
+       // Create save and cancel buttons
+       Panel bot = new Panel();
+       bot.setLayout(new FlowLayout(FlowLayout.RIGHT));
+       bot.add(save_b = new CbButton(filemgr.get_image("save.gif"),
+                                     filemgr.text("save"),
+                                     CbButton.LEFT, this));
+       bot.add(cancel_b = new CbButton(filemgr.get_image("cancel.gif"),
+                                       filemgr.text("cancel"),
+                                       CbButton.LEFT, this));
+       add("South", bot);
+       Util.recursiveBody(this);
+       pack();
+       show();
+       }
+
+       public void click(CbButton b)
+       {
+       if (b == save_b) {
+               // Update samba settings on server
+               if (sshare != null && samba_on.getState()) {
+                       // Updating share
+                       sshare.available = available_on.getState();
+                       sshare.writable = writable_on.getState();
+                       sshare.guest = guest_only.getState() ? 2 :
+                                      guest_on.getState() ? 1 : 0;
+                       sshare.comment = comment.getText();
+                       String rv[] = filemgr.get_text(
+                               "save_share.cgi?"+sshare.params());
+                       }
+               else if (sshare != null) {
+                       // Deleting share
+                       String rv[] = filemgr.get_text(
+                               "save_share.cgi?delete=1&"+sshare.params());
+                       filemgr.stab.remove(sshare.path);
+                       }
+               else if (samba_on.getState()) {
+                       // Creating share
+                       sshare = new SambaShare(file.path,
+                                               available_on.getState(),
+                                               writable_on.getState(),
+                                               guest_only.getState() ? 2 :
+                                               guest_on.getState() ? 1 : 0,
+                                               comment.getText());
+                       filemgr.stab.put(sshare.path, sshare);
+                       String rv[] = filemgr.get_text(
+                               "save_share.cgi?new=1&"+sshare.params());
+                       }
+
+               // Update NFS settings on server
+               if (filemgr.nfsmode == 1) {
+                       if (lexport != null && nfs_on.getState()) {
+                               // Updating export
+                               export_options(lexport);
+                               String rv[] = filemgr.get_text(
+                                       "save_export.cgi?"+lexport.params());
+                               }
+                       else if (lexport != null) {
+                               // Deleting export
+                               String rv[] = filemgr.get_text(
+                                 "save_export.cgi?delete=1&"+lexport.params());
+                               filemgr.ntab.remove(lexport.path);
+                               }
+                       else if (nfs_on.getState()) {
+                               // Creating export
+                               lexport = new LinuxExport(file.path, null,
+                                                         null, null);
+                               export_options(lexport);
+                               String rv[] = filemgr.get_text(
+                                 "save_export.cgi?new=1&"+lexport.params());
+                               filemgr.ntab.put(lexport.path, lexport);
+                               }
+                       }
+               else if (filemgr.nfsmode == 2) {
+                       if (dexport != null && nfs_on.getState()) {
+                               // Updating share
+                               dexport.desc = desc.getText();
+                               dexport.ro = ro[0].getState() ? "-" :
+                                            ro[1].getState() ? "" :
+                                            rohosts.getText();
+                               dexport.rw = rw[0].getState() ? "-" :
+                                            rw[1].getState() ? "" :
+                                            rwhosts.getText();
+                               dexport.root = root[0].getState() ? "-" :
+                                              roothosts.getText();
+                               String rv[] = filemgr.get_text(
+                                       "save_export.cgi?"+dexport.params());
+                               }
+                       else if (dexport != null) {
+                               // Deleting share
+                               String rv[] = filemgr.get_text(
+                                 "save_export.cgi?delete=1&"+dexport.params());
+                               filemgr.ntab.remove(dexport.path);
+                               }
+                       else if (nfs_on.getState()) {
+                               // Creating new share
+                               dexport = new DFSAdminExport(file.path,
+                                       desc.getText(),
+                                       ro[0].getState() ? "-" :
+                                       ro[1].getState() ? "" :
+                                       rohosts.getText(),
+                                       rw[0].getState() ? "-" :
+                                       rw[1].getState() ? "" :
+                                       rwhosts.getText(),
+                                       root[0].getState() ? "-" :
+                                       roothosts.getText());
+                               String rv[] = filemgr.get_text(
+                                   "save_export.cgi?new=1&"+dexport.params());
+                               filemgr.ntab.put(dexport.path, dexport);
+                               }
+                       }
+               else if (filemgr.nfsmode == 3) {
+                       }
+
+               filemgr.show_files(filemgr.showing_files);
+               dispose();
+               }
+       else if (b == cancel_b)
+               dispose();
+       }
+
+       void setup_leftright(Panel m, Panel l, Panel r)
+       {
+       m.setLayout(new BorderLayout());
+       Panel p = new Panel();
+       p.setLayout(new BorderLayout());
+       p.add("West", l);
+       p.add("Center", r);
+       l.setLayout(new GridLayout(0, 1));
+       r.setLayout(new GridLayout(0, 1));
+       m.add("North", p);
+       }
+
+       void add_item(String t, Component c, Panel l, Panel r)
+       {
+       l.add(new Label(t));
+       Panel p = new Panel();
+       p.setLayout(new BorderLayout());
+       p.add("West", c);
+       r.add(p);
+       }
+
+       TextField add_hosts(String name, String v, Checkbox cb[],
+                           Panel l, Panel r)
+       {
+       Panel p = new Panel();
+       p.setLayout(new GridLayout(1, 3));
+       CheckboxGroup g = new CheckboxGroup();
+       p.add(cb[0] = new Checkbox(filemgr.text("share_none"), g,
+                                  v.equals("-")));
+       p.add(cb[1] = new Checkbox(filemgr.text("share_all"), g,
+                                  v.length() == 0));
+       p.add(cb[2] = new Checkbox(filemgr.text("share_listed"), g,
+                                  v.length() > 1));
+       add_item(name, p, l, r);
+       TextField t = new TextField(v.equals("-") ? "" : v, 25);
+       t.setFont(filemgr.fixed);
+       add_item("", t, l, r);
+       return t;
+       }
+
+       Choice squashbox(int s)
+       {
+       Choice rv = new Choice();
+       rv.addItem(filemgr.text("share_s0"));
+       rv.addItem(filemgr.text("share_s1"));
+       rv.addItem(filemgr.text("share_s2"));
+       rv.select(s);
+       return rv;
+       }
+
+       Choice robox(boolean r)
+       {
+       Choice rv = new Choice();
+       rv.addItem(filemgr.text("share_lrw"));
+       rv.addItem(filemgr.text("share_lro"));
+       rv.select(r ? 1 : 0);
+       return rv;
+       }
+
+       Panel opts_panel(Component ro, Component squash)
+       {
+       Panel p = new Panel();
+       p.setLayout(new BorderLayout());
+       p.add("West", ro);
+       p.add("East", squash);
+       return p;
+       }
+
+       void export_options(LinuxExport e)
+       {
+       int c = 0;
+       for(int i=0; i<host.length; i++)
+               if (host[i].getText().length() > 0)
+                       c++;
+       e.host = new String[c];
+       e.ro = new boolean[c];
+       e.squash = new int[c];
+       for(int i=0,j=0; i<host.length; i++) {
+               if (host[i].getText().trim().length() > 0) {
+                       e.host[j] = host[i].getText();
+                       e.ro[j] = lro[i].getSelectedIndex() == 1;
+                       e.squash[j] = squash[i].getSelectedIndex();
+                       j++;
+                       }
+               }
+       }
+
+}
+
+class SearchWindow extends FixedFrame
+       implements CbButtonCallback,MultiColumnCallback
+{
+       TabbedPanel tab;
+       MultiColumn list;
+       CbButton search_b, cancel_b, down_b;
+       FileManager filemgr;
+       TextField dir, match, user, group;
+       Checkbox uany, usel, gany, gsel;
+       Choice type;
+       Checkbox sany, smore, sless;
+       TextField more, less;
+       Checkbox xon, xoff;
+       String types[] = { "", "f", "d", "l", "p" };
+       TextField cont;
+       RemoteFile results[];
+
+       SearchWindow(String d, FileManager p)
+       {
+       filemgr = p;
+       setTitle(filemgr.text("search_title"));
+
+       // setup UI
+       setLayout(new BorderLayout());
+       tab = new TabbedPanel();
+       Panel search = new Panel();
+       search.setLayout(new BorderLayout());
+       tab.addItem(filemgr.text("search_crit"), search);
+       Panel l = new Panel(), r = new Panel();
+       l.setLayout(new GridLayout(0, 1));
+       r.setLayout(new GridLayout(0, 1));
+
+       String cols[] = { "", filemgr.text("right_name"),
+                         filemgr.text("right_size") };
+       float widths[] = { .07f, .78f, .15f };
+       list = new MultiColumn(cols, this);
+       list.setWidths(widths);
+       list.setDrawLines(false);
+       list.setFont(filemgr.fixed);
+       tab.addItem(filemgr.text("search_list"), list);
+
+       add_item(filemgr.text("search_dir"), dir = new TextField(d, 30), l, r);
+       dir.setFont(filemgr.fixed);
+
+       // Filename
+       add_item(filemgr.text("search_match"), match = new TextField(20), l, r);
+       match.setFont(filemgr.fixed);
+
+       if (filemgr.search_contents) {
+               // File contents
+               add_item(filemgr.text("search_cont"),
+                        cont = new TextField(30), l, r);
+               cont.setFont(filemgr.fixed);
+               }
+
+       // User or group owners
+       if (filemgr.can_users) {
+               Panel up = new Panel();
+               up.setLayout(new FlowLayout(FlowLayout.LEFT, 1, 1));
+               CheckboxGroup ug = new CheckboxGroup();
+               up.add(uany = new Checkbox(filemgr.text("search_any"), ug, true));
+               up.add(usel = new Checkbox("", ug, false));
+               up.add(user = new TextField(10));
+               user.setFont(filemgr.fixed);
+               add_item(filemgr.text("search_user"), up, l, r);
+
+               Panel gp = new Panel();
+               gp.setLayout(new FlowLayout(FlowLayout.LEFT, 1, 1));
+               CheckboxGroup gg = new CheckboxGroup();
+               gp.add(gany = new Checkbox(filemgr.text("search_any"), gg, true));
+               gp.add(gsel = new Checkbox("", gg, false));
+               gp.add(group = new TextField(10));
+               group.setFont(filemgr.fixed);
+               add_item(filemgr.text("search_group"), gp, l, r);
+               }
+
+       // File type
+       if (!filemgr.follow_links) {
+               type = new Choice();
+               for(int i=0; i<types.length; i++)
+                       type.addItem(filemgr.text("search_types_"+types[i]));
+               add_item(filemgr.text("search_type"), type, l, r);
+               }
+
+       // File size
+       CheckboxGroup sg = new CheckboxGroup();
+       add_item(filemgr.text("search_size"),
+                sany = new Checkbox(filemgr.text("search_any"), sg, true),
+                l, r);
+       Panel mp = new Panel();
+       mp.setLayout(new FlowLayout(FlowLayout.LEFT, 1, 1));
+       mp.add(smore = new Checkbox(filemgr.text("search_more"), sg, false));
+       mp.add(more = new TextField(10));
+       more.setFont(filemgr.fixed);
+       add_item("", mp, l, r);
+       Panel lp = new Panel();
+       lp.setLayout(new FlowLayout(FlowLayout.LEFT, 1, 1));
+       lp.add(sless = new Checkbox(filemgr.text("search_less"), sg, false));
+       lp.add(less = new TextField(10));
+       less.setFont(filemgr.fixed);
+       add_item("", lp, l, r);
+
+       if (filemgr.got_filesystems) {
+               // Search past mounts
+               CheckboxGroup xg = new CheckboxGroup();
+               Panel xp = new Panel();
+               xp.setLayout(new FlowLayout(FlowLayout.LEFT, 1, 1));
+               xp.add(xoff = new Checkbox(filemgr.text("yes"), xg, true));
+               xp.add(xon = new Checkbox(filemgr.text("no"), xg, false));
+               add_item(filemgr.text("search_xdev"), xp, l, r);
+               }
+
+       search.add("West", l); search.add("East", r);
+       add("Center", tab);
+
+       // Create search and cancel buttons
+       Panel bot = new Panel();
+       bot.setLayout(new FlowLayout(FlowLayout.RIGHT));
+       bot.add(down_b = new CbButton(filemgr.get_image("down.gif"),
+                                     filemgr.text("search_down"),
+                                     CbButton.LEFT, this));
+       bot.add(search_b = new CbButton(filemgr.get_image("save.gif"),
+                                     filemgr.text("search_ok"),
+                                     CbButton.LEFT, this));
+       bot.add(cancel_b = new CbButton(filemgr.get_image("cancel.gif"),
+                                       filemgr.text("cancel"),
+                                       CbButton.LEFT, this));
+       add("South", bot);
+       Util.recursiveBody(this);
+       pack();
+       show();
+       }
+
+       void add_item(String t, Component c, Panel l, Panel r)
+       {
+       l.add(new Label(t));
+       Panel p = new Panel();
+       p.setLayout(new BorderLayout());
+       p.add("West", c);
+       r.add(p);
+       }
+
+       public void click(CbButton b)
+       {
+       if (b == cancel_b)
+               dispose();
+       else if (b == search_b) {
+               // validate inputs and build search URL
+               String url = "search.cgi";
+               String d = dir.getText().trim();
+               if (d.length() == 0 || d.charAt(0) != '/') {
+                       new ErrorWindow(filemgr.text("search_edir"));
+                       return;
+                       }
+               url += "?dir="+filemgr.urlize(d);
+               String mt = match.getText().trim();
+               if (mt.length() == 0) {
+                       mt = "*";
+                       //new ErrorWindow(filemgr.text("search_ematch"));
+                       //return;
+                       }
+               url += "&match="+filemgr.urlize(mt);
+               if (type != null && type.getSelectedIndex() > 0)
+                       url += "&type="+types[type.getSelectedIndex()];
+               if (usel != null && usel.getState()) {
+                       String u = user.getText().trim();
+                       if (u.length() == 0) {
+                               new ErrorWindow(filemgr.text("search_euser"));
+                               return;
+                               }
+                       url += "&user="+filemgr.urlize(u);
+                       }
+               if (gsel != null && gsel.getState()) {
+                       String g = group.getText().trim();
+                       if (g.length() == 0) {
+                               new ErrorWindow(filemgr.text("search_egroup"));
+                               return;
+                               }
+                       url += "&group="+filemgr.urlize(g);
+                       }
+               if (smore.getState()) {
+                       String m = more.getText().trim();
+                       try { Integer.parseInt(m); }
+                       catch(Exception e) {
+                               new ErrorWindow(filemgr.text("search_esize"));
+                               return;
+                               }
+                       url += "&size=%2B"+m+"c";
+                       }
+               else if (sless.getState()) {
+                       String l = less.getText().trim();
+                       try { Integer.parseInt(l); }
+                       catch(Exception e) {
+                               new ErrorWindow(filemgr.text("search_esize"));
+                               return;
+                               }
+                       url += "&size=%2D"+l+"c";
+                       }
+               if (xon != null && xon.getState())
+                       url += "&xdev=1";
+               if (cont != null && cont.getText().trim().length() > 0)
+                       url += "&cont="+filemgr.urlize(cont.getText());
+
+               // send off the search
+               setCursor(WAIT_CURSOR);
+               String f[] = filemgr.get_text(url);
+               if (f[0].length() > 0) {
+                       new ErrorWindow(f[0]);
+                       return;
+                       }
+               Object rows[][] = new Object[f.length-1][];
+               results = new RemoteFile[f.length-1];
+               for(int i=1; i<f.length; i++) {
+                       RemoteFile r = new RemoteFile(filemgr, f[i], null);
+                       results[i-1] = r;
+                       Object row[] = rows[i-1] = new Object[3];
+                       row[0] = filemgr.get_image(RemoteFile.tmap[r.type]);
+                       row[1] = r.path;
+                       if (r.size < 1000)
+                               row[2] = filemgr.spad(r.size, 5)+" B";
+                       else if (r.size < 1000000)
+                               row[2] = filemgr.spad(r.size/1000, 5)+" kB";
+                       else
+                               row[2] = filemgr.spad(r.size/1000000, 5)+" MB";
+                       }
+               list.clear();
+               list.addItems(rows);
+               tab.select(filemgr.text("search_list"));
+               setCursor(DEFAULT_CURSOR);
+               }
+       else if (b == down_b) {
+               // Download selected file (if any)
+               int num = list.selected();
+               if (num < 0 || results.length == 0) {
+                       new ErrorWindow(filemgr.text("search_edown"));
+                       return;
+                       }
+               filemgr.download_file(results[num]);
+               }
+       }
+
+       public void singleClick(MultiColumn list, int num)
+       {
+       }
+
+       // go to the directory of the double-clicked file
+       public void doubleClick(MultiColumn list, int num)
+       {
+       RemoteFile f = results[num];
+       int sl = f.path.lastIndexOf('/');
+       String dir = sl == 0 ? "/" : f.path.substring(0, sl);
+       filemgr.find_directory(dir, true);
+       RemoteFile l[] = filemgr.showing_list;
+       for(int i=0; i<l.length; i++) {
+               if (l[i].name.equals(f.name)) {
+                       // select the file in the list
+                       filemgr.files.select(i+1);
+                       filemgr.files.scrollto(i+1);
+                       break;
+                       }
+               }
+       dispose();
+       }
+
+       public void headingClicked(MultiColumn list, int col)
+       {
+       }
+}
+
+// A popup window showing previously entered paths
+class HistoryWindow extends FixedFrame
+       implements CbButtonCallback,ActionListener
+{
+
+       java.awt.List hlist;
+       CbButton ok_b, cancel_b;
+       FileManager filemgr;
+
+       HistoryWindow(FileManager p)
+       {
+       filemgr = p;
+       setTitle(filemgr.text("history_title"));
+
+       // Setup UI
+       hlist = new java.awt.List();
+       for(int i=0; i<filemgr.history_list.size(); i++) {
+               hlist.add((String)filemgr.history_list.elementAt(i));
+               }
+       hlist.addActionListener(this);
+       setLayout(new BorderLayout());
+       add("Center", hlist);
+       Panel bot = new Panel();
+       bot.setLayout(new FlowLayout(FlowLayout.RIGHT));
+       bot.add(ok_b = new CbButton(filemgr.get_image("save.gif"),
+                                   filemgr.text("history_ok"),
+                                   CbButton.LEFT, this));
+       bot.add(cancel_b = new CbButton(filemgr.get_image("cancel.gif"),
+                                        filemgr.text("cancel"),
+                                        CbButton.LEFT, this));
+       add("South", bot);
+        Util.recursiveBody(this);
+        pack();
+        show();
+       }
+
+       public void click(CbButton b)
+       {
+       if (b == cancel_b)
+               dispose();
+       else if (b == ok_b) {
+               // Go to the selected directory
+               String p = hlist.getSelectedItem();
+               if (p != null) {
+                       filemgr.find_directory(p, true);
+                       dispose();
+                       }
+               }
+       }
+
+       public void actionPerformed(ActionEvent e)
+       {
+       // List entry double-clicked .. go to it
+       String p = hlist.getSelectedItem();
+       filemgr.find_directory(p, true);
+       dispose();
+       }
+
+       public Dimension minimumSize()
+       {
+       return new Dimension(300, 300);
+       }
+}
+
+class FileSystem
+{
+       String mount;
+       String dev;
+       String type;
+       String opts[];
+       boolean acls;
+       boolean attrs;
+       boolean ext;
+       boolean mtab, fstab;
+
+       FileSystem(String l)
+       {
+       StringSplitter tok = new StringSplitter(l, ' ');
+       mount = tok.nextToken();
+       dev = tok.nextToken();
+       type = tok.nextToken();
+       String optstr = tok.nextToken();
+       acls = tok.nextToken().equals("1");
+       attrs = tok.nextToken().equals("1");
+       ext = tok.nextToken().equals("1");
+       mtab = tok.nextToken().equals("1");
+       fstab = tok.nextToken().equals("1");
+
+       StringTokenizer tok2 = new StringTokenizer(optstr, ",");
+       opts = new String[tok2.countTokens()];
+       for(int i=0; i<opts.length; i++)
+               opts[i] = tok2.nextToken();
+       }
+}
+
+class ACLEntry
+{
+       FileManager filemgr;
+       RemoteFile file;
+       boolean def;
+       String type;
+       String owner;
+       boolean read, write, exec;
+
+       ACLEntry(String l, ACLWindow w)
+       {
+       filemgr = w.filemgr;
+       file = w.file;
+       StringSplitter tok = new StringSplitter(l, ':');
+       type = tok.nextToken();
+       if (type.equals("default")) {
+               def = true;
+               type = tok.nextToken();
+               }
+       if (!type.equals("mask") && !type.equals("other")) {
+               owner = tok.nextToken();
+               if (owner.length() == 0)
+                       owner = null;
+               }
+       String rwx = tok.nextToken();
+       if (rwx.length() == 0)
+               rwx = tok.nextToken();  // getfacl outputs a blank owner for mask
+                                       // and other on some systems
+       read = (rwx.charAt(0) == 'r');
+       write = (rwx.charAt(1) == 'w');
+       exec = (rwx.charAt(2) == 'x');
+       }
+
+       ACLEntry(ACLWindow w)
+       {
+       filemgr = w.filemgr;
+       file = w.file;
+       }
+
+       String[] getRow()
+       {
+       String rv[] = new String[3];
+       String t = def ? "acltype_default_"+type : "acltype_"+type;
+       rv[0] = filemgr.text(t);
+       if (type.equals("mask") || type.equals("other") ||
+           (def && owner == null))
+               rv[1] = "";
+       else if (owner != null)
+               rv[1] = owner;
+       else if (type.equals("user"))
+               rv[1] = filemgr.text("eacl_user", file.user);
+       else
+               rv[1] = filemgr.text("eacl_group", file.group);
+       rv[2] = "";
+       if (read) rv[2] += filemgr.text("info_read")+" ";
+       if (write) rv[2] += filemgr.text("info_write")+" ";
+       if (exec) rv[2] += filemgr.text("info_exec")+" ";
+       return rv;
+       }
+
+       public String toString()
+       {
+       String rv = def ? "default:" : "";
+       rv += type+":";
+       if (!type.equals("mask") && !type.equals("other"))
+               rv += (owner == null ? "" : owner)+":";
+       rv += (read ? 'r' : '-');
+       rv += (write ? 'w' : '-');
+       rv += (exec ? 'x' : '-');
+       return rv;
+       }
+}
+
+class ACLEditor extends FixedFrame implements CbButtonCallback
+{
+       FileManager filemgr;
+       ACLWindow aclwin;
+       ACLEntry acl;
+       boolean creating;
+       CbButton ok, del;
+       Checkbox read, write, exec, owner1, owner2;
+       TextField owner;
+
+       // Editing an existing ACL entry
+       ACLEditor(ACLWindow w, ACLEntry a)
+       {
+       aclwin = w;
+       filemgr = aclwin.filemgr;
+       acl = a;
+       creating = false;
+       makeUI();
+       }
+
+       // Creating a new ACL entry
+       ACLEditor(ACLWindow w, String type, boolean def)
+       {
+       aclwin = w;
+       filemgr = aclwin.filemgr;
+       acl = new ACLEntry(aclwin);
+       acl.def = def;
+       acl.type = type;
+       creating = true;
+       makeUI();
+       }
+
+       void makeUI()
+       {
+       setTitle(filemgr.text(creating ? "eacl_create" : "eacl_edit"));
+       setLayout(new BorderLayout());
+       Panel left = new Panel();
+       left.setLayout(new GridLayout(0, 1));
+       add("West", left);
+       Panel right = new Panel();
+       right.setLayout(new GridLayout(0, 1));
+       add("East", right);
+
+       left.add(new Label(filemgr.text("eacl_acltype")));
+       TextField type;
+       right.add(type = new TextField(
+                               (acl.def ? "default " : "")+acl.type, 20));
+       type.setEditable(false);
+       type.setFont(filemgr.fixed);
+
+       if (!acl.type.equals("mask") && !acl.type.equals("other")) {
+               left.add(new Label(filemgr.text("eacl_aclname")));
+               if (acl.def) {
+                       // A default user or group ACL .. can be for
+                       // a specific user, or for the file owner
+                       Panel op = new Panel();
+                       op.setLayout(new FlowLayout(FlowLayout.LEFT, 0, 0));
+                       CheckboxGroup gr = new CheckboxGroup();
+                       op.add(owner1 = new Checkbox(filemgr.text("eacl_owner"),
+                                           gr, acl.owner == null));
+                       op.add(owner2 = new Checkbox("",
+                                           gr, acl.owner != null));
+                       op.add(owner = new TextField(
+                               acl.owner == null ? "" : acl.owner, 20));
+                       owner.setFont(filemgr.fixed);
+                       right.add(op);
+                       }
+               else if (creating || acl.owner != null) {
+                       // A user or group ACL for a specific user
+                       owner = new TextField(
+                                       acl.owner == null ? "" : acl.owner, 20);
+                       owner.setFont(filemgr.fixed);
+                       right.add(owner);
+                       }
+               else {
+                       // A user or group ACL for the file owner
+                       String str;
+                       if (acl.type.equals("user"))
+                           str = filemgr.text("eacl_user", aclwin.file.user);
+                       else
+                           str = filemgr.text("eacl_group", aclwin.file.group);
+                       TextField o = new TextField(str);
+                       o.setEditable(false);
+                       o.setFont(filemgr.fixed);
+                       right.add(o);
+                       }
+               }
+
+       left.add(new Label(filemgr.text("eacl_aclperms")));
+       Panel pp = new Panel();
+       pp.setLayout(new FlowLayout(FlowLayout.RIGHT));
+       pp.add(read = new Checkbox(filemgr.text("info_read"), null, acl.read));
+       pp.add(write = new Checkbox(filemgr.text("info_write"), null, acl.write));
+       pp.add(exec = new Checkbox(filemgr.text("info_exec"), null, acl.exec));
+       right.add(pp);
+
+       Panel bot = new Panel();
+       bot.setLayout(new FlowLayout(FlowLayout.RIGHT));
+       bot.add(ok = new CbButton(filemgr.get_image("save.gif"),
+                                 filemgr.text("save"),
+                                 CbButton.LEFT, this));
+       if (!creating && (acl.owner != null || acl.def))
+               bot.add(del = new CbButton(filemgr.get_image("cancel.gif"),
+                                          filemgr.text("delete"),
+                                          CbButton.LEFT, this));
+       add("South", bot);
+
+       Util.recursiveBody(this);
+       pack();
+       show();
+       }
+
+       public void click(CbButton b)
+       {
+       if (b == ok) {
+               // Update or add the ACL entry
+               if (owner1 != null && owner1.getState()) {
+                       acl.owner = null;
+                       }
+               else if (owner != null) {
+                       String o = owner.getText().trim();
+                       if (o.length() == 0 && !acl.def) {
+                               new ErrorWindow(filemgr.text("eacl_eowner"));
+                               return;
+                               }
+                       acl.owner = owner.getText();
+                       if (acl.owner.length() == 0)
+                               acl.owner = null;
+                       }
+               acl.read = read.getState();
+               acl.write = write.getState();
+               acl.exec = exec.getState();
+               if (creating) {
+                       // Add to the ACL table
+                       aclwin.acllist.addElement(acl);
+                       aclwin.acltable.addItem(acl.getRow());
+                       }
+               else {
+                       // Update the table
+                       int idx = aclwin.acllist.indexOf(acl);
+                       aclwin.acltable.modifyItem(acl.getRow(), idx);
+                       }
+               dispose();
+               }
+       else if (b == del) {
+               // Remove this entry
+               int idx = aclwin.acllist.indexOf(acl);
+               aclwin.acllist.removeElementAt(idx);
+               aclwin.acltable.deleteItem(idx);
+               dispose();
+               }
+       }
+
+       public void dispose()
+       {
+       aclwin.edmap.remove(acl);
+       super.dispose();
+       }
+}
+
+class ACLWindow extends FixedFrame implements CbButtonCallback,MultiColumnCallback
+{
+       FileManager filemgr;
+       RemoteFile file;
+       Vector acllist = new Vector();
+       Hashtable edmap = new Hashtable();
+
+       CbButton ok, cancel, add;
+       Choice addtype;
+       MultiColumn acltable;
+
+       String acltypes[] = { "user", "group", "mask",
+                             "default user", "default group", "default other",
+                             "default mask" };
+
+       ACLWindow(FileManager p, RemoteFile f)
+       {
+       super(400, 300);
+       setTitle(p.text("eacl_title", f.path));
+       filemgr = p;
+       file = f;
+
+       // Get the ACLs
+       String a[] = filemgr.get_text(
+                       "getfacl.cgi?file="+filemgr.urlize(file.path));
+       if (a[0].length() != 0) {
+               new ErrorWindow(filemgr.text("eacl_eacls", a[0]));
+               return;
+               }
+
+       // Create the UI
+       setLayout(new BorderLayout());
+       String titles[] = { filemgr.text("eacl_acltype"),
+                           filemgr.text("eacl_aclname"),
+                           filemgr.text("eacl_aclperms") };
+       acltable = new MultiColumn(titles, this);
+       for(int i=1; i<a.length; i++) {
+               ACLEntry acl = new ACLEntry(a[i], this);
+               acllist.addElement(acl);
+               acltable.addItem(acl.getRow());
+               }
+       add("Center", acltable);
+       Panel abot = new Panel();
+       abot.setLayout(new FlowLayout(FlowLayout.RIGHT));
+       abot.add(add = new CbButton(filemgr.get_image("add.gif"),
+                                  filemgr.text("eacl_add"),
+                                  CbButton.LEFT, this));
+       int len = file.type == RemoteFile.DIR ? acltypes.length : 3;
+       abot.add(addtype = new Choice());
+       for(int i=0; i<len; i++) {
+               String t = "acltype_"+acltypes[i].replace(' ', '_');
+               addtype.addItem(filemgr.text(t));
+               }
+       abot.add(new Label(" "));
+       abot.add(ok = new CbButton(filemgr.get_image("save.gif"),
+                                  filemgr.text("save"),
+                                  CbButton.LEFT, this));
+       abot.add(cancel = new CbButton(filemgr.get_image("cancel.gif"),
+                                      filemgr.text("cancel"),
+                                      CbButton.LEFT, this));
+       add("South", abot);
+
+       Util.recursiveBody(this);
+       pack();
+       show();
+       }
+
+       public void click(CbButton b)
+       {
+       if (b == ok) {
+               // Check if there are any defaults, and if so there must
+               // be default user, group and other
+               boolean anydef = false, defuser = false,
+                       defgroup = false, defother = false;
+               for(int i=0; i<acllist.size(); i++) {
+                       ACLEntry e = (ACLEntry)acllist.elementAt(i);
+                       if (e.def) anydef = true;
+                       if (e.def && e.owner == null) {
+                               if (e.type.equals("user")) defuser = true;
+                               if (e.type.equals("group")) defgroup = true;
+                               if (e.type.equals("other")) defother = true;
+                               }
+                       }
+               if (anydef && (!defuser || !defgroup || !defother)) {
+                       new ErrorWindow(filemgr.text("eacl_edefaults"));
+                       return;
+                       }
+
+               // Save the ACLs
+               String aclstr = "";
+               for(int i=0; i<acllist.size(); i++)
+                       aclstr += (ACLEntry)acllist.elementAt(i)+"\n";
+               String rv[] = filemgr.get_text("setfacl.cgi?file="+
+                                               filemgr.urlize(file.path)+
+                                               "&acl="+filemgr.urlize(aclstr));
+               if (rv[0].length() > 0)
+                       new ErrorWindow(filemgr.text("eacl_efailed",
+                               file.path, rv[0]));
+               else
+                       dispose();
+               }
+       else if (b == add) {
+               // Open a window for a new ACL entry
+               String t = acltypes[addtype.getSelectedIndex()];
+               String d = "default ";
+               boolean def = t.startsWith(d);
+               if (def)
+                       t = t.substring(d.length());
+               if (t.equals("mask")) {
+                       // Only allow one mask
+                       for(int i=0; i<acllist.size(); i++) {
+                               ACLEntry a = (ACLEntry)acllist.elementAt(i);
+                               if (a.type.equals(t) && a.def == def) {
+                                       new ErrorWindow(filemgr.text(def ?
+                                           "eacl_edefmask" : "eacl_emask"));
+                                       return;
+                                       }
+                               }
+                       }
+               new ACLEditor(this, t, def);
+               }
+       else if (b == cancel) {
+               // Don't save
+               dispose();
+               }
+       }
+
+       // Bring up an editor for an ACL
+        public void doubleClick(MultiColumn list, int num)
+       {
+       int idx = list.selected();
+       if (idx >= 0) {
+               ACLEntry e = (ACLEntry)acllist.elementAt(idx);
+               ACLEditor ed = (ACLEditor)edmap.get(e);
+               if (ed == null)
+                       edmap.put(e, new ACLEditor(this, e));
+               else {
+                       ed.toFront();
+                       ed.requestFocus();
+                       }
+               }
+       }
+
+        public void singleClick(MultiColumn list, int num)
+       {
+       }
+
+       public void headingClicked(MultiColumn list, int col)
+       {
+       }
+}
+
+class AttributesWindow extends FixedFrame
+       implements CbButtonCallback,MultiColumnCallback
+{
+       FileManager filemgr;
+       RemoteFile file;
+       Vector attrlist = new Vector();
+       Hashtable edmap = new Hashtable();
+
+       CbButton ok, cancel, add;
+       MultiColumn attrtable;
+
+       AttributesWindow(FileManager p, RemoteFile f)
+       {
+       super(400, 300);
+       setTitle(p.text("attr_title", f.path));
+       filemgr = p;
+       file = f;
+
+       // Get the attributes
+       String a[] = filemgr.get_text(
+                       "getattrs.cgi?file="+filemgr.urlize(file.path));
+       if (a[0].length() != 0) {
+               new ErrorWindow(filemgr.text("attr_eattrs", a[0]));
+               return;
+               }
+
+       // Create the UI
+       setLayout(new BorderLayout());
+       String titles[] = { filemgr.text("attr_name"),
+                           filemgr.text("attr_value") };
+       attrtable = new MultiColumn(titles, this);
+       for(int i=1; i<a.length; i++) {
+               FileAttribute at = new FileAttribute(a[i], filemgr);
+               attrlist.addElement(at);
+               attrtable.addItem(at.getRow());
+               }
+       add("Center", attrtable);
+       Panel abot = new Panel();
+       abot.setLayout(new FlowLayout(FlowLayout.RIGHT));
+       abot.add(add = new CbButton(filemgr.get_image("add.gif"),
+                                  filemgr.text("attr_add"),
+                                  CbButton.LEFT, this));
+       abot.add(new Label(" "));
+       abot.add(ok = new CbButton(filemgr.get_image("save.gif"),
+                                  filemgr.text("save"),
+                                  CbButton.LEFT, this));
+       abot.add(cancel = new CbButton(filemgr.get_image("cancel.gif"),
+                                      filemgr.text("cancel"),
+                                      CbButton.LEFT, this));
+       add("South", abot);
+
+       Util.recursiveBody(this);
+       pack();
+       show();
+       }
+
+       public void click(CbButton b)
+       {
+       if (b == ok) {
+               // Save the attributes
+               String pstr = "";
+               for(int i=0; i<attrlist.size(); i++) {
+                       FileAttribute at = (FileAttribute)attrlist.elementAt(i);
+                       pstr += "&name"+i+"="+filemgr.urlize(at.name)+
+                               "&value"+i+"="+filemgr.urlize(at.value);
+                       }
+               String rv[] = filemgr.get_text("setattrs.cgi?file="+
+                                               filemgr.urlize(file.path)+pstr);
+               if (rv[0].length() > 0)
+                       new ErrorWindow(filemgr.text("attr_efailed",
+                               file.path, rv[0]));
+               else
+                       dispose();
+               }
+       else if (b == add) {
+               // Open a window for a new ACL entry
+               new AttributeEditor(this);
+               }
+       else if (b == cancel) {
+               // Don't save
+               dispose();
+               }
+       }
+
+       // Bring up an editor for an ACL
+        public void doubleClick(MultiColumn list, int num)
+       {
+       int idx = list.selected();
+       if (idx >= 0) {
+               FileAttribute at = (FileAttribute)attrlist.elementAt(idx);
+               AttributeEditor ed = (AttributeEditor)edmap.get(at);
+               if (ed == null)
+                       edmap.put(at, new AttributeEditor(this, at));
+               else {
+                       ed.toFront();
+                       ed.requestFocus();
+                       }
+               }
+       }
+
+        public void singleClick(MultiColumn list, int num)
+       {
+       }
+
+       public void headingClicked(MultiColumn list, int col)
+       {
+       }
+}
+
+class FileAttribute
+{
+       String name;
+       String value;
+
+       FileAttribute(String l, FileManager f)
+       {
+       int eq = l.indexOf('=');
+       name = f.un_urlize(l.substring(0, eq));
+       value = f.un_urlize(l.substring(eq+1));
+       }
+
+       FileAttribute(String n, String v)
+       {
+       name = n;
+       value = v;
+       }
+
+       String[] getRow()
+       {
+       return new String[] { name, value };
+       }
+}
+
+class AttributeEditor extends FixedFrame implements CbButtonCallback
+{
+       FileManager filemgr;
+       AttributesWindow attrwin;
+       FileAttribute attr;
+       boolean creating;
+       CbButton ok, del;
+       TextField name;
+       TextArea value;
+
+       AttributeEditor(AttributesWindow w, FileAttribute a)
+       {
+       attrwin = w;
+       attr = a;
+       filemgr = w.filemgr;
+       creating = false;
+       makeUI();
+       }
+
+       AttributeEditor(AttributesWindow w)
+       {
+       attrwin = w;
+       attr = new FileAttribute("", "");
+       filemgr = w.filemgr;
+       creating = true;
+       makeUI();
+       }
+
+       void makeUI()
+       {
+       setTitle(filemgr.text(creating ? "attr_create" : "attr_edit"));
+       setLayout(new BorderLayout());
+
+       Panel top = new Panel();
+       top.setLayout(new GridLayout(1, 2));
+       top.add(new Label(filemgr.text("attr_name")));
+       top.add(name = new TextField(attr.name, 20));
+       name.setFont(filemgr.fixed);
+       add("North", top);
+
+       Panel mid = new Panel();
+       mid.setLayout(new GridLayout(1, 2));
+       mid.add(new Label(filemgr.text("attr_value")));
+       mid.add(value = new TextArea(attr.value, 5, 20));
+       add("Center", mid);
+
+       Panel bot = new Panel();
+       bot.setLayout(new FlowLayout(FlowLayout.RIGHT));
+       bot.add(ok = new CbButton(filemgr.get_image("save.gif"),
+                                 filemgr.text("save"),
+                                 CbButton.LEFT, this));
+       if (!creating)
+               bot.add(del = new CbButton(filemgr.get_image("cancel.gif"),
+                                          filemgr.text("delete"),
+                                          CbButton.LEFT, this));
+       add("South", bot);
+
+       Util.recursiveBody(this);
+       pack();
+       show();
+       }
+
+       public void click(CbButton b)
+       {
+       if (b == ok) {
+               // Update or add the attribute
+               if (name.getText().length() == 0) {
+                       new ErrorWindow(filemgr.text("attr_ename"));
+                       return;
+                       }
+               attr.name = name.getText();
+               attr.value = value.getText();
+               if (creating) {
+                       // Add to the attribs table
+                       attrwin.attrlist.addElement(attr);
+                       attrwin.attrtable.addItem(attr.getRow());
+                       }
+               else {
+                       // Update the table
+                       int idx = attrwin.attrlist.indexOf(attr);
+                       attrwin.attrtable.modifyItem(attr.getRow(), idx);
+                       }
+               dispose();
+               }
+       else if (b == del) {
+               // Remove this entry
+               int idx = attrwin.attrlist.indexOf(attr);
+               attrwin.attrlist.removeElementAt(idx);
+               attrwin.attrtable.deleteItem(idx);
+               dispose();
+               }
+       }
+
+       public void dispose()
+       {
+       attrwin.edmap.remove(attr);
+       super.dispose();
+       }
+}
+
+class EXTWindow extends FixedFrame implements CbButtonCallback
+{
+       FileManager filemgr;
+       RemoteFile file;
+
+       CbButton ok, cancel;
+       Checkbox cbs[];
+
+       String attrs[] = { "A", "a", "c", "d", "i", "s", "S", "u" };
+       Hashtable attrmap = new Hashtable();
+
+       EXTWindow(FileManager p, RemoteFile f)
+       {
+       super();
+       setTitle(p.text("ext_title", f.path));
+       filemgr = p;
+       file = f;
+
+       // Get the attributes
+       String a[] = filemgr.get_text(
+                       "getext.cgi?file="+filemgr.urlize(file.path));
+       if (a[0].length() != 0) {
+               new ErrorWindow(filemgr.text("ext_eattrs", a[0]));
+               return;
+               }
+       for(int i=0; i<a[1].length(); i++)
+               attrmap.put(a[1].substring(i, i+1), "");
+
+       // Create the UI
+       setLayout(new BorderLayout());
+       Panel top = new LinedPanel(filemgr.text("ext_header"));
+       top.setLayout(new GridLayout(0, 1));
+       cbs = new Checkbox[attrs.length];
+       for(int i=0; i<attrs.length; i++) {
+               cbs[i] = new Checkbox(filemgr.text("eattr_"+attrs[i]));
+               cbs[i].setState(attrmap.get(attrs[i]) != null);
+               top.add(cbs[i]);
+               }
+       add("Center", top);
+
+       Panel bot = new Panel();
+       bot.setLayout(new FlowLayout(FlowLayout.RIGHT));
+       bot.add(ok = new CbButton(filemgr.get_image("save.gif"),
+                                 filemgr.text("save"),
+                                 CbButton.LEFT, this));
+       bot.add(cancel = new CbButton(filemgr.get_image("cancel.gif"),
+                                     filemgr.text("cancel"),
+                                     CbButton.LEFT, this));
+       add("South", bot);
+
+       Util.recursiveBody(this);
+       pack();
+       show();
+       }
+
+       public void click(CbButton b)
+       {
+       if (b == ok) {
+               // Save the attributes (including unknown ones)
+               String astr = "";
+               for(int i=0; i<cbs.length; i++) {
+                       if (cbs[i].getState())
+                               astr += attrs[i];
+                       attrmap.remove(attrs[i]);
+                       }
+               for(Enumeration e = attrmap.keys(); e.hasMoreElements(); )
+                       astr += e.nextElement();
+
+               // Try to set on the server
+               String rv[] = filemgr.get_text("setext.cgi?file="+
+                               filemgr.urlize(file.path)+"&attrs="+astr);
+               if (rv[0].length() > 0)
+                       new ErrorWindow(filemgr.text("ext_efailed",
+                               file.path, rv[0]));
+               else
+                       dispose();
+               }
+       else if (b == cancel) {
+               dispose();
+               }
+       }
+}
+
+class MountWindow extends FixedFrame implements CbButtonCallback
+{
+       CbButton yes, no;
+       FileManager filemgr;
+       FileSystem fs;
+       RemoteFile file;
+
+       MountWindow(FileManager filemgr, FileSystem fs, RemoteFile file)
+       {
+       super();
+       setTitle(filemgr.text(fs.mtab ? "mount_title2" : "mount_title1"));
+       this.filemgr = filemgr;
+       this.fs = fs;
+       this.file = file;
+
+       // Create the UI
+       setLayout(new BorderLayout());
+       Panel cen = new BorderPanel(1, Util.body);
+       cen.setLayout(new GridLayout(1, 1));
+       String rusure = fs.mtab ? "mount_rusure2" : "mount_rusure1";
+       cen.add(new Label(filemgr.text(rusure, fs.mount, fs.dev)));
+       add("Center", cen);
+       Panel bot = new Panel();
+       bot.setLayout(new FlowLayout(FlowLayout.CENTER));
+       bot.add(yes = new CbButton(filemgr.text("yes"), this));
+       bot.add(no = new CbButton(filemgr.text("no"), this));
+       add("South", bot);
+       pack();
+       show();
+       Util.recursiveBody(this);
+       }
+
+       public void click(CbButton b)
+       {
+       if (b == yes) {
+               // Go ahread and do it!
+               String rv[] = filemgr.get_text("mount.cgi?dir="+
+                                              filemgr.urlize(fs.mount));
+               dispose();
+               if (rv[0].equals("")) {
+                       // It worked - refresh this directory and the mount list
+                       filemgr.get_filesystems();
+                       FileNode d = (FileNode)filemgr.nodemap.get(file);
+                       if (d != null) {
+                               d.setimage();
+                               d.known = false;
+                               d.file.list = null;
+                               d.fill();
+                               }
+                       if (fs.mtab)
+                               filemgr.show_files(file.directory);
+                       else
+                               filemgr.show_files(filemgr.showing_files);
+                       }
+               else {
+                       // Failed - show the error
+                       new ErrorWindow(filemgr.text(
+                               fs.mtab ? "mount_err2" : "mount_err1",
+                               fs.mount, rv[0]));
+                       }
+               }
+       else {
+               // Just close the window
+               dispose();
+               }
+       }
+}
+
+// A label that is limited to a maximum number of characters wide
+class MultiLabel extends BorderPanel
+{
+       public MultiLabel(String s, int max)
+       {
+       this(s, max, 1);
+       }
+
+       public MultiLabel(String s, int max, int b)
+       {
+       this(s, max, b, Label.CENTER);
+       }
+
+       
+       public MultiLabel(String s, int max, int b, int align)
+       {
+       super(b, Util.body);
+       Vector v = new Vector();
+       StringTokenizer tok = new StringTokenizer(s.trim(), " \t");
+       String line = null;
+       while(tok.hasMoreTokens()) {
+               String w = tok.nextToken();
+               line = (line == null ? w : line+" "+w);
+               if (line.length() > max || !tok.hasMoreTokens()) {
+                       v.addElement(line);
+                       line = null;
+                       }
+               }
+       setLayout(new GridLayout(v.size(), 1, 0, 0));
+       for(int i=0; i<v.size(); i++) {
+               Label l = new Label((String)v.elementAt(i), Label.CENTER);
+               add(l);
+               }
+       }
+}
+
+// A window for choosing the format in which a directory will be downloaded
+class DownloadDirWindow extends FixedFrame implements CbButtonCallback
+{
+       CbButton zip, tgz, tar, cancel;
+       FileManager filemgr;
+       RemoteFile file;
+
+       DownloadDirWindow(FileManager filemgr, RemoteFile file)
+       {
+       super();
+       setTitle(filemgr.text("ddir_title"));
+       this.filemgr = filemgr;
+       this.file = file;
+
+       // Create the UI
+       setLayout(new BorderLayout());
+       Panel cen = new BorderPanel(1, Util.body);
+       cen.setLayout(new GridLayout(1, 1));
+       cen.add(new Label(filemgr.text("ddir_rusure", file.path)));
+       add("Center", cen);
+
+       Panel bot = new Panel();
+       bot.setLayout(new FlowLayout(FlowLayout.CENTER));
+       bot.add(zip = new CbButton(filemgr.text("ddir_zip"), this));
+       bot.add(tgz = new CbButton(filemgr.text("ddir_tgz"), this));
+       bot.add(tar = new CbButton(filemgr.text("ddir_tar"), this));
+       bot.add(cancel = new CbButton(filemgr.text("cancel"), this));
+       add("South", bot);
+       pack();
+       show();
+       Util.recursiveBody(this);
+       }
+
+       public void click(CbButton b)
+       {
+       if (b == cancel) {
+               // just close the window
+               dispose();
+               }
+       else {
+               // open the download window
+               int format = b == zip ? 1 :
+                            b == tgz ? 2 : 3;
+               dispose();
+               filemgr.open_file_window(file, true, format);
+               }
+       }
+}
+
+class PreviewWindow extends Frame implements CbButtonCallback
+{
+       CbButton close_b;
+       RemoteFile file;
+       FileManager filemgr;
+       ImagePanel ip;
+
+       // Previewing a file
+       public PreviewWindow(FileManager p, RemoteFile f)
+       {
+       //super(350, 350);
+       file = f; filemgr = p;
+       makeUI();
+       setTitle(filemgr.text("preview_title", file.path));
+
+       // Load the file
+       try {
+               URL u = new URL(filemgr.getDocumentBase(),
+                               "preview.cgi"+filemgr.urlize(file.path)+
+                               "?rand="+System.currentTimeMillis()+
+                               "&trust="+filemgr.trust+
+                               filemgr.extra);
+               URLConnection uc = u.openConnection();
+               filemgr.set_cookie(uc);
+               int len = uc.getContentLength();
+               InputStream is = uc.getInputStream();
+               byte buf[];
+               if (len >= 0) {
+                       // Length is known
+                       buf = new byte[uc.getContentLength()];
+                       int got = 0;
+                       while(got < buf.length)
+                               got += is.read(buf, got, buf.length-got);
+                       }
+               else {
+                       // Length is unknown .. read till the end
+                       buf = new byte[0];
+                       while(true) {
+                           byte data[] = new byte[16384];
+                           int got;
+                           try { got = is.read(data); }
+                           catch(EOFException ex) { break; }
+                           if (got <= 0) break;
+                           byte nbuf[] = new byte[buf.length + got];
+                           System.arraycopy(buf, 0, nbuf, 0, buf.length);
+                           System.arraycopy(data, 0, nbuf, buf.length, got);
+                           buf = nbuf;
+                           }
+                       }
+
+               // Check if this is really an error
+               if (uc.getContentType().equals("text/plain")) {
+                       String s = new String(buf, 0);
+                       new ErrorWindow(s);
+                       dispose();
+                       return;
+                       }
+
+               // Show the image
+               Image img = Toolkit.getDefaultToolkit().createImage(buf);
+                MediaTracker waiter = new MediaTracker(this);
+                waiter.addImage(img, 666);
+                try { waiter.waitForAll(); }
+                catch(InterruptedException e) { }
+               if (img.getWidth(this) <= 0) {
+                       new ErrorWindow(filemgr.text("preview_bad"));
+                       dispose();
+                       return;
+                       }
+               ip.setImage(img);
+
+               pack();
+               show();
+               }
+       catch(Exception e) { e.printStackTrace(); }
+       }
+
+       void makeUI()
+       {
+       setLayout(new BorderLayout());
+
+       // Image viewing area
+       BorderPanel mid = new BorderPanel(2, Util.body);
+       mid.setLayout(new BorderLayout());
+       ip = new ImagePanel(null);
+       mid.add("Center", ip);
+       add("Center", mid);
+
+       // Button panel
+       Panel bot = new Panel();
+       bot.setLayout(new FlowLayout(FlowLayout.RIGHT));
+       bot.add(close_b = new CbButton(filemgr.get_image("cancel.gif"),
+                                       filemgr.text("close"),
+                                       CbButton.LEFT, this));
+       add("South", bot);
+       Util.recursiveBody(this);
+       }
+
+       public void click(CbButton b)
+       {
+       if (b == close_b) {
+               // Just close
+               dispose();
+               }
+       }
+}
+
+class ImagePanel extends Panel
+{
+       Image img;
+
+       public ImagePanel(Image img)
+       {
+       this.img = img;
+       }
+
+       public void paint(Graphics g)
+       {
+       if (img != null) {
+               g.drawImage(img, 0, 0, this);
+               }
+       }
+
+       public void setImage(Image img)
+       {
+       this.img = img;
+       repaint();
+       }
+
+       public Dimension minimumSize()
+       {
+       return new Dimension(img.getWidth(this), img.getHeight(this));
+       }
+
+       public Dimension preferredSize()
+       {
+       return minimumSize();
+       }
+}
+
+class ExtractWindow extends FixedFrame implements CbButtonCallback
+{
+       CbButton yes, yesdelete, no;
+       FileManager filemgr;
+       RemoteFile file;
+
+       ExtractWindow(FileManager filemgr, RemoteFile file)
+       {
+       super();
+       setTitle(filemgr.text("extract_title"));
+       this.filemgr = filemgr;
+       this.file = file;
+
+       // Create the UI
+       setLayout(new BorderLayout());
+       Panel cen = new BorderPanel(1, Util.body);
+       cen.setLayout(new GridLayout(3, 1));
+       cen.add(new Label(filemgr.text("extract_rusure")));
+       cen.add(new Label(file.path));
+       cen.add(new Label(filemgr.text("extract_rusure2")));
+       add("Center", cen);
+       Panel bot = new Panel();
+       bot.setLayout(new FlowLayout(FlowLayout.CENTER));
+       bot.add(yes = new CbButton(filemgr.text("yes"), this));
+       bot.add(yesdelete = new CbButton(filemgr.text("extract_yes"), this));
+       bot.add(no = new CbButton(filemgr.text("no"), this));
+       add("South", bot);
+       pack();
+       show();
+       Util.recursiveBody(this);
+       }
+
+       public void click(CbButton b)
+       {
+       if (b == yes || b == yesdelete) {
+               // Go ahread and do it!
+               String rv[] = filemgr.get_text("extract.cgi?file="+
+                                      filemgr.urlize(file.path)+
+                                      "&delete="+(b == yesdelete ? 1 : 0));
+               dispose();
+               if (rv[0].equals("")) {
+                       // It worked - refresh the directory
+                       RemoteFile par = file.directory;
+                       FileNode d = (FileNode)filemgr.nodemap.get(par);
+                       if (d != null) {
+                               d.setimage();
+                               d.known = false;
+                               d.file.list = null;
+                               d.fill();
+                               }
+                       filemgr.show_files(filemgr.showing_files);
+                       }
+               else {
+                       // Failed - show the error
+                       new ErrorWindow(filemgr.text("extract_err", rv[0]));
+                       }
+               }
+       else {
+               // Just close the window
+               dispose();
+               }
+       }
+}
+
+
diff --git a/file/FileManager.java.bak b/file/FileManager.java.bak
new file mode 100644 (file)
index 0000000..6f590ae
--- /dev/null
@@ -0,0 +1,3497 @@
+import java.awt.*;
+import java.io.*;
+import java.applet.*;
+import java.net.*;
+import java.util.*;
+import netscape.javascript.JSObject;
+
+// A java filemanager that allows the user to manipulate files on the
+// Webmin server. Layout is similar to the windows explorer - directory
+// tree on the left, files on the right, action buttons on the top.
+public class FileManager extends Applet
+       implements CbButtonCallback, HierarchyCallback, MultiColumnCallback
+{
+       // top buttons
+       CbButton ret_b, down_b, edit_b, refresh_b, props_b,
+                copy_b, cut_b, paste_b, delete_b, new_b, upload_b, mkdir_b,
+                makelink_b, rename_b, share_b, search_b, acl_b, attr_b, ext_b;
+
+       // Directory tree
+       Hierarchy dirs;
+       FileNode root;
+       Hashtable nodemap = new Hashtable();
+
+       // File list
+       MultiColumn files;
+       TextField pathname;
+       RemoteFile showing_files;
+       RemoteFile showing_list[];
+
+       // Copying and pasting
+       RemoteFile cut_buffer[];
+       boolean cut_mode;
+
+       static final String monmap[] = { "Jan", "Feb", "Mar", "Apr",
+                                        "May", "Jun", "Jul", "Aug",
+                                        "Sep", "Oct", "Nov", "Dec" };
+       String accroot[];
+       Hashtable lang = new Hashtable();
+       Hashtable stab = new Hashtable(),
+                 ntab = new Hashtable();
+       boolean sambamode;
+       int nfsmode;
+       String trust;
+
+       boolean got_filesystems;
+       Vector fslist = new Vector();
+       boolean read_only = false;
+
+       public void init()
+       {
+       setLayout(new BorderLayout());
+       StringTokenizer tok = new StringTokenizer(getParameter("root"), " ");
+       accroot = new String[tok.countTokens()];
+       for(int i=0; tok.hasMoreTokens(); i++)
+               accroot[i] = tok.nextToken();
+       trust = getParameter("trust");
+
+       // download language strings
+       String l[] = get_text("lang.cgi");
+       for(int i=0; i<l.length; i++) {
+               int eq = l[i].indexOf('=');
+               if (eq >= 0)
+                       lang.put(l[i].substring(0, eq), l[i].substring(eq+1));
+               }
+
+       // list samba file shares
+       String s[] = get_text("list_shares.cgi");
+       if (s[0].equals("1")) {
+               for(int i=1; i<s.length; i++) {
+                       SambaShare ss = new SambaShare(s[i]);
+                       stab.put(ss.path, ss);
+                       }
+               sambamode = true;
+               }
+
+       // list NFS exports
+       String e[] = get_text("list_exports.cgi");
+       nfsmode = e.length == 0 ? 0 : Integer.parseInt(e[0]);
+       if (nfsmode != 0) {
+               for(int i=1; i<e.length; i++) {
+                       if (nfsmode == 1) {
+                               // Linux export
+                               LinuxExport le = new LinuxExport(e[i]);
+                               ntab.put(le.path, le);
+                               }
+                       else if (nfsmode == 2) {
+                               // Solaris share
+                               DFSAdminExport de = new DFSAdminExport(e[i]);
+                               ntab.put(de.path, de);
+                               }
+                       }
+               }
+
+       // list filesystems
+       String f[] = get_text("filesystems.cgi");
+       got_filesystems = f[0].equals("1");
+       boolean acl_support = false,
+               attr_support = false,
+               ext_support = false;
+       if (got_filesystems) {
+               for(int i=1; i<f.length; i++) {
+                       FileSystem fs = new FileSystem(f[i]);
+                       fslist.addElement(fs);
+                       if (fs.acls) acl_support = true;
+                       if (fs.attrs) attr_support = true;
+                       if (fs.ext) ext_support = true;
+                       }
+               }
+
+       // get read-only flag
+       if (getParameter("ro").equals("1"))
+               read_only = true;
+
+       // create button panel
+       BorderPanel top = new BorderPanel(2);
+       top.setLayout(new FlowLayout(FlowLayout.LEFT, 2, 2));
+
+       Panel top1 = new Panel();
+       top1.setLayout(new GridLayout(1, 0));
+       if (getParameter("return") != null)
+               top1.add(ret_b = make_button("ret.gif", text("top_ret")));
+       top1.add(down_b = make_button("down.gif", text("top_down")));
+       if (!read_only)
+               top1.add(edit_b = make_button("edit.gif", text("top_edit")));
+       top1.add(refresh_b = make_button("refresh.gif", text("top_refresh")));
+       if (!read_only)
+               top1.add(props_b = make_button("props.gif", text("top_info")));
+       if (acl_support && !read_only)
+               top1.add(acl_b = make_button("acl.gif", text("top_eacl")));
+       if (attr_support && !read_only)
+               top1.add(attr_b = make_button("attr.gif", text("top_attr")));
+       if (ext_support && !read_only)
+               top1.add(ext_b = make_button("ext.gif", text("top_ext")));
+       top1.add(search_b = make_button("search.gif", text("top_search")));
+       top.add(top1);
+       top.add(new Label(""));
+       
+       if (!read_only) {
+               Panel top2 = new Panel();
+               top2.setLayout(new GridLayout(1, 0));
+               top2.add(delete_b = make_button("cancel.gif",
+                                               text("top_delete")));
+               top2.add(new_b = make_button("new.gif", text("top_new")));
+               top2.add(upload_b = make_button("upload.gif",
+                                               text("top_upload")));
+               top2.add(mkdir_b = make_button("mkdir.gif", text("top_new")));
+               if (!getParameter("follow").equals("1"))
+                       top2.add(makelink_b = make_button("makelink.gif",
+                                               text("top_new")));
+               top2.add(rename_b = make_button("rename.gif",
+                                               text("top_rename")));
+               if ((sambamode || nfsmode != 0) &&
+                   getParameter("sharing").equals("1"))
+                       top2.add(share_b = make_button("share.gif",
+                                                      text("top_share")));
+               top.add(top2);
+               top.add(new Label(""));
+
+               Panel top3 = new Panel();
+               top3.setLayout(new GridLayout(1, 0));
+               top3.add(copy_b = make_button("copy.gif", text("top_copy")));
+               top3.add(cut_b = make_button("cut.gif", text("top_cut")));
+               top3.add(paste_b = make_button("paste.gif", text("top_paste")));
+               top.add(top3);
+               }
+       add("North", top);
+
+       // create directory tree
+       BorderPanel left = new BorderPanel(2);
+       left.setLayout(new BorderLayout());
+       root = new FileNode(new RemoteFile(this, get_text("root.cgi")[0],null));
+       left.add("Center", dirs = new Hierarchy(root, this));
+       root.open = true; root.fill();
+
+       // create file window
+       BorderPanel right = new BorderPanel(2);
+       right.setLayout(new BorderLayout());
+       right.add("North", pathname = new TextField());
+       //pathname.setFont(new Font("courier", Font.PLAIN, 8));
+       String cols[] = { "", text("right_name"), text("right_size"),
+                         text("right_user"), text("right_group"),
+                         text("right_date") };
+       float widths[] = { .07f, .33f, .15f, .15f, .15f, .15f };
+       right.add("Center", files = new MultiColumn(cols, this));
+       files.setWidths(widths);
+       files.setDrawLines(false);
+       files.setMultiSelect(true);
+       show_files(root.file);
+
+       ResizePanel mid = new ResizePanel(left, right, .3, false);
+       add("Center", mid);
+
+       // Go to the restricted directory
+       String home = getParameter("home");
+       String go = getParameter("goto");
+       String open = getParameter("open");
+       if (open != null) {
+               find_directory(open, true);
+               }
+       elsif (go != null && go.equals("1")) {
+               if (home != null)
+                       find_directory(home, true);
+               else if (!accroot[0].equals("/"))
+                       find_directory(accroot[0], true);
+               }
+       }
+
+       CbButton make_button(String f, String t)
+       {
+       if (size().width < 700 && size().width > 1)
+               return new CbButton(get_image(f), this);
+       else
+               return new CbButton(get_image(f), t, CbButton.ABOVE, this);
+       }
+
+       // Gets an image from the images directory
+       Image get_image(String img)
+       {
+       return getImage(getDocumentBase(), "images/"+img);
+       }
+
+       String[] get_text(String url)
+       {
+       try {
+               long now = System.currentTimeMillis();
+               if (url.indexOf('?') > 0) url += "&rand="+now;
+               else url += "?rand="+now;
+               url += "&trust="+trust;
+               URL u = new URL(getDocumentBase(), url);
+               Vector lv = new Vector();
+               LineInputStream is = new LineInputStream(u.openStream());
+               while(true)
+                       try { lv.addElement(is.gets()); }
+                       catch(EOFException eof) { break; }
+               is.close();
+               String rv[] = new String[lv.size()];
+               lv.copyInto(rv);
+               return rv;
+               }
+       catch(Exception e) {
+               e.printStackTrace();
+               //return null;
+               String err[] = { e.getMessage() };
+               return err;
+               }
+       }
+
+       // Fill the multicolumn list with files from some directory
+       boolean show_files(RemoteFile f)
+       {
+       RemoteFile fl[] = f.list();
+       if (fl == null) return false;
+       files.clear();
+       Object rows[][] = new Object[fl.length+1][];
+       long now = System.currentTimeMillis();
+
+       // Sort listing by chosen column
+       if (f != showing_files) {
+               // Directory has changed .. assume sort by name
+               files.sortingArrow(1, 1);
+               }
+       else if (files.sortdir != 0) {
+               // Sort by chosen order
+               RemoteFile fls[] = new RemoteFile[fl.length];
+               System.arraycopy(fl, 0, fls, 0, fl.length);
+               QuickSort.sort(fls, files.sortcol, files.sortdir);
+               fl = fls;
+               }
+
+       // Create parent directory row
+       rows[0] = new Object[6];
+       rows[0][0] = get_image("dir.gif");
+       rows[0][1] = "..";
+       rows[0][2] = rows[0][3] = rows[0][4] = rows[0][5] = "";
+
+       // Create file rows
+       Date n = new Date(now);
+       for(int i=0; i<fl.length; i++) {
+               Object row[] = rows[i+1] = new Object[6];
+               if (fl[i].shared())
+                       row[0] = get_image("sdir.gif");
+               else
+                       row[0] = get_image(RemoteFile.tmap[fl[i].type]);
+               row[1] = fl[i].name;
+               if (fl[i].size < 1000)
+                       row[2] = spad(fl[i].size, 5)+" B";
+               else if (fl[i].size < 1000000)
+                       row[2] = spad(fl[i].size/1000, 5)+" kB";
+               else
+                       row[2] = spad(fl[i].size/1000000, 5)+" MB";
+               row[3] = fl[i].user;
+               row[4] = fl[i].group;
+               Date d = new Date(fl[i].modified);
+               //if (now - fl[i].modified < 24*60*60*1000) {
+               if (n.getDate() == d.getDate() &&
+                   n.getMonth() == d.getMonth() &&
+                   n.getYear() == d.getYear()) {
+                       // show as hour:min
+                       row[5] = pad(d.getHours(),2)+":"+
+                                pad(d.getMinutes(),2);
+                       }
+               //else if (now - fl[i].modified < 24*60*60*365*1000) {
+               else if (n.getYear() == d.getYear()) {
+                       // show as day/mon
+                       row[5] = pad(d.getDate(),2)+"/"+
+                                monmap[d.getMonth()];
+                       }
+               else {
+                       // show as mon/year
+                       row[5] = monmap[d.getMonth()]+"/"+
+                                pad(d.getYear()%100, 2);
+                       }
+               }
+       files.addItems(rows);
+       showing_files = f;
+       showing_list = fl;
+       pathname.setText(f.path);
+       return true;
+       }
+
+       String pad(int n, int s)
+       {
+       String rv = String.valueOf(n);
+       while(rv.length() < s)
+               rv = "0"+rv;
+       return rv;
+       }
+
+       String spad(long n, int s)
+       {
+       String rv = String.valueOf(n);
+       while(rv.length() < s)
+               rv = " "+rv;
+       return rv;
+       }
+
+       String trim_path(String p)
+       {
+       while(p.endsWith("/"))
+               p = p.substring(0, p.length()-1);
+       return p;
+       }
+
+       // openNode
+       // Called when a node with children is opened
+       public void openNode(Hierarchy h, HierarchyNode n)
+       {
+       FileNode fn = (FileNode)n;
+       fn.fill();
+       }
+
+       // closeNode
+       // Called when a node is closed
+       public void closeNode(Hierarchy h, HierarchyNode n)
+       {
+       }
+
+       // clickNode
+       // Called when the user clicks on a node
+       public void clickNode(Hierarchy h, HierarchyNode n)
+       {
+       FileNode fn = (FileNode)n;
+       if (showing_files != fn.file)
+               show_files(fn.file);
+       }
+
+       // doubleNode
+       // Called when a user double-clicks on a node
+       public void doubleNode(Hierarchy h, HierarchyNode n)
+       {
+       }
+
+       // Called when a button is clicked
+       public void click(CbButton b)
+       {
+       int s = files.selected();
+       int ss[] = files.allSelected();
+       RemoteFile f = null, ff[] = new RemoteFile[0];
+       if (s > 0 || s == 0 && ss.length > 1) {
+               // At least one non-.. file was selected
+               boolean parentsel = false;
+               for(int i=0; i<ss.length; i++)
+                       if (ss[i] == 0)
+                               parentsel = true;
+               RemoteFile list[] = showing_list;
+               if (parentsel) {
+                       // need to exclude .. from selected list!
+                       ff = new RemoteFile[ss.length-1];
+                       for(int i=0,j=0; i<ss.length; i++)
+                               if (ss[i] != 0)
+                                       ff[j++] = list[ss[i]-1];
+                       f = s == 0 ? ff[0] : list[s-1];
+                       }
+               else {
+                       // include all selected files
+                       f = list[s-1];
+                       ff = new RemoteFile[ss.length];
+                       for(int i=0; i<ss.length; i++)
+                               ff[i] = list[ss[i]-1];
+                       }
+               }
+       FileNode d = (FileNode)dirs.selected();
+       if (b == ret_b) {
+               // Return to the webmin index
+               try {
+                       URL u = new URL(getDocumentBase(),
+                                       getParameter("return"));
+                       getAppletContext().showDocument(u);
+                       }
+               catch(Exception e) { }
+               }
+       else if (b == edit_b) {
+               // Open a window for editing the selected file
+               if (f == null) return;
+               if (f.type == 0 || f.type > 4)
+                       new ErrorWindow(text("edit_enormal"));
+               else
+                       new EditorWindow(f, this);
+               }
+       else if (b == down_b) {
+               // Force download of the selected file
+               if (f == null) return;
+               if (f.type == 0 || f.type > 4)
+                       new ErrorWindow(text("view_enormal2"));
+               else
+                       show_file(f, true);
+               }
+       else if (b == refresh_b) {
+               // Refesh the selected directory (and thus any subdirs)
+               d.known = false;
+               d.file.list = null;
+               d.fill();
+               show_files(d.file);
+               }
+       else if (b == props_b) {
+               // Display the properties window
+               if (f == null) return;
+               new PropertiesWindow(f, this);
+               }
+       else if (b == acl_b) {
+               // Display the ACL window (if filesystem supports them)
+               if (f == null) return;
+               FileSystem filefs = find_filesys(f);
+               if (filefs == null) return;
+               if (filefs.acls)
+                       new ACLWindow(this, f);
+               else
+                       new ErrorWindow(text("eacl_efs", filefs.mount));
+               }
+       else if (b == attr_b) {
+               // Display the attributes window (if filesystem supports them)
+               if (f == null) return;
+               FileSystem filefs = find_filesys(f);
+               if (filefs == null) return;
+               if (filefs.attrs)
+                       new AttributesWindow(this, f);
+               else
+                       new ErrorWindow(text("attr_efs", filefs.mount));
+               }
+       else if (b == ext_b) {
+               // Display EXT attributes window (if filesystem supports them)
+               if (f == null) return;
+               FileSystem filefs = find_filesys(f);
+               if (filefs == null) return;
+               if (filefs.ext)
+                       new EXTWindow(this, f);
+               else
+                       new ErrorWindow(text("ext_efs", filefs.mount));
+               }
+       else if (b == copy_b) {
+               // Copy the selected files
+               if (f == null) return;
+               cut_buffer = ff;
+               cut_mode = false;
+               }
+       else if (b == cut_b) {
+               // Cut the selected file
+               if (f == null) return;
+               cut_buffer = ff;
+               cut_mode = true;
+               }
+       else if (b == paste_b) {
+               // Paste the copied file
+               if (cut_buffer == null) {
+                       new ErrorWindow(text("paste_ecopy"));
+                       return;
+                       }
+
+               // Check for existing file clashes
+               // XXX
+
+               // Go through all the files to paste
+               for(int i=0; i<cut_buffer.length; i++) {
+                       RemoteFile cf = cut_buffer[i];
+
+                       // Check for an existing file
+                       RemoteFile already = showing_files.find(cf.name);
+                       String sp = showing_files.path;
+                       String dest_path = sp.equals("/") ? sp+cf.name
+                                                         : sp+"/"+cf.name;
+                       if (already != null) {
+                               // File exists .. offer to rename
+                               new OverwriteWindow(this, already, cf, i);
+                               }
+                       else {
+                               // do the move or copy
+                               RemoteFile nf = paste_file(cf, showing_files,
+                                                  dest_path, null, cut_mode);
+                               if (cut_mode && nf != null) {
+                                       // Paste from the destination path
+                                       // from now on
+                                       cut_buffer[i] = nf;
+                                       }
+                               }
+                       }
+               cut_mode = false;
+               }
+       else if (b == delete_b) {
+               // Delete the selected files
+               if (f == null) return;
+               new DeleteWindow(this, ff);
+               }
+       else if (b == new_b) {
+               // Open a window for creating a file
+               new EditorWindow(showing_files.path, this);
+               }
+       else if (b == upload_b) {
+               // Call javascript to open an upload window
+               try {
+                       JSObject win = JSObject.getWindow(this);
+                       String params[] = { showing_files.path };
+                       win.call("upload", params);
+                       }
+               catch(Exception e) {
+                       new ErrorWindow(text("upload_efailed", e.getMessage()));
+                       }
+               }
+       else if (b == mkdir_b) {
+               // Prompt for new directory
+               new MkdirWindow(showing_files.path, this);
+               }
+       else if (b == makelink_b) {
+               // Prompt for a new symlink
+               new LinkWindow(showing_files.path, this);
+               }
+       else if (b == rename_b) {
+               // Prompt for new filename
+               if (f == null) return;
+               new RenameWindow(this, f);
+               }
+       else if (b == share_b) {
+               // Open a window for editing sharing options
+               if (f == null || f.type != RemoteFile.DIR) return;
+               new SharingWindow(f, this);
+               }
+       else if (b == search_b) {
+               // Open window for finding a file
+               new SearchWindow(showing_files.path, this);
+               }
+       }
+
+       // Returns the object for some directory, or null if not found.
+       RemoteFile find_directory(String p, boolean fill)
+       {
+       int l = p.length();
+       boolean can = false;
+       for(int r=0; r<accroot.length; r++) {
+               int rl = accroot[r].length();
+               if (accroot[r].equals("/"))
+                       can = true;
+               else if (l >= rl && p.substring(0, rl).equals(accroot[r]))
+                       can = true;
+               else if (l < rl && accroot[r].substring(0, l).equals(p))
+                       can = true;
+               }
+       if (!can) {
+               new ErrorWindow(text("find_eaccess", p));
+               return null;
+               }
+       FileNode posnode = root;
+       RemoteFile pos = posnode.file;
+       StringTokenizer tok = new StringTokenizer(p, "/");
+       while(tok.hasMoreTokens()) {
+               String fn = tok.nextToken();
+               if (fn.equals("")) continue;
+               RemoteFile fl[] = pos.list();
+               if (fl == null) return null;
+               if (fill) {
+                       posnode.open = true;
+                       posnode.fill();
+                       }
+               boolean found = false;
+               for(int i=0; i<fl.length; i++)
+                       if (fl[i].name.equals(fn)) {
+                               pos = fl[i];
+                               found = true;
+                               }
+               if (!found) {
+                       new ErrorWindow(text("find_eexist", fn, p));
+                       return null;
+                       }
+               if (pos.type != 0) {
+                       new ErrorWindow(text("find_edir", fn, p));
+                       return null;
+                       }
+               if (fill)
+                       posnode = (FileNode)nodemap.get(pos);
+               }
+       if (fill) {
+               if (show_files(pos)) {
+                       posnode.fill();
+                       posnode.open = true;
+                       dirs.select(posnode);
+                       dirs.redraw();
+                       }
+               }
+       return pos;
+       }
+
+       FileSystem find_filesys(RemoteFile f)
+       {
+       FileSystem filefs = null;
+       for(int i=0; i<fslist.size(); i++) {
+               FileSystem fs = (FileSystem)fslist.elementAt(i);
+               int l = fs.mount.length();
+               if (fs.mount.equals(f.path) ||
+                   (f.path.length() >= l+1 &&
+                    f.path.substring(0, l+1).equals(fs.mount+"/")) ||
+                   fs.mount.equals("/")) {
+                       filefs = fs;
+                       }
+               }
+       return filefs;
+       }
+
+       public boolean action(Event e, Object o)
+       {
+       if (e.target == pathname) {
+               // A new path was entered.. cd to it
+               String p = pathname.getText().trim();
+               if (p.equals("")) return true;
+               find_directory(p, true);
+               return true;
+               }
+       return false;
+       }
+
+        // singleClick
+        // Called on a single click on a list item
+        public void singleClick(MultiColumn list, int num)
+       {
+       }
+
+        // doubleClick
+        // Called upon double-clicking on a list item
+        public void doubleClick(MultiColumn list, int num)
+       {
+       if (num == 0) {
+               // Go to parent directory
+               if (showing_files.directory != null) {
+                       ((FileNode)nodemap.get(showing_files)).open = false;
+                       show_files(showing_files.directory);
+                       dirs.select((FileNode)nodemap.get(showing_files));
+                       dirs.redraw();
+                       }
+               return;
+               }
+       RemoteFile d = showing_list[num-1];
+       if (d.type == 0) {
+               // Open this directory
+               FileNode pn = (FileNode)nodemap.get(showing_files);
+               pn.fill();
+               pn.open = true;
+               FileNode fn = (FileNode)nodemap.get(d);
+               if (show_files(d)) {
+                       fn.fill();
+                       fn.open = true;
+                       dirs.select(fn);
+                       dirs.redraw();
+                       }
+               }
+       else if (d.type <= 4) {
+               // Direct the browser to this file
+               show_file(d, list.last_event.shiftDown());
+               }
+       }
+
+       // Called when the user clicks on a column heading so that it can
+       // be sorted.
+       public void headingClicked(MultiColumn list, int col)
+       {
+       if (col == 0)
+               return; // ignore click on icon column?
+       if (col == list.sortcol) {
+               list.sortingArrow(col, list.sortdir == 2 ? 1 : 2);
+               }
+       else {
+               list.sortingArrow(col, 1);
+               }
+
+       // Re-show the list in the new order, but with the same files selected
+       int ss[] = files.allSelected();
+       RemoteFile ssf[] = new RemoteFile[ss.length];
+       for(int i=0; i<ss.length; i++)
+               ssf[i] = showing_list[ss[i]-1];
+       show_files(showing_files);
+       for(int i=0; i<ss.length; i++) {
+               for(int j=0; j<showing_list.length; j++) {
+                       if (showing_list[j] == ssf[i]) {
+                               ss[i] = j+1;
+                               break;
+                               }
+                       }
+               }
+       files.select(ss);
+       }
+
+       void show_file(RemoteFile f, boolean download)
+       {
+       try {
+               if (download) {
+                       URL u = new URL(getDocumentBase(),
+                                       "show.cgi"+urlize(f.path)+
+                                       "?rand="+System.currentTimeMillis()+
+                                       "&type=application%2Funknown"+
+                                       "&trust="+trust);
+                       getAppletContext().showDocument(u);
+                       }
+               else {
+                       URL u = new URL(getDocumentBase(),
+                                       "show.cgi"+urlize(f.path)+
+                                       "?rand="+System.currentTimeMillis()+
+                                       "&trust="+trust);
+                       getAppletContext().showDocument(u, "show");
+                       }
+               }
+       catch(Exception e) { }
+       }
+
+       static String urlize(String s)
+       {
+       StringBuffer rv = new StringBuffer();
+       for(int i=0; i<s.length(); i++) {
+               char c = s.charAt(i);
+               if (c < 16)
+                       rv.append("%0"+Integer.toString(c, 16));
+               else if (!Character.isLetterOrDigit(c) && c != '/' &&
+                   c != '.' && c != '_' && c != '-')
+                       rv.append("%"+Integer.toString(c, 16));
+               else
+                       rv.append(c);
+               }
+       return rv.toString();
+       }
+
+       static String un_urlize(String s)
+       {
+       StringBuffer rv = new StringBuffer();
+       for(int i=0; i<s.length(); i++) {
+               char c = s.charAt(i);
+               if (c == '%') {
+                       rv.append((char)Integer.parseInt(
+                               s.substring(i+1, i+3), 16));
+                       i += 2;
+                       }
+               else
+                       rv.append(c);
+               }
+       return rv.toString();
+       }
+
+       public void upload_notify(String path_str, String info)
+       {
+       int sl = path_str.lastIndexOf('/');
+       String par_str = path_str.substring(0, sl),
+              file_str = path_str.substring(sl+1);
+       RemoteFile par = find_directory(par_str, false);
+       RemoteFile upfile = par.find(file_str);
+       if (upfile == null) {
+               upfile = new RemoteFile(this, info, par);
+               par.add(upfile);
+               }
+       show_files(showing_files);
+       }
+
+       public String text(String k, String p[])
+       {
+       String rv = (String)lang.get(k);
+       if (rv == null) rv = "???";
+       for(int i=0; i<p.length; i++) {
+               int idx = rv.indexOf("$"+(i+1));
+               if (idx != -1)
+                       rv = rv.substring(0, idx)+p[i]+rv.substring(idx+2);
+               }
+       return rv;
+       }
+
+       public String text(String k)
+       {
+       String p[] = { };
+       return text(k, p);
+       }
+
+       public String text(String k, String p1)
+       {
+       String p[] = { p1 };
+       return text(k, p);
+       }
+
+       public String text(String k, String p1, String p2)
+       {
+       String p[] = { p1, p2 };
+       return text(k, p);
+       }
+
+       RemoteFile paste_file(RemoteFile src, RemoteFile dir,
+                             String dest, RemoteFile already, boolean mode)
+       {
+       // Move or copy the actual file
+       String[] rv = get_text((mode ? "move.cgi" : "copy.cgi")+
+                              "?from="+urlize(src.path)+
+                              "&to="+urlize(dest));
+       if (rv[0].length() > 0) {
+               new ErrorWindow(text(
+                       mode ? "paste_emfailed" : "paste_ecfailed", rv[0]));
+               return null;
+               }
+       RemoteFile file = new RemoteFile(this, rv[1], dir);
+       if (already == null) {
+               // Add to the parent directory
+               dir.add(file);
+               }
+       else {
+               // Update the existing file
+               already.type = file.type;
+               already.user = file.user;
+               already.group = file.group;
+               already.size = file.size;
+               already.perms = file.perms;
+               already.modified = file.modified;
+               file = already;
+               }
+       if (mode) {
+               // Delete the old file
+               src.directory.delete(src);
+               }
+       if (src.type == 0) {
+               // Moving or copying a directory.. update the tree
+               FileNode dest_par_node =
+                       (FileNode)nodemap.get(showing_files);
+               dest_par_node.add(new FileNode(file));
+               if (mode) {
+                       FileNode cut_par_node =
+                               (FileNode)nodemap.get(src.directory);
+                       FileNode cut_file_node =
+                               (FileNode)nodemap.get(src);
+                       if (cut_par_node != null &&
+                           cut_file_node != null)
+                               cut_par_node.ch.removeElement(
+                                                       cut_file_node);
+                       }
+               dirs.redraw();
+               }
+       show_files(showing_files);
+       return file;
+       }
+}
+
+// A node in the directory tree
+class FileNode extends HierarchyNode
+{
+       FileManager parent;
+       RemoteFile file;
+       boolean known;
+
+       FileNode(RemoteFile file)
+       {
+       this.file = file;
+       parent = file.parent;
+       im = parent.get_image(file.shared() ? "sdir.gif" : "dir.gif");
+       ch = new Vector();
+       text = file.name;
+       parent.nodemap.put(file, this);
+       }
+
+       // Create the nodes for subdirectories
+       void fill()
+       {
+       if (!known) {
+               RemoteFile l[] = file.list();
+               if (l == null) return;
+               ch.removeAllElements();
+               for(int i=0; i<l.length; i++)
+                       if (l[i].type == 0)
+                               ch.addElement(new FileNode(l[i]));
+               parent.dirs.redraw();
+               known = true;
+               }
+       }
+
+       void add(FileNode n)
+       {
+       for(int i=0; i<=ch.size(); i++) {
+               FileNode ni = i==ch.size() ? null : (FileNode)ch.elementAt(i);
+               if (ni == null || ni.text.compareTo(n.text) > 0) {
+                       ch.insertElementAt(n, i);
+                       break;
+                       }
+               }
+       }
+
+}
+
+class RemoteFile
+{
+       static final int DIR = 0;
+       static final int TEXT = 1;
+       static final int IMAGE = 2;
+       static final int BINARY = 3;
+       static final int UNKNOWN = 4;
+       static final int SYMLINK = 5;
+       static final int DEVICE = 6;
+       static final int PIPE = 7;
+       static final String[] tmap = { "dir.gif", "text.gif", "image.gif",
+                                      "binary.gif", "unknown.gif",
+                                      "symlink.gif", "device.gif",
+                                      "pipe.gif" };
+
+       FileManager parent;
+       String path, name;
+       int type;
+       String user, group;
+       long size;
+       int perms;
+       long modified;
+       String linkto;
+       RemoteFile list[];
+       RemoteFile directory;
+
+       // Parse a line of text to a file object
+       RemoteFile(FileManager parent, String line, RemoteFile d)
+       {
+       this.parent = parent;
+       StringTokenizer tok = new StringTokenizer(line, "\t");
+       path = tok.nextToken();
+       type = Integer.parseInt(tok.nextToken());
+       user = tok.nextToken();
+       group = tok.nextToken();
+       size = Long.parseLong(tok.nextToken());
+       perms = Integer.parseInt(tok.nextToken());
+       modified = Long.parseLong(tok.nextToken())*1000;
+       if (type == 5) linkto = tok.nextToken();
+       directory = d;
+       if (path.equals("/")) name = "/";
+       else name = path.substring(path.lastIndexOf('/')+1);
+       }
+
+       // Create a new, empty file object
+       RemoteFile() { }
+
+       // Returns a list of files in this directory
+       RemoteFile[] list()
+       {
+       if (list == null) {
+               String l[] = parent.get_text("list.cgi?dir="+
+                                            parent.urlize(path));
+               if (l[0].length() > 0) {
+                       //list = new RemoteFile[0];
+                       // Error reading the remote directory!
+                       new ErrorWindow(parent.text("list_edir", path, l[0]));
+                       list = null;
+                       }
+               else {
+                       list = new RemoteFile[l.length-3];
+                       for(int i=3; i<l.length; i++)
+                               list[i-3] = new RemoteFile(parent, l[i], this);
+                       }
+               }
+       return list;
+       }
+
+       RemoteFile find(String n)
+       {
+       RemoteFile l[] = list();
+       if (l != null) {
+               for(int i=0; i<l.length; i++)
+                       if (l[i].name.equals(n))
+                               return l[i];
+               }
+       return null;
+       }
+
+       void add(RemoteFile f)
+       {
+       RemoteFile nlist[] = new RemoteFile[list.length+1];
+       int offset = 0;
+       for(int i=0; i<list.length; i++) {
+               if (list[i].name.compareTo(f.name) > 0 && offset == 0) {
+                       nlist[i] = f;
+                       offset++;
+                       }
+               nlist[i+offset] = list[i];
+               }
+       if (offset == 0) nlist[list.length] = f;
+       list = nlist;
+       }
+
+       void delete(RemoteFile f)
+       {
+       RemoteFile nlist[] = new RemoteFile[list.length-1];
+       for(int i=0,j=0; i<list.length; i++)
+               if (list[i] != f)
+                       nlist[j++] = list[i];
+       list = nlist;
+       }
+
+       boolean shared()
+       {
+       return type == DIR &&
+              (parent.stab.get(path) != null ||
+               parent.ntab.get(path) != null);
+       }
+}
+
+class EditorWindow extends FixedFrame implements CbButtonCallback
+{
+       TextField name;
+       TextArea edit;
+       CbButton save_b, cancel_b, goto_b, find_b;
+       RemoteFile file;
+       FileManager filemgr;
+       GotoWindow goto_window;
+       FindReplaceWindow find_window;
+
+       // Editing an existing file
+       EditorWindow(RemoteFile f, FileManager p)
+       {
+       super(500, 300);
+       file = f; filemgr = p;
+       makeUI(false);
+       setTitle(filemgr.text("edit_title", file.path));
+
+       // Load the file
+       try {
+               URL u = new URL(filemgr.getDocumentBase(),
+                               "show.cgi"+filemgr.urlize(file.path)+
+                               "?rand="+System.currentTimeMillis()+
+                               "&trust="+filemgr.trust+"&edit=1");
+               URLConnection uc = u.openConnection();
+               int len = uc.getContentLength();
+               InputStream is = uc.getInputStream();
+               byte buf[];
+               if (len >= 0) {
+                       // Length is known
+                       buf = new byte[uc.getContentLength()];
+                       int got = 0;
+                       while(got < buf.length)
+                               got += is.read(buf, got, buf.length-got);
+                       }
+               else {
+                       // Length is unknown .. read till the end
+                       buf = new byte[0];
+                       while(true) {
+                           byte data[] = new byte[16384];
+                           int got;
+                           try { got = is.read(data); }
+                           catch(EOFException ex) { break; }
+                           if (got <= 0) break;
+                           byte nbuf[] = new byte[buf.length + got];
+                           System.arraycopy(buf, 0, nbuf, 0, buf.length);
+                           System.arraycopy(data, 0, nbuf, buf.length, got);
+                           buf = nbuf;
+                           }
+                       }
+               edit.setText(new String(buf, 0));
+               is.close();
+               file.size = buf.length;
+               }
+       catch(Exception e) { e.printStackTrace(); }
+       }
+
+       // Creating a new file
+       EditorWindow(String f, FileManager p)
+       {
+       super(500, 300);
+       filemgr = p;
+       makeUI(true);
+       setTitle(filemgr.text("edit_title2"));
+       name.setText(f.equals("/") ? f : f+"/");
+       name.select(name.getText().length(), name.getText().length());
+       }
+
+       void makeUI(boolean add_name)
+       {
+       setLayout(new BorderLayout());
+       if (add_name) {
+               Panel np = new Panel();
+               np.setLayout(new BorderLayout());
+               np.add("West", new Label(filemgr.text("edit_filename")));
+               np.add("Center", name = new TextField());
+               add("North", np);
+               }
+       add("Center", edit = new TextArea(20, 80));
+       edit.setFont(new Font("courier", Font.PLAIN, 14));
+       Panel bot = new Panel();
+       bot.setLayout(new FlowLayout(FlowLayout.RIGHT));
+       bot.add(goto_b = new CbButton(filemgr.get_image("goto.gif"),
+                                     filemgr.text("edit_goto"),
+                                     CbButton.LEFT, this));
+       bot.add(find_b = new CbButton(filemgr.get_image("find.gif"),
+                                     filemgr.text("edit_find"),
+                                     CbButton.LEFT, this));
+       bot.add(new Label(" "));
+       bot.add(save_b = new CbButton(filemgr.get_image("save.gif"),
+                                     filemgr.text("save"),
+                                     CbButton.LEFT, this));
+       bot.add(cancel_b = new CbButton(filemgr.get_image("cancel.gif"),
+                                       filemgr.text("cancel"),
+                                       CbButton.LEFT, this));
+       add("South", bot);
+       pack();
+       show();
+       }
+
+       public void click(CbButton b)
+       {
+       if (b == save_b) {
+               RemoteFile par = null, already = null;
+               String save_path;
+               if (file == null) {
+                       // Locate the filemgr directory
+                       save_path = filemgr.trim_path(name.getText());
+                       int sl = save_path.lastIndexOf('/');
+                       par = filemgr.find_directory(
+                                       save_path.substring(0, sl), false);
+                       if (par == null) return;
+                       already = par.find(save_path.substring(sl+1));
+                       if (already != null &&
+                           (already.type == 0 || already.type == 5)) {
+                               new ErrorWindow(
+                                       filemgr.text("edit_eover", save_path));
+                               return;
+                               }
+                       }
+               else save_path = file.path;
+
+               // Save the file back again
+               String s = edit.getText(), line;
+               try {
+                       URL u = new URL(filemgr.getDocumentBase(),
+                                       "save.cgi"+filemgr.urlize(save_path)+
+                                       "?rand="+System.currentTimeMillis()+
+                                       "&trust="+filemgr.trust);
+                       URLConnection uc = u.openConnection();
+                       uc.setDoOutput(true);
+                       OutputStream os = uc.getOutputStream();
+                       byte buf[] = new byte[s.length()];
+                       s.getBytes(0, buf.length, buf, 0);
+                       os.write(buf);
+                       os.close();
+                       LineInputStream is = new LineInputStream(
+                                                       uc.getInputStream());
+                       String err = is.gets();
+                       if (err.length() > 0) {
+                               new ErrorWindow(
+                                       filemgr.text("edit_esave", err));
+                               is.close();
+                               return;
+                               }
+                       line = is.gets();
+                       is.close();
+                       }
+               catch(Exception e) { e.printStackTrace(); return; }
+
+               if (file == null) {
+                       // Create and insert or replace the file object
+                       file = new RemoteFile(filemgr, line, par);
+                       if (already != null) {
+                               // A file with this name exists
+                               already.type = file.type;
+                               already.user = file.user;
+                               already.group = file.group;
+                               already.size = file.size;
+                               already.perms = file.perms;
+                               already.modified = file.modified;
+                               }
+                       else {
+                               // Add to the list
+                               par.add(file);
+                               }
+                       }
+               else {
+                       file.size = s.length();
+                       file.modified = System.currentTimeMillis();
+                       }
+               filemgr.show_files(filemgr.showing_files);
+               dispose();
+               }
+       else if (b == cancel_b) {
+               // Just close
+               dispose();
+               }
+       else if (b == goto_b) {
+               // Open a dialog asking which line to go to
+               if (goto_window != null)
+                       goto_window.toFront();
+               else
+                       goto_window = new GotoWindow(this);
+               }
+       else if (b == find_b) {
+               // Open the search (and replace) dialog
+               if (find_window != null)
+                       find_window.toFront();
+               else
+                       find_window = new FindReplaceWindow(this);
+               }
+       }
+
+       public void dispose()
+       {
+       super.dispose();
+       if (goto_window != null) goto_window.dispose();
+       if (find_window != null) find_window.dispose();
+       }
+}
+
+class GotoWindow extends FixedFrame implements CbButtonCallback
+{
+       EditorWindow editor;
+       FileManager filemgr;
+       TextField line;
+       CbButton goto_b, cancel_b;
+
+       GotoWindow(EditorWindow e)
+       {
+       editor = e;
+       filemgr = e.filemgr;
+
+       setLayout(new BorderLayout());
+       add("West", new Label(filemgr.text("edit_gotoline")));
+       add("Center", line = new TextField(10));
+       Panel bot = new Panel();
+       bot.setLayout(new FlowLayout(FlowLayout.RIGHT));
+       bot.add(goto_b = new CbButton(filemgr.get_image("goto.gif"),
+                                     filemgr.text("edit_goto"),
+                                     CbButton.LEFT, this));
+       bot.add(cancel_b = new CbButton(filemgr.get_image("cancel.gif"),
+                                       filemgr.text("close"),
+                                       CbButton.LEFT, this));
+       add("South", bot);
+       pack();
+       show();
+       }
+
+       public void click(CbButton b)
+       {
+       if (b == goto_b) {
+               // Go to the chose line, if it exists
+               int lnum;
+               try { lnum = Integer.parseInt(line.getText()); }
+               catch(Exception e) { return; }
+
+               String txt = editor.edit.getText();
+               int c, l = 0;
+               for(c=0; c<txt.length(); c++) {
+                       if (txt.charAt(c) == '\n') {
+                               l++;
+                               if (l == lnum) {
+                                       // Found the line!
+                                       editor.edit.select(c, c);
+                                       dispose();
+                                       return;
+                                       }
+                               }
+                       }
+               }
+       else if (b == cancel_b) {
+               // Just close the window
+               dispose();
+               }
+       }
+
+       public void dispose()
+       {
+       super.dispose();
+       editor.goto_window = null;
+       }
+
+       public boolean handleEvent(Event e)
+       {
+       if (e.target == line && e.id == Event.KEY_RELEASE && e.key == 10) {
+               click(goto_b);
+               return true;
+               }
+       return false;
+       }
+}
+
+class FindReplaceWindow extends FixedFrame implements CbButtonCallback
+{
+       EditorWindow editor;
+       FileManager filemgr;
+       TextField find, replace;
+       CbButton find_b, replace_b, all_b, cancel_b;
+
+       FindReplaceWindow(EditorWindow e)
+       {
+       editor = e;
+       filemgr = e.filemgr;
+       setLayout(new BorderLayout());
+
+       Panel left = new Panel();
+       left.setLayout(new GridLayout(2, 1));
+       left.add(new Label(filemgr.text("edit_searchfor")));
+       left.add(new Label(filemgr.text("edit_replaceby")));
+       add("West", left);
+
+       Panel right = new Panel();
+       right.setLayout(new GridLayout(2, 1));
+       right.add(find = new TextField(40));
+       right.add(replace = new TextField(40));
+       add("Center", right);
+
+       Panel bot = new Panel();
+       bot.setLayout(new FlowLayout(FlowLayout.RIGHT));
+       bot.add(find_b = new CbButton(filemgr.get_image("find.gif"),
+                                     filemgr.text("edit_find"),
+                                     CbButton.LEFT, this));
+       bot.add(replace_b = new CbButton(filemgr.get_image("replace.gif"),
+                                     filemgr.text("edit_replace"),
+                                     CbButton.LEFT, this));
+       bot.add(all_b = new CbButton(filemgr.get_image("all.gif"),
+                                     filemgr.text("edit_all"),
+                                     CbButton.LEFT, this));
+       bot.add(cancel_b = new CbButton(filemgr.get_image("cancel.gif"),
+                                       filemgr.text("close"),
+                                       CbButton.LEFT, this));
+       add("South", bot);
+       pack();
+       show();
+       }
+
+       public void click(CbButton b)
+       {
+       String findtxt = find.getText();
+       String edittxt = editor.edit.getText();
+       if (findtxt.length() == 0)
+               return;
+       if (b == find_b) {
+               // Find the next occurrance of the text, starting from
+               // the cursor + 1, and select it
+               int pos = edittxt.indexOf(findtxt,
+                                          editor.edit.getSelectionStart()+1);
+               if (pos < 0)
+                       new ErrorWindow(filemgr.text("edit_notfound", findtxt));
+               else
+                       editor.edit.select(pos, pos+findtxt.length());
+               }
+       else if (b == replace_b) {
+               // If the word to search for is selected, replace it. Otherwise
+               // just search for the next one
+               int st = editor.edit.getSelectionStart(),
+                   en = editor.edit.getSelectionEnd();
+               if (st >= 0) {
+                       String sel = edittxt.substring(st, en);
+                       if (sel.equals(findtxt)) {
+                               // Replace the selected
+                               editor.edit.setText(edittxt.substring(0, st)+
+                                                   replace.getText()+
+                                                   edittxt.substring(en));
+                               editor.edit.select(st, st);
+                               return;
+                               }
+                       }
+               click(find_b);
+               }
+       else if (b == all_b) {
+               // Replace all occurrances of the text in the editor
+               int pos = 0;
+               int len = findtxt.length();
+               int st = editor.edit.getSelectionStart(),
+                   en = editor.edit.getSelectionEnd();
+               while((pos = edittxt.indexOf(findtxt, pos)) != -1) {
+                       edittxt = edittxt.substring(0, pos)+
+                                 replace.getText()+
+                                 edittxt.substring(pos+len);
+                       pos += len;
+                       }
+               editor.edit.setText(edittxt);
+               editor.edit.select(st, en);     // put back old selection
+               }
+       else if (b == cancel_b) {
+               // Just close the window
+               dispose();
+               }
+       }
+
+       public void dispose()
+       {
+       super.dispose();
+       editor.find_window = null;
+       }
+}
+
+class PropertiesWindow extends FixedFrame implements CbButtonCallback
+{
+       RemoteFile file;
+       FileManager filemgr;
+       CbButton save_b, cancel_b;
+
+       TextField linkto;
+       TextField user, group;
+       Checkbox setuid, setgid;
+       PermissionsPanel user_p, group_p, other_p;
+       Checkbox sticky;
+       Choice rec_mode;
+       TextField octal;
+
+       PropertiesWindow(RemoteFile f, FileManager p)
+       {
+       file = f;
+       filemgr = p;
+
+       // Create UI
+       setTitle(f.path);
+       setLayout(new BorderLayout());
+       Panel bot = new Panel();
+       bot.setLayout(new FlowLayout(FlowLayout.RIGHT));
+       bot.add(save_b = new CbButton(filemgr.get_image("save.gif"),
+                                     filemgr.text("save"),
+                                     CbButton.LEFT, this));
+       bot.add(cancel_b = new CbButton(filemgr.get_image("cancel.gif"),
+                                       filemgr.text("cancel"),
+                                       CbButton.LEFT, this));
+       add("South", bot);
+
+       Panel mid = new Panel();
+       mid.setLayout(new BorderLayout());
+       TabbedPanel tab = null;
+       add("Center", mid);
+
+       // Create file details section
+       Panel det = new LinedPanel(filemgr.text("info_file")),
+             dl = new Panel(), dr = new Panel();
+       setup_leftright(det, dl, dr);
+       add_item(filemgr.text("info_path"),
+               new Label(file.path), dl, dr);
+       add_item(filemgr.text("info_type"),
+               new Label(filemgr.text("file_type"+file.type)), dl, dr);
+       add_item(filemgr.text("info_size"),
+               new Label(String.valueOf(file.size)),dl,dr);
+       add_item(filemgr.text("info_mod"),
+               new Label(String.valueOf(new Date(file.modified))), dl, dr);
+       if (file.type == 5)
+               add_item(filemgr.text("info_link"),
+                        linkto = new TextField(file.linkto, 30), dl, dr);
+       mid = add_panel(mid, det);
+
+       // Create permissions section
+       Panel per = new LinedPanel(filemgr.text("info_perms")),
+             pl = new Panel(), pr = new Panel();
+       setup_leftright(per, pl, pr);
+       add_item(filemgr.text("info_user"),
+                user_p = new PermissionsPanel(file, 64, filemgr), pl, pr);
+       add_item(filemgr.text("info_group"),
+                group_p = new PermissionsPanel(file, 8, filemgr), pl, pr);
+       add_item(filemgr.text("info_other"),
+                other_p = new PermissionsPanel(file, 1, filemgr), pl,pr);
+       if (file.type == 0) {
+               add_item(filemgr.text("info_sticky"), sticky = new Checkbox(
+                                       filemgr.text("info_sticky2")), pl,pr);
+               sticky.setState((file.perms&01000) != 0);
+               }
+       add_item(filemgr.text("info_octal"), octal = new TextField(4), pl, pr);
+       octal.setEditable(false);
+       mid = add_panel(mid, per);
+
+       // Create ownership section
+       Panel own = new LinedPanel(filemgr.text("info_own")),
+             ol = new Panel(), or = new Panel();
+       setup_leftright(own, ol, or);
+       add_item(filemgr.text("info_user"),
+                user = new TextField(file.user, 10), ol, or);
+       if (file.type != 0) {
+               add_item(filemgr.text("info_setuid"),
+                        setuid = new Checkbox(filemgr.text("info_setuid2")),
+                        ol, or);
+               setuid.setState((file.perms & 0x800) != 0);
+               }
+       add_item(filemgr.text("info_group"),
+                group = new TextField(file.group, 10), ol, or);
+       if (file.type == 0)
+               add_item(filemgr.text("info_setgid"),
+                 setgid = new Checkbox(filemgr.text("info_setgid2")), ol, or);
+       else
+               add_item(filemgr.text("info_setgid"),
+                 setgid = new Checkbox(filemgr.text("info_setgid3")), ol, or);
+       setgid.setState((file.perms & 0x400) != 0);
+       mid = add_panel(mid, own);
+
+       if (file.type == 0) {
+               // Create recursion section
+               Panel rec = new LinedPanel(filemgr.text("info_apply"));
+               rec.setLayout(new BorderLayout());
+               rec_mode = new Choice();
+               for(int i=1; i<=3; i++)
+                       rec_mode.addItem(filemgr.text("info_apply"+i));
+               rec.add("Center", rec_mode);
+               mid = add_panel(mid, rec);
+               }
+
+       set_octal();
+       pack();
+       show();
+       }
+
+       Panel add_panel(Panel p, Component c)
+       {
+       p.add("North", c);
+       Panel np = new Panel();
+       np.setLayout(new BorderLayout());
+       p.add("Center", np);
+       return np;
+       }
+
+       public void click(CbButton b)
+       {
+       if (b == save_b) {
+               // Update the file
+               int perms = get_perms();
+               int rec = 0;
+               if (file.type == 0)
+                       rec = rec_mode.getSelectedIndex();
+               String rv[] = filemgr.get_text(
+                       "chmod.cgi?path="+filemgr.urlize(file.path)+
+                       "&perms="+perms+"&user="+user.getText()+
+                       "&group="+group.getText()+"&rec="+rec+
+                       (linkto==null ? "" : "&linkto="+linkto.getText()));
+               if (rv[0].length() > 0) {
+                       // Something went wrong
+                       new ErrorWindow(filemgr.text("info_efailed",
+                                       file.path, rv[0]));
+                       }
+               else {
+                       // Update all changed file objects
+                       if (linkto != null)
+                               file.linkto = linkto.getText();
+                       else if (rec == 0)
+                               update_file(file, perms, false);
+                       else if (rec == 1) {
+                               // Update files in this directory
+                               update_file(file, perms, false);
+                               recurse_files(file, perms, false);
+                               }
+                       else if (rec == 2) {
+                               // Update files and subdirs
+                                update_file(file, perms, false);
+                               recurse_files(file, perms, true);
+                               }
+
+                       // Update directory list
+                       int os = filemgr.files.selected();
+                       filemgr.show_files(filemgr.showing_files);
+                       filemgr.files.select(os);
+                       dispose();
+                       }
+               }
+       else {
+               // Just close
+               dispose();
+               }
+       }
+
+       void update_file(RemoteFile f, int perms, boolean perms_only)
+       {
+       f.user = user.getText();
+       f.group = group.getText();
+       if (perms_only)
+               f.perms = (perms & 0777) | (f.perms & 037777777000);
+       else
+               f.perms = perms;
+       }
+
+       void recurse_files(RemoteFile f, int perms, boolean do_subs)
+       {
+       if (f.list == null) return;
+       for(int i=0; i<f.list.length; i++) {
+               RemoteFile ff = f.list[i];
+               if (ff.type == 5) continue;
+               else if (ff.type == 0) {
+                       if (do_subs) {
+                               update_file(ff, perms, false);
+                               recurse_files(ff, perms, true);
+                               }
+                       }
+               else update_file(ff, perms, true);
+               }
+       }
+
+       void setup_leftright(Panel m, Panel l, Panel r)
+       {
+       m.setLayout(new BorderLayout());
+       Panel p = new Panel();
+       p.setLayout(new BorderLayout());
+       p.add("West", l);
+       p.add("Center", r);
+       l.setLayout(new GridLayout(0, 1));
+       r.setLayout(new GridLayout(0, 1));
+       m.add("North", p);
+       }
+
+       void add_item(String t, Component c, Panel l, Panel r)
+       {
+       l.add(new Label(t));
+       Panel p = new Panel();
+       p.setLayout(new BorderLayout());
+       p.add("West", c);
+       r.add(p);
+       }
+
+       void set_octal()
+       {
+       String oct = Integer.toOctalString(get_perms());
+       while(oct.length() < 4)
+               oct = "0"+oct;
+       octal.setText(oct);
+       }
+
+       int get_perms()
+       {
+       int perms = 0;
+       if (setuid == null)
+               perms |= (file.perms & 0x800);
+       else
+               perms |= (setuid.getState() ? 0x800 : 0);
+       perms |= (setgid.getState() ? 0x400 : 0);
+       perms |= user_p.getPerms();
+       perms |= group_p.getPerms();
+       perms |= other_p.getPerms();
+       if (sticky == null)
+               perms |= (file.perms & 01000);
+       else
+               perms |= (sticky.getState() ? 01000 : 0);
+       return perms;
+       }
+
+       public boolean handleEvent(Event e)
+       {
+       if (e.target instanceof Checkbox) {
+               set_octal();
+               return true;
+               }
+       return false;
+       }
+}
+
+class PermissionsPanel extends Panel
+{
+       Checkbox read, write, exec;
+       int base;
+
+       PermissionsPanel(RemoteFile file, int base, FileManager filemgr)
+       {
+       int perms = file.perms;
+       this.base = base;
+       setLayout(new GridLayout(1, 3));
+       add(read = new Checkbox(filemgr.text("info_read")));
+       read.setState((perms&(base<<2)) != 0);
+       add(write = new Checkbox(filemgr.text("info_write")));
+       write.setState((perms&(base<<1)) != 0);
+       add(exec = new Checkbox(
+               filemgr.text(file.type==0 ? "info_list" : "info_exec")));
+       exec.setState((perms&base) != 0);
+       }
+
+       int getPerms()
+       {
+       int rv = 0;
+       rv |= (read.getState() ? (base<<2) : 0);
+       rv |= (write.getState() ? (base<<1) : 0);
+       rv |= (exec.getState() ? base : 0);
+       return rv;
+       }
+}
+
+class DeleteWindow extends FixedFrame implements CbButtonCallback
+{
+       CbButton delete_b, cancel_b;
+       FileManager filemgr;
+       RemoteFile files[];
+
+       DeleteWindow(FileManager p, RemoteFile ff[])
+       {
+       filemgr = p;
+       files = ff;
+       setTitle(filemgr.text(ff.length > 1 ? "delete_mtitle" :
+                             ff[0].type == 0 ? "delete_dtitle" :
+                                               "delete_ftitle"));
+
+       setLayout(new BorderLayout());
+       if (ff.length > 1) {
+               add("North", new Label(filemgr.text("delete_mdesc")));
+               Panel mp = new Panel();
+               mp.setLayout(new GridLayout(ff.length, 1));
+               for(int i=0; i<ff.length; i++)
+                       mp.add(new Label(ff[i].path));
+               add("Center", mp);
+               }
+       else
+               add("Center", new MultiLabel(filemgr.text(
+                       ff[0].type == 0 ? "delete_ddesc" : "delete_fdesc",
+                       ff[0].path), 35));
+       Panel bot = new Panel();
+       bot.setLayout(new FlowLayout(FlowLayout.CENTER));
+       bot.add(delete_b = new CbButton(filemgr.get_image("save.gif"),
+                                       filemgr.text("delete"),
+                                       CbButton.LEFT, this));
+       bot.add(cancel_b = new CbButton(filemgr.get_image("cancel.gif"),
+                                       filemgr.text("cancel"),
+                                       CbButton.LEFT, this));
+       add("South", bot);
+
+       pack();
+       show();
+       }
+
+       public void click(CbButton b)
+       {
+       if (b == delete_b) {
+               // Delete the file or directory
+               boolean need_redraw = false, need_reshow = false;
+               for(int i=0; i<files.length; i++) {
+                       RemoteFile file = files[i];
+                       String rv[] = filemgr.get_text("delete.cgi?file="+
+                                              filemgr.urlize(file.path));
+                       if (rv[0].length() > 0) {
+                               new ErrorWindow(filemgr.text("delete_efailed",
+                                               file.path, rv[0]));
+                               break;
+                               }
+                       else {
+                               // done the deed.. update data structures
+                               RemoteFile pf = file.directory;
+                               pf.delete(file);
+                               if (filemgr.showing_files == pf) {
+                                       // Need to refresh the list as well..
+                                       need_reshow = true;
+                                       }
+
+                               FileNode node = (FileNode)filemgr.nodemap.get(
+                                                       file);
+                               FileNode pnode = (FileNode)filemgr.nodemap.get(
+                                                       pf);
+                               if (node != null) {
+                                       // Take the directory out of the tree..
+                                       pnode.ch.removeElement(node);
+                                       need_redraw = true;
+                                       }
+                               }
+                       }
+               if (need_reshow) filemgr.show_files(filemgr.showing_files);
+               if (need_redraw) filemgr.dirs.redraw();
+               dispose();
+               }
+       else if (b == cancel_b)
+               dispose();
+       }
+}
+
+class MkdirWindow extends FixedFrame implements CbButtonCallback
+{
+       FileManager filemgr;
+       TextField dir;
+       CbButton create_b, cancel_b;
+
+       MkdirWindow(String d, FileManager p)
+       {
+       filemgr = p;
+       setTitle(filemgr.text("mkdir_title"));
+       setLayout(new BorderLayout());
+       add("West", new Label(filemgr.text("mkdir_dir")));
+       add("Center", dir = new TextField(d.equals("/") ? "/" : d+"/", 40));
+       dir.select(dir.getText().length(), dir.getText().length());
+       Panel bot = new Panel();
+       bot.setLayout(new FlowLayout(FlowLayout.CENTER));
+       bot.add(create_b = new CbButton(filemgr.get_image("save.gif"),
+                                       filemgr.text("create"),
+                                       CbButton.LEFT, this));
+       bot.add(cancel_b = new CbButton(filemgr.get_image("cancel.gif"),
+                                       filemgr.text("cancel"),
+                                       CbButton.LEFT, this));
+       add("South", bot);
+       pack();
+       show();
+       }
+
+       public void click(CbButton b)
+       {
+       if (b == create_b) {
+               // Find the filemgr directory
+               String path = dir.getText();
+               path = filemgr.trim_path(path);
+               int sl = path.lastIndexOf('/');
+               RemoteFile par = filemgr.find_directory(
+                                       path.substring(0, sl), false);
+               if (par.find(path.substring(sl+1)) != null) {
+                       new ErrorWindow(filemgr.text("mkdir_eexists", path));
+                       return;
+                       }
+               String rv[] = filemgr.get_text("mkdir.cgi?dir="+
+                                              filemgr.urlize(path));
+               if (rv[0].length() > 0) {
+                       new ErrorWindow(filemgr.text("mkdir_efailed", rv[0]));
+                       return;
+                       }
+               RemoteFile file = new RemoteFile(filemgr, rv[1], par);
+               par.add(file);
+               FileNode parnode = (FileNode)filemgr.nodemap.get(par);
+               if (parnode != null) {
+                       // Update the tree
+                       parnode.add(new FileNode(file));
+                       filemgr.dirs.redraw();
+                       }
+               filemgr.show_files(filemgr.showing_files);
+               dispose();
+               }
+       else dispose();
+       }
+}
+
+class LinkWindow extends FixedFrame implements CbButtonCallback
+{
+       FileManager filemgr;
+       TextField from, to;
+       CbButton create_b, cancel_b;
+
+       LinkWindow(String d, FileManager p)
+       {
+       filemgr = p;
+       setLayout(new BorderLayout());
+       setTitle(filemgr.text("link_title"));
+       Panel l = new Panel(), r = new Panel();
+       l.setLayout(new GridLayout(0, 1));
+       l.add(new Label(filemgr.text("link_from")));
+       l.add(new Label(filemgr.text("link_to")));
+       r.setLayout(new GridLayout(0, 1));
+       r.add(from = new TextField(d.equals("/") ? "/" : d+"/", 40));
+       from.select(from.getText().length(), from.getText().length());
+       r.add(to = new TextField());
+       add("West", l); add("Center", r);
+       Panel bot = new Panel();
+       bot.setLayout(new FlowLayout(FlowLayout.CENTER));
+       bot.add(create_b = new CbButton(filemgr.get_image("save.gif"),
+                                       filemgr.text("create"),
+                                       CbButton.LEFT, this));
+       bot.add(cancel_b = new CbButton(filemgr.get_image("cancel.gif"),
+                                       filemgr.text("cancel"),
+                                       CbButton.LEFT, this));
+       add("South", bot);
+       pack();
+       show();
+       }
+
+       public void click(CbButton b)
+       {
+       if (b == create_b) {
+               // Check inputs
+               String from_str = from.getText().trim();
+               int sl = from_str.lastIndexOf('/');
+               String par_str = from_str.substring(0, sl),
+                      file_str = from_str.substring(sl+1);
+               RemoteFile par = filemgr.find_directory(par_str, false);
+               if (par == null) return;
+               if (par.find(file_str) != null) {
+                       new ErrorWindow(filemgr.text("link_eexists", from_str));
+                       return;
+                       }
+
+               // Create the actual link
+               String rv[] = filemgr.get_text("makelink.cgi?from="+
+                                              filemgr.urlize(from_str)+"&to="+
+                                              filemgr.urlize(to.getText()));
+               if (rv[0].length() > 0) {
+                       new ErrorWindow(filemgr.text("link_efailed", rv[0]));
+                       return;
+                       }
+               RemoteFile file = new RemoteFile(filemgr, rv[1], par);
+               par.add(file);
+               filemgr.show_files(filemgr.showing_files);
+               dispose();
+               }
+       else if (b == cancel_b)
+               dispose();
+       }
+}
+
+class RenameWindow extends FixedFrame implements CbButtonCallback
+{
+       FileManager filemgr;
+       RemoteFile file;
+       TextField oldname, newname;
+       CbButton rename_b, cancel_b;
+
+       RenameWindow(FileManager p, RemoteFile f)
+       {
+       filemgr = p; file = f;
+       setLayout(new BorderLayout());
+       setTitle(filemgr.text("rename_title", file.path));
+       Panel l = new Panel(), r = new Panel();
+       l.setLayout(new GridLayout(0, 1));
+       l.add(new Label(filemgr.text("rename_old")));
+       l.add(new Label(filemgr.text("rename_new")));
+       r.setLayout(new GridLayout(0, 1));
+       r.add(oldname = new TextField(file.name, 20));
+       oldname.setEditable(false);
+       r.add(newname = new TextField(file.name, 20));
+       newname.select(file.name.length(), file.name.length());
+       add("West", l); add("Center", r);
+
+       Panel bot = new Panel();
+       bot.setLayout(new FlowLayout(FlowLayout.CENTER));
+       bot.add(rename_b = new CbButton(filemgr.get_image("save.gif"),
+                                       filemgr.text("rename_ok"),
+                                       CbButton.LEFT, this));
+       bot.add(cancel_b = new CbButton(filemgr.get_image("cancel.gif"),
+                                       filemgr.text("cancel"),
+                                       CbButton.LEFT, this));
+       add("South", bot);
+       pack();
+       show();
+       }
+
+       public void click(CbButton b)
+       {
+       if (b == rename_b) {
+               // Check for an existing file
+               String newstr = newname.getText().trim();
+               if (newstr.length() == 0) return;
+               RemoteFile already = file.directory.find(newstr);
+               if (already != null) {
+                       new ErrorWindow(filemgr.text("rename_eexists", newstr));
+                       return;
+                       }
+
+               // Rename the real file
+               int sl = file.path.lastIndexOf('/');
+               String newpath = file.path.substring(0, sl)+"/"+newstr;
+               String rv[] = filemgr.get_text(
+                               "rename.cgi?old="+filemgr.urlize(file.path)+
+                               "&new="+filemgr.urlize(newpath));
+               if (rv[0].length() > 0) {
+                       new ErrorWindow(filemgr.text("rename_efailed", rv[0]));
+                       return;
+                       }
+
+               // Update data structure
+               file.name = newstr;
+               file.path = newpath;
+               file.directory.delete(file);
+               file.directory.add(file);
+               file.list = null;
+               FileNode parnode = (FileNode)filemgr.nodemap.get(file.directory);
+               FileNode filenode = (FileNode)filemgr.nodemap.get(file);
+               if (parnode != null && filenode != null) {
+                       filenode.text = file.name;
+                       parnode.ch.removeElement(filenode);
+                       parnode.add(filenode);
+                       dispose();
+                       filemgr.dirs.redraw();
+                       }
+
+               filemgr.show_files(filemgr.showing_files);
+               dispose();
+               }
+       else if (b == cancel_b)
+               dispose();
+       }
+}
+
+class MultiLabel extends BorderPanel
+{
+       public MultiLabel(String s, int max)
+       {
+       this(s, max, 1);
+       }
+
+       public MultiLabel(String s, int max, int b)
+       {
+       super(b);
+       Vector v = new Vector();
+       StringTokenizer tok = new StringTokenizer(s.trim(), " \t");
+       String line = null;
+       while(tok.hasMoreTokens()) {
+               String w = tok.nextToken();
+               line = (line == null ? w : line+" "+w);
+               if (line.length() > max || !tok.hasMoreTokens()) {
+                       v.addElement(line);
+                       line = null;
+                       }
+               }
+       setLayout(new GridLayout(v.size(), 1, 0, 0));
+       for(int i=0; i<v.size(); i++)
+               add(new Label((String)v.elementAt(i), Label.CENTER));
+       }
+}
+
+class OverwriteWindow extends FixedFrame implements CbButtonCallback
+{
+       FileManager filemgr;
+       RemoteFile src, already;
+       TextField newname;
+       CbButton ok, cancel;
+       int idx;
+       boolean mode;
+
+       OverwriteWindow(FileManager p, RemoteFile a, RemoteFile s, int i)
+       {
+       filemgr = p; src = s; already = a; idx = i;
+       mode = filemgr.cut_mode;
+       setLayout(new BorderLayout());
+       setTitle(filemgr.text("over_title"));
+       add("North",
+           new MultiLabel(filemgr.text("over_msg", already.path), 30, 0));
+       add("West", new Label(filemgr.text("over_new")));
+       add("East", newname = new TextField(a.name, 30));
+
+       Panel bot = new Panel();
+       bot.setLayout(new FlowLayout(FlowLayout.RIGHT));
+       bot.add(ok = new CbButton(filemgr.get_image("save.gif"),
+                                 filemgr.text("over_ok"),
+                                 CbButton.LEFT, this));
+       bot.add(cancel = new CbButton(filemgr.get_image("cancel.gif"),
+                                 filemgr.text("cancel"),
+                                 CbButton.LEFT, this));
+       add("South", bot);
+       pack();
+       show();
+       }
+
+       public void click(CbButton b)
+       {
+       if (b == cancel)
+               dispose();
+       else if (b == ok && newname.getText().length() > 0) {
+               // paste the file, but with a new name
+               RemoteFile ap = already.directory;
+               RemoteFile newalready = ap.find(newname.getText());
+               if (newalready == src) {
+                       new ErrorWindow(filemgr.text("paste_eself"));
+                       return;
+                       }
+               if (newalready != null && (newalready.type == 0 ||
+                                          newalready.type == 5)) {
+                       new ErrorWindow(
+                               filemgr.text("paste_eover", newalready.path));
+                       return;
+                       }
+               String dpath = (ap.path.equals("/") ? "/" :
+                               ap.path+"/")+newname.getText();
+               RemoteFile nf = filemgr.paste_file(src, already.directory,
+                                                  dpath, newalready, mode);
+               if (filemgr.cut_mode && nf != null) {
+                       // Paste from the destination path from now on
+                       filemgr.cut_buffer[idx] = nf;
+                       }
+               dispose();
+               }
+       }
+}
+
+class SambaShare
+{
+       String path;
+       boolean available;
+       boolean writable;
+       int guest;
+       String comment;
+
+       SambaShare(String l)
+       {
+       StringSplitter tok = new StringSplitter(l, ':');
+       path = tok.nextToken();
+       available = tok.nextToken().equals("1");
+       writable = tok.nextToken().equals("1");
+       guest = Integer.parseInt(tok.nextToken());
+       comment = tok.nextToken();
+       }
+
+       SambaShare(String p, boolean a, boolean w, int g, String c)
+       {
+       path = p;
+       available = a;
+       writable = w;
+       guest = g;
+       comment = c;
+       }
+
+       String params()
+       {
+       return "path="+FileManager.urlize(path)+
+              "&available="+(available ? 1 : 0)+
+              "&writable="+(writable ? 1 : 0)+
+              "&guest="+guest+
+              "&comment="+FileManager.urlize(comment);
+       }
+}
+
+class DFSAdminExport
+{
+       String path;
+       String desc;
+       String ro, rw, root;
+
+       DFSAdminExport(String l)
+       {
+       StringSplitter tok = new StringSplitter(l, ':');
+       path = tok.nextToken();
+       ro = tok.nextToken();
+       rw = tok.nextToken();
+       root = tok.nextToken();
+       desc = tok.nextToken();
+       }
+
+       DFSAdminExport(String p, String d, String ro, String rw, String root)
+       {
+       path = p;
+       desc = d;
+       this.ro = ro;
+       this.rw = rw;
+       this.root = root;
+       }
+
+       String[] split(String s)
+       {
+       StringTokenizer stok = new StringTokenizer(s, " ");
+       String rv[] = new String[stok.countTokens()];
+       for(int i=0; i<rv.length; i++)
+               rv[i] = stok.nextToken();
+       return rv;
+       }
+
+       String params()
+       {
+       return "path="+FileManager.urlize(path)+
+              "&ro="+FileManager.urlize(ro)+
+              "&rw="+FileManager.urlize(rw)+
+              "&root="+FileManager.urlize(root)+
+              "&desc="+FileManager.urlize(desc);
+       }
+}
+
+class LinuxExport
+{
+       String path;
+       String host[];
+       boolean ro[];
+       int squash[];
+
+       LinuxExport(String l)
+       {
+       StringSplitter tok = new StringSplitter(l, ':');
+       path = tok.nextToken();
+       int c = tok.countTokens() / 3;
+       host = new String[c];
+       ro = new boolean[c];
+       squash = new int[c];
+       for(int i=0; tok.hasMoreTokens(); i++) {
+               host[i] = tok.nextToken();
+               ro[i] = tok.nextToken().equals("1");
+               squash[i] = Integer.parseInt(tok.nextToken());
+               }
+       }
+
+       LinuxExport(String p, String h[], String r[], String s[])
+       {
+       path = p;
+       }
+
+       String params()
+       {
+       String rv = "path="+FileManager.urlize(path)+
+                   "&count="+host.length;
+       for(int i=0; i<host.length; i++) {
+               rv += "&host"+i+"="+FileManager.urlize(host[i]);
+               rv += "&ro"+i+"="+(ro[i] ? 1 : 0);
+               rv += "&squash"+i+"="+squash[i];
+               }
+       return rv;
+       }
+}
+
+class SharingWindow extends FixedFrame implements CbButtonCallback
+{
+       CbButton save_b, cancel_b;
+       RemoteFile file;
+       FileManager filemgr;
+       SambaShare sshare;
+       DFSAdminExport dexport;
+       LinuxExport lexport;
+       Checkbox samba_on, samba_off;
+       Checkbox writable_on, writable_off;
+       Checkbox available_on, available_off;
+       Checkbox guest_on, guest_off, guest_only;
+       TextField comment;
+
+       TextField desc;
+       Checkbox nfs_on, nfs_off;
+       TextField rwhosts, rohosts, roothosts;
+       Checkbox rw[] = new Checkbox[3], ro[] = new Checkbox[3],
+                root[] = new Checkbox[3];
+
+       TextField host[];
+       Choice lro[], squash[];
+
+       SharingWindow(RemoteFile f, FileManager p)
+       {
+       file = f; filemgr = p;
+       setTitle(filemgr.text("share_title", file.path));
+       sshare = (SambaShare)filemgr.stab.get(file.path);
+       Object nshare = filemgr.ntab.get(file.path);
+       if (filemgr.nfsmode == 1)
+               lexport = (LinuxExport)nshare;
+       else if (filemgr.nfsmode == 2)
+               dexport = (DFSAdminExport)nshare;
+
+       // setup UI
+       setLayout(new BorderLayout());
+       Panel samba = new Panel(), sl = new Panel(), sr = new Panel();
+       samba.setLayout(new BorderLayout());
+       Panel st = new Panel();
+       st.setLayout(new GridLayout(2, 1));
+       CheckboxGroup sg = new CheckboxGroup();
+       st.add(samba_off = new Checkbox(filemgr.text("share_soff"), sg, 
+                                      sshare == null));
+       st.add(samba_on = new Checkbox(filemgr.text("share_son"), sg,
+                                       sshare != null));
+       samba.add("North", st);
+
+       Panel stop = new LinedPanel(filemgr.text("share_sheader"));
+       setup_leftright(stop, sl, sr);
+
+       comment = new TextField(sshare == null ? "" : sshare.comment, 25);
+       add_item(filemgr.text("share_comment"), comment, sl, sr);
+
+       Panel ap = new Panel();
+       ap.setLayout(new GridLayout(1, 0));
+       CheckboxGroup ag = new CheckboxGroup();
+       ap.add(available_on = new Checkbox(filemgr.text("yes"), ag,
+                                         sshare == null || sshare.available));
+       ap.add(available_off = new Checkbox(filemgr.text("no"), ag,
+                                         sshare != null && !sshare.available));
+       add_item(filemgr.text("share_available"), ap, sl, sr);
+
+       Panel wp = new Panel();
+       wp.setLayout(new GridLayout(1, 0));
+       CheckboxGroup wg = new CheckboxGroup();
+       wp.add(writable_on = new Checkbox(filemgr.text("yes"), wg,
+                                         sshare == null || sshare.writable));
+       wp.add(writable_off = new Checkbox(filemgr.text("no"), wg,
+                                          sshare != null && !sshare.writable));
+       add_item(filemgr.text("share_writable"), wp, sl, sr);
+
+       Panel gp = new Panel();
+       gp.setLayout(new GridLayout(1, 0));
+       CheckboxGroup gg = new CheckboxGroup();
+       gp.add(guest_only = new Checkbox(filemgr.text("share_only"), gg,
+                               sshare != null && sshare.guest == 2));
+       gp.add(guest_on = new Checkbox(filemgr.text("yes"), gg,
+                               sshare == null || sshare.guest == 1));
+       gp.add(guest_off = new Checkbox(filemgr.text("no"), gg,
+                               sshare != null && sshare.guest == 0));
+       add_item(filemgr.text("share_guest"), gp, sl, sr);
+
+       samba.add("Center", stop);
+
+       // Setup NFS UI
+       Panel nfs = new Panel(), nl = new Panel(), nr = new Panel();
+       nfs.setLayout(new BorderLayout());
+       Panel nt = new Panel();
+       nt.setLayout(new GridLayout(2, 1));
+       CheckboxGroup ng = new CheckboxGroup();
+       nt.add(nfs_off = new Checkbox(filemgr.text("share_noff"), ng, 
+                                     nshare == null));
+       nt.add(nfs_on = new Checkbox(filemgr.text("share_non"), ng,
+                                    nshare != null));
+       nfs.add("North", nt);
+
+       Panel ntop = new LinedPanel(filemgr.text("share_nheader"));
+       setup_leftright(ntop, nl, nr);
+       if (filemgr.nfsmode == 1) {
+               // Linux export mode
+               nl.setLayout(new GridLayout(0, 1, 2, 2));
+               nr.setLayout(new GridLayout(0, 1, 2, 2));
+               nl.add(new Label(filemgr.text("share_host")));
+               nr.add(new Label(filemgr.text("share_opts")));
+               int c = lexport==null ? 0 : lexport.host.length;
+               host = new TextField[c+1];
+               lro = new Choice[c+1];
+               squash = new Choice[c+1];
+               for(int i=0; i<c; i++) {
+                       host[i] = new TextField(lexport.host[i], 20);
+                       lro[i] = robox(lexport.ro[i]);
+                       squash[i] = squashbox(lexport.squash[i]);
+                       nl.add(host[i]);
+                       nr.add(opts_panel(lro[i], squash[i]));
+                       }
+               host[c] = new TextField("", 20);
+               lro[c] = robox(false);
+               squash[c] = squashbox(1);
+               nl.add(host[c]);
+               nr.add(opts_panel(lro[c], squash[c]));
+               }
+       else if (filemgr.nfsmode == 2) {
+               // Solaris share mode
+               desc = new TextField(dexport == null ? "" : dexport.desc, 25);
+               add_item(filemgr.text("share_desc"), desc, nl, nr);
+
+               rohosts = add_hosts(filemgr.text("share_ro"),
+                                   dexport == null ? "-" : dexport.ro,
+                                   ro, nl, nr);
+               rwhosts = add_hosts(filemgr.text("share_rw"),
+                                   dexport == null ? "-" : dexport.rw,
+                                   rw, nl, nr);
+               roothosts = add_hosts(filemgr.text("share_root"),
+                                   dexport == null ? "-" : dexport.root,
+                                   root, nl, nr);
+               root[1].getParent().remove(root[1]);
+               }
+       else if (filemgr.nfsmode == 3) {
+               }
+       nfs.add("Center", ntop);
+
+       // Add the appropriate tabs
+       if (filemgr.sambamode && filemgr.nfsmode != 0) {
+               TabbedPanel tab = new TabbedPanel();
+               tab.addItem(filemgr.text("share_samba"), samba);
+               tab.addItem(filemgr.text("share_nfs"), nfs);
+               add("Center", tab);
+               }
+       else if (filemgr.sambamode)
+               add("Center", samba);
+       else if (filemgr.nfsmode != 0)
+               add("Center", nfs);
+
+       // Create save and cancel buttons
+       Panel bot = new Panel();
+       bot.setLayout(new FlowLayout(FlowLayout.RIGHT));
+       bot.add(save_b = new CbButton(filemgr.get_image("save.gif"),
+                                     filemgr.text("save"),
+                                     CbButton.LEFT, this));
+       bot.add(cancel_b = new CbButton(filemgr.get_image("cancel.gif"),
+                                       filemgr.text("cancel"),
+                                       CbButton.LEFT, this));
+       add("South", bot);
+       pack();
+       show();
+       }
+
+       public void click(CbButton b)
+       {
+       if (b == save_b) {
+               // Update samba settings on server
+               if (sshare != null && samba_on.getState()) {
+                       // Updating share
+                       sshare.available = available_on.getState();
+                       sshare.writable = writable_on.getState();
+                       sshare.guest = guest_only.getState() ? 2 :
+                                      guest_on.getState() ? 1 : 0;
+                       sshare.comment = comment.getText();
+                       String rv[] = filemgr.get_text(
+                               "save_share.cgi?"+sshare.params());
+                       }
+               else if (sshare != null) {
+                       // Deleting share
+                       String rv[] = filemgr.get_text(
+                               "save_share.cgi?delete=1&"+sshare.params());
+                       filemgr.stab.remove(sshare.path);
+                       }
+               else if (samba_on.getState()) {
+                       // Creating share
+                       sshare = new SambaShare(file.path,
+                                               available_on.getState(),
+                                               writable_on.getState(),
+                                               guest_only.getState() ? 2 :
+                                               guest_on.getState() ? 1 : 0,
+                                               comment.getText());
+                       filemgr.stab.put(sshare.path, sshare);
+                       String rv[] = filemgr.get_text(
+                               "save_share.cgi?new=1&"+sshare.params());
+                       }
+
+               // Update NFS settings on server
+               if (filemgr.nfsmode == 1) {
+                       if (lexport != null && nfs_on.getState()) {
+                               // Updating export
+                               export_options(lexport);
+                               String rv[] = filemgr.get_text(
+                                       "save_export.cgi?"+lexport.params());
+                               }
+                       else if (lexport != null) {
+                               // Deleting export
+                               String rv[] = filemgr.get_text(
+                                 "save_export.cgi?delete=1&"+lexport.params());
+                               filemgr.ntab.remove(lexport.path);
+                               }
+                       else if (nfs_on.getState()) {
+                               // Creating export
+                               lexport = new LinuxExport(file.path, null,
+                                                         null, null);
+                               export_options(lexport);
+                               String rv[] = filemgr.get_text(
+                                 "save_export.cgi?new=1&"+lexport.params());
+                               filemgr.ntab.put(lexport.path, lexport);
+                               }
+                       }
+               else if (filemgr.nfsmode == 2) {
+                       if (dexport != null && nfs_on.getState()) {
+                               // Updating share
+                               dexport.desc = desc.getText();
+                               dexport.ro = ro[0].getState() ? "-" :
+                                            ro[1].getState() ? "" :
+                                            rohosts.getText();
+                               dexport.rw = rw[0].getState() ? "-" :
+                                            rw[1].getState() ? "" :
+                                            rwhosts.getText();
+                               dexport.root = root[0].getState() ? "-" :
+                                              roothosts.getText();
+                               String rv[] = filemgr.get_text(
+                                       "save_export.cgi?"+dexport.params());
+                               }
+                       else if (dexport != null) {
+                               // Deleting share
+                               String rv[] = filemgr.get_text(
+                                 "save_export.cgi?delete=1&"+dexport.params());
+                               filemgr.ntab.remove(dexport.path);
+                               }
+                       else if (nfs_on.getState()) {
+                               // Creating new share
+                               dexport = new DFSAdminExport(file.path,
+                                       desc.getText(),
+                                       ro[0].getState() ? "-" :
+                                       ro[1].getState() ? "" :
+                                       rohosts.getText(),
+                                       rw[0].getState() ? "-" :
+                                       rw[1].getState() ? "" :
+                                       rwhosts.getText(),
+                                       root[0].getState() ? "-" :
+                                       roothosts.getText());
+                               String rv[] = filemgr.get_text(
+                                   "save_export.cgi?new=1&"+dexport.params());
+                               filemgr.ntab.put(dexport.path, dexport);
+                               }
+                       }
+               else if (filemgr.nfsmode == 3) {
+                       }
+
+               filemgr.show_files(filemgr.showing_files);
+               dispose();
+               }
+       else if (b == cancel_b)
+               dispose();
+       }
+
+       void setup_leftright(Panel m, Panel l, Panel r)
+       {
+       m.setLayout(new BorderLayout());
+       Panel p = new Panel();
+       p.setLayout(new BorderLayout());
+       p.add("West", l);
+       p.add("Center", r);
+       l.setLayout(new GridLayout(0, 1));
+       r.setLayout(new GridLayout(0, 1));
+       m.add("North", p);
+       }
+
+       void add_item(String t, Component c, Panel l, Panel r)
+       {
+       l.add(new Label(t));
+       Panel p = new Panel();
+       p.setLayout(new BorderLayout());
+       p.add("West", c);
+       r.add(p);
+       }
+
+       TextField add_hosts(String name, String v, Checkbox cb[],
+                           Panel l, Panel r)
+       {
+       Panel p = new Panel();
+       p.setLayout(new GridLayout(1, 3));
+       CheckboxGroup g = new CheckboxGroup();
+       p.add(cb[0] = new Checkbox(filemgr.text("share_none"), g,
+                                  v.equals("-")));
+       p.add(cb[1] = new Checkbox(filemgr.text("share_all"), g,
+                                  v.length() == 0));
+       p.add(cb[2] = new Checkbox(filemgr.text("share_listed"), g,
+                                  v.length() > 1));
+       add_item(name, p, l, r);
+       TextField t = new TextField(v.equals("-") ? "" : v, 25);
+       add_item("", t, l, r);
+       return t;
+       }
+
+       Choice squashbox(int s)
+       {
+       Choice rv = new Choice();
+       rv.addItem(filemgr.text("share_s0"));
+       rv.addItem(filemgr.text("share_s1"));
+       rv.addItem(filemgr.text("share_s2"));
+       rv.select(s);
+       return rv;
+       }
+
+       Choice robox(boolean r)
+       {
+       Choice rv = new Choice();
+       rv.addItem(filemgr.text("share_lrw"));
+       rv.addItem(filemgr.text("share_lro"));
+       rv.select(r ? 1 : 0);
+       return rv;
+       }
+
+       Panel opts_panel(Component ro, Component squash)
+       {
+       Panel p = new Panel();
+       p.setLayout(new BorderLayout());
+       p.add("West", ro);
+       p.add("East", squash);
+       return p;
+       }
+
+       void export_options(LinuxExport e)
+       {
+       int c = 0;
+       for(int i=0; i<host.length; i++)
+               if (host[i].getText().length() > 0)
+                       c++;
+       e.host = new String[c];
+       e.ro = new boolean[c];
+       e.squash = new int[c];
+       for(int i=0,j=0; i<host.length; i++) {
+               if (host[i].getText().trim().length() > 0) {
+                       e.host[j] = host[i].getText();
+                       e.ro[j] = lro[i].getSelectedIndex() == 1;
+                       e.squash[j] = squash[i].getSelectedIndex();
+                       j++;
+                       }
+               }
+       }
+
+}
+
+class SearchWindow extends FixedFrame
+       implements CbButtonCallback,MultiColumnCallback
+{
+       TabbedPanel tab;
+       MultiColumn list;
+       CbButton search_b, cancel_b;
+       FileManager filemgr;
+       TextField dir, match, user, group;
+       Checkbox uany, usel, gany, gsel;
+       Choice type;
+       Checkbox sany, smore, sless;
+       TextField more, less;
+       Checkbox xon, xoff;
+       String types[] = { "", "f", "d", "l", "p" };
+       RemoteFile results[];
+
+       SearchWindow(String d, FileManager p)
+       {
+       filemgr = p;
+       setTitle(filemgr.text("search_title"));
+
+       // setup UI
+       setLayout(new BorderLayout());
+       tab = new TabbedPanel();
+       Panel search = new Panel();
+       search.setLayout(new BorderLayout());
+       tab.addItem(filemgr.text("search_crit"), search);
+       Panel l = new Panel(), r = new Panel();
+       l.setLayout(new GridLayout(0, 1));
+       r.setLayout(new GridLayout(0, 1));
+
+       String cols[] = { "", filemgr.text("right_name"),
+                         filemgr.text("right_size") };
+       float widths[] = { .07f, .78f, .15f };
+       list = new MultiColumn(cols, this);
+       list.setWidths(widths);
+       list.setDrawLines(false);
+       tab.addItem(filemgr.text("search_list"), list);
+
+       add_item(filemgr.text("search_dir"), dir = new TextField(d, 30), l, r);
+
+       add_item(filemgr.text("search_match"), match = new TextField(20), l, r);
+
+       Panel up = new Panel();
+       up.setLayout(new FlowLayout(FlowLayout.LEFT, 1, 1));
+       CheckboxGroup ug = new CheckboxGroup();
+       up.add(uany = new Checkbox(filemgr.text("search_any"), ug, true));
+       up.add(usel = new Checkbox("", ug, false));
+       up.add(user = new TextField(10));
+       add_item(filemgr.text("search_user"), up, l, r);
+
+       Panel gp = new Panel();
+       gp.setLayout(new FlowLayout(FlowLayout.LEFT, 1, 1));
+       CheckboxGroup gg = new CheckboxGroup();
+       gp.add(gany = new Checkbox(filemgr.text("search_any"), gg, true));
+       gp.add(gsel = new Checkbox("", gg, false));
+       gp.add(group = new TextField(10));
+       add_item(filemgr.text("search_group"), gp, l, r);
+
+       type = new Choice();
+       for(int i=0; i<types.length; i++)
+               type.addItem(filemgr.text("search_types_"+types[i]));
+       add_item(filemgr.text("search_type"), type, l, r);
+
+       CheckboxGroup sg = new CheckboxGroup();
+       add_item(filemgr.text("search_size"),
+                sany = new Checkbox(filemgr.text("search_any"), sg, true),
+                l, r);
+       Panel mp = new Panel();
+       mp.setLayout(new FlowLayout(FlowLayout.LEFT, 1, 1));
+       mp.add(smore = new Checkbox(filemgr.text("search_more"), sg, false));
+       mp.add(more = new TextField(10));
+       add_item("", mp, l, r);
+       Panel lp = new Panel();
+       lp.setLayout(new FlowLayout(FlowLayout.LEFT, 1, 1));
+       lp.add(sless = new Checkbox(filemgr.text("search_less"), sg, false));
+       lp.add(less = new TextField(10));
+       add_item("", lp, l, r);
+
+       CheckboxGroup xg = new CheckboxGroup();
+       Panel xp = new Panel();
+       xp.setLayout(new FlowLayout(FlowLayout.LEFT, 1, 1));
+       xp.add(xoff = new Checkbox(filemgr.text("yes"), xg, true));
+       xp.add(xon = new Checkbox(filemgr.text("no"), xg, false));
+       add_item(filemgr.text("search_xdev"), xp, l, r);
+
+       search.add("West", l); search.add("East", r);
+       add("Center", tab);
+
+       // Create search and cancel buttons
+       Panel bot = new Panel();
+       bot.setLayout(new FlowLayout(FlowLayout.RIGHT));
+       bot.add(search_b = new CbButton(filemgr.get_image("save.gif"),
+                                     filemgr.text("search_ok"),
+                                     CbButton.LEFT, this));
+       bot.add(cancel_b = new CbButton(filemgr.get_image("cancel.gif"),
+                                       filemgr.text("cancel"),
+                                       CbButton.LEFT, this));
+       add("South", bot);
+       pack();
+       show();
+       }
+
+       void add_item(String t, Component c, Panel l, Panel r)
+       {
+       l.add(new Label(t));
+       Panel p = new Panel();
+       p.setLayout(new BorderLayout());
+       p.add("West", c);
+       r.add(p);
+       }
+
+       public void click(CbButton b)
+       {
+       if (b == cancel_b)
+               dispose();
+       else if (b == search_b) {
+               // validate inputs and build search URL
+               String url = "search.cgi";
+               String d = dir.getText().trim();
+               if (d.length() == 0 || d.charAt(0) != '/') {
+                       new ErrorWindow(filemgr.text("search_edir"));
+                       return;
+                       }
+               url += "?dir="+filemgr.urlize(d);
+               String mt = match.getText().trim();
+               if (mt.length() == 0) {
+                       new ErrorWindow(filemgr.text("search_ematch"));
+                       return;
+                       }
+               url += "&match="+filemgr.urlize(mt);
+               if (type.getSelectedIndex() > 0)
+                       url += "&type="+types[type.getSelectedIndex()];
+               if (usel.getState()) {
+                       String u = user.getText().trim();
+                       if (u.length() == 0) {
+                               new ErrorWindow(filemgr.text("search_euser"));
+                               return;
+                               }
+                       url += "&user="+filemgr.urlize(u);
+                       }
+               if (gsel.getState()) {
+                       String g = group.getText().trim();
+                       if (g.length() == 0) {
+                               new ErrorWindow(filemgr.text("search_egroup"));
+                               return;
+                               }
+                       url += "&group="+filemgr.urlize(g);
+                       }
+               if (smore.getState()) {
+                       String m = more.getText().trim();
+                       try { Integer.parseInt(m); }
+                       catch(Exception e) {
+                               new ErrorWindow(filemgr.text("search_esize"));
+                               return;
+                               }
+                       url += "&size=%2B"+m+"c";
+                       }
+               else if (sless.getState()) {
+                       String l = less.getText().trim();
+                       try { Integer.parseInt(l); }
+                       catch(Exception e) {
+                               new ErrorWindow(filemgr.text("search_esize"));
+                               return;
+                               }
+                       url += "&size=%2D"+l+"c";
+                       }
+               if (xon.getState())
+                       url += "&xdev=1";
+
+               // send off the search
+               setCursor(WAIT_CURSOR);
+               String f[] = filemgr.get_text(url);
+               if (f[0].length() > 0) {
+                       new ErrorWindow(f[0]);
+                       return;
+                       }
+               Object rows[][] = new Object[f.length-1][];
+               results = new RemoteFile[f.length-1];
+               for(int i=1; i<f.length; i++) {
+                       RemoteFile r = new RemoteFile(filemgr, f[i], null);
+                       results[i-1] = r;
+                       Object row[] = rows[i-1] = new Object[3];
+                       row[0] = filemgr.get_image(RemoteFile.tmap[r.type]);
+                       row[1] = r.path;
+                       if (r.size < 1000)
+                               row[2] = filemgr.spad(r.size, 5)+" B";
+                       else if (r.size < 1000000)
+                               row[2] = filemgr.spad(r.size/1000, 5)+" kB";
+                       else
+                               row[2] = filemgr.spad(r.size/1000000, 5)+" MB";
+                       }
+               list.clear();
+               list.addItems(rows);
+               tab.select(filemgr.text("search_list"));
+               setCursor(DEFAULT_CURSOR);
+               }
+       }
+
+       public void singleClick(MultiColumn list, int num)
+       {
+       }
+
+       // go to the directory of the double-clicked file
+       public void doubleClick(MultiColumn list, int num)
+       {
+       RemoteFile f = results[num];
+       int sl = f.path.lastIndexOf('/');
+       String dir = sl == 0 ? "/" : f.path.substring(0, sl);
+       filemgr.find_directory(dir, true);
+       RemoteFile l[] = filemgr.showing_list;
+       for(int i=0; i<l.length; i++) {
+               if (l[i].name.equals(f.name)) {
+                       // select the file in the list
+                       filemgr.files.select(i+1);
+                       filemgr.files.scrollto(i+1);
+                       break;
+                       }
+               }
+       dispose();
+       }
+
+       public void headingClicked(MultiColumn list, int col)
+       {
+       }
+}
+
+class FileSystem
+{
+       String mount;
+       String dev;
+       String type;
+       String opts[];
+       boolean acls;
+       boolean attrs;
+       boolean ext;
+
+       FileSystem(String l)
+       {
+       StringSplitter tok = new StringSplitter(l, ' ');
+       mount = tok.nextToken();
+       dev = tok.nextToken();
+       type = tok.nextToken();
+       String optstr = tok.nextToken();
+       acls = tok.nextToken().equals("1");
+       attrs = tok.nextToken().equals("1");
+       ext = tok.nextToken().equals("1");
+
+       StringTokenizer tok2 = new StringTokenizer(optstr, ",");
+       opts = new String[tok2.countTokens()];
+       for(int i=0; i<opts.length; i++)
+               opts[i] = tok2.nextToken();
+       }
+}
+
+class ACLEntry
+{
+       FileManager filemgr;
+       RemoteFile file;
+       boolean def;
+       String type;
+       String owner;
+       boolean read, write, exec;
+
+       ACLEntry(String l, ACLWindow w)
+       {
+       filemgr = w.filemgr;
+       file = w.file;
+       StringSplitter tok = new StringSplitter(l, ':');
+       type = tok.nextToken();
+       if (type.equals("default")) {
+               def = true;
+               type = tok.nextToken();
+               }
+       if (!type.equals("mask") && !type.equals("other")) {
+               owner = tok.nextToken();
+               if (owner.length() == 0)
+                       owner = null;
+               }
+       String rwx = tok.nextToken();
+       if (rwx.length() == 0)
+               rwx = tok.nextToken();  // getfacl outputs a blank owner for mask
+                                       // and other on some systems
+       read = (rwx.charAt(0) == 'r');
+       write = (rwx.charAt(1) == 'w');
+       exec = (rwx.charAt(2) == 'x');
+       }
+
+       ACLEntry(ACLWindow w)
+       {
+       filemgr = w.filemgr;
+       file = w.file;
+       }
+
+       String[] getRow()
+       {
+       String rv[] = new String[3];
+       String t = def ? "acltype_default_"+type : "acltype_"+type;
+       rv[0] = filemgr.text(t);
+       if (type.equals("mask") || type.equals("other") ||
+           (def && owner == null))
+               rv[1] = "";
+       else if (owner != null)
+               rv[1] = owner;
+       else if (type.equals("user"))
+               rv[1] = filemgr.text("eacl_user", file.user);
+       else
+               rv[1] = filemgr.text("eacl_group", file.group);
+       rv[2] = "";
+       if (read) rv[2] += filemgr.text("info_read")+" ";
+       if (write) rv[2] += filemgr.text("info_write")+" ";
+       if (exec) rv[2] += filemgr.text("info_exec")+" ";
+       return rv;
+       }
+
+       public String toString()
+       {
+       String rv = def ? "default:" : "";
+       rv += type+":";
+       if (!type.equals("mask") && !type.equals("other"))
+               rv += (owner == null ? "" : owner)+":";
+       rv += (read ? 'r' : '-');
+       rv += (write ? 'w' : '-');
+       rv += (exec ? 'x' : '-');
+       return rv;
+       }
+}
+
+class ACLEditor extends FixedFrame implements CbButtonCallback
+{
+       FileManager filemgr;
+       ACLWindow aclwin;
+       ACLEntry acl;
+       boolean creating;
+       CbButton ok, del;
+       Checkbox read, write, exec, owner1, owner2;
+       TextField owner;
+
+       // Editing an existing ACL entry
+       ACLEditor(ACLWindow w, ACLEntry a)
+       {
+       aclwin = w;
+       filemgr = aclwin.filemgr;
+       acl = a;
+       creating = false;
+       makeUI();
+       }
+
+       // Creating a new ACL entry
+       ACLEditor(ACLWindow w, String type, boolean def)
+       {
+       aclwin = w;
+       filemgr = aclwin.filemgr;
+       acl = new ACLEntry(aclwin);
+       acl.def = def;
+       acl.type = type;
+       creating = true;
+       makeUI();
+       }
+
+       void makeUI()
+       {
+       setTitle(filemgr.text(creating ? "eacl_create" : "eacl_edit"));
+       setLayout(new BorderLayout());
+       Panel left = new Panel();
+       left.setLayout(new GridLayout(0, 1));
+       add("West", left);
+       Panel right = new Panel();
+       right.setLayout(new GridLayout(0, 1));
+       add("East", right);
+
+       left.add(new Label(filemgr.text("eacl_acltype")));
+       TextField type;
+       right.add(type = new TextField(
+                               (acl.def ? "default " : "")+acl.type, 20));
+       type.setEditable(false);
+
+       if (!acl.type.equals("mask") && !acl.type.equals("other")) {
+               left.add(new Label(filemgr.text("eacl_aclname")));
+               if (acl.def) {
+                       // A default user or group ACL .. can be for
+                       // a specific user, or for the file owner
+                       Panel op = new Panel();
+                       op.setLayout(new FlowLayout(FlowLayout.LEFT, 0, 0));
+                       CheckboxGroup gr = new CheckboxGroup();
+                       op.add(owner1 = new Checkbox(filemgr.text("eacl_owner"),
+                                           gr, acl.owner == null));
+                       op.add(owner2 = new Checkbox("",
+                                           gr, acl.owner != null));
+                       op.add(owner = new TextField(
+                               acl.owner == null ? "" : acl.owner, 20));
+                       right.add(op);
+                       }
+               else if (creating || acl.owner != null) {
+                       // A user or group ACL for a specific user
+                       owner = new TextField(
+                                       acl.owner == null ? "" : acl.owner, 20);
+                       right.add(owner);
+                       }
+               else {
+                       // A user or group ACL for the file owner
+                       String str;
+                       if (acl.type.equals("user"))
+                           str = filemgr.text("eacl_user", aclwin.file.user);
+                       else
+                           str = filemgr.text("eacl_group", aclwin.file.group);
+                       TextField o = new TextField(str);
+                       o.setEditable(false);
+                       right.add(o);
+                       }
+               }
+
+       left.add(new Label(filemgr.text("eacl_aclperms")));
+       Panel pp = new Panel();
+       pp.setLayout(new FlowLayout(FlowLayout.RIGHT));
+       pp.add(read = new Checkbox(filemgr.text("info_read"), null, acl.read));
+       pp.add(write = new Checkbox(filemgr.text("info_write"), null, acl.write));
+       pp.add(exec = new Checkbox(filemgr.text("info_exec"), null, acl.exec));
+       right.add(pp);
+
+       Panel bot = new Panel();
+       bot.setLayout(new FlowLayout(FlowLayout.RIGHT));
+       bot.add(ok = new CbButton(filemgr.get_image("save.gif"),
+                                 filemgr.text("save"),
+                                 CbButton.LEFT, this));
+       if (!creating && (acl.owner != null || acl.def))
+               bot.add(del = new CbButton(filemgr.get_image("cancel.gif"),
+                                          filemgr.text("delete"),
+                                          CbButton.LEFT, this));
+       add("South", bot);
+
+       pack();
+       show();
+       }
+
+       public void click(CbButton b)
+       {
+       if (b == ok) {
+               // Update or add the ACL entry
+               if (owner1 != null && owner1.getState()) {
+                       acl.owner = null;
+                       }
+               else if (owner != null) {
+                       String o = owner.getText().trim();
+                       if (o.length() == 0 && !acl.def) {
+                               new ErrorWindow(filemgr.text("eacl_eowner"));
+                               return;
+                               }
+                       acl.owner = owner.getText();
+                       if (acl.owner.length() == 0)
+                               acl.owner = null;
+                       }
+               acl.read = read.getState();
+               acl.write = write.getState();
+               acl.exec = exec.getState();
+               if (creating) {
+                       // Add to the ACL table
+                       aclwin.acllist.addElement(acl);
+                       aclwin.acltable.addItem(acl.getRow());
+                       }
+               else {
+                       // Update the table
+                       int idx = aclwin.acllist.indexOf(acl);
+                       aclwin.acltable.modifyItem(acl.getRow(), idx);
+                       }
+               dispose();
+               }
+       else if (b == del) {
+               // Remove this entry
+               int idx = aclwin.acllist.indexOf(acl);
+               aclwin.acllist.removeElementAt(idx);
+               aclwin.acltable.deleteItem(idx);
+               dispose();
+               }
+       }
+
+       public void dispose()
+       {
+       aclwin.edmap.remove(acl);
+       super.dispose();
+       }
+}
+
+class ACLWindow extends FixedFrame implements CbButtonCallback,MultiColumnCallback
+{
+       FileManager filemgr;
+       RemoteFile file;
+       Vector acllist = new Vector();
+       Hashtable edmap = new Hashtable();
+
+       CbButton ok, cancel, add;
+       Choice addtype;
+       MultiColumn acltable;
+
+       String acltypes[] = { "user", "group", "mask",
+                             "default user", "default group", "default other",
+                             "default mask" };
+
+       ACLWindow(FileManager p, RemoteFile f)
+       {
+       super(400, 300);
+       setTitle(p.text("eacl_title", f.path));
+       filemgr = p;
+       file = f;
+
+       // Get the ACLs
+       String a[] = filemgr.get_text(
+                       "getfacl.cgi?file="+filemgr.urlize(file.path));
+       if (a[0].length() != 0) {
+               new ErrorWindow(filemgr.text("eacl_eacls", a[0]));
+               return;
+               }
+
+       // Create the UI
+       setLayout(new BorderLayout());
+       String titles[] = { filemgr.text("eacl_acltype"),
+                           filemgr.text("eacl_aclname"),
+                           filemgr.text("eacl_aclperms") };
+       acltable = new MultiColumn(titles, this);
+       for(int i=1; i<a.length; i++) {
+               ACLEntry acl = new ACLEntry(a[i], this);
+               acllist.addElement(acl);
+               acltable.addItem(acl.getRow());
+               }
+       add("Center", acltable);
+       Panel abot = new Panel();
+       abot.setLayout(new FlowLayout(FlowLayout.RIGHT));
+       abot.add(add = new CbButton(filemgr.get_image("add.gif"),
+                                  filemgr.text("eacl_add"),
+                                  CbButton.LEFT, this));
+       int len = file.type == RemoteFile.DIR ? acltypes.length : 3;
+       abot.add(addtype = new Choice());
+       for(int i=0; i<len; i++) {
+               String t = "acltype_"+acltypes[i].replace(' ', '_');
+               addtype.addItem(filemgr.text(t));
+               }
+       abot.add(new Label(" "));
+       abot.add(ok = new CbButton(filemgr.get_image("save.gif"),
+                                  filemgr.text("save"),
+                                  CbButton.LEFT, this));
+       abot.add(cancel = new CbButton(filemgr.get_image("cancel.gif"),
+                                      filemgr.text("cancel"),
+                                      CbButton.LEFT, this));
+       add("South", abot);
+
+       pack();
+       show();
+       }
+
+       public void click(CbButton b)
+       {
+       if (b == ok) {
+               // Check if there are any defaults, and if so there must
+               // be default user, group and other
+               boolean anydef = false, defuser = false,
+                       defgroup = false, defother = false;
+               for(int i=0; i<acllist.size(); i++) {
+                       ACLEntry e = (ACLEntry)acllist.elementAt(i);
+                       if (e.def) anydef = true;
+                       if (e.def && e.owner == null) {
+                               if (e.type.equals("user")) defuser = true;
+                               if (e.type.equals("group")) defgroup = true;
+                               if (e.type.equals("other")) defother = true;
+                               }
+                       }
+               if (anydef && (!defuser || !defgroup || !defother)) {
+                       new ErrorWindow(filemgr.text("eacl_edefaults"));
+                       return;
+                       }
+
+               // Save the ACLs
+               String aclstr = "";
+               for(int i=0; i<acllist.size(); i++)
+                       aclstr += (ACLEntry)acllist.elementAt(i)+"\n";
+               String rv[] = filemgr.get_text("setfacl.cgi?file="+
+                                               filemgr.urlize(file.path)+
+                                               "&acl="+filemgr.urlize(aclstr));
+               if (rv[0].length() > 0)
+                       new ErrorWindow(filemgr.text("eacl_efailed",
+                               file.path, rv[0]));
+               else
+                       dispose();
+               }
+       else if (b == add) {
+               // Open a window for a new ACL entry
+               String t = acltypes[addtype.getSelectedIndex()];
+               String d = "default ";
+               boolean def = t.startsWith(d);
+               if (def)
+                       t = t.substring(d.length());
+               if (t.equals("mask")) {
+                       // Only allow one mask
+                       for(int i=0; i<acllist.size(); i++) {
+                               ACLEntry a = (ACLEntry)acllist.elementAt(i);
+                               if (a.type.equals(t) && a.def == def) {
+                                       new ErrorWindow(filemgr.text(def ?
+                                           "eacl_edefmask" : "eacl_emask"));
+                                       return;
+                                       }
+                               }
+                       }
+               new ACLEditor(this, t, def);
+               }
+       else if (b == cancel) {
+               // Don't save
+               dispose();
+               }
+       }
+
+       // Bring up an editor for an ACL
+        public void doubleClick(MultiColumn list, int num)
+       {
+       int idx = list.selected();
+       if (idx >= 0) {
+               ACLEntry e = (ACLEntry)acllist.elementAt(idx);
+               ACLEditor ed = (ACLEditor)edmap.get(e);
+               if (ed == null)
+                       edmap.put(e, new ACLEditor(this, e));
+               else {
+                       ed.toFront();
+                       ed.requestFocus();
+                       }
+               }
+       }
+
+        public void singleClick(MultiColumn list, int num)
+       {
+       }
+
+       public void headingClicked(MultiColumn list, int col)
+       {
+       }
+}
+
+class AttributesWindow extends FixedFrame
+       implements CbButtonCallback,MultiColumnCallback
+{
+       FileManager filemgr;
+       RemoteFile file;
+       Vector attrlist = new Vector();
+       Hashtable edmap = new Hashtable();
+
+       CbButton ok, cancel, add;
+       MultiColumn attrtable;
+
+       AttributesWindow(FileManager p, RemoteFile f)
+       {
+       super(400, 300);
+       setTitle(p.text("attr_title", f.path));
+       filemgr = p;
+       file = f;
+
+       // Get the attributes
+       String a[] = filemgr.get_text(
+                       "getattrs.cgi?file="+filemgr.urlize(file.path));
+       if (a[0].length() != 0) {
+               new ErrorWindow(filemgr.text("attr_eattrs", a[0]));
+               return;
+               }
+
+       // Create the UI
+       setLayout(new BorderLayout());
+       String titles[] = { filemgr.text("attr_name"),
+                           filemgr.text("attr_value") };
+       attrtable = new MultiColumn(titles, this);
+       for(int i=1; i<a.length; i++) {
+               FileAttribute at = new FileAttribute(a[i], filemgr);
+               attrlist.addElement(at);
+               attrtable.addItem(at.getRow());
+               }
+       add("Center", attrtable);
+       Panel abot = new Panel();
+       abot.setLayout(new FlowLayout(FlowLayout.RIGHT));
+       abot.add(add = new CbButton(filemgr.get_image("add.gif"),
+                                  filemgr.text("attr_add"),
+                                  CbButton.LEFT, this));
+       abot.add(new Label(" "));
+       abot.add(ok = new CbButton(filemgr.get_image("save.gif"),
+                                  filemgr.text("save"),
+                                  CbButton.LEFT, this));
+       abot.add(cancel = new CbButton(filemgr.get_image("cancel.gif"),
+                                      filemgr.text("cancel"),
+                                      CbButton.LEFT, this));
+       add("South", abot);
+
+       pack();
+       show();
+       }
+
+       public void click(CbButton b)
+       {
+       if (b == ok) {
+               // Save the attributes
+               String pstr = "";
+               for(int i=0; i<attrlist.size(); i++) {
+                       FileAttribute at = (FileAttribute)attrlist.elementAt(i);
+                       pstr += "&name"+i+"="+filemgr.urlize(at.name)+
+                               "&value"+i+"="+filemgr.urlize(at.value);
+                       }
+               String rv[] = filemgr.get_text("setattrs.cgi?file="+
+                                               filemgr.urlize(file.path)+pstr);
+               if (rv[0].length() > 0)
+                       new ErrorWindow(filemgr.text("attr_efailed",
+                               file.path, rv[0]));
+               else
+                       dispose();
+               }
+       else if (b == add) {
+               // Open a window for a new ACL entry
+               new AttributeEditor(this);
+               }
+       else if (b == cancel) {
+               // Don't save
+               dispose();
+               }
+       }
+
+       // Bring up an editor for an ACL
+        public void doubleClick(MultiColumn list, int num)
+       {
+       int idx = list.selected();
+       if (idx >= 0) {
+               FileAttribute at = (FileAttribute)attrlist.elementAt(idx);
+               AttributeEditor ed = (AttributeEditor)edmap.get(at);
+               if (ed == null)
+                       edmap.put(at, new AttributeEditor(this, at));
+               else {
+                       ed.toFront();
+                       ed.requestFocus();
+                       }
+               }
+       }
+
+        public void singleClick(MultiColumn list, int num)
+       {
+       }
+
+       public void headingClicked(MultiColumn list, int col)
+       {
+       }
+}
+
+class FileAttribute
+{
+       String name;
+       String value;
+
+       FileAttribute(String l, FileManager f)
+       {
+       int eq = l.indexOf('=');
+       name = f.un_urlize(l.substring(0, eq));
+       value = f.un_urlize(l.substring(eq+1));
+       }
+
+       FileAttribute(String n, String v)
+       {
+       name = n;
+       value = v;
+       }
+
+       String[] getRow()
+       {
+       return new String[] { name, value };
+       }
+}
+
+class AttributeEditor extends FixedFrame implements CbButtonCallback
+{
+       FileManager filemgr;
+       AttributesWindow attrwin;
+       FileAttribute attr;
+       boolean creating;
+       CbButton ok, del;
+       TextField name;
+       TextArea value;
+
+       AttributeEditor(AttributesWindow w, FileAttribute a)
+       {
+       attrwin = w;
+       attr = a;
+       filemgr = w.filemgr;
+       creating = false;
+       makeUI();
+       }
+
+       AttributeEditor(AttributesWindow w)
+       {
+       attrwin = w;
+       attr = new FileAttribute("", "");
+       filemgr = w.filemgr;
+       creating = true;
+       makeUI();
+       }
+
+       void makeUI()
+       {
+       setTitle(filemgr.text(creating ? "attr_create" : "attr_edit"));
+       setLayout(new BorderLayout());
+
+       Panel top = new Panel();
+       top.setLayout(new GridLayout(1, 2));
+       top.add(new Label(filemgr.text("attr_name")));
+       top.add(name = new TextField(attr.name, 20));
+       add("North", top);
+
+       Panel mid = new Panel();
+       mid.setLayout(new GridLayout(1, 2));
+       mid.add(new Label(filemgr.text("attr_value")));
+       mid.add(value = new TextArea(attr.value, 5, 20));
+       add("Center", mid);
+
+       Panel bot = new Panel();
+       bot.setLayout(new FlowLayout(FlowLayout.RIGHT));
+       bot.add(ok = new CbButton(filemgr.get_image("save.gif"),
+                                 filemgr.text("save"),
+                                 CbButton.LEFT, this));
+       if (!creating)
+               bot.add(del = new CbButton(filemgr.get_image("cancel.gif"),
+                                          filemgr.text("delete"),
+                                          CbButton.LEFT, this));
+       add("South", bot);
+
+       pack();
+       show();
+       }
+
+       public void click(CbButton b)
+       {
+       if (b == ok) {
+               // Update or add the attribute
+               if (name.getText().length() == 0) {
+                       new ErrorWindow(filemgr.text("attr_ename"));
+                       return;
+                       }
+               attr.name = name.getText();
+               attr.value = value.getText();
+               if (creating) {
+                       // Add to the attribs table
+                       attrwin.attrlist.addElement(attr);
+                       attrwin.attrtable.addItem(attr.getRow());
+                       }
+               else {
+                       // Update the table
+                       int idx = attrwin.attrlist.indexOf(attr);
+                       attrwin.attrtable.modifyItem(attr.getRow(), idx);
+                       }
+               dispose();
+               }
+       else if (b == del) {
+               // Remove this entry
+               int idx = attrwin.attrlist.indexOf(attr);
+               attrwin.attrlist.removeElementAt(idx);
+               attrwin.attrtable.deleteItem(idx);
+               dispose();
+               }
+       }
+
+       public void dispose()
+       {
+       attrwin.edmap.remove(attr);
+       super.dispose();
+       }
+}
+
+class EXTWindow extends FixedFrame implements CbButtonCallback
+{
+       FileManager filemgr;
+       RemoteFile file;
+
+       CbButton ok, cancel;
+       Checkbox cbs[];
+
+       String attrs[] = { "A", "a", "c", "d", "i", "s", "S", "u" };
+       Hashtable attrmap = new Hashtable();
+
+       EXTWindow(FileManager p, RemoteFile f)
+       {
+       super();
+       setTitle(p.text("ext_title", f.path));
+       filemgr = p;
+       file = f;
+
+       // Get the attributes
+       String a[] = filemgr.get_text(
+                       "getext.cgi?file="+filemgr.urlize(file.path));
+       if (a[0].length() != 0) {
+               new ErrorWindow(filemgr.text("ext_eattrs", a[0]));
+               return;
+               }
+       for(int i=0; i<a[1].length(); i++)
+               attrmap.put(a[1].substring(i, i+1), "");
+
+       // Create the UI
+       setLayout(new BorderLayout());
+       Panel top = new LinedPanel(filemgr.text("ext_header"));
+       top.setLayout(new GridLayout(0, 1));
+       cbs = new Checkbox[attrs.length];
+       for(int i=0; i<attrs.length; i++) {
+               cbs[i] = new Checkbox(filemgr.text("eattr_"+attrs[i]));
+               cbs[i].setState(attrmap.get(attrs[i]) != null);
+               top.add(cbs[i]);
+               }
+       add("Center", top);
+
+       Panel bot = new Panel();
+       bot.setLayout(new FlowLayout(FlowLayout.RIGHT));
+       bot.add(ok = new CbButton(filemgr.get_image("save.gif"),
+                                 filemgr.text("save"),
+                                 CbButton.LEFT, this));
+       bot.add(cancel = new CbButton(filemgr.get_image("cancel.gif"),
+                                     filemgr.text("cancel"),
+                                     CbButton.LEFT, this));
+       add("South", bot);
+
+       pack();
+       show();
+       }
+
+       public void click(CbButton b)
+       {
+       if (b == ok) {
+               // Save the attributes (including unknown ones)
+               String astr = "";
+               for(int i=0; i<cbs.length; i++) {
+                       if (cbs[i].getState())
+                               astr += attrs[i];
+                       attrmap.remove(attrs[i]);
+                       }
+               for(Enumeration e = attrmap.keys(); e.hasMoreElements(); )
+                       astr += e.nextElement();
+
+               // Try to set on the server
+               String rv[] = filemgr.get_text("setext.cgi?file="+
+                               filemgr.urlize(file.path)+"&attrs="+astr);
+               if (rv[0].length() > 0)
+                       new ErrorWindow(filemgr.text("ext_efailed",
+                               file.path, rv[0]));
+               else
+                       dispose();
+               }
+       else if (b == cancel) {
+               dispose();
+               }
+       }
+}
+
diff --git a/file/FixedFrame.java b/file/FixedFrame.java
new file mode 100644 (file)
index 0000000..a22674b
--- /dev/null
@@ -0,0 +1,46 @@
+import java.awt.*;
+import java.io.*;
+
+public class FixedFrame extends Frame
+{
+       int mw = 0, mh = 0;
+
+       public FixedFrame()
+       {
+       Dimension d = Util.tk.getScreenSize();
+       double rx = Math.random(), ry = Math.random();
+       move((int)((d.width/2)*rx), (int)((d.height/2)*ry));
+       }
+
+       public FixedFrame(int w, int h)
+       {
+       this();
+       mw = w; mh = h;
+       }
+
+       public boolean handleEvent(Event evt)
+       {
+       if (evt.target == this && evt.id == Event.WINDOW_DESTROY) {
+               dispose();
+               return true;
+               }
+       return super.handleEvent(evt);
+       }
+
+       public Dimension minimumSize()
+       {
+       if (mw != 0 && mh != 0) return new Dimension(mw, mh);
+       else return super.minimumSize();
+       }
+
+       public Dimension preferredSize()
+       {
+       return minimumSize();
+       }
+
+       public void setFixedSize(int w, int h)
+       {
+       mw = w; mh = h;
+       }
+}
+
diff --git a/file/GrayPanel.java b/file/GrayPanel.java
new file mode 100644 (file)
index 0000000..754bb99
--- /dev/null
@@ -0,0 +1,10 @@
+import java.awt.*;
+
+public class GrayPanel extends Panel
+{
+       public void paint(Graphics g)
+       {
+       g.setColor(Util.body);
+       g.fillRect(0, 0, size().width, size().height);
+       }
+}
diff --git a/file/Hierarchy.java b/file/Hierarchy.java
new file mode 100644 (file)
index 0000000..abb358f
--- /dev/null
@@ -0,0 +1,345 @@
+// Hierarchy
+// An AWT component for displaying a tree-like heirachy, with each node
+// having an icon and a name. This heirachy can be expanded or contracted
+// by the user.
+import java.awt.*;
+import java.util.Vector;
+
+public class Hierarchy extends BorderPanel implements CbScrollbarCallback
+{
+       HierarchyNode root;             // the root of the tree
+       CbScrollbar sb;                 // scrollbar at right
+       int width, height;              // usable drawing area
+       int sbwidth;                    // size of scrollbar
+       HierarchyCallback callback;     // who to call on open / close
+       Image bim;                      // double-buffer image
+       Font font = new Font("courier", Font.PLAIN, 12);
+       FontMetrics fnm;                // size of font used
+       Graphics bg;                    // back-images graphics
+       int top = 0;                    // top-most row displayed
+       int count = 0;                  // total rows in the tree
+       Insets in;                      // insets from border
+       HierarchyNode sel;              // selected node
+       long last;                      // time of last mouse click
+       static boolean broken_awt = System.getProperty("os.name").
+                                   startsWith("Windows");
+
+       // Create a new Hierarchy object with the given root
+       Hierarchy(HierarchyNode r)
+       {
+       this();
+       root = r;
+       }
+
+       // Create a new Hierarchy object that calls back to the given object
+       // when nodes are clicked on.
+       Hierarchy(HierarchyNode r, HierarchyCallback c)
+       {
+       this(r);
+       callback = c;
+       }
+
+       // Create an empty hierarchy object, with no callback
+       Hierarchy()
+       {
+       super(3, Util.dark_edge_hi, Util.body_hi);
+
+       // Create UI
+       setLayout(null);
+       sb = new CbScrollbar(CbScrollbar.VERTICAL, this);
+       add(sb);
+       }
+
+       // Create an empty hierarchy object, set to report user actions to
+       // the given object.
+       Hierarchy(HierarchyCallback c)
+       {
+       this();
+       callback = c;
+       }
+
+       // redraw
+       // Called by the using class when the tree passed to this object
+       // changes, to force a redraw and resizing of the scrollbar
+       void redraw()
+       {
+       if (fnm != null) {
+               render();
+               paint(getGraphics());
+               compscroll();
+               }
+       }
+
+       // setRoot
+       // Set the root node for this hierarchy
+       void setRoot(HierarchyNode r)
+       {
+       root = r;
+       redraw();
+       }
+
+       // selected
+       // Return the currently selected node, or null
+       HierarchyNode selected()
+       {
+       return sel;
+       }
+
+       // select
+       // Selected the given node
+       void select(HierarchyNode s)
+       {
+       sel = s;
+       }
+
+       // force the use of some font
+       public void setFont(Font f)
+       {
+       font = f;
+       bim = null;
+       repaint();
+       }
+
+       // reshape
+       // Called when this component gets resized
+       public void reshape(int nx, int ny, int nw, int nh)
+       {
+       in = insets();
+       sbwidth = sb.minimumSize().width;
+       width = nw-sbwidth - (in.left + in.right);
+       height = nh - (in.top + in.bottom);
+       sb.reshape(width+in.left, in.top, sbwidth, height);
+
+       // force creation of a new backing images
+       bim = null;
+       repaint();
+       compscroll();
+
+       super.reshape(nx, ny, nw, nh);
+       }
+
+       // update
+       // Called sometime after repaint()
+       public void update(Graphics g)
+       {
+       render();
+       paint(g);
+       }
+
+       // paint
+       // Blit the backing image to the front
+       public void paint(Graphics g)
+       {
+       super.paint(g);
+       if (bim == null) {
+               // This is the first rendering
+               bim = createImage(width, height);
+               bg = bim.getGraphics();
+               bg.setFont(font);
+               fnm = bg.getFontMetrics();
+               render();
+               compscroll();
+               }
+       g.drawImage(bim, in.left, in.top, this);
+       }
+
+       // mouseDown
+       // Called upon a mouseclick
+       public boolean mouseDown(Event evt, int x, int y)
+       {
+       if (root == null)
+               return false;           // nothing to do
+       HierarchyNode s = nodeat(root, x/16, (y/16)+top);
+       if (s == null) {
+               // Just deselect
+               sel = null;
+               repaint();
+               return true;
+               }
+
+       // Check for double-click
+       boolean dc = false;
+       if (evt.when-last < 500 && sel == s)
+               dc = true;
+       else
+               last = evt.when;
+       sel = s;
+
+       if (dc && sel.ch != null) {
+               // Open or close this node
+               sel.open = !sel.open;
+               if (callback != null) {
+                       // Notify callback, which MAY do something to change
+                       // the structure of the tree
+                       if (sel.open) callback.openNode(this, sel);
+                       else          callback.closeNode(this, sel);
+                       }
+               }
+       else if (callback != null) {
+               // Single click on a node or double-click on leaf node
+               if (dc) callback.doubleNode(this, sel);
+               else    callback.clickNode(this, sel);
+               }
+       compscroll();
+       repaint();
+       return true;
+       }
+
+       public void moved(CbScrollbar s, int v)
+       {
+       moving(s, v);
+       }
+
+       public void moving(CbScrollbar s, int v)
+       {
+       top = sb.getValue();
+       compscroll();
+       repaint();
+       }
+
+       // render
+       // Draw the current tree view into the backing image
+       private void render()
+       {
+       if (fnm != null) {
+               int fh = fnm.getHeight(),       // useful font metrics
+                   fa = fnm.getAscent();
+               bg.setColor(Util.light_bg);
+               bg.fillRect(0, 0, width, height);
+               if (root == null)
+                       return;         // nothing to do
+               bg.setColor(Util.text);
+               recurse(root, 0, 0, fh, fa);
+               }
+       }
+
+       // recurse
+       // Render a node in the tree at the given location, maybe followed
+       // by all it's children. Return the number of rows this node took
+       // to display.
+       private int recurse(HierarchyNode n, int x, int y, int fh, int fa)
+       {
+       int xx = x*16, yy = (y-top)*16;
+       int len = 1;
+
+       n.x = x;
+       n.y = y;
+       int tw = fnm.stringWidth(n.text);
+       if (yy >= 0 && yy <= height) {
+               // Draw this node
+               if (n.im != null)
+                       bg.drawImage(n.im, xx, yy, this);
+               if (sel == n) {
+                       // Select this node
+                       bg.setColor(Util.body);
+                       bg.fillRect(xx+17, yy+2, tw+2, 13);
+                       bg.setColor(Util.text);
+                       }
+               bg.drawString(n.text, xx+18, yy+12);
+               }
+       if (n.ch != null && n.open && yy <= height) {
+               // Mark this node
+               bg.drawLine(xx+18, yy+14, xx+17+tw, yy+14);
+
+               // Draw subnodes
+               yy += 16;
+               for(int i=0; i<n.ch.size() && yy<=height; i++) {
+                       int l=recurse((HierarchyNode)n.ch.elementAt(i),
+                                     x+1, y+len, fh, fa);
+                       bg.drawLine(xx+7, yy+7, xx+15, yy+7);
+                       if (i == n.ch.size()-1)
+                               bg.drawLine(xx+7, yy, xx+7, yy+7);
+                       else
+                               bg.drawLine(xx+7, yy, xx+7,yy+(l*16)-1);
+                       len += l;
+                       yy += l*16;
+                       }
+               }
+       return len;
+       }
+
+       // compscroll
+       // Re-compute scrollbar size
+       private void compscroll()
+       {
+       if (fnm == null)
+               return;
+       int ct = root!=null ? count(root) : 1;
+       int r = Math.min(ct, height/16 - 1);
+       int c = ct - r;
+       //sb.setValues(top, r==0?1:r, c<0?0:c);
+       sb.setValues(top, r==0?1:r, ct);
+       }
+
+       // count
+       // Returns the number of visible rows from a node
+       private int count(HierarchyNode n)
+       {
+       int l = 1;
+       if (n.open && n.ch != null)
+               for(int i=0; i<n.ch.size(); i++)
+                       l += count((HierarchyNode)n.ch.elementAt(i));
+       return l;
+       }
+
+       // nodeat
+       // Is the given node at the given position? If not, check its
+       // children too.
+       private HierarchyNode nodeat(HierarchyNode n, int x, int y)
+       {
+       if (y == n.y && x >= n.x)
+               return n;
+       if (n.ch == null || !n.open)
+               return null;
+       for(int i=0; i<n.ch.size(); i++) {
+               HierarchyNode c = nodeat((HierarchyNode)n.ch.elementAt(i),x,y);
+               if (c != null) return c;
+               }
+       return null;
+       }
+}
+
+// HierarchyNode
+// One node in the tree displayed by the Hierarchy object.
+class HierarchyNode
+{
+       boolean open;           // is this node open?
+       Image im;               // icon for this node (assumed to be 16x16!)
+       Vector ch;              // sub-nodes of this one, or null
+       String text;            // name of this node
+       int x, y;               // row/column in list
+
+       HierarchyNode() { }
+
+       HierarchyNode(boolean o, Image i, Vector c, String t)
+       {
+       open = o;
+       im = i;
+       ch = c;
+       text = t;
+       }
+}
+
+// HierarchyCallback
+// Programmers using the Hierarchy class pass an object that implements the
+// HierarchyCallback interface to its constructor, to receive information
+// about user actions.
+interface HierarchyCallback
+{
+       // openNode
+       // Called when a node with children is opened
+       void openNode(Hierarchy h, HierarchyNode n);
+
+       // closeNode
+       // Called when a node is closed
+       void closeNode(Hierarchy h, HierarchyNode n);
+
+       // clickNode
+       // Called when the user clicks on a node
+       void clickNode(Hierarchy h, HierarchyNode n);
+
+       // doubleNode
+       // Called when a user double-clicks on a node
+       void doubleNode(Hierarchy h, HierarchyNode n);
+}
+
diff --git a/file/Hierarchy.java.bak b/file/Hierarchy.java.bak
new file mode 100644 (file)
index 0000000..c97a7ac
--- /dev/null
@@ -0,0 +1,345 @@
+// Hierarchy
+// An AWT component for displaying a tree-like heirachy, with each node
+// having an icon and a name. This heirachy can be expanded or contracted
+// by the user.
+import java.awt.*;
+import java.util.Vector;
+
+public class Hierarchy extends BorderPanel implements CbScrollbarCallback
+{
+       HierarchyNode root;             // the root of the tree
+       CbScrollbar sb;                 // scrollbar at right
+       int width, height;              // usable drawing area
+       int sbwidth;                    // size of scrollbar
+       HierarchyCallback callback;     // who to call on open / close
+       Image bim;                      // double-buffer image
+       Font font = new Font("courier", Font.PLAIN, 12);
+       FontMetrics fnm;                // size of font used
+       Graphics bg;                    // back-images graphics
+       int top = 0;                    // top-most row displayed
+       int count = 0;                  // total rows in the tree
+       Insets in;                      // insets from border
+       HierarchyNode sel;              // selected node
+       long last;                      // time of last mouse click
+       static boolean broken_awt = System.getProperty("os.name").
+                                   startsWith("Windows");
+
+       // Create a new Hierarchy object with the given root
+       Hierarchy(HierarchyNode r)
+       {
+       this();
+       root = r;
+       }
+
+       // Create a new Hierarchy object that calls back to the given object
+       // when nodes are clicked on.
+       Hierarchy(HierarchyNode r, HierarchyCallback c)
+       {
+       this(r);
+       callback = c;
+       }
+
+       // Create an empty hierarchy object, with no callback
+       Hierarchy()
+       {
+       super(3, new Color(50,50,50), new Color(220,220,220));
+
+       // Create UI
+       setLayout(null);
+       sb = new CbScrollbar(CbScrollbar.VERTICAL, this);
+       add(sb);
+       }
+
+       // Create an empty hierarchy object, set to report user actions to
+       // the given object.
+       Hierarchy(HierarchyCallback c)
+       {
+       this();
+       callback = c;
+       }
+
+       // redraw
+       // Called by the using class when the tree passed to this object
+       // changes, to force a redraw and resizing of the scrollbar
+       void redraw()
+       {
+       if (fnm != null) {
+               render();
+               paint(getGraphics());
+               compscroll();
+               }
+       }
+
+       // setRoot
+       // Set the root node for this hierarchy
+       void setRoot(HierarchyNode r)
+       {
+       root = r;
+       redraw();
+       }
+
+       // selected
+       // Return the currently selected node, or null
+       HierarchyNode selected()
+       {
+       return sel;
+       }
+
+       // select
+       // Selected the given node
+       void select(HierarchyNode s)
+       {
+       sel = s;
+       }
+
+       // force the use of some font
+       void setFont(Font f)
+       {
+       font = f;
+       bim = null;
+       repaint();
+       }
+
+       // reshape
+       // Called when this component gets resized
+       public void reshape(int nx, int ny, int nw, int nh)
+       {
+       in = insets();
+       sbwidth = sb.minimumSize().width;
+       width = nw-sbwidth - (in.left + in.right);
+       height = nh - (in.top + in.bottom);
+       sb.reshape(width+in.left, in.top, sbwidth, height);
+
+       // force creation of a new backing images
+       bim = null;
+       repaint();
+       compscroll();
+
+       super.reshape(nx, ny, nw, nh);
+       }
+
+       // update
+       // Called sometime after repaint()
+       public void update(Graphics g)
+       {
+       render();
+       paint(g);
+       }
+
+       // paint
+       // Blit the backing image to the front
+       public void paint(Graphics g)
+       {
+       super.paint(g);
+       if (bim == null) {
+               // This is the first rendering
+               bim = createImage(width, height);
+               bg = bim.getGraphics();
+               bg.setFont(font);
+               fnm = bg.getFontMetrics();
+               render();
+               compscroll();
+               }
+       g.drawImage(bim, in.left, in.top, this);
+       }
+
+       // mouseDown
+       // Called upon a mouseclick
+       public boolean mouseDown(Event evt, int x, int y)
+       {
+       if (root == null)
+               return false;           // nothing to do
+       HierarchyNode s = nodeat(root, x/16, (y/16)+top);
+       if (s == null) {
+               // Just deselect
+               sel = null;
+               repaint();
+               return true;
+               }
+
+       // Check for double-click
+       boolean dc = false;
+       if (evt.when-last < 500 && sel == s)
+               dc = true;
+       else
+               last = evt.when;
+       sel = s;
+
+       if (dc && sel.ch != null) {
+               // Open or close this node
+               sel.open = !sel.open;
+               if (callback != null) {
+                       // Notify callback, which MAY do something to change
+                       // the structure of the tree
+                       if (sel.open) callback.openNode(this, sel);
+                       else          callback.closeNode(this, sel);
+                       }
+               }
+       else if (callback != null) {
+               // Single click on a node or double-click on leaf node
+               if (dc) callback.doubleNode(this, sel);
+               else    callback.clickNode(this, sel);
+               }
+       compscroll();
+       repaint();
+       return true;
+       }
+
+       public void moved(CbScrollbar s, int v)
+       {
+       moving(s, v);
+       }
+
+       public void moving(CbScrollbar s, int v)
+       {
+       top = sb.getValue();
+       compscroll();
+       repaint();
+       }
+
+       // render
+       // Draw the current tree view into the backing image
+       private void render()
+       {
+       if (fnm != null) {
+               int fh = fnm.getHeight(),       // useful font metrics
+                   fa = fnm.getAscent();
+               bg.setColor(Color.white);
+               bg.fillRect(0, 0, width, height);
+               if (root == null)
+                       return;         // nothing to do
+               bg.setColor(Color.black);
+               recurse(root, 0, 0, fh, fa);
+               }
+       }
+
+       // recurse
+       // Render a node in the tree at the given location, maybe followed
+       // by all it's children. Return the number of rows this node took
+       // to display.
+       private int recurse(HierarchyNode n, int x, int y, int fh, int fa)
+       {
+       int xx = x*16, yy = (y-top)*16;
+       int len = 1;
+
+       n.x = x;
+       n.y = y;
+       int tw = fnm.stringWidth(n.text);
+       if (yy >= 0 && yy <= height) {
+               // Draw this node
+               if (n.im != null)
+                       bg.drawImage(n.im, xx, yy, this);
+               if (sel == n) {
+                       // Select this node
+                       bg.setColor(Color.lightGray);
+                       bg.fillRect(xx+17, yy+2, tw+2, 13);
+                       bg.setColor(Color.black);
+                       }
+               bg.drawString(n.text, xx+18, yy+12);
+               }
+       if (n.ch != null && n.open && yy <= height) {
+               // Mark this node
+               bg.drawLine(xx+18, yy+14, xx+17+tw, yy+14);
+
+               // Draw subnodes
+               yy += 16;
+               for(int i=0; i<n.ch.size() && yy<=height; i++) {
+                       int l=recurse((HierarchyNode)n.ch.elementAt(i),
+                                     x+1, y+len, fh, fa);
+                       bg.drawLine(xx+7, yy+7, xx+15, yy+7);
+                       if (i == n.ch.size()-1)
+                               bg.drawLine(xx+7, yy, xx+7, yy+7);
+                       else
+                               bg.drawLine(xx+7, yy, xx+7,yy+(l*16)-1);
+                       len += l;
+                       yy += l*16;
+                       }
+               }
+       return len;
+       }
+
+       // compscroll
+       // Re-compute scrollbar size
+       private void compscroll()
+       {
+       if (fnm == null)
+               return;
+       int ct = root!=null ? count(root) : 1;
+       int r = Math.min(ct, height/16 - 1);
+       int c = ct - r;
+       //sb.setValues(top, r==0?1:r, c<0?0:c);
+       sb.setValues(top, r==0?1:r, ct);
+       }
+
+       // count
+       // Returns the number of visible rows from a node
+       private int count(HierarchyNode n)
+       {
+       int l = 1;
+       if (n.open && n.ch != null)
+               for(int i=0; i<n.ch.size(); i++)
+                       l += count((HierarchyNode)n.ch.elementAt(i));
+       return l;
+       }
+
+       // nodeat
+       // Is the given node at the given position? If not, check its
+       // children too.
+       private HierarchyNode nodeat(HierarchyNode n, int x, int y)
+       {
+       if (y == n.y && x >= n.x)
+               return n;
+       if (n.ch == null || !n.open)
+               return null;
+       for(int i=0; i<n.ch.size(); i++) {
+               HierarchyNode c = nodeat((HierarchyNode)n.ch.elementAt(i),x,y);
+               if (c != null) return c;
+               }
+       return null;
+       }
+}
+
+// HierarchyNode
+// One node in the tree displayed by the Hierarchy object.
+class HierarchyNode
+{
+       boolean open;           // is this node open?
+       Image im;               // icon for this node (assumed to be 16x16!)
+       Vector ch;              // sub-nodes of this one, or null
+       String text;            // name of this node
+       int x, y;               // row/column in list
+
+       HierarchyNode() { }
+
+       HierarchyNode(boolean o, Image i, Vector c, String t)
+       {
+       open = o;
+       im = i;
+       ch = c;
+       text = t;
+       }
+}
+
+// HierarchyCallback
+// Programmers using the Hierarchy class pass an object that implements the
+// HierarchyCallback interface to its constructor, to receive information
+// about user actions.
+interface HierarchyCallback
+{
+       // openNode
+       // Called when a node with children is opened
+       void openNode(Hierarchy h, HierarchyNode n);
+
+       // closeNode
+       // Called when a node is closed
+       void closeNode(Hierarchy h, HierarchyNode n);
+
+       // clickNode
+       // Called when the user clicks on a node
+       void clickNode(Hierarchy h, HierarchyNode n);
+
+       // doubleNode
+       // Called when a user double-clicks on a node
+       void doubleNode(Hierarchy h, HierarchyNode n);
+}
+
diff --git a/file/LinedPanel.java b/file/LinedPanel.java
new file mode 100644 (file)
index 0000000..c344844
--- /dev/null
@@ -0,0 +1,39 @@
+import java.awt.*;
+
+class LinedPanel extends GrayPanel
+{
+       String title;
+
+       LinedPanel(String t)
+       {
+       title = t;
+       }
+
+       public void paint(Graphics g)
+       {
+       super.paint(g);
+       Font f = g.getFont();
+       FontMetrics fnm = g.getFontMetrics();
+       int w = size().width-1, h = size().height - 1;
+       int tl = fnm.stringWidth(title);
+
+       g.setColor(Util.light_edge);
+       g.drawLine(5, 5, 5, h-5);
+       g.drawLine(5, h-5, w-5, h-5);
+       g.drawLine(w-5, h-5, w-5, 5);
+       g.drawLine(tl+9, 5, w-5, 5);
+
+       g.setColor(Util.dark_edge);
+       g.drawLine(4, 4, 4, h-6);
+       g.drawLine(6, h-6, w-6, h-6);
+       g.drawLine(w-6, h-6, w-6, 6);
+       g.drawLine(w-6, 4, tl+9, 4);
+       g.drawString(title, 7, fnm.getAscent());
+       }
+
+       public Insets insets()
+       {
+       return new Insets(15, 10, 10, 10);
+       }
+}
+
diff --git a/file/Makefile b/file/Makefile
new file mode 100644 (file)
index 0000000..db71104
--- /dev/null
@@ -0,0 +1,4 @@
+file.jar:              FileManager.java Util.java
+                       CLASSPATH=/usr/local/netscape7/plugins/java2/lib/javaplugin.jar:. javac -target 1.1 *.java
+                       jar cf file.jar *.class
+                       [ -d "../zomos-virtual" ] && jar cf ../zomos-virtual/file.jar *.class
diff --git a/file/MultiColumn.java b/file/MultiColumn.java
new file mode 100644 (file)
index 0000000..7211718
--- /dev/null
@@ -0,0 +1,583 @@
+// MultiColumn
+// A List box that supports multiple columns.
+import java.awt.*;
+import java.util.Vector;
+
+public class MultiColumn extends BorderPanel implements CbScrollbarCallback
+{
+       MultiColumnCallback callback;   // what to call back to 
+       String title[];                 // column titles
+       boolean adjustable = true;
+       boolean drawlines = true;
+       Color colors[][] = null;
+       boolean enabled = true;
+       boolean multiselect = false;
+       int cpos[];                     // column x positions
+       float cwidth[];                 // proportional column widths
+       Vector list[];                  // columns of the list
+       CbScrollbar sb;                 // scrollbar at the right side
+       int width, height;              // size, minus the scrollbar
+       Insets in;                      // used space around the border
+       int sbwidth;                    // width of the scrollbar
+       int th;                         // height of title bar
+       Image bim;                      // backing image
+       Graphics bg;                    // backing graphics
+       Font font = new Font("timesRoman", Font.PLAIN, 12);
+       FontMetrics fnm;                // drawing font size
+       int coldrag = -1;               // column being resized
+       int sel = -1;                   // selected row
+       int sels[] = new int[0];        // all selected rows
+       int top = 0;                    // first row displayed
+       long last;                      // last mouse click time
+       int rowh = 16;                  // row height
+       Event last_event;               // last event that triggered callback
+       int sortcol;                    // Column currently being sorted
+       int sortdir;                    // Sort direction (0=none, 1=up, 2=down)
+
+       // Create a new list with the given column titles
+       MultiColumn(String t[])
+       {
+       super(3, Util.dark_edge_hi, Util.body_hi);
+       title = new String[t.length];
+       for(int i=0; i<t.length; i++)
+               title[i] = t[i];
+       list = new Vector[t.length];
+       for(int i=0; i<t.length; i++)
+               list[i] = new Vector();
+       cwidth = new float[t.length];
+       for(int i=0; i<t.length; i++)
+               cwidth[i] = 1.0f/t.length;
+       cpos = new int[t.length+1];
+       setLayout(null);
+       sb = new CbScrollbar(CbScrollbar.VERTICAL, this);
+       add(sb);
+       }
+
+       // Create a new list that calls back to the given object on
+       // single or double clicks.
+       MultiColumn(String t[], MultiColumnCallback c)
+       {
+       this(t);
+       callback = c;
+       }
+
+       // addItem
+       // Add a row to the list
+       void addItem(Object item[])
+       {
+       for(int i=0; i<title.length; i++)
+               list[i].addElement(item[i]);
+       repaint();
+       compscroll();
+       }
+
+       // addItems
+       // Add several rows to the list
+       void addItems(Object item[][])
+       {
+       for(int i=0; i<item.length; i++)
+               for(int j=0; j<title.length; j++)
+                       list[j].addElement(item[i][j]);
+       repaint();
+       compscroll();
+       }
+
+       // modifyItem
+       // Changes one row of the table
+       void modifyItem(Object item[], int row)
+       {
+       for(int i=0; i<title.length; i++)
+               list[i].setElementAt(item[i], row);
+       repaint();
+       compscroll();
+       }
+
+       // getItem
+       // Returns the contents of a given row
+       Object []getItem(int n)
+       {
+       Object r[] = new Object[title.length];
+       for(int i=0; i<title.length; i++)
+               r[i] = list[i].elementAt(n);
+       return r;
+       }
+
+       // selected
+       // Return the most recently selected row
+       int selected()
+       {
+       return sel;
+       }
+
+       // select
+       // Select some row
+       void select(int s)
+       {
+       sel = s;
+       sels = new int[1];
+       sels[0] = s;
+       repaint();
+       }
+
+       // select
+       // Select multiple rows
+       void select(int s[])
+       {
+       if (s.length == 0) {
+               sel = -1;
+               sels = new int[0];
+               }
+       else {
+               sel = s[0];
+               sels = s;
+               }
+       repaint();
+       }
+
+       // allSelected
+       // Returns all the selected rows
+       int[] allSelected()
+       {
+       return sels;
+       }
+
+       // scrollto
+       // Scroll to make some row visible
+       void scrollto(int s)
+       {
+       int r = rows();
+       if (s < top || s >= top+r) {
+               top = s-1;
+               if (top > list[0].size() - r)
+                       top = list[0].size() - r;
+               sb.setValue(top);
+               repaint();
+               }
+       }
+
+       // deleteItem
+       // Remove one row from the list
+       void deleteItem(int n)
+       {
+       for(int i=0; i<title.length; i++)
+               list[i].removeElementAt(n);
+       if (n == sel) {
+               // De-select deleted file
+               sel = -1;
+               }
+       for(int i=0; i<sels.length; i++) {
+               if (sels[i] == n) {
+                       // Remove from selection list
+                       int nsels[] = new int[sels.length-1];
+                       if (nsels.length > 0) {
+                               System.arraycopy(sels, 0, nsels, 0, i);
+                               System.arraycopy(sels, i+1, nsels, i,
+                                                nsels.length-i);
+                               sel = nsels[0];
+                               }
+                       break;
+                       }
+               }
+       repaint();
+       compscroll();
+       }
+
+       // clear
+       // Remove everything from the list
+       void clear()
+       {
+       for(int i=0; i<title.length; i++)
+               list[i].removeAllElements();
+       sel = -1;
+       sels = new int[0];
+       top = 0;
+       repaint();
+       sb.setValues(0, 1, 0);
+       }
+
+       // setWidths
+       // Set the proportional widths of each column
+       void setWidths(float w[])
+       {
+       for(int i=0; i<title.length; i++)
+               cwidth[i] = w[i];
+       respace();
+       repaint();
+       }
+
+       /**Turns on or off the user's ability to adjust column widths
+        * @param a     Can adjust or not?
+        */
+       void setAdjustable(boolean a)
+       {
+       adjustable = a;
+       }
+
+       /**Turns on or off the drawing of column lines
+        * @param d     Draw lines or not?
+        */
+       void setDrawLines(boolean d)
+       {
+       drawlines = d;
+       }
+
+       /**Sets the array of colors used to draw text items.
+        * @param c     The color array (in row/column order), or null to
+        *              use the default
+        */
+       void setColors(Color c[][])
+       {
+       colors = c;
+       repaint();
+       }
+
+       // Turns on or off multi-row selection with ctrl and shift
+       void setMultiSelect(boolean m)
+       {
+       multiselect = m;
+       }
+
+       // Enables the entire list
+       public void enable()
+       {
+       enabled = true;
+       sb.enable();
+       repaint();
+       }
+
+       // Disables the entire list
+       public void disable()
+       {
+       enabled = false;
+       sb.disable();
+       repaint();
+       }
+
+       // Sets or turns off the sort indication arrow for a column
+       // Direction 0 = None, 1 = Up arrow, 2 = Down arrow
+       public void sortingArrow(int col, int dir)
+       {
+       sortcol = col;
+       sortdir = dir;
+       repaint();
+       }
+
+       public void setFont(Font f)
+       {
+       font = f;
+       bim = null;
+       repaint();
+       }
+
+       // reshape
+       // Called when this component gets resized
+       public void reshape(int nx, int ny, int nw, int nh)
+       {
+       if (nw != width+sbwidth || nh != height) {
+               in = insets();
+               sbwidth = sb.minimumSize().width;
+               width = nw-sbwidth - (in.left + in.right);
+               height = nh - (in.top + in.bottom);
+               sb.reshape(width+in.left, in.top, sbwidth, height);
+               respace();
+
+               // Force creation of a new backing image and re-painting
+               bim = null;
+               repaint();
+               compscroll();
+               }
+       super.reshape(nx, ny, nw, nh);
+       }
+
+       // respace
+       // Compute pixel column widths from proportional widths
+       void respace()
+       {
+       cpos[0] = 0;
+       for(int i=0; i<title.length; i++)
+               cpos[i+1] = cpos[i] + (int)(width*cwidth[i]);
+       }
+
+       // paint
+       // Blit the backing image to the front
+       public void paint(Graphics g)
+       {
+       super.paint(g);
+       if (bim == null) {
+               // This is the first rendering
+               bim = createImage(width, height);
+               bg = bim.getGraphics();
+               bg.setFont(font);
+               fnm = bg.getFontMetrics();
+               th = fnm.getHeight() + 4;
+               render();
+               compscroll();
+               }
+       g.drawImage(bim, in.left, in.top, this);
+       }
+
+       // update
+       // Called sometime after repaint()
+       public void update(Graphics g)
+       {
+       if (fnm != null) {
+               render();
+               paint(g);
+               }
+       }
+
+       // render
+       // Re-draw the list into the backing image
+       void render()
+       {
+       int fh = fnm.getHeight(),       // useful font metrics
+           fd = fnm.getDescent(),
+           fa = fnm.getAscent();
+       int bot = Math.min(top+rows()-1, list[0].size()-1);
+
+       // Clear title section and list
+       bg.setColor(Util.body);
+       bg.fillRect(0, 0, width, th);
+       bg.setColor(Util.light_bg);
+       bg.fillRect(0, th, width, height-th);
+       Color lighterGray = Util.body_hi;
+
+       if (enabled) {
+               // Mark the selected rows
+               for(int i=0; i<sels.length; i++) {
+                       if (sels[i] >= top && sels[i] <= bot) {
+                               bg.setColor(sels[i] == sel ? Util.body
+                                                          : lighterGray);
+                               bg.fillRect(0, th+(sels[i]-top)*rowh,
+                                           width, rowh);
+                               }
+                       }
+               }
+
+       // Draw each column
+       for(int i=0; i<title.length; i++) {
+               int x = cpos[i], w = cpos[i+1]-x-1;
+
+               // Column title
+               bg.setColor(Util.light_edge);
+               bg.drawLine(x, 0, x+w, 0);
+               bg.drawLine(x, 1, x+w-1, 1);
+               bg.drawLine(x, 0, x, th-1);
+               bg.drawLine(x+1, 0, x+1, th-2);
+               bg.setColor(Util.dark_edge);
+               bg.drawLine(x, th-1, x+w, th-1);
+               bg.drawLine(x, th-2, x+w-1, th-2);
+               bg.drawLine(x+w, th-1, x+w, 0);
+               bg.drawLine(x+w-1, th-1, x+w-1, 1);
+               int tw = fnm.stringWidth(title[i]);
+               if (tw < w-6)
+                       bg.drawString(title[i], x+(w-tw)/2, th-fd-2);
+
+               // Sorting arrow
+               int as = th-8;
+               if (sortcol == i && sortdir == 1) {
+                       bg.setColor(Util.light_edge);
+                       bg.drawLine(x+4, th-5, x+4+as, th-5);
+                       bg.drawLine(x+4+as, th-5, x+4+as/2, th-5-as);
+                       bg.setColor(Util.dark_edge);
+                       bg.drawLine(x+4+as/2, th-5-as, x+4, th-5);
+                       }
+               else if (sortcol == i && sortdir == 2) {
+                       bg.setColor(Util.light_edge);
+                       bg.drawLine(x+4+as/2, th-5, x+4+as, th-5-as);
+                       bg.setColor(Util.dark_edge);
+                       bg.drawLine(x+4, th-5-as, x+4+as, th-5-as);
+                       bg.drawLine(x+4, th-5-as, x+4+as/2, th-5);
+                       }
+
+               // Column items
+               if (drawlines) {
+                       bg.setColor(Util.body);
+                       bg.drawLine(x+w-1, th, x+w-1, height);
+                       bg.setColor(Util.dark_edge);
+                       bg.drawLine(x+w, th, x+w, height);
+                       }
+               for(int j=top; j<=bot; j++) {
+                       Object o = list[i].elementAt(j);
+                       if (o instanceof String) {
+                               // Render string in column
+                               String s = (String)o;
+                               while(fnm.stringWidth(s) > w-3)
+                                       s = s.substring(0, s.length()-1);
+                               if (!enabled)
+                                       bg.setColor(Util.body);
+                               else if (colors != null)
+                                       bg.setColor(colors[j][i]);
+                               bg.drawString(s, x+1, th+(j+1-top)*rowh-fd);
+                               }
+                       else if (o instanceof Image) {
+                               // Render image in column
+                               Image im = (Image)o;
+                               bg.drawImage(im, x+1, th+(j-top)*rowh, this);
+                               }
+                       }
+               }
+       }
+
+       // mouseDown
+       // Select a list item or a column to drag
+       public boolean mouseDown(Event e, int x, int y)
+       {
+       if (!enabled) {
+               return true;
+               }
+       x -= in.left;
+       y -= in.top;
+       coldrag = -1;
+       if (y < th) {
+               // Click in title bar
+               for(int i=0; i<title.length; i++) {
+                       if (adjustable && i > 0 && Math.abs(cpos[i] - x) < 3) {
+                               // clicked on a column separator
+                               coldrag = i;
+                               }
+                       else if (x >= cpos[i] && x < cpos[i+1]) {
+                               // clicked in a title
+                               callback.headingClicked(this, i);
+                               }
+                       }
+               }
+       else {
+               // Item chosen from list
+               int row = (y-th)/rowh + top;
+               if (row < list[0].size()) {
+                       // Double-click?
+                       boolean dclick = false;
+                       if (e.when-last < 1000 && sel == row)
+                               dclick = true;
+                       else
+                               last = e.when;
+
+                       if (e.shiftDown() && multiselect && sel != -1) {
+                               // Select all from last selection to this one
+                               int zero = sels[0];
+                               if (zero < row) {
+                                       sels = new int[row-zero+1];
+                                       for(int i=zero; i<=row; i++)
+                                               sels[i-zero] = i;
+                                       }
+                               else {
+                                       sels = new int[zero-row+1];
+                                       for(int i=zero; i>=row; i--)
+                                               sels[zero-i] = i;
+                                       }
+                               }
+                       else if (e.controlDown() && multiselect) {
+                               // Add this one to selection
+                               int nsels[] = new int[sels.length + 1];
+                               System.arraycopy(sels, 0, nsels, 0,sels.length);
+                               nsels[sels.length] = row;
+                               sels = nsels;
+                               }
+                       else {
+                               // Select one row only, and de-select others
+                               sels = new int[1];
+                               sels[0] = row;
+                               }
+                       sel = row;
+                       repaint();
+                       last_event = e;
+                       if (callback != null) {
+                               // Callback the right function
+                               if (dclick) callback.doubleClick(this, row);
+                               else        callback.singleClick(this, row);
+                               }
+                       else {
+                               // Send an event
+                               getParent().postEvent(
+                                       new Event(this,
+                                                 Event.ACTION_EVENT,
+                                                 dclick?"Double":"Single"));
+                               }
+                       }
+               }
+       return true;
+       }
+
+       // mouseDrag
+       // If a column is selected, change it's width
+       public boolean mouseDrag(Event e, int x, int y)
+       {
+       if (!enabled) {
+               return true;
+               }
+       x -= in.left;
+       y -= in.top;
+       if (coldrag != -1) {
+               if (x > cpos[coldrag-1]+3 && x < cpos[coldrag+1]-3) {
+                       cpos[coldrag] = x;
+                       cwidth[coldrag-1] = (cpos[coldrag]-cpos[coldrag-1]) /
+                                           (float)width;
+                       cwidth[coldrag] = (cpos[coldrag+1]-cpos[coldrag]) /
+                                           (float)width;
+                       repaint();
+                       }
+               }
+       return true;
+       }
+
+       public void moved(CbScrollbar s, int v)
+       {
+       moving(s, v);
+       }
+
+       public void moving(CbScrollbar s, int v)
+       {
+       top = sb.getValue();
+       compscroll();
+       repaint();
+       }
+
+       // compscroll
+       // Re-compute the size of the scrollbar
+       private void compscroll()
+       {
+       if (fnm == null)
+               return;         // not visible
+       int r = rows();
+       int c = list[0].size() - r;
+       sb.setValues(top, r==0?1:r, list[0].size());
+       }
+
+       // rows
+       // Returns the number of rows visible in the list
+       private int rows()
+       {
+       return Math.min(height/rowh - 1, list[0].size());
+       }
+
+       public Dimension minimumSize()
+       {
+       return new Dimension(400, 100);
+       }
+
+       public Dimension preferredSize()
+       {
+       return minimumSize();
+       }
+}
+
+// MultiColumnCallback
+// Objects implementing this interface can be passed to the MultiColumn
+// class, to have their singleClick() and doubleClick() functions called in
+// response to single or double click in the list.
+interface MultiColumnCallback
+{
+       // singleClick
+       // Called on a single click on a list item
+       void singleClick(MultiColumn list, int num);
+
+       // doubleClick
+       // Called upon double-clicking on a list item
+       void doubleClick(MultiColumn list, int num);
+
+       // headingClicked
+       // Called when a column heading is clicked on
+       void headingClicked(MultiColumn list, int col);
+}
+
diff --git a/file/MultiColumn.java.bak b/file/MultiColumn.java.bak
new file mode 100644 (file)
index 0000000..65ec795
--- /dev/null
@@ -0,0 +1,578 @@
+// MultiColumn
+// A List box that supports multiple columns.
+import java.awt.*;
+import java.util.Vector;
+
+public class MultiColumn extends BorderPanel implements CbScrollbarCallback
+{
+       MultiColumnCallback callback;   // what to call back to 
+       String title[];                 // column titles
+       boolean adjustable = true;
+       boolean drawlines = true;
+       Color colors[][] = null;
+       boolean enabled = true;
+       boolean multiselect = false;
+       int cpos[];                     // column x positions
+       float cwidth[];                 // proportional column widths
+       Vector list[];                  // columns of the list
+       CbScrollbar sb;                 // scrollbar at the right side
+       int width, height;              // size, minus the scrollbar
+       Insets in;                      // used space around the border
+       int sbwidth;                    // width of the scrollbar
+       int th;                         // height of title bar
+       Image bim;                      // backing image
+       Graphics bg;                    // backing graphics
+       Font font = new Font("timesRoman", Font.PLAIN, 12);
+       FontMetrics fnm;                // drawing font size
+       int coldrag = -1;               // column being resized
+       int sel = -1;                   // selected row
+       int sels[] = new int[0];        // all selected rows
+       int top = 0;                    // first row displayed
+       long last;                      // last mouse click time
+       int rowh = 16;                  // row height
+       Event last_event;               // last event that triggered callback
+       int sortcol;                    // Column currently being sorted
+       int sortdir;                    // Sort direction (0=none, 1=up, 2=down)
+
+       // Create a new list with the given column titles
+       MultiColumn(String t[])
+       {
+       super(3, new Color(50,50,50), new Color(220,220,220));
+       title = new String[t.length];
+       for(int i=0; i<t.length; i++)
+               title[i] = t[i];
+       list = new Vector[t.length];
+       for(int i=0; i<t.length; i++)
+               list[i] = new Vector();
+       cwidth = new float[t.length];
+       for(int i=0; i<t.length; i++)
+               cwidth[i] = 1.0f/t.length;
+       cpos = new int[t.length+1];
+       setLayout(null);
+       sb = new CbScrollbar(CbScrollbar.VERTICAL, this);
+       add(sb);
+       }
+
+       // Create a new list that calls back to the given object on
+       // single or double clicks.
+       MultiColumn(String t[], MultiColumnCallback c)
+       {
+       this(t);
+       callback = c;
+       }
+
+       // addItem
+       // Add a row to the list
+       void addItem(Object item[])
+       {
+       for(int i=0; i<title.length; i++)
+               list[i].addElement(item[i]);
+       repaint();
+       compscroll();
+       }
+
+       // addItems
+       // Add several rows to the list
+       void addItems(Object item[][])
+       {
+       for(int i=0; i<item.length; i++)
+               for(int j=0; j<title.length; j++)
+                       list[j].addElement(item[i][j]);
+       repaint();
+       compscroll();
+       }
+
+       // modifyItem
+       // Changes one row of the table
+       void modifyItem(Object item[], int row)
+       {
+       for(int i=0; i<title.length; i++)
+               list[i].setElementAt(item[i], row);
+       repaint();
+       compscroll();
+       }
+
+       // getItem
+       // Returns the contents of a given row
+       Object []getItem(int n)
+       {
+       Object r[] = new Object[title.length];
+       for(int i=0; i<title.length; i++)
+               r[i] = list[i].elementAt(n);
+       return r;
+       }
+
+       // selected
+       // Return the most recently selected row
+       int selected()
+       {
+       return sel;
+       }
+
+       // select
+       // Select some row
+       void select(int s)
+       {
+       sel = s;
+       sels = new int[1];
+       sels[0] = s;
+       repaint();
+       }
+
+       // select
+       // Select multiple rows
+       void select(int s[])
+       {
+       if (s.length == 0) {
+               sel = -1;
+               sels = new int[0];
+               }
+       else {
+               sel = s[0];
+               sels = s;
+               }
+       repaint();
+       }
+
+       // allSelected
+       // Returns all the selected rows
+       int[] allSelected()
+       {
+       return sels;
+       }
+
+       // scrollto
+       // Scroll to make some row visible
+       void scrollto(int s)
+       {
+       int r = rows();
+       if (s < top || s >= top+r) {
+               top = s-1;
+               if (top > list[0].size() - r)
+                       top = list[0].size() - r;
+               sb.setValue(top);
+               repaint();
+               }
+       }
+
+       // deleteItem
+       // Remove one row from the list
+       void deleteItem(int n)
+       {
+       for(int i=0; i<title.length; i++)
+               list[i].removeElementAt(n);
+       if (n == sel) {
+               // De-select deleted file
+               sel = -1;
+               }
+       for(int i=0; i<sels.length; i++) {
+               if (sels[i] == n) {
+                       // Remove from selection list
+                       int nsels[] = new int[sels.length-1];
+                       if (nsels.length > 0) {
+                               System.arraycopy(sels, 0, nsels, 0, i);
+                               System.arraycopy(sels, i+1, nsels, i,
+                                                nsels.length-i);
+                               sel = nsels[0];
+                               }
+                       break;
+                       }
+               }
+       repaint();
+       compscroll();
+       }
+
+       // clear
+       // Remove everything from the list
+       void clear()
+       {
+       for(int i=0; i<title.length; i++)
+               list[i].removeAllElements();
+       sel = -1;
+       sels = new int[0];
+       top = 0;
+       repaint();
+       sb.setValues(0, 1, 0);
+       }
+
+       // setWidths
+       // Set the proportional widths of each column
+       void setWidths(float w[])
+       {
+       for(int i=0; i<title.length; i++)
+               cwidth[i] = w[i];
+       respace();
+       repaint();
+       }
+
+       /**Turns on or off the user's ability to adjust column widths
+        * @param a     Can adjust or not?
+        */
+       void setAdjustable(boolean a)
+       {
+       adjustable = a;
+       }
+
+       /**Turns on or off the drawing of column lines
+        * @param d     Draw lines or not?
+        */
+       void setDrawLines(boolean d)
+       {
+       drawlines = d;
+       }
+
+       /**Sets the array of colors used to draw text items.
+        * @param c     The color array (in row/column order), or null to
+        *              use the default
+        */
+       void setColors(Color c[][])
+       {
+       colors = c;
+       repaint();
+       }
+
+       // Turns on or off multi-row selection with ctrl and shift
+       void setMultiSelect(boolean m)
+       {
+       multiselect = m;
+       }
+
+       // Enables the entire list
+       public void enable()
+       {
+       enabled = true;
+       sb.enable();
+       repaint();
+       }
+
+       // Disables the entire list
+       public void disable()
+       {
+       enabled = false;
+       sb.disable();
+       repaint();
+       }
+
+       // Sets or turns off the sort indication arrow for a column
+       // Direction 0 = None, 1 = Up arrow, 2 = Down arrow
+       public void sortingArrow(int col, int dir)
+       {
+       sortcol = col;
+       sortdir = dir;
+       repaint();
+       }
+
+       // reshape
+       // Called when this component gets resized
+       public void reshape(int nx, int ny, int nw, int nh)
+       {
+       if (nw != width+sbwidth || nh != height) {
+               in = insets();
+               sbwidth = sb.minimumSize().width;
+               width = nw-sbwidth - (in.left + in.right);
+               height = nh - (in.top + in.bottom);
+               sb.reshape(width+in.left, in.top, sbwidth, height);
+               respace();
+
+               // Force creation of a new backing image and re-painting
+               bim = null;
+               repaint();
+               compscroll();
+               }
+       super.reshape(nx, ny, nw, nh);
+       }
+
+       // respace
+       // Compute pixel column widths from proportional widths
+       void respace()
+       {
+       cpos[0] = 0;
+       for(int i=0; i<title.length; i++)
+               cpos[i+1] = cpos[i] + (int)(width*cwidth[i]);
+       }
+
+       // paint
+       // Blit the backing image to the front
+       public void paint(Graphics g)
+       {
+       super.paint(g);
+       if (bim == null) {
+               // This is the first rendering
+               bim = createImage(width, height);
+               bg = bim.getGraphics();
+               bg.setFont(font);
+               fnm = bg.getFontMetrics();
+               th = fnm.getHeight() + 4;
+               render();
+               compscroll();
+               }
+       g.drawImage(bim, in.left, in.top, this);
+       }
+
+       // update
+       // Called sometime after repaint()
+       public void update(Graphics g)
+       {
+       if (fnm != null) {
+               render();
+               paint(g);
+               }
+       }
+
+       // render
+       // Re-draw the list into the backing image
+       void render()
+       {
+       int fh = fnm.getHeight(),       // useful font metrics
+           fd = fnm.getDescent(),
+           fa = fnm.getAscent();
+       int bot = Math.min(top+rows()-1, list[0].size()-1);
+
+       // Clear title section and list
+       bg.setColor(Color.lightGray);
+       bg.fillRect(0, 0, width, th);
+       bg.setColor(Color.white);
+       bg.fillRect(0, th, width, height-th);
+       Color lighterGray = new Color(Color.lightGray.getRed() + 20,
+                                     Color.lightGray.getGreen() + 20,
+                                     Color.lightGray.getBlue() + 20);
+
+       if (enabled) {
+               // Mark the selected rows
+               for(int i=0; i<sels.length; i++) {
+                       if (sels[i] >= top && sels[i] <= bot) {
+                               bg.setColor(sels[i] == sel ? Color.lightGray
+                                                          : lighterGray);
+                               bg.fillRect(0, th+(sels[i]-top)*rowh,
+                                           width, rowh);
+                               }
+                       }
+               }
+
+       // Draw each column
+       for(int i=0; i<title.length; i++) {
+               int x = cpos[i], w = cpos[i+1]-x-1;
+
+               // Column title
+               bg.setColor(Color.white);
+               bg.drawLine(x, 0, x+w, 0);
+               bg.drawLine(x, 1, x+w-1, 1);
+               bg.drawLine(x, 0, x, th-1);
+               bg.drawLine(x+1, 0, x+1, th-2);
+               bg.setColor(Color.black);
+               bg.drawLine(x, th-1, x+w, th-1);
+               bg.drawLine(x, th-2, x+w-1, th-2);
+               bg.drawLine(x+w, th-1, x+w, 0);
+               bg.drawLine(x+w-1, th-1, x+w-1, 1);
+               int tw = fnm.stringWidth(title[i]);
+               if (tw < w-6)
+                       bg.drawString(title[i], x+(w-tw)/2, th-fd-2);
+
+               // Sorting arrow
+               int as = th-8;
+               if (sortcol == i && sortdir == 1) {
+                       bg.setColor(Color.white);
+                       bg.drawLine(x+4, th-5, x+4+as, th-5);
+                       bg.drawLine(x+4+as, th-5, x+4+as/2, th-5-as);
+                       bg.setColor(Color.black);
+                       bg.drawLine(x+4+as/2, th-5-as, x+4, th-5);
+                       }
+               else if (sortcol == i && sortdir == 2) {
+                       bg.setColor(Color.white);
+                       bg.drawLine(x+4+as/2, th-5, x+4+as, th-5-as);
+                       bg.setColor(Color.black);
+                       bg.drawLine(x+4, th-5-as, x+4+as, th-5-as);
+                       bg.drawLine(x+4, th-5-as, x+4+as/2, th-5);
+                       }
+
+               // Column items
+               if (drawlines) {
+                       bg.setColor(Color.lightGray);
+                       bg.drawLine(x+w-1, th, x+w-1, height);
+                       bg.setColor(Color.black);
+                       bg.drawLine(x+w, th, x+w, height);
+                       }
+               for(int j=top; j<=bot; j++) {
+                       Object o = list[i].elementAt(j);
+                       if (o instanceof String) {
+                               // Render string in column
+                               String s = (String)o;
+                               while(fnm.stringWidth(s) > w-3)
+                                       s = s.substring(0, s.length()-1);
+                               if (!enabled)
+                                       bg.setColor(Color.lightGray);
+                               else if (colors != null)
+                                       bg.setColor(colors[j][i]);
+                               bg.drawString(s, x+1, th+(j+1-top)*rowh-fd);
+                               }
+                       else if (o instanceof Image) {
+                               // Render image in column
+                               Image im = (Image)o;
+                               bg.drawImage(im, x+1, th+(j-top)*rowh, this);
+                               }
+                       }
+               }
+       }
+
+       // mouseDown
+       // Select a list item or a column to drag
+       public boolean mouseDown(Event e, int x, int y)
+       {
+       if (!enabled) {
+               return true;
+               }
+       x -= in.left;
+       y -= in.top;
+       coldrag = -1;
+       if (y < th) {
+               // Click in title bar
+               for(int i=0; i<title.length; i++) {
+                       if (adjustable && i > 0 && Math.abs(cpos[i] - x) < 3) {
+                               // clicked on a column separator
+                               coldrag = i;
+                               }
+                       else if (x >= cpos[i] && x < cpos[i+1]) {
+                               // clicked in a title
+                               callback.headingClicked(this, i);
+                               }
+                       }
+               }
+       else {
+               // Item chosen from list
+               int row = (y-th)/rowh + top;
+               if (row < list[0].size()) {
+                       // Double-click?
+                       boolean dclick = false;
+                       if (e.when-last < 1000 && sel == row)
+                               dclick = true;
+                       else
+                               last = e.when;
+
+                       if (e.shiftDown() && multiselect && sel != -1) {
+                               // Select all from last selection to this one
+                               int zero = sels[0];
+                               if (zero < row) {
+                                       sels = new int[row-zero+1];
+                                       for(int i=zero; i<=row; i++)
+                                               sels[i-zero] = i;
+                                       }
+                               else {
+                                       sels = new int[zero-row+1];
+                                       for(int i=zero; i>=row; i--)
+                                               sels[zero-i] = i;
+                                       }
+                               }
+                       else if (e.controlDown() && multiselect) {
+                               // Add this one to selection
+                               int nsels[] = new int[sels.length + 1];
+                               System.arraycopy(sels, 0, nsels, 0,sels.length);
+                               nsels[sels.length] = row;
+                               sels = nsels;
+                               }
+                       else {
+                               // Select one row only, and de-select others
+                               sels = new int[1];
+                               sels[0] = row;
+                               }
+                       sel = row;
+                       repaint();
+                       last_event = e;
+                       if (callback != null) {
+                               // Callback the right function
+                               if (dclick) callback.doubleClick(this, row);
+                               else        callback.singleClick(this, row);
+                               }
+                       else {
+                               // Send an event
+                               getParent().postEvent(
+                                       new Event(this,
+                                                 Event.ACTION_EVENT,
+                                                 dclick?"Double":"Single"));
+                               }
+                       }
+               }
+       return true;
+       }
+
+       // mouseDrag
+       // If a column is selected, change it's width
+       public boolean mouseDrag(Event e, int x, int y)
+       {
+       if (!enabled) {
+               return true;
+               }
+       x -= in.left;
+       y -= in.top;
+       if (coldrag != -1) {
+               if (x > cpos[coldrag-1]+3 && x < cpos[coldrag+1]-3) {
+                       cpos[coldrag] = x;
+                       cwidth[coldrag-1] = (cpos[coldrag]-cpos[coldrag-1]) /
+                                           (float)width;
+                       cwidth[coldrag] = (cpos[coldrag+1]-cpos[coldrag]) /
+                                           (float)width;
+                       repaint();
+                       }
+               }
+       return true;
+       }
+
+       public void moved(CbScrollbar s, int v)
+       {
+       moving(s, v);
+       }
+
+       public void moving(CbScrollbar s, int v)
+       {
+       top = sb.getValue();
+       compscroll();
+       repaint();
+       }
+
+       // compscroll
+       // Re-compute the size of the scrollbar
+       private void compscroll()
+       {
+       if (fnm == null)
+               return;         // not visible
+       int r = rows();
+       int c = list[0].size() - r;
+       sb.setValues(top, r==0?1:r, list[0].size());
+       }
+
+       // rows
+       // Returns the number of rows visible in the list
+       private int rows()
+       {
+       return Math.min(height/rowh - 1, list[0].size());
+       }
+
+       public Dimension minimumSize()
+       {
+       return new Dimension(400, 100);
+       }
+
+       public Dimension preferredSize()
+       {
+       return minimumSize();
+       }
+}
+
+// MultiColumnCallback
+// Objects implementing this interface can be passed to the MultiColumn
+// class, to have their singleClick() and doubleClick() functions called in
+// response to single or double click in the list.
+interface MultiColumnCallback
+{
+       // singleClick
+       // Called on a single click on a list item
+       void singleClick(MultiColumn list, int num);
+
+       // doubleClick
+       // Called upon double-clicking on a list item
+       void doubleClick(MultiColumn list, int num);
+
+       // headingClicked
+       // Called when a column heading is clicked on
+       void headingClicked(MultiColumn list, int col);
+}
+
diff --git a/file/QuickSort.java b/file/QuickSort.java
new file mode 100644 (file)
index 0000000..9f8858e
--- /dev/null
@@ -0,0 +1,77 @@
+public class QuickSort
+{
+    static int col, dir;
+
+    // Sorts entire array
+    public static void sort(RemoteFile array[], int c, int d)
+    {
+       col = c;
+       dir = d;
+        psort(array, 0, array.length - 1);
+    }
+
+    // Sorts partial array
+    public static void psort(RemoteFile array[], int start, int end)
+    {
+        int p;
+        if (end > start)
+        {
+            p = partition(array, start, end);
+            psort(array, start, p-1);
+            psort(array, p+1, end);
+        }
+    }
+
+    protected static int compare(RemoteFile a, RemoteFile b) {
+       long rv = 0;
+       if (col == 1)
+               rv = a.name.compareTo(b.name);
+       else if (col == 2)
+               rv = a.size - b.size;
+       else if (col == 3)
+               rv = a.user.compareTo(b.user);
+       else if (col == 4)
+               rv = a.group.compareTo(b.group);
+       else
+               rv = a.modified - b.modified;
+       rv = rv < 0 ? -1 : rv > 0 ? 1 : 0;
+       return (int)(dir == 2 ? -rv : rv);
+    }
+
+    protected static int partition(RemoteFile array[], int start, int end)
+    {
+        int left, right;
+        RemoteFile partitionElement;
+
+        // Arbitrary partition start...there are better ways...
+        partitionElement = array[end];
+
+        left = start - 1;
+        right = end;
+        for (;;)
+        {
+            while (compare(partitionElement, array[++left]) == 1)
+            {
+                if (left == end) break;
+            }
+            while (compare(partitionElement, array[--right]) == -1)
+            {
+                if (right == start) break;
+            }
+            if (left >= right) break;
+            swap(array, left, right);
+        }
+        swap(array, left, end);
+
+        return left;
+    }
+
+    protected static void swap(RemoteFile array[], int i, int j)
+    {
+        RemoteFile temp;
+       temp = array[i];
+       array[i] = array[j];
+       array[j] = temp;
+    }
+}
+
diff --git a/file/ResizePanel.java b/file/ResizePanel.java
new file mode 100644 (file)
index 0000000..7555204
--- /dev/null
@@ -0,0 +1,169 @@
+// ResizePanel
+// A panel with two parts, arranged either vertically or horizontally,
+// whose midpoint is adjustable
+import java.awt.*;
+import java.util.Vector;
+
+public class ResizePanel extends Panel implements LayoutManager
+{
+       Component one, two;
+       int pos = -1;
+       double ratio;
+       boolean vertical;
+       boolean dragging;
+       int border = 100;
+
+       // Provide two components where component one initially occupies rt fraction of
+       // parent area. When vertical=true components are layed out one above the other
+       public ResizePanel(Component one, Component two, double rt, boolean vertical)
+       {
+               this.one = one;
+               this.two = two;
+               this.vertical = vertical;
+               ratio = rt;
+               setLayout(this);
+               add(one);
+               add(two);
+       }
+
+       public void paint(Graphics g)
+       {
+               Dimension s = size();
+               if (vertical)
+               {
+                       // Draw horizontal bar between vertically aligned components
+                       pos = (int)(s.height * ratio);
+                       g.setColor(Color.white);
+                       g.drawLine(0, pos-2, 0, pos+1);
+                       g.drawLine(0, pos-2, s.width-2, pos-2);
+                       g.setColor(Color.black);
+                       g.drawLine(s.width-1, pos+2, s.width-1, pos-1);
+                       g.drawLine(s.width-1, pos+2, 1, pos+2);
+               }
+               else
+               {
+                       // Draw vertical divider bar
+                       pos = (int)(s.width * ratio);
+                       g.setColor(Color.white);
+                       g.drawLine(pos-2, 0, pos+1, 0);
+                       g.drawLine(pos-2, 0, pos-2, s.height-2);
+                       g.setColor(Color.black);
+                       g.drawLine(pos+2, s.height-1, pos-1, s.height-1);
+                       g.drawLine(pos+2, s.height-1, pos+2, 1);
+               }
+       }
+
+       // Detect mouse click on divider bar
+       public boolean mouseDown(Event evt, int x, int y)
+       {
+               int sh;
+               Dimension s = size();
+               if (vertical && y >= pos-2 && y <= pos+2)
+               {
+                       // Started dragging
+                       dragging = true;
+               }
+               if (!vertical && x >= pos-2 && x <= pos+2)
+               {
+                       // Started dragging
+                       dragging = true;
+               }
+               return dragging;
+       }
+
+       // Move division point on mouse drag
+       public boolean mouseDrag(Event evt, int x, int y)
+       {
+               if (dragging)
+               {
+                       Dimension s = size();
+                       if (vertical)
+                       {
+                               if (y < border)
+                                       pos = border;
+                               else if (y > s.height - border)
+                                       pos = s.height - border;
+                               else
+                                       pos = y;
+                               ratio = (double)pos / (double)s.height;
+                       }
+                       else
+                       {
+                               if (x < border)
+                                       pos = border;
+                               else if (x > s.width - border)
+                                       pos = s.width - border;
+                               else
+                                       pos = x;
+                               ratio = (double)pos / (double)s.width;
+                       }
+                       layoutContainer(this);
+                       repaint();
+               }
+               return dragging;
+       }
+
+       // No longer dragging on mouse button release
+       public boolean mouseUp(Event evt, int x, int y)
+       {
+               boolean o = dragging;
+               dragging = false;
+               return o;
+       }
+
+       public void addLayoutComponent(String name, Component comp)
+       {
+       }
+
+       // Arrange components within container
+       public void layoutContainer(Container parent)
+       {
+               Dimension s = parent.size();
+               if (vertical)
+               {
+                       pos = (int)(s.height * ratio);
+                       one.reshape(0, 0, s.width, pos-3);
+                       one.layout();
+                       two.reshape(0, pos+3, s.width, s.height - pos - 5);
+                       two.layout();
+               }
+               else
+               {
+                       pos = (int)(s.width * ratio);
+                       one.reshape(0, 0, pos-3, s.height);
+                       one.layout();
+                       two.reshape(pos+3, 0, s.width - pos - 5, s.height);
+                       two.layout();
+               }
+       }
+
+       // Determine minimum size for ResizePanel
+       public Dimension minimumLayoutSize(Container parent)
+       {
+               Dimension d1 = one.minimumSize(),
+                         d2 = two.minimumSize();
+
+               if (vertical)
+               {
+                       // Largest of the widths, sum of the heights
+                       return new Dimension(d1.width > d2.width ? d1.width : d2.width,
+                                            d1.height + d2.height);
+               }
+               else
+               {
+                       // Largest of the heights, sum of the widths
+                       return new Dimension(d1.width + d2.width,
+                                            d1.height > d2.height ? d1.height : d2.height);
+               }
+       }
+
+       public Dimension preferredLayoutSize(Container parent)
+       {
+               return minimumLayoutSize(parent);
+       }
+
+       public void removeLayoutComponent(Component comp)
+       {
+       }
+}
+
diff --git a/file/StaticTextField.java b/file/StaticTextField.java
new file mode 100644 (file)
index 0000000..57fd490
--- /dev/null
@@ -0,0 +1,24 @@
+import java.awt.*;
+
+// StaticTextField
+// A text field that is set to be non-editable by default
+class StaticTextField extends TextField
+{
+       StaticTextField()
+       {
+       super();
+       setEditable(false);
+       }
+
+       StaticTextField(String s)
+       {
+       super(s);
+       setEditable(false);
+       }
+
+       StaticTextField(String s, int i)
+       {
+       super(s,i);
+       setEditable(false);
+       }
+}
diff --git a/file/StringSplitter.java b/file/StringSplitter.java
new file mode 100644 (file)
index 0000000..48ba1e6
--- /dev/null
@@ -0,0 +1,103 @@
+import java.util.Vector;
+
+// StringSplitter
+// A stringsplitter object splits a string into a number of substrings,
+// each separated by one separator character. Separator characters can be
+// included in the string by escaping them with a \
+public class StringSplitter
+{
+       Vector parts = new Vector();
+       int pos = 0;
+
+       StringSplitter(String str, char sep)
+       {
+       this(str, sep, true);
+       }
+
+       StringSplitter(String str, char sep, boolean escape)
+       {
+       StringBuffer current;
+
+       parts.addElement(current = new StringBuffer());
+       for(int i=0; i<str.length(); i++) {
+               char c = str.charAt(i);
+               if (c == '\\' && i != str.length()-1 && escape)
+                       current.append(str.charAt(++i));
+               else if (c == sep)
+                       parts.addElement(current = new StringBuffer());
+               else
+                       current.append(c);
+               }
+       }
+
+       // countTokens
+       // The number of tokens left in the string
+       int countTokens()
+       {
+       return parts.size() - pos;
+       }
+
+       // hasMoreTokens
+       // Can we call nextToken?
+       boolean hasMoreTokens()
+       {
+       return pos < parts.size();
+       }
+
+       // nextToken
+       // Returns the string value of the next token
+       String nextToken()
+       {
+       if (pos < parts.size())
+               return ((StringBuffer)parts.elementAt(pos++)).toString();
+       else
+               return null;
+       }
+
+       // gettokens
+       // Returns a vector of strings split from the given input string
+       Vector gettokens()
+       {
+       return parts;
+       }
+}
+
+
+// StringJoiner
+// The complement of StringSplitter. Takes a number of substrings and adds
+// them to a string, separated by some character. If the separator character
+// appears in one of the substrings, escape it with a \
+class StringJoiner
+{
+       char sep;
+       StringBuffer str = new StringBuffer();
+       int count = 0;
+
+       // Create a new StringJoiner using the given separator
+       StringJoiner(char s)
+       {
+       sep = s;
+       }
+
+       // add
+       // Add one string, and a separator
+       void add(String s)
+       {
+       if (count != 0)
+               str.append(sep);
+       for(int i=0; i<s.length(); i++) {
+               char c = s.charAt(i);
+               if (c == sep || c == '\\') str.append('\\');
+               str.append(c);
+               }
+       count++;
+       }
+
+       // toString
+       // Get the resulting string
+       public String toString()
+       {
+       return str.toString();
+       }
+}
+
diff --git a/file/TabbedPanel.java b/file/TabbedPanel.java
new file mode 100644 (file)
index 0000000..dc6cc61
--- /dev/null
@@ -0,0 +1,185 @@
+// TabbedPanel
+// A panel capable of displaying one of many components at a time. The
+// component to display is chosen by a row of tab buttons.
+import java.awt.*;
+import java.util.Vector;
+
+public class TabbedPanel extends Panel
+{
+       TabSelector tab;                // component for choosing panel
+       TabbedDisplayPanel disp;        // where other panels are displayed
+       CardLayout card;
+
+       TabbedPanel()
+       {
+       this(Util.body_hi, Util.dark_edge_hi, Util.body);
+       }
+
+       TabbedPanel(Color hi, Color lo, Color bk)
+       {
+       setLayout(new BorderLayout());
+       add("North",tab = new TabSelector(hi, lo, bk));
+       add("Center",disp = new TabbedDisplayPanel(hi, lo));
+       disp.setLayout(card = new CardLayout());
+       }
+
+       // addItem
+       // Add a component to be chosen by a tab with the given name
+       void addItem(String n, Component c)
+       {
+       tab.addItem(n);
+       disp.addItem(n, c);
+       }
+
+       // select
+       // Display a component in the panel
+       void select(String n)
+       {
+       tab.choose(n);
+       disp.choose(n);
+       }
+
+       // chose
+       // Called back by a TabSelector object when the user clicks on a tab
+       void chose(String n)
+       {
+       disp.choose(n);
+       }
+}
+
+class TabSelector extends Canvas
+{
+       Color hi, lo, bk;
+       Vector name = new Vector();
+       int chosen = 0;
+       Font font = new Font("timesRoman", Font.PLAIN, 12),
+            chfont = new Font(font.getName(), Font.BOLD, 13);
+
+       TabSelector(Color h, Color l, Color b)
+       {
+       hi = h; lo = l; bk = b;
+       }
+
+       void addItem(String n)
+       {
+       name.addElement(n);
+       paint(getGraphics());
+       }
+
+       void choose(String n)
+       {
+       for(int i=0; i<name.size(); i++)
+               if (((String)name.elementAt(i)).equals(n)) {
+                       chosen = i;
+                       paint(getGraphics());
+                       }
+       }
+
+       public void paint(Graphics g)
+       {
+       if (g == null || name.size() == 0)
+               return;
+       g.setColor(bk);
+       g.fillRect(0, 0, size().width, size().height);
+       int tw = size().width / name.size(),
+           th = size().height;
+       for(int i=0; i<name.size(); i++) {
+               int x = tw*i;
+               if (i == chosen) {
+                       g.setColor(lo);
+                       g.drawLine(x+tw-3, 1, x+tw-3, th-1);
+                       g.drawLine(x+tw-4, 2, x+tw-4, th-1);
+                       g.setColor(hi);
+                       g.drawLine(x, 0, x, th-1);
+                       g.drawLine(x+1, 0, x+1, th-1);
+                       g.drawLine(x, 0, x+tw-4, 0);
+                       g.drawLine(x, 1, x+tw-5, 1);
+                       g.drawLine(x+tw-3, th-1, x+tw-1, th-1);
+                       g.drawLine(x+tw-3, th-2, x+tw-1, th-2);
+                       }
+               else {
+                       g.setColor(lo);
+                       g.drawLine(x+tw-3, 6, x+tw-3, th-1);
+                       g.drawLine(x+tw-4, 7, x+tw-4, th-1);
+                       g.setColor(hi);
+                       g.drawLine(x, 5, x, th-1);
+                       g.drawLine(x+1, 5, x+1, th-1);
+                       g.drawLine(x, 5, x+tw-4, 5);
+                       g.drawLine(x, 6, x+tw-5, 6);
+                       g.drawLine(x, th-1, x+tw-1, th-1);
+                       g.drawLine(x, th-2, x+tw-1, th-2);
+                       }
+               g.setColor(lo);
+               if (i == chosen) g.setFont(chfont);
+               else g.setFont(font);
+               String str = (String)name.elementAt(i);
+               int textw = g.getFontMetrics().stringWidth(str);
+               int texth = g.getFontMetrics().getHeight();
+               if (textw < tw-5)
+                       g.drawString(str, x+(tw-textw)/2, (th-texth)/2+texth);
+               }
+       }
+
+       public boolean mouseDown(Event evt, int x, int y)
+       {
+       if (name.size() == 0) return false;
+       chosen = x / (size().width / name.size());
+       paint(getGraphics());
+       ((TabbedPanel)getParent()).chose((String)name.elementAt(chosen));
+       return true;
+       }
+
+       public Dimension minimumSize()
+       {
+       return new Dimension(50,25);
+       }
+
+       public Dimension preferredSize()
+       {
+       return minimumSize();
+       }
+}
+
+class TabbedDisplayPanel extends Panel
+{
+       Color hi, lo;
+       CardLayout card;
+
+       TabbedDisplayPanel(Color h, Color l)
+       {
+       hi = h; lo = l;
+       setLayout(card = new CardLayout());
+       }
+
+       // addItem
+       // Add one component to the set of possibles to be shown
+       void addItem(String n, Component c)
+       {
+       add(n, c);
+       }
+
+       // choose
+       // Display the named panel
+       void choose(String n)
+       {
+       ((CardLayout)getLayout()).show(this, n);
+       }
+
+       public Insets insets()
+       {
+       return new Insets(5,5,5,5);
+       }
+
+       public void paint(Graphics g)
+       {
+       g.setColor(hi);
+       g.drawLine(0, 0, 0, size().height-1);
+       g.drawLine(1, 0, 1, size().height-1);
+       g.setColor(lo);
+       g.drawLine(0, size().height-1, size().width-1, size().height-1);
+       g.drawLine(0, size().height-2, size().width-1, size().height-2);
+       g.drawLine(size().width-1, size().height-1, size().width-1, 0);
+       g.drawLine(size().width-2, size().height-1, size().width-2, 0);
+       }
+}
+
diff --git a/file/ToolbarLayout.java b/file/ToolbarLayout.java
new file mode 100644 (file)
index 0000000..9a9f9d9
--- /dev/null
@@ -0,0 +1,333 @@
+import java.awt.*;
+import java.lang.Math;
+
+/**
+ * A ToolbarLayout arranges components in a left-to-right flow, much 
+ * like the FlowLayout which is supplied with the JDK.  However, it
+ * fixes the problem with the FlowLayout that occurs when a FlowLayout
+ * is for a North aligned component of a BorderLayout--namely, that
+ * if the window is shrunk so that some of the components within the
+ * FlowLayout wrap to the next line the component does not grow in
+ * height to support this wrapping.  This bug was caused by the library
+ * designers using the preferred size in recalculating, not the size
+ * which is determined by the window width.  As such, the flow layout
+ * would always want to be the height of one row.
+ *
+ * A ToolbarLayout lets each component assume its natural (preferred) size.
+ *
+ * NOTE: This class was initially a subclass of FlowLayout, but we
+ *       encountered problems using that approach.
+ *
+ * @version    0.10, 1999-04-27
+ * @author     Peter Armstrong
+ * @author     Tony Johnson
+ */
+public class ToolbarLayout implements LayoutManager, java.io.Serializable {
+
+    /**
+     * This value indicates that each row of components
+     * should be left-justified. 
+     */
+    public static final int LEFT       = 0;
+
+    /**
+     * This value indicates that each row of components
+     * should be centered. 
+     */
+    public static final int CENTER     = 1;
+
+    /**
+     * This value indicates that each row of components
+     * should be right-justified. 
+     */
+    public static final int RIGHT      = 2;
+
+    int align;
+    int hgap;
+    int vgap;
+
+
+    /**
+     * Constructs a new ToolbarLayout with a left alignment and a
+     * default 5-unit horizontal and vertical gap.
+     */
+    public ToolbarLayout() {
+               this(LEFT, 5, 5);
+    }
+
+    /**
+     * Constructs a new ToolbarLayout with the specified alignment and a
+     * default 5-unit horizontal and vertical gap.
+     * The value of the alignment argument must be one of 
+     * <code>ToolbarLayout.LEFT</code>, <code>ToolbarLayout.RIGHT</code>, 
+     * or <code>ToolbarLayout.CENTER</code>.
+     * @param align the alignment value
+     */
+    public ToolbarLayout(int align) {
+               this(align, 5, 5);
+    }
+
+    /**
+     * Creates a new ToolbarLayout with the indicated alignment 
+     * and the indicated horizontal and vertical gaps. 
+     * <p>
+     * The value of the alignment argument must be one of 
+     * <code>ToolbarLayout.LEFT</code>, <code>ToolbarLayout.RIGHT</code>, 
+     * or <code>ToolbarLayout.CENTER</code>.
+     * @param      align   the alignment value.
+     * @param      hgap    the horizontal gap between components.
+     * @param      vgap    the vertical gap between components.
+     */
+    public ToolbarLayout(int align, int hgap, int vgap) {
+               this.align = align;
+               this.hgap = hgap;
+               this.vgap = vgap;
+    }
+
+    /**
+     * Gets the alignment for this layout.
+     * Possible values are <code>ToolbarLayout.LEFT</code>,  
+     * <code>ToolbarLayout.RIGHT</code>, or <code>ToolbarLayout.CENTER</code>.  
+     * @return     the alignment value for this layout.
+     * @see        ToolbarLayout#setAlignment
+     */
+    public int getAlignment() {
+               return align;
+    }
+    
+    /**
+     * Sets the alignment for this layout.
+     * Possible values are <code>ToolbarLayout.LEFT</code>,  
+     * <code>ToolbarLayout.RIGHT</code>, and <code>ToolbarLayout.CENTER</code>.  
+     * @param      align the alignment value.
+     * @see        ToolbarLayout#getAlignment
+     */
+    public void setAlignment(int align) {
+               this.align = align;
+    }
+
+    /**
+     * Gets the horizontal gap between components.
+     * @return     the horizontal gap between components.
+     * @see        ToolbarLayout#setHgap
+     */
+    public int getHgap() {
+               return hgap;
+    }
+    
+    /**
+     * Sets the horizontal gap between components.
+     * @param hgap the horizontal gap between components
+     * @see        ToolbarLayout#getHgap
+     */
+    public void setHgap(int hgap) {
+               this.hgap = hgap;
+    }
+    
+    /**
+     * Gets the vertical gap between components.
+     * @return     the vertical gap between components.
+     * @see        ToolbarLayout#setVgap
+     */
+    public int getVgap() {
+               return vgap;
+    }
+    
+    /**
+     * Sets the vertical gap between components.
+     * @param vgap the vertical gap between components
+     * @see        ToolbarLayout#getVgap
+     */
+    public void setVgap(int vgap) {
+               this.vgap = vgap;
+    }
+
+    /**
+     * Adds the specified component to the layout.  Sets the orientation to be horizontal.
+     * @param name the name of the component
+     * @param comp the component to be added
+     */
+    public void addLayoutComponent(String name, Component comp) {
+    }
+
+    /**
+     * Removes the specified component from the layout. Not used by
+     * this class.  
+     * @param comp the component to remove
+     * @see       java.awt.Container#removeAll
+     */
+    public void removeLayoutComponent(Component comp) {
+    }
+
+    /**
+     * Returns the preferred dimensions for this layout given the components
+     * in the specified target container.  This method is the difference
+     * between ToolbarLayout and FlowLayout.
+     * @param target the component which needs to be laid out
+     * @return    the preferred dimensions to lay out the 
+     *                    subcomponents of the specified container.
+     * @see Container
+     * @see #minimumLayoutSize
+     * @see    java.awt.Container#getPreferredSize
+     */
+    public Dimension preferredLayoutSize(Container target) {
+               synchronized (target.getTreeLock()) {
+                       Dimension dim = new Dimension(0, 0);
+                       int nmembers = target.getComponentCount();
+
+                       Insets insets = target.getInsets();
+
+                       int numRows             = 1;                                                    //the number of rows
+                       int rowSumWidth = insets.left + insets.right;   //the width of the row so far
+                       int rowMaxWidth = target.getSize().width;               //the width that the ToolbarLayout is in
+                       int rowHeight   = 0;                                                    //the height of each row
+                       int numOnRow    = 0;                                                    //the number of components on the row
+                       
+                       for (int i = 0 ; i < nmembers ; i++) {
+                           Component m = target.getComponent(i);
+                           if (m.isVisible()) {
+                                       Dimension d = m.getPreferredSize();
+                                       rowHeight = Math.max(rowHeight, d.height);      //make each row the height of the biggest component of all
+                                       if (i > 0) {
+                                               rowSumWidth += hgap;//add on the pre-spacing if this is not the first component
+                                       }
+                                       rowSumWidth += d.width; //add the width of the component
+                                       
+                                       //if it overflowed and if there are components already on this row then bump this component to next row
+                                       if ((rowSumWidth + hgap) > rowMaxWidth) {
+                                               if (numOnRow > 0) {
+                                                       numRows++;
+                                                       rowSumWidth = insets.left + insets.right + d.width;
+                                                       numOnRow = 0;//reset the number of components on the next row (we ++ no matter what later)
+                                               }
+                                       }
+                                       numOnRow++;//add this component to the count of the number on the row
+                       }
+                       }
+                       dim.width = rowMaxWidth;
+                       dim.height = insets.top + insets.bottom + numRows*rowHeight + vgap*(numRows + 1);
+                       return dim;
+               }
+    }
+     
+    /**
+     * Returns the minimum dimensions needed to layout the components
+     * contained in the specified target container.
+     * @param target the component which needs to be laid out 
+     * @return    the minimum dimensions to lay out the 
+     *                    subcomponents of the specified container.
+     * @see #preferredLayoutSize
+     * @see       java.awt.Container
+     * @see       java.awt.Container#doLayout
+     */
+    public Dimension minimumLayoutSize(Container target) {
+               synchronized (target.getTreeLock()) {
+                       Dimension dim = new Dimension(0, 0);
+                       int nmembers = target.getComponentCount();
+
+                       for (int i = 0 ; i < nmembers ; i++) {
+                           Component m = target.getComponent(i);
+                           if (m.isVisible()) {
+                                       Dimension d = m.getMinimumSize();
+                                       dim.height = Math.max(dim.height, d.height);
+                                       if (i > 0) {
+                                           dim.width += hgap;
+                                       }
+                                       dim.width += d.width;
+                           }
+                       }
+                       Insets insets = target.getInsets();
+                       dim.width += insets.left + insets.right + hgap*2;
+                       dim.height += insets.top + insets.bottom + vgap*2;
+                       return dim;
+               }
+    }
+
+    /** 
+     * Centers the elements in the specified row, if there is any slack.
+     * @param target the component which needs to be moved
+     * @param x the x coordinate
+     * @param y the y coordinate
+     * @param width the width dimensions
+     * @param height the height dimensions
+     * @param rowStart the beginning of the row
+     * @param rowEnd the the ending of the row
+     */
+    private void moveComponents(Container target, int x, int y, int width, int height, int rowStart, int rowEnd) {
+               synchronized (target.getTreeLock()) {
+                       switch (align) {
+                       case LEFT:
+                           break;
+                       case CENTER:
+                           x += width / 2;
+                           break;
+                       case RIGHT:
+                           x += width;
+                           break;
+                       }
+                       for (int i = rowStart ; i < rowEnd ; i++) {
+                           Component m = target.getComponent(i);
+                           if (m.isVisible()) {
+                                       m.setLocation(x, y + (height - m.size().height) / 2);
+                                       x += hgap + m.size().width;
+                           }
+                       }
+               }
+    }
+
+    /**
+     * Lays out the container. This method lets each component take 
+     * its preferred size by reshaping the components in the 
+     * target container in order to satisfy the constraints of
+     * this <code>ToolbarLayout</code> object. 
+     * @param target the specified component being laid out.
+     * @see Container
+     * @see       java.awt.Container#doLayout
+     */
+    public void layoutContainer(Container target) {
+               synchronized (target.getTreeLock()) {
+                       Insets insets = target.getInsets();
+                       int maxwidth = target.size().width - (insets.left + insets.right + hgap*2);
+                       int nmembers = target.getComponentCount();
+                       int x = 0, y = insets.top + vgap;
+                       int rowh = 0, start = 0;
+
+                       for (int i = 0 ; i < nmembers ; i++) {
+                               Component m = target.getComponent(i);
+                           if (m.isVisible()) {
+                                       Dimension d = m.getPreferredSize();
+                                       m.setSize(d.width, d.height);
+                                       if ((x == 0) || ((x + d.width) <= maxwidth)) {
+                                           if (x > 0) {
+                                                       x += hgap;
+                                           }
+                                           x += d.width;
+                                           rowh = Math.max(rowh, d.height);
+                                       } else {
+                                           moveComponents(target, insets.left + hgap, y, maxwidth - x, rowh, start, i);
+                                           x = d.width;
+                                           y += vgap + rowh;
+                                           rowh = d.height;
+                                           start = i;
+                                       }
+                           }
+                       }
+                       moveComponents(target, insets.left + hgap, y, maxwidth - x, rowh, start, nmembers);
+               }
+    }
+    
+    /**
+     * Returns a string representation of this <code>ToolbarLayout</code>
+     * object and its values.
+     * @return     a string representation of this layout.
+     */
+    public String toString() {
+               String str = "";
+               switch (align) {
+                       case LEFT:    str = ",align=left"; break;
+                       case CENTER:  str = ",align=center"; break;
+                       case RIGHT:   str = ",align=right"; break;
+               }
+               return getClass().getName() + "[hgap=" + hgap + ",vgap=" + vgap + str + "]";
+    }
+}
diff --git a/file/Util.java b/file/Util.java
new file mode 100644 (file)
index 0000000..95d9a7d
--- /dev/null
@@ -0,0 +1,148 @@
+import java.awt.*;
+import java.awt.image.*;
+
+class Util
+{
+       static Frame fr;
+       static Graphics g;
+       static Font f;
+       static FontMetrics fnm;
+       static Toolkit tk;
+
+       static Color light_edge = Color.white;
+       static Color dark_edge = Color.black;
+       static Color body = Color.lightGray;
+       static Color body_hi = new Color(210, 210, 210);
+       static Color light_edge_hi = Color.white;
+       static Color dark_edge_hi = Color.darkGray;
+       static Color dark_bg = new Color(150, 150, 150);
+       static Color text = Color.black;
+       static Color light_bg = Color.white;
+
+       static
+       {
+       fr = new Frame();
+       fr.addNotify();
+       g = fr.getGraphics();
+       setFont(new Font("TimesRoman", Font.PLAIN, 8));
+       tk = Toolkit.getDefaultToolkit();
+       }
+
+       static boolean waitForImage(Image i)
+       {
+       MediaTracker mt = new MediaTracker(fr);
+       mt.addImage(i, 0);
+       try { mt.waitForAll(); } catch(Exception e) { return false; }
+       return !mt.isErrorAny();
+       }
+
+       static boolean waitForImage(Image i, int w, int h)
+       {
+       MediaTracker mt = new MediaTracker(fr);
+       mt.addImage(i, w, h, 0);
+       try { mt.waitForAll(); } catch(Exception e) { return false; }
+       return !mt.isErrorAny();
+       }
+
+       static int getWidth(Image i)
+       {
+       waitForImage(i);
+       return i.getWidth(fr);
+       }
+
+       static int getHeight(Image i)
+       {
+       waitForImage(i);
+       return i.getHeight(fr);
+       }
+
+       static Image createImage(int w, int h)
+       {
+       return fr.createImage(w, h);
+       }
+
+       static Image createImage(ImageProducer p)
+       {
+       return fr.createImage(p);
+       }
+
+       static Object createObject(String name)
+       {
+       try {
+               Class c = Class.forName(name);
+               return c.newInstance();
+               }
+       catch(Exception e) {
+               System.err.println("Failed to create object "+name+" : "+
+                                  e.getClass().getName());
+               System.exit(1);
+               }
+       return null;
+       }
+
+       /**Create a new instance of some object
+        */
+       static Object createObject(Object o)
+       {
+       try { return o.getClass().newInstance(); }
+       catch(Exception e) {
+               System.err.println("Failed to reproduce object "+o+" : "+
+                                e.getClass().getName());
+               System.exit(1);
+               }
+       return null;
+       }
+
+
+       static void dottedRect(Graphics g, int x1, int y1,
+                              int x2, int y2, int s)
+       {
+       int i, s2 = s*2, t;
+       if (x2 < x1) { t = x1; x1 = x2; x2 = t; }
+       if (y2 < y1) { t = y1; y1 = y2; y2 = t; }
+       for(i=x1; i<=x2; i+=s2)
+               g.drawLine(i, y1, i+s > x2 ? x2 : i+s, y1);
+       for(i=y1; i<=y2; i+=s2)
+               g.drawLine(x2, i, x2, i+s > y2 ? y2 : i+s);
+       for(i=x2; i>=x1; i-=s2)
+               g.drawLine(i, y2, i-s < x1 ? x1 : i-s, y2);
+       for(i=y2; i>=y1; i-=s2)
+               g.drawLine(x1, i, x1, i-s < y1 ? y1 : i-s);
+       }
+
+       static void recursiveLayout(Container c)
+       {
+       c.layout();
+       for(int i=0; i<c.countComponents(); i++) {
+               Component cc = c.getComponent(i);
+               if (cc instanceof Container)
+                       recursiveLayout((Container)cc);
+               }
+       }
+
+       static void recursiveBackground(Component c, Color b)
+       {
+       if (c instanceof TextField || c instanceof Choice ||
+           c instanceof TextArea)
+               return;         // leave these alone
+       c.setBackground(b);
+       if (c instanceof Container) {
+               Container cn = (Container)c;
+               for(int i=0; i<cn.countComponents(); i++)
+                       recursiveBackground(cn.getComponent(i), b);
+               }
+       }
+
+       static void recursiveBody(Component c)
+       {
+       recursiveBackground(c, Util.body);
+       }
+
+       static void setFont(Font nf)
+       {
+       f = nf;
+       g.setFont(f);
+       fnm = g.getFontMetrics();
+       }
+}
+
diff --git a/file/acl_security.pl b/file/acl_security.pl
new file mode 100644 (file)
index 0000000..571f255
--- /dev/null
@@ -0,0 +1,143 @@
+
+require 'file-lib.pl';
+do '../ui-lib.pl';
+
+# acl_security_form(&options)
+# Output HTML for editing security options for the file module
+sub acl_security_form
+{
+print "<tr> <td><b>$text{'acl_user'}</b></td>\n";
+local $u = $_[0]->{'uid'} < 0 ? '' : getpwuid($_[0]->{'uid'});
+printf "<td colspan=3><input type=radio name=uid_def value=1 %s> %s\n",
+       $_[0]->{'uid'} < 0 ? 'checked' : '', $text{'acl_user_def'};
+printf "<input type=radio name=uid_def value=0 %s>\n",
+       $_[0]->{'uid'} < 0 ? '' : 'checked';
+print "<input name=uid size=8 value='$u'> ",
+       &user_chooser_button("uid", 0),"</td> </tr>\n";
+
+print "<tr> <td><b>$text{'acl_umask'}</b></td>\n";
+print "<td colspan=3><input name=umask size=3 value='$_[0]->{'umask'}'></td> </tr>\n";
+
+print "<tr> <td><b>$text{'acl_follow'}</b></td> <td colspan=3>\n";
+printf "<input type=radio name=follow value=1 %s> $text{'yes'}\n",
+       $_[0]->{'follow'} == 1 ? "checked" : "";
+printf "<input type=radio name=follow value=2 %s> $text{'acl_fyes'}\n",
+       $_[0]->{'follow'} == 2 ? "checked" : "";
+printf "<input type=radio name=follow value=0 %s> $text{'no'}</td> </tr>\n",
+       $_[0]->{'follow'} == 0 ? "checked" : "";
+
+print "<tr> <td><b>$text{'acl_ro'}</b></td> <td colspan=3>\n";
+printf "<input type=radio name=ro value=1 %s> $text{'yes'}\n",
+       $_[0]->{'ro'} ? "checked" : "";
+printf "<input type=radio name=ro value=0 %s> $text{'no'}</td> </tr>\n",
+       $_[0]->{'ro'} ? "" : "checked";
+
+print "<tr> <td><b>$text{'acl_max'}</b></td>\n";
+printf "<td colspan=3><input type=radio name=max_def value=1 %s> %s\n",
+       $_[0]->{'max'} ? "" : "checked", $text{'acl_unlim'};
+printf "<input type=radio name=max_def value=0 %s>\n",
+       $_[0]->{'max'} ? "checked" : "";
+printf "<input name=max size=8 value='%s'> %s</td> </tr>\n",
+       $_[0]->{'max'}, $text{'acl_b'};
+
+print "<tr> <td><b>$text{'acl_archive'}</b></td> <td colspan=3>\n";
+printf "<input type=radio name=archive value=1 %s> $text{'yes'}\n",
+       $_[0]->{'archive'} == 1 ? "checked" : "";
+printf "<input type=radio name=archive value=2 %s> $text{'acl_archmax'}\n",
+       $_[0]->{'archive'} == 2 ? "checked" : "";
+printf "<input name=archmax size=10 value='%s'> %s\n",
+       $_[0]->{'archmax'}, $text{'acl_b'};
+printf "<input type=radio name=archive value=0 %s> $text{'no'}</td> </tr>\n",
+       $_[0]->{'archive'} == 0 ? "checked" : "";
+
+print "<tr> <td><b>$text{'acl_unarchive'}</b></td> <td colspan=3>\n";
+printf "<input type=radio name=unarchive value=2 %s> %s\n",
+       $_[0]->{'unarchive'} == 2 ? "checked" : "", $text{'acl_unarchive2'};
+printf "<input type=radio name=unarchive value=1 %s> %s\n",
+       $_[0]->{'unarchive'} == 1 ? "checked" : "", $text{'acl_unarchive1'};
+printf "<input type=radio name=unarchive value=0 %s> %s</td> </tr>\n",
+       $_[0]->{'unarchive'} == 0 ? "checked" : "", $text{'acl_unarchive0'};
+
+print "<tr> <td><b>$text{'acl_dostounix'}</b></td> <td colspan=3>\n";
+printf "<input type=radio name=dostounix value=1 %s> %s\n",
+       $_[0]->{'dostounix'} == 1 ? "checked" : "", $text{'yes'};
+printf "<input type=radio name=dostounix value=0 %s> %s</td> </tr>\n",
+       $_[0]->{'dostounix'} == 0 ? "checked" : "", $text{'no'};
+
+print "<tr> <td valign=top><b>$text{'acl_buttons'}</b></td> <td colspan=3>\n";
+foreach $b (@file_buttons) {
+       printf "<input type=checkbox name=button_%s %s> %s<br>\n",
+               $b, $_[0]->{'button_'.$b} ? "checked" : "",
+               $text{'acl_button_'.$b};
+       }
+print "</td> </tr>\n";
+
+print "<tr> <td><b>$text{'acl_noperms'}</b></td>\n";
+print "<td>",&ui_radio("noperms", int($_[0]->{'noperms'}),
+              [ [ 0, $text{'yes'} ], [ 1, $text{'no'} ] ]),"</td>\n";
+
+print "<td><b>$text{'acl_nousers'}</b></td>\n";
+print "<td>",&ui_radio("nousers", int($_[0]->{'nousers'}),
+              [ [ 0, $text{'yes'} ], [ 1, $text{'no'} ] ]),"</td> </tr>\n";
+
+print "<tr> <td><b>$text{'acl_filesystems'}</b></td>\n";
+print "<td>",&ui_yesno_radio("filesystems",
+                            int($_[0]->{'filesystems'})),"</td>\n";
+
+print "<td><b>$text{'acl_contents'}</b></td>\n";
+print "<td>",&ui_yesno_radio("contents",
+                            int($_[0]->{'contents'})),"</td> </tr>\n";
+
+print "<tr> <td><b>$text{'acl_chroot'}</b></td>\n";
+printf "<td colspan=3><input name=chroot size=40 value='%s'></td>\n",
+       $_[0]->{'chroot'};
+
+print "<tr> <td valign=top><b>$text{'acl_dirs'}</b><br>$text{'acl_relto'}</td>\n";
+print "<td colspan=3><textarea name=root rows=3 cols=40>",
+       join("\n", split(/\s+/, $_[0]->{'root'})),"</textarea><br>\n";
+printf "<input type=checkbox name=home value=1 %s> %s<br>\n",
+       $_[0]->{'home'} ? 'checked' : '', $text{'acl_home'};
+printf "<input type=checkbox name=goto value=1 %s> %s</td>\n",
+       $_[0]->{'goto'} ? 'checked' : '', $text{'acl_goto'};
+
+print "<tr> <td valign=top><b>$text{'acl_nodirs'}</b><br>$text{'acl_relto'}</td>\n";
+print "<td colspan=3><textarea name=noroot rows=3 cols=40>",
+       join("\n", split(/\s+/, $_[0]->{'noroot'})),"</textarea><br>\n";
+}
+
+# acl_security_save(&options)
+# Parse the form for security options for the file module
+sub acl_security_save
+{
+$_[0]->{'uid'} = $in{'uid_def'} ? -1 : getpwnam($in{'uid'});
+$in{'root'} =~ s/\r//g;
+local @root = split(/\s+/, $in{'root'});
+map { s/\/+/\//g } @root;
+map { s/([^\/])\/+$/$1/ } @root;
+$_[0]->{'root'} = join(" ", @root);
+$in{'noroot'} =~ s/\r//g;
+local @noroot = split(/\s+/, $in{'noroot'});
+map { s/\/+/\//g } @noroot;
+map { s/([^\/])\/+$/$1/ } @noroot;
+$_[0]->{'noroot'} = join(" ", @noroot);
+$_[0]->{'follow'} = $in{'follow'};
+$_[0]->{'ro'} = $in{'ro'};
+$in{'umask'} =~ /^[0-7]{3}$/ || &error("Invalid umask");
+$_[0]->{'umask'} = $in{'umask'};
+$_[0]->{'home'} = $in{'home'};
+$_[0]->{'goto'} = $in{'goto'};
+$_[0]->{'max'} = $in{'max_def'} ? undef : $in{'max'};
+$_[0]->{'archive'} = $in{'archive'};
+$_[0]->{'archmax'} = $in{'archmax'};
+foreach $b (@file_buttons) {
+       $_[0]->{"button_$b"} = $in{"button_$b"};
+       }
+$_[0]->{'unarchive'} = $in{'unarchive'};
+$_[0]->{'dostounix'} = $in{'dostounix'};
+$_[0]->{'chroot'} = $in{'chroot'};
+$_[0]->{'noperms'} = $in{'noperms'};
+$_[0]->{'nousers'} = $in{'nousers'};
+$_[0]->{'filesystems'} = $in{'filesystems'};
+$_[0]->{'contents'} = $in{'contents'};
+}
+
diff --git a/file/chmod.cgi b/file/chmod.cgi
new file mode 100755 (executable)
index 0000000..914b5c1
--- /dev/null
@@ -0,0 +1,92 @@
+#!/usr/local/bin/perl
+# chmod.cgi
+# Change the ownership and permissions on a file
+
+require './file-lib.pl';
+$disallowed_buttons{'info'} && &error($text{'ebutton'});
+&ReadParse();
+&webmin_log($in{'linkto'} ? "relink" : "chmod", undef, $in{'path'}, \%in);
+&switch_acl_uid_and_chroot();
+print "Content-type: text/plain\n\n";
+!$access{'ro'} && &can_access($in{'path'}) ||
+       &failure(&text('chmod_eaccess', $in{'path'}));
+
+if (defined($in{'user'})) {
+       $uid = $in{'user'} =~ /^\d+$/ ? $in{'user'} :
+              defined(%user_to_uid) ? $user_to_uid{$in{'user'}} :
+                                      getpwnam($in{'user'});
+       &failure(&text('chmod_euser', $in{'user'})) if (!defined($uid));
+       $gid = $in{'group'} =~ /^\d+$/ ? $in{'group'} :
+              defined(%group_to_gid) ? $group_to_gid{$in{'group'}} :
+                                        getgrnam($in{'group'});
+       &failure(&text('chmod_egroup', $in{'group'})) if (!defined($gid));
+       }
+
+if ($in{'linkto'}) {
+       # Just changing the link target
+       $follow && &failure($text{'chmod_efollow'});
+       &lock_file($in{'path'});
+       unlink($in{'path'});
+       symlink($in{'linkto'}, $in{'path'}) ||
+               &failure(&text('chmod_elink', $1));
+       &unlock_file($in{'path'});
+       }
+elsif ($in{'rec'} == 0) {
+       # Just this file
+       &update($in{'path'}, 0);
+       }
+elsif ($in{'rec'} == 1) {
+       # This directory and all its files
+       &update($in{'path'}, 0);
+       opendir(DIR, $in{'path'});
+       foreach $f (readdir(DIR)) {
+               next if ($f eq "." || $f eq "..");
+               next if (-l $full);
+               &update("$in{'path'}/$f", 1) if (!-d $full);
+               }
+       closedir(DIR);
+       }
+elsif ($in{'rec'} == 2) {
+       # Directory and all subdirectories
+       &update($in{'path'}, 0);
+       &recurse($in{'path'});
+       }
+print "\n";
+
+sub recurse
+{
+local(@files, $f, $full);
+opendir(DIR, $_[0]);
+@files = readdir(DIR);
+closedir(DIR);
+foreach $f (@files) {
+       $full = "$_[0]/$f";
+       next if ($f eq "." || $f eq "..");
+       next if (-l $full);
+       &update($full, !-d $full);
+       &recurse($full) if (-d $full);
+       }
+}
+
+sub failure
+{
+print @_,"\n";
+exit;
+}
+# update(file, perms_only)
+sub update
+{
+local $perms = $in{'perms'};
+if (defined($perms)) {
+       if ($_[1]) {
+               @st = stat($_[0]);
+               $perms = ($perms & 0777) | ($st[2] & 037777777000);
+               }
+       chmod($perms, $_[0]) || &failure(&text('chmod_echmod', $!));
+       }
+if (defined($uid)) {
+       chown($uid, $gid, $_[0]) || &failure(&text('chmod_echown', $!));
+       }
+}
+
diff --git a/file/config b/file/config
new file mode 100644 (file)
index 0000000..ca12645
--- /dev/null
@@ -0,0 +1,5 @@
+hide_dot_files=0
+iconsize=0
+nocharset=0
+extract=1
+force_text=0
diff --git a/file/config-*-linux b/file/config-*-linux
new file mode 100644 (file)
index 0000000..172d018
--- /dev/null
@@ -0,0 +1,16 @@
+xfs_acl=&has_command("getfacl") && &has_command("setfacl")
+ext2_acl=&has_command("getfacl") && &has_command("setfacl")
+ext3_acl=&has_command("getfacl") && &has_command("setfacl")
+reiserfs_acl=&has_command("getfacl") && &has_command("setfacl")
+xfs_attr=&has_command("attr")
+ext2_attr=&has_command("attr")
+ext3_attr=&has_command("attr")
+ext2_ext=&has_command("lsattr") && &has_command("chattr")
+ext3_ext=&has_command("lsattr") && &has_command("chattr")
+getfacl=getfacl
+setfacl=setfacl --set-file=-
+hide_dot_files=0
+iconsize=0
+nocharset=0
+extract=1
+force_text=0
diff --git a/file/config-irix b/file/config-irix
new file mode 100644 (file)
index 0000000..fe0330a
--- /dev/null
@@ -0,0 +1,9 @@
+xfs_acl=&has_command("chacl")
+xfs_attr=&has_command("attr")
+getfacl=./irix-getfacl.pl
+setfacl=./irix-setfacl.pl
+hide_dot_files=0
+iconsize=0
+nocharset=0
+extract=1
+force_text=0
diff --git a/file/config-solaris b/file/config-solaris
new file mode 100644 (file)
index 0000000..1f3020c
--- /dev/null
@@ -0,0 +1,10 @@
+ufs_acl=&has_command("getfacl") && &has_command("setfacl")
+nfs_acl=&has_command("getfacl") && &has_command("setfacl")
+lofs_acl=&has_command("getfacl") && &has_command("setfacl")
+getfacl=getfacl
+setfacl=setfacl -f -
+hide_dot_files=0
+iconsize=0
+nocharset=0
+extract=1
+force_text=0
diff --git a/file/config.info b/file/config.info
new file mode 100644 (file)
index 0000000..9d062a2
--- /dev/null
@@ -0,0 +1,8 @@
+hide_dot_files=Show files starting with a dot?,1,0-Yes,1-No
+iconsize=Size of buttons in toolbar,1,1-Small,0-Large with labels
+nocharset=Attempt to use proper character set?,1,0-Yes,1-No
+extract=Extract .class files from JAR?,1,1-Yes,0-No
+width=Width for scaled images,3,Default (300 pixels)
+fixed=Font size for text,3,Default (12 points)
+small_fixed=Font size for buttons,3,Default (10 points)
+force_text=Editor for HTML files,1,1-Text editor,0-HTML editor
diff --git a/file/config.info.ca b/file/config.info.ca
new file mode 100755 (executable)
index 0000000..06bfec8
--- /dev/null
@@ -0,0 +1,8 @@
+hide_dot_files=Mostra els fitxers que comencen amb un punt,1,0-Sí,1-No
+iconsize=Mida dels botons a la barra d'eines,1,1-Petita,0-Gran amb etiquetes
+nocharset=Intenta fer servir el joc de caràcters apropiat,1,0-Sí,1-No
+extract=Extreu els fitxers .class del JAR?,1,1-Sí,0-No
+width=Amplada de les imatges escalades,3,Per defecte (300 píxels)
+fixed=Mida de la tipografia del text,3,Per defecte (12 punts)
+small_fixed=Mida de la tipografia dels botons,3,Per defecte (10 punts)
+force_text=Editor de fitxers HTML,1,1-Editor de text,0-Editor HTML
diff --git a/file/config.info.de b/file/config.info.de
new file mode 100644 (file)
index 0000000..5b2fcce
--- /dev/null
@@ -0,0 +1,4 @@
+hide_dot_files=Zeige Dateien mit einem Punkt am Anfang?,1,0-Ja,1-Nein
+iconsize=Gr&#246;&#223;e der Buttons in der Toolbar,1,1-Klein,0-Gro&#223; mit Bildunterschrift
+nocharset=Versuche passende Zeichens&#228;tze zu benutzen?,1,0-Ja,1-Nein
+extract=.class&#45;Dateien aus dem JAR extrahieren?,1,1-Ja,0-Nein
diff --git a/file/config.info.es b/file/config.info.es
new file mode 100644 (file)
index 0000000..8ae5a5e
--- /dev/null
@@ -0,0 +1,4 @@
+hide_dot_files=&#191;Mostrar archivos que comienzan con punto?,1,0-S&#237;,1-No
+iconsize=Tama&#241;o de botones en la barra de herramientas,1,1-Peque&#241;o,0-Grande con etiquetas
+nocharset=&#191;Intentar usar el juego de caracteres apropiado?,1,0-S&#237;,1-No
+extract=&#191;Extraer archivos .class del JAR?,1,1-S&#237;,0-No
diff --git a/file/config.info.fa b/file/config.info.fa
new file mode 100644 (file)
index 0000000..6cef9a7
--- /dev/null
@@ -0,0 +1,6 @@
+\r
+hide_dot_files=آيا پرونده‌هايي که با يک نقطه آغاز مي‌شوند نمايش داده شوند؟,1,0-بله,1-خير\r
+iconsize=اندازه دگمه‌ها در نوار ابزار,1,1-کوچک,0-بزرگ به همراه برچسبها\r
+nocharset=آيا مي‌خواهيد از مجموعه کاراکترهاي خاص استفاده کنيد؟,1,0-بله,1-خير\r
+extract=آيا پرونده‌هاي .class از پرونده‌هاي JAR استخراج شوند؟,1,1-بله,0-خير\r
+\r
diff --git a/file/config.info.it b/file/config.info.it
new file mode 100755 (executable)
index 0000000..8c8f49c
--- /dev/null
@@ -0,0 +1,4 @@
+hide_dot_files=Visualizza i file che iniziano con un punto?,1,0-Si,1-No
+iconsize=Dimensioni dei pulsanti nella barra degli strumenti,1,1-Piccoli,0-Grandi con etichette
+nocharset=Tenta di usare il set di caratteri appropriato?,1,0-Si,1-No
+extract=Estrarre i file .class dall'archivio JAR?,1,1-Si,0-No
diff --git a/file/config.info.tr b/file/config.info.tr
new file mode 100644 (file)
index 0000000..6dab867
--- /dev/null
@@ -0,0 +1,4 @@
+hide_dot_files=Nokta ile baþlayan dosyalar gösterilsin mi?,1,0-Evet,1-Hayýr
+iconsize=Araç çubuðundaki butonlarýn boyutu,1,1-Küçük,0-Geniþ ve etiketli
+nocharset=Uygun karakter seti kullanýlmaya çalýþýlsýn mý?,1,0-Evet,1-Hayýr
+extract=JAR'daki .class dosyalarý açýlsýn mý?,1,1-Evet,0-Hayýr
diff --git a/file/copy.cgi b/file/copy.cgi
new file mode 100755 (executable)
index 0000000..f3a8ada
--- /dev/null
@@ -0,0 +1,49 @@
+#!/usr/local/bin/perl
+# copy.cgi
+# Copy some file or directory
+
+require './file-lib.pl';
+$disallowed_buttons{'copy'} && &error($text{'ebutton'});
+&ReadParse();
+&webmin_log("copy", undef, $in{'from'}, \%in);
+print "Content-type: text/plain\n\n";
+if ($access{'ro'} || !&can_access($in{'from'})) {
+       print &text('copy_efrom', $in{'from'}),"\n";
+       exit;
+       }
+if (!&can_access($in{'to'})) {
+       print &text('copy_eto', $in{'to'}),"\n";
+       exit;
+       }
+if (-l &unmake_chroot($in{'from'})) {
+       # Remake the link
+       &switch_acl_uid_and_chroot();
+       &lock_file($in{'to'});
+       if (!symlink(readlink($in{'from'}), $in{'to'})) {
+               print &text('copy_elink', $!),"\n";
+               exit;
+               }
+       &unlock_file($in{'to'});
+       $err = undef;
+       $info = $in{'to'};
+       }
+else {
+       &switch_acl_uid();
+       ($ok, $err) = &copy_source_dest(&unmake_chroot($in{'from'}), &unmake_chroot($in{'to'}));
+       $err = undef if ($ok);
+       $info = &unmake_chroot($in{'to'});
+       }
+if ($err) {
+       print $err,"\n";
+       }
+else {
+       print "\n";
+       print &file_info_line($info),"\n";
+       }
+
+sub split_dir
+{
+$_[0] =~ /^(.*\/)([^\/]+)$/;
+return ($1, $2);
+}
+
diff --git a/file/defaultacl b/file/defaultacl
new file mode 100644 (file)
index 0000000..f8f93ec
--- /dev/null
@@ -0,0 +1,33 @@
+noconfig=0
+user=0
+root=/
+follow=0
+umask=022
+log=0
+ro=0
+goto=1
+archive=1
+button_save=1
+button_edit=1
+button_info=1
+button_acl=1
+button_attr=1
+button_ext=1
+button_search=1
+button_delete=1
+button_new=1
+button_upload=1
+button_mkdir=1
+button_makelink=1
+button_rename=1
+button_sharing=1
+button_mount=1
+button_copy=1
+button_preview=1
+unarchive=1
+dostounix=1
+chroot=/
+noperms=0
+nousers=0
+filesystems=1
+contents=1
diff --git a/file/delete.cgi b/file/delete.cgi
new file mode 100755 (executable)
index 0000000..6efc366
--- /dev/null
@@ -0,0 +1,26 @@
+#!/usr/local/bin/perl
+# delete.cgi
+# Delete some file or directory
+
+require './file-lib.pl';
+$disallowed_buttons{'delete'} && &error($text{'ebutton'});
+&ReadParse();
+&webmin_log("delete", undef, $in{'file'}, \%in);
+print "Content-type: text/plain\n\n";
+if ($access{'ro'} || !&can_access($in{'file'})) {
+       print &text('delete_eaccess', $in{'file'}),"\n";
+       exit;
+       }
+if (-r &unmake_chroot($in{'file'}) && !-d &unmake_chroot($in{'file'})) {
+       &switch_acl_uid_and_chroot();
+       $rv = unlink($in{'file'});
+       if (!$rv) { print "$!\n"; }
+       else { print "\n"; }
+       }
+else {
+       &switch_acl_uid();
+       ($ok, $err) = &unlink_file(&unmake_chroot($in{'file'}));
+       if (!$ok) { print "$err\n"; }
+       else { print "\n"; }
+       }
+
diff --git a/file/edit_html.cgi b/file/edit_html.cgi
new file mode 100755 (executable)
index 0000000..8ccf55d
--- /dev/null
@@ -0,0 +1,54 @@
+#!/usr/local/bin/perl
+# Show an HTML editor window
+
+require './file-lib.pl';
+do '../ui-lib.pl';
+$disallowed_buttons{'edit'} && &error($text{'ebutton'});
+&ReadParse();
+&popup_header($in{'file'} ? $text{'html_title'} : $text{'html_title2'},
+             undef, "onload='initEditor()'");
+
+# Output HTMLarea init code
+print <<EOF;
+<script type="text/javascript">
+  _editor_url = "$gconfig{'webprefix'}/$module_name/xinha/";
+  _editor_lang = "en";
+</script>
+<script type="text/javascript" src="xinha/htmlarea.js"></script>
+
+<script type="text/javascript">
+var editor = null;
+function initEditor() {
+  editor = new HTMLArea("body");
+  editor.generate();
+  return false;
+}
+</script>
+EOF
+
+# Read the file
+&switch_acl_uid_and_chroot();
+$data = &read_file_contents($in{'file'});
+
+# Output text area
+print &ui_form_start("save_html.cgi", "form-data");
+if ($in{'file'}) {
+       # Editing existing file
+       print &ui_hidden("file", $in{'file'}),"\n";
+       $pc = 95;
+       }
+else {
+       # Creating new, so prompt for path
+       print $text{'edit_filename'}," ",
+             &ui_textbox("file", $in{'dir'}, 70),"<br>\n";
+       $pc = 90;
+       }
+print "<textarea rows=20 cols=80 style='width:100%;height:$pc%' name=body id=body>";
+print &html_escape($data);
+print "</textarea>\n";
+print &ui_submit($text{'html_save'});
+print &ui_form_end();
+
+&popup_footer();
+
+
diff --git a/file/extract.cgi b/file/extract.cgi
new file mode 100755 (executable)
index 0000000..5570bb3
--- /dev/null
@@ -0,0 +1,21 @@
+#!/usr/local/bin/perl
+# Extract a zip, tar, tar.gz or tar.bz file on the server
+
+require './file-lib.pl';
+&ReadParse();
+print "Content-type: text/plain\n\n";
+
+# Check permissions
+$disallowed_buttons{'upload'} && &error($text{'ebutton'});
+if (!&can_access($in{'file'})) {
+       print &text('extract_eperm', $in{'file'}),"\n";
+       exit(0);
+       }
+
+# Go for it
+&webmin_log("extract", undef, $in{'file'});
+$realfile = &unmake_chroot($in{'file'});
+&switch_acl_uid();
+$err = &extract_archive($in{'file'}, $in{'delete'});
+print $err,"\n";
+
diff --git a/file/file-lib.pl b/file/file-lib.pl
new file mode 100644 (file)
index 0000000..fdee103
--- /dev/null
@@ -0,0 +1,452 @@
+# file-lib.pl
+# Common functions for file manager CGIs
+
+do '../web-lib.pl';
+&ReadParse(\%prein, 'GET');
+if ($prein{'trust'}) {
+       &open_trust_db();
+       if ($trustdb{$prein{'trust'}}) {
+               $trust_unknown_referers = 1;
+               $trustdb{$prein{'trust'}} = time();
+               }
+       dbmclose(%trustdb);
+       }
+&init_config();
+do '../ui-lib.pl';
+
+@file_buttons = ( "save", "preview", "edit", "info", "acl", "attr", "ext", "search",
+                 "delete", "new", "upload", "mkdir", "makelink",
+                 "rename", "sharing", "mount", "copy" );
+
+if ($module_info{'usermin'}) {
+       # Usermin gets the allowed list from the module config
+       &switch_to_remote_user();
+       &create_user_config_dirs();
+       $hide_dot_files = $userconfig{'hide_dot_files'};
+       $follow = int($config{'follow'});
+       $real_home_dir = &simplify_path(&resolve_links($remote_user_info[7]));
+       $upload_max = $config{'max'};
+
+       if ($config{'home_only'} == 1) {
+               @allowed_roots = ( $real_home_dir,
+                                  split(/\s+/, $config{'root'}) );
+               }
+       elsif ($config{'home_only'} == 2) {
+               @allowed_roots = split(/\s+/, $config{'root'});
+               }
+       else {
+               @allowed_roots = ( "/" );
+               }
+       @denied_roots = split(/\s+/, $config{'noroot'});
+
+       if ($config{'archive'} eq 'y') {
+               $archive = 1;
+               }
+       elsif ($config{'archive'} eq 'n') {
+               $archive = 0;
+               }
+       else {
+               $archive = 2;
+               $archmax = $config{'archive'};
+               }
+       $unarchive = 1;
+       $dostounix = 1;
+       $chroot = "/";
+
+       @disallowed_buttons = ( );
+       foreach $k (keys %config) {
+               if ($k =~ /^button_(.*)/ && $config{$k} == 0) {
+                       push(@disallowed_buttons, $1);
+                       }
+               }
+       $canperms = 1;
+       $canusers = 1;
+       $contents = 1;
+       }
+else {
+       # Webmin gets the list of allowed directories from the ACL
+       %access = &get_module_acl();
+       $hide_dot_files = $config{'hide_dot_files'};
+       $follow = int($access{'follow'});
+       $upload_max = $access{'max'};
+
+       @allowed_roots = split(/\s+/, $access{'root'});
+       if ($access{'home'}) {
+               local @u = getpwnam($remote_user);
+               if (@u) {
+                       push(@allowed_roots,
+                            &simplify_path(&resolve_links($u[7])));
+                       }
+               }
+       @denied_roots = split(/\s+/, $access{'noroot'});
+
+       $archive = $access{'archive'};
+       $archmax = $access{'archmax'};
+       $unarchive = $access{'unarchive'};
+       $dostounix = $access{'dostounix'};
+       $chroot = $access{'chroot'};
+       $access{'button_search'} = 0 if (!&has_command("find"));
+       $access{'button_makelink'} = 0 if (!&supports_symlinks());
+       $access{'button_info'} = 0 if (!&supports_users());
+
+       @disallowed_buttons = grep { !$access{'button_'.$_} } @file_buttons;
+       if (&is_readonly_mode()) {
+               # Force read-only mode for file manager if global readonly
+               # is in effect.
+               $access{'ro'} = 1;
+               }
+       $canperms = $access{'noperms'} ? 0 : 1;
+       $canusers = $access{'nousers'} ? 0 : 1;
+       $contents = $access{'contents'};
+       }
+%disallowed_buttons = map { $_, 1 } @disallowed_buttons;
+
+$icon_map = (  "c", 1,    "txt", 1,
+               "pl", 1,   "cgi", 1,
+               "html", 1, "htm", 1,
+               "gif", 2,  "jpg", 2,
+               "tar", 3
+               );
+
+# file_info_line(path, [displaypath])
+# Returns a line of text containing encoded details of some file
+sub file_info_line
+{
+local @st;
+local $islink = (-l $_[0]);
+local $f = $islink && &must_follow($_[0]);
+local @st = $f ? stat($_[0]) : lstat($_[0]);
+local $ext = $_[0] =~ /\S+\.([^\.\/]+)$/ ? $1 : undef;
+local $dp = $_[1] || $_[0];
+$dp =~ s/\\/\\\\/g;
+$dp =~ s/\t/\\t/g;
+return undef if ($dp =~ /\r|\n/);
+if (!@st) {
+       # Work around a broken stat function on large files on redhat 7.x
+       &has_command("stat") || return undef;
+       local $out = `stat -t '$_[0]'`;
+       return undef if ($?);
+       $out =~ /^(.*)\s+(\d+)\s+(\d+)\s+(\S+)\s+(\d+)\s+(\d+)\s+(\S+)\s+(\S+)\s+(\S+)\s+(\S+)\s+(\S+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)/;
+       local $type = defined($icon_map{$ext}) ? $icon_map{$ext} : 4;
+       local $user = defined(%uid_to_user) ? $uid_to_user{$5} : getpwuid($5);
+       $user = $5 if (!$user);
+       local $group = defined(%gid_to_group) ? $gid_to_group{$6} :getgrgid($6);
+       $group = $6 if (!$group);
+       local $size = $2;
+       local $mtime = $13;
+       local $mode = hex($4);
+       return sprintf ("%s\t%u\t%s\t%s\t%u\t%u\t%u\t%s",
+               $dp, $type, $user, $group, $size, $mode, $mtime, undef);
+       }
+local $type = $islink && !$f ? 5 :
+             -d _ ? 0 :
+             -b _ ? 6 :
+             -c _ ? 6 :
+             -p _ ? 7 :
+             -S _ ? 7 : defined($icon_map{$ext}) ? $icon_map{$ext} : 4;
+local $user = !&supports_users() ? "root" :
+             defined(%uid_to_user) ? $uid_to_user{$st[4]} : getpwuid($st[4]);
+$user = $st[4] if (!$user);
+local $group = !&supports_users() ? "root" :
+              defined(%gid_to_group) ? $gid_to_group{$st[5]} :getgrgid($st[5]);
+$group = $st[5] if (!$group);
+local $rl = readlink($_[0]);
+return join("\t", $dp, $type,
+                 $user, $group,
+                 $st[7] < 0 ? 2**32+$st[7] : $st[7], $st[2],
+                 $st[9], $f ? "" : $islink && !$rl ? "???" : $rl);
+}
+
+# switch_acl_uid()
+sub switch_acl_uid
+{
+if (!$module_info{'usermin'} && $access{'uid'}) {
+       local @u = $access{'uid'} < 0 ? getpwnam($remote_user)
+                                     : getpwuid($access{'uid'});
+       @u || &error($text{'switch_euser'});
+       $( = $u[3]; $) = "$u[3] ".join(" ", $u[3], &other_groups($u[0]));
+       ($>, $<) = ($u[2], $u[2]);
+       umask(oct($access{'umask'}));
+       }
+}
+
+# switch_acl_uid_and_chroot()
+# Combines the switch_acl_uid and go_chroot functions
+sub switch_acl_uid_and_chroot
+{
+if (!$module_info{'usermin'} && $access{'uid'}) {
+       local @u = $access{'uid'} < 0 ? getpwnam($remote_user)
+                                     : getpwuid($access{'uid'});
+       @u || &error($text{'switch_euser'});
+       local @other = &other_groups($u[0]);
+       &go_chroot();
+       $( = $u[3]; $) = "$u[3] ".join(" ", $u[3], @other);
+       ($>, $<) = ($u[2], $u[2]);
+       umask(oct($access{'umask'}));
+       }
+else {
+       &go_chroot();
+       }
+}
+
+# can_access(file)
+# Returns 1 if some file can be edited/deleted
+sub can_access
+{
+return &under_root_dir($_[0], \@allowed_roots) &&
+       ($_[0] eq "/" || !&under_root_dir($_[0], \@denied_roots));
+}
+
+# under_root_dir(file, &roots)
+# Returns 1 if some file is under one of the given roots
+sub under_root_dir
+{
+local @f = grep { $_ ne '' } split(/\//, $_[0]);
+local $r;
+DIR: foreach $r (@{$_[1]}) {
+       return 1 if ($r eq '/' || $_[0] eq '/' || $_[0] eq $r);
+       local @a = grep { $_ ne '' } split(/\//, $r);
+       local $i;
+       for($i=0; $i<@a; $i++) {
+               next DIR if ($a[$i] ne $f[$i]);
+               }
+       return 1;
+       }
+return 0;
+}
+
+# can_list(dir)
+# Returns 1 if some directory can be listed. Parent directories of allowed
+# directories are included as well.
+sub can_list
+{
+return &under_root_dir_or_parent($_[0], \@allowed_roots) &&
+       ($_[0] eq "/" || !&under_root_dir($_[0], \@denied_roots));
+}
+
+# under_root_dir_or_parent(file, &roots)
+# Returns 1 if some file is under one of the given roots, or their parents
+sub under_root_dir_or_parent
+{
+local @f = grep { $_ ne '' } split(/\//, $_[0]);
+DIR: foreach $r (@allowed_roots) {
+       return 1 if ($r eq '/' || $_[0] eq '/' || $_[0] eq $r);
+       local @a = grep { $_ ne '' } split(/\//, $r);
+       local $i;
+       for($i=0; $i<@a && $i<@f; $i++) {
+               next DIR if ($a[$i] ne $f[$i]);
+               }
+       return 1;
+       }
+return 0;
+}
+
+# accessible_subdir(dir)
+# Returns the path to a dir under the given one that we can access
+sub accessible_subdir
+{
+local ($r, @rv);
+foreach $r (@allowed_roots) {
+       if ($r =~ /^(\Q$_[0]\E\/[^\/]+)/) {
+               push(@rv, $1);
+               }
+       }
+return @rv;
+}
+
+sub open_trust_db
+{
+local $trust = "$ENV{'WEBMIN_CONFIG'}/file/trust";
+eval "use SDBM_File";
+dbmopen(%trustdb, $trust, 0700);
+eval { $trustdb{'1111111111'} = 'foo bar' };
+if ($@) {
+       dbmclose(%trustdb);
+       eval "use NDBM_File";
+       dbmopen(%trustdb, $trust, 0700);
+       }
+}
+
+# must_follow(path)
+# For symlinks, returns 1 if a link should be follow, 0 if not
+sub must_follow
+{
+if ($follow == 1) {
+       return 1;
+       }
+elsif ($follow == 0) {
+       return 0;
+       }
+else {
+       local @s = stat($_[0]);
+       local @l = lstat($_[0]);
+       @st = ($s[4] == $l[4] ? @s : @l);
+       return $s[4] == $l[4];
+       }
+}
+
+# extract_archive(path, delete)
+# Called by upload to extract some zip or tar.gz file. Returns undef if something
+# was actually done, an error message otherwise.
+sub extract_archive
+{
+local $out;
+$_[0] =~ /^(\S*\/)/ || return 0;
+local $dir = $1;
+local $qdir = quotemeta($dir);
+local $qpath = quotemeta($_[0]);
+if ($_[0] =~ /\.zip$/i) {
+       # Extract zip file
+       return &text('zip_ecmd', "unzip") if (!&has_command("unzip"));
+       $out = `(cd $qdir; unzip -o $qpath) 2>&1 </dev/null`;
+       if ($?) {
+               return &text('zip_eunzip', $out);
+               }
+       }
+elsif ($_[0] =~ /\.tar$/i) {
+       # Extract un-compressed tar file
+       return &text('zip_ecmd', "tar") if (!&has_command("tar"));
+       $out = `(cd $qdir; tar xf $qpath) 2>&1 </dev/null`;
+       if ($?) {
+               return &text('zip_euntar', $out);
+               }
+       }
+elsif ($_[0] =~ /\.(tar\.gz|tgz|tar\.bz|tbz|tar\.bz2|tbz2)$/i) {
+       # Extract gzip or bzip2-compressed tar file
+       local $zipper = $_[0] =~ /bz(2?)$/i ? "bunzip2"
+                                           : "gunzip";
+       return &text('zip_ecmd', "tar") if (!&has_command("tar"));
+       return &text('zip_ecmd', $zipper) if (!&has_command($zipper));
+       $out = `(cd $qdir; $zipper -c $qpath | tar xf -) 2>&1`;
+       if ($?) {
+               return &text('zip_euntar2', $out);
+               }
+       }
+elsif ($_[0] =~ /\.gz$/i) {
+       # Uncompress gzipped file
+       return &text('zip_ecmd', "gunzip") if (!&has_command("gunzip"));
+       local $final = $_[0];
+       $final =~ s/\.gz$//;
+       local $qfinal = quotemeta($final);
+       $out = `(cd $qdir; gunzip -c $qpath >$qfinal) 2>&1`;
+       if ($?) {
+               return &text('zip_euntar2', $out);
+               }
+       }
+else {
+       return $text{'zip_ename'};
+       }
+if ($_[1]) {
+       unlink($_[0]);
+       }
+return undef;
+}
+
+# post_upload(path, dir, unzip)
+sub post_upload
+{
+local ($path, $dir, $zip) = @_;
+if ($unarchive == 2) {
+       $zip = $path =~ /\.(zip|tgz|tar|tar\.gz)$/i ? 1 : 0;
+       }
+elsif ($unarchive == 0) {
+       $zip = 0;
+       }
+local $refresh = $path;
+local $err;
+if ($zip) {
+       $err = &extract_archive($path, $zip-1);
+       if (!$err) {
+               # Refresh whole dir
+               $refresh = $in{'dir'};
+               }
+       }
+$info = &file_info_line(&unmake_chroot($refresh), $refresh);
+print "<script>\n";
+print "opener.document.FileManager.",
+      "upload_notify(\"$refresh\", \"$info\");\n";
+if ($err) {
+       $err =~ s/\r//g;
+       $err =~ s/\n/\\n/g;
+       print "opener.document.FileManager.","upload_error(\"",&text('zip_err', $err),"\");\n";
+       }
+print "close();\n";
+print "</script>\n";
+}
+
+sub go_chroot
+{
+if ($chroot ne "/" && $chroot ne "") {
+       # First build hash of users and groups, which will not be accessible
+       # after a chroot
+       local (@u, @g);
+       setpwent();
+       while(@u = getpwent()) {
+               $uid_to_user{$u[2]} = $u[0] if (!defined($uid_to_user{$u[2]}));
+               $user_to_uid{$u[0]} = $u[2] if (!defined($user_to_uid{$u[0]}));
+               }
+       endpwent();
+       setgrent();
+       while(@g = getgrent()) {
+               $gid_to_group{$g[2]} = $g[0] if(!defined($gid_to_group{$g[2]}));
+               $group_to_gid{$g[0]} = $g[2] if(!defined($group_to_gid{$g[0]}));
+               }
+       endgrent();
+       chroot($chroot) || die("chroot to $chroot failed");
+       }
+}
+
+# make_chroot(dir)
+# Converts some real directory to the chroot form
+sub make_chroot
+{
+if ($chroot eq "/") {
+       return $_[0];
+       }
+elsif ($_[0] eq $chroot) {
+       return "/";
+       }
+else {
+       local $rv = $_[0];
+       if ($rv =~ /^$chroot\//) {
+               $rv =~ s/^$chroot//;
+               return $rv;
+               }
+       else {
+               return undef;
+               }
+       }
+}
+
+# unmake_chroot(dir)
+# Converts some chroot'd directory to the real form
+sub unmake_chroot
+{
+if ($chroot eq "/") {
+       return $_[0];
+       }
+elsif ($_[0] eq "/") {
+       return $chroot;
+       }
+else {
+       return $chroot.$_[0];
+       }
+}
+
+# print_content_type()
+# Prints the content-type header, with a charset
+sub print_content_type
+{
+if ($userconfig{'nocharset'} || $config{'nocharset'}) {
+       # Never try to use charset
+       print "Content-type: text/plain\n\n";
+       }
+else {
+       $charset = &get_charset();
+       print "Content-type: text/plain; charset=$charset\n\n";
+       }
+}
+
+
+1;
+
diff --git a/file/filesystems.cgi b/file/filesystems.cgi
new file mode 100755 (executable)
index 0000000..cdf8cd0
--- /dev/null
@@ -0,0 +1,64 @@
+#!/usr/local/bin/perl
+# filesystems.cgi
+# List all filesystems and their types
+
+require './file-lib.pl';
+print "Content-type: text/plain\n\n";
+if (!&foreign_check("mount") || !$access{'filesystems'}) {
+       print "0\n";
+       exit;
+       }
+&foreign_require("mount", "mount-lib.pl");
+@mtab = &mount::list_mounted();
+%mtab = map { $_->[0], $_ } @mtab;
+@fstab = &mount::list_mounts();
+%fstab = map { $_->[0], $_ } @fstab;
+@mounts = ( @fstab, grep { !$fstab{$_->[0]} } @mtab );
+
+print "1\n";
+foreach $m (sort { length($a->[0]) <=> length($b->[0]) } @mounts) {
+       next if ($m->[0] !~ /^\//);
+       local @supp = @{$support{$m->[2]}};
+       if (!@supp) {
+               # Work out what this filesystem supports
+               @supp = ( eval $config{$m->[2]."_acl"} ? 1 : 0,
+                         eval $config{$m->[2]."_attr"} ? 1 : 0,
+                         eval $config{$m->[2]."_ext"} ? 1 : 0 );
+               $support{$m->[2]} = \@supp;
+               }
+
+       # Check if the filesystem really does support attrs and ACLs
+       local @supp2 = @supp;
+       if ($mtab{$m->[0]}) {
+               if ($supp2[0]) {
+                       local $out = `$config{'getfacl'} '$m->[0]' 2>/dev/null`;
+                       if ($?) {
+                               $supp2[0] = 0;
+                               }
+                       else {
+                               local $aclcount;
+                               foreach $l (split(/\n/, $out)) {
+                                       $l =~ s/#.*$//;
+                                       $l =~ s/\s+$//;
+                                       $aclcount++ if ($l =~ /\S/);
+                                       }
+                               $supp2[0] = 0 if (!$aclcount);
+                               }
+                       }
+               if ($supp2[1]) {
+                       local $out = `attr -l '$m->[0]' 2>/dev/null`;
+                       if ($?) {
+                               $supp2[1] = 0;
+                               }
+                       }
+               }
+
+       $m->[1] =~ s/\\/\//g;
+       $chrooted = &make_chroot($m->[0]);
+       if ($chrooted) {
+               print join(" ", $chrooted, @$m[1..3], @supp2,
+                               $mtab{$m->[0]} ? 1 : 0,
+                               $fstab{$m->[0]} ? 1 : 0),"\n";
+               }
+       }
+
diff --git a/file/getattrs.cgi b/file/getattrs.cgi
new file mode 100755 (executable)
index 0000000..e926f80
--- /dev/null
@@ -0,0 +1,36 @@
+#!/usr/local/bin/perl
+# getattrs.cgi
+# Returns a list in URL-encode name=value format of attributes on some file
+
+require './file-lib.pl';
+&ReadParse();
+&switch_acl_uid_and_chroot();
+print "Content-type: text/plain\n\n";
+if (!&can_access($in{'file'})) {
+       print $text{'facl_eaccess'},"\n";
+       }
+else {
+       $out = `attr -l '$in{'file'}' 2>&1`;
+       if ($?) {
+               print $out,"\n";
+               }
+       else {
+               foreach $l (split(/[\r\n]+/, $out)) {
+                       if ($l =~ /Attribute\s+"(.*)"/i) {
+                               # Get the valid for this attribute
+                               local $name = $1;
+                               $got = `attr -g '$name' '$in{'file'}' 2>&1`;
+                               if ($? || $got !~ /^(.*)\n([\0-\377]*)\n$/) {
+                                       print $got,"\n";
+                                       exit;
+                                       }
+                               push(@rv, [ $name, $2 ] );
+                               }
+                       }
+               print "\n";
+               foreach $r (@rv) {
+                       print &urlize($r->[0]),"=",&urlize($r->[1]),"\n";
+                       }
+               }
+       }
+
diff --git a/file/getext.cgi b/file/getext.cgi
new file mode 100755 (executable)
index 0000000..ab13940
--- /dev/null
@@ -0,0 +1,25 @@
+#!/usr/local/bin/perl
+# getext.cgi
+# Returns a string of EXT attributes for some file
+
+require './file-lib.pl';
+&ReadParse();
+&switch_acl_uid_and_chroot();
+print "Content-type: text/plain\n\n";
+if (!&can_access($in{'file'})) {
+       print $text{'facl_eaccess'},"\n";
+       }
+else {
+       $out = `lsattr -d '$in{'file'}' 2>&1`;
+       $out =~ s/^lsattr.*\n//;
+       if ($? || $out !~ /^(\S+)\s/) {
+               print $out,"\n";
+               }
+       else {
+               print "\n";
+               @a = split(//, $1);
+               print join("", grep { $_ ne '-' } @a),"\n";
+               }
+       }
+
+
diff --git a/file/getext.cgi.bak b/file/getext.cgi.bak
new file mode 100755 (executable)
index 0000000..b78b9f6
--- /dev/null
@@ -0,0 +1,24 @@
+#!/usr/local/bin/perl
+# getext.cgi
+# Returns a string of EXT attributes for some file
+
+require './file-lib.pl';
+&ReadParse();
+&switch_acl_uid();
+print "Content-type: text/plain\n\n";
+if (!&can_access($in{'file'})) {
+       print $text{'facl_eaccess'},"\n";
+       }
+else {
+       $out = `lsattr -d '$in{'file'}' 2>&1`;
+       if ($? || $out !~ /^(\S+)\s/) {
+               print $out,"\n";
+               }
+       else {
+               print "\n";
+               @a = split(//, $1);
+               print join("", grep { $_ ne '-' } @a),"\n";
+               }
+       }
+
+
diff --git a/file/getfacl.cgi b/file/getfacl.cgi
new file mode 100755 (executable)
index 0000000..4adf7e5
--- /dev/null
@@ -0,0 +1,45 @@
+#!/usr/local/bin/perl
+# getfacl.cgi
+# Gets the ACLs for some file
+
+require './file-lib.pl';
+&ReadParse();
+&switch_acl_uid_and_chroot();
+print "Content-type: text/plain\n\n";
+if (!&can_access($in{'file'})) {
+       print $text{'facl_eaccess'},"\n";
+       }
+else {
+       $getfacl = $config{'getfacl'};
+       if ($getfacl =~ /^\.\//) {
+               $getfacl =~ s/^\./$module_root_directory/;
+               }
+       chdir("/");
+       if ($in{'file'} eq '/') {
+               $in{'file'} = '.';
+               }
+       else {
+               $in{'file'} =~ s/^\///;
+               }
+       $out = `$getfacl '$in{'file'}' 2>&1`;
+       if ($?) {
+               print $out,"\n";
+               }
+       else {
+               foreach $l (split(/\n/, $out)) {
+                       $l =~ s/#.*$//;
+                       $l =~ s/\s+$//;
+                       push(@rv, $l) if ($l =~ /\S/);
+                       }
+               if (!@rv) {
+                       print "Filesystem does not support ACLs\n";
+                       }
+               else {
+                       print "\n";
+                       foreach $l (@rv) {
+                               print $l,"\n";
+                               }
+                       }
+               }
+       }
+
diff --git a/file/images/.xvpics/preview.gif b/file/images/.xvpics/preview.gif
new file mode 100644 (file)
index 0000000..ec6e911
Binary files /dev/null and b/file/images/.xvpics/preview.gif differ
diff --git a/file/images/Thumbs.db b/file/images/Thumbs.db
new file mode 100755 (executable)
index 0000000..1bfc2c2
Binary files /dev/null and b/file/images/Thumbs.db differ
diff --git a/file/images/acl.gif b/file/images/acl.gif
new file mode 100644 (file)
index 0000000..a224066
Binary files /dev/null and b/file/images/acl.gif differ
diff --git a/file/images/add.gif b/file/images/add.gif
new file mode 100644 (file)
index 0000000..0b79476
Binary files /dev/null and b/file/images/add.gif differ
diff --git a/file/images/all.gif b/file/images/all.gif
new file mode 100644 (file)
index 0000000..0199aa8
Binary files /dev/null and b/file/images/all.gif differ
diff --git a/file/images/attr.gif b/file/images/attr.gif
new file mode 100644 (file)
index 0000000..858a2b4
Binary files /dev/null and b/file/images/attr.gif differ
diff --git a/file/images/binary.gif b/file/images/binary.gif
new file mode 100644 (file)
index 0000000..016a6e9
Binary files /dev/null and b/file/images/binary.gif differ
diff --git a/file/images/cancel.gif b/file/images/cancel.gif
new file mode 100644 (file)
index 0000000..f53c1e2
Binary files /dev/null and b/file/images/cancel.gif differ
diff --git a/file/images/config.gif b/file/images/config.gif
new file mode 100644 (file)
index 0000000..ff95e3b
Binary files /dev/null and b/file/images/config.gif differ
diff --git a/file/images/copy.gif b/file/images/copy.gif
new file mode 100644 (file)
index 0000000..6cd331a
Binary files /dev/null and b/file/images/copy.gif differ
diff --git a/file/images/cut.gif b/file/images/cut.gif
new file mode 100644 (file)
index 0000000..00484ed
Binary files /dev/null and b/file/images/cut.gif differ
diff --git a/file/images/delete.gif b/file/images/delete.gif
new file mode 100644 (file)
index 0000000..18304a8
Binary files /dev/null and b/file/images/delete.gif differ
diff --git a/file/images/device.gif b/file/images/device.gif
new file mode 100644 (file)
index 0000000..aa30add
Binary files /dev/null and b/file/images/device.gif differ
diff --git a/file/images/dir.gif b/file/images/dir.gif
new file mode 100644 (file)
index 0000000..d9ef302
Binary files /dev/null and b/file/images/dir.gif differ
diff --git a/file/images/down.gif b/file/images/down.gif
new file mode 100644 (file)
index 0000000..bab413d
Binary files /dev/null and b/file/images/down.gif differ
diff --git a/file/images/edit.gif b/file/images/edit.gif
new file mode 100644 (file)
index 0000000..54890d3
Binary files /dev/null and b/file/images/edit.gif differ
diff --git a/file/images/ext.gif b/file/images/ext.gif
new file mode 100644 (file)
index 0000000..32a9edd
Binary files /dev/null and b/file/images/ext.gif differ
diff --git a/file/images/extract.gif b/file/images/extract.gif
new file mode 100644 (file)
index 0000000..6cbcfc5
Binary files /dev/null and b/file/images/extract.gif differ
diff --git a/file/images/file.gif b/file/images/file.gif
new file mode 100644 (file)
index 0000000..f865d6f
Binary files /dev/null and b/file/images/file.gif differ
diff --git a/file/images/find.gif b/file/images/find.gif
new file mode 100644 (file)
index 0000000..5471239
Binary files /dev/null and b/file/images/find.gif differ
diff --git a/file/images/goto.gif b/file/images/goto.gif
new file mode 100644 (file)
index 0000000..99535c8
Binary files /dev/null and b/file/images/goto.gif differ
diff --git a/file/images/html.gif b/file/images/html.gif
new file mode 100755 (executable)
index 0000000..831835e
Binary files /dev/null and b/file/images/html.gif differ
diff --git a/file/images/icon.gif b/file/images/icon.gif
new file mode 100644 (file)
index 0000000..4b51c79
Binary files /dev/null and b/file/images/icon.gif differ
diff --git a/file/images/image.gif b/file/images/image.gif
new file mode 100644 (file)
index 0000000..6b90209
Binary files /dev/null and b/file/images/image.gif differ
diff --git a/file/images/makelink.gif b/file/images/makelink.gif
new file mode 100644 (file)
index 0000000..feede93
Binary files /dev/null and b/file/images/makelink.gif differ
diff --git a/file/images/mdir.gif b/file/images/mdir.gif
new file mode 100644 (file)
index 0000000..bfe0d51
Binary files /dev/null and b/file/images/mdir.gif differ
diff --git a/file/images/mkdir.gif b/file/images/mkdir.gif
new file mode 100644 (file)
index 0000000..f8c1594
Binary files /dev/null and b/file/images/mkdir.gif differ
diff --git a/file/images/mount.gif b/file/images/mount.gif
new file mode 100644 (file)
index 0000000..eb824e7
Binary files /dev/null and b/file/images/mount.gif differ
diff --git a/file/images/new.gif b/file/images/new.gif
new file mode 100644 (file)
index 0000000..94c4b2f
Binary files /dev/null and b/file/images/new.gif differ
diff --git a/file/images/open.gif b/file/images/open.gif
new file mode 100644 (file)
index 0000000..06b68f6
Binary files /dev/null and b/file/images/open.gif differ
diff --git a/file/images/paste.gif b/file/images/paste.gif
new file mode 100644 (file)
index 0000000..62c79dc
Binary files /dev/null and b/file/images/paste.gif differ
diff --git a/file/images/pipe.gif b/file/images/pipe.gif
new file mode 100644 (file)
index 0000000..5cff0d8
Binary files /dev/null and b/file/images/pipe.gif differ
diff --git a/file/images/preview.gif b/file/images/preview.gif
new file mode 100644 (file)
index 0000000..d7c2cf4
Binary files /dev/null and b/file/images/preview.gif differ
diff --git a/file/images/props.gif b/file/images/props.gif
new file mode 100644 (file)
index 0000000..c5586a5
Binary files /dev/null and b/file/images/props.gif differ
diff --git a/file/images/refresh.gif b/file/images/refresh.gif
new file mode 100644 (file)
index 0000000..b96b6df
Binary files /dev/null and b/file/images/refresh.gif differ
diff --git a/file/images/rename.gif b/file/images/rename.gif
new file mode 100644 (file)
index 0000000..5a53743
Binary files /dev/null and b/file/images/rename.gif differ
diff --git a/file/images/replace.gif b/file/images/replace.gif
new file mode 100644 (file)
index 0000000..0f33d5b
Binary files /dev/null and b/file/images/replace.gif differ
diff --git a/file/images/ret.gif b/file/images/ret.gif
new file mode 100644 (file)
index 0000000..d7a3d4c
Binary files /dev/null and b/file/images/ret.gif differ
diff --git a/file/images/run.gif b/file/images/run.gif
new file mode 100644 (file)
index 0000000..1f14249
Binary files /dev/null and b/file/images/run.gif differ
diff --git a/file/images/save.gif b/file/images/save.gif
new file mode 100644 (file)
index 0000000..eda222b
Binary files /dev/null and b/file/images/save.gif differ
diff --git a/file/images/sdir.gif b/file/images/sdir.gif
new file mode 100644 (file)
index 0000000..ee9e5d7
Binary files /dev/null and b/file/images/sdir.gif differ
diff --git a/file/images/search.gif b/file/images/search.gif
new file mode 100644 (file)
index 0000000..378d87e
Binary files /dev/null and b/file/images/search.gif differ
diff --git a/file/images/share.gif b/file/images/share.gif
new file mode 100644 (file)
index 0000000..c10f927
Binary files /dev/null and b/file/images/share.gif differ
diff --git a/file/images/smallicon.gif b/file/images/smallicon.gif
new file mode 100644 (file)
index 0000000..d7c9b61
Binary files /dev/null and b/file/images/smallicon.gif differ
diff --git a/file/images/smdir.gif b/file/images/smdir.gif
new file mode 100644 (file)
index 0000000..20d10d1
Binary files /dev/null and b/file/images/smdir.gif differ
diff --git a/file/images/sub.gif b/file/images/sub.gif
new file mode 100644 (file)
index 0000000..ebb8987
Binary files /dev/null and b/file/images/sub.gif differ
diff --git a/file/images/sudir.gif b/file/images/sudir.gif
new file mode 100644 (file)
index 0000000..5f164a2
Binary files /dev/null and b/file/images/sudir.gif differ
diff --git a/file/images/symlink.gif b/file/images/symlink.gif
new file mode 100644 (file)
index 0000000..4b322ea
Binary files /dev/null and b/file/images/symlink.gif differ
diff --git a/file/images/text.gif b/file/images/text.gif
new file mode 100644 (file)
index 0000000..c878641
Binary files /dev/null and b/file/images/text.gif differ
diff --git a/file/images/udir.gif b/file/images/udir.gif
new file mode 100644 (file)
index 0000000..d216740
Binary files /dev/null and b/file/images/udir.gif differ
diff --git a/file/images/unknown.gif b/file/images/unknown.gif
new file mode 100644 (file)
index 0000000..d3664bf
Binary files /dev/null and b/file/images/unknown.gif differ
diff --git a/file/images/upload.gif b/file/images/upload.gif
new file mode 100644 (file)
index 0000000..d97a910
Binary files /dev/null and b/file/images/upload.gif differ
diff --git a/file/images/view.gif b/file/images/view.gif
new file mode 100644 (file)
index 0000000..6f3bcb1
Binary files /dev/null and b/file/images/view.gif differ
diff --git a/file/index.cgi b/file/index.cgi
new file mode 100755 (executable)
index 0000000..f426cb6
--- /dev/null
@@ -0,0 +1,126 @@
+#!/usr/local/bin/perl
+# index.cgi
+# Output HTML for the file manager applet
+
+require './file-lib.pl';
+&ReadParse();
+$theme_no_table = 1;
+if ($access{'uid'} < 0 && !defined(getpwnam($remote_user))) {
+       &error(&text('index_eremote', $remote_user));
+       }
+
+# Display header, depending on how many modules the user has
+&read_acl(undef, \%acl);
+$mc = @{$acl{$base_remote_user}} == 1;
+$nolo = $ENV{'ANONYMOUS_USER'} ||
+      $ENV{'SSL_USER'} || $ENV{'LOCAL_USER'} ||
+      $ENV{'HTTP_USER_AGENT'} =~ /webmin/i;
+if ($gconfig{'gotoone'} && $mc == 1 && !$nolo) {
+       &header($text{'index_title'}, "", undef, 0, 1);
+       $w = 100;
+       $h = 80;
+       }
+else {
+       &header($text{'index_title'});
+       $w = 100;
+       $h = 100;
+       if (!$tconfig{'inframe'}) {
+               $return = "<param name=return value=\"$gconfig{'webprefix'}/?cat=$module_info{'category'}\">";
+               $returnhtml = &text('index_index',
+                                   "$gconfig{'webprefix'}/")."<p>";
+               }
+       }
+
+if ($gconfig{'referers_none'}) {
+       # Because java applet HTTP requests don't always include a referer:
+       # header, we need to use a DBM of trust keys to identify trusted applets
+       if (defined(&seed_random)) { &seed_random(); }
+       else { srand(time() ^ $$); }
+       $trust = int(rand(1000000000));
+       local $now = time();
+       &open_trust_db();
+       foreach $k (keys %trustdb) {
+               if ($now - $trustdb{$k} > 30*24*60*60) {
+                       delete($trustdb{$k});
+                       }
+               }
+       $trustdb{$trust} = $now;
+       dbmclose(%trustdb);
+       }
+
+$sharing = $access{'uid'} ? 0 : 1;
+$mounting = !$access{'uid'} && &foreign_check("mount") ? 1 : 0;
+if ($in{'open'}) {
+       $open = "<param name=open value=\"$in{'open'}\">";
+       }
+if ($session_id) {
+       $session = "<param name=session value=\"sid=$session_id\">";
+       }
+if (!$access{'noconfig'}) {
+       $config = "<param name=config value=\"$gconfig{'webprefix'}/config.cgi?$module_name\">";
+       }
+$iconsize = int($config{'iconsize'});
+$root = join(" ", @allowed_roots);
+$noroot = join(" ", @denied_roots);
+
+foreach $d (@disallowed_buttons) {
+       $disallowed .= "<param name=no_$d value=1>\n";
+       }
+
+# Create parameters for custom colours
+foreach $k (keys %tconfig) {
+       if ($k =~ /^applet_(.*)/) {
+               $colours .= "<param name=$k value=\"$tconfig{$k}\">\n";
+               }
+       }
+
+# Extract classes from jar, if we can
+if ($config{'extract'} &&
+    &has_command("unzip") && !-r "$module_root_directory/FileManager.class") {
+       system("unzip file.jar >/dev/null 2>&1");
+       }
+
+print <<EOF;
+<script>
+function upload(dir)
+{
+open("upform.cgi?dir="+escape(dir)+"&trust=$trust", "upload", "toolbar=no,menubar=no,scrollbar=no,width=450,height=200");
+}
+function htmledit(file, dir)
+{
+open("edit_html.cgi?file="+escape(file)+"&dir="+escape(dir)+"&trust=$trust", "html", "toolbar=no,menubar=no,scrollbar=no,width=800,height=600");
+}
+</script>
+
+<applet code=FileManager name=FileManager archive=file.jar width=$w% height=$h% MAYSCRIPT>
+<param name=root value="$root">
+<param name=noroot value="$noroot">
+<param name=follow value="$follow">
+<param name=ro value="$access{'ro'}">
+<param name=sharing value="$sharing">
+<param name=mounting value="$mounting">
+<param name=trust value="$trust">
+<param name=goto value="$access{'goto'}">
+<param name=iconsize value="$iconsize">
+<param name=doarchive value="$archive">
+<param name=unarchive value="$unarchive">
+<param name=dostounix value="$dostounix">
+<param name=fixed value="$config{'fixed'}">
+<param name=small_fixed value="$config{'small_fixed'}">
+<param name=canperms value="$canperms">
+<param name=canusers value="$canusers">
+<param name=contents value="$contents">
+<param name=force_text value="$config{'force_text'}">
+$config
+$session
+$open
+$return
+$disallowed
+$colours
+$text{'index_nojava'} <p>
+$returnhtml
+</applet>
+EOF
+&footer();
+
+
diff --git a/file/index.cgi.bak b/file/index.cgi.bak
new file mode 100755 (executable)
index 0000000..012d8f7
--- /dev/null
@@ -0,0 +1,79 @@
+#!/usr/local/bin/perl
+# index.cgi
+# Output HTML for the file manager applet
+
+require './file-lib.pl';
+&ReadParse();
+$theme_no_table = 1;
+if ($access{'uid'} < 0 && !defined(getpwnam($remote_user))) {
+       &error(&text('index_eremote', $remote_user));
+       }
+
+# Display header, depending on how many modules the user has
+&read_acl(undef, \%acl);
+$mc = @{$acl{$base_remote_user}} == 1;
+$nolo = $ENV{'ANONYMOUS_USER'} ||
+      $ENV{'SSL_USER'} || $ENV{'LOCAL_USER'} ||
+      $ENV{'HTTP_USER_AGENT'} =~ /webmin/i;
+if ($gconfig{'gotoone'} && $mc == 1 && !$nolo) {
+       &header($text{'index_title'}, "", undef, 0, 1);
+       $w = 100;
+       $h = 80;
+       }
+else {
+       &header($text{'index_title'});
+       $w = 100;
+       $h = 100;
+       }
+
+if ($gconfig{'referers_none'}) {
+       # Because java applet HTTP requests don't always include a referer:
+       # header, we need to use a DBM of trust keys to identify trusted applets
+       if (defined(&seed_random)) { &seed_random(); }
+       else { srand(time() ^ $$); }
+       $trust = int(rand(1000000000));
+       local $now = time();
+       &open_trust_db();
+       foreach $k (keys %trustdb) {
+               if ($now - $trustdb{$k} > 30*24*60*60) {
+                       delete($trustdb{$k});
+                       }
+               }
+       $trustdb{$trust} = $now;
+       dbmclose(%trustdb);
+       }
+
+
+$sharing = $access{'uid'} ? 0 : 1;
+&read_acl(undef, \%acl);
+$mc = @{$acl{$base_remote_user}};
+if (!$gconfig{'gotoone'} || $mc > 1) {
+       %minfo = &get_module_info();
+       $return = "<param name=return value=\"$gconfig{'webprefix'}/?cat=$minfo{'category'}\">";
+       }
+if ($in{'open'}) {
+       $open = "<param name=open value=\"$in{'open'}\">\n";
+       }
+print <<EOF;
+<script>
+function upload(dir)
+{
+open("upform.cgi?dir="+dir+"&trust=$trust", "upload", "toolbar=no,menubar=no,scrollbar=no,width=450,height=200");
+}
+</script>
+
+<applet code=FileManager name=FileManager width=$w% height=$h% MAYSCRIPT>
+<param name=root value="$access{'root'}">
+<param name=follow value="$access{'follow'}">
+<param name=ro value="$access{'ro'}">
+<param name=sharing value="$sharing">
+<param name=trust value="$trust">
+<param name=goto value="$access{'goto'}">
+$open
+$return
+$text{'index_nojava'} <p>
+</applet>
+EOF
+&footer();
+
+
diff --git a/file/irix-getfacl.pl b/file/irix-getfacl.pl
new file mode 100755 (executable)
index 0000000..5882336
--- /dev/null
@@ -0,0 +1,53 @@
+#!/usr/local/bin/perl
+# irix-getfacl.pl
+# Wrapper for the ls -D command
+
+$esc = quotemeta($ARGV[0]);
+$out = `ls -dDL $esc 2>&1`;
+if ($?) {
+       print STDERR $out;
+       exit 1;
+       }
+if ($out !~ /\[([^\]]*)\]/) {
+       print STDERR "Failed to parse ls -D output : $out\n";
+       exit 1;
+       }
+if ($1) {
+       # Convert to normal ACL form
+       ($acl, $dacl) = split(/\//, $1);
+       foreach (split(/,/, $acl)) {
+               s/^u:/user:/;
+               s/^g:/group:/;
+               s/^o:/other:/;
+               s/^m:/mask:/;
+               print $_,"\n";
+               }
+       foreach (split(/,/, $dacl)) {
+               s/^u:/user:/;
+               s/^g:/group:/;
+               s/^o:/other:/;
+               s/^m:/mask:/;
+               print "default:",$_,"\n";
+               }
+       }
+else {
+       # Make up ACL from perms
+       local @st = stat($ARGV[0]);
+       local $other = $st[2] & 7;
+       local $group = ($st[2] >> 3) & 7;
+       local $user = ($st[2] >> 6) & 7;
+       print "user::",&octal_to_perms($user),"\n";
+       print "group::",&octal_to_perms($group),"\n";
+       print "other::",&octal_to_perms($other),"\n";
+       print "mask::",&octal_to_perms($user | $group),"\n";
+       }
+
+sub octal_to_perms
+{
+local $rv;
+$rv .= ($_[0] & 4 ? "r" : "-");
+$rv .= ($_[0] & 2 ? "w" : "-");
+$rv .= ($_[0] & 1 ? "x" : "-");
+return $rv;
+}
+
diff --git a/file/irix-setfacl.pl b/file/irix-setfacl.pl
new file mode 100755 (executable)
index 0000000..43d92be
--- /dev/null
@@ -0,0 +1,40 @@
+#!/usr/local/bin/perl
+# irix-setfacl.pl
+# Wrapper for the chacl command
+
+while(<STDIN>) {
+       s/\r|\n//g;
+       $default = ($_ =~ s/^default://);
+       s/^(other|mask):([rwx\-]{3})$/\1::\2/g;
+       if ($default) {
+               push(@dacl, $_);
+               }
+       else {
+               push(@acl, $_);
+               }
+       }
+$esc = quotemeta($ARGV[0]);
+$acl = join(",", @acl);
+$dacl = join(",", @dacl);
+if ($acl && $dacl) {
+       $out = `chacl -b $acl $dacl $esc 2>&1`;
+       }
+elsif ($acl) {
+       if (-d $ARGV[0]) {
+               $out = `chacl $acl $esc 2>&1 && chacl -D $esc 2>&1`;
+               }
+       else {
+               $out = `chacl $acl $esc 2>&1`;
+               }
+       }
+elsif ($dacl) {
+       $out = `chacl -d $dacl $esc 2>&1 && chacl -R $esc 2>&1`;
+       }
+else {
+       $out = `chacl -B $esc 2>&1`;
+       }
+if ($?) {
+       print STDERR $out;
+       exit 1;
+       }
+
diff --git a/file/lang.cgi b/file/lang.cgi
new file mode 100755 (executable)
index 0000000..1327774
--- /dev/null
@@ -0,0 +1,21 @@
+#!/usr/local/bin/perl
+# lang.cgi
+# Return language translation values
+
+require './file-lib.pl';
+
+print "Content-type: text/plain\n\n";
+
+if (&get_charset() eq $default_charset) {
+       # Convert any HTML entities to their 'real' single-byte forms,
+       # as we are using the iso-8859-1 character set.
+       foreach $k (keys %text) {
+               print $k,"=",&entities_to_ascii($text{$k}),"\n";
+               }
+       }
+else {
+       # Don't do HTML entity conversion for other character sets
+       foreach $k (keys %text) {
+               print $k,"=",$text{$k},"\n";
+               }
+       }
diff --git a/file/lang/bg b/file/lang/bg
new file mode 100644 (file)
index 0000000..243e87d
--- /dev/null
@@ -0,0 +1,322 @@
+index_title=File Manager
+index_nojava=Òîçè ìîäóë èçïîëçâà java, íî Âàøèÿò áðàóçúð íÿìà java ïîääðúæêà
+index_eremote=Íÿìà Unix ïîòðåáèòåë êîéòî äà ñúâïàäà ñ Webmin ïîòðåáèòåëñêîòî èìå $1.
+switch_euser=Unix ïîòðåáèòåëÿ íå ñúùåñòâóâà!
+
+top_ret=Index
+top_down=Çàïèñ
+top_edit=Ðåäàêòèðàíå
+top_refresh=Îïðåñíè
+top_info=Èíôîðìàöèÿ
+top_eacl=ACL
+top_attr=Àòòð.
+top_ext=EXT
+top_delete=Èçòðèé
+top_new=Íîâ
+top_upload=Upload
+top_rename=Ïðåèìåíóâàé
+top_copy=Êîïèðàé
+top_cut=Îòðåæè
+top_paste=Ïîñòàâè
+top_share=Sharing
+top_mount=Mount
+top_search=Òúðñè
+top_config=Êîíôèãóðàöèÿ
+
+right_name=Èìå
+right_size=Ðàçìåð
+right_user=Ïîòðåáèòåë
+right_group=Ãðóïà
+right_date=Äàòà
+
+edit_enormal=Ñàìî îáèêíîâåíèòå ôàéëîâå ìîãàò äà áúäà ðåäàêòèðàíè
+edit_title=Ðåäàêòèðàíå íà $1
+edit_title2=Ñúçäàâàíå íà ôàéë
+edit_filename=Ôàéë:
+edit_goto=Âúðâè
+edit_find=Òúðñè
+edit_gotoline=Âúðâè íà ðåä
+edit_replace=Çàìåíè
+edit_all=Çàìåíè âñè÷êè
+edit_searchfor=Òúðñè çà
+edit_replaceby=Çàìåíè ñúñ
+edit_eover=$1 íå ìîæå äà áúäå ïðåçàïèñàí
+edit_esave=Ãðåøêà ïðè çàïèñ íà ôàéë : $1
+edit_eaccess=Íÿìàòå ïðàâà äà çàïèøåòå '$1'
+edit_notfound=Òåêñòúò $1 íå å íàìåðåí
+edit_saveclose=Çàïèøè & Çàòâîðè
+
+info_file=Ôàéë
+info_path=Ïúò:
+info_type=Òèï:
+info_size=Ðàçìåð:
+info_mod=Ìîäèôèöèðàí íà:
+info_link=Âðúçêà êúì:
+info_perms=Ïðàâà
+info_user=Ïîòðåáèòåëñêè:
+info_group=Ãðóïîâè:
+info_other=Âñè÷êè äðóãè:
+info_octal=Îñìè÷íè:
+info_sticky=Sticky:
+info_sticky2=Ñàìî ñîáñâåíèöèòå ìîãàò äà èçòðèâàò ôàéëîâå
+info_own=Ñîáñòâåíèê
+info_setuid=Setuid:
+info_setuid2=Èçïúëíÿâàé êàòî ïîòðåáèòåë
+info_setgid=Setgid:
+info_setgid2=Ôàéëîâåòå íàñëåäÿâàò ãðóïà
+info_setgid3=Èçïúëíÿâàé êàòî ãðóïà
+info_apply=Ïðèëîæè ïðîìåíèòå íà
+info_apply1=Ñàìî òàçè äèðåêòîðèÿ
+info_apply2=Òàçè äèðåêòîðèÿ è ôàéëîâåòå â íåÿ
+info_apply3=Òàçè äèðåêòîðèÿ è âñè÷êè íåéíè ïîääèðåêòîðèè
+info_efailed=Ãðåøêà ïðè îáíîâÿâàíå íà $1 : $2
+info_read=×åòåíå
+info_write=Çàïèñ
+info_list=Ïðåãëåä
+info_exec=Èçïúëíÿâàíå
+
+eacl_eacls=Ãðåøêà ïðè ÷åòåíå íà ACL : $1
+eacl_acltype=ACL Òèï
+eacl_aclname=Ïðèëîæè íà
+eacl_aclperms=Ïðàâà
+eacl_add=Äîáàâè ACL îò òèïà :
+eacl_remove=Ïðåìàõíè ACL
+eacl_efs=Ôàéëîâàòà ñèñòåìà $1 íå ïîääúðæà ACL
+eacl_create=Ñúçäàé ACL
+eacl_edit=Ðåäàêòèðàé ACL
+eacl_user=Ñîáñòâåíèê íà ôàéë $1
+eacl_group=Ãðóïà çà ôàéë $1
+eacl_eowner=Ëèïñâàù ïîòðåáèòåë èëè ãðóïà çà ïðèëàãàíå
+eacl_efailed=Ãðåøêà ïðè çàäàâàíå íà ACL çà $1 : $2
+eacl_emask=Ìîæå äà ñúùåñòâóâà ñàìî åäèí mask ACL 
+eacl_edefmask=Ìîæå äà ñúùåñòâóâà íàé-ìíîãî åäíî ïîäðàçáèðàùî ñå mask ACL
+eacl_title=ACL çà $1
+eacl_owner=Ñîáñòâåíèê íà ôàéë
+eacl_edefaults=Àêî íà ôàéë å çàäåäàí ïîäðàçáèðàù ñå ACL, òîé òðÿáâà äà èìà ïîäðàçáèðàù ñå ïîòðåáèòåëñêè, ãðóïîâ è äðóã ACL.
+
+acltype_user=Ïîòðåáèòåë
+acltype_group=Ãðóïà
+acltype_other=Äðóãè
+acltype_mask=Ìàñêà
+acltype_default_user=Ïîäðàçáèðàù ñå ïîòðåáèòåë
+acltype_default_group=Ïîäðàçáèðàùà ñå ãðóïà
+acltype_default_other=Ïîäðàçáèðàùè ñå äðóãè
+acltype_default_mask=Ïîäðàçáèðàùà ñå ìàñêà
+
+delete_mtitle=Èçòðèé íÿêîëêî ôàéëà
+delete_dtitle=Èçòðîé äèðåêòîðèÿ
+delete_ftitle=Èçòðèé ôàéë
+delete_ddesc=Ñèãóðíè ëè ñòå, ÷å èñêàòå äà èçòðèåòå äèðåêòîðèÿ $1 è âñè÷êî â íåÿ?
+delete_fdesc=Ñèãóðíè ëè ñòå, ÷å èñêàòå äà èçòðèåòå ôàéë $1 ?
+delete_mdesc=Ñèãóðíè ëè ñòå, ÷å èñêàòå äà èçòðèå òåçè ôàéëîâå è äèðåêòîðèè? :
+delete_efailed=Ãðåøêà ïðè èçòðèâàíå íà $1 : $2
+
+mkdir_title=Ñúçäàâàíå íà äèðåêòîðèÿ
+mkdir_dir=Íîâà äèðåêòîðèÿ:
+mkdir_eexists=$1 âå÷å ñúùåñòâóâà
+mkdir_efailed=Ãðåøêà ïðè ñúçäàâàíå íà äèðåêòîðèÿ : $1
+mkdir_eaccess=Íÿìàòå ïðàâà çà ñúçäàâàíå íà '$1'
+
+link_title=Ñúçäàâàíå íà ïðåïðàòêà
+link_from=Ïðåïðàòêà çà:
+link_to=Ïðåïðàòêà êúì:
+link_eexists=$1 âå÷å ñúùåñòâóâà
+link_efailed=Ãðåøêà : $1
+link_efrom=Íÿìàòå ïðàâà çà ñúçäàâàòå ïðåïðàòêà íà '$1'
+link_efollow=Íÿìàòå ïðàâà çà ñúçäàâàíå íà ïðåïðàòêè
+
+rename_title=Ïðåèìåíóâàíå íà $1
+rename_old=Ñòàðî èìà:
+rename_new=Íîâî èìå:
+rename_ok=Ïðåèìåíóâàé
+rename_eexists=Ôàéë ñ èìå $1 âå÷å ñúùåñòâóâà
+rename_efailed=Ãðåøêà ïðè ïðåèìåíóâàíå : $1
+rename_eold=Íÿìàòå ïðàâà çà ïðåèìåíóâàíå íà '$1'
+rename_enew=Íåâúçìîæíî ïðåèìåíóâàíå â '$1'
+
+file_type0=Äèðåêòîðèÿ
+file_type1=Òåêñòîâ ôàéë
+file_type2=Ãðàôè÷åí ôàéë
+file_type3=Áèíàðåí ôàéë
+file_type4=Ôàéë
+file_type5=Ïðåïðàòêà
+file_type6=Óñòðîéñòâî
+file_type7=Pipe
+
+view_enormal=Ñàìî îáèêíîâåííè ôàéëîâå ìîãàò äà áúäàò ðàçãëåæäàíè
+view_enormal2=Ñàìî îáèêíîâåííè ôàéëîâå ìîãàò äà áúäàò download-âàíè
+view_eaccess=Íÿìàòå ïðàâà çà äîñòúï äî $1
+view_eopen=Ãðåøêà ïðè îòâàðÿíå íà $1 : $2
+
+paste_ecopy=Òðÿáâà äà ñòå êîïèðàëè èëè îòðÿçàëè íåùî ïðåäè äà èçïúëÿâàòå "ïîñòàâè"
+paste_egone=Êîïèðàíèÿò ôàéë $1 âå÷å íå ñúùåñòâóâà
+paste_eover=$1 íå ìîæå äà áúäå ïðåçàïèñàí
+paste_eself=Íå ìîæåòå äà èçïúëíèòå "ïîñòàâè" âúðõó ñúùèÿò ôàéë
+paste_emfailed=Ãðåøêà ïðè ïðåìåñòâàíå íà : $1
+paste_ecfailed=Ãðåøêà ïðè êîïèðàíå íà : $1
+
+over_title=Ôàéëúò ñúùåñòâóâà âå÷å
+over_msg=Ôàéëúò $1 ñúùåñòâóâà âå÷å. Èçïîëçâàéòå ïîëåòî çà äà âúâåäåòå íîâî èìå íà ôàéëà.
+over_new=Íîâî èìå:
+over_ok=Îê
+
+upload_efailed=Ãðåøêà ïðè îòâàðÿíå íà upload : $1
+upload_title=Upload íà ôàéë
+upload_file=Ôàéë çà upload
+upload_dir=Upload â äèðåêòîðèÿ
+upload_ok=Upload
+upload_conv=Êîâåðòèðàé â DOS ôîðìàò íîâèòå ðåäîâå?
+upload_efile=Íå å ïîñî÷åí ôàéë çà upload.
+upload_edir=Upload äèðåêòîðèÿòà íå ñúùåñòâóâà.
+upload_eperm=Íÿìàòå ïðàâà çà ñúçäàâàíå íà $1
+upload_ewrite=Ãðåøêà ïðè çàïèñ íà $1 : $2.
+upload_already=Ôàéëúò $1 âå÷å ñúùåñòâóâà. Ñèãóðíè ëè ñòå, ÷å èñêàòå äà ãî ïðåçàïèøåòå?
+
+find_eaccess=Íÿìàòå ïðàâà çà äîñòúï äî $1
+find_eexist=$1 íå ñúùåñòâóâà â $2
+find_edir=$1 íå å äèðåêòîðèÿ â $2
+
+cancel=Îòêàæè
+close=Çàòâîðè
+
+chmod_eaccess=Íÿìàòå ïðàâà çà äîñòúï äî '$1'
+chmod_euser=$1 : íåñúùåñòâóâàù ïîòðåáèòåë
+chmod_egroup=$1 : íåñúùåñòâóâàùà ãðóïà
+chmod_elink=Ãðåøêà ïðè ñúçäàâàíå íà ïðåïðàòêà : $1
+chmod_echown=Ãðåøêà ïðè ïðîìÿíà íà ñîáñòâåíèêà : $1
+chmod_echmod=Ãðåøêà ïðè ïðîìÿíà íà ïðàâàòà : $1
+chmod_efollow=Íÿìàòå ïðàâà çà ïðîìÿíà íà ïðåïðàòêè
+
+copy_efrom=Íÿìàòå ïðàâà äà êîïèðàòå îò '$1'
+copy_eto=Íÿìàòå ïðàâà äà êîïèðàòå íà '$1'
+copy_elink=ãðåøêà ïðè ñúçäàâàíå íà ïðåïðàòêà : $1
+
+delete_eaccess=Íÿìàòå ïðàâà äà èçòðèåòå '$1'
+
+list_eaccess=Íÿìàòå ïðàâà çà äîñòúï äî òàçè äèðåêòîðèÿ
+list_edir=Ãðåøêà ïðè ïîêàçâàíå íå $1 : $2
+
+move_eto=Íÿìàòå ïðàâà äà ïðåìåñâàòå â '$1'
+move_afrom=Íÿìàòå ïðàâà äà ïðåìåñòèòå '$1'
+
+acl_user=Äîñòúï äî ôàéëîâåòå íà ñúðâúðà êàòî ïîòðåáèòåë
+acl_user_def=Ñúùèÿò êàòî Webmin login
+acl_umask=Umask çà íîâèòå ôàéëîâå
+acl_follow=Âèíàãè ñëåäâàé ïðåïðàòêèòå?
+acl_ro=Ðåæèì ñàìî çà ÷åòåíå?
+acl_dirs=Ðàçðåøè äîñòúï ñàìî äî äèðåêòîðèèòå
+acl_home=Âêëþ÷è home äèðåêòîðèÿòà íà Webmin ïîòðåáèòåëÿ
+acl_log=Çàïèñâàé â æóðíàëà âñè÷êè ïðîìåíè íà ôàéëîâå?
+acl_goto=Ïîêàçâàé ïúðâàòà ðàçðåøåíà äèðåêòîðèÿ?
+
+share_title=Sharing
+share_samba=Windows
+share_nfs=NFS
+share_son=Windows ñïîäåëÿíå íà ôàéëîâå ðàçðåøåíî
+share_soff=Windows ñïîäåëÿíå íà ôàéëîâå çàáðàíåíî
+share_writable=Ïðàâà çà çàïèñ?
+share_available=Ðàçðåøåì â ìîìåíòà?
+share_sheader=Sharing íàñòðîéêèòå
+share_only=Ñàìî
+share_guest=Àíîíèìåí äîñòúï?
+share_comment=Êîìåíòàð
+share_nheader=Íàñòðîéêè íà NFS åñïîðò
+share_non=NFS ñïîäåëÿíå íà ôàéëîâå ðàçðåøåíî
+share_noff=NFS ñïîäåëÿíå íà ôàéëîâå çàáðàíåíî
+share_desc=Îïèñàíèå
+share_ro=Õîñòîâå ñ ïðàâà ñàìî çà ÷åòåíå
+share_rw=Õîñòîâå ñ ïðàâà çà ÷åòåíå è çàïèñ
+share_root=Õîñòîâå ñ àäìèíñòðàòîðñêè ïðàâà
+share_none=Íèùî
+share_all=Âñè÷êî
+share_listed=Èçáðàíèòå..
+share_host=Õîñòîâå
+share_opts=Íàñòðîéêè
+share_s0=Íå ñå äîâåðÿâàé íà íèêîé
+share_s1=Äîâåðè íå íå àäìèíñòðàòîðèòå
+share_s2=Äîâåðè ñå íà âñè÷êè
+share_lro=Ñàìî çà ÷åòåíå
+share_lrw=×åòåíå è çàïèñ
+
+log_create_export=Ñúçäàäåí NFS åêñïîðò $1
+log_modify_export=Ìîäèôèöèðàí NFS åêñïîðò $1
+log_delete_export=Èçòðèò NFS åêñïîðò $1
+log_create_share=Ñúçäàäåíî Samba ñïîäåëÿíå $1
+log_modify_share=Ìîäîôèöèðàíî Samba ñïîäåëÿíå $1
+log_delete_share=Èçòðèòî Samba ñïîäåëÿíå $1
+log_save=Çàïèñàí å ôàéë $1
+log_chmod=Ïðîìåíåíè ïðàâàòà íà ôàéë $1
+log_mkdir=Ñúçäàäåíà äèðåêòîðèÿ $1
+log_upload=Upload-íàò ôàéë $1
+log_link=Ñúçäàäåíå ïðåïðàòêà îò $1 êúì $2
+log_relink=Ìîäèôèöèðàíå ïðåïðàòêà îò $1 êúì $2
+log_copy=Êîïèðàí å ôàéë îò $1 íà $2
+log_move=Ïðåìåñòåí å ôàéë îò $1 íà $2
+log_delete=Èçòðè ôàéë $1
+log_attr=Çàäàäåíè àòðèáóòè íà ôàéë $1
+log_acl=Çàäàäåí ACL íà ôàéë $1
+
+search_eaccess=Íÿìàòå ïðàâà çà òúðñåíå â òàçè äèðåêòîðèÿ
+search_title=Òúðñè ôàéë
+search_ok=Òúðñè
+search_dir=Òúðñè äèðåêòîðèÿ
+search_match=Ôàéëîâå, êîèòî
+search_user=Ñîáñòâåíîñò íà ïîòðåáèòåë
+search_group=Ñîáñòâåíîñò íà ãðóïà
+search_any=Âñè÷êè
+search_type=Âèä ôàéë
+search_types_=Âñè÷êè
+search_types_f=Ôàéë
+search_types_d=Äèðåêòîðèÿ
+search_types_l=Ïðàïðàòêà
+search_types_p=Named pipe
+search_size=Ðàçìåð íà ôàéë
+search_more=Ïîâå÷å îò
+search_less=Ïî-ìàëêî îò
+search_xdev=Òúðñè â ìîíòèðàíèòå ó-âà?
+search_edir=Ëèïñâàùà èëè íåâàëèäíà äèòðåêòîðèÿ çà òúðñåíå
+search_ematch=Ëèïñâàù ðåãóëÿðåí èçðàç çà òúðñåíå
+search_euser=Ëèïñâàùî èìå íà ïîòðåáèòåë
+search_egroup=Ëèïñâàùî èìå íà ãðóïà
+search_esize=Ðàçìåðà íà ôàéëà òðÿáâà äà å öÿëî ÷èñëî
+search_crit=Êðèòåðèè çà òúðñåíå
+search_list=Ðåçóëòàòè îò òúðñåíå
+
+facl_eaccess=Íÿìàòå ïðàâà çà çàäàâàíå íà ACL ìà òîçè ôàéë
+
+attr_eattrs=Ãðåøêà ïðè ðàç÷èòàíå íà àòðèáóòèòå : $1
+attr_efs=Ôàéëîâàòà ñèñòåìà $1 íå ïîääúðæà àòðèáóòè
+attr_add=Äîáàâè àòðèáóò
+attr_name=Èìå íà àòðèáóò
+attr_value=Ñòîéíîñò íà àòðèáóò
+attr_efailed=Ãðåøêà ïðè ðàç÷èòàíå íà àòðèáóòèòå $1 : $2
+attr_title=Ôàéëîâè àòðèáóòè íà $1
+attr_create=Äîáàâè àòðèáóò
+attr_edit=Ïðîìÿíà íà àòðèáóò
+attr_ename=Ëèïñâà èìå íà àòðèáóò
+
+ext_eattrs=Ãðåøêà ïðè ðàç÷èòàíå íà EXT àòðèáóòèòå : $1
+ext_efs=Ôàéëîâàòà ñèñòåìà $1 íå ïîääúðæà EXT àòðèáóòè
+ext_title=EXT àòðèáóòè íà $1
+ext_header=EXT ôàéëîâè àòðèáóòè
+ext_efailed=Ãåðøêà ïðè çàäàâàíå àòðèáóòèòå íà  $1 : $2
+
+eattr_A=Íå îáíîâÿâàé ïîñëåäíèÿò ìîìåíò íà äîñòúï
+eattr_a=Ìîæå ñàìî äà áúäå äîáàâÿíî êúì ôàéëà
+eattr_c=Êîìïðåñèðàé äàííèòå íà äèñê
+eattr_d=Íå ïðàâè backup ñúñ dump
+eattr_i=Çàáðàâè ìîäèôèêàöèè
+eattr_s=Çàïúëíè ñ íóëè ïðè èçòðèâàíå
+eattr_S=Âèíàãè ïðàâè sync ñëåä îïåðàöèÿ çà çàïèñ
+eattr_u=Çàïèñè ñúäúðæàíèåòî çà äà èìà âúçìîæíîñò çà âðúùàíå íà èçòðèò ôàéë
+
+mount_eaccess=Íÿìàòå ïðàâà çà ìîíòèðàíå íà ôàéëîâè ñèñòåìè
+mount_efstab=Íÿìà íàìåðåíà ôàéëîâà ñúñòåìà íà ïîñî÷åíèÿò mounth point
+mount_epoint=$1 å íåâàëèäåí mount point
+mount_rusure1=Ñèãóðíè ëè ñòå, ÷å èñêàòå äà ìîíòèðàòå $1 îò $2 ?
+mount_rusure2=Ñèãóðíè ëè ñòå, ÷å èñêàòå äà ðàçìîíòèðàíå $1 îò $2 ?
+mount_err1=Ãðåøêà ïðè ìîíòèðàíå $1 : $2
+mount_err2=Ãðåøêà ïðè ðàçìîíòèðàíå $1 : $2
+mount_title1=Ìîíòèðàíà å ôàéëîâà ñèñòåìà
+mount_title2=Ðàçìîíòèðàíå å ôàéëîâà ñèñòåìà
+
diff --git a/file/lang/ca b/file/lang/ca
new file mode 100644 (file)
index 0000000..5700de1
--- /dev/null
@@ -0,0 +1,424 @@
+index_title=Administrador de Fitxers
+index_nojava=Aquest mòdul requereix java per funcionar, però el teu navegador no suporta java
+index_eremote=No hi ha cap usuari Unix que coincideixi amb l'entrada $1 de Webmin.
+index_index=Torna a l'<a href='$1'>Índex de Webmin</a>.
+switch_euser=L'usuari Unix no existeix!
+
+top_ret=Índex
+top_down=Desa
+top_preview=Previsualitza
+top_edit=Edita
+top_html=HTML
+top_refresh=Refresca
+top_info=Info
+top_eacl=ACL
+top_attr=Atributs
+top_ext=EXT
+top_delete=Suprimeix
+top_new=Nou
+top_upload=Puja
+top_extract=Extreu
+top_rename=Renomena
+top_copy=Copia
+top_cut=Retalla
+top_paste=Enganxa
+top_share=Xarxa
+top_mount=Munta
+top_search=Busca
+top_config=Configura
+top_efile=No has seleccionat cap fitxer
+
+right_name=Nom
+right_size=Mida
+right_user=Usuari
+right_group=Grup
+right_date=Data
+
+edit_enormal=Només es poden editar els fitxers normals
+edit_title=Editant $1
+edit_title2=Creant el fitxer
+edit_filename=Nom del fitxer:
+edit_goto=Ves
+edit_find=Busca
+edit_gotoline=Ves a la línia
+edit_replace=Reemplaça
+edit_all=Reemplaça-ho tot
+edit_searchfor=Busca
+edit_replaceby=Reemplaça amb
+edit_eover=$1 no es pot reescriure
+edit_esave=No he pogut desar el fitxer: $1
+edit_eaccess=No tens permís per desar '$1'
+edit_efollow=No tens permís per gravar l'enllaç simbòlic '$1'
+edit_notfound=El text $1 no s'ha trobat
+edit_saveclose=Desa & Tanca
+edit_elength=S'ha escapçat el fitxer!
+
+info_file=Fitxer
+info_path=Camí:
+info_type=Tipus:
+info_size=Mida:
+info_mod=Modificat:
+info_link=Enllaç a:
+info_perms=Permisos
+info_user=Usuari:
+info_group=Grup:
+info_other=Altres:
+info_octal=Octal:
+info_sticky=Sticky:
+info_sticky2=Només els propietaris poden esborrar fitxers
+info_own=Propietat
+info_setuid=Setuid:
+info_setuid2=Executa com a usuari
+info_setgid=Setgid:
+info_setgid2=Els fitxers hereten el grup
+info_setgid3=Executa com a grup
+info_apply=Aplica els canvis a
+info_apply1=Aquest directori nomes
+info_apply2=Aquest directori i els seus fitxers
+info_apply3=Aquest directori i tots els seus subdirectoris
+info_efailed=No he pogut actualitzar $1: $2
+info_read=Llegir
+info_write=Escriure
+info_list=Llistar
+info_exec=Executar
+info_sizeheader=Mida del directori
+info_bytes=Total bytes:
+info_files=Total fitxers:
+info_dirs=Total directoris:
+info_getsize=Obtingues la Mida
+info_esize=No he pogut obtenir les mides: $1
+
+eacl_eacls=No he pogut llegir les ACLs: $1
+eacl_acltype=Tipus d'ACL
+eacl_aclname=Aplica a
+eacl_aclperms=Permisos
+eacl_add=Afegeix ACL del tipus:
+eacl_remove=Esborra ACL
+eacl_efs=El sistema de fitxers $1 no suporta ACLs
+eacl_create=Crea ACL
+eacl_edit=Edita ACL
+eacl_user=Propietari del fitxer $1
+eacl_group=Grup del fitxer $1
+eacl_eowner=Hi falta l'usuari o el grup per aplicar
+eacl_efailed=No he pogut establir l'ACL per a $1: $2
+eacl_emask=Hi pot haver com a molt una entrada de màscara ACL
+eacl_edefmask=Hi pot haver com a molt una entrada per defecte de màscara ACL
+eacl_title=ACL de $1
+eacl_owner=Propietari del fitxer
+eacl_edefaults=Si un fitxer té alguna ACL per defecte, ha de tenir usuari i grup per defecte.
+
+acltype_user=Usuari
+acltype_group=Grup
+acltype_other=Altres
+acltype_mask=Màscara
+acltype_default_user=Usuari per defecte
+acltype_default_group=Grup per defecte
+acltype_default_other=Altres per defecte
+acltype_default_mask=Màscara per defecte
+
+delete_mtitle=Suprimeix fitxers múltiples
+delete_dtitle=Supressió de directori
+delete_ftitle=Supressió de fitxer
+delete_ddesc=Segur que vols esborrar de forma definitiva el directori $1 i tot el seu contingut?
+delete_fdesc=Segur que vols esborrar de forma definitiva el fitxer $1?
+delete_efailed=No he pogut esborrar $1: $2
+delete_mdesc=Segur que vols suprimir de forma permanent aquests fitxers i directoris?
+
+mkdir_title=Nou Directori
+mkdir_dir=Nou directori:
+mkdir_eexists=$1 ja existeix
+mkdir_efailed=La creació del director ha fallat: $1
+mkdir_eaccess=No tens permís per crear '$1'
+
+link_title=Creació d'enllaç
+link_from=Enllaça:
+link_to=A:
+link_eexists=$1 ja existeix
+link_efrom=L'origen de l'enllaç ha de ser un camí absolut
+link_efailed=L'enllaç ha fallat: $1
+link_efrom2=No tens permís per enllaçar '$1'
+link_efollow=No tens permís per crear enllaços simbòlics
+
+rename_title=Renomenament de $1
+rename_old=Nom vell:
+rename_new=Nom nou:
+rename_ok=Renomena
+rename_eexists=Ja existeix un fitxer anomenat $1
+rename_eslash=El nom de fitxer nou $1 conté una /
+rename_efailed=El renomenament ha fallat: $1
+iename_eold=No tens permís per renomenar '$1'
+rename_enew=No tens permís per renomenar a '$1'
+
+file_type0=Directori
+file_type1=Fitxer de text
+file_type2=Fitxer d'imatge
+file_type3=Fitxer binari
+file_type4=Fitxer
+file_type5=Enllaç simbòlic
+file_type6=Fitxer de dispositiu
+file_type7=Pipe
+
+view_enormal=Només es poden mostrar els fitxers normals
+view_enormal2=Només es poden descarregar fitxers normals
+view_eaccess=No tens permís per accedir a $1
+view_eopen=No he pogut obrir $1: $2
+view_edir=Un arxiu només es pot crear per a un directori
+view_ecmd=L'ordre $1 necessària per a crear un arxiu no està instal·lada
+view_ecomp=No he pogut crear l'arxiu: $1
+view_earchive=No tens permís per descarregar arxius
+view_earchmax=El directori seleccionat és més gran que el màxim permès per arxivar ($1 bytes)
+
+paste_ecopy=Has de copiar o retallar abans d'enganxar
+paste_egone=El fitxer copiat $1 ja no existeix
+paste_eover=$1 no es pot reescriure
+paste_eself=No pots enganxar un fitxer sobre ell mateix
+paste_emfailed=El desplaçament ha fallat: $1
+paste_ecfailed=La copia ha fallat: $1
+
+over_title=Fitxer Existent
+over_msg=El fitxer $1 ja existeix. Utilitza el camp inferior per introduir un nom de fitxer nou per al fitxer enganxat.
+over_new=Nom de fitxer nou:
+over_ok=Bé
+
+upload_efailed=No he pogut obrir el fitxer per pujar: $1
+upload_title=Puja de Fitxer
+upload_file=Fitxer per pujar
+upload_dir=Puja'l al directori
+upload_ok=Puja
+upload_conv=Converteix salts de línia DOS
+upload_efile=No s'ha seleccionat cap fitxer per pujar.
+upload_edir=El directori de destinació no existeix.
+upload_eperm=No tens permís per crear $1
+upload_ewrite=No he pogut escriure a $1: $2.
+upload_already=El fitxer $1 ja existeix. Segur que el vols reescriure?
+upload_elink=No puc fer la puja sobre un enllaç simbòlic
+upload_zip=Descomprimeixo el fitxer ZIP o TAR?
+upload_yes=Sí, llavors suprimeix-lo
+
+find_eaccess=No tens permís per accedir $1
+find_eexist=$1 no existeix a $2
+find_edir=$1 no és un directori de $2
+
+cancel=Cancel·la
+close=Tanca
+eopen=La descàrrega ha fallat: $1
+
+chmod_eaccess=No tens permís per accedir '$1'
+chmod_euser=$1: no existeix l'usuari
+chmod_egroup=$1: no existeix el grup
+chmod_elink=enllaç simbòlic fallit: $1
+chmod_echown=chown fallit: $1
+chmod_echmod=chmod fallit: $1
+chmod_efollow=No tens permís per editar els enllaços simbòlics
+
+copy_efrom=No tens permís per copiar de '$1'
+copy_eto=No tens permís per copiar a '$1'
+copy_elink=enllaç simbòlic fallit: $1
+
+delete_eaccess=No tens permís per esborrar '$1'
+
+list_eaccess=No tens permís per accedir a aquest directori
+list_edir=No he pogut llistar $1: $2
+
+move_eto=No tens permís per desplaçar a '$1'
+move_afrom=No tens permís per desplaçar '$1'
+
+acl_user=Accedeix els fitxers del servidor com a usuari
+acl_user_def=Igual que l'usuari Webmin
+acl_umask=Umask per a fitxers nous
+acl_follow=Segueix sempre els enllaços simbòlics
+acl_fyes=Si el propietari coincideix
+acl_ro=Mode només lectura
+acl_dirs=Permet l'accés nomes als directoris
+acl_nodirs=Denega l'accés als directoris
+acl_home=Inclou el directori arrel de l'usuari Webmin
+acl_log=Enregistra totes les modificacions de fitxers
+acl_goto=Obre el primer directori permès
+acl_max=Mida màxima de pujada
+acl_unlim=Il·limitada
+acl_b=bytes
+acl_archive=Pot descarregar arxius de directoris
+acl_archmax=Sí, si són més petits de
+acl_buttons=Botons disponibles a la barra d'eines
+acl_button_save=Desa (descarrega el fitxer)
+acl_button_preview=Previsualitza (visualitza imatge escalada)
+acl_button_edit=Edita (edita el fitxer de text)
+acl_button_info=Info (edita els permisos i propietat del fitxer)
+acl_button_acl=ACL (edita l'ACL Posix)
+acl_button_attr=Atr (edita els atributs XFS)
+acl_button_ext=EXT (edita els atributs EXT)
+acl_button_search=Busca (busca fitxers)
+acl_button_delete=Suprimeix (suprimeix fitxers)
+acl_button_new=Nou (crea fitxer de text)
+acl_button_upload=Puja (puja un fitxer des del client)
+acl_button_mkdir=Nou (crea un directori)
+acl_button_makelink=Nou (crea un enllaç simbòlic)
+acl_button_rename=Renomena (renomena el fitxer)
+acl_button_sharing=Comparteix (configura els fitxers compartits Samba i NFS)
+acl_button_mount=Munta (munta o desmunta el sistema de fitxers)
+acl_button_copy=Copia, Retalla i Enganxa
+acl_unarchive=Pot extreure fitxers d'un arxiu pujat
+acl_unarchive2=Intenta-ho sempre
+acl_unarchive1=$yes
+acl_unarchive0=$no
+acl_dostounix=Pot convertir els salts de línia DOS
+acl_chroot=Directori chroot del gestor de fitxers complet
+acl_relto=(relatiu a qualsevol directori chroot)
+acl_noperms=Pot canviar els permisos dels fitxers
+acl_nousers=Pot canviar el propietari dels fitxers
+acl_filesystems=Pot veure els punts de muntatge dels sistemes de fitxers
+acl_contents=Permet la recerca de continguts de fitxers
+
+share_title=Xarxa
+share_samba=Windows
+share_nfs=NFS
+share_son=Compartició de fitxers Windows activada
+share_soff=Compartició de fitxers Windows desactivada
+share_writable=Gravable
+share_available=Actualment actiu
+share_sheader=Opcions del recurs
+share_only=Nomes
+share_guest=Accés hoste
+share_comment=Comentari
+share_nheader=Opcions d'exportació NFS
+share_non=Compartició de fitxers NFS activada
+share_noff=Compartició de fitxers NFS desactivada
+share_desc=Descripció
+share_ro=Hosts nomes lectura
+share_rw=Hosts lectura/escriptura
+share_root=Hosts amb accés root
+share_none=Cap
+share_all=Tot
+share_listed=Llistats...
+share_host=Hosts
+share_opts=Opcions
+share_s0=No et refiïs de ningú
+share_s1=Refia't de no-root
+share_s2=Refia't de tothom
+share_lro=Només lectura
+share_lrw=Lectura-escriptura
+
+log_create_export=He creat l'exportació NFS $1
+log_modify_export=He modificat l'exportació NFS $1
+log_delete_export=He suprimit l'exportació NFS $1
+log_create_share=He creat el recurs Samba $1
+log_modify_share=He modificat el recurs Samba $1
+log_delete_share=He suprimit el recurs Samba $1
+log_save=He desat el fitxer $1
+log_chmod=He canviat els permisos del fitxer $1
+log_mkdir=He creat el directori $1
+log_upload=He pujat el fitxer $1
+log_link=He creat l'enllaç simbòlic $1 a $2
+log_relink=He modificat l'enllaç simbòlic $1 a $2
+log_copy=He copiat el fitxer $1 a $2
+log_move=He desplaçat el fitxer $1 a $2
+log_delete=He esborrat el fitxer $1
+log_attr=Estableix atributs sobre el fitxer $1
+log_acl=Estableix ACL sobre el fitxer $1
+
+search_eaccess=No tens permís per buscar en aquest directori
+search_title=Busca Fitxers
+search_ok=Busca ara
+search_dir=Busca al directori
+search_match=els fitxers que coincideixin amb
+search_cont=que continguin el text
+search_user=Usuari propietari
+search_group=Grup propietari
+search_any=Qualsevol
+search_type=Tipus de fitxer
+search_types_=Qualsevol
+search_types_f=Fitxer
+search_types_d=Directori
+search_types_l=Enllaç simbòlic
+search_types_p=Pipe
+search_size=Mida del fitxer
+search_more=Més de
+search_less=Menys de
+search_xdev=Busca muntatges anteriors
+search_edir=Hi falta el directori de recerca o bé no és correcte
+search_ematch=Hi falta una expressió regular
+search_euser=Hi falta un nom d'usuari
+search_egroup=Hi falta un nom de grup
+search_esize=La mida del fitxer ha de ser un enter
+search_crit=Criteris de Recerca
+search_list=Resultats de la Recerca
+search_down=Descarrega
+search_edown=No has seleccionat cap fitxer de resultats de recerca per descarregar
+
+facl_eaccess=No tens permís per establir ACLs per a aquest fitxer
+
+attr_eattrs=No he trobat els atributs: $1
+attr_efs=El sistema de fitxers $1 no suporta atributs
+attr_add=Afegeix Atribut
+attr_name=Nom de l'Atribut
+attr_value=Valor de l'Atribut
+attr_efailed=No he pogut establir els atributs de $1: $2
+attr_title=Atributs de Fitxer de $1
+attr_create=Crea Atribut
+attr_edit=Edita Atribut
+attr_ename=Hi falta el nom de l'atribut
+
+ext_eattrs=No he pogut obtenir els atributs EXT: $1
+ext_efs=El sistema de fitxers $1 no suporta atributs EXT
+ext_title=Atributs EXT de $1
+ext_header=Atributs EXT de fitxer
+ext_efailed=No he pogut establir els atributs de $1: $2
+
+eattr_A=No actualitzis els temps d'accés
+eattr_a=Només pot afegir al fitxer
+eattr_c=Comprimeix les dades del disc
+eattr_d=No en facis còpia amb <tt>dump</tt>
+eattr_i=No en permetis la modificació
+eattr_s=Posa els blocs a zero en suprimir
+eattr_S=Fes sempre un <i>sync</i> després de gravar
+eattr_u=Desa el contingut per a desfer
+
+mount_eaccess=No tens permís per muntar sistemes de fitxers
+mount_efstab=No hi ha cap sistema de fitxers en aquest punt de muntatge
+mount_epoint=$1 no és un punt de muntatge
+mount_rusure1=Segur que vols muntar $1 de $2 ?
+mount_rusure2=Segur que vols desmuntar $1 de $2 ?
+mount_err1=No he pogut muntar $1: $2
+mount_err2=No he pogut desmuntar $1: $2
+mount_title1=Munta el sistema de fitxers
+mount_title2=Desmunta el sistema de fitxers
+
+zip_err=No puc extreure el fitxer: $1
+zip_ecmd=Hi falta l'ordre $1
+zip_eunzip=La descompressió ZIP ha fallat: $1
+zip_ename=No sembla que sigui cap fitxer zip, tar ni tar.gz
+zip_euntar=El desarxivat TAR ha fallat: $1
+zip_euntar2=La descompressió i desarxivat TAR ha fallat: $1
+
+ddir_title=Descàrrega de Directori
+ddir_rusure=Per descarregar el contingut de $1 com un fitxer d'arxiu, fes clic sobre una dels botons de tipus d'arxiu de sota.
+ddir_zip=ZIP
+ddir_tgz=TAR.GZ
+ddir_tar=TAR
+
+ebutton=Aquesta característica no està disponible
+
+preview_etype=No he pogut determinar el tipus de fitxer per a $1
+preview_etype2=$1 no és cap format d'imatge suportat
+preview_ecmd=L'ordre $1 necessària per escalar la imatge no està  instal·lada
+preview_eimage=Només es poden previsualitzar imatges
+preview_title=Vista Prèvia de $1
+preview_bad=No he pogut carregar la imatge a previsualitzar
+preview_egd=No he pogut carregar la imatge amb GD
+
+html_efailed=No he pogut obrir l'editor HTML: $1
+html_title=Edició de Fitxer HTML
+html_title2=Creació de Fitxer HTML
+html_save=Desa i Tanca
+html_err=No he pogut desar el fitxer HTML
+
+history_title=Historial de Camins
+history_ok=Vés A
+history_button=Historial
+
+extract_etype=Només es poden exterure fitxers
+extract_title=Extracció de Fitxer Comprimit
+extract_rusure=Segur que vols extreure el fitxer comprimit:
+extract_rusure2=Els fitxers existents al mateix directori es reescriuran.
+extract_err=No he pogut extreure el fitxer: $1
+extract_yes=Sí, després suprimeix-lo
diff --git a/file/lang/de b/file/lang/de
new file mode 100644 (file)
index 0000000..4563961
--- /dev/null
@@ -0,0 +1,345 @@
+acl_archive=Darf Archive von Verzeichnissen herunterladen?
+acl_archmax=Ja, wenn sie kleiner sind als
+acl_b=bytes
+acl_button_acl=ACL (bearbeite Posix ACL)
+acl_button_attr=ATTR (bearbeite XFS-Attribute)
+acl_button_copy=Kopieren, Ausschneiden und Einf&#252;gen
+acl_button_delete=L&#246;schen (Dateien l&#246;schen)
+acl_button_edit=Bearbeiten (Textdatei bearbeiten)
+acl_button_ext=EXT (bearbeite EXT-Attribute)
+acl_button_info=Info (bearbeite Dateirechte und -inhaberschaft)
+acl_button_makelink=Neu (Erzeuge symbolischen Link)
+acl_button_mkdir=Neu (Erzeuge Verzeichnis)
+acl_button_mount=Einbinden (Einbinden oder Aush&#228;ngen von Dateisystemen)
+acl_button_new=Neu (Erzeuge Textdatei)
+acl_button_rename=Umbenennen (Datei umbenennen)
+acl_button_save=Speichern (heruntergeladene Datei)
+acl_button_search=Finde (Dateien finden)
+acl_button_sharing=Gemeinsamer Dateizugriff (richte Samba- und NFS Dateizugriff ein)
+acl_button_upload=Hochladen (lade Dateien eines Clients hoch)
+acl_buttons=Verf&#252;gbare Kn&#246;pfe in der Toolbar
+acl_chroot=ChangeRoot-Verzeichnis f&#252;r den gesamten Dateimanager
+acl_dirs=Erlaube nur Zugriff auf Verzeichnisse
+acl_dostounix=Darf DOS-"Neue Zeile"-Zeichen konvertieren?
+acl_follow=Folge immer symbolischen Links?
+acl_fyes=Wenn Benutzer &#252;bereinstimmt
+acl_goto=&#214;ffne erstes erlaubtes Verzeichnis?
+acl_home=Das Heimatverzeichnis des Webmin-Benutzers einbinden?
+acl_log=Alle Datei&#228;nderungen protokollieren?
+acl_max=Maximale Hochladegr&#246;&#223;e
+acl_nodirs=Verweigere Zugriff auf Verzeichnisse
+acl_relto=(relativ zu jedem ChangeRoot-Verzeichnis)
+acl_ro=Nur-Lesen-Modus?
+acl_umask=Umask f&#252;r neue Dateien
+acl_unarchive=Darf hochgeladene Archivdateien auspacken?
+acl_unarchive0=$nein
+acl_unarchive1=$ja
+acl_unarchive2=Versuche immer
+acl_unlim=Unbegrenzt
+acl_user=Greife als Benutzer auf Dateien auf dem Server zu
+acl_user_def=Wie Webmin-Benutzer
+acltype_default_group=Standard-Gruppe
+acltype_default_mask=Standard-Maske
+acltype_default_other=Andere Standards
+acltype_default_user=Standard-Benutzer
+acltype_group=Gruppe
+acltype_mask=Maske
+acltype_other=Andere
+acltype_user=Benutzer
+attr_add=Attribut hinzuf&#252;gen
+attr_create=Attribut hinzuf&#252;gen
+attr_eattrs=Konnte Attribute nicht lesen: $1
+attr_edit=Attribut bearbeiten
+attr_efailed=Konnte Attribute f&#252;r $1 nicht setzen: $2
+attr_efs=Dateisystem $1 unterst&#252;tzt keine Attribute
+attr_ename=Fehlender Attributname
+attr_name=Attributname
+attr_title=Dateiattribute f&#252;r $1
+attr_value=Attribut Wert
+cancel=Abbrechen
+chmod_eaccess=Sie haben keine Berechtigung zum Zugriff auf '$1'
+chmod_echmod=chmod schlug fehl : $1
+chmod_echown=chown schlug fehl : $1
+chmod_efollow=Sie d&#252;rfen keine symbolischen Links bearbeiten
+chmod_egroup=$1 : Gruppe nicht gefunden
+chmod_elink=Symbolischer Link schlug fehl : $1
+chmod_euser=$1 : Benutzer nicht gefunden
+close=Schlie&#223;en
+copy_efrom=Sie haben keine Berechtigung aus '$1' zu kopieren
+copy_elink=Symbolischer Link schlug fehl : $1
+copy_eto=Sie haben keine Berechtigung in '$1' zu kopieren
+ddir_rusure=Um den Inhalt von $1 als Archivdatei herunterzuladen, klicken Sie bitte auf den Archivbutton.
+ddir_tar=TAR
+ddir_tgz=TAR.GZ
+ddir_title=Download-Verzeichnis
+ddir_zip=ZIP
+delete_ddesc=Sind Sie sicher, da&#223; Sie das Verzeichnis $1 und alle Unterverzeichnisse unwiderruflich l&#246;schen wollen?
+delete_dtitle=L&#246;sche Verzeichnis
+delete_eaccess=Sie haben keine Berechtigung '$1' zu l&#246;schen
+delete_efailed=Fehler beim L&#246;schen von $1 : $2
+delete_fdesc=Sind Sie sicher, dass Sie die Datei $1 unwiderruflich l&#246;schen wollen?
+delete_ftitle=L&#246;sche Datei
+delete_mdesc=Sind Sie sicher, dass Sie diese Dateien und Verzeichnisse permanent l&#246;schen wollen? :
+delete_mtitle=Mehrere Dateien l&#246;schen
+eacl_aclname=Anwenden auf
+eacl_aclperms=Berechtigungen
+eacl_acltype=ACL-Typ
+eacl_add=ACL des Typs hinzuf&#252;gen :
+eacl_create=ACL einrichten
+eacl_eacls=Konnte ACLs nicht einlesen: $1
+eacl_edefaults=Wenn eine Datei eine Standard-ACL hat, dann mu&#223; ein Standard-Benutzer, eine -Gruppe und andere ACLs eingerichtet sein.
+eacl_edefmask=Es kann nur einen Standard-ACL-Eintrag geben
+eacl_edit=ACL bearbeiten
+eacl_efailed=Konnte ACL f&#252;r $1 nicht setzen : $2
+eacl_efs=Dateisystem $1 unterst&#252;tzt keine ACLs
+eacl_emask=Es kann nur einen ACL-Eintrag geben
+eacl_eowner=Benutzer oder Gruppe nicht angegeben
+eacl_group=Datei-Gruppe $1
+eacl_owner=Datei-Besitzer
+eacl_remove=ACL entfernen
+eacl_title=ACL f&#252;r $1
+eacl_user=Datei-Besitzer $1
+eattr_A=Zugriffszeit nicht &#228;ndern
+eattr_S=Nach Schreiben immer synchronisieren
+eattr_a=Kann nur an Datei anh&#228;ngen
+eattr_c=Datei auf Festplatte komprimieren
+eattr_d=Nicht mit dump sichern
+eattr_i=Keine &#196;nderungen zulassen
+eattr_s=Beim L&#246;schen mit Null-Bl&#246;cken &#252;berschreiben
+eattr_u=Inhalte f&#252;r Wiederherstellung sichern
+ebutton=Diese Funktion ist nicht verf&#252;gbar
+edit_all=Alles ersetzen
+edit_eaccess=Sie haben keine Berechtigung die Datei '$1' zu speichern
+edit_efollow=Sie d&#252;rfen nicht auf den symbolischen Link '$1' schreiben
+edit_elength=Datei wurde abgeschnitten!
+edit_enormal=Nur normale Dateien k&#246;nnen bearbeitet werden
+edit_eover=$1 kann nicht &#252;berschrieben werden
+edit_esave=Fehler beim Speichern der Datei : $1
+edit_filename=Dateiname:
+edit_find=Finde
+edit_goto=Gehe zu
+edit_gotoline=Gehe zu Zeile
+edit_notfound=Der Text $1 wurde nicht gefunden
+edit_replace=Ersetzen
+edit_replaceby=Ersetzen mit
+edit_saveclose=Speichern & Schliessen
+edit_searchfor=Suchen nach
+edit_title=Bearbeite $1
+edit_title2=Erstelle Datei
+eopen=Download gescheitert: $1
+ext_eattrs=Konnte EXT-Attribute nicht lesen : $1
+ext_efailed=Konnte EXT-Attribute f&#252;r $1 nicht lesen : $2
+ext_efs=Dateisystem $1 unterst&#252;tzt keine EXT-Attribute
+ext_header=EXT-Datei-Attribute
+ext_title=EXT-Datei-Attribute f&#252;r $1
+facl_eaccess=Sie d&#252;rfen keine ACLs f&#252;r diese Datei setzen
+file_type0=Verzeichnis
+file_type1=Textdatei
+file_type2=Bilddatei
+file_type3=Bin&#228;re Datei
+file_type4=Datei
+file_type5=Symbolischer Link
+file_type6=Ger&#228;tedatei
+file_type7=Pipe
+find_eaccess=Sie haben keine Berechtigung um auf $1 zuzugreifen
+find_edir=$1 ist kein Verzeichnis in $2
+find_eexist=$1 existiert nicht in $2
+index_eremote=F&#252;r diesen Unix-Benutzer gibt es keine Webmin-Anmeldung $1.
+index_nojava=Dieses Modul ben&#246;tigt Java. Entweder unterst&#252;tzt Ihr Browser kein Java oder Java ist nicht richtig installiert.
+index_title=Datei-Manager
+info_apply=&#196;nderungen anwenden auf
+info_apply1=Nur dieses Verzeichnis
+info_apply2=Dieses Verzeichnisse und seine Dateien
+info_apply3=Dieses Verzeichnis und alle Unterverzeichnisse
+info_efailed=Fehler beim Update von $1 : $2
+info_exec=Ausf&#252;hren
+info_file=Datei
+info_group=Gruppe:
+info_link=Link nach:
+info_list=Liste
+info_mod=Ver&#228;ndert:
+info_octal=Oktal:
+info_other=Andere:
+info_own=Besitzer
+info_path=Pfad:
+info_perms=Rechte
+info_read=Lesen
+info_setgid=SetGID:
+info_setgid2=Dateien erben Gruppen
+info_setgid3=F&#252;hre aus als Gruppe
+info_setuid=SetUID:
+info_setuid2=F&#252;hre aus als Benutzer
+info_size=Gr&#246;&#223;e:
+info_sticky=Sticky:
+info_sticky2=Nur Besitzer darf Dateien l&#246;schen
+info_type=Typ:
+info_user=Benutzer:
+info_write=Schreiben
+link_eexists=$1 existiert bereits
+link_efailed=Link schlug fehl : $1
+link_efollow=Sie haben keine Berechtigung symbolische Links anzulegen
+link_efrom=Sie haben keine Berechtigung von '$1' zu verkn&#252;pfen
+link_efrom2=Sie d&#252;rfen nicht von '$1' linken
+link_from=Link von:
+link_title=Erstelle Link
+link_to=Link zu:
+list_eaccess=Sie haben keine Berechtigung zum Zugriff auf dieses Verzeichnis
+list_edir=Konnte $1 nicht auflisten : $2
+log_acl=ACL f&#252;r Datei $1 gesetzt
+log_attr=Attribute f&#252;r Datei $1 gesetzt
+log_chmod=Berechtigungen f&#252;r Datei $1 ge&#228;ndert
+log_copy=Datei von $1 nach $2 kopiert
+log_create_export=NFS-Export $1 erzeugt
+log_create_share=Samba-Freigabe $1 erzeugt
+log_delete=Datei $1 gel&#246;scht
+log_delete_export=NFS-Export $1 gel&#246;scht
+log_delete_share=Samba-Freigabe $1 gel&#246;scht
+log_link=Symbolischer Link von $1 nach $2 erzeugt
+log_mkdir=Verzeichnis angelegt $1
+log_modify_export=NFS-Export $1 ge&#228;ndert
+log_modify_share=Samba-Freigabe $1 ge&#228;ndert
+log_move=Datei von $1 nach $2 verschoben
+log_relink=Symbolischer Link von $1 nach $2 ge&#228;ndert
+log_save=Datei $1 gespeichert
+log_upload=Datei $1 hochgeladen
+mkdir_dir=Neues Verzeichnis:
+mkdir_eaccess=Sie haben keine Berechtigung, '$1' anzulegen
+mkdir_eexists=$1 existiert bereits
+mkdir_efailed=Erstellen des Verzeichnisses schlug fehl : $1
+mkdir_title=Neues Verzeichnis
+mount_eaccess=Sie d&#252;rfen keine Dateisysteme einbinden
+mount_efstab=Es existiert kein Dateisystem an diesem Einh&#228;ngepunkt
+mount_epoint=$1 ist kein Einh&#228;ngepunkt
+mount_err1=Konnte $1 nicht einh&#228;ngen : $2
+mount_err2=Konnte $1 nicht aush&#228;ngen : $2
+mount_rusure1=Sind Sie sicher, dass Sie $1 in $2 einh&#228;ngen wollen ?
+mount_rusure2=Sind Sie sicher, dass Sie $1 von $2 aush&#228;ngen wollen ?
+mount_title1=Dateisystem einh&#228;ngen
+mount_title2=Dateisystem aush&#228;ngen
+move_afrom=Sie haben keine Berechtigung '$1' zu verschieben
+move_eto=Sie haben keine Berechtigung, Dateien nach '$1' zu verschieben
+over_msg=Die Datei $1 existiert bereits. Benutzen Sie das Feld unten um einen anderen Namen f&#252;r die neue Datei anzugeben.
+over_new=Neuer Dateiname:
+over_ok=Ok
+over_title=Datei existiert
+paste_ecfailed=Kopieren schlug fehl : $1
+paste_ecopy=Vor dem Einf&#252;gen mu&#223;en Sie kopieren oder ausschneiden
+paste_egone=Kopierte Datei $1 existiert nicht mehr
+paste_emfailed=Verschieben schlug fehl : $1
+paste_eover=$1 kann nicht &#252;berschrieben werden
+paste_eself=Sie k&#246;nnen keine Datei auf sich selber einf&#252;gen
+rename_eexists=Es existiert bereits eine Datei mit dem Namen $1
+rename_efailed=Umbennenen schlug fehl : $1
+rename_enew=Sie haben keine Berechtigung zu '$1' umzubennenen
+rename_eold=Sie haben keine Berechtigung, '$1' umzubennenen
+rename_new=Neuer Name:
+rename_ok=Umbenennen
+rename_old=Alter Name:
+rename_title=Umbennenen von $1
+right_date=Datum
+right_group=Gruppe
+right_name=Name
+right_size=Gr&#246;&#223;e
+right_user=Benutzer
+search_any=Alle
+search_crit=Suchkriterien
+search_dir=Suche in Verzeichnis
+search_eaccess=Sie haben keine Berechtigung, dieses Verzeichnis zu durchsuchen
+search_edir=Fehlendes oder ung&#252;ltiges Verzeichnis
+search_egroup=Fehlender Gruppen-Name
+search_ematch=Fehlender anwendbarer regul&#228;rer Ausdruck
+search_esize=Dateigr&#246;&#223;e mu&#223; eine Ganzzahl sein
+search_euser=Fehlender Benutzername
+search_group=Im Besitz der Gruppe
+search_less=Weniger als
+search_list=Suchergebnisse
+search_match=auf Dateien passend
+search_more=Mehr als
+search_ok=Jetzt suchen
+search_size=Dateigr&#246;sse
+search_title=Dateien suchen
+search_type=Datei-Typ
+search_types_=Alle
+search_types_d=Verzeichnis
+search_types_f=Datei
+search_types_l=Symbolischer Link
+search_types_p=Benannte Pipe
+search_user=Im Besitz des Benutzers
+search_xdev=Hinter Einh&#228;ngepunkten suchen?
+share_all=Alle
+share_available=Momentan aktiv?
+share_comment=Kommentar
+share_desc=Beschreibung
+share_guest=Gast-Zugriff
+share_host=Hosts
+share_listed=Aufgelistet..
+share_lro=Nur-Lesen
+share_lrw=Lesen-Schreiben
+share_nfs=NFS
+share_nheader=NFS-Exporteinstellungen
+share_noff=NFS-Dateifreigabe deaktiviert
+share_non=NFS-Dateifreigabe aktiviert
+share_none=Keine
+share_only=Nur
+share_opts=Einstellungen
+share_ro=Nur-Lesen Hosts
+share_root=Root-Zugriff Hosts
+share_rw=Lesen-Schreiben Hosts
+share_s0=Niemandem trauen
+share_s1=Nicht-Root trauen
+share_s2=Jedem trauen
+share_samba=Windows
+share_sheader=Windows-Freigabeeinstellungen
+share_soff=Windows-Datei-Freigabe deaktiviert
+share_son=Windows-Datei-Freigabe aktiviert
+share_title=Freigabe
+share_writable=Beschreibbar?
+switch_euser=Dieser Unix-Benutzer existiert nicht!
+top_attr=Attribute
+top_config=Konfig.
+top_copy=Kopieren
+top_cut=Ausschneiden
+top_delete=L&#246;schen
+top_down=Speichern
+top_eacl=ACL
+top_edit=Bearbeiten
+top_ext=EXT
+top_info=Info
+top_mount=Mount
+top_new=Neu
+top_paste=Einf&#252;gen
+top_refresh=Neu laden
+top_rename=Umbenennen
+top_ret=Webmin
+top_search=Suchen
+top_share=Freigabe
+top_upload=Hochladen
+upload_already=Die Datei $1 existiert bereits. M&#246;chten Sie diese &#252;berschreiben?
+upload_conv=Konvertiere DOS Zeilenvorsch&#252;be?
+upload_dir=Hochladen zu Verzeichnis
+upload_edir=Hochlade-Verzeichnis existiert nicht.
+upload_efailed=Fehler beim &#214;ffnen der hochzuladenden Datei : $1
+upload_efile=Keine Datei zum Hochladen ausgew&#228;hlt.
+upload_elink=Kann auf einen symbolischen Link nicht hochladen
+upload_eperm=Sie haben keine Berechtigung $1 zu erstellen
+upload_ewrite=Fehler beim Schreiben von $1 : $2.
+upload_file=Datei zum Hochladen
+upload_ok=Hochladen
+upload_title=Datei hochladen
+upload_yes=Ja, dann l&#246;schen
+upload_zip=ZIP- oder TAR-Dateien dekomprimieren?
+view_eaccess=Sie haben keine Berechtigung zum Zugriff auf $1
+view_earchive=Sie d&#252;rfen keine Archive herunterladen
+view_earchmax=Das ausgew&#228;hlte Verzeichnis ist gr&#246;&#223;er als die maximal erlaubte Gr&#246;&#223;e f&#252;r Archive ($1 bytes)
+view_ecmd=Der Befehl $1, der zum Erzeugen von Archiven ben&#246;tigt wird, ist nicht installiert.
+view_ecomp=Konnte Archiv nicht erzeugen: $1
+view_edir=Ein Archiv kann nur f&#252;r ein Verzeichnis angelegt werden
+view_enormal=Nur normale Dateien k&#246;nnen betrachtet werden
+view_enormal2=Nur normale Dateien k&#246;nnen lokal gespeichert werden.
+view_eopen=Fehler beim &#214;ffnen von $1 : $2
+zip_ecmd=Fehlendes $1 Kommando
+zip_ename=Scheint keine ZIP-, TAR- oder TAR.GZ-Datei zu sein.
+zip_err=Konnte Datei nicht auspacken: $1
+zip_euntar=Un-TAR gescheitert: $1
+zip_euntar2=Dekomprimierung und Un-TAR gescheitert: $1
+zip_eunzip=Un-ZIP gescheitert: $1
diff --git a/file/lang/en b/file/lang/en
new file mode 100644 (file)
index 0000000..cbd8700
--- /dev/null
@@ -0,0 +1,424 @@
+index_title=File Manager
+index_nojava=This module requires java to function, but your browser does not support java
+index_eremote=There is no Unix user matching the Webmin login $1.
+index_index=Return to <a href='$1'>Webmin index</a>.
+switch_euser=Unix user does not exist!
+
+top_ret=Index
+top_down=Save
+top_preview=Preview
+top_edit=Edit
+top_html=HTML
+top_refresh=Refresh
+top_info=Info
+top_eacl=ACL
+top_attr=Attrs
+top_ext=EXT
+top_delete=Delete
+top_new=New
+top_upload=Upload
+top_extract=Extract
+top_rename=Rename
+top_copy=Copy
+top_cut=Cut
+top_paste=Paste
+top_share=Sharing
+top_mount=Mount
+top_search=Find
+top_config=Config
+top_efile=No file selected
+
+right_name=Name
+right_size=Size
+right_user=User
+right_group=Group
+right_date=Date
+
+edit_enormal=Only normal files can be edited
+edit_title=Editing $1
+edit_title2=Creating file
+edit_filename=Filename:
+edit_goto=Goto
+edit_find=Find
+edit_gotoline=Go to line
+edit_replace=Replace
+edit_all=Replace all
+edit_searchfor=Search for
+edit_replaceby=Replace by
+edit_eover=$1 cannot be overwritten
+edit_esave=Failed to save file : $1
+edit_eaccess=You are not allowed to save '$1'
+edit_efollow=You are not allowed to write to the symbolic link '$1'
+edit_notfound=The text $1 was not found
+edit_saveclose=Save & Close
+edit_elength=File was truncated!
+
+info_file=File
+info_path=Path:
+info_type=Type:
+info_size=Size:
+info_mod=Modified:
+info_link=Link to:
+info_perms=Permissions
+info_user=User:
+info_group=Group:
+info_other=Other:
+info_octal=Octal:
+info_sticky=Sticky:
+info_sticky2=Only owners can delete files
+info_own=Ownership
+info_setuid=Setuid:
+info_setuid2=Execute as user
+info_setgid=Setgid:
+info_setgid2=Files inherit group
+info_setgid3=Execute as group
+info_apply=Apply changes to
+info_apply1=This directory only
+info_apply2=This directory and its files
+info_apply3=This directory and all subdirectories
+info_efailed=Failed to update $1 : $2
+info_read=Read
+info_write=Write
+info_list=List
+info_exec=Exec
+info_sizeheader=Directory size
+info_bytes=Total bytes:
+info_files=Total files:
+info_dirs=Total directories:
+info_getsize=Get Size
+info_esize=Failed to get sizes : $1
+
+eacl_eacls=Failed to read ACLs : $1
+eacl_acltype=ACL Type
+eacl_aclname=Apply to
+eacl_aclperms=Permissions
+eacl_add=Add ACL of type :
+eacl_remove=Remove ACL
+eacl_efs=The filesystem $1 does not support ACLs
+eacl_create=Create ACL
+eacl_edit=Edit ACL
+eacl_user=File owner $1
+eacl_group=File group $1
+eacl_eowner=Missing user or group to apply to
+eacl_efailed=Failed to set ACL for $1 : $2
+eacl_emask=There can be at most one mask ACL entry
+eacl_edefmask=There can be at most one default mask ACL entry
+eacl_title=ACL for $1
+eacl_owner=File owner
+eacl_edefaults=If a file has any default ACL, it must have default user, group and other ACLs.
+
+acltype_user=User
+acltype_group=Group
+acltype_other=Others
+acltype_mask=Mask
+acltype_default_user=Default User
+acltype_default_group=Default Group
+acltype_default_other=Default Others
+acltype_default_mask=Default Mask
+
+delete_mtitle=Delete multiple files
+delete_dtitle=Delete directory
+delete_ftitle=Delete file
+delete_ddesc=Are you sure you want to permanently delete the directory $1 and all its contents?
+delete_fdesc=Are you sure you want to permanently delete the file $1 ?
+delete_mdesc=Are you sure you want to permanently delete these files and directories? :
+delete_efailed=Failed to delete $1 : $2
+
+mkdir_title=New Directory
+mkdir_dir=New directory:
+mkdir_eexists=$1 already exists
+mkdir_efailed=Create directory failed : $1
+mkdir_eaccess=You are not allowed to create '$1'
+
+link_title=Create Link
+link_from=Link from:
+link_to=Link to:
+link_eexists=$1 already exists
+link_efrom=Link source must be an absolute path
+link_efailed=Link failed : $1
+link_efrom2=You are not allowed to link from '$1'
+link_efollow=You are not allowed to create symlinks
+
+rename_title=Rename $1
+rename_old=Old name:
+rename_new=New name:
+rename_ok=Rename
+rename_eexists=A file called $1 already exists
+rename_eslash=The new file name $1 contains a /
+rename_efailed=Rename failed : $1
+rename_eold=You are not allowed to rename '$1'
+rename_enew=You are not allowed to rename to '$1'
+
+file_type0=Directory
+file_type1=Text file
+file_type2=Image file
+file_type3=Binary file
+file_type4=File
+file_type5=Symbolic link
+file_type6=Device file
+file_type7=Pipe
+
+view_enormal=Only normal files can be viewed
+view_enormal2=Only normal files can be downloaded
+view_eaccess=You are not allowed to access $1
+view_eopen=Failed to open $1 : $2
+view_edir=An archive can only be created for a directory
+view_ecmd=The command $1 needed to create an archive is not installed
+view_ecomp=Failed to create archive : $1
+view_earchive=You are not allowed to download archives
+view_earchmax=The selected directory is larger than the maximum allowed for archiving ($1 bytes)
+
+paste_ecopy=You must cut or copy before pasting
+paste_egone=Copied file $1 no longer exists
+paste_eover=$1 cannot be overwritten
+paste_eself=You cannot paste a file over itself
+paste_emfailed=Move failed : $1
+paste_ecfailed=Copy failed : $1
+
+over_title=File Exists
+over_msg=The file $1 already exists. Use the field below to enter a new filename for the pasted file.
+over_new=New filename:
+over_ok=Ok
+
+upload_efailed=Failed to open upload : $1
+upload_title=Upload File
+upload_file=File to upload
+upload_dir=Upload to directory
+upload_ok=Upload
+upload_conv=Convert DOS newlines?
+upload_efile=No file selected to upload.
+upload_edir=Upload directory does not exist.
+upload_eperm=You are not allowed to create $1
+upload_ewrite=Failed to write to $1 : $2.
+upload_already=The file $1 already exists. Are you sure that you want to overwrite it?
+upload_elink=Cannot upload to a symbolic link
+upload_zip=Uncompress ZIP or TAR file?
+upload_yes=Yes, then delete
+
+find_eaccess=You are not allowed to access $1
+find_eexist=$1 does not exist in $2
+find_edir=$1 is not a directory in $2
+
+cancel=Cancel
+close=Close
+eopen=Download failed : $1
+
+chmod_eaccess=You are not allowed to access '$1'
+chmod_euser=$1 : no such user
+chmod_egroup=$1 : no such group
+chmod_elink=symlink failed : $1
+chmod_echown=chown failed : $1
+chmod_echmod=chmod failed : $1
+chmod_efollow=You are not allowed to edit symbolic links
+
+copy_efrom=You are not allowed to copy from '$1'
+copy_eto=You are not allowed to copy to '$1'
+copy_elink=symlink failed : $1
+
+delete_eaccess=You are not allowed to delete '$1'
+
+list_eaccess=You are not allowed to access this directory
+list_edir=Failed to list $1 : $2
+
+move_eto=You are not allowed to move to '$1'
+move_afrom=You are not allowed to move '$1'
+
+acl_user=Access files on server as user
+acl_user_def=Same as Webmin login
+acl_umask=Umask for new files
+acl_follow=Always follow symlinks?
+acl_fyes=If owners match
+acl_ro=Read-only mode?
+acl_dirs=Only allow access to directories
+acl_nodirs=Deny access to directories
+acl_home=Include home directory of Webmin user
+acl_log=Log all file modifications?
+acl_goto=Open first allowed directory?
+acl_max=Maximum upload size
+acl_unlim=Unlimited
+acl_b=bytes
+acl_archive=Can download archives of directories?
+acl_archmax=Yes, if smaller than
+acl_buttons=Available buttons on toolbar
+acl_button_save=Save (download file)
+acl_button_preview=Preview (view scaled-down image)
+acl_button_edit=Edit (edit text file)
+acl_button_info=Info (edit file permissions and ownership)
+acl_button_acl=ACL (edit Posix ACL)
+acl_button_attr=Attr (edit XFS attributes)
+acl_button_ext=EXT (edit EXT attributes)
+acl_button_search=Find (find files)
+acl_button_delete=Delete (delete files)
+acl_button_new=New (create text file)
+acl_button_upload=Upload (upload file from client)
+acl_button_mkdir=New (create directory)
+acl_button_makelink=New (create symbolic link)
+acl_button_rename=Rename (rename file)
+acl_button_sharing=Sharing (setup Samba and NFS file sharing)
+acl_button_mount=Mount (mount or un-mount filesystem)
+acl_button_copy=Copy, Cut and Paste
+acl_unarchive=Can extract uploaded archive files?
+acl_unarchive2=Always attempt to
+acl_unarchive1=$yes
+acl_unarchive0=$no
+acl_dostounix=Can have DOS newlines converted?
+acl_chroot=Chroot directory for entire file manager
+acl_relto=(relative to any chroot directory)
+acl_noperms=Can change file permissions?
+acl_nousers=Can change file ownership?
+acl_filesystems=Can see filesystem mount points?
+acl_contents=Allow searching of file contents?
+
+share_title=Sharing
+share_samba=Windows
+share_nfs=NFS
+share_son=Windows file sharing enabled
+share_soff=Windows file sharing disabled
+share_writable=Writable?
+share_available=Currently active?
+share_sheader=Sharing options
+share_only=Only
+share_guest=Guest access?
+share_comment=Comment
+share_nheader=NFS export options
+share_non=NFS file sharing enabled
+share_noff=NFS file sharing disabled
+share_desc=Description
+share_ro=Read-only hosts
+share_rw=Read-write hosts
+share_root=Root access hosts
+share_none=None
+share_all=All
+share_listed=Listed..
+share_host=Hosts
+share_opts=Options
+share_s0=Trust nobody
+share_s1=Trust non-root
+share_s2=Trust everybody
+share_lro=Read-only
+share_lrw=Read-write
+
+log_create_export=Created NFS export $1
+log_modify_export=Modified NFS export $1
+log_delete_export=Deleted NFS export $1
+log_create_share=Created Samba share $1
+log_modify_share=Modified Samba share $1
+log_delete_share=Deleted Samba share $1
+log_save=Saved file $1
+log_chmod=Changed permissions on file $1
+log_mkdir=Created directory $1
+log_upload=Uploaded file $1
+log_link=Created symbolic link $1 to $2
+log_relink=Modified symbolic link $1 to $2
+log_copy=Copied file $1 to $2
+log_move=Moved file $1 to $2
+log_delete=Deleted file $1
+log_attr=Set attributes on file $1
+log_acl=Set ACL on file $1
+
+search_eaccess=You are not allowed to search this directory
+search_title=Find files
+search_ok=Search Now
+search_dir=Search directory
+search_match=For files matching
+search_cont=Containing text
+search_user=Owned by user
+search_group=Owned by group
+search_any=Any
+search_type=File type
+search_types_=Any
+search_types_f=File
+search_types_d=Directory
+search_types_l=Symbolic link
+search_types_p=Named pipe
+search_size=File size
+search_more=More than
+search_less=Less than
+search_xdev=Search past mounts?
+search_edir=Missing or invalid search directory
+search_ematch=Missing matching regexp
+search_euser=Missing username
+search_egroup=Missing group name
+search_esize=File size must be an integer
+search_crit=Search criteria
+search_list=Search results
+search_down=Download
+search_edown=No search result file to download selected
+
+facl_eaccess=You are not allowed to set ACLs for this file
+
+attr_eattrs=Failed to get attributes : $1
+attr_efs=The filesystem $1 does not support attributes
+attr_add=Add Attribute
+attr_name=Attribute Name
+attr_value=Attribute Value
+attr_efailed=Failed to set attributes for $1 : $2
+attr_title=File Attributes for $1
+attr_create=Add Attribute
+attr_edit=Edit Attribute
+attr_ename=Missing attribute name
+
+ext_eattrs=Failed to get EXT attributes : $1
+ext_efs=The filesystem $1 does not support EXT attributes
+ext_title=EXT attributes for $1
+ext_header=EXT file attributes
+ext_efailed=Failed to set attributes for $1 : $2
+
+eattr_A=Do not update access times
+eattr_a=Can only append to file
+eattr_c=Compress data on disk
+eattr_d=Do not backup with dump
+eattr_i=Do not allow modification
+eattr_s=Zero blocks when deleting
+eattr_S=Always sync after writing
+eattr_u=Save contents for undeletion
+
+mount_eaccess=You are not allowed to mount filesystems
+mount_efstab=No filesystem exists at this mount point
+mount_epoint=$1 is not a mount point
+mount_rusure1=Are you sure you want to mount $1 from $2 ?
+mount_rusure2=Are you sure you want to un-mount $1 from $2 ?
+mount_err1=Failed to mount $1 : $2
+mount_err2=Failed to un-mount $1 : $2
+mount_title1=Mount filesystem
+mount_title2=Un-mount filesystem
+
+zip_err=Could not extract file : $1
+zip_ecmd=Missing $1 command
+zip_eunzip=Unzip failed : $1
+zip_ename=Does not appear to be a zip, tar or tar.gz file
+zip_euntar=Un-tar failed : $1
+zip_euntar2=Un-compress and un-tar failed : $1
+
+ddir_title=Download Directory
+ddir_rusure=To download the contents of $1 as an archive file, click on one of the archive type buttons below.
+ddir_zip=ZIP
+ddir_tgz=TAR.GZ
+ddir_tar=TAR
+
+ebutton=This feature is not available
+
+preview_etype=Could not work out file type for $1
+preview_etype2=$1 is not in a supported image format
+preview_ecmd=The command $1 needed to scale this image is not installed
+preview_eimage=Only images can be previewed
+preview_title=Preview of $1
+preview_bad=Could not load image to preview
+preview_egd=Failed to load image with GD
+
+html_efailed=Failed to open HTML editor : $1
+html_title=Edit HTML File
+html_title2=Create HTML File
+html_save=Save and Close
+html_err=Failed to save HTML file
+
+history_title=Path History
+history_ok=Go To
+history_button=History
+
+extract_etype=Only files can be extracted
+extract_title=Extract Compressed File
+extract_rusure=Are you sure you want to extract the compressed file :
+extract_rusure2=Existing files in the same directory may be overwritten.
+extract_err=Failed to extract file : $1
+extract_yes=Yes, then delete
diff --git a/file/lang/es b/file/lang/es
new file mode 100644 (file)
index 0000000..916e67a
--- /dev/null
@@ -0,0 +1,345 @@
+acl_archive=&#191;Puede descargar archivos de directorios?
+acl_archmax=S&#237;, si son menores que
+acl_b=bytes
+acl_button_acl=ACL (editar ACL Posix)
+acl_button_attr=Attr (editar atributos XFS)
+acl_button_copy=Copiar, Cortar y Pegar
+acl_button_delete=Borrar (borrar archivos)
+acl_button_edit=Editar (editar archivo texto)
+acl_button_ext=EXT (editar atributos EXT)
+acl_button_info=Info (editar permisos y propiedad de archivo)
+acl_button_makelink=Nuevo (crear link simb&#243;lico)
+acl_button_mkdir=Nuevo (crear directorio)
+acl_button_mount=Montar (montar o desmontar sistema de archivos)
+acl_button_new=Nuevo (crear archivo de texto)
+acl_button_rename=Renombrar (renombrar archivo)
+acl_button_save=Guardar (descargar archivo)
+acl_button_search=Buscar (buscar archivos)
+acl_button_sharing=Compartir (configurar compartici&#243;n de archivo por Samba y NFS)
+acl_button_upload=Subir (subir archivo desde cliente)
+acl_buttons=Botones disponibles en la barra de herramientas
+acl_chroot=Cambiar directorio raiz (chroot) para todo el explorador de archivos
+acl_dirs=Permitir acceso solo a los directorios
+acl_dostounix=&#191;Se le pueden convertir las nuevas l&#237;neas de DOS?
+acl_follow=&#191;Seguir siempre los v&#237;nculos simb&#243;licos?
+acl_fyes=Si los propietarios coinciden
+acl_goto=&#191;Abrir el primer directorio permitido?
+acl_home=Incluir directorio de inicio del usuario Webmin
+acl_log=&#191;Registrar todas las modificaciones de archivos?
+acl_max=M&#225;ximo tama&#241;o de subida
+acl_nodirs=Denegar acceso a directorios
+acl_relto=(relativo a cualquier directorio raiz)
+acl_ro=&#191;Modo de solo lectura?
+acl_umask=M&#225;scara de Usuario para Nuevos archivos
+acl_unarchive=&#191;Puede extraer archivos subidos?
+acl_unarchive0=$no
+acl_unarchive1=$s&#237;
+acl_unarchive2=Siempre intentar
+acl_unlim=Ilimitado
+acl_user=Acceder a archivos en el servidor como usuario
+acl_user_def=Igual que el nombre de ingreso de Webmin
+acltype_default_group=Grupo por defecto
+acltype_default_mask=M&#225;scara por defecto
+acltype_default_other=Otros por defecto
+acltype_default_user=Usuario por defecto
+acltype_group=Grupo
+acltype_mask=M&#225;scara
+acltype_other=Otros
+acltype_user=Usuario
+attr_add=Agregar Atributo
+attr_create=Agregar atributo
+attr_eattrs=Fallo al obtener atributos : $1
+attr_edit=Editar atributo
+attr_efailed=Fallo al configurar atributo para $1 : $2
+attr_efs=El sistema de archivos $1 no soporta atributos
+attr_ename=Nombre de atributo no ingresado
+attr_name=Nombre del atributo
+attr_title=Atributos de archivo para $1
+attr_value=Valor del atributo
+cancel=Cancelar
+chmod_eaccess=No est&#225; autorizado a acceder a '$1'
+chmod_echmod=fallo al cambiar modo : $1
+chmod_echown=fallo al cambiar due&#241;o : $1
+chmod_efollow=No est&#225; autorizado a editar v&#237;nculos simb&#243;licos
+chmod_egroup=$1 : no existe dicho grupo
+chmod_elink=fallo en v&#237;nculo simb&#243;lico : $1
+chmod_euser=$1 : no existe dicho usuario
+close=Cerrar
+copy_efrom=No est&#225; autorizado a copiar desde '$1'
+copy_elink=fallo en v&#237;nculo simb&#243;lico : $1
+copy_eto=No est&#225; autorizado a copiar a '$1'
+ddir_rusure=Para descargar los contenidos de $1 como un archivo de ficheros, elija abajo uno de los botones de tipo de archivo.
+ddir_tar=TAR
+ddir_tgz=TAR.GZ
+ddir_title=Descargar Directorio
+ddir_zip=ZIP
+delete_ddesc=&#191;Est&#225; seguro que quiere borrar permanentemente el directorio $1 y todo su contenido?
+delete_dtitle=Borrar directorio
+delete_eaccess=No est&#225; autorizado a borrar '$1'
+delete_efailed=Fallo al borrar $1 : $2
+delete_fdesc=&#191;Est&#225; seguro que quiere borrar permanentemente el archivo $1 ?
+delete_ftitle=Borrar archivo
+delete_mdesc=&#191;Est&#225; seguro que quiere borrar permanentemente estos archivos y directorios? :
+delete_mtitle=Borrar m&#250;ltiples archivos
+eacl_aclname=Aplicar a
+eacl_aclperms=Permisos
+eacl_acltype=Tipo de ACL
+eacl_add=Agregar ACL de tipo :
+eacl_create=Crear ACL
+eacl_eacls=Fallo al leer ACLs : $1
+eacl_edefaults=si un archivo tiene alguna ACL por defecto, debe tener usuario, grupo y otras ACL por defecto.
+eacl_edefmask=Puede haber a lo sumo una entrada de m&#225;scara de ACL por defecto
+eacl_edit=Editar ACL
+eacl_efailed=Fallo al asignar ACL para $1 : $2
+eacl_efs=El sistema de archivos $1 no soporta ACLs
+eacl_emask=Puede haber a lo sumo una entrada de m&#225;scara de ACL
+eacl_eowner=Usuario o grupo a aplicar no ingresado
+eacl_group=Grupo de archivo $1
+eacl_owner=Due&#241;o del archivo
+eacl_remove=Borrar ACL
+eacl_title=ACL para $1
+eacl_user=Due&#241;o de archivo $1
+eattr_A=No actualizar tiempos de acceso
+eattr_S=Siempre sincronizar despues de escribir
+eattr_a=Solo puede agregar a archivo
+eattr_c=Comprimir datos en el disco
+eattr_d=No respaldar con volcado
+eattr_i=No permitir modificaciones
+eattr_s=Poner a cero los bloques a borrar
+eattr_u=Salvar contenido para recuperaci&#243;n despu&#233;s de borrado
+ebutton=Esta opci&#243;n no est&#225; disponible
+edit_all=Reemplazar todo
+edit_eaccess=No est&#225; autorizado a salvar '$1'
+edit_efollow=No est&#225; autorizado a escribir en el v&#237;nculo simb&#243;lico '$1'
+edit_elength=&#161;El archivo se trunc&#243;!
+edit_enormal=Solo los archivos normales pueden ser editados
+edit_eover=$1 no puede ser sobreescrito
+edit_esave=Fallo al salvar archivo : $1
+edit_filename=Nombre de archivo:
+edit_find=Buscar
+edit_goto=Ir a
+edit_gotoline=Ir a l&#237;nea
+edit_notfound=El texto $1 no fue encontrado
+edit_replace=Reemplazar
+edit_replaceby=Reemplazar por
+edit_saveclose=Salvar y Cerrar
+edit_searchfor=Buscar
+edit_title=Editando $1
+edit_title2=Creando archivo
+eopen=Descarga fallida: $1
+ext_eattrs=Fallo al obtener atributos EXT : $1
+ext_efailed=Fallo al configurar atributos para $1 : $2
+ext_efs=El sistema de archivos $1 no soporta atributos EXT
+ext_header=Atributos EXT de archivo
+ext_title=Atributos EXT para $1
+facl_eaccess=No est&#225; autorizado a configurar ACLs para este archivo
+file_type0=Directorio
+file_type1=Archivo de texto
+file_type2=Archivo de im&#225;gen
+file_type3=Archivo binario
+file_type4=Archivo
+file_type5=V&#237;nculo simb&#243;lico
+file_type6=Archivo de dispositivo
+file_type7=Tuber&#237;a
+find_eaccess=No est&#225; autorizado a acceder a $1
+find_edir=$1 no es un directorio en $2
+find_eexist=$1 no existe en $2
+index_eremote=No hay ning&#250;n usuario Unix que coincida con el usuario de ingreso de Webmin $1.
+index_nojava=Este m&#243;dulo requiere java para funcionar, pero su navegador no soporta java
+index_title=Administrador de Archivos
+info_apply=Aplicar cambios a
+info_apply1=Este directorio solamente
+info_apply2=Este directorio y sus archivos
+info_apply3=Este directorio y todos sus subdirectorios
+info_efailed=Fallo al actualizar $1 : $2
+info_exec=Ejecutar
+info_file=Archivo
+info_group=Grupo:
+info_link=V&#237;nculo a:
+info_list=Listar
+info_mod=Modificado:
+info_octal=Octal:
+info_other=Otro:
+info_own=Propiedad
+info_path=Ruta:
+info_perms=Permisos
+info_read=Leer
+info_setgid=Ingresar ID de grupo:
+info_setgid2=Archivos heredan grupo
+info_setgid3=Ejecutar como grupo
+info_setuid=Ingresar ID de usuario:
+info_setuid2=Ejecutar como usuario
+info_size=Tama&#241;o:
+info_sticky=Restricci&#243;n:
+info_sticky2=Solo los due&#241;os pueden borrar archivos
+info_type=Tipo:
+info_user=Usuario:
+info_write=Escribir
+link_eexists=$1 ya existe
+link_efailed=Fallo v&#237;nculo : $1
+link_efollow=No est&#225; autorizado a crear v&#237;nculos simb&#243;licos
+link_efrom=No est&#225; autorizado a vincular desde '$1'
+link_efrom2=No tiene permiso para linkar desde '$1'
+link_from=Vincular desde:
+link_title=Crear V&#237;nculo
+link_to=Vincular hacia:
+list_eaccess=No est&#225; autorizado a acceder a este directorio
+list_edir=Fallo al listar $1 : $2
+log_acl=Configurado ACL sobre archivo $1
+log_attr=Configurados atributos en archivo $1
+log_chmod=Permisos cambiados en el archivo $1
+log_copy=Copiado archivo $1 a $2
+log_create_export=Creada exportaci&#243;n NFS $1
+log_create_share=Creada compartici&#243;n Samba $1
+log_delete=Borrado archivo $1
+log_delete_export=Borrada exportaci&#243;n NFS $1
+log_delete_share=Borrada compartici&#243;n Samba $1
+log_link=Creado v&#237;nculo simb&#243;lico $1 a $2
+log_mkdir=Creado directorio $1
+log_modify_export=Modificada exportaci&#243;n NFS $1
+log_modify_share=Modificada compartici&#243;n Samba $1
+log_move=Movido archivo $1 a $2
+log_relink=Modificado v&#237;nculo simb&#243;lico $1 a $2
+log_save=Salvado archivo $1
+log_upload=Cargado archivo $1
+mkdir_dir=Nuevo directorio:
+mkdir_eaccess=No est&#225; autorizado a crear '$1'
+mkdir_eexists=$1 ya existe
+mkdir_efailed=Fallo al crear directorio : $1
+mkdir_title=Nuevo Directorio
+mount_eaccess=No est&#225; autorizado a montar sistemas de archivos
+mount_efstab=No existe sistema de archivo en este punto de montaje
+mount_epoint=$1 no es un punto de montaje
+mount_err1=Fallo al montar $1 : $2
+mount_err2=Fallo al desmontar $1 : F2
+mount_rusure1=&#191;Est&#225; seguro que desea montar $1 desde $2 ?
+mount_rusure2=&#191;Est&#225; seguro que desea desmontar $1 desde $2 ?
+mount_title1=Montar sistema de archivos
+mount_title2=Desmontar sistema de archivos
+move_afrom=No est&#225; autorizado a mover '$1'
+move_eto=No est&#225; autorizado a mover a '$1'
+over_msg=El archivo $1 ya existe. Use el campo de abajo para ingresar un nuevo nombre de archivo para el archivo pegado.
+over_new=Nuevo nombre de archivo:
+over_ok=Ok
+over_title=Archivo existe
+paste_ecfailed=Fallo al copiar : $1
+paste_ecopy=Debe cortar o copiar antes de pegar
+paste_egone=El archivo copiado $1 ya no existe
+paste_emfailed=Fallo al mover : $1
+paste_eover=$1 no puede ser sobreescrito
+paste_eself=No puede pegar un archivo sobre si mismo
+rename_eexists=Ya existe un archivo llamado $1
+rename_efailed=Fallo al renombrar : $1
+rename_enew=No est&#225; autorizado a renombrar a '$1'
+rename_eold=No est&#225; autorizado a renombrar '$1'
+rename_new=Nombre nuevo:
+rename_ok=Renombrar
+rename_old=Nombre anterior:
+rename_title=Renombrar $1
+right_date=Fecha
+right_group=Grupo
+right_name=Nombre
+right_size=Tama&#241;o
+right_user=Usuario
+search_any=Cualquiera
+search_crit=Criterio de b&#250;squeda
+search_dir=Buscar directorio
+search_eaccess=No est&#225; autorizado a buscar este directorio
+search_edir=Directorio de b&#250;squeda no ingresado o no v&#225;lido
+search_egroup=Nombre de grupo no ingresado
+search_ematch=Expresi&#243;n de coincidencia no ingresada
+search_esize=El tama&#241;o del archivo debe ser un entero
+search_euser=Nombre de usuario no ingresado
+search_group=Perteneciente al grupo
+search_less=Menos que
+search_list=Resultados de la b&#250;squeda
+search_match=Para archivos que coincidan
+search_more=Mas que
+search_ok=Buscar Ahora
+search_size=Tama&#241;o de archivo
+search_title=Buscar archivos
+search_type=Tipo de archivo
+search_types_=Cualquiera
+search_types_d=Directorio
+search_types_f=Archivo
+search_types_l=V&#237;nculo Simb&#243;lico
+search_types_p=Tuber&#237;a nombrada
+search_user=Perteneciente al usuario
+search_xdev=&#191;Buscar en montajes anteriores?
+share_all=Todos
+share_available=&#191;Actualmente activo?
+share_comment=Comentario
+share_desc=Descripci&#243;n
+share_guest=&#191;Acceso como invitado?
+share_host=M&#225;quinas
+share_listed=Listado..
+share_lro=Solo lectura
+share_lrw=Lectura-escritura
+share_nfs=NFS
+share_nheader=Opciones de exportaci&#243;n NFS
+share_noff=Compartir archivos NFS deshabilitado
+share_non=Compartir archivos NFS habilitado
+share_none=Ninguno
+share_only=Solo
+share_opts=Opciones
+share_ro=M&#225;quina de solo lectura
+share_root=M&#225;quinas de acceso ra&#237;z
+share_rw=M&#225;quina de lectura-escritura
+share_s0=No confiar en nadie
+share_s1=Confiar en aquellos que no sean ra&#237;z
+share_s2=Confiar en todos
+share_samba=Windows
+share_sheader=Opciones para compartir
+share_soff=Deshabilitar compartir archivos de Windows
+share_son=Habilitar compartir archivos de Windows
+share_title=Compartir
+share_writable=&#191;Editable?
+switch_euser=&#161;El usuario Unix no existe!
+top_attr=Atributos
+top_config=Configurar
+top_copy=Copiar
+top_cut=Cortar
+top_delete=Borrar
+top_down=Salvar
+top_eacl=ACL
+top_edit=Editar
+top_ext=EXT
+top_info=Informaci&#243;n
+top_mount=Montar
+top_new=Nuevo
+top_paste=Pegar
+top_refresh=Refrescar
+top_rename=Renombrar
+top_ret=Indice
+top_search=Buscar
+top_share=Compartir
+top_upload=Cargar
+upload_already=El archivo $1 ya existe. &#191;Est&#225; seguro que desea sobreescribirlo?
+upload_conv=&#191;Convertir nuevas l&#237;neas de DOS?
+upload_dir=Cargar a directorio
+upload_edir=El directorio para cargar no existe.
+upload_efailed=Fallo al abrir carga : $1
+upload_efile=No se seleccion&#243; ning&#250;n archivo para cargar.
+upload_elink=No se puede cargar a un v&#237;nculo simb&#243;lico
+upload_eperm=No est&#225; autorizado a crear $1
+upload_ewrite=Fallo al escribir a $1 : $2.
+upload_file=Archivo para cargar
+upload_ok=Cargar
+upload_title=Cargar Archivos
+upload_yes=Si, despu&#233;s borrar
+upload_zip=&#191;Descomprimir archivo ZIP o TAR?
+view_eaccess=No est&#225; autorizado a acceder a $1
+view_earchive=No tiene permiso para descargar archivos
+view_earchmax=El directorio elegido es mayor que el m&#225;ximo permitido para archivar ($1 bytes)
+view_ecmd=El comando $1, necesario para crear un archivo no est&#225; instalado
+view_ecomp=Fall&#243; la creaci&#243;n de archivo: $1
+view_edir=Un archivo s&#243;lo puede ser creado en un directorio
+view_enormal=Solo los archivos normales pueden ser visualizados
+view_enormal2=Solo los archivos normales pueden ser descargados
+view_eopen=Fallo al abrir $1 : $2
+zip_ecmd=Comando $1 no introducido
+zip_ename=No parece ser un archivo zip, tar o tar.gz v&#225;lido
+zip_err=No se pudo extraer archivo : $1
+zip_euntar=Extracci&#243;n tar fallida : $1
+zip_euntar2=Descompresi&#243;n y extracci&#243;n tar fallida : $1
+zip_eunzip=Extracci&#243;n zip fallida : $1
diff --git a/file/lang/fa b/file/lang/fa
new file mode 100644 (file)
index 0000000..27e2f43
--- /dev/null
@@ -0,0 +1,383 @@
+\r
+index_title=مدير پرونده\r
+index_nojava=اين پيمانه جهت اجرا شدن نياز به جاوا دارد اما مرورگر شمااز جاوا پشتيباني نمي‌کند.\r
+index_eremote=کاربر يونيکسي که با $1 جهت ورود به وب‌مين مطابقت کند وجود ندارد.\r
+\r
+switch_euser=کاربر يونيکس وجود ندارد!\r
+\r
+top_ret=شاخص\r
+top_down=ذخيره\r
+top_edit=ويرايش\r
+top_refresh=بازآوري\r
+top_info=اطلاعات\r
+top_eacl=ACL\r
+top_attr=Attrs\r
+top_ext=EXT\r
+top_delete=حذف\r
+top_new=جديد\r
+top_upload=بارگيري\r
+top_rename=تغيير نام\r
+top_copy=رونوشت\r
+top_cut=بريدن\r
+top_paste=چسباندن\r
+top_share=اشتراک گذاري\r
+top_mount=سوار کردن\r
+top_search=يافتن\r
+top_config=پيکربندي\r
+\r
+right_name=نام\r
+right_size=اندازه\r
+right_user=کاربر\r
+right_group=گروه\r
+right_date=تاريخ\r
+\r
+edit_enormal=فقط پرونده‌هاي عادي قابل ويرايش مي‌باشند\r
+edit_title=در حال ويرايش $1\r
+edit_title2=در حال ايجاد کردن پرونده\r
+edit_filename=نام پرونده: \r
+edit_goto=برو به\r
+edit_find=يافتن\r
+edit_gotoline=برو به خط\r
+edit_replace=جايگزين کردن\r
+edit_all=جايگزين کردن همه\r
+edit_searchfor=جستجو براي\r
+edit_replaceby=جايگزين کردن با\r
+edit_eover=$1 قادر به نوشتن مجدد نمي‌باشد\r
+edit_esave=عدم موفقيت در ذخيره کردن پرونده: $1\r
+edit_eaccess=شما اجازه ذخيره کردن نداريد '$1'\r
+edit_efollow=شما اجازه نوشتن برروي اتصال نمادين '$1' را نداريد\r
+edit_notfound=متن '$1' يافت نشد\r
+edit_saveclose=ذخيره کردن و بستن\r
+edit_elength=پرونده کوتاه شده‌است\r
+\r
+info_file=پرونده\r
+info_path=مسير:\r
+info_type=نوع:\r
+info_size=اندازه:\r
+info_mod=تاريخ اصلاح:\r
+info_link=اتصال به:\r
+info_perms=مجوزها\r
+info_user=کاربر:\r
+info_group=گروه:\r
+info_other=ديگران:\r
+info_octal=مبناي هشت:\r
+info_sticky=محکم:\r
+info_sticky2=تنها مالک مي‌تواند پرونده‌ها را حذف کند\r
+info_own=مالکيت\r
+info_setUID=قراردادن UID:\r
+info_setUID2=اجرا به‌عنوان کاربر\r
+info_setgid=قراردادن GID:\r
+info_setgid2=پرونده‌ها از گروه ارث برند\r
+info_setgid3=اجرا به‌عنوان گروه\r
+info_apply=به‌کاربستن تغييرات در\r
+info_apply1=فقط اين فهرست راهنما\r
+info_apply2=اين فهرست راهنما و پرونده‌هايش\r
+info_apply3=اين فهرست راهنما و کليه زير فهرستها\r
+info_efailed=عدم موفقيت در به‌روزرساني $1\r
+info_read=خواندن\r
+info_write=نوشتن\r
+info_list=ليست\r
+info_exec=اجرا\r
+\r
+eacl_eacls=عدم موفقيت در خواندن ACLها: $1\r
+eacl_acltype=نوع ACL\r
+eacl_aclname=به‌کاربستن در\r
+eacl_aclperms=مجوزها\r
+eacl_add=اضافه کردن ACL نوع:\r
+eacl_remove=حذف ACL\r
+eacl_efs=سيستم پرونده $1 از ACLها پشتيباني نمي‌کند\r
+eacl_create=ايجاد ACL\r
+eacl_edit=ويرايش ACL\r
+eacl_user=مالک پرونده $1\r
+eacl_group=گروه پرونده $1\r
+eacl_eowner=کاربر يا گروه جهت به‌کاربستن يافت نشد\r
+eacl_efailed=عدم موفقيت در قرار دادن ACL براي $1: $2\r
+eacl_emask=حداکثر مي‌توان يک پوشش ورودي ACL داشت\r
+eacl_edefmask=حداکثر مي‌توان يک پوشش ورودي ACL پيش‌گزيده داشت\r
+eacl_title=ACL براي $1\r
+eacl_owner=مالک پرونده\r
+eacl_edefaults=اگر پرونده شامل تعدادي ACL پيش‌گزيده باشد بايد کاربر، گروه و ACLهاي پيش‌گزيده ديگري داشته باشد\r
+\r
+acltype_user=کاربر\r
+acltype_group=گروه\r
+acltype_other=ديگران\r
+acltype_mask=پوشش\r
+acltype_default_user=کاربر پيش‌گزيده\r
+acltype_default_group=گروه پيش‌گزيده\r
+acltype_default_other=ديگران پيش‌گزيده\r
+acltype_default_mask=پوشش پيش‌گزيده\r
+\r
+delete_mtitle=حذف چندين پرونده\r
+delete_dtitle=حذف فهرست راهنما\r
+delete_ftitle=حذف پرونده\r
+delete_ddesc=آيا از حذف دائم فهرست راهنماي $1 و همه محتويات آن مطمئن هستيد؟\r
+delete_fdesc=آيا از حذف پرونده $1 مطمئن هستيد؟\r
+delete_mdesc=آيا از حذف دائمي اين پرونده‌ها و فهرست راهنماها مطمئن هستيد؟\r
+delete_efailed=عدم موفقيت در حذف $1: $2\r
+\r
+mkdir_title=فهرست راهنماي جديد\r
+mkdir_dir=فهرست راهنماي جديد:\r
+mkdir_eexists=$1 از قبل وجود دارد\r
+mkdir_efailed=عدم موفقيت در ايجاد فهرست راهنماي: $1\r
+mkdir_eaccess=شما اجازه ايجاد $1 را نداريد\r
+\r
+link_title=ايجاد اتصال\r
+link_from=اتصال از:\r
+link_to=اتصال به:\r
+link_eexists=$1 از قبل وجود دارد\r
+link_efrom=منبع اتصال بايد يک مسير مطلق باشد\r
+link_efailed=عدم موفقيت در اتصال: $1\r
+link_efrom2=شما اجازه اتصال از '$1' را نداريد\r
+link_efollow=شما اجازه ايجاد اتصال نمادين را نداريد\r
+\r
+rename_title=تغيير نام $1\r
+rename_old=نام قبلي:\r
+rename_new=نام جديد:\r
+rename_ok=تغيير نام\r
+rename_eexists=پرونده‌اي با نام $1 از قبل وجود دارد\r
+rename_efailed=عدم موفقيت در تغيير نام: $1\r
+rename_eold=شما اجازه تغيير نام '$1' را نداريد\r
+rename_enew=شما اجازه تغيير نام به '$1' را نداريد\r
+\r
+file_type0=فهرست راهنما\r
+file_type1=پرونده متني\r
+file_type2=پرونده تصويري\r
+file_type3=پرونده دودويي\r
+file_type4=پرونده\r
+file_type5=اتصال نمادين\r
+file_type6=پرونده دستگاه\r
+file_type7=لوله\r
+\r
+view_enormal=فقط پرونده‌هاي متني را مي‌توان ديد\r
+view_enormal2=فقط پرونده‌هاي معمولي را مي‌توان بار کرد\r
+view_eaccess=شما اجازه دستيابي به $1 را نداريد\r
+view_eopen=عدم موفقيت در باز کردن $1:$2\r
+view_edir=ايجاد بايگاني تنها براي فهرست راهنماهاي امکان پذير مي‌باشد\r
+view_ecmd=دستور $1 که براي ايجاد بايگاني لازم است نصب نشده‌است\r
+view_ecomp=عدم موفقيت در ايجاد بايگاني: $1\r
+view_earchive=شما اجازه بارکردن بايگانيها را نداريد\r
+view_earchmax=فهرست راهنماي انتخاب شده بزرگ‌تر از بيشترين مقداري است که براي بايگاني در نظر گرفته شده‌است ($1 بايت)\r
+\r
+paste_ecopy=شما قبل از چسباندن بايد ببريد يا رونوشت نمائيد\r
+paste_egone=پرونده رونوشت شده $1 وجود ندارد\r
+paste_eover=$1 قابل باز نويسي نمي‌باشد\r
+paste_eself=شما نمي‌توانيد يک پرونده را برروي خودش بچسبانيد\r
+paste_emfailed=عدم موفقيت در انتقال: $1\r
+paste_ecfailed=عدم موفقيت در رونوشت: $1\r
+\r
+over_title=پرونده وجود دارد\r
+over_msg=پرونده $1 از قبل وجود دارد از حوزه زير جهت وارد کردن نام جديد براي پرونده چسبانده شده‌استفاده نمائيد\r
+over_new=نام پرونده جديد:\r
+over_ok=تاييد\r
+\r
+upload_efailed=عدم موفقيت در باز کردن بارگيري شده: $1\r
+upload_title=بارگيري پرونده\r
+upload_file=پرونده جهت بارگيري\r
+upload_dir=بارگيري به فهرست راهنما\r
+upload_ok=بارگيري\r
+upload_conv=آيا خطوط جديد DOS تبديل شوند؟\r
+upload_efile=پرونده‌اي براي بارگيري انتخاب نشده\r
+upload_edir=فهرست راهنماي مقصد بارگيري موجود نيست\r
+upload_eperm=شما اجازه ايجاد $1 را نداريد\r
+upload_ewrite=عدم موفقيت در نوشتن درون $1:$2\r
+upload_already=پرونده $1 از قبل وجود دارد از باز نويسي روي آن مطمئن هستيد\r
+upload_elink=نمي‌توان به يک اتصال نمادين بارگيري نمود\r
+upload_zip=آيا پرونده‌هاي tarيا zip از فشردگي خارج شوند؟\r
+upload_yes=بله و سپس حذف شود\r
+\r
+find_eaccess=شما اجازه دستيابي به $1 را نداريد\r
+find_eexist=$1 درون $2 وجود ندارد\r
+find_edir=$1 يک فهرست راهنما در $2 نيست\r
+\r
+cancel=لغو\r
+close=بستن\r
+eopen=عدم موفقيت در بارگيري کردن: $1\r
+\r
+chmod_eaccess=شما اجازه دستيابي به '$1' را نداريد\r
+chmod_euser=$1: چنين کاربري وجود ندارد\r
+chmod_egroup=$1: چنين گروهي وجود ندارد\r
+chmod_elink=عدم موفقيت در اتصال نمادين\r
+chmod_echown=عدم موفقيت در تغيير مالکيت: $1\r
+chmod_echmod=عدم موفقيت در تغيير chmod: $1\r
+chmod_efollow=شما اجازه اجازه ويرايش اتصالهاي نمادين را نداريد\r
+\r
+copy_efrom=شما اجازه اجازه رونوشت برداشتن از $1 را نداريد\r
+copy_eto=شما اجازه اجازه رونويسي در $1 را نداريد\r
+copy_elink=عدم موفقيت در اتصال نمادين: $1\r
+\r
+delete_eaccess=شما اجازه اجازه حذف $1 را نداريد\r
+\r
+list_eaccess=شما اجازه اجازه دستيابي به اين فهرست راهنما را نداريد\r
+list_edir=عدم موفقيت در ليست کردن$1: $2\r
+\r
+move_eto=شما اجازه اجازه منتقل کردن به $1 را نداريد\r
+move_afrom=شما اجازه اجازه انتقال '$1' را نداريد\r
+\r
+acl_user=دستيابي به پرونده برروي کارساز به‌عنوان کاربر\r
+acl_user_def=مانند وب‌مين\r
+acl_umask=پوشش براي پرونده‌هاي جديد\r
+acl_follow=آيا اتصالهاي نمادين دنبال شوند؟\r
+acl_fyes=اگر صاحبان آن يکسان باشند\r
+acl_ro=آيا حالت فقط خواندني است؟\r
+acl_dirs=فقط اجازه دستيابي به اين فهرست (هاي) راهنما داه شود\r
+acl_nodirs=دستيابي به فهرست (هاي) راهنما ممنوع شود\r
+acl_home=به اضافه فهرست شخصي کاربر وب‌مين\r
+acl_log=آيا کليه اصلاحات پرونده‌ها ثبت شود؟\r
+acl_goto=اولين فهرست راهنماي اجازه داده شده باز شود؟\r
+acl_max=بيشينه اندازه بار گذاري\r
+acl_unlim=نامحدود\r
+acl_b=بايت\r
+acl_archive=آيا مي‌توان بايگانيهاي فهرستهاي راهنما را بار کرد؟\r
+acl_archmax=بله، اگر کوچک‌تر است از:\r
+acl_buttons=دگمه‌هاي قابل دستيابي در نوار ابزار\r
+acl_button_save=ذخيره(بار کردن پرونده)\r
+acl_button_edit=ويرايش(ويرايش پرونده‌هاي متني)\r
+acl_button_info=اطلاعات (ويرايش مجوزها و مالکيت )\r
+acl_button_acl=ACL (ويرايش ACL)\r
+acl_button_attr=Attr (ويرايش خصيصه‌هاي XFS)\r
+acl_button_ext=EXT (ويرايش خصيصه‌هاي EXT )\r
+acl_button_search=يافتن (يافتن پرونده‌ها)\r
+acl_button_delete=حذف(حذف پرونده‌ها)\r
+acl_button_new=جديد(ايجاد پرونده متني)\r
+acl_button_upload=بارگيري(بارگيري پرونده‌ها توسط کارخواه)\r
+acl_button_mkdir=جديد (ايجاد فهرست راهنما)\r
+acl_button_makelink=جديد (ايجاد اتصال نمادين)\r
+acl_button_rename=تغيير نام (تغيير نام پرونده)\r
+acl_button_sharing=اشتراک گذاري(تنظيم کردن سامبا و اشتراک گذاري پرونده NFS)\r
+acl_button_mount=سوار (سوار يا پياده کردن سيستم پرونده )\r
+acl_button_copy=رونوشت٬ برش و چسباندن\r
+acl_unarchive=آيا مي‌توان پرونده‌هاي بايگاني بارگيري شده را استخراج نمود؟\r
+acl_unarchive2=هميشه سعي شود\r
+acl_unarchive1=$Yes\r
+acl_unarchive0=$No\r
+acl_dostounix=آيا مي‌توان خطوط جديد تبديل شده Dos داشت؟\r
+acl_chroot=تغيير فهرست راهنماي مدير سيستم براي مدير پرونده وارد شده:\r
+acl_relto=(وابسته به فهرست راهنماي هر مدير سيستم chroot)\r
+\r
+share_title=اشتراک گذاري\r
+share_samba=ويندوز\r
+share_nfs=NFS\r
+share_son=اشتراک گذاري پرونده ويندوز فعال شود\r
+share_soff=اشتراک گذاري پرونده ويندوز غيرفعال شود\r
+share_writable=آيا قابل نوشتن است؟\r
+share_available=آيا در حال حاضر فعال است؟\r
+share_sheader=گزينه‌هاي اشتراک گذاري\r
+share_only=فقط\r
+share_guest=آيا براي مهمان قابل دستيابي است؟\r
+share_comment=شرح\r
+share_nheader=گزينه‌هاي برون‌برد NFS\r
+share_non=اشتراک گذاري پرونده NFS فعال شود\r
+share_noff=اشتراک گذاري پرونده NFS غيرفعال شود\r
+share_desc=شرح\r
+share_ro=ميزبانهاي فقط خواندني\r
+share_rw=ميزبانهاي خواندني و نوشتني\r
+share_root=ميزبانهاي دستيابي به ريشه\r
+share_none=هيچ\r
+share_all=همه\r
+share_listed=ليست شده..\r
+share_host=ميزبانها\r
+share_opts=گزينه‌ها\r
+share_s0=هيچ کس مطمئن نيست\r
+share_s1=هيچ کس غير از root مطمئن نيست\r
+share_s2=همه مطمئن هستند\r
+share_lro=فقط خواندني\r
+share_lrw=خواندني و نوشتني\r
+\r
+log_create_export=برون‌برد $1 NFS ايجاد شد.\r
+log_modify_export=برون‌برد $1 NFS تغيير کرد.\r
+log_delete_export=برون‌برد $1 NFS حذف شد.\r
+log_create_share=اشتراک $1 سامبا ايجاد شد\r
+log_modify_share=اشتراک $1 سامبا تغيير کرد\r
+log_delete_share=اشتراک $1 سامبا حذف شد\r
+log_save=پرونده $1 ذخيره شد\r
+log_chmod=مجوزهاي پرونده $1 تغيير داده شد\r
+log_mkdir=فهرست راهنماي $1ايجاد شد\r
+log_upload=پرونده $1 بار گذاري شد\r
+log_link=اتصال نمادين از $1 به $2 ايجاد شد\r
+log_relink=اتصال نمادين از $1 به $2 تغيير کرد\r
+log_copy=از پرونده $1 در $2 رونويسي شد\r
+log_move=پرونده $1 به $2 حرکت داده شد\r
+log_delete=پرونده $1حذف شد\r
+log_attr=خصيصه‌هاي پرونده $1 قرار داده شد\r
+log_acl=ACL پرونده $1قرار داده شد\r
+\r
+search_eaccess=شما اجازه جستجو در اين فهرست راهنما را نداريد\r
+search_title=يافتن پرونده‌ها\r
+search_ok=جستجو\r
+search_dir=جستجو در فهرست راهنما\r
+search_match=براي تطبق دادن پرونده‌ها\r
+search_user=کاربر مالک\r
+search_group=گروه مالک\r
+search_any=همه\r
+search_type=نوع پرونده\r
+search_types_=همه\r
+search_types_f=پرونده\r
+search_types_d=فهرست راهنما\r
+search_types_l=اتصال نمادين\r
+search_types_p=لوله نامدار\r
+search_size=اندازه پرونده\r
+search_more=بيش از\r
+search_less=کمتر از\r
+search_xdev=آيا سوار شده‌هاي قبلي نيز جستجو شود؟\r
+search_edir=فهرست راهنماي جستجو نامعتبر است و يا يافت نشد\r
+search_ematch=عبارت با قاعده تطبيق يافت نشد\r
+search_euser=اسم‌کاربر يافت نشد\r
+search_egroup=نام گروه يافت نشد\r
+search_esize=اندازه پرونده بايد يک عدد صحيح باشد\r
+search_crit=معيارهاي جستجو\r
+search_list=نتايج جستجو\r
+\r
+facl_eaccess=شما اجازه قرار دادن ACLها براي اين پرونده را نداريد\r
+\r
+attr_eattrs=عدم موفقيت در دريافت خصيصه‌ها: $1\r
+attr_efs=سيستم پرونده $1 از خصيصه‌ها پشتيباني نمي‌کند\r
+attr_add=افزودن خصيصه\r
+attr_name=نام خصيصه\r
+attr_value=مقدار خصيصه\r
+attr_efailed=عدم موفقيت در قرار دادن خصيصه‌ها براي $1: $2\r
+attr_title=خصيصه پرونده براي $1\r
+attr_create=افزودن خصيصه\r
+attr_edit=ويرايش خصيصه\r
+attr_ename=نام خصيصه يافت نشد\r
+\r
+ext_eattrs=عدم موفقيت در دريافت خصيصه‌هايEXT: $1\r
+ext_efs=سيستم پرونده $1از خصيصه‌هاي EXT پشتيباني نمي‌کند\r
+ext_title=خصيصه‌هايEXTبراي $1\r
+ext_header=خصيصه‌هاي پروندهEXT\r
+ext_efailed=عدم موفقيت در قرار دادن خصيصه‌ها براي $1: $2\r
+\r
+eattr_A=زمانهاي دستيابي به‌روزرساني نشود\r
+eattr_a=تنها مي‌توان به پرونده‌ها اضافه کرد\r
+eattr_c=داده‌ها برروي ديسک فشرده سازي شوند\r
+eattr_d=با زباله پشتيبان تهيه نشود\r
+eattr_i=اجازه اصلاحات داده نشود\r
+eattr_s=به هنگام حذف کردن بلاکها صفر شوند\r
+eattr_S=هميشه پس از نوشتن همزمان شوند\r
+eattr_u=محتويات براي غيرحذفيها حفظ شوند\r
+\r
+mount_eaccess=شما اجازه سوار کردن سيستم پرونده را نداريد\r
+mount_efstab=هيچ سيستم پرونده‌اي در اين نقطه از سوار وجود دارد\r
+mount_epoint=$1 يک نقطه سوار نيست\r
+mount_rusure1=آيا شما مطمئن هستيد که مي‌خواهيد $1 از $2را سوار نمائيد ?\r
+mount_rusure2=آيا شما مطمئن هستيد که مي‌خواهيد $1 از $2را پياده نمائيد؟\r
+mount_err1=عدم موفقيت در سوار کردن $1: $2\r
+mount_err2=عدم موفقيت در پياده کردن $1: $2\r
+mount_title1=سوار کردن سيستم پرونده\r
+mount_title2=پياده کردن سيستم پرونده\r
+\r
+zip_err=نمي توان پرونده : $1را استخراج نمود\r
+zip_ecmd=دستور $1يافت نشد\r
+zip_eunzip=عدم موفقيت در Unzipکردن: $1\r
+zip_ename=به نظر مي‌رسد يک پرونده zip, tar يا tar.gz نيست\r
+zip_euntar=عدم موفقيت درUn-tarکردن: $1\r
+zip_euntar2=عدم موفقيت در خارج کردن از فشرده سازي و un-tar کردن: $1\r
+\r
+ddir_title=فهرست راهنماي بار کردن\r
+ddir_rusure=براي بار کردن محتويات $1 به‌صورت يک پرونده بايگاني يکي از دگمه‌هاي نوع بايگاني زير را فشار دهيد.\r
+ddir_zip=ZIP\r
+ddir_tgz=TAR.GZ\r
+ddir_tar=TAR\r
+\r
+ebutton=اين خصوصيت قابل دستيابي نيست\r
+\r
+\r
diff --git a/file/lang/fr b/file/lang/fr
new file mode 100644 (file)
index 0000000..d1d5b19
--- /dev/null
@@ -0,0 +1,229 @@
+index_title=Gestionnaire de Fichier
+index_nojava=Ce module nécessite que java soit en fonction, mais votre fureteur ne le supporte pas
+switch_euser=L'utilisateur Unix n'existe pas !
+
+top_open=Ouvrir
+top_view=Voir
+top_edit=Éditer
+top_refresh=Rafraîchir
+top_info=Information
+top_delete=Supprimer
+top_new=Nouveau
+top_upload=Envoyer
+top_rename=Renommer
+top_copy=Copier
+top_cut=Couper
+top_paste=Coller
+top_share=Partage
+top_search=Trouver
+
+right_name=Nom
+right_size=Taille
+right_user=Usager
+right_group=Groupe
+right_date=Date
+
+edit_enormal=Seulement les fichiers normaux peuvent être éditer
+edit_title=Éditer '$1'
+edit_title2=Créer un fichier
+edit_filename=Nom de fichier:
+edit_eover='$1' ne peut être écrit par-dessus
+edit_esave=Impossible d'ouvrir le fichier '$1'
+edit_eaccess=Vous n'êtes pas autorisé à sauver '$1'
+
+info_file=Fichier
+info_path=Chemin:
+info_type=Type:
+info_size=Taille:
+info_mod=Modifié:
+info_link=Liens vers:
+info_perms=Permissions
+info_user=Usager:
+info_group=Groupe:
+info_other=Autre:
+info_sticky=Collant:
+info_sticky2=Seulement son propriétaire peut effacer un fichier
+info_own=Propriétaire
+info_setuid=SUID:
+info_setuid2=Exécuter comme usager
+info_setgid=SGID:
+info_setgid2=Les fichiers hérite du groupe
+info_setgid3=Exécuter comme groupe
+info_apply=Appliquer les changements à
+info_apply1=Ce répertoire seulement
+info_apply2=Ce répertoire et à ses fichiers
+info_apply3=Ce répertoire et tout ses sous-répertoires
+info_efailed=Impossible de mettre à jour '$1' : $2
+info_read=Lire
+info_write=Écrire
+info_list=Lister
+info_exec=Exécuter
+
+delete_dtitle=Supprimer un répertoire
+delete_ftitle=Supprimer un fichier
+delete_ddesc=Vous êtes sur que vous voulez supprimer définitivement le répertoire '$1' et tout son contenu?
+delete_fdesc=Vous êtes sur que vous voulez supprimer définitivement le fichier $1?
+delete_efailed=Impossible de supprimer '$1' : $2
+
+mkdir_title=Nouveau Répertoire
+mkdir_dir=Nouveau répertoire:
+mkdir_eexists='$1' existe déjà
+mkdir_efailed=Impossible de créer le répertoire : $1
+mkdir_eaccess=Vous n'êtes pas autorisé à créer '$1'
+
+link_title=Créer un Lien Symbolique
+link_from=Lien de:
+link_to=Lien vers:
+link_eexists='$1' Existe déjà
+link_efailed=Impossible de faire le lien symbolique '$1'
+link_efrom=Vous n'êtes pas autorisé à créer aucun lien symbolique de '$1'
+link_efollow=Vous n'êtes pas autorisé à créer aucun lien symbolique
+
+rename_title=Renommer '$1'
+rename_old=Ancien nom:
+rename_new=Nouveau nom:
+rename_ok=Renommer
+rename_eexists=Un fichier appelé '$1' existe déjà
+rename_efailed=Impossible de renommer '$1'
+rename_eold=Vous n'êtes pas autorisé à renommer '$1'
+rename_enew=Vous n'êtes pas autorisé à renommer pour '$1'
+
+file_type0=Répertoire
+file_type1=Fichier texte
+file_type2=Fichier image
+file_type3=Fichier binaire
+file_type4=Fichier
+file_type5=Lien symbolique
+file_type6=Fichier de périphérique
+file_type7=Tuyau
+
+view_enormal=Seulement un fichier normal peut être visionné
+view_eaccess=Vous n'êtes pas autorisé d'accéder à '$1'
+view_eopen=Impossible d'ouvrir '$1' : $2
+
+paste_ecopy=Vous devez copier ou couper avant de coller
+paste_egone=Le fichier copié '$1' n'existe plus
+paste_eover='$1' ne peut être écrasé
+paste_eself=Vous ne pouvez coller un fichier par-dessus lui-même
+paste_emfailed=Impossible de déplacer '$1'
+paste_ecfailed=Impossible de copier '$1'
+
+over_title=Le fichier existe
+over_msg=Le fichier $1 existe déjà. Utiliser le champ ci-dessous pour entrer un nouveau fichier pour le fichier collé.
+over_new=Nouveau nom de fichier::
+over_ok=Ok
+
+upload_efailed=Impossible d'ouvrir l'envoie '$1'
+upload_title=Envoie de Fichier
+upload_file=Fichier à envoyer
+upload_dir=Envoyer dans un répertoire
+upload_ok=Envoie
+upload_conv=Convertir les retours de chariot en format DOS?
+upload_efile=Aucun fichier n'ont été sélectionné pour envoyer.
+upload_edir=Le répertoire d'envoi n'existe pas.
+upload_eperm=Vous n'êtes pas autorisé à créer '$1'
+upload_ewrite=Impossible d'écrire dans '$1' : $2
+
+find_eaccess=Vous n'êtes pas autorisé d'accéder à '$1'
+find_eexist='$1' n'existe pas dans '$2'
+find_edir='$1' n'est pas un répertoire dans '$2'
+
+cancel=Annuler
+
+chmod_eaccess=Vous n'êtes pas autorisé d'accéder à '$1'
+chmod_euser=$1 : usager inexistant
+chmod_egroup=$1 : groupe inexistant
+chmod_elink=lien symbolique impossible : $1
+chmod_echown=Impossible de changer de propriétaire : $1
+chmod_echmod=Impossible de changer de permission : $1
+
+copy_efrom=Vous n'êtes pas autorisé à copier de '$1'
+copy_eto=Vous n'êtes pas autorisé à copier vers '$1'
+copy_elink=lien symbolique impossible : $1
+
+delete_eaccess=Vous n'êtes pas autorisé à supprimer '$1'
+
+list_eaccess=Vous n'êtes pas autorisé d'accéder ce répertoire
+list_edir=Echec du listage de $1 : $2
+
+move_eto=Vous n'êtes pas autorisé à déplacer vers '$1'
+move_afrom=Vous n'êtes pas autorisé à déplacer '$1'
+
+acl_user=Accéder au système de fichier en étant un usager
+acl_user_def=Pareil que le login Webmin
+acl_umask=Démasquer tout nouveau fichier
+acl_follow=Toujours suivre les liens symboliques?
+acl_dirs=Seulement accès aux répertoires
+acl_home=Inclure le répertoire personnel de l'utilisateur Webmin
+
+share_title=Partage
+share_samba=Windows
+share_nfs=NFS
+share_son=Partage de fichiers Windows activé
+share_soff=Partage de fichiers Windows désactivé
+share_writable=En écriture ?
+share_available=Actuellement disponible ?
+share_sheader=Options de partage
+share_only=Seulement
+share_guest=Accès Invité ?
+share_comment=Commentaire
+share_nheader=Options d'export NFS
+share_non=Partage de fichiers NFS activé
+share_noff=Partage de fichiers NFS désactivé
+share_desc=Description
+share_ro=Machines en lecture seule
+share_rw=Machines en lecture/écriture
+share_root=Machines avec accès root
+share_none=Aucune
+share_all=Toutes
+share_listed=Listées ...
+share_host=Machines
+share_opts=Options
+share_s0=Ne faire confiance à personne
+share_s1=Faire confiance aux non-root
+share_s2=Faire confiance à tout le monde
+share_lro=Lecture seule
+share_lrw=Lecture-écriture
+
+log_create_export=Export NFS $1 créé
+log_modify_export=Export NFS $1 modifié
+log_delete_export=Export NFS $1 supprimé
+log_create_share=Ressource partagée Samba $1 créée
+log_modify_share=Ressource partagée Samba $1 modifiée
+log_delete_share=Ressource partagée Samba $1 supprimée
+log_save=Fichier $1 sauvé
+log_chmod=Permissions modifiées sur le fichier $1
+log_mkdir=Répertoire $1 créé
+log_upload=Fichier $1 téléchargé
+log_link=Lien symbolique de $1 vers $2 créé
+log_relink=Lien symbolique de $1 vers $2 modifié
+log_copy=Lien symbolique de $1 vers $2 copié
+log_move=Fichier renommé de $1 en $2
+log_delete=Fichier $1 supprimé
+
+search_eaccess=Vous n'avez pas le droit de chercher dans ce répertoire
+search_title=Trouver des fichiers
+search_ok=Chercher maintenant
+search_dir=Répertoire de recherche
+search_match=Pour les fichiers correspondant à
+search_user=Possédé par utilisateur
+search_group=Possédé par groupe
+search_any=N'importe
+search_type=Type de fichier
+search_types_=N'importe
+search_types_f=Fichier
+search_types_d=Répertoire
+search_types_l=Lien symbolique
+search_types_p=Tube nommé
+search_size=Taille de fichier
+search_more=Plus de
+search_less=Moins de
+search_xdev=Chercher au-delà des points de montage ?
+search_edir=Répertoire de recherche manquant ou invalide
+search_ematch=Expression rationnelle manquante
+search_euser=Nom d'utilisateur manquant
+search_egroup=Nom de groupe manquant
+search_esize=La taille de fichier doit être un entier
+search_crit=Critères de recherche
+search_list=Résultats de la recherche
+
diff --git a/file/lang/it b/file/lang/it
new file mode 100644 (file)
index 0000000..a95c3e7
--- /dev/null
@@ -0,0 +1,321 @@
+index_title=File Manager
+index_nojava=Questo modulo richiede java per funzionare, il tuo browser non supporta java
+index_eremote=Non esiste un utente Unix corrispondente il login Webmin $1.
+switch_euser=L'utente Unix non esiste!
+
+top_ret=Indice
+top_down=Salva
+top_edit=Modifica
+top_refresh=Ricarica
+top_info=Informazioni
+top_eacl=ACL
+top_attr=Attributi
+top_ext=EXT
+top_delete=Cancella
+top_new=Nuovo
+top_upload=Upload
+top_rename=Rinomina
+top_copy=Copia
+top_cut=Taglia
+top_paste=Incolla
+top_share=Condivisione
+top_mount=Mount
+top_search=Cerca
+top_config=Configura
+
+right_name=Nome
+right_size=Dimensione
+right_user=Utente
+right_group=Gruppo
+right_date=Data
+
+edit_enormal=Puoi modificare solo file normali
+edit_title=Modifica di $1
+edit_title2=Creazione file
+edit_filename=Nome file:
+edit_goto=Vai a
+edit_find=Cerca
+edit_gotoline=Vai alla riga
+edit_replace=Sostituisci
+edit_all=Sostituisci tutto
+edit_searchfor=Cerca
+edit_replaceby=Sostituisci con
+edit_eover=$1 non pu&#242;�essere sovrascritto
+edit_esave=Salvataggio file fallito : $1
+edit_eaccess=Non sei autorizzato a salvare '$1'
+edit_notfound=Il testo $1 non &#232;�stato trovato
+edit_saveclose=Salva & Esci
+
+info_file=File
+info_path=Percorso:
+info_type=Tipo:
+info_size=Dimensione:
+info_mod=Modificato:
+info_link=Link a:
+info_perms=Permessi
+info_user=Utente:
+info_group=Gruppo:
+info_other=Altri:
+info_octal=Ottale:
+info_sticky=Sticky:
+info_sticky2=Solo i proprietari possono cancellare file
+info_own=Possesso
+info_setuid=Setuid:
+info_setuid2=Esegui come utente
+info_setgid=Setgid:
+info_setgid2=I file ereditano il gruppo
+info_setgid3=Esegui come gruppo
+info_apply=Applica modifiche a
+info_apply1=Solo questa directory
+info_apply2=Questa directory e i suoi file
+info_apply3=Questa directory e tutte le subdirectory
+info_efailed=Aggiornamento $1 fallito : $2
+info_read=Lettura
+info_write=Scrittura
+info_list=Elenco
+info_exec=Esecuzione
+
+eacl_eacls=Lettura ACL fallito : $1
+eacl_acltype=Tipo ACL
+eacl_aclname=Applica a
+eacl_aclperms=Permessi
+eacl_add=Aggiungi ACL di tipo :
+eacl_remove=Rimuovi ACL
+eacl_efs=Il filesystem $1 non supporta ACL
+eacl_create=Crea ACL
+eacl_edit=Modifica ACL
+eacl_user=Proprietario file $1
+eacl_group=Gruppo file $1
+eacl_eowner=Utente o gruppo da applicare mancante
+eacl_efailed=Settaggio ACL fallito per $1 : $2
+eacl_emask=Pu�esserci al massimo una maschera ACL
+eacl_edefmask=Pu�esserci al massimo una maschera ACL di default
+eacl_title=ACL per $1
+eacl_owner=Proprietario File
+eacl_edefaults=Se il file ha ACL di default, deve avere utente, gruppo e altre ACL di default
+
+acltype_user=Utente
+acltype_group=Gruppo
+acltype_other=Altri
+acltype_mask=Maschera
+acltype_default_user=Utente di default
+acltype_default_group=Gruppo di default
+acltype_default_other=Altri di default
+acltype_default_mask=Maschera di default
+
+delete_mtitle=Cancellazione multipla di file
+delete_dtitle=Cancellazione directory
+delete_ftitle=Cancellazione file
+delete_ddesc=Sei sicuro di voler cancellare definitivamente la directory $1 e tutto il suo contenuto?
+delete_fdesc=Sei sicuro di voler cancellare definitivamente il file $1 ?
+delete_mdesc=Sei sicuro di voler cancellare definitivamente questi file e directory? :
+delete_efailed=Cancellazione $1 fallita : $2
+
+mkdir_title=Nuova Directory
+mkdir_dir=Nuova directory:
+mkdir_eexists=$1 gi�esistente
+mkdir_efailed=Creazione directory fallita : $1
+mkdir_eaccess=Non sei autorizzato a creare '$1'
+
+link_title=Creazione Link
+link_from=Link da:
+link_to=Link a:
+link_eexists=$1 gi�esistente
+link_efailed=Link fallito : $1
+link_efrom=Non sei autorizzato a fare un link da '$1'
+link_efollow=Non sei autorizzato a creare link simbolici
+
+rename_title=Rinomina $1
+rename_old=Vecchio nome:
+rename_new=Nuovo nome:
+rename_ok=Rinomina
+rename_eexists=Esiste gi�un file di nome $1
+rename_efailed=Rinominazione fallita : $1
+rename_eold=Non sei autorizzato a rinominare '$1'
+rename_enew=Non sei autorizzato a rinominare a '$1'
+
+file_type0=Directory
+file_type1=File di testo
+file_type2=File immagine
+file_type3=File binario
+file_type4=File
+file_type5=Link simbolico
+file_type6=File Device
+file_type7=Pipe
+
+view_enormal=Possono essere visualizzati solo file normali
+view_enormal2=Possono essere scaricati solo file normali
+view_eaccess=Non sei autorizzato ad accedere $1
+view_eopen=Apertura $1 fallita : $2
+
+paste_ecopy=Devi tagliare o copiare prima di incollare
+paste_egone=Il file copiato $1 non esite pi
+paste_eover=$1 non pu�essere sovrascritto
+paste_eself=Non puoi incollare un file sopra se stesso
+paste_emfailed=Spostamento fallito : $1
+paste_ecfailed=Copia fallita : $1
+
+over_title=Il File Esiste Gi�over_msg=Il file $1 esiste gi� Usa il campo qui sotto per inserire un nuovo nome per il file incollato.
+over_new=Nuovo nome:
+over_ok=Vai
+
+upload_efailed=Inizio trasferimento fallito : $1
+upload_title=Trasferisci file (upload)
+upload_file=File da trasferire
+upload_dir=Trasferisci alla directory
+upload_ok=Trasferimento
+upload_conv=Convertire nuovariga DOS?
+upload_efile=Nessun file selezionato per il trasferimento.
+upload_edir=La directory di destinazine non esiste.
+upload_eperm=Non sei autorizzato a creare $1
+upload_ewrite=Scrittura $1 fallita : $2.
+upload_already=Il file $1 esiste gi� Sei sicuro di volerlo sovrascrivere?
+
+find_eaccess=Non sei autorizzato ad accedere $1
+find_eexist=$1 non esiste in $2
+find_edir=$1 non �una directory in $2
+
+cancel=Cancella
+close=Chiudi
+
+chmod_eaccess=Non sei autorizzato ad accedere '$1'
+chmod_euser=$1 : utente non esistente
+chmod_egroup=$1 : gruppo non esistente
+chmod_elink=Link simbolico fallito : $1
+chmod_echown=chown fallito : $1
+chmod_echmod=chmod fallito : $1
+chmod_efollow=Non sei autorizzato a modificare link simbolici
+
+copy_efrom=Non sei autorizzato a copiare da '$1'
+copy_eto=Non sei autorizzato a copiare in '$1'
+copy_elink=Link simbolico fallito : $1
+
+delete_eaccess=Non sei autorizzato a cancellare '$1'
+
+list_eaccess=Non sei autorizzato ad accedere questa directory
+list_edir=Elenco $1 fallito : $2
+
+move_eto=Non sei autorizzato a muovere in '$1'
+move_afrom=Non sei autorizzato a muovere '$1'
+
+acl_user=Accesso file sul server come utente
+acl_user_def=Uguale al login Webmin
+acl_umask=Umask per i nuovi file
+acl_follow=Seguire sempre i link simbolici?
+acl_ro=Modalit�sola lettura?
+acl_dirs=Autorizza accesso alle sole directory
+acl_home=Includi la home directory dell'utente Webmin
+acl_log=Eseguo log di tutte le modifiche ai file?
+acl_goto=Aprire la prima directory autorizzata?
+
+share_title=Sharing
+share_samba=Windows
+share_nfs=NFS
+share_son=File sharing windows abilitato
+share_soff=File sharing windows disabilitato
+share_writable=Scrivibile?
+share_available=Attualmente attivo?
+share_sheader=Opzioni di sharing
+share_only=Solamente
+share_guest=Accesso guest?
+share_comment=Commento
+share_nheader=Opzioni export NFS
+share_non=File sharing NFS abilitato
+share_noff=File sharing NFS disabilitato
+share_desc=Descrizione
+share_ro=Host sola lettura
+share_rw=Host lettura e scrittura
+share_root=Host con accesso root access
+share_none=Nessuno
+share_all=Tutti
+share_listed=Elencati..
+share_host=Host
+share_opts=Opzioni
+share_s0=Non fidarti di nessuno
+share_s1=Fidati degli utenti non-root
+share_s2=Fidati di tutti
+share_lro=Sola lettura
+share_lrw=Lettura scrittura
+
+log_create_export=Creato export NFS $1
+log_modify_export=Modificato export NFS $1
+log_delete_export=Cancellato export NFS $1
+log_create_share=Creato share Samba $1
+log_modify_share=Modificato share Samba $1
+log_delete_share=Cancellato share Samba $1
+log_save=File $1 salvato
+log_chmod=Cambiati permessi del file $1
+log_mkdir=Creata directory $1
+log_upload=Trasferito file $1
+log_link=Creato link simbolico $1 a $2
+log_relink=Modificato link simbolico $1 a $2
+log_copy=Copiato file $1 a $2
+log_move=Spostato file $1 a $2
+log_delete=Cancellato file $1
+log_attr=Settati attributi del file $1
+log_acl=Settati ACL al file $1
+
+search_eaccess=Non sei autorizzato a cercare questa directory
+search_title=Cerca file
+search_ok=Esegui Ricerca
+search_dir=Cerca directory
+search_match=File corrispondenti
+search_user=Utente proprietario
+search_group=Gruppo proprietario
+search_any=Qualsiasi
+search_type=Tipo file
+search_types_=Qualsiasi
+search_types_f=File
+search_types_d=Directory
+search_types_l=Link simbolico
+search_types_p=Named pipe
+search_size=Dimensione File
+search_more=Pi di
+search_less=Meno di
+search_xdev=Cerco sotto i mount?
+search_edir=Directory di ricerca mancante o invalido
+search_ematch=regexp di corrispondenza mancante o invalida
+search_euser=Nome utente mancante o invalido
+search_egroup=Nome gruppo mancante
+search_esize=La dimensione del file deve essere un intero
+search_crit=Criterio di ricerca
+search_list=Risultati ricerca
+
+facl_eaccess=Non sei autorizzato a settare ACL per questo file
+
+attr_eattrs=Lettura attributi fallito : $1
+attr_efs=Il filesystem $1 non supporta attributi
+attr_add=Aggiungi Attributo
+attr_name=Nome Attributo
+attr_value=Valore Attributo
+attr_efailed=Settaggio attributo fallito per $1 : $2
+attr_title=Attributi per $1
+attr_create=Aggiungi Attributo
+attr_edit=Modifica Attributo
+attr_ename=Nome attributo mancante
+
+ext_eattrs=Lettura attributi EXT fallito : $1
+ext_efs=Il filesystem $1 non supporta attributi EXT
+ext_title=Attributi EXT per $1
+ext_header=Attributi EXT
+ext_efailed=Settaggio attributi fallito per $1 : $2
+
+eattr_A=Non aggiornare il tempo di accesso
+eattr_a=Puoi solo appendere al file
+eattr_c=Dati compressi sul disco
+eattr_d=Non fare backup con dump
+eattr_i=Non permettere modifiche
+eattr_s=Azzera blocchi in cancellazione
+eattr_S=Sync dopo la scrittura
+eattr_u=Salva il contenuto per de-cancellazione
+
+mount_eaccess=Non sei autorizzato a montare filesystem
+mount_efstab=Non esiste filesystem in questo mount point
+mount_epoint=$1 non �un mount point
+mount_rusure1=Sei sicuro di voler montare $1 in $2 ?
+mount_rusure2=Sei sicuro di voler smontare $1 da $2 ?
+mount_err1=Mount di $1 fallito : $2
+mount_err2=Un-mount di $1 fallito : $2
+mount_title1=Mount filesystem
+mount_title2=Un-mount filesystem
+
diff --git a/file/lang/ja_JP.UTF-8 b/file/lang/ja_JP.UTF-8
new file mode 100644 (file)
index 0000000..30f5a44
--- /dev/null
@@ -0,0 +1,226 @@
+index_title=ファイル マネージャ
+index_nojava=このモジュールは動作するのに Java を必要としますが、ご使用のブラウザは Java をサポートしていません
+
+top_open=開く
+top_view=表示
+top_edit=編集
+top_refresh=更新
+top_info=情報
+top_delete=削除
+top_new=新規
+top_upload=アップロード
+top_rename=名称変更
+top_copy=コピー
+top_cut=カット
+top_paste=ペースト
+top_share=共有
+top_search=検索
+
+right_name=ファイル名
+right_size=サイズ
+right_user=ユーザ
+right_group=グループ
+right_date=日時
+
+edit_enormal=標準ファイルのみ編集できます
+edit_title=$1 を編集中
+edit_title2=ファイルを作成中
+edit_filename=ファイル名:
+edit_eover=$1 は上書きできません
+edit_esave=ファイルを保存できませんでした: $1
+edit_eaccess='$1' を保存できません
+
+info_file=ファイル
+info_path=パス:
+info_type=種類:
+info_size=サイズ:
+info_mod=変更:
+info_link=リンク先:
+info_perms=許可
+info_user=ユーザ:
+info_group=グループ:
+info_other=その他:
+info_sticky=スティッキー:
+info_sticky2=所有者のみがファイルを削除できます
+info_own=所有権
+info_setuid=setuid (ユーザ ID を設定):
+info_setuid2=次のユーザとして実行
+info_setgid=setgid (グループ ID を設定):
+info_setgid2=ファイルの継承グループ
+info_setgid3=次のグループとして実行
+info_apply=変更を適用
+info_apply1=このディレクトリのみ
+info_apply2=このディレクトリとそこに含まれているファイル
+info_apply3=このディレクトリとすべてのサブディレクトリ
+info_efailed=$1 を更新できませんでした: $2
+info_read=読取り
+info_write=書込み
+info_list=リスト
+info_exec=実行
+
+delete_dtitle=ディレクトリの削除
+delete_ftitle=ファイルの削除
+delete_ddesc=ディレクトリ $1 とその内容を永久に削除してもよろしいですか?
+delete_fdesc=ユーザ $1を永久に削除してもよろしいですか?
+delete_efailed=$1 を削除できませんでした: $2
+
+mkdir_title=新規のディレクトリ
+mkdir_dir=新規のディレクトリ:
+mkdir_eexists=$1 はすでに存在します
+mkdir_efailed=ディレクトリを作成できませんでした: $1
+mkdir_eaccess='$1' を作成できません
+
+link_title=リンクの作成
+link_from=リンク元:
+link_to=リンク先:
+link_eexists=$1 はすでに存在します
+link_efailed=リンクできませんでした: $1
+link_efrom='$1' からリンクできません
+link_efollow=シンボリック リンクを作成できません
+
+rename_title=$1 の名称変更
+rename_old=古い名前:
+rename_new=新しい名前:
+rename_ok=名称変更
+rename_eexists=$1 というファイルはすでに存在しています
+rename_efailed=名前を変更できませんでした: $1
+rename_eold='$1' の名前を変更できません
+rename_enew='$1' の名前を変更できません
+
+file_type0=ディレクトリ
+file_type1=テキスト ファイル
+file_type2=画像ファイル
+file_type3=バイナリ ファイル
+file_type4=ファイル
+file_type5=シンボリック リンク
+file_type6=デバイス ファイル
+file_type7=パイプ
+
+view_enormal=標準ファイルのみ表示できます
+view_eaccess=$1 にアクセスできません
+view_eopen=$1 を開けませんでした: $2
+
+paste_ecopy=ペーストする前にカットまたはコピーしてください
+paste_egone=コピーしたファイル $1 はもう存在しません
+paste_eover=$1 は上書きできません
+paste_eself=ファイルをそのファイル自身にペーストすることはできません
+paste_emfailed=移動できませんでした: $1
+paste_ecfailed=コピーできませんでした: $1
+
+over_title=既存のファイル
+over_msg=ファイル $1 はすでに存在します。ペーストされたファイルの新規ファイル名を下のフィールドに入力してください。
+over_new=新規のファイル名:
+over_ok=OK
+
+upload_efailed=アップロードを開けませんでした: $1
+upload_title=ファイルのアップロード
+upload_file=アップロードするファイル
+upload_dir=アップロード ディレクトリ
+upload_ok=アップロード
+upload_conv=DOS 改行に変換しますか?
+upload_efile=アップロードするファイルが選択されていません。
+upload_edir=アップロードのディレクトリが存在しません。
+upload_eperm=$1 を作成できません
+upload_ewrite=$1 に書き込めませんでした: $2.
+
+find_eaccess=$1 にアクセスできません
+find_eexist=$1 は $2 には存在しません
+find_edir=$1 は $2 内のディレクトリではありません
+
+cancel=キャンセル
+
+chmod_eaccess='$1' にアクセスできません
+chmod_euser=$1 : そのユーザは存在しません
+chmod_egroup=$1 : そのグループは存在しません
+chmod_elink=symlink が失敗しました: $1
+chmod_echown=chown が失敗しました: $1
+chmod_echmod=chmod  が失敗しました: $1
+
+copy_efrom='$1' からはコピーできません
+copy_eto='$1' へはコピーできません
+copy_elink=symlink が失敗しました: $1
+
+delete_eaccess='$1' を削除できません
+
+list_eaccess=このディレクトリにアクセスできません
+list_edir=$1 をリストできませんでした: $2
+
+move_eto='$1' へは移動できません
+move_afrom='$1' は移動できません
+
+acl_user=サーバのファイルに次のユーザとしてアクセス
+acl_umask=新規ファイルの Umask
+acl_follow=symlink を常にたどりますか?
+acl_dirs=次のディレクトリへのアクセスのみを許可
+
+share_title=共有
+share_samba=Windows
+share_nfs=NFS
+share_son=Windows ファイルの共有を有効
+share_soff=Windows ファイルの共有を無効
+share_writable=書込みできますか?
+share_available=現在アクティブですか?
+share_sheader=共有オプション
+share_only=次のみ
+share_guest=ゲストのアクセスを可能にしますか?
+share_comment=コメント
+share_nheader=NFS エクスポート オプション
+share_non=NFS ファイルの共有を有効
+share_noff=NFS ファイルの共有を無効
+share_desc=説明
+share_ro=読取り専用のホスト
+share_rw=読取り-書込み可能のホスト
+share_root=root アクセスのホスト
+share_none=なし
+share_all=すべて
+share_listed=リスト..
+share_host=ホスト
+share_opts=オプション
+share_s0=すべてのユーザを信頼しない
+share_s1=root 以外を信頼
+share_s2=すべてのユーザを信頼
+share_lro=読取り専用
+share_lrw=読取り-書込み
+
+log_create_export=NFS エクスポート $1 を作成しました
+log_modify_export=NFS エクスポート $1 を変更しました
+log_delete_export=NFS エクスポート$1 を削除しました
+log_create_share=Samba の共有 $1 を作成しました
+log_modify_share=Samba の共有 $1 を変更しました
+log_delete_share=Samba の共有 $1 を削除しました
+log_save=ファイル $1 を保存しました
+log_chmod=ファイル $1 の許可を変更しました
+log_mkdir=ディレクトリ $1 を作成しました
+log_upload=ファイル $1 をアップロードしました
+log_link=$2へのシンボリック リンク $1 を作成しました
+log_relink=$2 へのシンボリック リンク $1 を変更しました
+log_copy=ファイル $1 を $2 にコピーしました
+log_move=ファイル $1 を $2 に移動しました
+log_delete=ファイル $1 を削除しました
+
+search_eaccess=このディレクトリを検索できません
+search_title=ファイルの検索
+search_ok=すぐに検索
+search_dir=検索ディレクトリ
+search_match=一致したファイル
+search_user=所有ユーザ
+search_group=所有グループ
+search_any=任意
+search_type=ファイルの種類
+search_types_=任意
+search_types_f=ファイル
+search_types_d=ディレクトリ
+search_types_l=シンボリック リンク
+search_types_p=名前付きパイプ
+search_size=ファイル サイズ
+search_more=次より大きい
+search_less=次より小さい
+search_xdev=過去のマウントを検索しますか?
+search_edir=検索ディレクトリがないか無効です
+search_ematch=一致した正規表現がありません
+search_euser=ユーザ名がありません
+search_egroup=グループ名がありません
+search_esize=ファイル サイズは整数でなければなりません
+search_crit=検索条件
+search_list=検索結果
+
diff --git a/file/lang/ja_JP.euc b/file/lang/ja_JP.euc
new file mode 100644 (file)
index 0000000..49a6bcd
--- /dev/null
@@ -0,0 +1,226 @@
+index_title=¥Õ¥¡¥¤¥ë ¥Þ¥Í¡¼¥¸¥ã
+index_nojava=¤³¤Î¥â¥¸¥å¡¼¥ë¤ÏÆ°ºî¤¹¤ë¤Î¤Ë Java ¤òɬÍפȤ·¤Þ¤¹¤¬¡¢¤´»ÈÍѤΥ֥饦¥¶¤Ï Java ¤ò¥µ¥Ý¡¼¥È¤·¤Æ¤¤¤Þ¤»¤ó
+
+top_open=³«¤¯
+top_view=ɽ¼¨
+top_edit=ÊÔ½¸
+top_refresh=¹¹¿·
+top_info=¾ðÊó
+top_delete=ºï½ü
+top_new=¿·µ¬
+top_upload=¥¢¥Ã¥×¥í¡¼¥É
+top_rename=̾¾ÎÊѹ¹
+top_copy=¥³¥Ô¡¼
+top_cut=¥«¥Ã¥È
+top_paste=¥Ú¡¼¥¹¥È
+top_share=¶¦Í­
+top_search=¸¡º÷
+
+right_name=¥Õ¥¡¥¤¥ë̾
+right_size=¥µ¥¤¥º
+right_user=¥æ¡¼¥¶
+right_group=¥°¥ë¡¼¥×
+right_date=Æü»þ
+
+edit_enormal=ɸ½à¥Õ¥¡¥¤¥ë¤Î¤ßÊÔ½¸¤Ç¤­¤Þ¤¹
+edit_title=$1 ¤òÊÔ½¸Ãæ
+edit_title2=¥Õ¥¡¥¤¥ë¤òºîÀ®Ãæ
+edit_filename=¥Õ¥¡¥¤¥ë̾:
+edit_eover=$1 ¤Ï¾å½ñ¤­¤Ç¤­¤Þ¤»¤ó
+edit_esave=¥Õ¥¡¥¤¥ë¤òÊݸ¤Ç¤­¤Þ¤»¤ó¤Ç¤·¤¿: $1
+edit_eaccess='$1' ¤òÊݸ¤Ç¤­¤Þ¤»¤ó
+
+info_file=¥Õ¥¡¥¤¥ë
+info_path=¥Ñ¥¹:
+info_type=¼ïÎà:
+info_size=¥µ¥¤¥º:
+info_mod=Êѹ¹:
+info_link=¥ê¥ó¥¯Àè:
+info_perms=µö²Ä
+info_user=¥æ¡¼¥¶:
+info_group=¥°¥ë¡¼¥×:
+info_other=¤½¤Î¾:
+info_sticky=¥¹¥Æ¥£¥Ã¥­¡¼:
+info_sticky2=½êÍ­¼Ô¤Î¤ß¤¬¥Õ¥¡¥¤¥ë¤òºï½ü¤Ç¤­¤Þ¤¹
+info_own=½êÍ­¸¢
+info_setuid=setuid (¥æ¡¼¥¶ ID ¤òÀßÄê):
+info_setuid2=¼¡¤Î¥æ¡¼¥¶¤È¤·¤Æ¼Â¹Ô
+info_setgid=setgid (¥°¥ë¡¼¥× ID ¤òÀßÄê):
+info_setgid2=¥Õ¥¡¥¤¥ë¤Î·Ñ¾µ¥°¥ë¡¼¥×
+info_setgid3=¼¡¤Î¥°¥ë¡¼¥×¤È¤·¤Æ¼Â¹Ô
+info_apply=Êѹ¹¤òŬÍÑ
+info_apply1=¤³¤Î¥Ç¥£¥ì¥¯¥È¥ê¤Î¤ß
+info_apply2=¤³¤Î¥Ç¥£¥ì¥¯¥È¥ê¤È¤½¤³¤Ë´Þ¤Þ¤ì¤Æ¤¤¤ë¥Õ¥¡¥¤¥ë
+info_apply3=¤³¤Î¥Ç¥£¥ì¥¯¥È¥ê¤È¤¹¤Ù¤Æ¤Î¥µ¥Ö¥Ç¥£¥ì¥¯¥È¥ê
+info_efailed=$1 ¤ò¹¹¿·¤Ç¤­¤Þ¤»¤ó¤Ç¤·¤¿: $2
+info_read=Æɼè¤ê
+info_write=½ñ¹þ¤ß
+info_list=¥ê¥¹¥È
+info_exec=¼Â¹Ô
+
+delete_dtitle=¥Ç¥£¥ì¥¯¥È¥ê¤Îºï½ü
+delete_ftitle=¥Õ¥¡¥¤¥ë¤Îºï½ü
+delete_ddesc=¥Ç¥£¥ì¥¯¥È¥ê $1 ¤È¤½¤ÎÆâÍƤò±Êµ×¤Ëºï½ü¤·¤Æ¤â¤è¤í¤·¤¤¤Ç¤¹¤«¡©
+delete_fdesc=¥æ¡¼¥¶ $1¤ò±Êµ×¤Ëºï½ü¤·¤Æ¤â¤è¤í¤·¤¤¤Ç¤¹¤«¡©
+delete_efailed=$1 ¤òºï½ü¤Ç¤­¤Þ¤»¤ó¤Ç¤·¤¿: $2
+
+mkdir_title=¿·µ¬¤Î¥Ç¥£¥ì¥¯¥È¥ê
+mkdir_dir=¿·µ¬¤Î¥Ç¥£¥ì¥¯¥È¥ê:
+mkdir_eexists=$1 ¤Ï¤¹¤Ç¤Ë¸ºß¤·¤Þ¤¹
+mkdir_efailed=¥Ç¥£¥ì¥¯¥È¥ê¤òºîÀ®¤Ç¤­¤Þ¤»¤ó¤Ç¤·¤¿: $1
+mkdir_eaccess='$1' ¤òºîÀ®¤Ç¤­¤Þ¤»¤ó
+
+link_title=¥ê¥ó¥¯¤ÎºîÀ®
+link_from=¥ê¥ó¥¯¸µ:
+link_to=¥ê¥ó¥¯Àè:
+link_eexists=$1 ¤Ï¤¹¤Ç¤Ë¸ºß¤·¤Þ¤¹
+link_efailed=¥ê¥ó¥¯¤Ç¤­¤Þ¤»¤ó¤Ç¤·¤¿: $1
+link_efrom='$1' ¤«¤é¥ê¥ó¥¯¤Ç¤­¤Þ¤»¤ó
+link_efollow=¥·¥ó¥Ü¥ê¥Ã¥¯ ¥ê¥ó¥¯¤òºîÀ®¤Ç¤­¤Þ¤»¤ó
+
+rename_title=$1 ¤Î̾¾ÎÊѹ¹
+rename_old=¸Å¤¤Ì¾Á°:
+rename_new=¿·¤·¤¤Ì¾Á°:
+rename_ok=̾¾ÎÊѹ¹
+rename_eexists=$1 ¤È¤¤¤¦¥Õ¥¡¥¤¥ë¤Ï¤¹¤Ç¤Ë¸ºß¤·¤Æ¤¤¤Þ¤¹
+rename_efailed=̾Á°¤òÊѹ¹¤Ç¤­¤Þ¤»¤ó¤Ç¤·¤¿: $1
+rename_eold='$1' ¤Î̾Á°¤òÊѹ¹¤Ç¤­¤Þ¤»¤ó
+rename_enew='$1' ¤Î̾Á°¤òÊѹ¹¤Ç¤­¤Þ¤»¤ó
+
+file_type0=¥Ç¥£¥ì¥¯¥È¥ê
+file_type1=¥Æ¥­¥¹¥È ¥Õ¥¡¥¤¥ë
+file_type2=²èÁü¥Õ¥¡¥¤¥ë
+file_type3=¥Ð¥¤¥Ê¥ê ¥Õ¥¡¥¤¥ë
+file_type4=¥Õ¥¡¥¤¥ë
+file_type5=¥·¥ó¥Ü¥ê¥Ã¥¯ ¥ê¥ó¥¯
+file_type6=¥Ç¥Ð¥¤¥¹ ¥Õ¥¡¥¤¥ë
+file_type7=¥Ñ¥¤¥×
+
+view_enormal=ɸ½à¥Õ¥¡¥¤¥ë¤Î¤ßɽ¼¨¤Ç¤­¤Þ¤¹
+view_eaccess=$1 ¤Ë¥¢¥¯¥»¥¹¤Ç¤­¤Þ¤»¤ó
+view_eopen=$1 ¤ò³«¤±¤Þ¤»¤ó¤Ç¤·¤¿: $2
+
+paste_ecopy=¥Ú¡¼¥¹¥È¤¹¤ëÁ°¤Ë¥«¥Ã¥È¤Þ¤¿¤Ï¥³¥Ô¡¼¤·¤Æ¤¯¤À¤µ¤¤
+paste_egone=¥³¥Ô¡¼¤·¤¿¥Õ¥¡¥¤¥ë $1 ¤Ï¤â¤¦Â¸ºß¤·¤Þ¤»¤ó
+paste_eover=$1 ¤Ï¾å½ñ¤­¤Ç¤­¤Þ¤»¤ó
+paste_eself=¥Õ¥¡¥¤¥ë¤ò¤½¤Î¥Õ¥¡¥¤¥ë¼«¿È¤Ë¥Ú¡¼¥¹¥È¤¹¤ë¤³¤È¤Ï¤Ç¤­¤Þ¤»¤ó
+paste_emfailed=°ÜÆ°¤Ç¤­¤Þ¤»¤ó¤Ç¤·¤¿: $1
+paste_ecfailed=¥³¥Ô¡¼¤Ç¤­¤Þ¤»¤ó¤Ç¤·¤¿: $1
+
+over_title=´û¸¤Î¥Õ¥¡¥¤¥ë
+over_msg=¥Õ¥¡¥¤¥ë $1 ¤Ï¤¹¤Ç¤Ë¸ºß¤·¤Þ¤¹¡£¥Ú¡¼¥¹¥È¤µ¤ì¤¿¥Õ¥¡¥¤¥ë¤Î¿·µ¬¥Õ¥¡¥¤¥ë̾¤ò²¼¤Î¥Õ¥£¡¼¥ë¥É¤ËÆþÎϤ·¤Æ¤¯¤À¤µ¤¤¡£
+over_new=¿·µ¬¤Î¥Õ¥¡¥¤¥ë̾:
+over_ok=OK
+
+upload_efailed=¥¢¥Ã¥×¥í¡¼¥É¤ò³«¤±¤Þ¤»¤ó¤Ç¤·¤¿: $1
+upload_title=¥Õ¥¡¥¤¥ë¤Î¥¢¥Ã¥×¥í¡¼¥É
+upload_file=¥¢¥Ã¥×¥í¡¼¥É¤¹¤ë¥Õ¥¡¥¤¥ë
+upload_dir=¥¢¥Ã¥×¥í¡¼¥É ¥Ç¥£¥ì¥¯¥È¥ê
+upload_ok=¥¢¥Ã¥×¥í¡¼¥É
+upload_conv=DOS ²þ¹Ô¤ËÊÑ´¹¤·¤Þ¤¹¤«¡©
+upload_efile=¥¢¥Ã¥×¥í¡¼¥É¤¹¤ë¥Õ¥¡¥¤¥ë¤¬ÁªÂò¤µ¤ì¤Æ¤¤¤Þ¤»¤ó¡£
+upload_edir=¥¢¥Ã¥×¥í¡¼¥É¤Î¥Ç¥£¥ì¥¯¥È¥ê¤¬Â¸ºß¤·¤Þ¤»¤ó¡£
+upload_eperm=$1 ¤òºîÀ®¤Ç¤­¤Þ¤»¤ó
+upload_ewrite=$1 ¤Ë½ñ¤­¹þ¤á¤Þ¤»¤ó¤Ç¤·¤¿: $2.
+
+find_eaccess=$1 ¤Ë¥¢¥¯¥»¥¹¤Ç¤­¤Þ¤»¤ó
+find_eexist=$1 ¤Ï $2 ¤Ë¤Ï¸ºß¤·¤Þ¤»¤ó
+find_edir=$1 ¤Ï $2 Æâ¤Î¥Ç¥£¥ì¥¯¥È¥ê¤Ç¤Ï¤¢¤ê¤Þ¤»¤ó
+
+cancel=¥­¥ã¥ó¥»¥ë
+
+chmod_eaccess='$1' ¤Ë¥¢¥¯¥»¥¹¤Ç¤­¤Þ¤»¤ó
+chmod_euser=$1 : ¤½¤Î¥æ¡¼¥¶¤Ï¸ºß¤·¤Þ¤»¤ó
+chmod_egroup=$1 : ¤½¤Î¥°¥ë¡¼¥×¤Ï¸ºß¤·¤Þ¤»¤ó
+chmod_elink=symlink ¤¬¼ºÇÔ¤·¤Þ¤·¤¿: $1
+chmod_echown=chown ¤¬¼ºÇÔ¤·¤Þ¤·¤¿: $1
+chmod_echmod=chmod  ¤¬¼ºÇÔ¤·¤Þ¤·¤¿: $1
+
+copy_efrom='$1' ¤«¤é¤Ï¥³¥Ô¡¼¤Ç¤­¤Þ¤»¤ó
+copy_eto='$1' ¤Ø¤Ï¥³¥Ô¡¼¤Ç¤­¤Þ¤»¤ó
+copy_elink=symlink ¤¬¼ºÇÔ¤·¤Þ¤·¤¿: $1
+
+delete_eaccess='$1' ¤òºï½ü¤Ç¤­¤Þ¤»¤ó
+
+list_eaccess=¤³¤Î¥Ç¥£¥ì¥¯¥È¥ê¤Ë¥¢¥¯¥»¥¹¤Ç¤­¤Þ¤»¤ó
+list_edir=$1 ¤ò¥ê¥¹¥È¤Ç¤­¤Þ¤»¤ó¤Ç¤·¤¿: $2
+
+move_eto='$1' ¤Ø¤Ï°ÜÆ°¤Ç¤­¤Þ¤»¤ó
+move_afrom='$1' ¤Ï°ÜÆ°¤Ç¤­¤Þ¤»¤ó
+
+acl_user=¥µ¡¼¥Ð¤Î¥Õ¥¡¥¤¥ë¤Ë¼¡¤Î¥æ¡¼¥¶¤È¤·¤Æ¥¢¥¯¥»¥¹
+acl_umask=¿·µ¬¥Õ¥¡¥¤¥ë¤Î Umask
+acl_follow=symlink ¤ò¾ï¤Ë¤¿¤É¤ê¤Þ¤¹¤«¡©
+acl_dirs=¼¡¤Î¥Ç¥£¥ì¥¯¥È¥ê¤Ø¤Î¥¢¥¯¥»¥¹¤Î¤ß¤òµö²Ä
+
+share_title=¶¦Í­
+share_samba=Windows
+share_nfs=NFS
+share_son=Windows ¥Õ¥¡¥¤¥ë¤Î¶¦Í­¤òÍ­¸ú
+share_soff=Windows ¥Õ¥¡¥¤¥ë¤Î¶¦Í­¤ò̵¸ú
+share_writable=½ñ¹þ¤ß¤Ç¤­¤Þ¤¹¤«¡©
+share_available=¸½ºß¥¢¥¯¥Æ¥£¥Ö¤Ç¤¹¤«¡©
+share_sheader=¶¦Í­¥ª¥×¥·¥ç¥ó
+share_only=¼¡¤Î¤ß
+share_guest=¥²¥¹¥È¤Î¥¢¥¯¥»¥¹¤ò²Äǽ¤Ë¤·¤Þ¤¹¤«¡©
+share_comment=¥³¥á¥ó¥È
+share_nheader=NFS ¥¨¥¯¥¹¥Ý¡¼¥È ¥ª¥×¥·¥ç¥ó
+share_non=NFS ¥Õ¥¡¥¤¥ë¤Î¶¦Í­¤òÍ­¸ú
+share_noff=NFS ¥Õ¥¡¥¤¥ë¤Î¶¦Í­¤ò̵¸ú
+share_desc=ÀâÌÀ
+share_ro=Æɼè¤êÀìÍѤΥۥ¹¥È
+share_rw=Æɼè¤ê-½ñ¹þ¤ß²Äǽ¤Î¥Û¥¹¥È
+share_root=root ¥¢¥¯¥»¥¹¤Î¥Û¥¹¥È
+share_none=¤Ê¤·
+share_all=¤¹¤Ù¤Æ
+share_listed=¥ê¥¹¥È..
+share_host=¥Û¥¹¥È
+share_opts=¥ª¥×¥·¥ç¥ó
+share_s0=¤¹¤Ù¤Æ¤Î¥æ¡¼¥¶¤ò¿®Íꤷ¤Ê¤¤
+share_s1=root °Ê³°¤ò¿®Íê
+share_s2=¤¹¤Ù¤Æ¤Î¥æ¡¼¥¶¤ò¿®Íê
+share_lro=Æɼè¤êÀìÍÑ
+share_lrw=Æɼè¤ê-½ñ¹þ¤ß
+
+log_create_export=NFS ¥¨¥¯¥¹¥Ý¡¼¥È $1 ¤òºîÀ®¤·¤Þ¤·¤¿
+log_modify_export=NFS ¥¨¥¯¥¹¥Ý¡¼¥È $1 ¤òÊѹ¹¤·¤Þ¤·¤¿
+log_delete_export=NFS ¥¨¥¯¥¹¥Ý¡¼¥È$1 ¤òºï½ü¤·¤Þ¤·¤¿
+log_create_share=Samba ¤Î¶¦Í­ $1 ¤òºîÀ®¤·¤Þ¤·¤¿
+log_modify_share=Samba ¤Î¶¦Í­ $1 ¤òÊѹ¹¤·¤Þ¤·¤¿
+log_delete_share=Samba ¤Î¶¦Í­ $1 ¤òºï½ü¤·¤Þ¤·¤¿
+log_save=¥Õ¥¡¥¤¥ë $1 ¤òÊݸ¤·¤Þ¤·¤¿
+log_chmod=¥Õ¥¡¥¤¥ë $1 ¤Îµö²Ä¤òÊѹ¹¤·¤Þ¤·¤¿
+log_mkdir=¥Ç¥£¥ì¥¯¥È¥ê $1 ¤òºîÀ®¤·¤Þ¤·¤¿
+log_upload=¥Õ¥¡¥¤¥ë $1 ¤ò¥¢¥Ã¥×¥í¡¼¥É¤·¤Þ¤·¤¿
+log_link=$2¤Ø¤Î¥·¥ó¥Ü¥ê¥Ã¥¯ ¥ê¥ó¥¯ $1 ¤òºîÀ®¤·¤Þ¤·¤¿
+log_relink=$2 ¤Ø¤Î¥·¥ó¥Ü¥ê¥Ã¥¯ ¥ê¥ó¥¯ $1 ¤òÊѹ¹¤·¤Þ¤·¤¿
+log_copy=¥Õ¥¡¥¤¥ë $1 ¤ò $2 ¤Ë¥³¥Ô¡¼¤·¤Þ¤·¤¿
+log_move=¥Õ¥¡¥¤¥ë $1 ¤ò $2 ¤Ë°ÜÆ°¤·¤Þ¤·¤¿
+log_delete=¥Õ¥¡¥¤¥ë $1 ¤òºï½ü¤·¤Þ¤·¤¿
+
+search_eaccess=¤³¤Î¥Ç¥£¥ì¥¯¥È¥ê¤ò¸¡º÷¤Ç¤­¤Þ¤»¤ó
+search_title=¥Õ¥¡¥¤¥ë¤Î¸¡º÷
+search_ok=¤¹¤°¤Ë¸¡º÷
+search_dir=¸¡º÷¥Ç¥£¥ì¥¯¥È¥ê
+search_match=°ìÃפ·¤¿¥Õ¥¡¥¤¥ë
+search_user=½êÍ­¥æ¡¼¥¶
+search_group=½êÍ­¥°¥ë¡¼¥×
+search_any=Ǥ°Õ
+search_type=¥Õ¥¡¥¤¥ë¤Î¼ïÎà
+search_types_=Ǥ°Õ
+search_types_f=¥Õ¥¡¥¤¥ë
+search_types_d=¥Ç¥£¥ì¥¯¥È¥ê
+search_types_l=¥·¥ó¥Ü¥ê¥Ã¥¯ ¥ê¥ó¥¯
+search_types_p=̾Á°ÉÕ¤­¥Ñ¥¤¥×
+search_size=¥Õ¥¡¥¤¥ë ¥µ¥¤¥º
+search_more=¼¡¤è¤êÂ礭¤¤
+search_less=¼¡¤è¤ê¾®¤µ¤¤
+search_xdev=²áµî¤Î¥Þ¥¦¥ó¥È¤ò¸¡º÷¤·¤Þ¤¹¤«¡©
+search_edir=¸¡º÷¥Ç¥£¥ì¥¯¥È¥ê¤¬¤Ê¤¤¤«Ìµ¸ú¤Ç¤¹
+search_ematch=°ìÃפ·¤¿Àµµ¬É½¸½¤¬¤¢¤ê¤Þ¤»¤ó
+search_euser=¥æ¡¼¥¶Ì¾¤¬¤¢¤ê¤Þ¤»¤ó
+search_egroup=¥°¥ë¡¼¥×̾¤¬¤¢¤ê¤Þ¤»¤ó
+search_esize=¥Õ¥¡¥¤¥ë ¥µ¥¤¥º¤ÏÀ°¿ô¤Ç¤Ê¤±¤ì¤Ð¤Ê¤ê¤Þ¤»¤ó
+search_crit=¸¡º÷¾ò·ï
+search_list=¸¡º÷·ë²Ì
+
diff --git a/file/lang/ko_KR.UTF-8 b/file/lang/ko_KR.UTF-8
new file mode 100644 (file)
index 0000000..ae53eb2
--- /dev/null
@@ -0,0 +1,226 @@
+index_title=督析 淫軒切
+index_nojava=戚 乞汲精 Java人 敗臆 紫遂背醤 馬走幻 薄仙 崎虞酔煽拭辞 Java研 走据馬走 省柔艦陥
+
+top_open=伸奄
+top_view=左奄
+top_edit=畷増
+top_refresh=歯稽 壱徴
+top_info=舛左
+top_delete=肢薦
+top_new=歯稽 幻級奄
+top_upload=穣稽球
+top_rename=戚硯 郊荷奄
+top_copy=差紫
+top_cut=設虞鎧奄
+top_paste=細食隔奄
+top_share=因政
+top_search=達奄
+
+right_name=戚硯
+right_size=滴奄
+right_user=紫遂切
+right_group=益血
+right_date=劾促
+
+edit_enormal=析鋼 督析幻 畷増拝 呪 赤柔艦陥
+edit_title=$1 畷増
+edit_title2=督析 拙失
+edit_filename=督析 戚硯:
+edit_eover=$1精(澗) 気嬢承 呪 蒸柔艦陥
+edit_esave=督析聖 煽舌馬走 公梅柔艦陥:  $1
+edit_eaccess='$1'聖(研) 煽舌拝 呪 蒸柔艦陥
+
+info_file=督析
+info_path=井稽:
+info_type=政莫:
+info_size=滴奄:
+info_mod=呪舛廃 劾促:
+info_link=元滴 企雌:
+info_perms=紫遂 映廃
+info_user=紫遂切:
+info_group=益血:
+info_other=益 須:
+info_sticky=壱舛:
+info_sticky2=社政切幻 督析聖 肢薦拝 呪 赤柔艦陥
+info_own=社政映
+info_setuid=Setuid:
+info_setuid2=紫遂切稽辞 叔楳
+info_setgid=Setgid:
+info_setgid2=督析戚 益血 雌紗
+info_setgid3=益血生稽辞 叔楳
+info_apply=痕井 鎧遂 旋遂 企雌
+info_apply1=薄仙 巨刑塘軒幻
+info_apply2=薄仙 巨刑塘軒人 背雁 督析
+info_apply3=薄仙 巨刑塘軒人 乞窮 馬是 巨刑塘軒
+info_efailed=$1聖(研) 穣汽戚闘馬走 公梅柔艦陥: $2
+info_read=石奄
+info_write=床奄
+info_list=蟹伸
+info_exec=叔楳
+
+delete_dtitle=巨刑塘軒 肢薦
+delete_ftitle=督析 肢薦
+delete_ddesc=巨刑塘軒 $1引(人) 益 照税 乞窮 督析聖 慎姥旋生稽 肢薦馬獣畏柔艦猿?
+delete_fdesc=督析 $1聖(研) 慎姥旋生稽 肢薦馬獣畏柔艦猿?
+delete_efailed=$1聖(研) 肢薦馬走 公梅柔艦陥: $2
+
+mkdir_title=歯 巨刑塘軒
+mkdir_dir=歯 巨刑塘軒:
+mkdir_eexists=$1戚(亜) 戚耕 赤柔艦陥
+mkdir_efailed=巨刑塘軒研 拙失馬走 公梅柔艦陥: $1
+mkdir_eaccess='$1'聖(研) 拙失拝 呪 蒸柔艦陥
+
+link_title=元滴 拙失
+link_from=元滴 社什:
+link_to=元滴 企雌:
+link_eexists=$1戚(亜) 戚耕 赤柔艦陥
+link_efailed=元滴 叔鳶: $1
+link_efrom='$1'拭辞 元滴拝 呪 蒸柔艦陥
+link_efollow=宿瑳遣 元滴研 拙失拝 呪 蒸柔艦陥
+
+rename_title=$1 戚硯 郊荷奄
+rename_old=奄糎 戚硯:
+rename_new=歯 戚硯:
+rename_ok=戚硯 郊荷奄
+rename_eexists=督析 $1戚(亜) 戚耕 赤柔艦陥
+rename_efailed=戚硯聖 郊荷走 公梅柔艦陥: $1
+rename_eold='$1'税 戚硯聖 郊蝦 呪 蒸柔艦陥
+rename_enew='$1'(生)稽 戚硯聖 郊蝦 呪 蒸柔艦陥
+
+file_type0=巨刑塘軒
+file_type1=努什闘 督析
+file_type2=戚耕走 督析
+file_type3=戚遭 督析
+file_type4=督析
+file_type5=宿瑳遣 元滴
+file_type6=舌帖 督析
+file_type7=督戚覗
+
+view_enormal=析鋼 督析幻 瑳 呪 赤柔艦陥
+view_eaccess=$1拭 羨悦拝 呪 蒸柔艦陥
+view_eopen=$1聖(研) 伸走 公梅柔艦陥: $2
+
+paste_ecopy=細食隔奄 穿拭 設虞鎧暗蟹 差紫背醤 杯艦陥
+paste_egone=差紫廃 督析 $1(戚)亜 希 戚雌 糎仙馬走 省柔艦陥
+paste_eover=$1精(澗) 気嬢承 呪 蒸柔艦陥
+paste_eself=旭精 督析拭澗 細食隔聖 呪 蒸柔艦陥
+paste_emfailed=戚疑馬走 公梅柔艦陥: $1
+paste_ecfailed=差紫馬走 公梅柔艦陥: $1
+
+over_title=督析 糎仙
+over_msg=督析 $1戚(亜) 戚耕 赤柔艦陥. 細食隔精 督析税 歯 戚硯聖 焼掘税 琶球拭 脊径馬淑獣神.
+over_new=歯 督析 戚硯:
+over_ok=溌昔
+
+upload_efailed=穣稽球拝 督析聖 伸走 公梅柔艦陥: $1
+upload_title=督析 穣稽球
+upload_file=穣稽球拝 督析
+upload_dir=巨刑塘軒拭 穣稽球
+upload_ok=穣稽球
+upload_conv=DOS 匝郊嘩聖 痕発杯艦猿?
+upload_efile=穣稽球拝 督析聖 識澱馬走 省紹柔艦陥.
+upload_edir=穣稽球 巨刑塘軒亜 糎仙馬走 省柔艦陥.
+upload_eperm=$1聖(研) 拙失拝 呪 蒸柔艦陥
+upload_ewrite=$1拭 床走 公梅柔艦陥: $2
+
+find_eaccess=$1拭 衝室什拝 呪 蒸柔艦陥
+find_eexist=$2拭 $1戚(亜) 糎仙馬走 省柔艦陥
+find_edir=$1精(澗) $2税 巨刑塘軒亜 焼鑑艦陥
+
+cancel=昼社
+
+chmod_eaccess='$1'拭 羨悦拝 呪 蒸柔艦陥
+chmod_euser=$1: 背雁 紫遂切 蒸製
+chmod_egroup=$1: 背雁 益血 蒸製
+chmod_elink=宿瑳遣 元滴 叔鳶: $1
+chmod_echown=chown 叔鳶: $1
+chmod_echmod=chmod 叔鳶: $1
+
+copy_efrom='$1'拭辞 差紫拝 呪 蒸柔艦陥
+copy_eto='$1'拭 差紫拝 呪 蒸柔艦陥
+copy_elink=宿瑳遣 元滴 叔鳶: $1
+
+delete_eaccess='$1'聖(研) 肢薦拝 呪 蒸柔艦陥
+
+list_eaccess=戚 巨刑塘軒拭 羨悦拝 呪 蒸柔艦陥
+list_edir=$1聖(研) 蟹伸馬走 公梅柔艦陥: $2
+
+move_eto='$1'(生)稽 戚疑拝 呪 蒸柔艦陥
+move_afrom='$1'聖(研) 戚疑拝 呪 蒸柔艦陥
+
+acl_user=紫遂切稽辞 辞獄 督析拭 羨悦
+acl_umask=歯 督析税 Umask
+acl_follow=牌雌 宿瑳遣 元滴研 魚絹艦猿?
+acl_dirs=巨刑塘軒拭 企廃 衝室什幻 買遂
+
+share_title=因政
+share_samba=制亀酔
+share_nfs=NFS
+share_son=制亀酔 督析 因政 亜管
+share_soff=制亀酔 督析 因政 災管
+share_writable=床奄 亜管杯艦猿?
+share_available=薄仙 醗失 雌殿脊艦猿?
+share_sheader=因政 辛芝
+share_only=穿遂
+share_guest=惟什闘 羨悦聖 買遂杯艦猿?
+share_comment=爽汐
+share_nheader=NFS 鎧左鎧奄 辛芝
+share_non=NFS 督析 因政 亜管
+share_noff=NFS 督析 因政 災管
+share_desc=竺誤
+share_ro=石奄 穿遂 硲什闘
+share_rw=石奄/床奄 硲什闘
+share_root=root 衝室什 硲什闘
+share_none=蒸製
+share_all=乞砧
+share_listed=蟹伸..
+share_host=硲什闘
+share_opts=辛芝
+share_s0=焼巷亀 重軽馬走 省製
+share_s1=root須 紫遂切 重軽
+share_s2=乞窮 紫遂切 重軽
+share_lro=石奄 穿遂
+share_lrw=石奄/床奄
+
+log_create_export=NFS 鎧左鎧奄 $1 拙失喫
+log_modify_export=NFS 鎧左鎧奄 $1 呪舛喫
+log_delete_export=NFS 鎧左鎧奄 $1 肢薦喫
+log_create_share=誌郊 因政 $1 拙失喫
+log_modify_share=誌郊 因政 $1 呪舛喫
+log_delete_share=誌郊 因政 $1 肢薦喫
+log_save=督析 $1 煽舌喫
+log_chmod=督析 $1税 紫遂 映廃 痕井喫
+log_mkdir=巨刑塘軒 $1 拙失喫
+log_upload=督析 $1 穣稽球喫
+log_link=$2拭 企廃 宿瑳遣 元滴 $1 拙失喫
+log_relink=$2拭 企廃 宿瑳遣 元滴 $1 呪舛喫
+log_copy=$2(生)稽 督析 $1 差紫喫
+log_move=$2(生)稽 督析 $1 戚疑喫
+log_delete=督析 $1 肢薦喫
+
+search_eaccess=戚 巨刑塘軒研 伊事拝 呪 蒸柔艦陥
+search_title=督析 達奄
+search_ok=走榎 伊事
+search_dir=巨刑塘軒 伊事
+search_match=督析 伊事 鳶渡
+search_user=紫遂切 社政
+search_group=益血 社政
+search_any=績税
+search_type=督析 政莫
+search_types_=績税
+search_types_f=督析
+search_types_d=巨刑塘軒
+search_types_l=宿瑳遣 元滴
+search_types_p=誤誤吉 督戚覗
+search_size=督析 滴奄
+search_more=左陥 鏑
+search_less=左陥 拙製
+search_xdev=走蟹娃 原錘闘研 伊事杯艦猿?
+search_edir=蒸暗蟹 設公吉 伊事 巨刑塘軒
+search_ematch=析帖馬澗 舛鋭 縦戚 蒸柔艦陥
+search_euser=蒸澗 紫遂切 戚硯
+search_egroup=蒸澗 益血 戚硯
+search_esize=督析 滴奄澗 舛呪食醤 杯艦陥
+search_crit=伊事 奄層
+search_list=伊事 衣引
+
diff --git a/file/lang/ko_KR.euc b/file/lang/ko_KR.euc
new file mode 100644 (file)
index 0000000..abb58f9
--- /dev/null
@@ -0,0 +1,226 @@
+index_title=ÆÄÀÏ °ü¸®ÀÚ
+index_nojava=ÀÌ ¸ðµâÀº Java¿Í ÇÔ²² »ç¿ëÇؾߠÇÏÁö¸¸ ÇöÀç ºê¶ó¿ìÀú¿¡¼­ Java¸¦ Áö¿øÇÏÁö ¾Ê½À´Ï´Ù
+
+top_open=¿­±â
+top_view=º¸±â
+top_edit=ÆíÁý
+top_refresh=»õ·Î °íħ
+top_info=Á¤º¸
+top_delete=»èÁ¦
+top_new=»õ·Î ¸¸µé±â
+top_upload=¾÷·Îµå
+top_rename=À̸§ ¹Ù²Ù±â
+top_copy=º¹»ç
+top_cut=À߶󳻱â
+top_paste=ºÙ¿©³Ö±â
+top_share=°øÀ¯
+top_search=ã±â
+
+right_name=À̸§
+right_size=Å©±â
+right_user=»ç¿ëÀÚ
+right_group=±×·ì
+right_date=³¯Â¥
+
+edit_enormal=ÀϹݠÆÄÀϸ¸ ÆíÁýÇÒ ¼ö ÀÖ½À´Ï´Ù
+edit_title=$1 ÆíÁý
+edit_title2=ÆÄÀÏ ÀÛ¼º
+edit_filename=ÆÄÀÏ À̸§:
+edit_eover=$1Àº(´Â) µ¤¾î¾µ ¼ö ¾ø½À´Ï´Ù
+edit_esave=ÆÄÀÏÀ» ÀúÀåÇÏÁö ¸øÇß½À´Ï´Ù:  $1
+edit_eaccess='$1'À»(¸¦) ÀúÀåÇÒ ¼ö ¾ø½À´Ï´Ù
+
+info_file=ÆÄÀÏ
+info_path=°æ·Î:
+info_type=À¯Çü:
+info_size=Å©±â:
+info_mod=¼öÁ¤ÇÑ ³¯Â¥:
+info_link=¸µÅ© ´ë»ó:
+info_perms=»ç¿ë ±ÇÇÑ
+info_user=»ç¿ëÀÚ:
+info_group=±×·ì:
+info_other=±× ¿Ü:
+info_sticky=°íÁ¤:
+info_sticky2=¼ÒÀ¯ÀÚ¸¸ ÆÄÀÏÀ» »èÁ¦ÇÒ ¼ö ÀÖ½À´Ï´Ù
+info_own=¼ÒÀ¯±Ç
+info_setuid=Setuid:
+info_setuid2=»ç¿ëÀڷμ­ ½ÇÇà
+info_setgid=Setgid:
+info_setgid2=ÆÄÀÏÀÌ ±×·ì »ó¼Ó
+info_setgid3=±×·ìÀ¸·Î¼­ ½ÇÇà
+info_apply=º¯°æ ³»¿ë Àû¿ë ´ë»ó
+info_apply1=ÇöÀç µð·ºÅ丮¸¸
+info_apply2=ÇöÀç µð·ºÅ丮¿Í ÇØ´ç ÆÄÀÏ
+info_apply3=ÇöÀç µð·ºÅ丮¿Í ¸ðµç ÇÏÀ§ µð·ºÅ丮
+info_efailed=$1À»(¸¦) ¾÷µ¥ÀÌÆ®ÇÏÁö ¸øÇß½À´Ï´Ù: $2
+info_read=Àбâ
+info_write=¾²±â
+info_list=³ª¿­
+info_exec=½ÇÇà
+
+delete_dtitle=µð·ºÅ丮 »èÁ¦
+delete_ftitle=ÆÄÀÏ »èÁ¦
+delete_ddesc=µð·ºÅ丮 $1°ú(¿Í) ±× ¾ÈÀÇ ¸ðµç ÆÄÀÏÀ» ¿µ±¸ÀûÀ¸·Î »èÁ¦ÇϽðڽÀ´Ï±î?
+delete_fdesc=ÆÄÀÏ $1À»(¸¦) ¿µ±¸ÀûÀ¸·Î »èÁ¦ÇϽðڽÀ´Ï±î?
+delete_efailed=$1À»(¸¦) »èÁ¦ÇÏÁö ¸øÇß½À´Ï´Ù: $2
+
+mkdir_title=»õ µð·ºÅ丮
+mkdir_dir=»õ µð·ºÅ丮:
+mkdir_eexists=$1ÀÌ(°¡) À̹̠ÀÖ½À´Ï´Ù
+mkdir_efailed=µð·ºÅ丮¸¦ ÀÛ¼ºÇÏÁö ¸øÇß½À´Ï´Ù: $1
+mkdir_eaccess='$1'À»(¸¦) ÀÛ¼ºÇÒ ¼ö ¾ø½À´Ï´Ù
+
+link_title=¸µÅ© ÀÛ¼º
+link_from=¸µÅ© ¼Ò½º:
+link_to=¸µÅ© ´ë»ó:
+link_eexists=$1ÀÌ(°¡) À̹̠ÀÖ½À´Ï´Ù
+link_efailed=¸µÅ© ½ÇÆÐ: $1
+link_efrom='$1'¿¡¼­ ¸µÅ©ÇÒ ¼ö ¾ø½À´Ï´Ù
+link_efollow=½Éº¼¸¯ ¸µÅ©¸¦ ÀÛ¼ºÇÒ ¼ö ¾ø½À´Ï´Ù
+
+rename_title=$1 À̸§ ¹Ù²Ù±â
+rename_old=±âÁ¸ À̸§:
+rename_new=»õ À̸§:
+rename_ok=À̸§ ¹Ù²Ù±â
+rename_eexists=ÆÄÀÏ $1ÀÌ(°¡) À̹̠ÀÖ½À´Ï´Ù
+rename_efailed=À̸§À» ¹Ù²ÙÁö ¸øÇß½À´Ï´Ù: $1
+rename_eold='$1'ÀÇ À̸§À» ¹Ù²Ü ¼ö ¾ø½À´Ï´Ù
+rename_enew='$1'(À¸)·Î À̸§À» ¹Ù²Ü ¼ö ¾ø½À´Ï´Ù
+
+file_type0=µð·ºÅ丮
+file_type1=ÅؽºÆ® ÆÄÀÏ
+file_type2=À̹ÌÁö ÆÄÀÏ
+file_type3=ÀÌÁø ÆÄÀÏ
+file_type4=ÆÄÀÏ
+file_type5=½Éº¼¸¯ ¸µÅ©
+file_type6=ÀåÄ¡ ÆÄÀÏ
+file_type7=ÆÄÀÌÇÁ
+
+view_enormal=ÀϹݠÆÄÀϸ¸ º¼ ¼ö ÀÖ½À´Ï´Ù
+view_eaccess=$1¿¡ Á¢±ÙÇÒ ¼ö ¾ø½À´Ï´Ù
+view_eopen=$1À»(¸¦) ¿­Áö ¸øÇß½À´Ï´Ù: $2
+
+paste_ecopy=ºÙ¿©³Ö±â Àü¿¡ À߶󳻰ųª º¹»çÇؾߠÇÕ´Ï´Ù
+paste_egone=º¹»çÇÑ ÆÄÀÏ $1(ÀÌ)°¡ ´õ ÀÌ»ó Á¸ÀçÇÏÁö ¾Ê½À´Ï´Ù
+paste_eover=$1Àº(´Â) µ¤¾î¾µ ¼ö ¾ø½À´Ï´Ù
+paste_eself=°°Àº ÆÄÀÏ¿¡´Â ºÙ¿©³ÖÀ» ¼ö ¾ø½À´Ï´Ù
+paste_emfailed=À̵¿ÇÏÁö ¸øÇß½À´Ï´Ù: $1
+paste_ecfailed=º¹»çÇÏÁö ¸øÇß½À´Ï´Ù: $1
+
+over_title=ÆÄÀÏ Á¸Àç
+over_msg=ÆÄÀÏ $1ÀÌ(°¡) À̹̠ÀÖ½À´Ï´Ù. ºÙ¿©³ÖÀº ÆÄÀÏÀÇ »õ À̸§À» ¾Æ·¡ÀÇ Çʵ忡 ÀÔ·ÂÇϽʽÿÀ.
+over_new=»õ ÆÄÀÏ À̸§:
+over_ok=È®ÀÎ
+
+upload_efailed=¾÷·ÎµåÇÒ ÆÄÀÏÀ» ¿­Áö ¸øÇß½À´Ï´Ù: $1
+upload_title=ÆÄÀÏ ¾÷·Îµå
+upload_file=¾÷·ÎµåÇÒ ÆÄÀÏ
+upload_dir=µð·ºÅ丮¿¡ ¾÷·Îµå
+upload_ok=¾÷·Îµå
+upload_conv=DOS ÁٹٲÞÀ» º¯È¯Çմϱî?
+upload_efile=¾÷·ÎµåÇÒ ÆÄÀÏÀ» ¼±ÅÃÇÏÁö ¾Ê¾Ò½À´Ï´Ù.
+upload_edir=¾÷·Îµå µð·ºÅ丮°¡ Á¸ÀçÇÏÁö ¾Ê½À´Ï´Ù.
+upload_eperm=$1À»(¸¦) ÀÛ¼ºÇÒ ¼ö ¾ø½À´Ï´Ù
+upload_ewrite=$1¿¡ ¾²Áö ¸øÇß½À´Ï´Ù: $2
+
+find_eaccess=$1¿¡ ¾×¼¼½ºÇÒ ¼ö ¾ø½À´Ï´Ù
+find_eexist=$2¿¡ $1ÀÌ(°¡) Á¸ÀçÇÏÁö ¾Ê½À´Ï´Ù
+find_edir=$1Àº(´Â) $2ÀÇ µð·ºÅ丮°¡ ¾Æ´Õ´Ï´Ù
+
+cancel=Ãë¼Ò
+
+chmod_eaccess='$1'¿¡ Á¢±ÙÇÒ ¼ö ¾ø½À´Ï´Ù
+chmod_euser=$1: ÇØ´ç »ç¿ëÀÚ ¾øÀ½
+chmod_egroup=$1: ÇØ´ç ±×·ì ¾øÀ½
+chmod_elink=½Éº¼¸¯ ¸µÅ© ½ÇÆÐ: $1
+chmod_echown=chown ½ÇÆÐ: $1
+chmod_echmod=chmod ½ÇÆÐ: $1
+
+copy_efrom='$1'¿¡¼­ º¹»çÇÒ ¼ö ¾ø½À´Ï´Ù
+copy_eto='$1'¿¡ º¹»çÇÒ ¼ö ¾ø½À´Ï´Ù
+copy_elink=½Éº¼¸¯ ¸µÅ© ½ÇÆÐ: $1
+
+delete_eaccess='$1'À»(¸¦) »èÁ¦ÇÒ ¼ö ¾ø½À´Ï´Ù
+
+list_eaccess=ÀÌ µð·ºÅ丮¿¡ Á¢±ÙÇÒ ¼ö ¾ø½À´Ï´Ù
+list_edir=$1À»(¸¦) ³ª¿­ÇÏÁö ¸øÇß½À´Ï´Ù: $2
+
+move_eto='$1'(À¸)·Î À̵¿ÇÒ ¼ö ¾ø½À´Ï´Ù
+move_afrom='$1'À»(¸¦) À̵¿ÇÒ ¼ö ¾ø½À´Ï´Ù
+
+acl_user=»ç¿ëÀڷμ­ ¼­¹ö ÆÄÀÏ¿¡ Á¢±Ù
+acl_umask=»õ ÆÄÀÏÀÇ Umask
+acl_follow=Ç×»ó ½Éº¼¸¯ ¸µÅ©¸¦ µû¸¨´Ï±î?
+acl_dirs=µð·ºÅ丮¿¡ ´ëÇÑ ¾×¼¼½º¸¸ Çã¿ë
+
+share_title=°øÀ¯
+share_samba=À©µµ¿ì
+share_nfs=NFS
+share_son=À©µµ¿ì ÆÄÀÏ °øÀ¯ °¡´É
+share_soff=À©µµ¿ì ÆÄÀÏ °øÀ¯ ºÒ´É
+share_writable=¾²±â °¡´ÉÇմϱî?
+share_available=ÇöÀç È°¼º »óÅÂÀԴϱî?
+share_sheader=°øÀ¯ ¿É¼Ç
+share_only=Àü¿ë
+share_guest=°Ô½ºÆ® Á¢±ÙÀ» Çã¿ëÇմϱî?
+share_comment=ÁÖ¼®
+share_nheader=NFS ³»º¸³»±â ¿É¼Ç
+share_non=NFS ÆÄÀÏ °øÀ¯ °¡´É
+share_noff=NFS ÆÄÀÏ °øÀ¯ ºÒ´É
+share_desc=¼³¸í
+share_ro=Àбâ Àü¿ë È£½ºÆ®
+share_rw=Àбâ/¾²±â È£½ºÆ®
+share_root=root ¾×¼¼½º È£½ºÆ®
+share_none=¾øÀ½
+share_all=¸ðµÎ
+share_listed=³ª¿­..
+share_host=È£½ºÆ®
+share_opts=¿É¼Ç
+share_s0=¾Æ¹«µµ ½Å·ÚÇÏÁö ¾ÊÀ½
+share_s1=root¿Ü »ç¿ëÀÚ ½Å·Ú
+share_s2=¸ðµç »ç¿ëÀÚ ½Å·Ú
+share_lro=Àбâ Àü¿ë
+share_lrw=Àбâ/¾²±â
+
+log_create_export=NFS ³»º¸³»±â $1 ÀÛ¼ºµÊ
+log_modify_export=NFS ³»º¸³»±â $1 ¼öÁ¤µÊ
+log_delete_export=NFS ³»º¸³»±â $1 »èÁ¦µÊ
+log_create_share=»ï¹Ù °øÀ¯ $1 ÀÛ¼ºµÊ
+log_modify_share=»ï¹Ù °øÀ¯ $1 ¼öÁ¤µÊ
+log_delete_share=»ï¹Ù °øÀ¯ $1 »èÁ¦µÊ
+log_save=ÆÄÀÏ $1 ÀúÀåµÊ
+log_chmod=ÆÄÀÏ $1ÀÇ »ç¿ë ±ÇÇÑ º¯°æµÊ
+log_mkdir=µð·ºÅ丮 $1 ÀÛ¼ºµÊ
+log_upload=ÆÄÀÏ $1 ¾÷·ÎµåµÊ
+log_link=$2¿¡ ´ëÇÑ ½Éº¼¸¯ ¸µÅ© $1 ÀÛ¼ºµÊ
+log_relink=$2¿¡ ´ëÇÑ ½Éº¼¸¯ ¸µÅ© $1 ¼öÁ¤µÊ
+log_copy=$2(À¸)·Î ÆÄÀÏ $1 º¹»çµÊ
+log_move=$2(À¸)·Î ÆÄÀÏ $1 À̵¿µÊ
+log_delete=ÆÄÀÏ $1 »èÁ¦µÊ
+
+search_eaccess=ÀÌ µð·ºÅ丮¸¦ °Ë»öÇÒ ¼ö ¾ø½À´Ï´Ù
+search_title=ÆÄÀϠã±â
+search_ok=Áö±Ý °Ë»ö
+search_dir=µð·ºÅ丮 °Ë»ö
+search_match=ÆÄÀÏ °Ë»ö ÆÐÅÏ
+search_user=»ç¿ëÀÚ ¼ÒÀ¯
+search_group=±×·ì ¼ÒÀ¯
+search_any=ÀÓÀÇ
+search_type=ÆÄÀÏ À¯Çü
+search_types_=ÀÓÀÇ
+search_types_f=ÆÄÀÏ
+search_types_d=µð·ºÅ丮
+search_types_l=½Éº¼¸¯ ¸µÅ©
+search_types_p=¸í¸íµÈ ÆÄÀÌÇÁ
+search_size=ÆÄÀÏ Å©±â
+search_more=º¸´Ù Å­
+search_less=º¸´Ù ÀÛÀ½
+search_xdev=Áö³ª°£ ¸¶¿îÆ®¸¦ °Ë»öÇմϱî?
+search_edir=¾ø°Å³ª À߸øµÈ °Ë»ö µð·ºÅ丮
+search_ematch=ÀÏÄ¡Çϴ Á¤±Ô ½ÄÀÌ ¾ø½À´Ï´Ù
+search_euser=¾ø´Â »ç¿ëÀÚ À̸§
+search_egroup=¾ø´Â ±×·ì À̸§
+search_esize=ÆÄÀÏ Å©±â´Â Á¤¼ö¿©¾ß ÇÕ´Ï´Ù
+search_crit=°Ë»ö ±âÁØ
+search_list=°Ë»ö °á°ú
+
diff --git a/file/lang/pl b/file/lang/pl
new file mode 100644 (file)
index 0000000..13e6c76
--- /dev/null
@@ -0,0 +1,326 @@
+index_title=Zarz±dca plików
+index_nojava=Ten modu³ wymaga do swojego dzia³ania javy, natomiast Twoja przegl±darka javy nie obs³uguje
+# switch_euser=U¿ytkownik Uniksa nie istnieje!
+switch_euser=Uzytkownik Uniksa nie istnieje!
+
+# top_open=Otwórz
+top_open=Otworz
+top_view=Obejrzyj
+top_edit=Modyfikuj
+# top_refresh=Od¶wie¿
+top_refresh=Odswiez
+top_info=Informacje
+# top_delete=Usuñ
+top_delete=Usun
+top_new=Nowy
+# top_upload=Prze¶lij
+top_upload=Przeslij
+# top_rename=Zmieñ nazwê
+top_rename=Zmien nazwe
+top_copy=Kopiuj
+top_cut=Wytnij
+top_paste=Wklej
+# top_share=Udostêpnianie
+top_share=Udostepnianie
+top_search=Szukaj
+
+right_name=Nazwa
+right_size=Rozmiar
+# right_user=W³a¶ciciel
+right_user=Wlasciciel
+right_group=Grupa
+right_date=Data
+
+# edit_enormal=Modyfikowaæ mo¿na tylko zwyk³e pliki
+edit_enormal=Modyfikowac mozna tylko zwykle pliki
+edit_title=Modyfikacja $1
+edit_title2=Tworzenie pliku
+edit_filename=Nazwa pliku:
+# edit_eover=$1 nie mo¿e byæ nadpisany
+edit_eover=$1 nie moze byc nadpisany
+# edit_esave=Nie uda³o siê zachowaæ pliku : $1
+edit_esave=Nie udalo sie zachowac pliku : $1
+# edit_eaccess=Nie masz uprawnieñ do zachowania '$1'
+edit_eaccess=Nie masz uprawnien do zachowania '$1'
+
+info_file=Plik
+# info_path=¦cie¿ka:
+info_path=Sciezka:
+info_type=Typ:
+info_size=Rozmiar:
+info_mod=Zmieniony:
+# info_link=Dowi±zanie do:
+info_link=Dowiazanie do:
+info_perms=Uprawnienia
+# info_user=W³a¶ciciel:
+info_user=Wlasciciel:
+info_group=Grupa:
+info_other=Inni:
+info_sticky=Sticky:
+# info_sticky2=Tylko w³a¶ciciele mog± usuwaæ pliki
+info_sticky2=Tylko wlasciciele moga usuwac pliki
+# info_own=Prawa w³asno¶ci
+info_own=Prawa wlasnosci
+info_setuid=Setuid:
+# info_setuid2=Uruchom jako u¿ytkownik
+info_setuid2=Uruchom jako uzytkownik
+info_setgid=Setgid:
+# info_setgid2=Pliki dziedzicz± grupê
+info_setgid2=Pliki dziedzicza grupe
+info_setgid3=Uruchom jako grupa
+info_apply=Zastosuj zmiany do
+info_apply1=tylko katalogu
+# info_apply2=katalogu i zawartych w nim plików
+info_apply2=katalogu i zawartych w nim plikow
+info_apply3=katalogu wraz z podkatalogami
+# info_efailed=Nie uda³o sie zaktualizowaæ $1 : $2
+info_efailed=Nie udalo sie zaktualizowac $1 : $2
+info_read=Odczyt
+info_write=Zapis
+# info_list=Przegl±danie
+info_list=Przegladanie
+info_exec=Uruchamianie
+
+delete_dtitle=Usuñ katalog
+delete_ftitle=Usuñ plik
+# delete_ddesc=Czy jeste¶ pewien, ¿e chcesz nieodwracalnie usun±æ katalog $1 wraz z ca³± zawarto¶ci±?
+delete_ddesc=Czy jestes pewien, ze chcesz nieodwracalnie usunac katalog $1 wraz z cala zawartoscia?
+# delete_fdesc=Czy jeste¶ pewien, ¿e chcesz nieodwracalnie usun±æ plik $1 ?
+delete_fdesc=Czy jestes pewien, ze chcesz nieodwracalnie usunac plik $1 ?
+# delete_efailed=Nie uda³o siê usun±æ $1 : $2
+delete_efailed=Nie udalo sie usunac $1 : $2
+
+mkdir_title=Nowy katalog
+mkdir_dir=Nowy katalog:
+# mkdir_eexists=$1 ju¿ istnieje
+mkdir_eexists=$1 juz istnieje
+# mkdir_efailed=Nie uda³o sie utworzyæ katalogu : $1
+mkdir_efailed=Nie udalo sie utworzyc katalogu : $1
+# mkdir_eaccess=Nie masz uprawnieñ do utworzenia '$1'
+mkdir_eaccess=Nie masz uprawnien do utworzenia '$1'
+
+link_title=Utwórz dowi±zanie
+link_from=Nazwa dowi±zania:
+link_to=Dowi±zanie do:
+# link_eexists=$1 ju¿ istnieje
+link_eexists=$1 juz istnieje
+# link_efailed=Nie uda³o siê utworzyæ dowi±zania : $1
+link_efailed=Nie udalo sie utworzyc dowiazania : $1
+# link_efrom=Nie masz uprawnieñ do utworzenia dowi±zania '$1'
+link_efrom=Nie masz uprawnien do utworzenia dowiazania '$1'
+# link_efollow=Nie masz uprawnieñ do tworzenia dowi±zañ symbolicznych
+link_efollow=Nie masz uprawnien do tworzenia dowiazan symbolicznych
+
+rename_title=Zmieñ nazwê $1
+rename_old=Stara nazwa:
+rename_new=Nowa nazwa:
+# rename_ok=Zmieñ nazwê
+rename_ok=Zmien nazwe
+# rename_eexists=Plik o nazwie $1 ju¿ istnieje
+rename_eexists=Plik o nazwie $1 juz istnieje
+# rename_efailed=Nie uda³o siê zmieniæ nazwy : $1
+rename_efailed=Nie udalo sie zmienic nazwy : $1
+# rename_eold=Nie masz uprawnieñ do zmiany nazwy '$1'
+rename_eold=Nie masz uprawnien do zmiany nazwy '$1'
+# rename_enew=Nie masz uprawnieñ do zmiany nazwy na '$1'
+rename_enew=Nie masz uprawnien do zmiany nazwy na '$1'
+
+file_type0=Katalog
+file_type1=Plik tekstowy
+file_type2=Plik Image
+file_type3=Plik binarny
+file_type4=Plik
+# file_type5=Dowi±zanie symboliczne
+file_type5=Dowiazanie symboliczne
+# file_type6=Plik urz±dzenia
+file_type6=Plik urzadzenia
+file_type7=plik FIFO
+
+# view_enormal=Mo¿na ogl±daæ tylko zwyk³e pliki
+view_enormal=Mozna ogladac tylko zwykle pliki
+# view_eaccess=Nie masz prawa dostêpu do $1
+view_eaccess=Nie masz prawa dostepu do $1
+# view_eopen=Nie uda³o siê otworzyæ $1 : $2
+view_eopen=Nie udalo sie otworzyc $1 : $2
+
+# paste_ecopy=Aby wkleiæ musisz wcze¶niej co¶ skopiowaæ lub wyci±æ
+paste_ecopy=Aby wkleic musisz wczesniej cos skopiowac lub wyciac
+# paste_egone=Kopiowany plik $1 ju¿ nie istnieje
+paste_egone=Kopiowany plik $1 juz nie istnieje
+# paste_eover=Nie mo¿na nadpisaæ $1
+paste_eover=Nie mozna nadpisac $1
+# paste_eself=Nie mo¿esz wkleiæ pliku w miejsce jego samego
+paste_eself=Nie mozesz wkleic pliku w miejsce jego samego
+# paste_emfailed=Nie uda³o siê przesun±æ : $1
+paste_emfailed=Nie udalo sie przesunac : $1
+# paste_ecfailed=Nie uda³o siê skopiowaæ : $1
+paste_ecfailed=Nie udalo sie skopiowac : $1
+
+over_title=Plik istnieje
+# over_msg=Plik $1 ju¿ istnieje. Podaj now± nazwê dla tworzonego pliku korzystaj±c z pola poni¿ej.
+over_msg=Plik $1 juz istnieje. Podaj nowa nazwe dla tworzonego pliku korzystajac z pola ponizej.
+over_new=Nowa nazwa pliku:
+over_ok=OK
+
+# upload_efailed=Nie uda³o siê otworzyæ przesy³ania : $1
+upload_efailed=Nie udalo sie otworzyc przesylania : $1
+# upload_title=Prze¶lij plik
+upload_title=Przeslij plik
+# upload_file=Plik do przes³ania
+upload_file=Plik do przeslania
+# upload_dir=Prze¶lij do katalogu
+upload_dir=Przeslij do katalogu
+# upload_ok=Prze¶lij
+upload_ok=Przeslij
+# upload_conv=Przekszta³ciæ DOS-owe koñce linii?
+upload_conv=Przekszta³cic DOS-owe konce linii?
+# upload_efile=Nie podano pliku do przes³ania.
+upload_efile=Nie podano pliku do przeslania.
+# upload_edir=Katalog, do którego ma nast±piæ przes³anie nie istnieje.
+upload_edir=Katalog, do ktorego ma nastapic przeslanie nie istnieje.
+# upload_eperm=Nie masz uprawnieñ do utworzenia $1
+upload_eperm=Nie masz uprawnien do utworzenia $1
+# upload_ewrite=Nie uda³o siê zapisaæ w $1 : $2.
+upload_ewrite=Nie udalo sie zapisac w $1 : $2.
+
+# find_eaccess=Nie masz prawa dostêpu do $1
+find_eaccess=Nie masz prawa dostepu do $1
+find_eexist=$1 nie istnieje w $2
+find_edir=$1 nie jest podkatalogiem $2
+
+cancel=Rezygnuj
+
+# chmod_eaccess=Nie masz prawa dostêpu do '$1'
+chmod_eaccess=Nie masz prawa dostepu do '$1'
+# chmod_euser=$1 : nie ma takiego u¿ytkownika
+chmod_euser=$1 : nie ma takiego uzytkownika
+chmod_egroup=$1 : nie ma takiej grupy
+# chmod_elink=nie uda³o siê utworzyæ dowi±zania symbolicznego : $1
+chmod_elink=nie udalo sie utworzyc dowiazania symbolicznego : $1
+# chmod_echown=nie uda³o siê zmieniæ w³a¶ciciela : $1
+chmod_echown=nie udalo sie zmienic w³asciciela : $1
+# chmod_echmod=nie uda³o siê zmieniæ uprawnieñ : $1
+chmod_echmod=nie udalo sie zmienic uprawnien : $1
+
+# copy_efrom=Nie masz uprawnieñ do kopiowania '$1'
+copy_efrom=Nie masz uprawnien do kopiowania '$1'
+# copy_eto=Nie masz uprawnieñ do kopiowania do '$1'
+copy_eto=Nie masz uprawnien do kopiowania do '$1'
+# copy_elink=nie uda³o siê utworzyæ dowi±zania symbolicznego : $1
+copy_elink=nie udalo sie utworzyc dowiazania symbolicznego : $1
+
+# delete_eaccess=Nie masz uprawnieñ do usuniêcia '$1'
+delete_eaccess=Nie masz uprawnien do usuniecia '$1'
+
+# list_eaccess=Nie masz prawa dostêpu do tego katalogu
+list_eaccess=Nie masz prawa dostepu do tego katalogu
+# list_edir=Nie uda³o siê przejrzeæ $1 : $2
+list_edir=Nie udalo sie przejrzec $1 : $2
+
+# move_eto=Nie masz uprawnieñ do przenoszenia do '$1'
+move_eto=Nie masz uprawnien do przenoszenia do '$1'
+# move_afrom=Nie masz uprawnieñ do przeniesienia '$1'
+move_afrom=Nie masz uprawnien do przeniesienia '$1'
+
+acl_user=Dostêp do plików na serwerze z prawami u¿ytkownika
+acl_user_def=Taki sam jak zalogowany do Webmina
+acl_umask=Umaska dla nowych plików
+acl_follow=Zawsze ¶ledziæ dowi±zania symboliczne?
+acl_dirs=Pozwoliæ jedynie na dostêp do katalogów
+# acl_home=Do³±cz katalog domowy u¿ytkownika Webmina
+acl_home=Dolacz katalog domowy uzytkownika Webmina
+
+share_title=Udostêpnianie
+share_samba=Windows
+share_nfs=NFS
+# share_son=Udostêpnianie plików dla Windows w³±czone
+share_son=Udostepnianie plikow dla Windows wlaczone
+# share_soff=Udostêpnianie plików dla Windows wy³±czone
+share_soff=Udostepnianie plikow dla Windows wylaczone
+share_writable=Prawa zapisu?
+share_available=Aktualnie czynne?
+# share_sheader=Opcje wspó³dzielenia
+share_sheader=Opcje wspoldzielenia
+# share_only=Wy³±cznie
+share_only=Wylacznie
+# share_guest=Dostêp go¶cinny?
+share_guest=Dostep goscinny?
+share_comment=Uwagi
+# share_nheader=Opcje udostêpniania NFS
+share_nheader=Opcje udostepniania NFS
+# share_non=Udostêpnianie NFS w³±czone
+share_non=Udostepnianie NFS wlaczone
+# share_noff=Udostêpnianie NFS wy³±czone
+share_noff=Udostepnianie NFS wylaczone
+share_desc=Opis
+# share_ro=Hosty z dostêpem tylko do odczytu
+share_ro=Hosty z dostepem tylko do odczytu
+# share_rw=Hosty z dostêpem do zapisu
+share_rw=Hosty z dostepem do zapisu
+# share_root=Hosty z dostêpem roota
+share_root=Hosty z dostepem roota
+share_none=Brak
+share_all=Wszystkie
+share_listed=Wymienione..
+share_host=Hosty
+share_opts=Opcje
+share_s0=Nie ufaj nikomu
+# share_s1=Ufaj wszystkim prócz roota
+share_s1=Ufaj wszystkim procz roota
+share_s2=Ufaj wszystkim
+share_lro=Tylko do odczytu
+share_lrw=Odczyt i zapis
+
+log_create_export=Utworzono udostêpnianie NFS $1
+log_modify_export=Zmieniono udostêpnianie NFS $1
+log_delete_export=Usuniêto udostêpnianie NFS $1
+log_create_share=Utworzono zasób Samby $1
+log_modify_share=Zmieniono zasób Samby $1
+log_delete_share=Usuniêto zasób Samby $1
+log_save=Zachowano plik $1
+log_chmod=Zmieniono uprawnienia dla pliku $1
+log_mkdir=Utworzono katalog $1
+log_upload=Przes³ano plik $1
+log_link=Utworzono dowi±zanie symboliczne $1 do $2
+log_relink=Zmieniono dowi±zanie symboliczne $1 do $2
+log_copy=Skopiowano plik $1 jako $2
+log_move=Przeniesiono plik $1 do $2
+log_delete=Usuniêto plik $1
+
+# search_eaccess=Nie masz uprawnieñ do przeszukiwania tego katalogu
+search_eaccess=Nie masz uprawnien do przeszukiwania tego katalogu
+search_title=Szukaj plików
+search_ok=Szukaj teraz
+search_dir=Szukaj w katalogu
+# search_match=Plików wg wzorca
+search_match=Plikow wg wzorca
+# search_user=Nale¿±cych do u¿ytkownika
+search_user=Nalezacych do uzytkownika
+# search_group=Nale¿±cych do grupy
+search_group=Nalezacych do grupy
+search_any=Dowolny
+search_type=Rodzaj pliku
+search_types_=Dowolny
+# search_types_f=Zwyk³y plik
+search_types_f=Zwykly plik
+search_types_d=Katalog
+# search_types_l=Dowi±zanie symboliczne
+search_types_l=Dowiazanie symboliczne
+search_types_p=Nazwane FIFO
+search_size=Rozmiar pliku
+# search_more=Wiêkszy ni¿
+search_more=Wiekszy niz
+# search_less=Mniejszy ni¿
+search_less=Mniejszy niz
+# search_xdev=Przeszukiwaæ inne systemy plików?
+search_xdev=Przeszukiwac inne systemy plikow?
+search_edir=Nie podany lub niepoprawny katalog przeszukiwania
+# search_ematch=Brak wyra¿enia regularnego do dopasowania
+search_ematch=Brak wyrazenia regularnego do dopasowania
+# search_euser=Nie podano nazwy u¿ytkownika
+search_euser=Nie podano nazwy uzytkownika
+search_egroup=Nie podano nazwy grupy
+# search_esize=Rozmiar pliku musi byæ liczb± ca³kowit±
+search_esize=Rozmiar pliku musi byc liczba calkowita
+search_crit=Kryteria szukania
+search_list=Wyniki szukania
diff --git a/file/lang/pt b/file/lang/pt
new file mode 100644 (file)
index 0000000..8f39838
--- /dev/null
@@ -0,0 +1,146 @@
+index_title=Administrador de Ficheiros
+index_nojava=Este módulo requer java para funcionar, mas o seu navegador não suporta java
+
+top_open=Abrir
+top_view=Ver
+top_edit=Editar
+top_refresh=Refrescar
+top_info=Informações
+top_delete=Apagar
+top_new=Novo
+top_upload=Carregar
+top_rename=Renomear
+top_copy=Copiar
+top_cut=Cortar
+top_paste=Colar
+
+right_name=Nome
+right_size=Tamanho
+right_user=Utilizador
+right_group=Grupo
+right_date=Data
+
+edit_enormal=Apenas ficheiros normais podem ser editados
+edit_title=A editar $1
+edit_title2=A criar ficheiro
+edit_filename=Nome do ficheiro:
+edit_eover=$1 não pode ser sobrescrito
+edit_esave=Erro ao guardar ficheiro : $1
+edit_eaccess=Você não está autorizado para guardar '$1'
+
+info_file=Ficheiro
+info_path=Caminho:
+info_type=Tipo:
+info_size=Tamanho:
+info_mod=Modificado:
+info_link=Enlace com:
+info_perms=Permissões
+info_user=Utilizador:
+info_group=Grupo:
+info_other=Outro:
+info_sticky=Restrição:
+info_sticky2=Apenas os proprietários podem apagar ficheiros
+info_own=Propriedade
+info_setuid=Colocar UID:
+info_setuid2=Executar como utilizador
+info_setgid=Colocar GID:
+info_setgid2=Ficheiros herdados do grupo
+info_setgid3=Executar como grupo
+info_apply=Aplicar alterações a
+info_apply1=Este directório apenas
+info_apply2=Este directório e os seus ficheiros
+info_apply3=Este directório e todos os subdirectórios
+info_efailed=Erro ao actualizar $1 : $2
+info_read=Leitura
+info_write=Escrita
+info_list=Lista
+info_exec=Execução
+
+delete_dtitle=Apagar directório
+delete_ftitle=Apagar ficheiro
+delete_ddesc=Tem a certeza que quer permanentemente apagar o directório $1 e todo o seu conteúdo?
+delete_fdesc=Tem a certeza que quer apagar permanentemente o ficheiro $1 ?
+delete_efailed=Erro ao apagar $1 : $2
+
+mkdir_title=Novo Directório
+mkdir_dir=Novo directório:
+mkdir_eexists=$1 já existe
+mkdir_efailed=Erro na criação de directório : $1
+mkdir_eaccess=Você não está autorizado para criar '$1'
+
+link_title=Criar Enlace
+link_from=Enlace de:
+link_to=Enlace com:
+link_eexists=$1 já existe
+link_efailed=Erro no enlace : $1
+link_efrom=Você não está autorizado para criar enlaces de '$1'
+link_efollow=Você não está autorizado para criar enlaces simbólicos
+
+rename_title=Renomear $1
+rename_old=Nome antigo:
+rename_new=Nome novo:
+rename_ok=Renomear
+rename_eexists=Já existe um ficheiro denominado $1
+rename_efailed=Erro na renomeação : $1
+rename_eold=Você não está autorizado para renomear '$1'
+rename_enew=Você não está autorizado para renomear para '$1'
+
+file_type0=Directório
+file_type1=Ficheiro de texto
+file_type2=Ficheiro de imagem
+file_type3=Ficheiro binário
+file_type4=Ficheiro
+file_type5=Enlace simbólico
+file_type6=Ficheiro de dispositivo
+file_type7=Tubo
+
+view_enormal=Apenas ficheiros normais podem ser vistos
+view_eaccess=Você não está autorizado para aceder a $1
+view_eopen=Erro ao abrir $1 : $2
+
+paste_ecopy=Tem de cortar ou copiar primeiro antes de colar
+paste_egone=O ficheiro cpoiado $1 já não existe
+paste_eover=$1 não pode ser sobrescrito
+paste_eself=Não pode colar um ficheiro sobre si mesmo
+paste_emfailed=Erro ao mover : $1
+paste_ecfailed=Erro ao copiar : $1
+
+upload_efailed=Erro ao abrir ficheiro carregado : $1
+upload_title=Carregar Ficheiro
+upload_file=Ficheiro paa carregar
+upload_dir=Carregar para o directório
+upload_ok=Carregar
+upload_conv=Converter linhas de DOS?
+upload_efile=Não foram seleccionados ficheiros para carregar.
+upload_edir=Directório de carga não existe.
+upload_eperm=Você não está autorizado para criar $1
+upload_ewrite=Erro ao escrever para $1 : $2.
+
+find_eaccess=Você não está autorizado para aceder a $1
+find_eexist=$1 não existe em $2
+find_edir=$1 não é um directório de $2
+
+cancel=Cancelar
+
+chmod_eaccess=Você não está autorizado para aceder a '$1'
+chmod_euser=$1 : não existe tal utilizador
+chmod_egroup=$1 : não existe tal grupo
+chmod_elink=erro no enlace simbólico : $1
+chmod_echown=erro na alteração de proprietário : $1
+chmod_echmod=erro na alteração de permissões : $1
+
+copy_efrom=Você não está autorizado para copiar de '$1'
+copy_eto=Você não está autorizado para copiar para '$1'
+copy_elink=erro no enlace simbólico : $1
+
+delete_eaccess=Você não está autorizado para apagar '$1'
+
+list_eaccess=Você não está autorizado para aceder a este directório
+
+move_eto=Você não está autorizado para mover para '$1'
+move_afrom=Você não está autorizado para mover '$1'
+
+acl_user=Aceder a ficheiros no servidor como utilizador
+acl_umask=Máscara de utilizador para novos ficheiros
+acl_follow=Seguir sempre enlaces simbólicos?
+acl_dirs=Apenas permitir acesso a directórios
diff --git a/file/lang/pt_BR b/file/lang/pt_BR
new file mode 100644 (file)
index 0000000..678c694
--- /dev/null
@@ -0,0 +1,282 @@
+acl_b=bytes
+acl_button_copy=Copiar, Cortar e Colar
+acl_button_delete=Deletar (deletar arquivos)
+acl_button_edit=Editar (editar arquivo texto)
+acl_button_ext=EXT (editar atributos EXT)
+acl_button_info=Informa&#231;&#245;es (editar permiss&#245;es e posse)
+acl_button_makelink=Novo (criar link simb&#243;lico)
+acl_button_mkdir=Novo (criar diret&#243;rio)
+acl_button_mount=Montar (montar ou demosntar dados)
+acl_button_new=Novo (criar arqiovo texto)
+acl_button_rename=Renomear (renomear arquivo)
+acl_button_save=Salvar (baixar arquivo)
+acl_button_search=Procurar (procurar arquivo)
+acl_button_upload=Upload (enviar arquivo do cliente)
+acl_dirs=Permite acesso somente aos diret&#243;rios
+acl_follow=Seguir links simb&#243;licos sempre?
+acl_goto=Abrir primeiro o diret&#243;rio permitido?
+acl_home=Incluir diret&#243;rio home do usu&#225;rio Webmin
+acl_log=Logar todas as modifica&#231;&#245;es nos arquivos?
+acl_ro=Modo somente leitura?
+acl_umask=Umask para novos arquivos
+acl_user=Acessar arquivos no servidor como usu&#225;rio
+acl_user_def=O mesmo que no login do Webmin
+acltype_default_group=Grupo Padr&#227;o
+acltype_default_mask=M&#225;scara Padr&#227;o
+acltype_default_other=Outros Padr&#227;o
+acltype_default_user=Usu&#225;rio Padr&#227;o
+acltype_group=Grupo
+acltype_mask=M&#225;scara
+acltype_other=Outros
+acltype_user=Usu&#225;rio
+attr_add=Adicionar atributo
+attr_create=Adicionar atributo
+attr_eattrs=Falha ao receber atributos de : $1
+attr_edit=Editar atributo
+attr_efailed=Falha ao setar atributos para $1 : $2
+attr_efs=O sistema de arquivos $1 n&#227;o suporta atributos
+attr_ename=Faltando o nome do atributo
+attr_name=Nome do atributo
+attr_title=Atributos do arquivo para $1
+attr_value=Valor do atributo
+cancel=Cancelar
+chmod_eaccess=Voc&#234; n&#227;o est&#225; autorizado a acessar '$1'
+chmod_echmod=falha no chmod : $1
+chmod_echown=falha no chown : $1
+chmod_efollow=Voc&#234; n&#227;o est&#225; autorizado a editar links simb&#243;licos
+chmod_egroup=$1 : grupo inv&#225;lido
+chmod_elink=falha no link simb&#243;lico : $1
+chmod_euser=$1 : usu&#225;rio inv&#225;lido
+copy_efrom=Voc&#234; n&#227;o est&#225; autorizado a copiar de '$1'
+copy_elink=falha no link simb&#243;lico : $1
+copy_eto=Voc&#234; n&#227;o esta autorizado a copiar para '$1'
+delete_ddesc=Voc&#234; tem certeza que deseja apagar permanentemente o diret&#243;rio $1 e todo o seu conte&#250;do?
+delete_dtitle=Apagar diret&#243;rio
+delete_eaccess=Voc&#234; n&#227;o esta autorizado a apagar '$1'
+delete_efailed=Falha ao apagar $1 : $2
+delete_fdesc=Voc&#234; tem certeza que deseja apagar permanentemente o arquivo $1 ?
+delete_ftitle=Apagar arquivo
+delete_mdesc=Voc&#234; tem certeza que deseja apagar permanentemente estes arquivos e diret&#243;rios? :
+delete_mtitle=Apagar m&#250;ltiplos arquivos
+eacl_aclname=Aplicar a
+eacl_aclperms=Permiss&#245;es
+eacl_acltype=Tipo de ACL
+eacl_add=Adicionar ACL do tipo :
+eacl_create=Criar ACL
+eacl_eacls=Falha ao ler ACLs : $1
+eacl_edefaults=Se o arquivo cont&#233;m qualquer ACL padr&#227;o, ele precisa ter usu&#225;rio, grupo e outras ACLs padr&#227;o.
+eacl_edefmask=Pode existir uma entrada a mais padr&#227;o para a m&#225;scara ACL
+eacl_edit=Editar ACL
+eacl_efailed=Falha ao setar ACL para $1 : $2
+eacl_efs=O sistema de arquivos $1 n&#227;o suporta ACLs
+eacl_emask=Pode existir uma entrada a mais para a m&#225;scara ACL
+eacl_eowner=Faltando o usu&#225;rio ou grupo para aplicar a
+eacl_group=Grupo do arquivo $1
+eacl_owner=Dono do arquivo
+eacl_remove=Remover ACL
+eacl_title=ACL para $1
+eacl_user=Dono do arquivo $1
+eattr_A=N&#226;o atualize os tempos de acesso
+eattr_S=Sempre sincronizar ap&#243;s escrever
+eattr_a=Pode somente adicionar ao fim do arquivo
+eattr_c=Comprimir dados no disco
+eattr_d=N&#227;o fazer backup com o dump
+eattr_i=N&#227;o permitir modifica&#231;&#227;o
+eattr_s=Zerar blocos quando apagar
+eattr_u=Salvar conte&#250;do para recuperar
+edit_eaccess=Voc&#234; n&#227;o est&#225; autorizado a salvar '$1'
+edit_enormal=Somente arquivos comuns podem ser editados
+edit_eover=$1 n&#227;o pode ser sobrescrito
+edit_esave=Falha ao salvar o arquivo : $1
+edit_filename=Nome do arquivo:
+edit_title=Editando $1
+edit_title2=Criando o arquivo
+ext_eattrs=Falha ao receber atributos EXT : $1
+ext_efailed=Falha ao fixar atributos para $1 : $2
+ext_efs=O sistema de arquivos $1 n&#227;o suporta atributos EXT
+ext_header=Atributos EXT do arquivo
+ext_title=Atributos EXT para $1
+facl_eaccess=Voc&#234; n&#227;o tem permiss&#227;o para alterar ACLs para este arquivo
+file_type0=Direct&#243;rio
+file_type1=Arquivo texto
+file_type2=Arquivo de Imagem
+file_type3=Arquivo Bin&#225;rio
+file_type4=Arquivo
+file_type5=Link simb&#243;lico
+file_type6=Arquivo de dispositivo
+file_type7=Pipe
+find_eaccess=Voc&#234; n&#227;o tem permiss&#227;o para acessar $1
+find_edir=$1 n&#227;o &#233; um diret&#243;rio em $2
+find_eexist=$1 n&#227;o existe em $2
+index_eremote=N&#227;o h&#225; nenhum usu&#225;rio Unix que corresponda ao login $1 do Webmin.
+index_nojava=Este m&#243;dulo requer java para funcionar, por&#233;m o seu navegador n&#227;o suporta java
+index_title=Gerenciador de Arquivos
+info_apply=Aplicar mudan&#231;as a
+info_apply1=Somente a este diret&#243;rio
+info_apply2=Este diret&#243;rio e seus arquivos
+info_apply3=Este diret&#243;rio e todos os seus subdiret&#243;rios
+info_efailed=Falha ao atualizar $1 : $2
+info_exec=Executar
+info_file=Arquivo
+info_group=Grupo:
+info_link=Link para:
+info_list=Listar
+info_mod=Modificado:
+info_octal=Octal:
+info_other=Outros:
+info_own=Dono
+info_path=Caminho:
+info_perms=Permiss&#245;es
+info_read=Ler
+info_setgid=Setar gid:
+info_setgid2=Grupo inerente dos arquivos
+info_setgid3=Executar como grupo
+info_setuid=Setar uid:
+info_setuid2=Executar como usu&#225;rio
+info_size=Tamanho:
+info_sticky=Sticky:
+info_sticky2=Somente os donos podem apagar os arquivos
+info_type=Tipo:
+info_user=Usu&#225;rio:
+info_write=Escrever
+link_eexists=$1 j&#225; existe
+link_efailed=Falha ao criar link : $1
+link_efollow=Voc&#234; n&#227;o est&#225; autorizado a criar links simb&#243;licos
+link_efrom=Voc&#234; n&#227;o est&#225; autorizado a linkar de '$1'
+link_from=Link de:
+link_title=Criar Link
+link_to=Link para:
+list_eaccess=Voc&#234; n&#227;o esta autorizado a acessar este diretorio
+list_edir=Falha ao listar $1 : $2
+log_acl=Setar ACL no arquivo $1
+log_attr=Foram setados os atributos no arquivo $1
+log_chmod=Permiss&#245;es alteradas no arquivo $1
+log_copy=Copiado o arquivo $1 para $2
+log_create_export=Criada exporta&#231;&#227;o NFS $1
+log_create_share=Criado compartilhamento Samba $1
+log_delete=Deletado o arquivo $1
+log_delete_export=Deletada exporta&#231;&#227;o NFS $1
+log_delete_share=Deletado compartilhamento Samba $1
+log_link=Criado o link simb&#243;lico $1 para $2
+log_mkdir=Criado o diret&#243;rio $1
+log_modify_export=Modificada exporta&#231;&#227;o NFS $1
+log_modify_share=Modificado compartilhamento Samba $1
+log_move=Movido o arquivo $1 para $2
+log_relink=Modificado o link simb&#243;lico $1 para $2
+log_save=Foi salvo o arquivo $1
+log_upload=Foi enviado o arquivo $1
+mkdir_dir=Novo diret&#243;rio:
+mkdir_eaccess=Voc&#234; n&#227;o est&#225; autorizado a criar '$1'
+mkdir_eexists=$1 j&#225; existe
+mkdir_efailed=Falha ao criar diret&#243;rio : $1
+mkdir_title=Novo Diret&#243;rio
+move_afrom=Voc&#234; n&#227;o est&#225; autorizado a mover '$1'
+move_eto=Voc&#234; n&#227;o est&#225; autorizado a mover para '$1'
+over_msg=O arquivo $1 j&#225; existe. Use o campo abaixo para digitar o nome do arquivo a ser colado.
+over_new=Novo nome do arquivo:
+over_ok=Ok
+over_title=Arquivo j&#225; existe
+paste_ecfailed=Falha ao copiar : $1
+paste_ecopy=Voc&#234; precisa recortar ou copiar antes de colar
+paste_egone=Arquivo copiado $1 n&#227;o existe mais
+paste_emfailed=Falha ao mover : $1
+paste_eover=$1 n&#227;o pode ser sobrescrito
+paste_eself=Voc&#234; n&#227;o pode colar um arquivo sobre ele mesmo
+rename_eexists=Um arquivo chamado $1 j&#225; existe
+rename_efailed=Falha ao renomear : $1
+rename_enew=Voc&#234; n&#227;o est&#225; autorizado a renomear para '$1'
+rename_eold=Voc&#234; n&#227;o est&#225; autorizado a renomear '$1'
+rename_new=Nome novo:
+rename_ok=Renomear
+rename_old=Nome antigo:
+rename_title=Renomear $1
+right_date=Data
+right_group=Grupo
+right_name=Nome
+right_size=Tamanho
+right_user=Usu&#225;rio
+search_any=Qualquer
+search_crit=Crit&#233;rio de pesquisa
+search_dir=Procurar no diret&#243;rio
+search_eaccess=Voc&#234; n&#227;o tem autoriza&#231;&#227;o para procurar neste diret&#243;rio
+search_edir=Diret&#243;rio de procura inv&#225;lido ou faltando
+search_egroup=Faltando nome do grupo
+search_ematch=Falatando coincidir regexp
+search_esize=O tamanho do arquivo precisa ser um inteiro
+search_euser=Faltando nome de usu&#225;rio
+search_group=Perten&#231;am ao grupo
+search_less=Menos do que
+search_list=Resultados da pesquisa
+search_match=Por arquivos que coincidam com
+search_more=Mais do que
+search_ok=Procurar agora
+search_size=Tamanho do arquivo
+search_title=Procurar arquivos
+search_type=Tipo de arquivo
+search_types_=Qualquer
+search_types_d=Diret&#243;rio
+search_types_f=Arquivo
+search_types_l=Link simb&#243;lico
+search_types_p=Pipe nomeado
+search_user=Perten&#231;am ao usu&#225;rio
+search_xdev=Procurar montagens anteriores?
+share_all=Todos
+share_available=Ativo atualmente?
+share_comment=Coment&#225;rio
+share_desc=Descri&#231;ao
+share_guest=Acesso a convidados?
+share_host=Hosts
+share_listed=Listados..
+share_lro=Somente leitura
+share_lrw=Leitura e escrita
+share_nfs=NFS
+share_nheader=Op&#231;&#245;es de exporta&#231;&#227;o NFS
+share_noff=Compartilhamento de arquivos NFS desabilitado
+share_non=Compartilhamento de arquivos NFS abilitado
+share_none=Nenhum
+share_only=Somente
+share_opts=Op&#231;&#245;es
+share_ro=Hosts somente leitura
+share_root=Hosts com acesso root
+share_rw=Hosts leitura e escrita
+share_s0=N&#227;o confiar em ningu&#233;m
+share_s1=Confiar em n&#227;o root
+share_s2=Confiar em todos
+share_samba=Windows
+share_sheader=Op&#231;&#245;es de compartilhamento
+share_soff=Compartilhamento de arquivos Windows desabilitado
+share_son=Compartilhamento de arquivos Windows abilitado
+share_title=Compartilhamento
+share_writable=Permitir escrita?
+switch_euser=O usu&#225;rio Unix n&#227;o existe!
+top_attr=Atributos
+top_copy=Copiar
+top_cut=Recortar
+top_delete=Apagar
+top_down=Salvar
+top_eacl=ACL
+top_edit=Editar
+top_ext=EXT
+top_info=Info
+top_new=Novo
+top_paste=Colar
+top_refresh=Atualizar
+top_rename=Renomear
+top_ret=&#205;ndice
+top_search=Procurar
+top_share=Compartilhar
+top_upload=Upload
+upload_conv=Converter novas linhas do DOS?
+upload_dir=Upload para qual diret&#243;rio
+upload_edir=Diret&#243;rio de upload n&#227;o existe.
+upload_efailed=Falha ao abrir upload : $1
+upload_efile=N&#227;o foi selecionado nenhum arquivo para upload.
+upload_eperm=Voc&#234; n&#227;o est&#225; autorizado a criar $1
+upload_ewrite=Falha a escrever para $1 : $2.
+upload_file=Arquivo para upload
+upload_ok=Upload
+upload_title=Arquivo para upload
+view_eaccess=Voc&#234; n&#227;o est&#225; autorizado a acessar $1
+view_enormal=Somente arquivos comuns podem ser vizualizados
+view_enormal2=Somente pode ser feito download de arquivos comuns
+view_eopen=Falha ao abrir $1 : $2
diff --git a/file/lang/ru_RU b/file/lang/ru_RU
new file mode 100644 (file)
index 0000000..949c3fb
--- /dev/null
@@ -0,0 +1,268 @@
+top_delete=Óäàëèòü
+info_apply=Ïðèìåíèòü èçìåíåíèÿ
+info_perms=Ïðàâà
+info_file=Ôàéë
+view_enormal=Ìîæíî ïðîñìàòðèâàòü òîëüêî îáû÷íûå ôàéëû
+top_upload=Çàãðóçèòü
+edit_esave=Íå óäàëîñü ñîõðàíèòü ôàéë : $1
+upload_ewrite=Íå óäàëîñü çàïèñàòü â $1 : $2.
+chmod_elink=Îøèáêà ïðè âûçîâå symlink : $1
+upload_ok=Çàãðóçèòü
+view_eaccess=Ó âàñ íåäîñòàòî÷íî ïðàâ äëÿ äîñòóïà ê $1
+info_group=Ãðóïïû:
+edit_title2=Ñîçäàíèå ôàéëà
+top_info=Ñâîéñòâà
+move_afrom=Ó âàñ íåäîñòàòî÷íî ïðàâ äëÿ ïåðåìåùåíèÿ '$1'
+link_efailed=Ñîçäàòü ññûëêó íå óäàëîñü : $1
+info_setuid2=Âûïîëíÿòü îò èìåíè ïîëüçîâàòåëÿ
+chmod_euser=$1 : íåò òàêîãî ïîëüçîâàòåëÿ
+link_efrom=Ó âàñ íåäîñòàòî÷íî ïðàâ äëÿ ñîçäàíèÿ ññûëêè '$1'
+info_mod=Èçìåíåí:
+right_date=Äàòà
+copy_efrom=Ó âàñ íåäîñòàòî÷íî ïðàâ äëÿ êîïèðîâàíèÿ èç '$1'
+info_sticky2=Òîëüêî âëàäåëüöû ìîãóò óäàëÿòü ôàéëû
+rename_old=Ñòàðîå èìÿ:
+find_eaccess=Ó âàñ íåäîñòàòî÷íî ïðàâ äëÿ äîñòóïà ê $1
+mkdir_eexists=$1 óæå ñóùåñòâóåò
+upload_dir=Çàãðóæàòü â êàòàëîã
+rename_enew=Ó âàñ íåäîñòàòî÷íî ïðàâ äëÿ ïåðåèìåíîâàíèÿ â '$1'
+find_edir=$1 íå ÿâëÿåòñÿ êàòàëîãîì â $2
+upload_efile=Íå âûáðàí ôàéë äëÿ çàãðóçêè.
+info_type=Òèï:
+info_setuid=Áèò setuid:
+right_group=Ãðóïïà
+upload_efailed=Íå óäàëîñü íà÷àòü çàãðóçêó : $1
+top_cut=Âûðåçàòü
+info_read=×òåíèå
+acl_user=Îáðàùàòüñÿ ê ôàéëàì íà ñåðâåðå ïîä èìåíåì ïîëüçîâàòåëÿ
+paste_ecopy=Ïåðåä âñòàâêîé íåîáõîäèìî âûðåçàòü èëè êîïèðîâàòü
+info_exec=Âûïîëíåíèå
+delete_ddesc=Íàâñåãäà óäàëèòü êàòàëîã $1 ñî âñåì åãî ñîäåðæèìûì?
+info_link=Ññûëêà íà:
+link_to=Ìåñòî íàçíà÷åíèÿ:
+info_size=Ðàçìåð:
+info_path=Ïóòü:
+copy_eto=Ó âàñ íåäîñòàòî÷íî ïðàâ äëÿ êîïèðîâàíèÿ â '$1'
+rename_ok=Ïåðåèìåíîâàòü
+chmod_egroup=$1 : íåò òàêîé ãðóïïû
+info_own=Ïðèíàäëåæíîñòü
+upload_conv=Ïðåîáðàçîâàòü ïåðåâîäû ñòðîê DOS?
+cancel=Îòìåíà
+info_list=Ïðîñìîòð
+index_nojava=Äëÿ ôóíêöèîíèðîâàíèÿ ýòîãî ìîäóëÿ òðåáóåòñÿ java, îäíàêî âàø áðàóçåð java íå ïîääåðæèâàåò
+paste_eover=$1 íå ìîæåò áûòü ïåðåçàïèñàí
+info_user=Ïîëüçîâàòåëÿ:
+delete_fdesc=Íàâñåãäà óäàëèòü ôàéë $1 ?
+edit_title=Ðåäàêòèðîâàíèå $1
+paste_egone=Êîïèðóåìûé ôàéë $1 áîëüøå íå ñóùåñòâóåò
+top_paste=Âñòàâèòü
+chmod_echmod=Îøèáêà ïðè âûçîâå chmod : $1
+edit_filename=Èìÿ ôàéëà:
+link_eexists=$1 óæå ñóùåñòâóåò
+edit_enormal=Ìîæíî ðåäàêòèðîâàòü òîëüêî îáû÷íûå ôàéëû
+info_setgid2=Ôàéëû íàñëåäóþò ãðóïïó
+info_setgid3=Âûïîëíÿòü îò èìåíè ãðóïïû
+top_copy=Êîïèðîâàòü
+edit_eaccess=Ó âàñ íåäîñòàòî÷íî ïðàâ äëÿ ñîõðàíåíèÿ '$1'
+right_name=Èìÿ
+rename_efailed=Ïåðåèìåíîâàòü íå óäàëîñü : $1
+upload_edir=Êàòàëîã äëÿ çàãðóçêè íå ñóùåñòâóåò.
+rename_new=Íîâîå èìÿ:
+delete_dtitle=Óäàëåíèå êàòàëîãà
+link_from=Èñòî÷íèê:
+index_title=Ìåíåäæåð ôàéëîâ
+file_type0=Êàòàëîã
+file_type1=Òåêñòîâûé ôàéë
+file_type2=Ôàéë èçîáðàæåíèÿ
+file_type3=Áèíàðíûé ôàéë
+file_type4=Ôàéë
+file_type5=Ñèìâîëüíàÿ ññûëêà
+file_type6=Ôàéë óñòðîéñòâà
+file_type7=Êàíàë
+info_sticky=Áèò sticky:
+upload_title=Çàãðóçêà ôàéëà
+top_edit=Èçìåíèòü
+upload_eperm=Ó âàñ íåäîñòàòî÷íî ïðàâ äëÿ ñîçäàíèÿ $1
+move_eto=Ó âàñ íåäîñòàòî÷íî ïðàâ äëÿ ïåðåìåùåíèÿ â '$1'
+paste_eself=Íåëüçÿ ïåðåïèñàòü ôàéë ñàìèì ñîáîé
+copy_elink=Îøèáêà ïðè âûçîâå symlink : $1
+chmod_echown=Îøèáêà ïðè âûçîâå chown : $1
+acl_umask=Umask äëÿ íîâûõ ôàéëîâ
+mkdir_dir=Íîâûé êàòàëîã:
+info_other=Ïðî÷èõ:
+mkdir_title=Íîâûé êàòàëîã
+delete_ftitle=Óäàëåíèå ôàéëà
+find_eexist=$1 íå ñóùåñòâóåò â $2
+right_size=Ðàçìåð
+edit_eover=$1 íå ìîæåò áûòü ïåðåçàïèñàí
+paste_emfailed=Íå óäàëîñü ïåðåìåñòèòü : $1
+link_title=Ñîçäàíèå ññûëêè
+info_apply1=Òîëüêî ê ýòîìó êàòàëîãó
+info_apply2=Ê ýòîìó êàòàëîãó è åãî ôàéëàì
+info_apply3=Ê Ýòîìó êàòàëîãó è âñåì åãî ïîäêàòàëîãàì
+info_efailed=Íå óäàëîñü îáíîâèòü $1 : $2
+acl_follow=Âñåãäà ïåðåõîäèòü ïî ñèìâîëüíûì ññûëêàì?
+upload_file=Ôàéë äëÿ çàãðóçêè
+info_setgid=Áèò setgid:
+paste_ecfailed=Íå óäàëîñü ñêîïèðîâàòü : $1
+mkdir_eaccess=Ó âàñ íåäîñòàòî÷íî ïðàâ äëÿ ñîçäàíèÿ '$1'
+right_user=Ïîëüçîâàòåëü
+rename_eold=Ó âàñ íåäîñòàòî÷íî ïðàâ äëÿ ïåðåèìåíîâàíèÿ '$1'
+link_efollow=Ó âàñ íåäîñòàòî÷íî ïðàâ äëÿ ñîçäàíèÿ ññûëîê
+rename_title=Ïåðåèìåíîâàíèå $1
+top_new=Ññûëêà
+mkdir_efailed=Íå óäàëîñü ñîçäàòü êàòàëîã : $1
+info_write=Çàïèñü
+rename_eexists=Ôàéë ñ èìåíåì $1 óæå ñóùåñòâóåò
+acl_dirs=Ïîçâîëÿòü äîñòóï òîëüêî ê êàòàëîãàì
+chmod_eaccess=Ó âàñ íåäîñòàòî÷íî ïðàâ äëÿ äîñòóïà ê '$1'
+top_refresh=Îáíîâèòü
+delete_eaccess=Ó âàñ íåäîñòàòî÷íî ïðàâ äëÿ óäàëåíèÿ '$1'
+view_eopen=Íå óäàëîñü îòêðûòü $1 : $2
+top_rename=Ïåðåèìåíîâàòü
+list_eaccess=Ó âàñ íåäîñòàòî÷íî ïðàâ äëÿ äîñòóïà ê ýòîìó êàòàëîãó
+delete_efailed=Íå óäàëîñü óäàëèòü $1 : $2
+log_chmod=Èçìåíåíû ïðàâà íà ôàéë $1
+eacl_edefmask=There can be at most one default mask ACL entry
+acl_log=Âåñòè æóðíàë âñåõ èçìåíåíèé ôàéëîâ?
+eacl_eacls=Íå óäàëîñü ñ÷èòàòü ACL : $1
+search_eaccess=Ó âàñ íåäîñòàòî÷íî ïðàâ äëÿ ïîèñêà â ýòîì êàòàëîãå
+over_msg=Ôàéë $1 óæå ñóùåñòâóåò. Óêàæèòå äëÿ âñòàâëÿåìîãî ôàéëà íîâîå èìÿ.
+share_nfs=NFS
+log_copy=Ñêîïèðîâàí ôàéë $1 â $2
+acltype_default_group=Ãðóïïà ïî óìîë÷àíèþ
+eacl_aclname=Ïðèìåíèòü ê
+search_size=Ðàçìåð ôàéëà
+log_upload=Çàãðóæåí ôàéë $1
+attr_create=Äîáàâèòü àòðèáóò
+search_types_d=Êàòàëîã
+ext_eattrs=Íå óäàëîñü ïîëó÷èòü àòðèáóòû EXT : $1
+search_group=Ïðèíàäëåæàùèå ãðóïïå
+search_types_f=Ôàéë
+index_eremote=Íåò ïîëüçîâàòåëÿ Unix ñîîòâåòñòâóþùåãî ïîëüçîâàòåëþ Webmin $1.
+search_types_l=Ñèìâîëüíàÿ ññûëêà
+search_types_p=Èìåíîâàííûé êàíàë
+search_dir=Èñêàòü â êàòàëîãå
+acltype_user=Ïîëüçîâàòåëü
+acl_user_def=Ïîëüçîâàòåëÿ Webmin
+top_share=Ñåòü
+switch_euser=Ïîëüçîâàòåëü Unix íå ñóùåñòâóåò!
+acl_home=Âêëþ÷àÿ äîìàøíèé êàòàëîã ïîëüçîâàòåëÿ Webmin
+search_match=Ôàéëû ñîâïàäàþùèå ñ
+share_writable=Çàïèñü ðàçðåøåíà?
+delete_mtitle=Óäàëåíèå íåñêîëüêèõ ôàéëîâ
+search_less=Ìåíåå
+top_attr=Àòðèáóòû
+log_link=Ñîçäàíà ñèìâîëüíàÿ ññûëêà $1 íà $2
+log_create_export=Ñîçäàí ðåñóðñ NFS $1
+eacl_acltype=Òèï ACL
+share_s0=Äîâåðÿòü nobody
+share_s1=Äîâåðÿòü íå-root
+share_s2=Äîâåðÿòü âñåì
+ext_header=Àòðèáóòû EXT ôàéëà
+search_title=Ïîèñê ôàéëîâ
+search_types_=Ëþáîé
+log_move=Ïåðåìåùåí ôàéë $1 â $2
+acl_goto=Îòêðûâàòü ïåðâûé äîñòóïíûé êàòàëîã?
+top_search=Íàéòè
+share_none=Íåò
+log_delete=Óäàëåí ôàéë $1
+share_opts=Íàñòðîéêà
+share_ro=Óçëû, èìåþùèå äîñòóï òîëüêî äëÿ ÷òåíèÿ
+share_rw=Óçëû, èìåþùèå äîñòóï äëÿ ÷òåíèÿ è çàïèñè
+share_guest=Äîñòóï áåç àóòåíòèôèêàöèè (Guest)?
+share_nheader=Íàñòðîéêà äîñòóïà ïî ïðîòîêîëó NFS
+over_title=Ôàéë ñóùåñòâóåò
+over_new=Íîâîå èìÿ ôàéëà:
+share_host=Óçëû
+search_esize=Ðàçìåð ôàéëà äîëæåí áûòü öåëûì ÷èñëîì
+acltype_other=Ïðî÷èå
+ext_efailed=Íå óäàëîñü óñòàíîâèòü àòðèáóòû EXT $1 : $2
+share_title=Äîñòóï ïî ñåòè
+share_only=Òîëüêî
+attr_edit=Èçìåíèòü àòðèáóò
+eacl_eowner=Íå óêàçàí ïîëüçîâàòåëü èëè ãðóïïà äëÿ êîòîðîé âíîñÿòñÿ èçìåíåíèÿ
+view_enormal2=Ìîæíî çàãðóæàòü òîëüêî îáû÷íûå ôàéëû
+search_user=Ïðèíàäëåæàùèå ïîëüçîâàòåëþ
+log_mkdir=Ñîçäàí êàòàëîã $1
+attr_ename=Íå óêàçàíî íàçâàíèå àòðèáóòà
+search_more=Áîëåå
+eacl_user=Âëàäåëåö ôàéëà $1
+ext_efs=Ôàéëîâàÿ ñèñòåìà $1 íå ïîääåðæèâàåò àòðèáóòû EXT
+acltype_default_user=Ïîëüçîâàòåëü ïî óìîë÷àíèþ
+top_ext=EXT
+eacl_efs=Ýòà ôàéëîâàÿ ñèñòåìà $1 íå ïîääåðæèâàåò ACL
+search_egroup=Èìÿ ãðóïïû íå óêàçàíî èëè óêàçàíî íåâåðíî
+share_lro=Òîëüêî ÷òåíèå
+acl_ro=Ðåæèì òîëüêî äëÿ ÷òåíèÿ?
+eacl_edefaults=If a file has any default ACL, it must have default user, group and other ACLs.
+list_edir=Íå óäàëîñü ïðîñìîòðåòü $1 : $2
+eacl_owner=Âëàäåëåö ôàéëà
+share_lrw=×òåíèå/çàïèñü
+attr_name=Íàçâàíèå àòðèáóòà
+eacl_aclperms=Ïðàâà
+acltype_default_other=Ïðî÷èå ïî óìîë÷àíèþ
+acltype_mask=Ìàñêà
+attr_eattrs=Íå óäàëîñü ñ÷èòàòü àòðèáóòû : $1
+search_ematch=Ðåãóëÿðíîå âûðàæåíèå äëÿ ïîèñêà íå óêàçàíî èëè óêàçàíî íåâåðíî
+eacl_add=Äîáàâèòü ACL :
+log_attr=Èçìåíåíû àòðèáóòû ôàéëà $1
+log_modify_export=Èçìåíåí ðåñóðñ NFS $1
+attr_efs=Ôàéëîâàÿ ñèñòåìà $1 íå ïîääåðæèâàåò àòðèáóòû
+top_down=Ñîõðàíèòü
+eattr_A=Íå îáíîâëÿòü âðåìÿ äîñòóïà
+search_crit=Êðèòåðèé ïîèñêà
+attr_add=Äîáàâèòü àòðèáóò
+chmod_efollow=Ó âàñ íåäîñòàòî÷íî ïðàâ äëÿ èçìåíåíèÿ ñèìâîëüíûõ ññûëîê
+ext_title=Àòðèáóòû EXT äëÿ $1
+share_son=Äîñòóï èç ñåòè Windows ðàçðåøåí
+log_create_share=Ñîçäàí ðåñóðñ Samba $1
+search_ok=Èñêàòü
+attr_title=Àòðèáóòû ôàéëà äëÿ $1
+eattr_S=Âñåãäà ñèíõðîíèçèðîâàòü ïîñëå çàïèñè
+search_edir=Êàòàëîã äëÿ ïîèñêà íå óêàçàí èëè óêàçàí íåâåðíî
+share_non=Äîñòóï ïî NFS ðàçðåøåí
+eattr_a=Âîçìîæíî òîëüêî äîáàâëåíèå ê ôàéëó
+share_samba=Windows
+eattr_c=Ñæèìàòü äàííûå íà äèñêå
+eattr_d=Íå ïðîèçâîäèòü ðåçåðâíîå êîïèðîâàíèå ñ ïîìîùüþ dump
+search_xdev=Èñêàòü â ïîäìîíòèðîâàííûõ ôàéëîâûõ ñèñòåìàõ?
+eacl_edit=Èçìåíèòü ACL
+eattr_i=Çàïðåòèòü èçìåíåíèå
+share_soff=Äîñòóï èç ñåòè Windows çàïðåùåí
+search_euser=Èìÿ ïîëüçîâàòåëÿ íå óêàçàíî èëè óêàçàíî íåâåðíî
+eattr_s=Îáíóëÿòü áëîêè ïðè óäàëåíèè
+eattr_u=Ñîõðàíÿòü ñîäåðæèìîå ôàéëà äëÿ âîññòàíîâëåíèÿ
+top_ret=Ìåíþ
+log_delete_share=Óäàëåí ðåñóðñ Samba $1
+eacl_emask=There can be at most one mask ACL entry
+log_delete_export=Óäàëåí ðåñóðñ NFS $1
+eacl_group=Ãðóïïà ôàéëà $1
+share_noff=Äîñòóï ïî NFS çàïðåùåí
+share_available=Äîñòóïåí â äàííûé ìîìåíò?
+info_octal=Âîñüìåðè÷íûé âèä:
+attr_efailed=Íå óäàëîñü óñòàíîâèòü àòðèáóòû äëÿ $1 : $2
+acltype_default_mask=Ìàñêà ïî óìîë÷àíèþ
+log_modify_share=Èçìåíåí ðåñóðñ Samba $1
+log_save=Ñîõðàíåí ôàéë $1
+share_comment=Êîììåíòàðèé
+share_desc=Îïèñàíèå
+eacl_remove=Óäàëèòü ACL
+attr_value=Çíà÷åíèå àòðèáóòà
+search_any=Ëþáîé
+search_type=Òèï ôàéëà
+eacl_title=ACL äëÿ $1
+top_eacl=ACL
+facl_eaccess=Ó âàñ íåäîñòàòî÷íî ïðàâ äëÿ èçìåíåíèÿ ACL äëÿ ýòîãî ôàéëà
+share_root=Óçëû, äëÿ êîòîðûõ ðàçðåøåí äîñòóï ñ ïðàâàìè root
+eacl_efailed=Íå óäàëîñü óñòàíîâèòü ACL äëÿ $1 : $2
+share_all=Âñå
+delete_mdesc=Íàâñåãäà óäàëèòü óêàçàííûå ôàéëû è êàòàëîãè? :
+acltype_group=Ãðóïïà
+log_acl=Äëÿ ôàéëà $1 íàçíà÷åí ACL
+share_listed=Óêàçàííûå..
+share_sheader=Íàñòðîéêà äîñòóïà
+search_list=Ðåçóëüòàòû ïîèñêà
+eacl_create=Ñîçäàòü ACL
+log_relink=Èçìåíåíà ñèìâîëüíàÿ ññûëêà $1 íà $2
+over_ok=ÎÊ
diff --git a/file/lang/ru_SU b/file/lang/ru_SU
new file mode 100644 (file)
index 0000000..99e68fa
--- /dev/null
@@ -0,0 +1,299 @@
+index_title=íÅÎÅÄÖÅÒ ÆÁÊÌÏ×
+index_nojava=äÌÑ ÆÕÎËÃÉÏÎÉÒÏ×ÁÎÉÑ ÜÔÏÇÏ ÍÏÄÕÌÑ ÔÒÅÂÕÅÔÓÑ java, ÏÄÎÁËÏ ×ÁÛ ÂÒÁÕÚÅÒ java ÎÅ ÐÏÄÄÅÒÖÉ×ÁÅÔ
+index_eremote=îÅÔ ÐÏÌØÚÏ×ÁÔÅÌÑ Unix ÓÏÏÔ×ÅÔÓÔ×ÕÀÝÅÇÏ ÐÏÌØÚÏ×ÁÔÅÌÀ Webmin $1.
+switch_euser=ðÏÌØÚÏ×ÁÔÅÌØ Unix ÎÅ ÓÕÝÅÓÔ×ÕÅÔ!
+
+top_ret=íÅÎÀ
+top_down=óÏÈÒÁÎÉÔØ
+top_edit=éÚÍÅÎÉÔØ
+top_refresh=ïÂÎÏ×ÉÔØ
+top_info=ó×ÏÊÓÔ×Á
+top_eacl=ACL
+top_attr=áÔÒÉÂÕÔÙ
+top_ext=EXT
+top_delete=õÄÁÌÉÔØ
+top_new=óÓÙÌËÁ
+top_upload=úÁÇÒÕÚÉÔØ
+top_rename=ðÅÒÅÉÍÅÎÏ×ÁÔØ
+top_copy=ëÏÐÉÒÏ×ÁÔØ
+top_cut=÷ÙÒÅÚÁÔØ
+top_paste=÷ÓÔÁ×ÉÔØ
+top_share=óÅÔØ
+top_search=îÁÊÔÉ
+
+right_name=éÍÑ
+right_size=òÁÚÍÅÒ
+right_user=ðÏÌØÚÏ×ÁÔÅÌØ
+right_group=çÒÕÐÐÁ
+right_date=äÁÔÁ
+
+edit_enormal=íÏÖÎÏ ÒÅÄÁËÔÉÒÏ×ÁÔØ ÔÏÌØËÏ ÏÂÙÞÎÙÅ ÆÁÊÌÙ
+edit_title=òÅÄÁËÔÉÒÏ×ÁÎÉÅ $1
+edit_title2=óÏÚÄÁÎÉÅ ÆÁÊÌÁ
+edit_filename=éÍÑ ÆÁÊÌÁ:
+edit_eover=$1 ÎÅ ÍÏÖÅÔ ÂÙÔØ ÐÅÒÅÚÁÐÉÓÁÎ
+edit_esave=îÅ ÕÄÁÌÏÓØ ÓÏÈÒÁÎÉÔØ ÆÁÊÌ : $1
+edit_eaccess=õ ×ÁÓ ÎÅÄÏÓÔÁÔÏÞÎÏ ÐÒÁ× ÄÌÑ ÓÏÈÒÁÎÅÎÉÑ '$1'
+
+info_file=æÁÊÌ
+info_path=ðÕÔØ:
+info_type=ôÉÐ:
+info_size=òÁÚÍÅÒ:
+info_mod=éÚÍÅÎÅÎ:
+info_link=óÓÙÌËÁ ÎÁ:
+info_perms=ðÒÁ×Á
+info_user=ðÏÌØÚÏ×ÁÔÅÌÑ:
+info_group=çÒÕÐÐÙ:
+info_other=ðÒÏÞÉÈ:
+info_octal=÷ÏÓØÍÅÒÉÞÎÙÊ ×ÉÄ:
+info_sticky=âÉÔ sticky:
+info_sticky2=ôÏÌØËÏ ×ÌÁÄÅÌØÃÙ ÍÏÇÕÔ ÕÄÁÌÑÔØ ÆÁÊÌÙ
+info_own=ðÒÉÎÁÄÌÅÖÎÏÓÔØ
+info_setuid=âÉÔ setuid:
+info_setuid2=÷ÙÐÏÌÎÑÔØ ÏÔ ÉÍÅÎÉ ÐÏÌØÚÏ×ÁÔÅÌÑ
+info_setgid=âÉÔ setgid:
+info_setgid2=æÁÊÌÙ ÎÁÓÌÅÄÕÀÔ ÇÒÕÐÐÕ
+info_setgid3=÷ÙÐÏÌÎÑÔØ ÏÔ ÉÍÅÎÉ ÇÒÕÐÐÙ
+info_apply=ðÒÉÍÅÎÉÔØ ÉÚÍÅÎÅÎÉÑ
+info_apply1=ôÏÌØËÏ Ë ÜÔÏÍÕ ËÁÔÁÌÏÇÕ
+info_apply2=ë ÜÔÏÍÕ ËÁÔÁÌÏÇÕ É ÅÇÏ ÆÁÊÌÁÍ
+info_apply3=ë üÔÏÍÕ ËÁÔÁÌÏÇÕ É ×ÓÅÍ ÅÇÏ ÐÏÄËÁÔÁÌÏÇÁÍ
+info_efailed=îÅ ÕÄÁÌÏÓØ ÏÂÎÏ×ÉÔØ $1 : $2
+info_read=þÔÅÎÉÅ
+info_write=úÁÐÉÓØ
+info_list=ðÒÏÓÍÏÔÒ
+info_exec=÷ÙÐÏÌÎÅÎÉÅ
+
+eacl_eacls=îÅ ÕÄÁÌÏÓØ ÓÞÉÔÁÔØ ACL : $1
+eacl_acltype=ôÉРACL
+eacl_aclname=ðÒÉÍÅÎÉÔØ Ë
+eacl_aclperms=ðÒÁ×Á
+eacl_add=äÏÂÁ×ÉÔØ ACL :
+eacl_remove=õÄÁÌÉÔØ ACL
+eacl_efs=üÔÁ ÆÁÊÌÏ×ÁÑ ÓÉÓÔÅÍÁ $1 ÎÅ ÐÏÄÄÅÒÖÉ×ÁÅÔ ACL
+eacl_create=óÏÚÄÁÔØ ACL
+eacl_edit=éÚÍÅÎÉÔØ ACL
+eacl_user=÷ÌÁÄÅÌÅàÆÁÊÌÁ $1
+eacl_group=çÒÕÐÐÁ ÆÁÊÌÁ $1
+eacl_eowner=îÅ ÕËÁÚÁΠÐÏÌØÚÏ×ÁÔÅÌØ ÉÌÉ ÇÒÕÐÐÁ ÄÌÑ ËÏÔÏÒÏÊ ×ÎÏÓÑÔÓÑ ÉÚÍÅÎÅÎÉÑ
+eacl_efailed=îÅ ÕÄÁÌÏÓØ ÕÓÔÁÎÏ×ÉÔØ ACL ÄÌÑ $1 : $2
+eacl_emask=There can be at most one mask ACL entry
+eacl_edefmask=There can be at most one default mask ACL entry
+eacl_title=ACL ÄÌÑ $1
+eacl_owner=÷ÌÁÄÅÌÅàÆÁÊÌÁ
+eacl_edefaults=If a file has any default ACL, it must have default user, group and other ACLs.
+
+acltype_user=ðÏÌØÚÏ×ÁÔÅÌØ
+acltype_group=çÒÕÐÐÁ
+acltype_other=ðÒÏÞÉÅ
+acltype_mask=íÁÓËÁ
+acltype_default_user=ðÏÌØÚÏ×ÁÔÅÌØ ÐÏ ÕÍÏÌÞÁÎÉÀ
+acltype_default_group=çÒÕÐÐÁ ÐÏ ÕÍÏÌÞÁÎÉÀ
+acltype_default_other=ðÒÏÞÉÅ ÐÏ ÕÍÏÌÞÁÎÉÀ
+acltype_default_mask=íÁÓËÁ ÐÏ ÕÍÏÌÞÁÎÉÀ
+
+delete_mtitle=õÄÁÌÅÎÉÅ ÎÅÓËÏÌØËÉÈ ÆÁÊÌÏ×
+delete_dtitle=õÄÁÌÅÎÉÅ ËÁÔÁÌÏÇÁ
+delete_ftitle=õÄÁÌÅÎÉÅ ÆÁÊÌÁ
+delete_ddesc=îÁ×ÓÅÇÄÁ ÕÄÁÌÉÔØ ËÁÔÁÌÏÇ $1 ÓÏ ×ÓÅÍ ÅÇÏ ÓÏÄÅÒÖÉÍÙÍ?
+delete_fdesc=îÁ×ÓÅÇÄÁ ÕÄÁÌÉÔØ ÆÁÊÌ $1 ?
+delete_mdesc=îÁ×ÓÅÇÄÁ ÕÄÁÌÉÔØ ÕËÁÚÁÎÎÙÅ ÆÁÊÌÙ É ËÁÔÁÌÏÇÉ? :
+delete_efailed=îÅ ÕÄÁÌÏÓØ ÕÄÁÌÉÔØ $1 : $2
+
+mkdir_title=îÏ×ÙÊ ËÁÔÁÌÏÇ
+mkdir_dir=îÏ×ÙÊ ËÁÔÁÌÏÇ:
+mkdir_eexists=$1 ÕÖÅ ÓÕÝÅÓÔ×ÕÅÔ
+mkdir_efailed=îÅ ÕÄÁÌÏÓØ ÓÏÚÄÁÔØ ËÁÔÁÌÏÇ : $1
+mkdir_eaccess=õ ×ÁÓ ÎÅÄÏÓÔÁÔÏÞÎÏ ÐÒÁ× ÄÌÑ ÓÏÚÄÁÎÉÑ '$1'
+
+link_title=óÏÚÄÁÎÉÅ ÓÓÙÌËÉ
+link_from=éÓÔÏÞÎÉË:
+link_to=íÅÓÔÏ ÎÁÚÎÁÞÅÎÉÑ:
+link_eexists=$1 ÕÖÅ ÓÕÝÅÓÔ×ÕÅÔ
+link_efailed=óÏÚÄÁÔØ ÓÓÙÌËÕ ÎÅ ÕÄÁÌÏÓØ : $1
+link_efrom=õ ×ÁÓ ÎÅÄÏÓÔÁÔÏÞÎÏ ÐÒÁ× ÄÌÑ ÓÏÚÄÁÎÉÑ ÓÓÙÌËÉ '$1'
+link_efollow=õ ×ÁÓ ÎÅÄÏÓÔÁÔÏÞÎÏ ÐÒÁ× ÄÌÑ ÓÏÚÄÁÎÉÑ ÓÓÙÌÏË
+
+rename_title=ðÅÒÅÉÍÅÎÏ×ÁÎÉÅ $1
+rename_old=óÔÁÒÏÅ ÉÍÑ:
+rename_new=îÏ×ÏÅ ÉÍÑ:
+rename_ok=ðÅÒÅÉÍÅÎÏ×ÁÔØ
+rename_eexists=æÁÊÌ Ó ÉÍÅÎÅÍ $1 ÕÖÅ ÓÕÝÅÓÔ×ÕÅÔ
+rename_efailed=ðÅÒÅÉÍÅÎÏ×ÁÔØ ÎÅ ÕÄÁÌÏÓØ : $1
+rename_eold=õ ×ÁÓ ÎÅÄÏÓÔÁÔÏÞÎÏ ÐÒÁ× ÄÌÑ ÐÅÒÅÉÍÅÎÏ×ÁÎÉÑ '$1'
+rename_enew=õ ×ÁÓ ÎÅÄÏÓÔÁÔÏÞÎÏ ÐÒÁ× ÄÌÑ ÐÅÒÅÉÍÅÎÏ×ÁÎÉÑ × '$1'
+
+file_type0=ëÁÔÁÌÏÇ
+file_type1=ôÅËÓÔÏ×ÙÊ ÆÁÊÌ
+file_type2=æÁÊÌ ÉÚÏÂÒÁÖÅÎÉÑ
+file_type3=âÉÎÁÒÎÙÊ ÆÁÊÌ
+file_type4=æÁÊÌ
+file_type5=óÉÍ×ÏÌØÎÁÑ ÓÓÙÌËÁ
+file_type6=æÁÊÌ ÕÓÔÒÏÊÓÔ×Á
+file_type7=ëÁÎÁÌ
+
+view_enormal=íÏÖÎÏ ÐÒÏÓÍÁÔÒÉ×ÁÔØ ÔÏÌØËÏ ÏÂÙÞÎÙÅ ÆÁÊÌÙ
+view_enormal2=íÏÖÎÏ ÚÁÇÒÕÖÁÔØ ÔÏÌØËÏ ÏÂÙÞÎÙÅ ÆÁÊÌÙ
+view_eaccess=õ ×ÁÓ ÎÅÄÏÓÔÁÔÏÞÎÏ ÐÒÁ× ÄÌÑ ÄÏÓÔÕÐÁ Ë $1
+view_eopen=îÅ ÕÄÁÌÏÓØ ÏÔËÒÙÔØ $1 : $2
+
+paste_ecopy=ðÅÒÅÄ ×ÓÔÁ×ËÏÊ ÎÅÏÂÈÏÄÉÍÏ ×ÙÒÅÚÁÔØ ÉÌÉ ËÏÐÉÒÏ×ÁÔØ
+paste_egone=ëÏÐÉÒÕÅÍÙÊ ÆÁÊÌ $1 ÂÏÌØÛÅ ÎÅ ÓÕÝÅÓÔ×ÕÅÔ
+paste_eover=$1 ÎÅ ÍÏÖÅÔ ÂÙÔØ ÐÅÒÅÚÁÐÉÓÁÎ
+paste_eself=îÅÌØÚÑ ÐÅÒÅÐÉÓÁÔØ ÆÁÊÌ ÓÁÍÉÍ ÓÏÂÏÊ
+paste_emfailed=îÅ ÕÄÁÌÏÓØ ÐÅÒÅÍÅÓÔÉÔØ : $1
+paste_ecfailed=îÅ ÕÄÁÌÏÓØ ÓËÏÐÉÒÏ×ÁÔØ : $1
+
+over_title=æÁÊÌ ÓÕÝÅÓÔ×ÕÅÔ
+over_msg=æÁÊÌ $1 ÕÖÅ ÓÕÝÅÓÔ×ÕÅÔ. õËÁÖÉÔÅ ÄÌÑ ×ÓÔÁ×ÌÑÅÍÏÇÏ ÆÁÊÌÁ ÎÏ×ÏÅ ÉÍÑ.
+over_new=îÏ×ÏÅ ÉÍÑ ÆÁÊÌÁ:
+over_ok=ïë
+
+upload_efailed=îÅ ÕÄÁÌÏÓØ ÎÁÞÁÔØ ÚÁÇÒÕÚËÕ : $1
+upload_title=úÁÇÒÕÚËÁ ÆÁÊÌÁ
+upload_file=æÁÊÌ ÄÌÑ ÚÁÇÒÕÚËÉ
+upload_dir=úÁÇÒÕÖÁÔØ × ËÁÔÁÌÏÇ
+upload_ok=úÁÇÒÕÚÉÔØ
+upload_conv=ðÒÅÏÂÒÁÚÏ×ÁÔØ ÐÅÒÅ×ÏÄÙ ÓÔÒÏË DOS?
+upload_efile=îÅ ×ÙÂÒÁΠÆÁÊÌ ÄÌÑ ÚÁÇÒÕÚËÉ.
+upload_edir=ëÁÔÁÌÏÇ ÄÌÑ ÚÁÇÒÕÚËÉ ÎÅ ÓÕÝÅÓÔ×ÕÅÔ.
+upload_eperm=õ ×ÁÓ ÎÅÄÏÓÔÁÔÏÞÎÏ ÐÒÁ× ÄÌÑ ÓÏÚÄÁÎÉÑ $1
+upload_ewrite=îÅ ÕÄÁÌÏÓØ ÚÁÐÉÓÁÔØ × $1 : $2.
+
+find_eaccess=õ ×ÁÓ ÎÅÄÏÓÔÁÔÏÞÎÏ ÐÒÁ× ÄÌÑ ÄÏÓÔÕÐÁ Ë $1
+find_eexist=$1 ÎÅ ÓÕÝÅÓÔ×ÕÅÔ × $2
+find_edir=$1 ÎÅ Ñ×ÌÑÅÔÓÑ ËÁÔÁÌÏÇÏÍ × $2
+
+cancel=ïÔÍÅÎÁ
+
+chmod_eaccess=õ ×ÁÓ ÎÅÄÏÓÔÁÔÏÞÎÏ ÐÒÁ× ÄÌÑ ÄÏÓÔÕÐÁ Ë '$1'
+chmod_euser=$1 : ÎÅÔ ÔÁËÏÇÏ ÐÏÌØÚÏ×ÁÔÅÌÑ
+chmod_egroup=$1 : ÎÅÔ ÔÁËÏÊ ÇÒÕÐÐÙ
+chmod_elink=ïÛÉÂËÁ ÐÒÉ ×ÙÚÏ×Å symlink : $1
+chmod_echown=ïÛÉÂËÁ ÐÒÉ ×ÙÚÏ×Å chown : $1
+chmod_echmod=ïÛÉÂËÁ ÐÒÉ ×ÙÚÏ×Å chmod : $1
+chmod_efollow=õ ×ÁÓ ÎÅÄÏÓÔÁÔÏÞÎÏ ÐÒÁ× ÄÌÑ ÉÚÍÅÎÅÎÉÑ ÓÉÍ×ÏÌØÎÙÈ ÓÓÙÌÏË
+
+copy_efrom=õ ×ÁÓ ÎÅÄÏÓÔÁÔÏÞÎÏ ÐÒÁ× ÄÌÑ ËÏÐÉÒÏ×ÁÎÉÑ ÉÚ '$1'
+copy_eto=õ ×ÁÓ ÎÅÄÏÓÔÁÔÏÞÎÏ ÐÒÁ× ÄÌÑ ËÏÐÉÒÏ×ÁÎÉÑ × '$1'
+copy_elink=ïÛÉÂËÁ ÐÒÉ ×ÙÚÏ×Å symlink : $1
+
+delete_eaccess=õ ×ÁÓ ÎÅÄÏÓÔÁÔÏÞÎÏ ÐÒÁ× ÄÌÑ ÕÄÁÌÅÎÉÑ '$1'
+
+list_eaccess=õ ×ÁÓ ÎÅÄÏÓÔÁÔÏÞÎÏ ÐÒÁ× ÄÌÑ ÄÏÓÔÕÐÁ Ë ÜÔÏÍÕ ËÁÔÁÌÏÇÕ
+list_edir=îÅ ÕÄÁÌÏÓØ ÐÒÏÓÍÏÔÒÅÔØ $1 : $2
+
+move_eto=õ ×ÁÓ ÎÅÄÏÓÔÁÔÏÞÎÏ ÐÒÁ× ÄÌÑ ÐÅÒÅÍÅÝÅÎÉÑ × '$1'
+move_afrom=õ ×ÁÓ ÎÅÄÏÓÔÁÔÏÞÎÏ ÐÒÁ× ÄÌÑ ÐÅÒÅÍÅÝÅÎÉÑ '$1'
+
+acl_user=ïÂÒÁÝÁÔØÓÑ Ë ÆÁÊÌÁÍ ÎÁ ÓÅÒ×ÅÒÅ ÐÏÄ ÉÍÅÎÅÍ ÐÏÌØÚÏ×ÁÔÅÌÑ
+acl_user_def=ðÏÌØÚÏ×ÁÔÅÌÑ Webmin
+acl_umask=Umask ÄÌÑ ÎÏ×ÙÈ ÆÁÊÌÏ×
+acl_follow=÷ÓÅÇÄÁ ÐÅÒÅÈÏÄÉÔØ ÐÏ ÓÉÍ×ÏÌØÎÙÍ ÓÓÙÌËÁÍ?
+acl_ro=òÅÖÉÍ ÔÏÌØËÏ ÄÌÑ ÞÔÅÎÉÑ?
+acl_dirs=ðÏÚ×ÏÌÑÔØ ÄÏÓÔÕРÔÏÌØËÏ Ë ËÁÔÁÌÏÇÁÍ
+acl_home=÷ËÌÀÞÁÑ ÄÏÍÁÛÎÉÊ ËÁÔÁÌÏÇ ÐÏÌØÚÏ×ÁÔÅÌÑ Webmin
+acl_log=÷ÅÓÔÉ ÖÕÒÎÁÌ ×ÓÅÈ ÉÚÍÅÎÅÎÉÊ ÆÁÊÌÏ×?
+acl_goto=ïÔËÒÙ×ÁÔØ ÐÅÒ×ÙÊ ÄÏÓÔÕÐÎÙÊ ËÁÔÁÌÏÇ?
+
+share_title=äÏÓÔÕРÐÏ ÓÅÔÉ
+share_samba=Windows
+share_nfs=NFS
+share_son=äÏÓÔÕРÉÚ ÓÅÔÉ Windows ÒÁÚÒÅÛÅÎ
+share_soff=äÏÓÔÕРÉÚ ÓÅÔÉ Windows ÚÁÐÒÅÝÅÎ
+share_writable=úÁÐÉÓØ ÒÁÚÒÅÛÅÎÁ?
+share_available=äÏÓÔÕÐÅΠנÄÁÎÎÙÊ ÍÏÍÅÎÔ?
+share_sheader=îÁÓÔÒÏÊËÁ ÄÏÓÔÕÐÁ
+share_only=ôÏÌØËÏ
+share_guest=äÏÓÔÕРÂÅÚ ÁÕÔÅÎÔÉÆÉËÁÃÉÉ (Guest)?
+share_comment=ëÏÍÍÅÎÔÁÒÉÊ
+share_nheader=îÁÓÔÒÏÊËÁ ÄÏÓÔÕÐÁ ÐÏ ÐÒÏÔÏËÏÌÕ NFS
+share_non=äÏÓÔÕРÐÏ NFS ÒÁÚÒÅÛÅÎ
+share_noff=äÏÓÔÕРÐÏ NFS ÚÁÐÒÅÝÅÎ
+share_desc=ïÐÉÓÁÎÉÅ
+share_ro=õÚÌÙ, ÉÍÅÀÝÉÅ ÄÏÓÔÕРÔÏÌØËÏ ÄÌÑ ÞÔÅÎÉÑ
+share_rw=õÚÌÙ, ÉÍÅÀÝÉÅ ÄÏÓÔÕРÄÌÑ ÞÔÅÎÉÑ É ÚÁÐÉÓÉ
+share_root=õÚÌÙ, ÄÌÑ ËÏÔÏÒÙÈ ÒÁÚÒÅÛÅΠÄÏÓÔÕРӠÐÒÁ×ÁÍÉ root
+share_none=îÅÔ
+share_all=÷ÓÅ
+share_listed=õËÁÚÁÎÎÙÅ..
+share_host=õÚÌÙ
+share_opts=îÁÓÔÒÏÊËÁ
+share_s0=äÏ×ÅÒÑÔØ nobody
+share_s1=äÏ×ÅÒÑÔØ ÎÅ-root
+share_s2=äÏ×ÅÒÑÔØ ×ÓÅÍ
+share_lro=ôÏÌØËÏ ÞÔÅÎÉÅ
+share_lrw=þÔÅÎÉÅ/ÚÁÐÉÓØ
+
+log_create_export=óÏÚÄÁΠÒÅÓÕÒÓ NFS $1
+log_modify_export=éÚÍÅÎÅΠÒÅÓÕÒÓ NFS $1
+log_delete_export=õÄÁÌÅΠÒÅÓÕÒÓ NFS $1
+log_create_share=óÏÚÄÁΠÒÅÓÕÒÓ Samba $1
+log_modify_share=éÚÍÅÎÅΠÒÅÓÕÒÓ Samba $1
+log_delete_share=õÄÁÌÅΠÒÅÓÕÒÓ Samba $1
+log_save=óÏÈÒÁÎÅΠÆÁÊÌ $1
+log_chmod=éÚÍÅÎÅÎÙ ÐÒÁ×Á ÎÁ ÆÁÊÌ $1
+log_mkdir=óÏÚÄÁΠËÁÔÁÌÏÇ $1
+log_upload=úÁÇÒÕÖÅΠÆÁÊÌ $1
+log_link=óÏÚÄÁÎÁ ÓÉÍ×ÏÌØÎÁÑ ÓÓÙÌËÁ $1 ÎÁ $2
+log_relink=éÚÍÅÎÅÎÁ ÓÉÍ×ÏÌØÎÁÑ ÓÓÙÌËÁ $1 ÎÁ $2
+log_copy=óËÏÐÉÒÏ×ÁΠÆÁÊÌ $1 × $2
+log_move=ðÅÒÅÍÅÝÅΠÆÁÊÌ $1 × $2
+log_delete=õÄÁÌÅΠÆÁÊÌ $1
+log_attr=éÚÍÅÎÅÎÙ ÁÔÒÉÂÕÔÙ ÆÁÊÌÁ $1
+log_acl=äÌÑ ÆÁÊÌÁ $1 ÎÁÚÎÁÞÅΠACL
+
+search_eaccess=õ ×ÁÓ ÎÅÄÏÓÔÁÔÏÞÎÏ ÐÒÁ× ÄÌÑ ÐÏÉÓËÁ × ÜÔÏÍ ËÁÔÁÌÏÇÅ
+search_title=ðÏÉÓË ÆÁÊÌÏ×
+search_ok=éÓËÁÔØ
+search_dir=éÓËÁÔØ × ËÁÔÁÌÏÇÅ
+search_match=æÁÊÌÙ ÓÏ×ÐÁÄÁÀÝÉÅ Ó
+search_user=ðÒÉÎÁÄÌÅÖÁÝÉÅ ÐÏÌØÚÏ×ÁÔÅÌÀ
+search_group=ðÒÉÎÁÄÌÅÖÁÝÉÅ ÇÒÕÐÐÅ
+search_any=ìÀÂÏÊ
+search_type=ôÉРÆÁÊÌÁ
+search_types_=ìÀÂÏÊ
+search_types_f=æÁÊÌ
+search_types_d=ëÁÔÁÌÏÇ
+search_types_l=óÉÍ×ÏÌØÎÁÑ ÓÓÙÌËÁ
+search_types_p=éÍÅÎÏ×ÁÎÎÙÊ ËÁÎÁÌ
+search_size=òÁÚÍÅÒ ÆÁÊÌÁ
+search_more=âÏÌÅÅ
+search_less=íÅÎÅÅ
+search_xdev=éÓËÁÔØ × ÐÏÄÍÏÎÔÉÒÏ×ÁÎÎÙÈ ÆÁÊÌÏ×ÙÈ ÓÉÓÔÅÍÁÈ?
+search_edir=ëÁÔÁÌÏÇ ÄÌÑ ÐÏÉÓËÁ ÎÅ ÕËÁÚÁΠÉÌÉ ÕËÁÚÁΠÎÅ×ÅÒÎÏ
+search_ematch=òÅÇÕÌÑÒÎÏÅ ×ÙÒÁÖÅÎÉÅ ÄÌÑ ÐÏÉÓËÁ ÎÅ ÕËÁÚÁÎÏ ÉÌÉ ÕËÁÚÁÎÏ ÎÅ×ÅÒÎÏ
+search_euser=éÍÑ ÐÏÌØÚÏ×ÁÔÅÌÑ ÎÅ ÕËÁÚÁÎÏ ÉÌÉ ÕËÁÚÁÎÏ ÎÅ×ÅÒÎÏ
+search_egroup=éÍÑ ÇÒÕÐÐÙ ÎÅ ÕËÁÚÁÎÏ ÉÌÉ ÕËÁÚÁÎÏ ÎÅ×ÅÒÎÏ
+search_esize=òÁÚÍÅÒ ÆÁÊÌÁ ÄÏÌÖÅΠÂÙÔØ ÃÅÌÙÍ ÞÉÓÌÏÍ
+search_crit=ëÒÉÔÅÒÉÊ ÐÏÉÓËÁ
+search_list=òÅÚÕÌØÔÁÔÙ ÐÏÉÓËÁ
+
+facl_eaccess=õ ×ÁÓ ÎÅÄÏÓÔÁÔÏÞÎÏ ÐÒÁ× ÄÌÑ ÉÚÍÅÎÅÎÉÑ ACL ÄÌÑ ÜÔÏÇÏ ÆÁÊÌÁ
+
+attr_eattrs=îÅ ÕÄÁÌÏÓØ ÓÞÉÔÁÔØ ÁÔÒÉÂÕÔÙ : $1
+attr_efs=æÁÊÌÏ×ÁÑ ÓÉÓÔÅÍÁ $1 ÎÅ ÐÏÄÄÅÒÖÉ×ÁÅÔ ÁÔÒÉÂÕÔÙ
+attr_add=äÏÂÁ×ÉÔØ ÁÔÒÉÂÕÔ
+attr_name=îÁÚ×ÁÎÉÅ ÁÔÒÉÂÕÔÁ
+attr_value=úÎÁÞÅÎÉÅ ÁÔÒÉÂÕÔÁ
+attr_efailed=îÅ ÕÄÁÌÏÓØ ÕÓÔÁÎÏ×ÉÔØ ÁÔÒÉÂÕÔÙ ÄÌÑ $1 : $2
+attr_title=áÔÒÉÂÕÔÙ ÆÁÊÌÁ ÄÌÑ $1
+attr_create=äÏÂÁ×ÉÔØ ÁÔÒÉÂÕÔ
+attr_edit=éÚÍÅÎÉÔØ ÁÔÒÉÂÕÔ
+attr_ename=îÅ ÕËÁÚÁÎÏ ÎÁÚ×ÁÎÉÅ ÁÔÒÉÂÕÔÁ
+
+ext_eattrs=îÅ ÕÄÁÌÏÓØ ÐÏÌÕÞÉÔØ ÁÔÒÉÂÕÔÙ EXT : $1
+ext_efs=æÁÊÌÏ×ÁÑ ÓÉÓÔÅÍÁ $1 ÎÅ ÐÏÄÄÅÒÖÉ×ÁÅÔ ÁÔÒÉÂÕÔÙ EXT
+ext_title=áÔÒÉÂÕÔÙ EXT ÄÌÑ $1
+ext_header=áÔÒÉÂÕÔÙ EXT ÆÁÊÌÁ
+ext_efailed=îÅ ÕÄÁÌÏÓØ ÕÓÔÁÎÏ×ÉÔØ ÁÔÒÉÂÕÔÙ EXT $1 : $2
+
+eattr_A=îÅ ÏÂÎÏ×ÌÑÔØ ×ÒÅÍÑ ÄÏÓÔÕÐÁ
+eattr_a=÷ÏÚÍÏÖÎÏ ÔÏÌØËÏ ÄÏÂÁ×ÌÅÎÉÅ Ë ÆÁÊÌÕ
+eattr_c=óÖÉÍÁÔØ ÄÁÎÎÙÅ ÎÁ ÄÉÓËÅ
+eattr_d=îÅ ÐÒÏÉÚ×ÏÄÉÔØ ÒÅÚÅÒ×ÎÏÅ ËÏÐÉÒÏ×ÁÎÉÅ Ó ÐÏÍÏÝØÀ dump
+eattr_i=úÁÐÒÅÔÉÔØ ÉÚÍÅÎÅÎÉÅ
+eattr_s=ïÂÎÕÌÑÔØ ÂÌÏËÉ ÐÒÉ ÕÄÁÌÅÎÉÉ
+eattr_S=÷ÓÅÇÄÁ ÓÉÎÈÒÏÎÉÚÉÒÏ×ÁÔØ ÐÏÓÌÅ ÚÁÐÉÓÉ
+eattr_u=óÏÈÒÁÎÑÔØ ÓÏÄÅÒÖÉÍÏÅ ÆÁÊÌÁ ÄÌÑ ×ÏÓÓÔÁÎÏ×ÌÅÎÉÑ
+
diff --git a/file/lang/sk b/file/lang/sk
new file mode 100644 (file)
index 0000000..58a4ffd
--- /dev/null
@@ -0,0 +1,297 @@
+index_title=Správca Systému Súborov
+index_eremote=&#381;iadny Unixový u&#382;ívate&#318; nezodpovedá hodnotám z Webmin prihláse&#269;nia $1.
+switch_euser=Tento Unixový u&#382;ívate&#318;  nejestvuje!
+
+top_ret=Index
+top_down=Ulo&#382;
+top_edit=Otvor pre zmeny
+top_refresh=Obnov
+top_info=Info
+top_eacl=ACL
+top_attr=Atribúty
+top_ext=EXT
+top_delete=Vyma&#382;
+top_new=Nový(á/é)
+top_upload=Nalo&#382;
+top_rename=Premenuj
+top_copy=Kópia
+top_cut=Vyber
+top_paste=Vlo&#382;
+top_share=Zdie&#318;anie
+top_search=Nájdi
+
+right_name=Meno
+right_size=Ve&#318;kos&#357;
+right_user=U&#382;ívate&#318;
+right_group=Skupina
+right_date=Dátum
+
+edit_enormal=Len normálne súbory mô&#382;u by&#357; menené
+edit_title=Zmeny na $1
+edit_title2=Vytvánie súboru
+edit_filename=Meno súboru:
+edit_eover=$1 nemô&#382;e by&#357; prepísané
+edit_esave=NEmoohol ulo&#382;i&#357; : $1
+edit_eaccess=Nemá&#353; povolenie na ukladanie '$1'
+
+info_file=Súbor
+info_path=Cesta:
+info_type=Typ:
+info_size=Ve&#318;kos&#357;:
+info_mod=Zmenené:
+info_link=Vázba na:
+info_perms=Povolenia
+info_user=U&#382;ívate&#318;:
+info_group=Skupina:
+info_other=Iné:
+info_sticky=Lep&#269;kavé:
+info_sticky2=Len majitelia mô&#382;u vymaza&#357; 
+info_own=Majetok
+info_setuid=Setuid:
+info_setuid2=Vykonaj ako u&#382;ívate&#318;
+info_setgid=Setgid:
+info_setgid2=Súbory dedia skupinu
+info_setgid3=Vykonaj ako skupina
+info_apply=AKtivuj zmeny
+info_apply1=Len tento adresár
+info_apply2=Tento adresár a jeho súbory
+info_apply3=Tento adresár a v&#353;etky jeho priradené adresáre 
+info_efailed=Porucha pri  zmene $1 : $2
+info_read=&#268;ítaj
+info_write=Pí&#353;
+info_list=Zoznam
+info_exec=Exec
+
+eacl_eacls=Na&#269;ítavanie ACL súborov nebolo úspe&#353;né : $1
+eacl_acltype=Typ ACL
+eacl_aclname=Pou&#382;i na
+eacl_aclperms=Povolenia
+eacl_add=Pridaj typ ACL :
+eacl_remove=Odním ACL
+eacl_efs=Súborový systém $1 plne nepodporuje ACLs
+eacl_create=Vytvor ACL
+eacl_edit=Edituj ACL
+eacl_user=Majite&#318; súboru $1
+eacl_group=Skupina súboru $1
+eacl_eowner=U&#382;ívate&#318; alebo skupina súboru neexistuje
+eacl_efailed=Nemohol som nastavi&#357; ACL pre $1 : $2
+eacl_emask=Maximálne mô&#382;e by&#357; pou&#382;itá jedna maskovaná ACL
+eacl_edefmask=Maximálne mô&#382;e by&#357; pou&#382;itá jedna základná maskovaná ACL
+eacl_title=ACL pre $1
+eacl_owner=Majite&#318; súboru
+eacl_edefaults=Ak súbor má základnú ACL, musí tie&#382; ma&#357; základného majite&#318;a, skupinu a ostatné ACL.
+
+acltype_user=U&#382;ívate&#318;
+acltype_group=Skupina
+acltype_other=Iné
+acltype_mask=Maska
+acltype_default_user=Základný u&#382;ívate&#318;
+acltype_default_group=Základná skupina
+acltype_default_other=Základné Iné
+acltype_default_mask=Základná Maska
+
+delete_mtitle=Vyma&#382; viacero súborov
+delete_dtitle=Vyma&#382; adresár
+delete_ftitle=Vyma&#382; súbor
+delete_ddesc=Si si istý &#382;e chce&#353; permanente vymaza&#357; adresár $1 a v&#353;etok jeho obsah?
+delete_fdesc=Si si istý &#382;e chce&#353; permanente vymaza&#357; súbor $1 ?
+delete_mdesc=Si si istý &#382;e chce&#353; permanente vymaza&#357; adresáre a súbory? :
+delete_efailed=Nemo&#382;né vymaza&#357; $1 : $2
+
+mkdir_title=Novo-vytvorený Adresár
+mkdir_dir=Nový Adresár:
+mkdir_eexists=$1 u&#382; jestvuje
+mkdir_efailed=Vytvorenie Adresáru nebolo úspe&#353;né : $1
+mkdir_eaccess=Nemá&#353; povolenie na vytvorenie '$1'
+
+link_title=Vytvor Spojenie
+link_from=Spojenie z:
+link_to=Spojenie na:
+link_eexists=$1 u&#382; jestvuje
+link_efailed=Spojenie nebolo úspe&#353;né : $1
+link_efrom=Nemá&#353; dovolené vytvára&#357; spojenia na '$1'
+link_efollow=Nemá&#353; dostato&#269;né povolenie na vytváranie symbolických spojení
+
+rename_title=Premenuj $1
+rename_old=Pôvodné meno:
+rename_new=Nové meno:
+rename_ok=Premenuj
+rename_eexists=Súbor s menom $1 u&#382; jestvuje
+rename_efailed=Premenovávanie nebolo úspe&#353;né : $1
+rename_eold=Nemá&#353; dovolené premenováva&#357; '$1'
+rename_enew=Nemá&#353; dovolené premenováva&#357; na '$1'
+
+file_type0=Adresár
+file_type1=Textový súbor
+file_type2=Obrázok
+file_type3=Binarny súbor
+file_type4=Súbor
+file_type5=Symbolické spojenie
+file_type6=Súbor ovláda&#269;a
+file_type7=Rúrka
+
+view_enormal=Len normálne súbory mô&#382;u by&#357; prezerané
+view_enormal2=Len normálne súbory mô&#382;u by&#357; s&#357;ahované
+view_eaccess=Nemá&#353; povolený prístup k $1
+view_eopen=Otvorenie nebolo úspe&#353;né $1 : $2
+
+paste_ecopy=Musí&#353; najprv vybra&#357; alebo skopírova&#357; ak chce&#353; vklada&#357;
+paste_egone=Súbor kopírovaný $1 u&#382; neexiststuje
+paste_eover=$1 nemô&#382;e by&#357; prepísaný
+paste_eself=Nemô&#382;e&#353; vklada&#357; súbor do adresára so súborom s rovnakým menom
+paste_emfailed=Premiestnenie nebolo úspe&#353;né : $1
+paste_ecfailed=Vytváranie kópie nebolo úspe&#353;né : $1
+
+over_title=Súbor u&#382; Existstuje
+over_msg=Súbor $1 u&#382; existstuje. Pou&#382;i polí&#269;ko ni&#382;&#353;ie na premenovanie vkladaného súboru.
+over_new=Nové meno súboru:
+over_ok=Ok
+
+upload_efailed=Nalo&#382;ený súbor : $1 sa nepodarilo otvori&#357;
+upload_title=Nalo&#382; súbor
+upload_file=Súbor pre nakladanie
+upload_dir=Nalo&#382; do adresára
+upload_ok=Nalo&#382;
+upload_conv=Kovertuj DOS-ové symboly pre nový riadok?
+upload_efile=Nebol vybratý &#382;iaden súbor na nakladanie.
+upload_edir=Adresár na nakladanie neexistuje.
+upload_eperm=You are not allowed to create $1
+upload_ewrite=Failed to write to $1 : $2.
+
+find_eaccess=You are not allowed to access $1
+find_eexist=$1 does not exist in $2
+find_edir=$1 is not a directory in $2
+
+cancel=Zru&#353;
+
+chmod_eaccess=You are not allowed to access '$1'
+chmod_euser=$1 : no such user
+chmod_egroup=$1 : no such group
+chmod_elink=symlink failed : $1
+chmod_echown=chown failed : $1
+chmod_echmod=chmod failed : $1
+chmod_efollow=You are not allowed to edit symbolic links
+
+copy_efrom=You are not allowed to copy from '$1'
+copy_eto=You are not allowed to copy to '$1'
+copy_elink=symlink failed : $1
+
+delete_eaccess=You are not allowed to delete '$1'
+
+list_eaccess=You are not allowed to access this directory
+list_edir=Failed to list $1 : $2
+
+move_eto=You are not allowed to move to '$1'
+move_afrom=You are not allowed to move '$1'
+
+acl_user=Access files on server as user
+acl_user_def=Same as Webmin login
+acl_umask=Umask for new files
+acl_follow=Always follow symlinks?
+acl_ro=Read-only mode?
+acl_dirs=Only allow access to directories
+acl_home=Include home directory of Webmin user
+acl_log=Log all file modifications?
+acl_goto=Open first allowed directory?
+
+share_title=Sharing
+share_samba=Windows
+share_nfs=NFS
+share_son=Windows file sharing enabled
+share_soff=Windows file sharing disabled
+share_writable=Writable?
+share_available=Currently active?
+share_sheader=Sharing options
+share_only=Only
+share_guest=Guest access?
+share_comment=Comment
+share_nheader=NFS export options
+share_non=NFS file sharing enabled
+share_noff=NFS file sharing disabled
+share_desc=Description
+share_ro=Read-only hosts
+share_rw=Read-write hosts
+share_root=Root access hosts
+share_none=None
+share_all=All
+share_listed=Listed..
+share_host=Hosts
+share_opts=Options
+share_s0=Trust nobody
+share_s1=Trust non-root
+share_s2=Trust everybody
+share_lro=Read-only
+share_lrw=Read-write
+
+log_create_export=Created NFS export $1
+log_modify_export=Modified NFS export $1
+log_delete_export=Deleted NFS export $1
+log_create_share=Created Samba share $1
+log_modify_share=Modified Samba share $1
+log_delete_share=Deleted Samba share $1
+log_save=Saved file $1
+log_chmod=Changed permissions on file $1
+log_mkdir=Created directory $1
+log_upload=Uploaded file $1
+log_link=Created symbolic link $1 to $2
+log_relink=Modified symbolic link $1 to $2
+log_copy=Copied file $1 to $2
+log_move=Moved file $1 to $2
+log_delete=Deleted file $1
+log_attr=Set attributes on file $1
+log_acl=Set ACL on file $1
+
+search_eaccess=You are not allowed to search this directory
+search_title=Find files
+search_ok=Search Now
+search_dir=Search directory
+search_match=For files matching
+search_user=Owned by user
+search_group=Owned by group
+search_any=Any
+search_type=File type
+search_types_=Any
+search_types_f=File
+search_types_d=Directory
+search_types_l=Symbolic link
+search_types_p=Named pipe
+search_size=File size
+search_more=More than
+search_less=Less than
+search_xdev=Search past mounts?
+search_edir=Missing or invalid search directory
+search_ematch=Missing matching regexp
+search_euser=Missing username
+search_egroup=Missing group name
+search_esize=File size must be an integer
+search_crit=Search criteria
+search_list=Search results
+
+facl_eaccess=You are not allowed to set ACLs for this file
+
+attr_eattrs=Failed to get attributes : $1
+attr_efs=The filesystem $1 does not support attributes
+attr_add=Add Attribute
+attr_name=Attribute Name
+attr_value=Attribute Value
+attr_efailed=Failed to set attributes for $1 : $2
+attr_title=File Attributes for $1
+attr_create=Add Attribute
+attr_edit=Edit Attribute
+attr_ename=Missing attribute name
+
+ext_eattrs=Failed to get EXT attributes : $1
+ext_efs=The filesystem $1 does not support EXT attributes
+ext_title=EXT attributes for $1
+ext_header=EXT file attributes
+ext_efailed=Failed to set attributes for $1 : $2
+
+eattr_A=Do not update access times
+eattr_a=Can only append to file
+eattr_c=Compress data on disk
+eattr_d=Do not backup with dump
+eattr_i=Do not allow modification
+eattr_s=Zero blocks when deleting
+eattr_S=Always sync after writing
+eattr_u=Save contents for undeletion
+
diff --git a/file/lang/sv b/file/lang/sv
new file mode 100644 (file)
index 0000000..14a203a
--- /dev/null
@@ -0,0 +1,153 @@
+index_title=Filhanterare
+index_nojava=Denna modul behöver java för att fungera, men din läsare stödjer inte java
+
+top_open=Öppna
+top_view=Visa
+top_edit=Ändra
+top_refresh=Uppdatera
+top_info=Info
+top_delete=Ta bort
+top_new=Ny
+top_upload=Ladda in
+top_rename=Byt namn
+top_copy=Kopiera
+top_cut=Klipp ut
+top_paste=Klistra in
+
+right_name=Namn
+right_size=Storlek
+right_user=Användare
+right_group=Grupp
+right_date=Datum
+
+edit_enormal=Du kan bara ändra standardfiler
+edit_title=Ändrar i $1
+edit_title2=Skapar fil
+edit_filename=Filnamn:
+edit_eover=$1 får inte skrivas över
+edit_esave=Det gick inte att spara filen: $1
+edit_eaccess=Du får inte spara filen '$1'
+
+info_file=Fil
+info_path=Sökväg:
+info_type=Typ:
+info_size=Storlek:
+info_mod=Ändrad:
+info_link=Länk till:
+info_perms=Rättigheter
+info_user=Användare:
+info_group=Grupp:
+info_other=Övriga:
+info_sticky=Sticky:
+info_sticky2=Det är bara ägare som får ta bort filer
+info_own=Ägare
+info_setuid=Setuid:
+info_setuid2=Utför som användare
+info_setgid=Setgid:
+info_setgid2=Filer ärver grupp
+info_setgid3=Utför som grupp
+info_apply=Utför ändringarna på
+info_apply1=Endast denna katalog
+info_apply2=Denna katalog och filerna i den
+info_apply3=Denna katalog och dess underkataloger
+info_efailed=Det gick inte att uppdatera $1 : $2
+info_read=Läs
+info_write=Skriv
+info_list=Lista
+info_exec=Exec
+
+delete_dtitle=Ta bort katalog
+delete_ftitle=Ta bort fil
+delete_ddesc=Vill du verkligen ta bort katalogen $1 med innehåll permanent?
+delete_fdesc=Vill du verkligen ta bort filen $1 permanent?
+delete_efailed=Det gick inte att ta bort $1 : $2
+
+mkdir_title=Ny katalog
+mkdir_dir=Ny katalog:
+mkdir_eexists=$1 finns redan
+mkdir_efailed=Det gick inte att skapa katalogen: $1
+mkdir_eaccess=Du får inte skapa '$1'
+
+link_title=Skapa länk
+link_from=Länk från:
+link_to=Länk till:
+link_eexists=$1 finns redan
+link_efailed=Det gick inte att länka: $1
+link_efrom=Du får inte länka från '$1'
+link_efollow=Du får inte skapa symboliska länkar
+
+rename_title=Byt namn på $1
+rename_old=Gammalt namn:
+rename_new=Nytt namn:
+rename_ok=Byt namn
+rename_eexists=Det finns redan en fil som heter $1
+rename_efailed=Det gick inte att byta namn: $1
+rename_eold=Du får inte byta namn på '$1'
+rename_enew=Du får inte byta namn till '$1'
+
+file_type0=Katalog
+file_type1=Textfil
+file_type2=Bildfile
+file_type3=Binärfil
+file_type4=Fil
+file_type5=Symbolisk länk
+file_type6=Device-fil
+file_type7=Pipe
+
+view_enormal=Endast normala filer kan visas
+view_eaccess=Du får inte komma åt $1
+view_eopen=Det gick inte att öppna $1: $2
+
+paste_ecopy=Något måste klippas ut eller kopieras för att du ska kunna klistra in
+paste_egone=Den kopierade filen $1 finns inte längre
+paste_eover=$1 får inte skrivas över
+paste_eself=En fil får inte klistras in över sig själv
+paste_emfailed=Det gick inte att flytta: $1
+paste_ecfailed=Det gick inte att kopiera: $1
+
+over_title=Filen finns
+over_msg=Filen $1 finns redan. Skriv in ett nytt filnamn för den inklistrade filen i fältet nedan.
+over_new=Nytt filnamn:
+over_ok=OK
+
+upload_efailed=Det gick inte att sätta igång nedladdningen: $1
+upload_title=Ladda ned fil
+upload_file=Fil att ladda ned
+upload_dir=Ladda ned till katalog
+upload_ok=Ladda ned
+upload_conv=Konvertera DOS-radbrytningar?
+upload_efile=Du har inte valt någon fil som ska laddas ned.
+upload_edir=Du har inte valt någon katalog att ladda ned till.
+upload_eperm=Du får inte skapa $1
+upload_ewrite=Det gick inte att skriva till $1: $2.
+
+find_eaccess=Du får inte komma åt $1
+find_eexist=$1 finns inte i $2
+find_edir=$1 är inte en katalog i $2
+
+cancel=Avbryt
+
+chmod_eaccess=Du får inte komma åt '$1'
+chmod_euser=$1 :användaren finns inte
+chmod_egroup=$1 :gruppen finns inte
+chmod_elink=symlink misslyckades: $1
+chmod_echown=chown misslyckades: $1
+chmod_echmod=chmod misslyckades: $1
+
+copy_efrom=Du får inte kopiera från '$1'
+copy_eto=Du får inte kopiera till '$1'
+copy_elink=symlink misslyckades: $1
+
+delete_eaccess=Du får inte ta bort '$1'
+
+list_eaccess=Du får inte komma åt denna katalog
+list_edir=Det gick inte att lista $1: $2
+
+move_eto=Du får inte flytta filer till '$1'
+move_afrom=Du får inte flytta på '$1'
+
+acl_user=Kom åt filer på servern som användare
+acl_umask=Umask för nya filer
+acl_follow=Ska symboliska länkar alltid följas?
+acl_dirs=Tillåt endast åtkomst till kataloger
+
diff --git a/file/lang/tr b/file/lang/tr
new file mode 100644 (file)
index 0000000..c589107
--- /dev/null
@@ -0,0 +1,290 @@
+acl_archive=Dizinlerin arþivlerini indirebilir mi?
+acl_archmax=Evet, bundan daha küçükse
+acl_b=byte
+acl_button_acl=ACL (Posix ACL düzenle)
+acl_button_copy=Kopyala, Kes ve Yapýþtýr
+acl_button_delete=Sil (dosyalarý sil)
+acl_button_edit=Düzenle (metin dosyasýný düzenle)
+acl_button_info=Bilgi (dosya izinlerini ve sahipliðini düzenle)
+acl_button_makelink=Yeni (sembolink link oluþtur)
+acl_button_mkdir=Yeni (dizin oluþtur)
+acl_button_mount=Mount (dosya sistemini mount et ya da umount et)
+acl_button_new=Yeni (metin dosyasý oluþtur)
+acl_button_rename=Yeniden Adlandýr (dosyayý yeniden adlandýr)
+acl_button_save=Kaydet (dosya indir)
+acl_button_search=Bul (dosya bul)
+acl_button_sharing=Paylaþtýrma (Samba ve NFS dosya paylaþýmýný ayarla)
+acl_button_upload=Yükle (istemciden dosya yükle)
+acl_buttons=Araç çubuðundaki eriþilebilir butonlar
+acl_chroot=Dosya yöneticisinin tamamý için chroot dizini
+acl_dirs=Sadece bu dizinlere eriþime izin ver
+acl_follow=Sembolik linkler her zaman takip edilsin mi?
+acl_log=Bütün dosya deðiþikliklerinin kayýtlarý tutulsun mu?
+acl_max=Maksimum yükleme boyutu
+acl_ro=Sadece okunabilir mod?
+acl_umask=Yeni dosyalar için umask
+acl_unarchive=Yüklenen arþiv dosyalarýný açabilir mi?
+acl_unarchive0=$no
+acl_unarchive1=$yes
+acl_unlim=Limitsiz
+acl_user=Sunucudaki dosyalara bu kullanýcý olarak ulaþ :
+acl_user_def=Webmin kullanýcý adý ile ayný
+acltype_default_group=Öntanýmlý Grup
+acltype_default_mask=Öntanýmlý Mask
+acltype_default_user=Öntanýmlý Kullanýcý
+acltype_group=Grup
+acltype_mask=Mask
+acltype_other=Diðerleri
+acltype_user=Kullanýcý
+cancel=Ýptal
+chmod_eaccess='$1'e eriþim için izininiz yoktur
+chmod_echmod=chmod'da hata oluþtu  : $1
+chmod_echown=chown'da hata oluþtu : $1
+chmod_efollow=Sembolink linkleri düzenlemek için izininiz yoktur
+chmod_egroup=$1 : böyle bir grup yok
+chmod_elink=Sembolik linkte hata oluþtu : $1
+chmod_euser=$1 : böyle bir kullanýcý yok
+close=Kapat
+copy_efrom='$1'den kopyalamanýza izin verilmemiþtir
+copy_elink=Sembolik linkte hata oluþtu : $1
+copy_eto='$1'e kopyalamanýza izin verilmemiþtir
+ddir_rusure=$1 'in içeriðini bir arþiv dosyasý olarak indirmek için, aþaðýdaki arþiv tipi butonlarýndan birini týklayýnýz.
+ddir_tar=TAR
+ddir_tgz=TAR.GZ
+ddir_title=Ýndirme Dizini
+ddir_zip=ZIP
+delete_ddesc=$1 dizinini ve içindekileri silmek istediðinizden emin misiniz?
+delete_dtitle=Dizin Sil
+delete_eaccess='$1'i silmede hata oluþtu
+delete_efailed=$1'i silme iþleminde hata oluþtu : $2
+delete_fdesc=$1 dosyasýný kalýcý olarak silmek istediðinizden emin misiniz?
+delete_ftitle=Dosya sil
+delete_mdesc=Bu dosya ve dizinleri kalýcý olarak silmek istediðinizden emin misiniz? :
+delete_mtitle=Birden fazla dosya sil
+eacl_aclperms=Ýzinler
+eacl_acltype=ACL Tipi
+eacl_create=ACL Oluþtur
+eacl_eacls=ACL'lerin okunmasýnda hata oluþtu : $1
+eacl_edefaults=Eðer bir dosyanýn öntanýmlý ACL'si varsa bu dosya öntanýmlý kullanýcý, grup ve diðer ACL'lere de sahip olmalýdýr.
+eacl_edit=ACL Düzenle
+eacl_efs=$1 dosya sistemi ACL'leri desteklemiyor
+eacl_group=Dosya grubu $1
+eacl_owner=Dosya sahibi
+eacl_remove=ACL Sil
+eacl_user=Dosya sahibi $1
+eattr_A=Eriþim zamanlarýný güncelleme
+eattr_S=Yazmadan sonra her zaman senkronize et
+eattr_a=Dosyaya sadece ekleme yapýlabilir
+eattr_c=Diskteki veriyi sýkýþtýr
+eattr_d=Dump ile yedekleme
+eattr_i=Deðiþtirilmesine izin verme
+ebutton=Bu özellik kullanýlamaz
+edit_all=Hepsini deðiþtir
+edit_eaccess='$1'e kaydedilmesine izininiz yoktur
+edit_efollow=Sembolink link '$1'e yazmak için izininiz yoktur
+edit_enormal=Sadece normal dosyalar düzenlenebilir
+edit_eover=$1 üzerine yazýlamaz
+edit_esave=Dosyanýn kaydedilmesinde hata oluþtu : $1
+edit_filename=Dosya Ýsmi:
+edit_find=Bul
+edit_goto=Git
+edit_gotoline=Satýra git
+edit_notfound=$1 metini bulunamadý
+edit_replace=Deðiþtir
+edit_replaceby=Bununla deðiþtir
+edit_saveclose=Kaydet & Kapat
+edit_title=$1 düzenleniyor
+edit_title2=Dosya oluþturuluyor
+eopen=Ýndirmede hata : $1
+facl_eaccess=Bu dosyanýn ACL'lerini belirlemek için izininiz yoktur
+file_type0=Dizin
+file_type1=Metin Dosyasý
+file_type2=Resim Dosyasý
+file_type3=Ýkili Dosya
+file_type4=Dosya
+file_type5=Sembolik Link
+file_type6=Aygýt dosyasý
+file_type7=Pipe
+find_eaccess=$1'e eriþim izininiz yoktur
+find_edir=$1, $2'de bir dizin deðil
+find_eexist=$1, $2 içinde mevcut deðil
+index_eremote=Webmin kullanýcýsý $1 ile eþleþen Unix kullanýcýsý yok.
+index_nojava=Bu modül java gerektirir, fakat sizin tarayýcýnýz java'yi desteklemiyor
+index_title=Dosya Yöneticisi
+info_apply=Deðiþiklikleri uygula ...
+info_apply1=Sadece bu dizine
+info_apply2=Bu dizin ve dosyalarýna
+info_apply3=Bu dizin ve alt dizinlerine
+info_efailed=Güncellemede hata oluþtu $1 : $2
+info_exec=Çalýþtýr
+info_file=Dosya
+info_group=Grup:
+info_link=Link to:
+info_list=Listele
+info_mod=Deðiþtirildi:
+info_octal=Sekizli:
+info_other=Diðerleri:
+info_own=Sahiplik
+info_path=Yol:
+info_perms=Ýzinler
+info_read=Oku
+info_setgid=Setgid:
+info_setgid2=Dosyalar grubun olsun
+info_setgid3=Grup olarak çalýþtýr
+info_setuid=Setuid:
+info_setuid2=Kullanýcý olarak çalýþtýr
+info_size=Boyut:
+info_sticky=Sticky:
+info_sticky2=Dosyalarý sadece sahipleri silebilir
+info_type=Tip:
+info_user=Kullanýcý:
+info_write=Yaz
+link_eexists=$1 zaten mevcut
+link_efailed=Link yapýlýrken hata oluþtu : $1
+link_efollow=Sembolik link oluþturmak için izininiz yoktur
+link_efrom=Link kaynaðý tam olarak verilmelidir
+link_efrom2='$1'den link yapmak için izininiz yoktur
+link_from=Link buradan:
+link_title=Link Oluþtur
+link_to=Buraya link oluþtur:
+list_eaccess=Bu dizine eriþim izininiz yoktur
+log_chmod=$1 dosyasýnýn haklarý deðiþtirildi
+log_copy=$1 dosyasý $2'ye kopyalandý
+log_create_export=NFS sunumu $1 oluþturuldu
+log_create_share=Samba paylaþýmý $1 oluþturuldu
+log_delete=$1 dosyasý silindi
+log_delete_export=NFS sunumu $1 silindi
+log_delete_share=Samba paylaþýmý $1 silindi
+log_link=$2'ye sembolik link $1 oluþturuldu
+log_mkdir=$1 dizini oluþturuldu
+log_modify_export=NFS sunumu $1 deðiþtirildi
+log_modify_share=Samba paylaþýmý $1 deðiþtirildi
+log_move=$1 dosyasý $2'ye taþýndý
+log_relink=$2'ye sembolik link $1 deðiþtirildi
+log_save=$1 dosyasý kaydedildi
+log_upload=$1 dosyasý yüklendi
+mkdir_dir=Yeni dizin:
+mkdir_eaccess='$1' dizini oluþturmaya hakkýnýz yok
+mkdir_eexists=$1 dizini mevcut
+mkdir_efailed=Dizin oluþturmada hata oluþtu : $1
+mkdir_title=Yeni Dizin
+mount_eaccess=Dosya sistemini mount etmek için izininiz yoktur
+mount_efstab=Bu mount noktasýnda bir dosya sistemi mevcut deðildir
+mount_epoint=$1 bir mount noktasý deðildir
+mount_err1=$1 'in mount edilmesinde hata oluþtu : $2
+mount_err2=$1 'in umount edilmesinde hata oluþtu : $2
+mount_rusure1=$1 'i $2 'ye mount etmek istediðinize emin misiniz ?
+mount_rusure2=$1 'i $2 'den umount etmek istediðinize emin misiniz ?
+mount_title1=Dosya sistemi mount et
+mount_title2=Dosya sistemi umount et
+move_afrom='$1'i taþýmak için izininiz yoktur
+move_eto='$1'e taþýmak için izininiz yoktur
+over_msg=$1 dosyasý zaten mevcut. Yapýþtýrýlan dosyaya yeni bir isim girmek için aþaðýdaki alaný kullanýnýz.
+over_new=Yeni dosya ismi:
+over_ok=Tamam
+over_title=Dosya Mevcut
+paste_ecfailed=Kopyalamada hata oluþtu : $1
+paste_ecopy=Yapýþtýrmadan önce kopyalamalý veya kesmelisiniz
+paste_egone=Kopyalanan dosya $1 artýk yok
+paste_emfailed=Taþýmada hata oluþtu : $1
+paste_eover=$1 üzerine yazýlamaz
+paste_eself=Dosyayý kendi üzerine yapýþtýramazsýnýz
+rename_eexists=$1 dosyasý zaten mevcut
+rename_efailed=Yeniden adlandýrmada hata oluþtu : $1
+rename_enew='$1' olarak yeniden adlandýrmak için izininiz yoktur
+rename_eold='$1'i yeniden adlandýrmak için izininiz yoktur
+rename_new=Yeni Ýsim:
+rename_ok=Yeniden Adlandýr
+rename_old=Eski Ýsim:
+rename_title=Yeniden Adlandýr $1
+right_date=Tarih
+right_group=Grup
+right_name=Ýsim
+right_size=Boyut
+right_user=Kullanýcý
+search_crit=Armama kriteri
+search_dir=Arama dizini
+search_eaccess=Bu dizinde arama yapmak için izininiz yoktur
+search_edir=Eksik ya da geçersiz arama dizini
+search_egroup=Grup adý girilmemiþ
+search_esize=Dosya boyutu bir tamsayý olmalýdýr
+search_euser=Kullanýcý adý girilmemiþ
+search_list=Arama sonuçlarý
+search_ok=Þimdi Ara
+search_size=Dosya boyutu
+search_title=Dosya bul
+search_type=Dosya tipi
+search_types_=Hepsi
+search_types_d=Dizin
+search_types_f=Dosya
+search_types_l=Sembolink link
+share_all=Hepsi
+share_available=Aktif mi?
+share_comment=Yorum
+share_desc=Açýklama
+share_guest=Ziyaretçi eriþimi?
+share_host=Makineler
+share_listed=Listeli...
+share_lro=Sadece okunur
+share_lrw=Okunur-yazýlýr
+share_nfs=NFS
+share_nheader=NFS sunum seçenekleri
+share_noff=NFS dosya paylaþýmý kapalý
+share_non=NFS dosya paylaþýmý açýk
+share_none=Hiçbiri
+share_only=Sadece
+share_opts=Seçenekler
+share_ro=Sadece okuma izinli makineler
+share_root=Root giriþli makineler
+share_rw=Okuma-yazma izinli makineler
+share_s0=Hiçkimseye güvenme
+share_s1=Root olmayanlara güven
+share_s2=Herkese güven
+share_samba=Windows
+share_sheader=Paylaþým seçenekleri
+share_soff=Windows dosya paylaþýmý kapalý
+share_son=Windows dosya paylaþýmý açýk
+share_title=Paylaþtýrma
+share_writable=Yazýlabilir?
+switch_euser=Unix kullanýcýsý mevcut deðil!
+top_config=Yapýlandýrma
+top_copy=Kopyala
+top_cut=Kes
+top_delete=Sil
+top_down=Kaydet
+top_eacl=ACL
+top_edit=Düzenle
+top_ext=EXT
+top_info=Bilgi
+top_mount=Mount
+top_new=Yeni
+top_paste=Yapýþtýr
+top_refresh=Yenile
+top_rename=Yeniden Adlandýr
+top_ret=Ýndeks
+top_search=Ara
+top_share=Paylaþtýrma
+top_upload=Yükleme
+upload_already=$1 dosyasý zaten mevcut. Üzerine yazmak istediðinizden emin misiniz?
+upload_conv=DOS satýrlarý çevirilsin mi?
+upload_dir=Dizine Yükle
+upload_edir=Yükleme dizini mevcut deðil
+upload_efailed=Dosyanýn yüklenmesinde hata oluþtu : $1
+upload_efile=Yüklenecek dosya seçilmedi.
+upload_eperm=$1 oluþturmanýza izin verilmedi
+upload_ewrite=$1'i yazmada hata oluþtu : $2.
+upload_file=Yüklenecek dosya
+upload_ok=Yükle
+upload_title=Dosyayý Yükle
+upload_zip=ZIP ya da TAR dosyasý açýlsýn mý?
+view_eaccess=$1'e eriþim izininiz yoktur
+view_earchive=Arþivleri indirmek için izininiz yoktur
+view_earchmax=Seçili dizin arþivleme için izin verilen maksimum boyuttan ($1 byte) daha büyük
+view_ecomp=Arþiv oluþturulmasýnda hata oluþtu : $1
+view_edir=Bir arþiv sadece bir dizin için oluþturulabilir
+view_enormal=Sadece normal dosyalar görüntülenebilir
+view_enormal2=Sadece normal dosyalar indirilebilir
+view_eopen=$1 'in açýlmasýnda hata oluþtu : $2
+zip_ename=Bir zip, tar ya da tar.gz gibi görünmüyor
+zip_err=Dosya açýlamýyor : $1
+zip_euntar=Tar dosyasýnýn açýlmasýnda hata oluþtu : $1
diff --git a/file/lang/tr.bak b/file/lang/tr.bak
new file mode 100644 (file)
index 0000000..b3cf5ad
--- /dev/null
@@ -0,0 +1,198 @@
+index_title=Dosya Yöneticisi
+index_nojava=Bu modül java gerektirir, fakat sizin tarayýcýnýz java'yi desteklemiyor
+
+top_open=Aç
+top_view=Göster
+top_edit=Deðiþtir
+top_refresh=Yenile
+top_info=Bilgi
+top_delete=Sil
+top_new=Yeni
+top_upload=Yükleme(Upload)
+top_rename=Yeniden Adlandýr
+top_copy=Kopyala
+top_cut=Kes
+top_paste=Yapýþtýr 
+top_share=Paylaþtýr
+
+right_name=Ad
+right_size=Boyut
+right_user=Kullanýcý
+right_group=Grup
+right_date=Tarih
+
+edit_enormal=Sadece normal dosyalar deðiþtirilebilir
+edit_title=$1 deðiþtiriliyor
+edit_title2=Dosya oluþturuluyor
+edit_filename=Dosya Ýsmi:
+edit_eover=$1 üzerine yazýlamaz
+edit_esave=Dosyanýn kaydedilmesinde hata oluþtu : $1
+edit_eaccess='$1' kaydedilmesine izininiz yoktur
+
+info_file=Dosya
+info_path=Yol:
+info_type=Tip:
+info_size=Boyut:
+info_mod=Deðiþtirildi:
+info_link=Link to:#####
+info_perms=Haklar
+info_user=Kullanýcý:
+info_group=Grup:
+info_other=Diðerleri:
+info_sticky=Sticky:
+info_sticky2=Dosyalarý sadece sahipleri silebilir
+info_own=Sahiplik
+info_setuid=Uid'yi belirt:
+info_setuid2=Kullanýcý Olarak Çalýþtýr
+info_setgid=Gid'yi Oluþtur:
+info_setgid2=Dosyalar grubun olsun
+info_setgid3=Grup olarak çalýþtýr
+info_apply=Deðiþiklikleri uygula ...
+info_apply1=Sadece bu dizine
+info_apply2=Bu dizin ve dosyalarýna
+info_apply3=Bu dizin ve alt dizinlerine
+info_efailed=Güncellemede hata oluþtu $1 : $2
+info_read=Oku
+info_write=Yaz
+info_list=Listele
+info_exec=Çalýþtýr
+
+delete_dtitle=Dizin Sil
+delete_ftitle=Dosya Sil
+delete_ddesc=$1 dizinini ve içindekileri silmek istediðinizden eminmisiniz?
+delete_fdesc=$1 dizinini silmek istediinizden eminmisiniz? 
+delete_efailed=Silme iþleminde hata oluþtu $1 : $2
+
+mkdir_title=Yeni Dizin
+mkdir_dir=Yeni dizin:
+mkdir_eexists=$1 dizini mevcut
+mkdir_efailed=Dizin oluþturmada hata oluþtu : $1
+mkdir_eaccess='$1' dizini oluþturmaya hakkýnýz yok
+
+link_title=Link Oluþtur
+link_from=Link buradan:
+link_to=Link buraya:
+link_eexists=$1 her zaman mevcut
+link_efailed=Link oluþturulurken hata oluþtu : $1
+link_efrom='$1' den link oluþturmaya hakkýnýz yoktur
+link_efollow=Sembolik linkler oluþturmaya hakkýnýz yoktur
+
+rename_title=Yeniden Adlandýr $1
+rename_old=Eski Ýsmi:
+rename_new=Yeni Ýsmi:
+rename_ok=Yeniden Adlandýr
+rename_eexists=$1 dosyasý her zaman mevcut
+rename_efailed=Yeniden adlandýrmada hata oluþtu : $1
+rename_eold='$1'i yeniden adlandýrmaya hakkýnýz yoktur
+rename_enew='$1' olarak yeniden adlandyrmaya hakkynyz yoktur
+
+file_type0=Dizin
+file_type1=Yazý Dosyasý
+file_type2=Resim Dosyasý
+file_type3=Ýkili Dosya
+file_type4=Dosya
+file_type5=Sembolik Link
+file_type6=Araç Dosyasý
+file_type7=Boru
+
+view_enormal=Sadece normal dosyalar görüntülenebilir
+view_eaccess=$1'e eriþime hakkýnýz yoktur
+view_eopen=Açma iþleminde hata oluþtu $1 : $2
+
+paste_ecopy=Yapýþtýrmadan önce kopyalamalý veya kesmelisiniz
+paste_egone=Kopyalanan dosya $1 artýk yok
+paste_eover=$1 üzerine yazýlamaz
+paste_eself=Dosyayý kendi üzerine yapýþtýramazsýnýz
+paste_emfailed=Taþýmada hata oluþtu : $1
+paste_ecfailed=Kopyalamada hata olutu : $1
+
+over_title=Dosya Mevcut
+over_msg=$1 dosyasý her zaman mevcut. Yapýþtýrýlan dosyaya yeni bir isim girmek için boþ formu kullanýnýz.
+over_new=Yeni dosya ismi
+over_ok=Tamam
+
+upload_efailed=Dosyanýn yüklenmesinde hata oluþtu : $1
+upload_title=Dosyayý Yükle
+upload_file=Yüklecek Dosya
+upload_dir=Dizine Yükleme
+upload_ok=Yükle
+upload_conv=DOS satýrlarý çevirilsin mi?
+upload_efile=Yüklenecek dosya seçilmedi.
+upload_edir=Yükleme dizini mevcut deðil
+upload_eperm=$1 oluþturmanýza izin verilmedi
+upload_ewrite=$1'i yazmada hata oluþtu : $2.
+
+find_eaccess=$1'e eriþim izininiz yoktur
+find_eexist=$1, $2 içinde mevcut deðil
+find_edir=$1, $2'de bir dizin deðil
+
+cancel=Ýptal
+
+chmod_eaccess='$1'e eriim izininiz yoktur 
+chmod_euser=$1 : Kullanýýcý yok
+chmod_egroup=$1 : Grup yok
+chmod_elink=Sembolik linkte hata oluþtu : $1
+chmod_echown=chown'da hata oluþtu : $1
+chmod_echmod=chmod'da hata olustu  : $1
+
+copy_efrom='$1'den kopyalamanýza izin verilmemiþtir
+copy_eto='$1'e kopyalamanýza izin verilmemiþtir 
+copy_elink=Sembolik linkte hata oluþtu : $1
+
+delete_eaccess='$1'i silmede hata oluþtu
+
+list_eaccess=Bu dizine giriþ izininiz yoktur
+
+move_eto='$1'e taþýmaya hakkýnýz yoktur
+move_afrom='$1'i taþýmaya hakkynyz yoktur 
+
+acl_user=Sunucuda kullanýcý olarak dosyalara ulaþ 
+acl_umask=Yeni dosyalar için umask
+acl_follow=Sembolik linkleri her zaman takip et.
+acl_dirs=Sadece dizinlere giriþlere izin ver.
+
+share_title=Paylaþtýrma
+share_samba=Windows
+share_nfs=NFS
+share_son=Windows dosya paylaþýmý açýk
+share_soff=Windows dosya paylaþýmý kapalý
+share_writable=Yazýlabilir?
+share_available=Aktif mi?
+share_sheader=Paylaþým seçenekleri
+share_only=Sadece
+share_guest=Ziyaretçi giriþleri?
+share_comment=Açýklama
+share_nheader=NFS sunum seçenekleri
+share_non=NFS dosya paylaþýmý açýk
+share_noff=NFS dosya paylaþýmý kapalý
+share_desc=Açýklama
+share_ro=Sadece okuma izinli makineler
+share_rw=Okuma-yazma izinli makineler
+share_root=Root giriþli makineler
+share_none=Hiçbiri
+share_all=Hepsi
+share_listed=Listeli...
+share_host=Makineler
+share_opts=Seçenekler
+share_s0=Hiçkimseye güvenme
+share_s1=Root dýþýndakilere güven
+share_s2=Herkese güven
+share_lro=Sadece okunur
+share_lrw=Okunur-yazýlýr
+
+log_create_export=NFS sunumu $1 oluþturuldu
+log_modify_export=NFS sunumu $1 deðiþtirildi
+log_delete_export=NFS sunumu $1 silindi
+log_create_share=Samba paylaþýmý $1 oluþturuldu
+log_modify_share=Samba paylaþýmý $1 deðiþtirildi
+log_delete_share=Samba paylaþýmý $1 silindi
+log_save=$1 dosyasý kaydedildi
+log_chmod=$1 dosyasýnýn haklarý deðiþtirildi
+log_mkdir=$1 dizini oluþturuldu
+log_upload=$1 dosyasý yüklendi
+log_link=$2'ye sembolik link $1 oluþturuldu
+log_relink=$2'ye sembolik link $1 deðiþtirildi
+log_copy=$1 dosyasý $2'ye kopyalandý
+log_move=$1 dosyasý $2'ye taþýndý
+log_delete=$1 dosyasý silindi
+
diff --git a/file/lang/uk_UA b/file/lang/uk_UA
new file mode 100644 (file)
index 0000000..548b8b4
--- /dev/null
@@ -0,0 +1,269 @@
+top_delete=Âèäàëèòè\r
+info_apply=Çàñòîñóâàòè çì³íè\r
+info_perms=Ïðàâà\r
+info_file=Ôàéë\r
+view_enormal=Ìîæíà ïåðåãëÿäàòè ò³ëüêè çâè÷àéí³ ôàéëè\r
+top_upload=Çàâàíòàæèòè\r
+edit_esave=Íå óäàëîñÿ çáåðåãòè ôàéë : $1\r
+upload_ewrite=Íå óäàëîñÿ çàïèñàòè â $1 : $2.\r
+chmod_elink=Ïîìèëêà ïðè âèêëèêó symlink : $1\r
+upload_ok=Çàâàíòàæèòè\r
+view_eaccess=Ó âàñ íåäîñòàòíüî ïðàâèé äëÿ äîñòóïó äî $1\r
+info_group=Ãðóïè:\r
+edit_title2=Ñòâîðåííÿ ôàéëó\r
+top_info=Âëàñòèâîñò³\r
+move_afrom=Ó âàñ íåäîñòàòíüî ïðàâèé äëÿ ïåðåì³ùåííÿ '$1'\r
+link_efailed=Ñòâîðèòè ïîñèëàííÿ íå óäàëîñÿ : $1\r
+info_setuid2=Âèêîíóâàòè â³ä ³ìåí³ êîðèñòóâà÷à\r
+chmod_euser=$1 : íåìຠòàêîãî êîðèñòóâà÷à\r
+link_efrom=Ó âàñ íåäîñòàòíüî ïðàâèé äëÿ ñòâîðåííÿ ïîñèëàííÿ '$1'\r
+info_mod=Çì³íåíèé:\r
+right_date=Äàòà\r
+copy_efrom=Ó âàñ íåäîñòàòíüî ïðàâèé äëÿ êîï³þâàííÿ ç '$1'\r
+info_sticky2=Ò³ëüêè âëàñíèêè ìîæóòü âèäàëÿòè ôàéëè\r
+rename_old=Ñòàðå ³ì'ÿ:\r
+find_eaccess=Ó âàñ íåäîñòàòíüî ïðàâèé äëÿ äîñòóïó äî $1\r
+mkdir_eexists=$1 âæå ³ñíóº\r
+upload_dir=Çàâàíòàæóâàòè â êàòàëîã\r
+rename_enew=Ó âàñ íåäîñòàòíüî ïðàâèé äëÿ ïåðåéìåíóâàííÿ â '$1'\r
+find_edir=$1 íå º êàòàëîãîì ó $2\r
+upload_efile=Íå îáðàíèé ôàéë äëÿ çàâàíòàæåííÿ.\r
+info_type=Òèï:\r
+info_setuid=Á³ò setuid:\r
+right_group=Ãðóïà\r
+upload_efailed=Íå óäàëîñÿ ïî÷àòè çàâàíòàæåííÿ : $1\r
+top_cut=Âèð³çóâàòè\r
+info_read=×èòàííÿ\r
+acl_user=Çâåðòàòèñÿ äî ôàéë³â íà ñåðâåð³ ï³ä ³ì'ÿì êîðèñòóâà÷à\r
+paste_ecopy=Ïåðåä âñòàâêîþ íåîáõ³äíî ÷è âèð³çóâàòè êîï³þâàòè\r
+info_exec=Âèêîíàííÿ\r
+delete_ddesc=Íàçàâæäè âèäàëèòè êàòàëîã $1 ³ç óñ³ì éîãî âì³ñòîì?\r
+info_link=Ïîñèëàííÿ íà:\r
+link_to=̳ñöå ïðèçíà÷åííÿ:\r
+info_size=Ðîçì³ð:\r
+info_path=Øëÿõ:\r
+copy_eto=Ó âàñ íåäîñòàòíüî ïðàâèé äëÿ êîï³þâàííÿ â '$1'\r
+rename_ok=Ïåðåéìåíóâàòè\r
+chmod_egroup=$1 : íåìຠòàêî¿ ãðóïè\r
+info_own=Ïðèíàëåæí³ñòü\r
+upload_conv=Ïåðåòâîðèòè ïåðåêëàäè ðÿäê³â DOS?\r
+cancel=Ñêàñóâàííÿ\r
+info_list=Ïåðåãëÿä\r
+index_nojava=Äëÿ ôóíêö³îíóâàííÿ öüîãî ìîäóëÿ ïîòð³áíî java, îäíàê âàø áðàóçåð java íå ï³äòðèìóº\r
+paste_eover=$1 íå ìîæå áóòè ïåðåçàïèñàíèé\r
+info_user=Êîðèñòóâà÷à:\r
+delete_fdesc=Íàçàâæäè âèäàëèòè ôàéë $1 ?\r
+edit_title=Ðåäàãóâàííÿ $1\r
+paste_egone=Êîï³éîâàí ôàéë $1 á³ëüøå íå ³ñíóº\r
+top_paste=Óñòàâèòè\r
+chmod_echmod=Ïîìèëêà ïðè âèêëèêó chmod : $1\r
+edit_filename=²ì'ÿ ôàéëó:\r
+link_eexists=$1 âæå ³ñíóº\r
+edit_enormal=Ìîæíà ðåäàãóâàòè ò³ëüêè çâè÷àéí³ ôàéëè\r
+info_setgid2=Ôàéëè óñïàäêîâóþòü ãðóïó\r
+info_setgid3=Âèêîíóâàòè â³ä ³ìåí³ ãðóïè\r
+top_copy=Êîï³þâàòè\r
+edit_eaccess=Ó âàñ íåäîñòàòíüî ïðàâèé äëÿ çáåðåæåííÿ '$1'\r
+right_name=²ì'ÿ\r
+rename_efailed=Ïåðåéìåíóâàòè íå óäàëîñÿ : $1\r
+upload_edir=Êàòàëîã äëÿ çàâàíòàæåííÿ íå ³ñíóº.\r
+rename_new=Íîâå ³ì'ÿ:\r
+delete_dtitle=Âèäàëåííÿ êàòàëîãó\r
+link_from=Äæåðåëî:\r
+index_title=Ìåíåäæåð ôàéë³â\r
+file_type0=Êàòàëîã\r
+file_type1=Òåêñòîâèé ôàéë\r
+file_type2=Ôàéë çîáðàæåííÿ\r
+file_type3=Á³íàðíèé ôàéë\r
+file_type4=Ôàéë\r
+file_type5=Ñèìâîëüíå ïîñèëàííÿ\r
+file_type6=Ôàéë ïðèñòðîþ\r
+file_type7=Êàíàë\r
+info_sticky=Á³ò sticky:\r
+upload_title=Çàâàíòàæåííÿ ôàéëó\r
+top_edit=Çì³íèòè\r
+upload_eperm=Ó âàñ íåäîñòàòíüî ïðàâèé äëÿ ñòâîðåííÿ $1\r
+move_eto=Ó âàñ íåäîñòàòíüî ïðàâèé äëÿ ïåðåì³ùåííÿ â '$1'\r
+paste_eself=Íå ìîæíà ïåðåïèñàòè ôàéë ñàìèì ñîáîþ\r
+copy_elink=Ïîìèëêà ïðè âèêëèêó symlink : $1\r
+chmod_echown=Ïîìèëêà ïðè âèêëèêó chown : $1\r
+acl_umask=Umask äëÿ íîâèõ ôàéë³â\r
+mkdir_dir=Íîâèé êàòàëîã:\r
+info_other=²íøèõ:\r
+mkdir_title=Íîâèé êàòàëîã\r
+delete_ftitle=Âèäàëåííÿ ôàéëó\r
+find_eexist=$1 íå ³ñíóº â $2\r
+right_size=Ðîçì³ð\r
+edit_eover=$1 íå ìîæå áóòè ïåðåçàïèñàíèé\r
+paste_emfailed=Íå óäàëîñÿ ïåðåì³ñòèòè : $1\r
+link_title=Ñòâîðåííÿ ïîñèëàííÿ\r
+info_apply1=Ò³ëüêè äî öüîãî êàòàëîãó\r
+info_apply2=Äî öüîãî êàòàëîãó ³ éîãî ôàéë³â\r
+info_apply3=Äî Öüîãî êàòàëîãó ³ âñ³õ éîãî ï³äêàòàëîã³â\r
+info_efailed=Íå óäàëîñÿ îáíîâèòè $1 : $2\r
+acl_follow=Çàâæäè ïåðåõîäèòè ïî ñèìâîëüíèõ ïîñèëàííÿõ?\r
+upload_file=Ôàéë äëÿ çàâàíòàæåííÿ\r
+info_setgid=Á³ò setgid:\r
+paste_ecfailed=Íå óäàëîñÿ ñêîï³þâàòè : $1\r
+mkdir_eaccess=Ó âàñ íåäîñòàòíüî ïðàâèé äëÿ ñòâîðåííÿ '$1'\r
+right_user=Êîðèñòóâà÷\r
+rename_eold=Ó âàñ íåäîñòàòíüî ïðàâèé äëÿ ïåðåéìåíóâàííÿ '$1'\r
+link_efollow=Ó âàñ íåäîñòàòíüî ïðàâèé äëÿ ñòâîðåííÿ ïîñèëàíü\r
+rename_title=Ïåðåéìåíóâàííÿ $1\r
+top_new=Ïîñèëàííÿ\r
+mkdir_efailed=Íå óäàëîñÿ ñòâîðèòè êàòàëîã : $1\r
+info_write=Çàïèñ\r
+rename_eexists=Ôàéë ç ³ì'ÿì $1 âæå ³ñíóº\r
+acl_dirs=Äîçâîëÿòè äîñòóï ò³ëüêè äî êàòàëîã³â\r
+chmod_eaccess=Ó âàñ íåäîñòàòíüî ïðàâèé äëÿ äîñòóïó äî '$1'\r
+top_refresh=Îáíîâèòè\r
+delete_eaccess=Ó âàñ íåäîñòàòíüî ïðàâèé äëÿ âèäàëåííÿ '$1'\r
+view_eopen=Íå óäàëîñÿ â³äêðèòè $1 : $2\r
+top_rename=Ïåðåéìåíóâàòè\r
+list_eaccess=Ó âàñ íåäîñòàòíüî ïðàâèé äëÿ äîñòóïó äî öüîãî êàòàëîãó\r
+delete_efailed=Íå óäàëîñÿ âèäàëèòè $1 : $2\r
+log_chmod=Çì³íåí³ ïðàâà íà ôàéë $1\r
+eacl_edefmask=There can be at most one default mask ACL entry\r
+acl_log=Âåñòè æóðíàë óñ³õ çì³í ôàéë³â?\r
+eacl_eacls=Íå óäàëîñÿ ââàæàòè ACL : $1\r
+search_eaccess=Ó âàñ íåäîñòàòíüî ïðàâèé äëÿ ïîøóêó â öüîìó êàòàëîç³\r
+over_msg=Ôàéë $1 âæå ³ñíóº. Óêàæ³òü äëÿ ôàéëó, ùî âñòàâëÿºòüñÿ, íîâå ³ì'ÿ.\r
+share_nfs=NFS\r
+log_copy=Ñêîï³éîâàíèé ôàéë $1 ó $2\r
+acltype_default_group=Ãðóïà çà çàìîâ÷óâàííÿì\r
+eacl_aclname=Çàñòîñóâàòè äî\r
+search_size=Ðîçì³ð ôàéëó\r
+log_upload=Çàâàíòàæåíèé ôàéë $1\r
+attr_create=Äîäàòè àòðèáóò\r
+search_types_d=Êàòàëîã\r
+ext_eattrs=Íå óäàëîñÿ îäåðæàòè àòðèáóòè EXT : $1\r
+search_group=Ïðèíàëåæí³ ãðóï³\r
+search_types_f=Ôàéë\r
+index_eremote=Íåìຠêîðèñòóâà÷à Unix â³äïîâ³äíîãî êîðèñòóâà÷ó Webmin $1.\r
+search_types_l=Ñèìâîëüíå ïîñèëàííÿ\r
+search_types_p=²ìåíîâàíèé êàíàë\r
+search_dir=Øóêàòè â êàòàëîç³\r
+acltype_user=Êîðèñòóâà÷\r
+acl_user_def=Êîðèñòóâà÷à Webmin\r
+top_share=Ìåðåæà\r
+switch_euser=Êîðèñòóâà÷ Unix íå ³ñíóº!\r
+acl_home=Âêëþ÷àþ÷è äîìàøí³é êàòàëîã êîðèñòóâà÷à Webmin\r
+search_match=Ôàéëè ñï³âïàäàþ÷³ ç\r
+share_writable=Çàïèñ äîçâîëåíèé?\r
+delete_mtitle=Âèäàëåííÿ äåê³ëüêîõ ôàéë³â\r
+search_less=Ìåíø\r
+top_attr=Àòðèáóòè\r
+log_link=Ñòâîðåíà ñèìâîëüíå ïîñèëàííÿ $1 íà $2\r
+log_create_export=Ñòâîðåíèé ðåñóðñ NFS $1\r
+eacl_acltype=Òèï ACL\r
+share_s0=Äîâ³ðÿòè nobody\r
+share_s1=Äîâ³ðÿòè íå-root\r
+share_s2=Äîâ³ðÿòè âñ³ì\r
+ext_header=Àòðèáóòè EXT ôàéëó\r
+search_title=Ïîøóê ôàéë³â\r
+search_types_=Ëþáîþ\r
+log_move=Ïåðåì³ùåíèé ôàéë $1 ó $2\r
+acl_goto=³äêðèâàòè ïåðøèé äîñòóïíèé êàòàëîã?\r
+top_search=Çíàéòè\r
+share_none=Íåìàº\r
+log_delete=Âèëó÷åíèé ôàéë $1\r
+share_opts=Íàñòðîþâàííÿ\r
+share_ro=Âóçëè, ùî ìàþòü äîñòóï ò³ëüêè äëÿ ÷èòàííÿ\r
+share_rw=Âóçëè, ùî ìàþòü äîñòóï äëÿ ÷èòàííÿ ³ çàïèñó\r
+share_guest=Äîñòóï áåç àóòåíòèô³êàö³¿ (Guest)?\r
+share_nheader=Íàñòðîþâàííÿ äîñòóïó ïî ïðîòîêîë³ NFS\r
+over_title=Ôàéë ³ñíóº\r
+over_new=Íîâå ³ì'ÿ ôàéëó:\r
+share_host=Âóçëè\r
+search_esize=Ðîçì³ð ôàéëó ïîâèííèé áóòè ö³ëèì ÷èñëîì\r
+acltype_other=²íø³\r
+ext_efailed=Íå óäàëîñÿ óñòàíîâèòè àòðèáóòè EXT $1 : $2\r
+share_title=Äîñòóï ïî ìåðåæ³\r
+share_only=Ò³ëüêè\r
+attr_edit=Çì³íèòè àòðèáóò\r
+eacl_eowner=Íå çàçíà÷åíèé ÷è êîðèñòóâà÷ ãðóïà äëÿ ÿêèé âíîñÿòüñÿ çì³íè\r
+view_enormal2=Ìîæíà çàâàíòàæóâàòè ò³ëüêè çâè÷àéí³ ôàéëè\r
+search_user=Ïðèíàëåæí³ êîðèñòóâà÷ó\r
+log_mkdir=Ñòâîðåíèé êàòàëîã $1\r
+attr_ename=Íå çàçíà÷åíà íàçâà àòðèáóòà\r
+search_more=Á³ëüø\r
+eacl_user=Âëàñíèê ôàéëó $1\r
+ext_efs=Ôàéëîâà ñèñòåìà $1 íå ï³äòðèìóº àòðèáóòè EXT\r
+acltype_default_user=Êîðèñòóâà÷ çà çàìîâ÷óâàííÿì\r
+top_ext=EXT\r
+eacl_efs=Öÿ ôàéëîâà ñèñòåìà $1 íå ï³äòðèìóº ACL\r
+search_egroup=²ì'ÿ ãðóïè ÷è íå çàçíà÷åíå çàçíà÷åíî íåâ³ðíî\r
+share_lro=Ò³ëüêè ÷èòàííÿ\r
+acl_ro=Ðåæèì ò³ëüêè äëÿ ÷èòàííÿ?\r
+eacl_edefaults=If a file has any default ACL, it must have default user, group and other ACLs.\r
+list_edir=Íå óäàëîñÿ ïåðåãëÿíóòè $1 : $2\r
+eacl_owner=Âëàñíèê ôàéëó\r
+share_lrw=×èòàííÿ/çàïèñ\r
+attr_name=Íàçâà àòðèáóòà\r
+eacl_aclperms=Ïðàâà\r
+acltype_default_other=²íø³ çà çàìîâ÷óâàííÿì\r
+acltype_mask=Ìàñêà\r
+attr_eattrs=Íå óäàëîñÿ ââàæàòè àòðèáóòè : $1\r
+search_ematch=Ðåãóëÿðíå âèðàæåííÿ äëÿ ïîøóêó ÷è íå çàçíà÷åíå çàçíà÷åíî íåâ³ðíî\r
+eacl_add=Äîäàòè ACL :\r
+log_attr=Çì³íåí³ àòðèáóòè ôàéëó $1\r
+log_modify_export=Çì³íåíèé ðåñóðñ NFS $1\r
+attr_efs=Ôàéëîâà ñèñòåìà $1 íå ï³äòðèìóº àòðèáóòè\r
+top_down=Çáåðåãòè\r
+eattr_A=Íå îáíîâëÿòè ÷àñ äîñòóïó\r
+search_crit=Êðèòåð³é ïîøóêó\r
+attr_add=Äîäàòè àòðèáóò\r
+chmod_efollow=Ó âàñ íåäîñòàòíüî ïðàâèé äëÿ çì³íè ñèìâîëüíèõ ïîñèëàíü\r
+ext_title=Àòðèáóòè EXT äëÿ $1\r
+share_son=Äîñòóï ç ìåðåæ³ Windows äîçâîëåíèé\r
+log_create_share=Ñòâîðåíèé ðåñóðñ Samba $1\r
+search_ok=Øóêàòè\r
+attr_title=Àòðèáóòè ôàéëó äëÿ $1\r
+eattr_S=Çàâæäè ñèíõðîí³çóâàòè ï³ñëÿ çàïèñó\r
+search_edir=Êàòàëîã äëÿ ïîøóêó ÷è íå çàçíà÷åíèé çàçíà÷åíèé íåâ³ðíî\r
+share_non=Äîñòóï ïî NFS äîçâîëåíèé\r
+eattr_a=Ìîæëèâî ò³ëüêè äîäàâàííÿ äî ôàéëó\r
+share_samba=Windows\r
+eattr_c=Ñòèñêàòè äàí³ íà äèñêó\r
+eattr_d=Íå çä³éñíþâàòè ðåçåðâíå êîï³þâàííÿ çà äîïîìîãîþ dump\r
+search_xdev=Øóêàòè â ï³äìîíòîâàíèõ ôàéëîâèõ ñèñòåìàõ?\r
+eacl_edit=Çì³íèòè ACL\r
+eattr_³=Çàáîðîíèòè çì³íà\r
+share_soff=Äîñòóï ç ìåðåæ³ Windows çàáîðîíåíèé\r
+search_euser=²ì'ÿ êîðèñòóâà÷à ÷è íå çàçíà÷åíå çàçíà÷åíî íåâ³ðíî\r
+eattr_s=Îáíóëÿòè áëîêè ïðè âèäàëåíí³\r
+eattr_u=Çáåð³ãàòè óì³ñò ôàéëó äëÿ â³äíîâëåííÿ\r
+top_ret=Ìåíþ\r
+log_delete_share=Âèëó÷åíèé ðåñóðñ Samba $1\r
+eacl_emask=There can be at most one mask ACL entry\r
+log_delete_export=Âèëó÷åíèé ðåñóðñ NFS $1\r
+eacl_group=Ãðóïà ôàéëó $1\r
+share_noff=Äîñòóï ïî NFS çàáîðîíåíèé\r
+share_available=Äîñòóïíèé ó äàíèé ìîìåíò?\r
+info_octal=Âîñüìåðè÷íèé âèä:\r
+attr_efailed=Íå óäàëîñÿ óñòàíîâèòè àòðèáóòè äëÿ $1 : $2\r
+acltype_default_mask=Ìàñêà çà çàìîâ÷óâàííÿì\r
+log_modify_share=Çì³íåíèé ðåñóðñ Samba $1\r
+log_save=Çáåðåæåíèé ôàéë $1\r
+share_comment=Êîìåíòàð\r
+share_desc=Îïèñ\r
+eacl_remove=Âèäàëèòè ACL\r
+attr_value=Çíà÷åííÿ àòðèáóòà\r
+search_any=Ëþáîþ\r
+search_type=Òèï ôàéëó\r
+eacl_title=ACL äëÿ $1\r
+top_eacl=ACL\r
+facl_eaccess=Ó âàñ íåäîñòàòíüî ïðàâèé äëÿ çì³íè ACL äëÿ öüîãî ôàéëó\r
+share_root=Âóçëè, äëÿ ÿêèõ äîçâîëåíèé äîñòóï ³ç ïðàâàìè root\r
+eacl_efailed=Íå óäàëîñÿ óñòàíîâèòè ACL äëÿ $1 : $2\r
+share_all=Óñ³\r
+delete_mdesc=Íàçàâæäè âèäàëèòè çàçíà÷åí³ ôàéëè ³ êàòàëîãè? :\r
+acltype_group=Ãðóïà\r
+log_acl=Äëÿ ôàéëó $1 ïðèçíà÷åíèé ACL\r
+share_listed=Çàçíà÷åí³..\r
+share_sheader=Íàñòðîþâàííÿ äîñòóïó\r
+search_list=Ðåçóëüòàòè ïîøóêó\r
+eacl_create=Ñòâîðèòè ACL\r
+log_relink=Çì³íåíà ñèìâîëüíå ïîñèëàííÿ $1 íà $2\r
+over_ok=ÎÊ\r
+\r
diff --git a/file/lang/zh_CN b/file/lang/zh_CN
new file mode 100644 (file)
index 0000000..f7ff6f2
--- /dev/null
@@ -0,0 +1,323 @@
+index_title=Îļþ¹ÜÀíÆ÷
+index_nojava=±¾Ä£¿éÐèÒªjavaÖ§³Ö²ÅÄܹ¤×÷£¬µ«ÊÇÄúµÄä¯ÀÂÆ÷²»Ö§³Öjava
+index_eremote=ûÓÐÇøÅä WebminµÇ¼Ãû $1 µÄUnixÓû§¡£
+switch_euser=Unix Óû§²»´æÔÚ£¡
+
+top_ret=Ë÷Òý
+top_down=±£´æ
+top_edit=±à¼­
+top_refresh=Ë¢ÐÂ
+top_info=ÐÅÏ¢
+top_eacl=ACL
+top_attr=ÊôÐÔ
+top_ext=À©Õ¹
+top_delete=ɾ³ý
+top_new=н¨
+top_upload=ÉÏ´«
+top_rename=¸ÄÃû
+top_copy=¸´ÖÆ
+top_cut=¼ôÇÐ
+top_paste=Õ³Ìù
+top_share=¹²Ïí
+top_mount=¼ÓÔØ
+top_search=²éÕÒ
+top_config=ÅäÖÃ
+
+right_name=Ãû³Æ
+right_size=´óС
+right_user=Óû§
+right_group=×é
+right_date=ÈÕÆÚ
+
+edit_enormal=Ö»Äܱ༭Õý³£Îļþ
+edit_title=ÕýÔڱ༭ $1
+edit_title2=ÕýÔÚ´´½¨Îļþ
+edit_filename=ÎļþÃû£º
+edit_goto=תµ½
+edit_find=²éÕÒ
+edit_gotoline=תµ½ÐÐ
+edit_replace=Ìæ»»
+edit_all=È«²¿Ìæ»»
+edit_searchfor=ËÑË÷
+edit_replaceby=±»Ìæ»»
+edit_eover=$1 ²»Äܱ»¸²¸Ç
+edit_esave=±£´æÎļþʧ°Ü £º $1
+edit_eaccess=Äãδ±»ÔÊÐí±£´æ '$1'
+edit_notfound=δÕÒµ½Îı¾ $1 
+edit_saveclose=±£´æ²¢¹Ø±Õ
+
+info_file=Îļþ
+info_path=·¾¶£º
+info_type=ÀàÐÍ£º
+info_size=´óС£º
+info_mod=Ð޸ģº
+info_link=Áª½áµ½£º
+info_perms=Ðí¿É
+info_user=Óû§£º
+info_group=×飺
+info_other=ÆäËü£º
+info_octal=°Ë½øÖÆ£º
+info_sticky=Õ³ÐÔ£º
+info_sticky2=Ö»ÓÐËùÓÉÕß²ÅÄÜɾ³ýÎļþ
+info_own=ËùÓÐȨ
+info_setuid=ÉèÖàuid£º
+info_setuid2=ÒÔÓû§Ö´ÐÐ
+info_setgid=ÉèÖàgid£º
+info_setgid2=Îļþ¼Ì³Ð×é
+info_setgid3=ÒÔ×éÖ´ÐÐ
+info_apply=Ó¦Óøü¸ÄÖÁ
+info_apply1=½ö´ËĿ¼
+info_apply2=±¾Ä¿Â¼¼°ÆäÎļþ
+info_apply3=±¾Ä¿Â¼¼°ÆäËùÓÐ×ÓĿ¼
+info_efailed=¸üР$1 £º$2 Ê§°Ü
+info_read=¶ÁÈ¡
+info_write=дÈë
+info_list=Áбí
+info_exec=Ö´ÐÐ
+
+eacl_eacls=¶ÁÈ¡ ACLs Ê§°Ü £º $1
+eacl_acltype=ACL ÀàÐÍ
+eacl_aclname=Ó¦Óõ½
+eacl_aclperms=Ðí¿É
+eacl_add=Ìí¼Ó ÀàÐ͵ÄACL£º
+eacl_remove=ÒƳýACL
+eacl_efs=Îļþϵͳ $1 ²»Ö§³Ö ACLs
+eacl_create=´´½¨ ACL
+eacl_edit=±à¼­ ACL
+eacl_user=ÎļþËùÓÐÕß $1
+eacl_group=Îļþ×é $1
+eacl_eowner=ȱÉÙÒªÓ¦Óõ½µÄÓû§»ò×é
+eacl_efailed=Ϊ$1 £º $2ÉèÖÃACLsʧ°Ü
+eacl_emask=×î¶àÖ»ÄÜÓÐÒ»¸öÑÚÂë ACL ÌõÄ¿
+eacl_edefmask=×î¶àÖ»ÄÜÓÐÒ»¸öĬÈÏÑÚÂë ACL ÌõÄ¿
+eacl_title=$1µÄACL
+eacl_owner=ÎļþËùÓÐÕß
+eacl_edefaults=Èç¹ûÒ»¸öÎļþÓÐÈκÎĬÈϵÄACL£¬ÔòËü±ØÐëÓÐĬÈÏÓû§£¬×éºÍÆäËûµÄACLs¡£
+
+acltype_user=Óû§
+acltype_group=×é
+acltype_other=ÆäËû
+acltype_mask=ÑÚÂë
+acltype_default_user=ĬÈÏÓû§
+acltype_default_group=ĬÈÏ×é
+acltype_default_other=ĬÈÏÆäËû
+acltype_default_mask=ĬÈÏÑÚÂë
+
+delete_mtitle=ɾ³ý¶à¸öÎļþ
+delete_dtitle=ɾ³ýĿ¼
+delete_ftitle=ɾ³ýÎļþ
+delete_ddesc=ȷʵҪÓÀ¾Ãɾ³ýĿ¼ $1 ¼°ÆäÄÚÈÝ£¿
+delete_fdesc=ȷʵҪÓÀ¾Ãɾ³ýÎļþ $1£¿
+delete_mdesc=ȷʵҪÓÀ¾Ãɾ³ýÕâЩĿ¼ºÍÎļþ£¿ £º
+delete_efailed=ɾ³ý $1 £º$2 Ê§°Ü
+
+mkdir_title=н¨Ä¿Â¼
+mkdir_dir=н¨Ä¿Â¼£º
+mkdir_eexists=$1 ÒѾ­´æÔÚ
+mkdir_efailed=´´½¨Ä¿Â¼ $1 Ê§°Ü
+mkdir_eaccess=ÄãÎÞȨ´´½¨ '$1'
+
+link_title=´´½¨Á´½Ó
+link_from=Á´½Ó×Ô£º
+link_to=Á´½Óµ½£º
+link_eexists=$1 ÒѾ­´æÔÚ
+link_efailed=Á´½Óʧ°Ü£º$1
+link_efrom=ÄãÎÞȨÁ´½Ó×Ô '$1'
+link_efollow=ÄãÎÞȨ´´½¨·ûºÅÁ´½Ó
+
+rename_title=ÖØÃüÃû$1
+rename_old=Ô­Ãû£º
+rename_new=ÐÂÃû£º
+rename_ok=ÖØÃüÃû
+rename_eexists=Îļþ $1 ÒÑ´æÔÚ
+rename_efailed=ÖØÃüÃûʧ°Ü£º $1
+rename_eold=ÄãÎÞȨÖØÃüÃû '$1'
+rename_enew=ÄãÎÞȨÖØÃüÃûΪ '$1'
+
+file_type0=Ŀ¼
+file_type1=Îı¾Îļþ
+file_type2=Ó³ÏóÎļþ
+file_type3=¶þ½øÖÆÎļþ
+file_type4=Îļþ
+file_type5=·ûºÅÁ´½Ó
+file_type6=É豸Îļþ
+file_type7=¹ÜµÀ
+
+view_enormal=Ö»Äܲ鿴Õý³£Îļþ
+view_enormal2=Ö»ÄÜÏÂÔØÕý³£Îļþ
+view_eaccess=ÄãÎÞȨ·ÃÎÊ $1
+view_eopen=´ò¿ª $1£º $2 Ê§°Ü
+
+paste_ecopy=ÔÚÕ³Ìù֮ǰ±ØÐë¼ôÇлò¿½±´
+paste_egone=¿½±´µÄÎļþ $1 ÒѾ­²»´æÔÚ
+paste_eover=$1 ²»Äܸ²¸Ç
+paste_eself=²»Äܽ«ÎļþÕ³ÌùÖÁ×ÔÉí
+paste_emfailed=Òƶ¯Ê§°Ü£º$1
+paste_ecfailed=¿½±´Ê§°Ü£º$1
+
+over_title=Îļþ´æÔÚ
+over_msg=Îļþ $1 ÒѾ­´æÔÚ¡£ÔÚÏÂÃæµÄ×Ö¶ÎÖÐΪҪճÌùµÄÎļþÊäÈëÒ»¸öÐÂÃû¡£
+over_new=ÐÂÎļþÃû£º
+over_ok=È·¶¨
+
+upload_efailed=´ò¿ªÉÏ´«Ê§°Ü£º$1
+upload_title=ÉÏ´«Îļþ
+upload_file=ÒªÉÏ´«µÄÎļþ
+upload_dir=ÉÏ´«µ½Ä¿Â¼
+upload_ok=ÉÏ´«
+upload_conv=ÊÇ·ñת»»DOS»»ÐзûºÅ£¿
+upload_efile=ûÓÐÑ¡¶¨ÒªÉÏ´«µÄÎļþ¡£
+upload_edir=ÉÏ´«Ä¿Â¼²»´æÔÚ¡£
+upload_eperm=ÄãÎÞȨ´´½¨ $1
+upload_ewrite=дÈë $1£º$2 Ê§°Ü¡£
+upload_already=Îļþ $1 ÒѾ­´æÔÚ¡£ÄúÈ·¶¨Òª¸²¸ÇËüÂð£¿
+
+find_eaccess=ÄãÎÞȨ·ÃÎÊ $1
+find_eexist=ÔÚ$2Öв»´æÔÚ$1
+find_edir=$1 ²»ÊÇ $2 ÖеÄĿ¼
+
+cancel=È¡Ïû
+close=¹Ø±Õ
+
+chmod_eaccess=ÄãÎÞȨ·ÃÎÊ '$1'
+chmod_euser=$1£ºÎÞ´ËÓû§
+chmod_egroup=$1£ºÎÞ´ËÓû§×é
+chmod_elink=symlink Ê§°Ü£º$1
+chmod_echown=chown Ê§°Ü£º$1
+chmod_echmod=chmod Ê§°Ü£º$1
+chmod_efollow=Äúδ±»ÔÊÐí±à¼­·ûºÅÁ´½Ó
+
+copy_efrom=ÄãÎÞȨ´Ó '$1' ¿½±´
+copy_eto=ÄãÎÞȨ¿½±´µ½ '$1'
+copy_elink=symlink Ê§°Ü '$1'
+
+delete_eaccess=ÄãÎÞȨɾ³ý '$1'
+
+list_eaccess=ÄãÎÞȨ·ÃÎÊ´ËĿ¼
+list_edir=ÏÔʾÁбíʧ°Ü $1 £º $2
+
+move_eto=ÄãÎÞȨÒƶ¯µ½ '$1'
+move_afrom=ÄãÎÞȨÒƶ¯ '$1'
+
+acl_user=×÷ΪÓû§·ÃÎÊ·þÎñÆ÷µÄÎļþ
+acl_user_def=ÓëWebminµÇ¼Ïàͬ
+acl_umask=¶ÔÐÂÎļþÈ¥³ýÑÚÂë
+acl_follow=ÊÇ·ñ×ÜÊǸúËæ·ûºÅÁ´½Ó£¿
+acl_ro=Ö»¶Áģʽ£¿
+acl_dirs=½öÔÊÐí·ÃÎÊĿ¼
+acl_home=°üº¬WebminÓû§µÄÖ÷Ŀ¼£¿
+acl_log=¶ÔËùÓÐÎļþµÄÐ޸ı£´æÈÕÖ¾£¿
+acl_goto=´ò¿ª×îÏÈÔÊÐíµÄĿ¼£¿
+
+share_title=¹²Ïí
+share_samba=Windows
+share_nfs=NFS
+share_son=ÆôÓàWindows Îļþ¹²Ïí
+share_soff=½ûÓàWindows Îļþ¹²Ïí
+share_writable=¿ÉдÈ룿
+share_available=ÏÖÔڻÂð£¿
+share_sheader=¹²ÏíÑ¡Ïî
+share_only=½ö
+share_guest=Guest ·ÃÎÊ£¿
+share_comment=×¢ÊÍ
+share_nheader=NFS µ¼³öÑ¡Ïî
+share_non=ÒÑÆôÓàNFS Îļþ¹²Ïí
+share_noff=ÒѽûÓàNFS Îļþ¹²Ïí
+share_desc=ÃèÊö
+share_ro=Ö»¶ÁÖ÷»ú
+share_rw=¶ÁдÖ÷»ú
+share_root=Root ·ÃÎÊÖ÷»ú
+share_none=ÎÞ
+share_all=ËùÓÐ
+share_listed=ÒÑÁгöµÄ¡­
+share_host=Ö÷»ú
+share_opts=Ñ¡Ïî
+share_s0=²»ÐÅÈÎÈκÎÈË
+share_s1=ÐÅÈηǠRoot Óû§
+share_s2=ÐÅÈÎËùÓÐÈË
+share_lro=Ö»¶Á
+share_lrw=¶Áд
+
+log_create_export=ÒÑ´´½¨µÄ NFS µ¼³ö $1
+log_modify_export=ÒÑÐ޸ĵĠNFS µ¼³ö $1
+log_delete_export=ÒÑɾ³ýµÄ NFS µ¼³ö $1
+log_create_share=ÒÑ´´½¨µÄ Samba ¹²Ïí $1
+log_modify_share=ÒÑÐ޸ĵĠSamba ¹²Ïí $1
+log_delete_share=ÒÑɾ³ýµÄ Samba ¹²Ïí $1
+log_save=Òѱ£´æÎļþ $1
+log_chmod=ÒѸıäÎļþ $1 µÄȨÏÞ
+log_mkdir=ÒÑ´´½¨µÄĿ¼ $1
+log_upload=ÒÑÉÏ´«µÄÎļþ $1
+log_link=ÒÑ´´½¨µÄ·ûºÅÁ¬½Ó $1 µ½ $2
+log_relink=ÒÑÐ޸ĵķûºÅÁ¬½Ó $1 µ½ $2
+log_copy=ÒÑ¿½±´µÄÎļþ $1 µ½ $2
+log_move=ÒÑÒƶ¯µÄÎļþ $1 µ½ $2
+log_delete=ÒÑɾ³ýµÄÎļþ $1
+log_attr=ÉèÖÃÎļþ $1 µÄÊôÐÔ
+log_acl=ÉèÖÃÎļþ $1 µÄACL
+
+search_eaccess=ÄúÎÞȨËÑË÷´ËĿ¼
+search_title=ÕÒµ½Îļþ
+search_ok=ÏÖÔÚËÑË÷
+search_dir=ËÑË÷Ŀ¼
+search_match=ÎļþÆ¥Åä
+search_user=Óû§ËùÓÐ
+search_group=×éËùÓÐ
+search_any=ÈκÎ
+search_type=ÎļþÀàÐÍ
+search_types_=ÈκÎ
+search_types_f=Îļþ
+search_types_d=Ŀ¼
+search_types_l=·ûºÅÁ¬½Ó
+search_types_p=ÃüÃû¹ÜµÀ
+search_size=Îļþ´óС
+search_more=¶àÓÚ
+search_less=ÉÙÓÚ
+search_xdev=ËÑË÷ÒÔÇ°µÄ¼ÓÔØ£¿
+search_edir=¶ªÊ§»òÎÞЧµÄËÑË÷Ŀ¼
+search_ematch=¶ªÊ§Æ¥ÅäµÄÕýÔò±í´ïʽ
+search_euser=¶ªÊ§Óû§Ãû
+search_egroup=¶ªÊ§×éÃû
+search_esize=Îļþ´óСֵ±ØÐëÊÇÕûÊý
+search_crit=ËÑË÷±ê×¼
+search_list=ËÑË÷½á¹û
+
+facl_eaccess=Äãδ±»ÔÊÐíÉèÖôËÎļþµÄACLs
+
+attr_eattrs=»ñÈ¡ÊôÐÔʧ°Ü£º$1
+attr_efs=Îļþϵͳ $1 ²»Ö§³ÖÊôÐÔ
+attr_add=Ìí¼ÓÊôÐÔ
+attr_name=ÊôÐÔÃû³Æ
+attr_value=ÊôÐÔÖµ
+attr_efailed=Ϊ $1 £º $2 ÉèÖÃÊôÐÔʧ°Ü
+attr_title=$1µÄÎļþÊôÐÔ
+attr_create=Ìí¼ÓÊôÐÔ
+attr_edit=±à¼­ÊôÐÔ
+attr_ename=ȱÉÙÊôÐÔÃû³Æ
+
+ext_eattrs=»ñÈ¡À©Õ¹ÊôÐÔʧ°Ü£º $1
+ext_efs=Îļþϵͳ $1 ²»Ö§³ÖÀ©Õ¹ÊôÐÔ
+ext_title=$1 µÄÀ©Õ¹ÊôÐÔ
+ext_header=À©Õ¹ÎļþÊôÐÔ
+ext_efailed=Ϊ $1 £º $2ÉèÖÃÊôÐÔʧ°Ü
+
+eattr_A=²»¸üзÃÎÊ´ÎÊý
+eattr_a=Ö»ÄÜÌí¼Óµ½Îļþĩβ
+eattr_c=ѹËõ´ÅÅÌÊý¾Ý
+eattr_d=²»Ê¹ÓÃdump±¸·Ý
+eattr_i=²»ÔÊÐíÐÞ¸Ä
+eattr_s=ɾ³ýʱ½«¿é¹éÁã
+eattr_S=дÈëºó×ÜÊÇͬ²½
+eattr_u=Ϊ·´É¾³ý±£´æÄÚÈÝ
+
+mount_eaccess=Äãδ±»ÔÊÐí¼ÓÔØÎļþϵͳ
+mount_efstab=´Ë¼ÓÔصãÉϲ»´æÔÚÎļþϵͳ
+mount_epoint=$1 ²»ÊǼÓÔصã
+mount_rusure1=ÄãÈ·¶¨Òª´Ó $2¼ÓÔØ $1 Âð£¿
+mount_rusure2=ÄãÈ·¶¨Òª´Ó $2жÔØ $1 Âð£¿
+mount_err1=¼ÓÔØʧ°Ü $1 £º $2
+mount_err2=жÔØʧ°Ü $1 £º $2
+mount_title1=¼ÓÔØÎļþϵͳ
+mount_title2=жÔØÎļþϵͳ
+
+
diff --git a/file/lang/zh_CN.UTF-8 b/file/lang/zh_CN.UTF-8
new file mode 100644 (file)
index 0000000..a69b9f0
--- /dev/null
@@ -0,0 +1,323 @@
+index_title=文件管理器
+index_nojava=本模块需要java支持才能工作,但是您的浏缆器不支持java
+index_eremote=没有区配 Webmin登录名 $1 的Unix用户。
+switch_euser=Unix 用户不存在!
+
+top_ret=索引
+top_down=保存
+top_edit=编辑
+top_refresh=刷新
+top_info=信息
+top_eacl=ACL
+top_attr=属性
+top_ext=扩展
+top_delete=删除
+top_new=新建
+top_upload=上传
+top_rename=改名
+top_copy=复制
+top_cut=剪切
+top_paste=粘贴
+top_share=共享
+top_mount=加载
+top_search=查找
+top_config=配置
+
+right_name=名称
+right_size=大小
+right_user=用户
+right_group=组
+right_date=日期
+
+edit_enormal=只能编辑正常文件
+edit_title=正在编辑 $1
+edit_title2=正在创建文件
+edit_filename=文件名:
+edit_goto=转到
+edit_find=查找
+edit_gotoline=转到行
+edit_replace=替换
+edit_all=全部替换
+edit_searchfor=搜索
+edit_replaceby=被替换
+edit_eover=$1 不能被覆盖
+edit_esave=保存文件失败 : $1
+edit_eaccess=你未被允许保存 '$1'
+edit_notfound=未找到文本 $1 
+edit_saveclose=保存并关闭
+
+info_file=文件
+info_path=路径:
+info_type=类型:
+info_size=大小:
+info_mod=修改:
+info_link=联结到:
+info_perms=许可
+info_user=用户:
+info_group=组:
+info_other=其它:
+info_octal=八进制:
+info_sticky=粘性:
+info_sticky2=只有所由者才能删除文件
+info_own=所有权
+info_setuid=设置 uid:
+info_setuid2=以用户执行
+info_setgid=设置 gid:
+info_setgid2=文件继承组
+info_setgid3=以组执行
+info_apply=应用更改至
+info_apply1=仅此目录
+info_apply2=本目录及其文件
+info_apply3=本目录及其所有子目录
+info_efailed=更新 $1 :$2 失败
+info_read=读取
+info_write=写入
+info_list=列表
+info_exec=执行
+
+eacl_eacls=读取 ACLs 失败 : $1
+eacl_acltype=ACL 类型
+eacl_aclname=应用到
+eacl_aclperms=许可
+eacl_add=添加 类型的ACL:
+eacl_remove=移除ACL
+eacl_efs=文件系统 $1 不支持 ACLs
+eacl_create=创建 ACL
+eacl_edit=编辑 ACL
+eacl_user=文件所有者 $1
+eacl_group=文件组 $1
+eacl_eowner=缺少要应用到的用户或组
+eacl_efailed=为$1 : $2设置ACLs失败
+eacl_emask=最多只能有一个掩码 ACL 条目
+eacl_edefmask=最多只能有一个默认掩码 ACL 条目
+eacl_title=$1的ACL
+eacl_owner=文件所有者
+eacl_edefaults=如果一个文件有任何默认的ACL,则它必须有默认用户,组和其他的ACLs。
+
+acltype_user=用户
+acltype_group=组
+acltype_other=其他
+acltype_mask=掩码
+acltype_default_user=默认用户
+acltype_default_group=默认组
+acltype_default_other=默认其他
+acltype_default_mask=默认掩码
+
+delete_mtitle=删除多个文件
+delete_dtitle=删除目录
+delete_ftitle=删除文件
+delete_ddesc=确实要永久删除目录 $1 及其内容?
+delete_fdesc=确实要永久删除文件 $1?
+delete_mdesc=确实要永久删除这些目录和文件? :
+delete_efailed=删除 $1 :$2 失败
+
+mkdir_title=新建目录
+mkdir_dir=新建目录:
+mkdir_eexists=$1 已经存在
+mkdir_efailed=创建目录 $1 失败
+mkdir_eaccess=你无权创建 '$1'
+
+link_title=创建链接
+link_from=链接自:
+link_to=链接到:
+link_eexists=$1 已经存在
+link_efailed=链接失败:$1
+link_efrom=你无权链接自 '$1'
+link_efollow=你无权创建符号链接
+
+rename_title=重命名$1
+rename_old=原名:
+rename_new=新名:
+rename_ok=重命名
+rename_eexists=文件 $1 已存在
+rename_efailed=重命名失败: $1
+rename_eold=你无权重命名 '$1'
+rename_enew=你无权重命名为 '$1'
+
+file_type0=目录
+file_type1=文本文件
+file_type2=映象文件
+file_type3=二进制文件
+file_type4=文件
+file_type5=符号链接
+file_type6=设备文件
+file_type7=管道
+
+view_enormal=只能查看正常文件
+view_enormal2=只能下载正常文件
+view_eaccess=你无权访问 $1
+view_eopen=打开 $1: $2 失败
+
+paste_ecopy=在粘贴之前必须剪切或拷贝
+paste_egone=拷贝的文件 $1 已经不存在
+paste_eover=$1 不能覆盖
+paste_eself=不能将文件粘贴至自身
+paste_emfailed=移动失败:$1
+paste_ecfailed=拷贝失败:$1
+
+over_title=文件存在
+over_msg=文件 $1 已经存在。在下面的字段中为要粘贴的文件输入一个新名。
+over_new=新文件名:
+over_ok=确定
+
+upload_efailed=打开上传失败:$1
+upload_title=上传文件
+upload_file=要上传的文件
+upload_dir=上传到目录
+upload_ok=上传
+upload_conv=是否转换DOS换行符号?
+upload_efile=没有选定要上传的文件。
+upload_edir=上传目录不存在。
+upload_eperm=你无权创建 $1
+upload_ewrite=写入 $1:$2 失败。
+upload_already=文件 $1 已经存在。您确定要覆盖它吗?
+
+find_eaccess=你无权访问 $1
+find_eexist=在$2中不存在$1
+find_edir=$1 不是 $2 中的目录
+
+cancel=取消
+close=关闭
+
+chmod_eaccess=你无权访问 '$1'
+chmod_euser=$1:无此用户
+chmod_egroup=$1:无此用户组
+chmod_elink=symlink 失败:$1
+chmod_echown=chown 失败:$1
+chmod_echmod=chmod 失败:$1
+chmod_efollow=您未被允许编辑符号链接
+
+copy_efrom=你无权从 '$1' 拷贝
+copy_eto=你无权拷贝到 '$1'
+copy_elink=symlink 失败 '$1'
+
+delete_eaccess=你无权删除 '$1'
+
+list_eaccess=你无权访问此目录
+list_edir=显示列表失败 $1 : $2
+
+move_eto=你无权移动到 '$1'
+move_afrom=你无权移动 '$1'
+
+acl_user=作为用户访问服务器的文件
+acl_user_def=与Webmin登录相同
+acl_umask=对新文件去除掩码
+acl_follow=是否总是跟随符号链接?
+acl_ro=只读模式?
+acl_dirs=仅允许访问目录
+acl_home=包含Webmin用户的主目录?
+acl_log=对所有文件的修改保存日志?
+acl_goto=打开最先允许的目录?
+
+share_title=共享
+share_samba=Windows
+share_nfs=NFS
+share_son=启用 Windows 文件共享
+share_soff=禁用 Windows 文件共享
+share_writable=可写入?
+share_available=现在活动吗?
+share_sheader=共享选项
+share_only=仅
+share_guest=Guest 访问?
+share_comment=注释
+share_nheader=NFS 导出选项
+share_non=已启用 NFS 文件共享
+share_noff=已禁用 NFS 文件共享
+share_desc=描述
+share_ro=只读主机
+share_rw=读写主机
+share_root=Root 访问主机
+share_none=无
+share_all=所有
+share_listed=已列出的…
+share_host=主机
+share_opts=选项
+share_s0=不信任任何人
+share_s1=信任非 Root 用户
+share_s2=信任所有人
+share_lro=只读
+share_lrw=读写
+
+log_create_export=已创建的 NFS 导出 $1
+log_modify_export=已修改的 NFS 导出 $1
+log_delete_export=已删除的 NFS 导出 $1
+log_create_share=已创建的 Samba 共享 $1
+log_modify_share=已修改的 Samba 共享 $1
+log_delete_share=已删除的 Samba 共享 $1
+log_save=已保存文件 $1
+log_chmod=已改变文件 $1 的权限
+log_mkdir=已创建的目录 $1
+log_upload=已上传的文件 $1
+log_link=已创建的符号连接 $1 到 $2
+log_relink=已修改的符号连接 $1 到 $2
+log_copy=已拷贝的文件 $1 到 $2
+log_move=已移动的文件 $1 到 $2
+log_delete=已删除的文件 $1
+log_attr=设置文件 $1 的属性
+log_acl=设置文件 $1 的ACL
+
+search_eaccess=您无权搜索此目录
+search_title=找到文件
+search_ok=现在搜索
+search_dir=搜索目录
+search_match=文件匹配
+search_user=用户所有
+search_group=组所有
+search_any=任何
+search_type=文件类型
+search_types_=任何
+search_types_f=文件
+search_types_d=目录
+search_types_l=符号连接
+search_types_p=命名管道
+search_size=文件大小
+search_more=多于
+search_less=少于
+search_xdev=搜索以前的加载?
+search_edir=丢失或无效的搜索目录
+search_ematch=丢失匹配的正则表达式
+search_euser=丢失用户名
+search_egroup=丢失组名
+search_esize=文件大小值必须是整数
+search_crit=搜索标准
+search_list=搜索结果
+
+facl_eaccess=你未被允许设置此文件的ACLs
+
+attr_eattrs=获取属性失败:$1
+attr_efs=文件系统 $1 不支持属性
+attr_add=添加属性
+attr_name=属性名称
+attr_value=属性值
+attr_efailed=为 $1 : $2 设置属性失败
+attr_title=$1的文件属性
+attr_create=添加属性
+attr_edit=编辑属性
+attr_ename=缺少属性名称
+
+ext_eattrs=获取扩展属性失败: $1
+ext_efs=文件系统 $1 不支持扩展属性
+ext_title=$1 的扩展属性
+ext_header=扩展文件属性
+ext_efailed=为 $1 : $2设置属性失败
+
+eattr_A=不更新访问次数
+eattr_a=只能添加到文件末尾
+eattr_c=压缩磁盘数据
+eattr_d=不使用dump备份
+eattr_i=不允许修改
+eattr_s=删除时将块归零
+eattr_S=写入后总是同步
+eattr_u=为反删除保存内容
+
+mount_eaccess=你未被允许加载文件系统
+mount_efstab=此加载点上不存在文件系统
+mount_epoint=$1 不是加载点
+mount_rusure1=你确定要从 $2加载 $1 吗?
+mount_rusure2=你确定要从 $2卸载 $1 吗?
+mount_err1=加载失败 $1 : $2
+mount_err2=卸载失败 $1 : $2
+mount_title1=加载文件系统
+mount_title2=卸载文件系统
+
+
diff --git a/file/lang/zh_TW.Big5 b/file/lang/zh_TW.Big5
new file mode 100644 (file)
index 0000000..7d1cfec
--- /dev/null
@@ -0,0 +1,197 @@
+index_title=Àɮ׺޲zªÌ
+index_nojava=³o­Ó¼Ò²Õ»Ý­n Java ¤~¯à¥¿½Tªº¹B§@, ¦ý¬O±zªºÂsÄý¾¹¦ü¥G¤£¤ä´© Java
+
+top_ret=ªð¦^
+top_config=³]©w
+top_down=¤U¸ü
+top_open=¶}±Ò
+top_view=À˵ø
+top_edit=½s¿è
+top_refresh=­«Åª
+top_info=¸ê°T
+top_search=·j´M
+top_delete=§R°£
+top_new=·s¼W
+top_upload=¤W¶Ç
+top_rename=§ï¦W
+top_copy=½Æ»s
+top_cut=°Å¤U
+top_paste=¶K¤W
+
+right_name=¦WºÙ
+right_size=¤j¤p
+right_user=¨Ï¥ÎªÌ
+right_group=¸s²Õ
+right_date=¤é´Á
+
+edit_enormal=¥u¯à½s¿è¤@¯ëªºÀÉ®×
+edit_title=½s¿è $1 ¤¤
+edit_title2=«Ø¥ßÀɮפ¤
+edit_filename=ÀɮצWºÙ:
+edit_goto=«e©¹
+edit_find=´M§ä
+edit_gotoline=«e©¹ªº¦C¼Æ
+edit_replace=¨ú¥N
+edit_all=¥þ³¡¨ú¥N
+edit_searchfor=´M§ä¤º®e
+edit_replaceby=¨ú¥N¦¨¬°
+edit_eover=$1 µLªk³QÂл\
+edit_esave=µLªkÀx¦sÀÉ®× : $1
+edit_eaccess=±z¤£³Q¤¹³\Àx¦s '$1'
+edit_efollow=±z¤£³Q¤¹³\¼g¤J²Å¸¹³sµ² '$1'
+edit_notfound=§ä¤£¨ì³o­Ó $1 ¤å¥ó
+edit_saveclose=Àx¦s¨ÃÃö³¬
+edit_elength=Àɮ׳QºIÂ_¡I
+
+info_file=ÀÉ®×
+info_path=¸ô®|:
+info_type=Ãþ§O:
+info_size=¤j¤p:
+info_mod=­×§ï®É¶¡:
+info_link=³sµ²¨ì:
+info_perms=Åv­­
+info_user=¨Ï¥ÎªÌ:
+info_group=¸s²Õ:
+info_other=¨ä¥L:
+info_sticky=³]©w¦ì¤¸:
+info_sticky2=¥u¦³¾Ö¦³ªÌ¤~¯à§R°£ÀÉ®×
+info_own=©Ò¦³Åv
+info_setuid=³]©w UID:
+info_setuid2=¥H¨Ï¥ÎªÌÅv­­°õ¦æ
+info_setgid=³]©w GID:
+info_setgid2=ÀÉ®×Ä~©Ó¸s²Õ
+info_setgid3=¥H¸s²ÕÅv­­°õ¦æ
+info_apply=®M¥ÎÅܧó¨ì
+info_apply1=¥u¦³³o­Ó¥Ø¿ý
+info_apply2=³o­Ó¥Ø¿ý»P¨ä¤¤ªºÀÉ®×
+info_apply3=³o­Ó¥Ø¿ý»P©Ò¦³ªº¤l¥Ø¿ý
+info_efailed=µLªk­«Åª $1 : $2
+info_read=Ū¨ú
+info_write=¼g¤J
+info_list=¦C¥X
+info_exec=°õ¦æ
+
+search_eaccess=±z¤£³Q¤¹³\·j´M³o­Ó¥Ø¿ý
+search_title=·j´MÀÉ®×
+search_ok=·j´M
+search_dir=·j´M¥Ø¿ý
+search_match=²Å¦XªºÀɮצWºÙ
+search_user=¾Ö¦³ªº¨Ï¥ÎªÌ
+search_group=¾Ö¦³ªº¸s²Õ
+search_any=¥ô·N
+search_type=ÀɮתºÃþ«¬
+search_types_=¥ô·N
+search_types_f=ÀÉ®×
+search_types_d=¥Ø¿ý
+search_types_l=²Å¸¹³sµ²(Symbolic link)
+search_types_p=¨ã¦WºÞ¹D(Named pipe)
+search_size=Àɮפj¤p
+search_more=¶W¹L
+search_less=¤p©ó
+search_xdev=Search past mounts?
+search_edir=¥¼§ä¨ì©ÎµL®Äªº·j´M¥Ø¿ý
+search_ematch=¥¼§ä¨ì²Å¦Xªº regexp
+search_euser=¥¼§ä¨ì¨Ï¥ÎªÌ¦WºÙ
+search_egroup=¥¼§ä¨ì¸s²Õ¦WºÙ
+search_esize=Àɮתº¤j¤p¥²¶·¬O¾ã¼Æ
+search_crit=·j´M±ø¥ó
+search_list=·j´Mµ²ªG
+
+delete_dtitle=§R°£¥Ø¿ý\r
+delete_ftitle=§R°£ÀÉ®×\r
+delete_mtitle=§R°£¦h­ÓÀÉ®×\r
+delete_ddesc=±z½T©w­n¥Ã¤[ªº§R°£¥Ø¿ý $1 »P¨ä©Ò¦³¤º®e¶Ü¡H\r
+delete_fdesc=±z½T©w­n¥Ã¤[ªº§R°£ÀÉ®× $1 ¶Ü¡H\r
+delete_mdesc=±z½T©w­n¥Ã¤[ªº§R°£³o¨ÇÀɮשM¥Ø¿ý¶Ü¡H :\r
+delete_efailed=µLªk§R°£ $1 : $2\r
+
+mkdir_title=·s¼W¥Ø¿ý
+mkdir_dir=¥Ø¿ý:
+mkdir_eexists=$1 ¤w¸g¦s¦b
+mkdir_efailed=«Ø¥ß¥Ø¿ý¥¢±Ñ : $1
+mkdir_eaccess=±z¤£³Q¤¹³\«Ø¥ß '$1'
+
+link_title=«Ø¥ß³sµ²
+link_from=³sµ²¦Û:
+link_to=³sµ²¨ì:
+link_eexists=$1 ¤w¸g¦s¦b
+link_efailed=³sµ²¥¢±Ñ : $1
+link_efrom=±z¤£³Q¤¹³\³sµ² '$1'
+link_efollow=±z¤£³Q¤¹³\«Ø¥ß²Å¸¹³sµ²
+
+rename_title=Åܧó¦WºÙ $1
+rename_old=ÂÂÀɦW:
+rename_new=Åܧó¬°:
+rename_ok=§ï¦W
+rename_eexists=¥s°µ $1 ªºÀɮפw¸g¦s¦b
+rename_efailed=§ï¦W¥¢±Ñ : $1
+rename_eold=±z¤£³Q¤¹³\§ï¦W¦Û '$1'
+rename_enew=±z¤£³Q¤¹³\§ï¦W¦¨ '$1'
+
+file_type0=¥Ø¿ý
+file_type1=¤å¦rÀÉ
+file_type2=¼v¹³ÀÉ
+file_type3=¤G¶i¦ìÀÉ
+file_type4=ÀÉ®×
+file_type5=²Å¸¹³sµ²
+file_type6=³]³Æ±±¨îÀÉ
+file_type7=ºÞ½u
+
+view_enormal=¥u¦³¤@¯ëªºÀÉ®×¥i¥H³QÀ˵ø
+view_eaccess=±z¤£³Q¤¹³\¦s¨ú $1
+view_eopen=µLªk¶}±Òn $1 : $2
+
+paste_ecopy=¦b±z¶K¤W­n¥ý°Å¤U©Î½Æ»s
+paste_egone=½Æ»sªºÀÉ®× $1 ¤w¸g¤£¦s¦b
+paste_eover=$1 µLªk³Q»\¼g
+paste_eself=±z¤£¯à§âÀɮ׶K¨ì¥»¨­¤W
+paste_emfailed=²¾°Ê¥¢±Ñ : $1
+paste_ecfailed=«þ¨©¥¢±Ñ : $1
+
+upload_efailed=¶}±Ò¤W¶Ç¥¢±Ñ : $1
+upload_title=¤W¶ÇÀÉ®×
+upload_file=­n¤W¶ÇªºÀÉ®×
+upload_dir=¤W¶Ç¨ì¥Ø¿ý
+upload_ok=¤W¶Ç
+upload_conv=¬O§_­n§ïÅÜ DOS ªº´«¦æ²Å¸¹?
+upload_efile=¨S¦³¿ï¾Ü­n¤W¶ÇªºÀÉ®×.
+upload_edir=¤W¶Çªº¥Ø¿ý¤£¦s¦b.
+upload_eperm=±z¤£³Q¤¹³\«Ø¥ß $1
+upload_ewrite=µLªk¼g¤J¨ì $1 : $2.
+upload_already=ÀÉ®× $1 ¤w¸g¦s¦b¡A±z½T©w­nÂл\¥¦¶Ü¡H
+upload_elink=µLªk¤W¶Ç²Å¸¹³sµ²
+upload_zip=­n¸ÑÀ£ÁY ZIP ©Î TAR ªºÀɮ׶ܡH
+upload_yes=¬Oªº¡AµM«á§R°£
+
+
+find_eaccess=±z¤£³Q¤¹³\¦s¨ú $1
+find_eexist=$1 ¤£¦s¦b©ó $2
+find_edir=$1 ¤£¬O¤@­Ó¥Ø¿ý¦b $2
+
+cancel=¨ú®ø
+close=Ãö³¬
+eopen=¤U¸ü¥¢±Ñ¡G$1
+
+chmod_eaccess=±z¤£³Q¤¹³\¦s¨ú '$1'
+chmod_euser=$1 : ¨S¦³³o­Ó¨Ï¥ÎªÌ
+chmod_egroup=$1 : ¨S¦³³o­Ó¸s²Õ
+chmod_elink=²Å¸¹³sµ²¥¢±Ñd : $1
+chmod_echown=Åܧó¾Ö¦³ªÌ¥¢±Ñ : $1
+chmod_echmod=ÅܧóÅv­­¥¢±Ñ : $1
+
+copy_efrom=±z¤£³Q¤¹³\«þ¨©¦Û '$1'
+copy_eto=±z¤£³Q¤¹³\«þ¨©¨ì '$1'
+copy_elink=²Å¸¹³sµ²¥¢±Ñ : $1
+
+delete_eaccess=±z¤£³Q¤¹³\§R°£ '$1'
+
+list_eaccess=±z¤£³Q¤¹³\¦s¨ú³o­Ó¥Ø¿ý
+
+move_eto=±z¤£³Q¤¹³\²¾°Ê¨ì '$1'
+move_afrom=±z¤£³Q¤¹³\²¾°Ê¦Û '$1'
+
+acl_user=¥H¨Ï¥ÎªÌÅv­­¦s¨ú¦øªA¾¹¤WªºÀÉ®×
+acl_umask=«Ø¥ßÀɮתº¾B¸n
+acl_follow=¬O§_¸òÀH²Å¸¹³sµ²?
+acl_dirs=¥u¤¹³\¦s¨ú¨ì¥Ø¿ý
+
diff --git a/file/lang/zh_TW.UTF-8 b/file/lang/zh_TW.UTF-8
new file mode 100644 (file)
index 0000000..86bb5ee
--- /dev/null
@@ -0,0 +1,197 @@
+index_title=檔案管理者
+index_nojava=這個模組需要 Java 才能正確的運作, 但是您的瀏覽器似乎不支援 Java
+
+top_ret=返回
+top_config=設定
+top_down=下載
+top_open=開啟
+top_view=檢視
+top_edit=編輯
+top_refresh=重讀
+top_info=資訊
+top_search=搜尋
+top_delete=刪除
+top_new=新增
+top_upload=上傳
+top_rename=改名
+top_copy=複製
+top_cut=剪下
+top_paste=貼上
+
+right_name=名稱
+right_size=大小
+right_user=使用者
+right_group=群組
+right_date=日期
+
+edit_enormal=只能編輯一般的檔案
+edit_title=編輯 $1 中
+edit_title2=建立檔案中
+edit_filename=檔案名稱:
+edit_goto=前往
+edit_find=尋找
+edit_gotoline=前往的列數
+edit_replace=取代
+edit_all=全部取代
+edit_searchfor=尋找內容
+edit_replaceby=取代成為
+edit_eover=$1 無法被覆蓋
+edit_esave=無法儲存檔案 : $1
+edit_eaccess=您不被允許儲存 '$1'
+edit_efollow=您不被允許寫入符號連結 '$1'
+edit_notfound=找不到這個 $1 文件
+edit_saveclose=儲存並關閉
+edit_elength=檔案被截斷!
+
+info_file=檔案
+info_path=路徑:
+info_type=類別:
+info_size=大小:
+info_mod=修改時間:
+info_link=連結到:
+info_perms=權限
+info_user=使用者:
+info_group=群組:
+info_other=其他:
+info_sticky=設定位元:
+info_sticky2=只有擁有者才能刪除檔案
+info_own=所有權
+info_setuid=設定 UID:
+info_setuid2=以使用者權限執行
+info_setgid=設定 GID:
+info_setgid2=檔案繼承群組
+info_setgid3=以群組權限執行
+info_apply=套用變更到
+info_apply1=只有這個目錄
+info_apply2=這個目錄與其中的檔案
+info_apply3=這個目錄與所有的子目錄
+info_efailed=無法重讀 $1 : $2
+info_read=讀取
+info_write=寫入
+info_list=列出
+info_exec=執行
+
+search_eaccess=您不被允許搜尋這個目錄
+search_title=搜尋檔案
+search_ok=搜尋
+search_dir=搜尋目錄
+search_match=符合的檔案名稱
+search_user=擁有的使用者
+search_group=擁有的群組
+search_any=任意
+search_type=檔案的類型
+search_types_=任意
+search_types_f=檔案
+search_types_d=目錄
+search_types_l=符號連結(Symbolic link)
+search_types_p=具名管道(Named pipe)
+search_size=檔案大小
+search_more=超過
+search_less=小於
+search_xdev=Search past mounts?
+search_edir=未找到或無效的搜尋目錄
+search_ematch=未找到符合的 regexp
+search_euser=未找到使用者名稱
+search_egroup=未找到群組名稱
+search_esize=檔案的大小必須是整數
+search_crit=搜尋條件
+search_list=搜尋結果
+
+delete_dtitle=刪除目錄\r
+delete_ftitle=刪除檔案\r
+delete_mtitle=刪除多個檔案\r
+delete_ddesc=您確定要永久的刪除目錄 $1 與其所有內容嗎?\r
+delete_fdesc=您確定要永久的刪除檔案 $1 嗎?\r
+delete_mdesc=您確定要永久的刪除這些檔案和目錄嗎? :\r
+delete_efailed=無法刪除 $1 : $2\r
+
+mkdir_title=新增目錄
+mkdir_dir=目錄:
+mkdir_eexists=$1 已經存在
+mkdir_efailed=建立目錄失敗 : $1
+mkdir_eaccess=您不被允許建立 '$1'
+
+link_title=建立連結
+link_from=連結自:
+link_to=連結到:
+link_eexists=$1 已經存在
+link_efailed=連結失敗 : $1
+link_efrom=您不被允許連結 '$1'
+link_efollow=您不被允許建立符號連結
+
+rename_title=變更名稱 $1
+rename_old=舊檔名:
+rename_new=變更為:
+rename_ok=改名
+rename_eexists=叫做 $1 的檔案已經存在
+rename_efailed=改名失敗 : $1
+rename_eold=您不被允許改名自 '$1'
+rename_enew=您不被允許改名成 '$1'
+
+file_type0=目錄
+file_type1=文字檔
+file_type2=影像檔
+file_type3=二進位檔
+file_type4=檔案
+file_type5=符號連結
+file_type6=設備控制檔
+file_type7=管線
+
+view_enormal=只有一般的檔案可以被檢視
+view_eaccess=您不被允許存取 $1
+view_eopen=無法開啟n $1 : $2
+
+paste_ecopy=在您貼上要先剪下或複製
+paste_egone=複製的檔案 $1 已經不存在
+paste_eover=$1 無法被蓋寫
+paste_eself=您不能把檔案貼到本身上
+paste_emfailed=移動失敗 : $1
+paste_ecfailed=拷貝失敗 : $1
+
+upload_efailed=開啟上傳失敗 : $1
+upload_title=上傳檔案
+upload_file=要上傳的檔案
+upload_dir=上傳到目錄
+upload_ok=上傳
+upload_conv=是否要改變 DOS 的換行符號?
+upload_efile=沒有選擇要上傳的檔案.
+upload_edir=上傳的目錄不存在.
+upload_eperm=您不被允許建立 $1
+upload_ewrite=無法寫入到 $1 : $2.
+upload_already=檔案 $1 已經存在,您確定要覆蓋它嗎?
+upload_elink=無法上傳符號連結
+upload_zip=要解壓縮 ZIP 或 TAR 的檔案嗎?
+upload_yes=是的,然後刪除
+
+
+find_eaccess=您不被允許存取 $1
+find_eexist=$1 不存在於 $2
+find_edir=$1 不是一個目錄在 $2
+
+cancel=取消
+close=關閉
+eopen=下載失敗:$1
+
+chmod_eaccess=您不被允許存取 '$1'
+chmod_euser=$1 : 沒有這個使用者
+chmod_egroup=$1 : 沒有這個群組
+chmod_elink=符號連結失敗d : $1
+chmod_echown=變更擁有者失敗 : $1
+chmod_echmod=變更權限失敗 : $1
+
+copy_efrom=您不被允許拷貝自 '$1'
+copy_eto=您不被允許拷貝到 '$1'
+copy_elink=符號連結失敗 : $1
+
+delete_eaccess=您不被允許刪除 '$1'
+
+list_eaccess=您不被允許存取這個目錄
+
+move_eto=您不被允許移動到 '$1'
+move_afrom=您不被允許移動自 '$1'
+
+acl_user=以使用者權限存取伺服器上的檔案
+acl_umask=建立檔案的遮罩
+acl_follow=是否跟隨符號連結?
+acl_dirs=只允許存取到目錄
+
diff --git a/file/list.cgi b/file/list.cgi
new file mode 100755 (executable)
index 0000000..955856b
--- /dev/null
@@ -0,0 +1,47 @@
+#!/usr/local/bin/perl
+# list.cgi
+# Return a list of files in some directory
+
+require './file-lib.pl';
+&ReadParse();
+&switch_acl_uid_and_chroot();
+print "Content-type: text/plain\n\n";
+$d = $in{'dir'} eq "/" ? "" : $in{'dir'};
+if (!&can_list($in{'dir'})) {
+       print $text{'list_eaccess'},"\n";
+       }
+elsif (!opendir(DIR, $in{'dir'})) {
+       # Cannot list the dir .. but maybe we don't have to!
+       # If a sub-directory was requested, just assume that it exists.
+       local $err = $!;
+       local @alt = &accessible_subdir($in{'dir'});
+       local $fil = &file_info_line($in{'dir'});
+       if (@alt && $fil) {
+               print "\n";
+               foreach $f ("$in{'dir'}/.", "$in{'dir'}/..", @alt) {
+                       $fil = &file_info_line($f);
+                       print "$fil\n" if (defined($fil));
+                       }
+               }
+       else {
+               print "$err\n";
+               }
+       }
+else {
+       # Can list the directory
+       print "\n";
+       @files = sort { $a cmp $b } readdir(DIR);
+       if ($hide_dot_files) {
+               @files = grep { $_ !~ /^\./ } @files;
+               }
+       else {
+               @files = grep { $_ ne "." && $_ ne ".." } @files;
+               }
+       @files = grep { &can_list("$d/$_") } @files;
+       closedir(DIR);
+       foreach $f (".", "..", @files) {
+               local $fil = &file_info_line("$d/$f");
+               print "$fil\n" if (defined($fil));
+               }
+       }
+
diff --git a/file/list_exports.cgi b/file/list_exports.cgi
new file mode 100755 (executable)
index 0000000..4f23acb
--- /dev/null
@@ -0,0 +1,95 @@
+#!/usr/local/bin/perl
+# list_exports.cgi
+# Output info about NFS exports
+
+require './file-lib.pl';
+print "Content-type: text/plain\n\n";
+if ($access{'uid'}) {
+       # User has no access to NFS
+       print "0\n";
+       exit;
+       }
+
+&read_acl(\%acl, undef);
+%einfo = &get_module_info("exports");
+%dinfo = &get_module_info("dfsadmin");
+#%binfo = &get_module_info("bsdexports");      # too hard
+
+if (%einfo && &check_os_support(\%einfo)) {
+       # Linux NFS exports
+       &module_check("exports");
+       if (!&has_command("rpc.nfsd") && !&has_command("nfsd")) {
+               print "0\n";
+               exit;
+               }
+       print "1\n";
+       &foreign_require("exports", "exports-lib.pl");
+       foreach $e (&foreign_call("exports", "list_exports")) {
+               push(@{$exp{$e->{'dir'}}}, $e)
+                       if ($e->{'dir'} !~ /:/ && $e->{'host'} !~ /:/);
+               }
+       foreach $d (keys %exp) {
+               local $host;
+               foreach $e (@{$exp{$d}}) {
+                       local $o = $e->{'options'};
+                       $host .= sprintf ":%s:%d:%d",
+                               $e->{'host'} ? $e->{'host'} : '*',
+                               defined($o->{'ro'}),
+                               defined($o->{'all_squash'}) ? 0 :
+                               defined($o->{'no_root_squash'}) ? 2 : 1;
+                       }
+               print &make_chroot($d),$host,"\n";
+               }
+       }
+elsif (%dinfo && &check_os_support(\%dinfo)) {
+       # Solaris NFS shares
+       &module_check("dfsadmin");
+       print "2\n";
+       &foreign_require("dfsadmin", "dfs-lib.pl");
+       foreach $s (&foreign_call("dfsadmin", "list_shares")) {
+               $opts = &foreign_call("dfsadmin", "parse_options",$s->{'opts'});
+               $opts->{'ro'} = '-' if (!defined($opts->{'ro'}));
+               $opts->{'ro'} =~ s/:/ /g;
+               $opts->{'rw'} = '-' if (!defined($opts->{'rw'}));
+               $opts->{'rw'} =~ s/:/ /g;
+               $opts->{'root'} = '-' if (!defined($opts->{'root'}));
+               $opts->{'root'} =~ s/:/ /g;
+               printf "%s:%s:%s:%s:%s\n",
+                       &make_chroot($s->{'dir'}), $opts->{'ro'}, $opts->{'rw'},
+                       $opts->{'root'}, $s->{'desc'};
+               }
+       }
+elsif (%binfo && &check_os_support(\%binfo)) {
+       # BSD NFS exports
+       &module_check("bsdexports");
+       print "3\n";
+       &foreign_require("bsdexports", "bsdexports-lib.pl");
+       foreach $e (&foreign_call("bsdexports", "list_exports")) {
+               foreach $d (@{$e->{'dirs'}}) {
+                       printf "%s:%s", $d, $e->{'ro'} ? 1 : 0;
+                       if ($e->{'network'}) {
+                               printf ":%s/%s\n",
+                                       $e->{'network'}, $e->{'mask'};
+                               }
+                       else {
+                               foreach $h (@{$e->{'hosts'}}) {
+                                       print ":$h";
+                                       }
+                               print "\n";
+                               }
+                       }
+               }
+       }
+else {
+       # No NFS modules installed or supported
+       print "0\n";
+       }
+
+sub module_check
+{
+if (!$acl{$base_remote_user,$_[0]}) {
+       print "0\n";
+       exit;
+       }
+}
+
diff --git a/file/list_shares.cgi b/file/list_shares.cgi
new file mode 100755 (executable)
index 0000000..456e7a1
--- /dev/null
@@ -0,0 +1,45 @@
+#!/usr/local/bin/perl
+# list_shares.cgi
+# Output info about samba shares
+
+require './file-lib.pl';
+print "Content-type: text/plain\n\n";
+if ($access{'uid'}) {
+       # User has no access to samba
+       print "0\n";
+       exit;
+       }
+
+%minfo = &get_module_info("samba");
+&read_acl(\%acl, undef);
+if (!%minfo || !&check_os_support(\%minfo) ||
+    !$acl{$base_remote_user,'samba'}) {
+       # Samba module not installed or supported
+       print "0\n";
+       exit;
+       }
+
+&foreign_require("samba", "samba-lib.pl");
+%sconfig = &foreign_config("samba");
+if (!-r $sconfig{'smb_conf'} || !&has_command($sconfig{'samba_server'})) {
+       # Samba not installed
+       print "0\n";
+       exit;
+       }
+
+print "1\n";
+foreach $s (&foreign_call("samba", "list_shares")) {
+       &foreign_call("samba", "get_share", $s);
+       if ($s ne 'global' && $s ne 'homes' && $s ne 'printers' &&
+           $samba::share{'path'} =~ /^\/[^\%\s\:]*$/ &&
+           $samba::share{'printable'} !~ /true|yes/i) {
+               printf "%s:%s:%s:%s:%s\n",
+                       $samba::share{'path'},
+                       $samba::share{'available'} =~ /no|false/i ? 0 : 1,
+                       $samba::share{'writable'} =~ /yes|true/i ? 1 : 0,
+                       $samba::share{'guest only'} =~ /yes|true/i ? 2 :
+                       $samba::share{'public'} =~ /yes|true/i ? 1 : 0,
+                       $samba::share{'comment'};
+               }
+       }
+
diff --git a/file/log_parser.pl b/file/log_parser.pl
new file mode 100644 (file)
index 0000000..6810f45
--- /dev/null
@@ -0,0 +1,45 @@
+# log_parser.pl
+# Functions for parsing this module's logs
+
+do 'file-lib.pl';
+
+# parse_webmin_log(user, script, action, type, object, &params)
+# Converts logged information from this module into human-readable form
+sub parse_webmin_log
+{
+local ($user, $script, $action, $type, $object, $p) = @_;
+if ($type eq 'export' || $type eq 'share') {
+       return &text("log_${action}_${type}",
+                    "<tt>".&html_escape($object)."</tt>");
+       }
+elsif ($action eq 'save' || $action eq 'chmod' || $action eq 'mkdir' ||
+       $action eq 'upload' || $action eq 'delete') {
+       return &text("log_${action}",
+                    "<tt>".&html_escape($object)."</tt>");
+       }
+elsif ($action eq 'link' || $action eq 'move' || $action eq 'copy') {
+       return &text("log_${action}",
+                    "<tt>".&html_escape($object)."</tt>",
+                    "<tt>".&html_escape($p->{'to'})."</tt>");
+       }
+elsif ($action eq 'relink') {
+       return &text('log_relink',
+                    "<tt>".&html_escape($object)."</tt>",
+                    "<tt>".&html_escape($p->{'linkto'})."</tt>");
+       }
+elsif ($action eq 'rename') {
+       return &text('log_move',
+                    "<tt>".&html_escape($object)."</tt>",
+                    "<tt>".&html_escape($p->{'new'})."</tt>");
+       }
+elsif ($action eq 'attr') {
+       return &text('log_attr', "<tt>".&html_escape($object)."</tt>");
+       }
+elsif ($action eq 'acl') {
+       return &text('log_acl', "<tt>".&html_escape($object)."</tt>");
+       }
+else {
+       return undef;
+       }
+}
+
diff --git a/file/makelink.cgi b/file/makelink.cgi
new file mode 100755 (executable)
index 0000000..de2ad2e
--- /dev/null
@@ -0,0 +1,26 @@
+#!/usr/local/bin/perl
+# makelink.cgi
+# Create a symbolic link
+
+require './file-lib.pl';
+$disallowed_buttons{'makelink'} && &error($text{'ebutton'});
+&ReadParse();
+&webmin_log("link", undef, $in{'from'}, \%in);
+&switch_acl_uid_and_chroot();
+print "Content-type: text/plain\n\n";
+&lock_file($in{'from'});
+if ($access{'ro'} || !&can_access($in{'from'})) {
+       print &text('link_efrom2', $in{'from'}),"\n";
+       }
+elsif ($follow) {
+       print $text{'link_efollow'},"\n";
+       }
+elsif (!symlink($in{'to'}, $in{'from'})) {
+       print "$!\n";
+       }
+else {
+       print "\n";
+       print &file_info_line($in{'from'}),"\n";
+       &unlock_file($in{'from'});
+       }
+
diff --git a/file/mkdir.cgi b/file/mkdir.cgi
new file mode 100755 (executable)
index 0000000..4ff365a
--- /dev/null
@@ -0,0 +1,24 @@
+#!/usr/local/bin/perl
+# mkdir.cgi
+# Create a directory
+
+require './file-lib.pl';
+$disallowed_buttons{'mkdir'} && &error($text{'ebutton'});
+&ReadParse();
+&webmin_log("mkdir", undef, $in{'dir'}, \%in);
+&switch_acl_uid_and_chroot();
+print "Content-type: text/plain\n\n";
+&lock_file($in{'dir'});
+if ($access{'ro'} || !&can_access($in{'dir'})) {
+       print &text('mkdir_eaccess', $in{'dir'}),"\n";
+       }
+elsif (!mkdir($in{'dir'}, 0777)) {
+       print "$!\n";
+       }
+else {
+       print "\n";
+       print &file_info_line($in{'dir'}),"\n";
+       &unlock_file($in{'dir'});
+       }
+
+
diff --git a/file/module.info b/file/module.info
new file mode 100644 (file)
index 0000000..d3ac774
--- /dev/null
@@ -0,0 +1,25 @@
+desc_ko_KR.euc=ÆÄÀÏ °ü¸®ÀÚ
+risk=high
+desc_ru_SU=íÅÎÅÄÖÅÒ ÆÁÊÌÏ×
+desc_zh_TW.Big5=Àɮ׺޲zªÌ
+desc_pl=Zarz±dzanie plikami
+desc_de=Datei-Manager (Java erforderlich)
+name=FileManager
+desc_zh_CN=Îļþ¹ÜÀíÆ÷
+desc_pt=Administrador de Ficheiros
+desc_tr=Dosya Yöneticisi
+desc=File Manager
+desc_es=Explorador de Archivos
+desc_sv=Filhanterare
+desc_fr=Gestionnaire de Fichiers
+desc_ja_JP.euc=¥Õ¥¡¥¤¥ë ¥Þ¥Í¡¼¥¸¥ã
+desc_ru_RU=Ìåíåäæåð ôàéëîâ
+desc_ca=Administrador de Fitxers
+desc_pt_BR=Gerenciador de Arquivos
+desc_sk=Správca Systému Súborov
+longdesc=View, edit and change permissions on files and directories on your system with a Windows-like file manager.
+readonly=1
+desc_zh_TW.UTF-8=檔案管理者
+desc_zh_CN.UTF-8=文件管理器
+desc_ja_JP.UTF-8=ファイル マネージャ
+desc_ko_KR.UTF-8=파일 관리자
diff --git a/file/mount.cgi b/file/mount.cgi
new file mode 100755 (executable)
index 0000000..470704d
--- /dev/null
@@ -0,0 +1,48 @@
+#!/usr/local/bin/perl
+# mount.cgi
+# Mount or un-mount some filesystem
+# XXX need way to detect current status?
+# XXX should return result
+# XXX client must force refresh:
+# XXX can only deal with stuff in /etc/fstab
+
+require './file-lib.pl';
+$disallowed_buttons{'mount'} && &error($text{'ebutton'});
+&ReadParse();
+print "Content-type: text/plain\n\n";
+if ($access{'ro'} || $access{'uid'}) {
+       # User is not allowed to mount
+       print "$text{'mount_eaccess'}\n";
+       exit;
+       }
+
+# Get current status
+$dir = &unmake_chroot($in{'dir'});
+&foreign_require("mount", "mount-lib.pl");
+@fstab = &mount::list_mounts();
+@mtab = &mount::list_mounted();
+($fstab) = grep { $_->[0] eq $dir } @fstab;
+if (!$fstab) {
+       # Doesn't exist!
+       print "$text{'mount_efstab'}\n";
+       exit;
+       }
+($mtab) = grep { $_->[0] eq $dir } @mtab;
+
+if ($mtab) {
+       # Attempt to un-mount now
+       $err = &mount::unmount_dir(@$mtab);
+       }
+else {
+       # Attempt to mount now
+       $err = &mount::mount_dir(@$fstab);
+       }
+if ($err) {
+       $err =~ s/<[^>]*>//g;
+       $err =~ s/\n/ /g;
+       print $err,"\n";
+       }
+else {
+       print "\n";
+       }
+
diff --git a/file/move.cgi b/file/move.cgi
new file mode 100755 (executable)
index 0000000..230a37a
--- /dev/null
@@ -0,0 +1,29 @@
+#!/usr/local/bin/perl
+# move.cgi
+# Move some file or directory
+
+require './file-lib.pl';
+$disallowed_buttons{'copy'} && &error($text{'ebutton'});
+&ReadParse();
+&webmin_log("move", undef, $in{'from'}, \%in);
+&switch_acl_uid();
+print "Content-type: text/plain\n\n";
+unlink($in{'to'}); # in case we are moving a directory
+if ($access{'ro'} || !&can_access($in{'to'})) {
+       print &text('move_eto', $in{'to'}),"\n";
+       exit;
+       }
+if (!&can_access($in{'from'})) {
+       print &text('move_efrom', $in{'from'}),"\n";
+       exit;
+       }
+$ok = &rename_logged(&unmake_chroot($in{'from'}),
+                    &unmake_chroot($in{'to'}));
+if (!$ok) {
+       print $!,"\n";
+       }
+else {
+       print "\n";
+       print &file_info_line(&unmake_chroot($in{'to'})),"\n";
+       }
+
diff --git a/file/preview.cgi b/file/preview.cgi
new file mode 100755 (executable)
index 0000000..51f9f5c
--- /dev/null
@@ -0,0 +1,124 @@
+#!/usr/local/bin/perl
+# Scale some image down to the preview size
+
+require './file-lib.pl';
+&ReadParse();
+use POSIX;
+$p = $ENV{'PATH_INFO'};
+
+# Try to guess type from filename
+$type = &guess_mime_type($p, undef);
+if (!$type) {
+       # No idea .. use the 'file' command
+       $out = &backquote_command("file ".
+                                 quotemeta(&resolve_links($p)), 1);
+       if ($out =~ /text|script/) {
+               $type = "text/plain";
+               }
+       else {
+               &error_exit(&text('preview_etype', $p));
+               }
+       }
+
+# Make sure the type is OK
+if ($type ne "image/gif" && $type ne "image/jpeg" && $type ne "image/png" &&
+    $type ne "image/tiff" && $type ne "application/pdf" &&
+    $type !~ /^image\/x-portable/ && $type ne "application/postscript") {
+       &error_exit(&text('preview_etype2', $p));
+       }
+
+&switch_acl_uid_and_chroot();
+if (!&can_access($p)) {
+       # ACL rules prevent access to file
+       &error_exit(&text('view_eaccess', $p));
+       }
+
+# Test if the file can be opened
+if (!open(FILE, $p)) {
+       # Unix permissions prevent access
+       &error_exit(&text('view_eopen', $p, $!));
+       }
+close(FILE);
+
+eval "use GD";
+if ($@ || $type eq "image/tiff" || $type eq "application/pdf" ||
+          $type =~ /^image\/x-portable/ || $type eq "application/postscript") {
+       # Find an appropriate scaler
+       $pnmcmd = $type eq "image/gif" ? "giftopnm" :
+                 $type eq "image/jpeg" ? "djpeg" :
+                 $type eq "image/png" ? "pngtopnm" :
+                 $type eq "image/tiff" ? "tifftopnm" :
+                 $type =~ /^image\/x-portable/ ? "cat" :
+                 $type eq "application/postscript" ? "pstopnm" :
+                 $type eq "application/pdf" ? "pdftoppm" :
+                                         undef;
+       &has_command($pnmcmd) ||
+               &error_exit(&text('preview_ecmd', $pnmcmd));
+       &has_command("pnmscale") ||
+               &error_exit(&text('preview_ecmd', "pnmscale"));
+       &has_command("cjpeg") ||
+               &error_exit(&text('preview_ecmd', "cjpeg"));
+
+       # Run scaler
+       $width = $config{'width'} || $userconfig{'width'} || 300;
+       $errout = &transname();
+       print "Content-type: image/jpeg\n";
+       print "\n";
+       if ($type eq "application/pdf") {
+               # Previewing first page of PDF
+               $temp = &tempname();
+               $out = &backquote_command("$pnmcmd -f 1 -l 1 ".quotemeta($p)." ".$temp." 2>&1");
+               if ($? || !-r "$temp-000001.ppm") {
+                       &error_exit("$pnmcmd failed : $out");
+                       }
+               open(SCALE, "(cat $temp-000001.ppm | pnmscale --width $width | cjpeg) 2>$errout |");
+               push(@main::temporary_files, "$temp-000001.ppm");
+               }
+       elsif ($type eq "application/postscript") {
+               # Previewing first page of a postscript file
+               $temp = &transname();
+               mkdir($temp, 0755);
+               &copy_source_dest($p, "$temp/file.ps");
+               $out = &backquote_command("$pnmcmd $temp/file.ps 2>&1");
+               if ($? || !-r "$temp/file001.ppm") {
+                       &error_exit("$pnmcmd failed : $out");
+                       }
+               open(SCALE, "(cat $temp/file001.ppm | pnmscale --width $width | cjpeg) 2>$errout |");
+               }
+       else {
+               # Converting to JPEG
+               open(SCALE, "($pnmcmd <".quotemeta($p)." | pnmscale --width $width | cjpeg) 2>$errout |");
+               }
+       $err = &read_file_contents($errout);
+       print STDERR $err;
+       while(<SCALE>) {
+               print;
+               }
+       close(SCALE);
+       }
+else {
+       # Use the GD library
+       $image = $type eq "image/gif" ? GD::Image->newFromGif($p) :
+                $type eq "image/jpeg" ? GD::Image->newFromJpeg($p) :
+                $type eq "image/png" ? GD::Image->newFromPng($p) : undef;
+       $image || &error_exit(&text('preview_egd'));
+
+       $width = $config{'width'} || $userconfig{'width'} || 300;
+       $height = $image->height * (($width*1.0) / $image->width);
+
+       $scaled = new GD::Image($width, $height);
+       $scaled->copyResampled($image, 0, 0, 0, 0, $width, $height,
+                              $image->width, $image->height);
+       print "Content-type: image/jpeg\n";
+       print "\n";
+       print $scaled->jpeg();
+       }
+
+sub error_exit
+{
+print "Content-type: text/plain\n";
+print "Content-length: ",length($_[0]),"\n\n";
+print $_[0];
+exit;
+}
+
diff --git a/file/rename.cgi b/file/rename.cgi
new file mode 100755 (executable)
index 0000000..836ff9b
--- /dev/null
@@ -0,0 +1,24 @@
+#!/usr/local/bin/perl
+# rename.cgi
+# Rename some file
+
+require './file-lib.pl';
+$disallowed_buttons{'rename'} && &error($text{'ebutton'});
+&ReadParse();
+&webmin_log("rename", undef, $in{'old'}, \%in);
+&switch_acl_uid_and_chroot();
+print "Content-type: text/plain\n\n";
+if ($access{'ro'} || !&can_access($in{'old'})) {
+       print &text('rename_eold', $in{'old'}),"\n";
+       }
+elsif (!&can_access($in{'new'})) {
+       print &text('rename_enew', $in{'new'}),"\n";
+       }
+elsif (!&rename_logged($in{'old'}, $in{'new'})) {
+       print "$!\n";
+       }
+else {
+       print "\n";
+       }
+
+
diff --git a/file/root.cgi b/file/root.cgi
new file mode 100755 (executable)
index 0000000..7da2ce2
--- /dev/null
@@ -0,0 +1,8 @@
+#!/usr/local/bin/perl
+# root.cgi
+# Return information about the root directory
+
+require './file-lib.pl';
+print "Content-type: text/plain\n\n";
+&go_chroot();
+print &file_info_line("/"),"\n";
diff --git a/file/save.cgi b/file/save.cgi
new file mode 100755 (executable)
index 0000000..7c2000b
--- /dev/null
@@ -0,0 +1,39 @@
+#!/usr/local/bin/perl
+# save.cgi
+# Write data to a file
+
+require './file-lib.pl';
+$disallowed_buttons{'edit'} && &error($text{'ebutton'});
+$p = $ENV{'PATH_INFO'};
+&webmin_log("save", undef, $p) if ($access{'uid'});
+&switch_acl_uid_and_chroot();
+print "Content-type: text/plain\n\n";
+
+# Read posted data
+$clen = $ENV{'CONTENT_LENGTH'};
+&read_fully(STDIN, \$buf, $clen) == $clen ||
+       &error("Failed to read POST input : $!");
+
+if (defined($in{'length'}) && length($buf) != $in{'length'}) {
+       print &text('edit_elength'),"\n";
+       }
+else {
+       &lock_file($p);
+       if ($access{'ro'} || !&can_access($p)) {
+               print &text('edit_eaccess', $p),"\n";
+               }
+       elsif (-l $p && !&must_follow($p)) {
+               print &text('edit_efollow', $p),"\n";
+               }
+       elsif (!&open_tempfile(FILE, ">$p", 1)) {
+               print "$!\n";
+               }
+       else {
+               &print_tempfile(FILE, $buf);
+               &close_tempfile(FILE);
+               &unlock_file($p);
+               print "\n";
+               print &file_info_line($p),"\n";
+               &webmin_log("save", undef, $p) if (!$access{'uid'});
+               }
+       }
diff --git a/file/save_export.cgi b/file/save_export.cgi
new file mode 100755 (executable)
index 0000000..b3d7b91
--- /dev/null
@@ -0,0 +1,144 @@
+#!/usr/local/bin/perl
+# save_export.cgi
+# Update, create or delete an NFS export
+
+require './file-lib.pl';
+$disallowed_buttons{'sharing'} && &error($text{'ebutton'});
+&ReadParse();
+print "Content-type: text/plain\n\n";
+if ($access{'ro'} || $access{'uid'}) {
+       # User has no access to NFS
+       print "0\n";
+       exit;
+       }
+
+&read_acl(\%acl, undef);
+%einfo = &get_module_info("exports");
+%dinfo = &get_module_info("dfsadmin");
+%binfo = &get_module_info("bsdexports");
+
+if (%einfo && &check_os_support(\%einfo)) {
+       # Linux NFS exports
+       &module_check("exports");
+       &foreign_require("exports", "exports-lib.pl");
+       %econfig = &foreign_config("exports");
+       &lock_file($econfig{'exports_file'});
+       foreach $e (&foreign_call("exports", "list_exports")) {
+               push(@{$exp{$e->{'dir'}}}, $e);
+               }
+       if ($in{'delete'}) {
+               # Delete all exports for some dir
+               foreach $e (reverse(@{$exp{$in{'path'}}})) {
+                       &foreign_call("exports", "delete_export", $e);
+                       }
+               }
+       else {
+               # Adding or updating an export
+               if (!$in{'new'}) {
+                       # Updating, so delete old exports first
+                       foreach $e (reverse(@{$exp{$in{'path'}}})) {
+                               $host{$e->{'host'}} = $e;
+                               &foreign_call("exports", "delete_export", $e);
+                               }
+                       }
+               for($i=0; $in{"host$i"}; $i++) {
+                       $h = $in{"host$i"} eq '*' ? '' : $in{"host$i"};
+                       $e = $host{$h};
+                       $e = { 'active' => 1,
+                              'host' => $h,
+                              'dir' => $in{'path'} } if (!$e);
+                       delete($e->{'options'}->{'ro'});
+                       if ($in{"ro$i"}) {
+                               $e->{'options'}->{'ro'} = '';
+                               }
+                       delete($e->{'options'}->{'all_squash'});
+                       delete($e->{'options'}->{'no_root_squash'});
+                       if ($in{"squash$i"} == 0) {
+                               $e->{'options'}->{'all_squash'} = '';
+                               }
+                       elsif ($in{"squash$i"} == 2) {
+                               $e->{'options'}->{'no_root_squash'} = '';
+                               }
+                       &foreign_call("exports", "create_export", $e);
+                       }
+               }
+       &unlock_file($econfig{'exports_file'});
+
+       # Apply configuration
+       &exports::restart_mountd();
+
+       &webmin_log($in{'delete'} ? 'delete' : $in{'new'} ? 'create' : 'modify',
+                   'export', $in{'path'});
+       print "1\n";
+       }
+elsif (%dinfo && &check_os_support(\%dinfo)) {
+       # Solaris NFS shares
+       &module_check("dfsadmin");
+       &foreign_require("dfsadmin", "dfs-lib.pl");
+       %iconfig = &foreign_config("dfsadmin");
+       &lock_file($iconfig{'dfstab_file'});
+       @shlist = &foreign_call("dfsadmin", "list_shares");
+       foreach $s (@shlist) {
+               $share = $s if ($s->{'dir'} eq $in{'path'});
+               }
+       if ($in{'delete'}) {
+               # Delete existing share
+               &foreign_call("dfsadmin", "delete_share", $share);
+               }
+       elsif ($in{'new'}) {
+               # Create new share
+               foreach $r ('ro', 'rw', 'root') {
+                       if ($in{$r} ne '-') {
+                               $in{$r} =~ s/\s+/:/g;
+                               $opts->{$r} = $in{$r};
+                               }
+                       }
+               $share->{'dir'} = $in{'path'};
+               $share->{'desc'} = $in{'desc'};
+               $share->{'opts'} =
+                       &foreign_call("dfsadmin", "join_options", $opts);
+               &foreign_call("dfsadmin", "create_share", $share);
+               }
+       else {
+               # Update existing share
+               $opts = &foreign_call("dfsadmin", "parse_options",
+                                     $share->{'opts'});
+               foreach $r ('ro', 'rw', 'root') {
+                       if ($in{$r} eq '-') { delete($opts->{$r}); }
+                       else {
+                               $in{$r} =~ s/\s+/:/g;
+                               $opts->{$r} = $in{$r};
+                               }
+                       }
+               $share->{'dir'} = $in{'path'};
+               $share->{'desc'} = $in{'desc'};
+               $share->{'opts'} =
+                       &foreign_call("dfsadmin", "join_options", $opts);
+               &foreign_call("dfsadmin", "modify_share", $share);
+               }
+       &unlock_file($iconfig{'dfstab_file'});
+
+       # Apply changes to NFS daemon
+       &dfsadmin::apply_configuration();
+
+       &webmin_log($in{'delete'} ? 'delete' : $in{'new'} ? 'create' : 'modify',
+                   'export', $in{'path'});
+       print "1\n";
+       }
+elsif (%binfo && &check_os_support(\%binfo)) {
+       # BSD NFS exports
+       &module_check("bsdexports");
+       }
+else {
+       # No NFS modules installed or supported
+       print "0\n";
+       }
+
+sub module_check
+{
+if (!$acl{$base_remote_user,$_[0]}) {
+       print "0\n";
+       exit;
+       }
+}
+
diff --git a/file/save_html.cgi b/file/save_html.cgi
new file mode 100755 (executable)
index 0000000..04995b8
--- /dev/null
@@ -0,0 +1,36 @@
+#!/usr/local/bin/perl
+# Write data from an HTML editor
+
+require './file-lib.pl';
+$disallowed_buttons{'edit'} && &error($text{'ebutton'});
+&ReadParseMime();
+&error_setup($text{'html_err'});
+
+# Try to write the file
+$p = $in{'file'};
+&switch_acl_uid_and_chroot();
+if ($access{'ro'} || !&can_access($p)) {
+       &popup_error(&text('edit_eaccess', $p));
+       }
+elsif (-l $p && !&must_follow($p)) {
+       &popup_error(&text('edit_efollow', $p));
+       }
+elsif (!&open_tempfile(FILE, ">$p", 1)) {
+       &popup_error("$!");
+       }
+else {
+       &print_tempfile(FILE, $in{'body'});
+       &close_tempfile(FILE);
+
+       # Show JS to close page
+       &popup_header($text{'html_title'});
+
+       $info = &file_info_line($p);
+       print "<script>\n";
+       print "opener.document.FileManager.",
+             "upload_notify(\"$p\", \"$info\");\n";
+       print "close();\n";
+       print "</script>\n";
+
+       &popup_footer();
+       }
diff --git a/file/save_share.cgi b/file/save_share.cgi
new file mode 100755 (executable)
index 0000000..cda75ef
--- /dev/null
@@ -0,0 +1,101 @@
+#!/usr/local/bin/perl
+# save_share.cgi
+# Create, update or delete a samba share
+
+require './file-lib.pl';
+$disallowed_buttons{'sharing'} && &error($text{'ebutton'});
+&ReadParse();
+print "Content-type: text/plain\n\n";
+if ($access{'ro'} || $access{'uid'}) {
+       # User has no access to samba
+       print "0\n";
+       exit;
+       }
+
+%minfo = &get_module_info("samba");
+&read_acl(\%acl, undef);
+if (!%minfo || !&check_os_support(\%minfo) ||
+    !$acl{$base_remote_user,'samba'}) {
+       # Samba module not installed or supported
+       print "0\n";
+       exit;
+       }
+
+&foreign_require("samba", "samba-lib.pl");
+%sconfig = &foreign_config("samba");
+&lock_file($sconfig{'smb_conf'});
+@shares = &foreign_call("samba", "list_shares");
+
+if ($in{'delete'}) {
+       # Deleting an old share
+       foreach $s (@shares) {
+               &foreign_call("samba", "get_share", $s);
+               if ($samba::share{'path'} &&
+                   $samba::share{'path'} eq $in{'path'}) {
+                       &foreign_call("samba", "delete_share", $s);
+                       last;
+                       }
+               }
+       print "1\n";
+       }
+elsif ($in{'new'}) {
+       # Creating a new share
+       map { $taken{$_}++ } @shares;
+       if ($in{'path'} =~ /\/([^\/]+)$/) {
+               $base = $1;
+               }
+       else {
+               $base = "root";
+               }
+       if ($taken{$base}) {
+               for($i=2; $taken{$base.$i}; $i++) { }
+               $base = $base.$i;
+               }
+       $samba::share{'path'} = $in{'path'};
+       $samba::share{'available'} = $in{'available'} ? 'yes' : 'no';
+       $samba::share{'writable'} = $in{'writable'} ? 'yes' : 'no';
+       $samba::share{'comment'} = $in{'comment'};
+       if ($in{'guest'} == 2) {
+               $samba::share{'public'} = 'yes';
+               $samba::share{'guest only'} = 'yes';
+               }
+       elsif ($in{'guest'} == 1) {
+               $samba::share{'public'} = 'yes';
+               }
+       &foreign_call("samba", "create_share", $base);
+       print "1\n";
+       }
+else {
+       # Updating an existing share
+       foreach $s (@shares) {
+               &foreign_call("samba", "get_share", $s);
+               if ($samba::share{'path'} &&
+                   $samba::share{'path'} eq $in{'path'}) {
+                       # found the share to update
+                       $samba::share{'available'} = $in{'available'} ? 'yes'
+                                                                     : 'no';
+                       $samba::share{'writable'} = $in{'writable'} ? 'yes'
+                                                                   : 'no';
+                       $samba::share{'comment'} = $in{'comment'};
+                       if ($in{'guest'} == 2) {
+                               $samba::share{'public'} = 'yes';
+                               $samba::share{'guest only'} = 'yes';
+                               }
+                       elsif ($in{'guest'} == 1) {
+                               $samba::share{'public'} = 'yes';
+                               delete($samba::share{'guest only'});
+                               }
+                       else {
+                               delete($samba::share{'public'});
+                               delete($samba::share{'guest only'});
+                               }
+                       &foreign_call("samba", "modify_share", $s, $s);
+                       last;
+                       }
+               }
+       print "1\n";
+       }
+&unlock_file($sconfig{'smb_conf'});
+&webmin_log($in{'delete'} ? 'delete' : $in{'new'} ? 'create' : 'modify',
+           'share', $in{'path'});
+
diff --git a/file/search.cgi b/file/search.cgi
new file mode 100755 (executable)
index 0000000..2a63ced
--- /dev/null
@@ -0,0 +1,63 @@
+#!/usr/local/bin/perl
+# search.cgi
+# Find files under some directory
+
+require './file-lib.pl';
+$disallowed_buttons{'search'} && &error($text{'ebutton'});
+&ReadParse();
+&switch_acl_uid();
+print "Content-type: text/plain\n\n";
+if (!&can_access($in{'dir'})) {
+       print $text{'search_eaccess'},"\n";
+       }
+
+$in{'dir'} =~ s/^\/+/\//g;
+if ($in{'dir'} ne '/') {
+       $in{'dir'} =~ s/\/$//;
+       }
+$cmd = "find ".quotemeta(&unmake_chroot($in{'dir'}))." -name ".quotemeta($in{'match'});
+if ($in{'type'}) {
+       $cmd .= " -type $in{'type'}";
+       }
+if ($in{'user'}) {
+       $cmd .= " -user $in{'user'}";
+       }
+if ($in{'group'}) {
+       $cmd .= " -group $in{'group'}";
+       }
+if ($in{'size'}) {
+       $cmd .= " -size $in{'size'}";
+       }
+if ($in{'xdev'}) {
+       $cmd .= " -mount";
+       }
+
+print "\n";
+open(CMD, "$cmd 2>/dev/null |");
+while($f = <CMD>) {
+       chop($f);
+       if (defined($in{'cont'})) {
+               # Check the file contents for the given pattern
+               $found = 0;
+               if ($f =~ /\.pdf$/i && &has_command("pdftotext")) {
+                       # Convert PDF to text
+                       open(FILE, "pdftotext -raw ".quotemeta($f)." - |");
+                       }
+               else {
+                       open(FILE, $f);
+                       }
+               while(<FILE>) {
+                       if (/\Q$in{'cont'}\E/i) {
+                               $found = 1;
+                               last;
+                               }
+                       }
+               close(FILE);
+               next if (!$found);
+               }
+       local $rf = &make_chroot($f);
+       local $fil = &file_info_line($f, $rf);
+       print $fil,"\n" if (defined($fil));
+       }
+close(CMD);
+
diff --git a/file/setattrs.cgi b/file/setattrs.cgi
new file mode 100755 (executable)
index 0000000..278b4c2
--- /dev/null
@@ -0,0 +1,44 @@
+#!/usr/local/bin/perl
+# setattrs.cgi
+# Sets all the XFS attributes for a file
+
+require './file-lib.pl';
+$disallowed_buttons{'attr'} && &error($text{'ebutton'});
+&ReadParse();
+&webmin_log("attr", undef, $in{'file'}, \%in);
+&switch_acl_uid_and_chroot();
+print "Content-type: text/plain\n\n";
+if ($access{'ro'} || !&can_access($in{'file'})) {
+       print $text{'facl_eaccess'},"\n";
+       }
+else {
+       # Set given attribs
+       $temp = &transname();
+       for($i=0; defined($n = $in{"name$i"}); $i++) {
+               $v = $in{"value$i"};
+               open(TEMP, ">$temp");
+               print TEMP $v;
+               close(TEMP);
+               $out = `attr -s '$n' '$in{'file'}' <$temp 2>&1`;
+               unlink($temp);
+               if ($?) {
+                       print $out,"\n";
+                       exit;
+                       }
+               $set{$n}++;
+               }
+
+       # Remove those that no longer exist
+       $out = `attr -l '$in{'file'}' 2>&1`;
+       foreach $l (split(/[\r\n]+/, $out)) {
+               if ($l =~ /Attribute\s+"(.*)"/i && !$set{$1}) {
+                       $out = `attr -r '$1' '$in{'file'}' 2>&1`;
+                       if ($?) {
+                               print $out,"\n";
+                               exit;
+                               }
+                       }
+               }
+       print "\n";
+       }
+
diff --git a/file/setext.cgi b/file/setext.cgi
new file mode 100755 (executable)
index 0000000..20fcf98
--- /dev/null
@@ -0,0 +1,23 @@
+#!/usr/local/bin/perl
+# setext.cgi
+# Sets the EXT attributes for some file
+
+require './file-lib.pl';
+$disallowed_buttons{'ext'} && &error($text{'ebutton'});
+&ReadParse();
+&switch_acl_uid_and_chroot();
+print "Content-type: text/plain\n\n";
+if ($access{'ro'} || !&can_access($in{'file'})) {
+       print $text{'facl_eaccess'},"\n";
+       }
+else {
+       $cmd = "chattr '=$in{'attrs'}' '$in{'file'}'";
+       $out = `$cmd 2>&1`;
+       if ($?) {
+               print $out,"\n";
+               }
+       else {
+               print "\n";
+               }
+       }
+
diff --git a/file/setfacl.cgi b/file/setfacl.cgi
new file mode 100755 (executable)
index 0000000..c862f5e
--- /dev/null
@@ -0,0 +1,45 @@
+#!/usr/local/bin/perl
+# setfacl.cgi
+# Sets the ACLs for some file
+
+require './file-lib.pl';
+$disallowed_buttons{'acl'} && &error($text{'ebutton'});
+&ReadParse();
+&webmin_log("acl", undef, $in{'file'}, \%in);
+&switch_acl_uid_and_chroot();
+print "Content-type: text/plain\n\n";
+if ($access{'ro'} || !&can_access($in{'file'})) {
+       print $text{'facl_eaccess'},"\n";
+       }
+else {
+       pipe(ACLINr, ACLINw);
+       pipe(ACLOUTr, ACLOUTw);
+       $pid = fork();
+       if (!$pid) {
+               untie(*STDIN);
+               untie(*STDOUT);
+               untie(*STDERR);
+               open(STDIN, "<&ACLINr");
+               open(STDOUT, ">&ACLOUTw");
+               open(STDERR, ">&ACLOUTw");
+               close(ACLINw);
+               close(ACLOUTr);
+               exec("$config{'setfacl'} '$in{'file'}'");
+               print "Exec failed : $!\n";
+               exit(1);
+               }
+       close(ACLINr);
+       close(ACLOUTw);
+       print ACLINw $in{'acl'},"\n";
+       close(ACLINw);
+       waitpid($pid, 0);
+       $rv = <ACLOUTr>;
+       close(ACLOUTr);
+       if ($rv) {
+               print $rv;
+               }
+       else {
+               print "\n";
+               }
+       }
+
diff --git a/file/show.cgi b/file/show.cgi
new file mode 100755 (executable)
index 0000000..b2acc11
--- /dev/null
@@ -0,0 +1,161 @@
+#!/usr/local/bin/perl
+# show.cgi
+# Output some file for the browser
+
+require './file-lib.pl';
+&ReadParse();
+use POSIX;
+$p = $ENV{'PATH_INFO'};
+if ($in{'type'}) {
+       # Use the supplied content type
+       $type = $in{'type'};
+       $download = 1;
+       }
+elsif ($in{'format'} == 1) {
+       # Type comes from compression format
+       $type = "application/zip";
+       }
+elsif ($in{'format'} == 2) {
+       $type = "application/x-gzip";
+       }
+elsif ($in{'format'} == 3) {
+       $type = "application/x-tar";
+       }
+else {
+       # Try to guess type from filename
+       $type = &guess_mime_type($p, undef);
+       if (!$type) {
+               # No idea .. use the 'file' command
+               $out = &backquote_command("file ".
+                                         quotemeta(&resolve_links($p)), 1);
+               if ($out =~ /text|script/) {
+                       $type = "text/plain";
+                       }
+               else {
+                       $type = "application/unknown";
+                       }
+               }
+       }
+
+# Dump the file
+$temp = &transname();
+&switch_acl_uid();
+$p = &unmake_chroot($p);
+if (!&can_access($p)) {
+       # ACL rules prevent access to file
+       &error_exit(&text('view_eaccess', $p));
+       }
+
+if ($in{'format'}) {
+       # An archive of a directory was requested .. create it
+       $archive || &error_exit($text{'view_earchive'});
+       if ($in{'format'} == 1) {
+               $p =~ s/\.zip$//;
+               }
+       elsif ($in{'format'} == 2) {
+               $p =~ s/\.tgz$//;
+               }
+       elsif ($in{'format'} == 3) {
+               $p =~ s/\.tar$//;
+               }
+       -d $p || &error_exit($text{'view_edir'}." ".$p);
+       if ($archive == 2 && $archmax > 0) {
+               # Check if directory is too large to archive
+               local $kb = &disk_usage_kb($p);
+               if ($kb*1024 > $archmax) {
+                       &error_exit(&text('view_earchmax', $archmax));
+                       }
+               }
+
+       # Work out the base directory and filename
+       if ($p =~ /^(.*\/)([^\/]+)$/) {
+               $pdir = $1;
+               $pfile = $2;
+               }
+       else {
+               $pdir = "/";
+               $pfile = $p;
+               }
+
+       # Work out the command to run
+       if ($in{'format'} == 1) {
+               &has_command("zip") || &error_exit(&text('view_ecmd', "zip"));
+               $cmd = "zip -r $temp ".quotemeta($pfile);
+               }
+       elsif ($in{'format'} == 2) {
+               &has_command("tar") || &error_exit(&text('view_ecmd', "tar"));
+               &has_command("gzip") || &error_exit(&text('view_ecmd', "gzip"));
+               $cmd = "tar cf - ".quotemeta($pfile)." | gzip -c >$temp";
+               }
+       elsif ($in{'format'} == 3) {
+               &has_command("tar") || &error_exit(&text('view_ecmd', "tar"));
+               $cmd = "tar cf $temp ".quotemeta($pfile);
+               }
+
+       if ($in{'test'}) {
+               # Don't actually do anything if in test mode
+               &ok_exit();
+               }
+
+       # Run the command, and send back the resulting file
+       local $qpdir = quotemeta($pdir);
+       local $out = `cd $qpdir ; ($cmd) 2>&1 </dev/null`;
+       if ($?) {
+               unlink($temp);
+               &error_exit(&text('view_ecomp', $out));
+               }
+       local @st = stat($temp);
+       print "Content-length: $st[7]\n";
+       print "Content-type: $type\n\n";
+       open(FILE, $temp);
+       while(read(FILE, $buf, 1024)) {
+               print $buf;
+               }
+       close(FILE);
+       unlink($temp);
+       }
+else {
+       if (!open(FILE, $p)) {
+               # Unix permissions prevent access
+               &error_exit(&text('view_eopen', $p, $!));
+               }
+
+       if ($in{'test'}) {
+               # Don't actually do anything if in test mode
+               close(FILE);
+               &ok_exit();
+               }
+
+       @st = stat($p);
+       print "X-no-links: 1\n";
+       print "Content-length: $st[7]\n";
+       print "Content-Disposition: Attachment\n" if ($download);
+       print "Content-type: $type\n\n";
+       if ($type =~ /^text\/html/i && !$in{'edit'}) {
+               while(read(FILE, $buf, 1024)) {
+                       $data .= $buf;
+                       }
+               print &filter_javascript($data);
+               }
+       else {
+               while(read(FILE, $buf, 1024)) {
+                       print $buf;
+                       }
+               }
+       close(FILE);
+       }
+
+sub error_exit
+{
+print "Content-type: text/plain\n";
+print "Content-length: ",length($_[0]),"\n\n";
+print $_[0];
+exit;
+}
+
+sub ok_exit
+{
+print "Content-type: text/plain\n\n";
+print "\n";
+}
+
diff --git a/file/size.cgi b/file/size.cgi
new file mode 100755 (executable)
index 0000000..7013e0b
--- /dev/null
@@ -0,0 +1,49 @@
+#!/usr/local/bin/perl
+# size.cgi
+# Returns the size in bytes, number of files and number of dirs in a directory
+
+require './file-lib.pl';
+&ReadParse();
+&switch_acl_uid_and_chroot();
+print "Content-type: text/plain\n\n";
+if (!&can_list($in{'dir'})) {
+       print $text{'list_eaccess'},"\n";
+       }
+($size, $files, $dirs) = &recursive_dir_info($in{'dir'});
+print "\n";
+print $size," ",$files," ",$dirs," ",&nice_size($size),"\n";
+
+# recursive_dir_info(directory)
+sub recursive_dir_info
+{
+local $dir = &translate_filename($_[0]);
+if (-l $dir) {
+       # Symlink
+       return (0, 1, 0);
+       }
+elsif (-f $dir) {
+       local @st = stat($dir);
+       return ($st[7], 1, 0);
+       }
+elsif (-d $dir) {
+       local @st = stat($dir);
+       local ($size, $files, $dirs) = ($st[7], 0, 1);
+       opendir(DIR, $dir);
+       local @files = readdir(DIR);
+       closedir(DIR);
+       foreach my $f (@files) {
+               next if ($f eq "." || $f eq "..");
+               local @r = &recursive_dir_info("$dir/$f");
+               $size += $r[0];
+               $files += $r[1];
+               $dirs += $r[2];
+               }
+       return ($size, $files, $dirs);
+       }
+else {
+       # Special file ..
+       return (0, 1, 0);
+       }
+}
+
+
diff --git a/file/unicode.pl b/file/unicode.pl
new file mode 100755 (executable)
index 0000000..c1b3c23
--- /dev/null
@@ -0,0 +1,17 @@
+#!/usr/bin/perl
+
+use Encode::HanConvert;
+
+$ENV{'WEBMIN_CONFIG'} ||= "/etc/webmin";
+$ENV{'WEBMIN_VAR'} ||= "/var/webmin";
+$no_acl_check++;
+do './file-lib.pl';
+
+@lang_order_list = ( "zh_TW.Big5" );
+%big5 = &load_language($module_name);
+
+foreach $k (keys %big5) {
+       $unicode{$k} = big5_to_trad($big5{$k});
+       }
+
+&write_file("$module_root_directory/unicode/zh_TW.Big5", \%unicode);
diff --git a/file/unicode/zh_TW.Big5 b/file/unicode/zh_TW.Big5
new file mode 100644 (file)
index 0000000..0829498
--- /dev/null
@@ -0,0 +1,324 @@
+feedback_module=關於模組
+help_efile=無法讀取說明檔 $1
+delete_eaccess=您不被允許刪除 '$1'
+session_logout=登出成功,使用下面的欄位重新登入
+session_failed=登入失敗,請再試一次
+main_none=您沒有任何可使用的Webmin模組
+month_4=四月
+copy_eto=您不被允許拷貝到 '$1'
+smonth_3=三月
+acl_gedit=選擇可以看到的群組
+config_dir=給模組 $1
+chmod_echown=變更擁有者失敗 : $1
+edit_title=編輯 $1 中
+elock_tries=無法鎖檔案 $1在 $2 分鐘後
+feedback_desc2=此報告將會傳送給Usermin發展群,而非您的系統管理者,網路提供者,或主機公司,回饋內容請以英文撰寫。
+paste_egone=複製的檔案 $1 已經不存在
+referer_again=以後不再顯示這個訊息
+delete_efailed=無法刪除 $1 : $2
+month_11=十一月
+rename_title=變更名稱 $1
+acl_uedit_uid=使用者 UID 範圍是
+main_logout=登出
+upload_ewrite=無法寫入到 $1 : $2.
+link_eexists=$1 已經存在
+sday_2=星期二
+feedback_ecannot=您不被允許寄送回饋信件
+feedback_prog=傳送回饋信件到 $1 使用sendmail程式 $2
+main_homepage=首頁
+delete_ddesc=您確定要永久的刪除目錄 $1 與其所有內容嗎?
+acl_home=使用者家目錄
+info_perms=權限
+info_user=使用者:
+chooser_dir=$1 的目錄
+header_servers=Webmin 伺服器
+smonth_12=十二月
+info_size=大小:
+config_nochange=不改變
+category_webmin=Webmin
+paste_ecopy=在您貼上要先剪下或複製
+file_type0=目錄
+groups_clear=清除
+feedback_config=在寄送郵件中包含模組組態設定?
+acl_gedit_all=所有群組
+feedback_ecannot2=您不被允許寄送包含設定組態檔案的回饋信件
+acl_uedit_all=所有使用者
+sday_3=星期三
+no=否
+session_header=登入Webmin
+acl_gedit_none=沒有群組
+paste_eself=您不能把檔案貼到本身上
+info_apply1=只有這個目錄
+config_none=無
+acl_rpc0=否
+longcategory_info=顯示您的系統資訊的模組
+mkdir_eexists=$1 已經存在
+right_user=使用者
+top_open=開啟
+sday_4=星期四
+config_title=組態設定
+acl_uedit_except=全部使用者除了
+smonth_7=七月
+acl_feedback=可以寄送回饋信件?
+header_webmin=Webmin 索引
+smonth_2=二月
+chooser_title1=選擇檔案...
+session_mesg=您必須輸入使用者名稱和密碼來登入Webmin 伺服器在 $1上。
+groups_title1=選擇群組...
+feedback_to=傳送回饋到位址
+info_setgid3=以群組權限執行
+copy_efrom=您不被允許拷貝自 '$1'
+feedback_configdesc=如果選取此項目,在回饋郵件中會附上相關的模組和模組會用到相關的所有設定檔案。例如 ,如果回饋是關於 <b><i>使用者和群組</i></b> 模組,你的 <tt>/etc/passwd</tt>和 <tt>/etc/shadow</tt>檔案會被附加上去。
+acl_feedback1=是,但不能包含組態檔案
+feedback_emodule=在信件中您選擇了包含模組組態設定,但卻沒有選取模組。
+month_1=一月
+smonth_6=六月
+users_ok=確定
+paste_ecfailed=拷貝失敗 : $1
+top_upload=上傳
+feedback_os=在寄送郵件中包含作業系統資訊?
+top_new=增加
+sday_6=星期六
+index_nojava=這個模組需要 Java 才能正確的運作, 但是您的瀏覽器似乎不支援 Java
+month_8=八月
+referer_warn_unknown=<b>警告!</b> Webmin發現程式 $2從不知名的位置連結來的,出現在 Webmin以外。並企圖嘗試在您的伺服器執行危險的指令。
+rename_eold=您不被允許改名自 '$1'
+link_to=連結到:
+yes=是
+acl_rpc2=只有 <tt>root</tt> 或 <tt>admin</tt>
+main_title=Webmin $1 在 $2 ($3)
+chooser_ok=確定
+info_own=所有權
+referer_title=安全警告
+mkdir_eaccess=您不被允許建立 '$1'
+header_config=模組組態
+category_=其他
+right_group=群組
+users_title1=選擇使用者...
+session_mesg2=您必須輸入使用者名稱和密碼來登入
+right_name=名稱
+top_refresh=重讀
+groups_sel=選擇的群組
+helpsearch=搜尋文件
+acl_umask=建立檔案的遮罩
+info_sticky=設定位元:
+help_err=無法顯示說明
+top_edit=編輯
+top_info=資訊
+top_copy=複製
+day_1=星期一
+upload_title=上傳檔案
+chooser_title2=選擇目錄...
+link_from=連結自:
+error=錯誤
+acl_rpc=可以使用RPC ?
+month_10=十月
+create=建立
+upload_file=要上傳的檔案
+feedback_text=描述問題或建議
+top_paste=貼上
+move_eto=您不被允許移動到 '$1'
+list_eaccess=您不被允許存取這個目錄
+feedback_email=您的電子郵件
+skill_medium=中等
+delete_fdesc=您確定要永久的刪除檔案 $1 嗎?
+rename_old=舊檔名:
+users_sel=選擇的使用者
+chmod_echmod=變更權限失敗 : $1
+acl_rpc1=是
+month_7=七月
+file_type7=管線
+smonth_1=一月
+default=預設
+info_type=類別:
+info_link=連結到:
+groups_cancel=取消
+category_net=網路
+day_4=星期四
+category_syslet=網管
+upload_edir=上傳的目錄不存在.
+smonth_5=五月
+header_help=說明...
+help_epath=遺失說明檔路徑
+groups_ok=確定
+category_cluster=電腦叢集
+chmod_egroup=$1 : 沒有這個群組
+rename_eexists=叫做 $1 的檔案已經存在
+modify=修改
+mkdir_dir=目錄:
+longcategory_net=網路和網路服務組態模組
+emodule=存取拒絕 : 使用者 $1 不允許使用 $2 模組
+top_view=檢視
+acl_feedback2=是
+top_cut=剪下
+header_module=模組索引
+find_eexist=$1 不存在於 $2
+referer_ok=繼續執行Webmin程式
+skill_low=新手
+sday_5=星期五
+save=儲存
+feedback_header=回饋內容
+month_9=九月
+info_efailed=無法重讀 $1 : $2
+upload_ok=上傳
+session_save=記得上次登入帳號?
+config_err=儲存組態錯誤
+longcategory_hardware=列表機,磁碟和其他硬體組態模組
+acl_dirs=只允許存取到目錄
+find_eaccess=您不被允許存取 $1
+feedback_econfig=您沒有完整的權限來選取模組
+right_date=日期
+file_type6=設備控制檔
+edit_filename=檔案名稱:
+category_info=資訊
+info_read=讀取
+main_skill=Skill level
+main_switch=切換使用者...
+info_write=寫入
+delete=刪除
+referer_warn=<b>警告!</b> Webmin發現程式 $2從 URL $1連結來的,出現在 Webmin以外。並企圖嘗試在您的伺服器執行危險的指令。
+info_setuid2=以使用者權限執行
+config_eaccess=您不被允許存取這個模組
+mkdir_title=增加目錄
+groups_title2=選擇群組...
+ok=確定
+progress_data2=下載 $1 位元組
+link_efailed=連結失敗 : $1
+help_eexec=$1 失敗 : $2
+delete_ftitle=刪除檔案
+rename_efailed=改名失敗 : $1
+view_eopen=無法開啟n $1 : $2
+top_delete=刪除
+index=索引
+feedback_mailserver=傳送經由SMTP伺服器
+day_3=星期三
+feedback_title=回饋Webmin
+progress_size=下載中 $1 ($2 位元組) ..
+month_12=十二月
+info_group=群組:
+acl_uedit_group=使用者群組
+paste_eover=$1 無法被蓋寫
+info_apply3=這個目錄與所有的子目錄
+feedback_enoto=沒有填寫傳送回饋位址位置
+main_title2=Webmin
+feedback_err=寄送回饋時發生錯誤
+top_rename=改名
+efilewrite=寫入$1失敗 : $2
+feedback_all=所有模組
+month_6=六月
+programname=Webmin
+copy_elink=符號連結失敗 : $1
+config_setto=Set to
+help_eheader=遺失 &lt;標頭&gt; 區段
+edit_eaccess=您不被允許儲存 '$1'
+acl_uedit_only=只有使用者
+category_system=系統
+smonth_10=十月
+longcategory_=無法分類的其他模組
+month_3=三月
+file_type1=文字檔
+delete_dtitle=刪除目錄
+day_5=星期五
+feedback_via=傳送回饋信件到 $1經由 SMTP伺服器 $2
+acl_gedit_except=所有群組除了
+acl_follow=是否跟隨符號連結?
+smonth_9=九月
+file_type4=檔案
+acl_gedit_gid=群組GID範圍是
+session_login=登入
+link_title=建立連結
+move_afrom=您不被允許移動自 '$1'
+category_servers=伺服器
+rename_ok=改名
+info_setgid=設定 GID:
+progress_done=.. 下載完成
+find_edir=$1 不是一個目錄在 $2
+upload_efile=沒有選擇要上傳的檔案.
+smonth_4=四月
+day_2=星期二
+upload_eperm=您不被允許建立 $1
+users_clear=清除
+info_other=其他:
+feedback_desc=這表單可以讓你回報錯誤(bugs)和建議Webmin發展群關於任何問題或所忽略的特色,當按下傳送按鈕後,這頁的內容將會寄到 $1。
+edit_eover=$1 無法被蓋寫
+progress_nosize=正在下載 $1 ..
+feedback_mailserver_def=本地sendmail程式
+acl_feedback0=否
+right_size=大小
+file_type2=影像檔
+longcategory_webmin=組態Webmin自己的模組
+sday_1=星期一
+info_apply=套用變更到
+users_cancel=取消
+acl_uedit=選擇可以看到的使用者
+rename_enew=您不被允許改名成 '$1'
+info_setgid2=檔案繼承群組
+file_type5=符號連結
+feedback_attach=加入要附上的檔案
+smonth_11=十一月
+link_efollow=您不被允許建立符號連結
+acl_uedit_none=沒有使用者
+main_version=版本 $1 在 $2 ($3)
+upload_dir=上傳到目錄
+session_pass=密碼
+find=找尋
+chmod_euser=$1 : 沒有這個使用者
+skill_high=專家 
+longcategory_cluster=從單一介面管理多重服務
+main_return=回到 $1
+info_exec=執行
+feedback_send=傳送回饋
+acl_gedit_only=只有群組
+longcategory_system=使用者,檔案系統,工作排程和其他系統設定
+rename_new=變更為:
+upload_efailed=開啟上傳失敗 : $1
+info_file=檔案
+month_5=五月
+chmod_elink=符號連結失敗d : $1
+acl_root=檔案選取時的根目錄
+groups_all=全部的群組
+feedback_name=您的姓名
+feedback_osdesc=如果選取此項目,會自動在回饋信件中加上您的作業系統名稱和版本。
+info_sticky2=只有擁有者才能刪除檔案
+switch_remote_euser=Unix使用者 $1 並不存在
+paste_emfailed=移動失敗 : $1
+view_enormal=只有一般的檔案可以被檢視
+config_header=給 $1 的可組態選項
+link_essl=您的系統尚未安裝 Net::SSLeay Perl模組所需要使用的HTTPS連線。
+longcategory_servers=web, email, FTP和其他服務的組態模組
+view_eaccess=您不被允許存取 $1
+mkdir_efailed=建立目錄失敗 : $1
+day_6=星期六
+info_mod=修改時間:
+session_clear=清除
+edit_title2=建立檔案中
+main_feedback=回饋...
+info_setuid=設定 UID:
+chmod_eaccess=您不被允許存取 '$1'
+help_einclude=無法引入 $1
+edit_esave=無法儲存檔案 : $1
+smonth_8=八月
+cancel=取消
+session_user=使用者名稱
+users_all=全部的使用者
+info_path=路徑:
+edit_enormal=只能編輯一般的檔案
+feedback_emodule2=選取的模組不存在
+config_ecannot=您不被允許組態這個模組
+session_timed_out=連線逾時於$1分鐘
+sday_0=星期日
+acl_user=以使用者權限存取伺服器上的檔案
+reset=重設
+progress_data=下載 $1 位元組 ($2 %)
+upload_conv=是否要改變 DOS 的換行符號?
+index_title=檔案管理者
+day_0=星期日
+help_eif=$1 失敗 : $2
+feedback_esend=使用sendmail或本地SMTP伺服器傳送回饋郵件錯誤
+info_list=列出
+users_title2=選擇使用者...
+info_apply2=這個目錄與其中的檔案
+month_2=二月
+file_type3=二進位檔
+link_efrom=您不被允許連結 '$1'
+category_hardware=硬體
diff --git a/file/upform.cgi b/file/upform.cgi
new file mode 100755 (executable)
index 0000000..d319930
--- /dev/null
@@ -0,0 +1,39 @@
+#!/usr/local/bin/perl
+# upform.cgi
+# Display the upload form
+
+require './file-lib.pl';
+$disallowed_buttons{'upload'} && &error($text{'ebutton'});
+&ReadParse(undef, undef, 1);
+&popup_header($text{'upload_title'});
+$upid = time().$$;
+$args = ($in{'extra'} ? $in{'extra'}."&" : "?")."id=$upid";
+
+print &ui_form_start("upload.cgi$args", "form-data", undef,
+                    &read_parse_mime_javascript($upid, [ "file" ]));
+print &ui_table_start($text{'upload_title'}, "width=100%", 2);
+
+print &ui_table_row($text{'upload_file'},
+                   &ui_upload("file", 20));
+
+print &ui_table_row($text{'upload_dir'},
+                   &ui_textbox("dir", $in{'dir'}, 20)."\n".
+                   &ui_submit($text{'upload_ok'}));
+
+if ($dostounix == 1) {
+       print &ui_table_row($text{'upload_conv'},
+                           &ui_yesno_radio("dos", 0));
+       }
+
+if ($unarchive == 1) {
+       print &ui_table_row($text{'upload_zip'},
+                           &ui_radio("zip", 0,
+                               [ [ 2, $text{'upload_yes'} ],
+                                 [ 1, $text{'yes'} ],
+                                 [ 0, $text{'no'} ] ]));
+       }
+
+print &ui_table_end();
+print &ui_form_end();
+&popup_footer();
+
diff --git a/file/upload.cgi b/file/upload.cgi
new file mode 100755 (executable)
index 0000000..00cd977
--- /dev/null
@@ -0,0 +1,76 @@
+#!/usr/local/bin/perl
+# upload.cgi
+# Upload a file
+
+require './file-lib.pl';
+$disallowed_buttons{'upload'} && &error($text{'ebutton'});
+&popup_header();
+&ReadParse(\%getin, "GET");
+$upid = $getin{'id'};
+&ReadParseMime($upload_max, \&read_parse_mime_callback, [ $upid ]);
+
+$realdir = &unmake_chroot($in{'dir'});
+if (!$in{'file_filename'}) {
+       print "<p><b>$text{'upload_efile'}</b><p>\n";
+       }
+elsif (!-d $realdir) {
+       print "<p><b>$text{'upload_edir'}</b><p>\n";
+       }
+else {
+       $in{'file_filename'} =~ /([^\\\/]+)$/;
+       $path = "$in{'dir'}/$1";
+       $realpath = "$realdir/$1";
+       if (-e $realpath) {
+               # File exists .. ask the user if he is sure
+               &switch_acl_uid();
+               $temp = &tempname();
+               &open_tempfile(TEMP, ">$temp");
+               if ($dostounix == 1 && $in{'dos'}) {
+                       $in{'file'} =~ s/\r\n/\n/g;
+                       }
+               &print_tempfile(TEMP, $in{'file'});
+               &close_tempfile(TEMP);
+               print "<form action=upload2.cgi>\n";
+               foreach $i (keys %prein) {
+                       print "<input type=hidden name=$i value='",
+                               &html_escape($prein{$i}),"'>\n";
+                       }
+               print "<input type=hidden name=dir value='",
+                       &html_escape($in{'dir'}),"'>\n";
+               print "<input type=hidden name=path value='",
+                       &html_escape($path),"'>\n";
+               print "<input type=hidden name=temp value='",
+                       &html_escape($temp),"'>\n";
+               print "<input type=hidden name=zip value='",
+                       &html_escape($in{'zip'}),"'>\n";
+               print "<center>\n";
+               print &text('upload_already', "<tt>$path</tt>"),"<p>\n";
+               print "<input type=submit name=yes value='$text{'yes'}'>\n";
+               print "<input type=submit name=no value='$text{'no'}'>\n";
+               print "</form>\n";
+               }
+       else {
+               # Go ahread and do it!
+               &webmin_log("upload", undef, $path);
+               &switch_acl_uid();
+               if ($access{'ro'} || !&can_access($path)) {
+                       print "<p><b>",&text('upload_eperm', $path),"</b><p>\n";
+                       }
+               elsif (-l $path && !&must_follow($realpath)) {
+                       print "<p><b>",&text('upload_elink', $path),"</b><p>\n";
+                       }
+               elsif (!&open_tempfile(FILE, ">$realpath", 1)) {
+                       print "<p><b>",&text('upload_ewrite', $path, $!),"</b><p>\n";
+                       }
+               else {
+                       if ($dostounix == 1 && $in{'dos'}) {
+                               $in{'file'} =~ s/\r\n/\n/g;
+                               }
+                       &print_tempfile(FILE, $in{'file'});
+                       &close_tempfile(FILE);
+                       &post_upload($path, $in{'dir'}, $in{'zip'});
+                       }
+               }
+       }
+
+&popup_footer();
diff --git a/file/upload2.cgi b/file/upload2.cgi
new file mode 100755 (executable)
index 0000000..a01fec2
--- /dev/null
@@ -0,0 +1,38 @@
+#!/usr/local/bin/perl
+# upload2.cgi
+# Rename a file that has already been uploaded
+
+require './file-lib.pl';
+$disallowed_buttons{'upload'} && &error($text{'ebutton'});
+&header();
+&ReadParse();
+
+if ($in{'yes'}) {
+       # Put it in place, overwriting any other file
+       &webmin_log("upload", undef, $in{'path'});
+       &switch_acl_uid();
+       if ($access{'ro'} || !&can_access($in{'path'})) {
+               print "<p><b>",&text('upload_eperm', $in{'path'}),"</b><p>\n";
+               }
+       elsif (!&open_tempfile(FILE, ">".&unmake_chroot($in{'path'}), 1)) {
+               print "<p><b>",&text('upload_ewrite', $in{'path'}, $!),"</b><p>\n";
+               }
+       else {
+               open(TEMP, $in{'temp'});
+               &copydata(TEMP, FILE) ||
+                       &error(&text('upload_ewrite', $in{'path'}, $!));
+               close(TEMP);
+               &close_tempfile(FILE);
+               &post_upload($in{'path'}, $in{'dir'}, $in{'zip'});
+               }
+       unlink($in{'temp'});
+       }
+else {
+       # Just delete the temp file
+       &switch_acl_uid();
+       unlink($in{'temp'});
+       print "<script>\n";
+       print "close();\n";
+       print "</script>\n";
+       }
+
diff --git a/file/xinha b/file/xinha
new file mode 120000 (symlink)
index 0000000..04a6e3b
--- /dev/null
@@ -0,0 +1 @@
+../mailboxes/xinha
\ No newline at end of file
diff --git a/mailboxes/CHANGELOG b/mailboxes/CHANGELOG
new file mode 100644 (file)
index 0000000..e956ddb
--- /dev/null
@@ -0,0 +1,37 @@
+---- Changes since 1.130 ----
+The first version of this module, which extracts the mail reading functionality from the Sendmail, Postfix and Qmail modules and puts it into a more powerful server-independent user email module instead.
+---- Changes since 1.150 ----
+Added a basic HTML editor for sending and replying to email in HTML format. Requires Java 1.4+ in the browser. Must be enabled on the Module Config page, as it is still rather unstable.
+Included support for SMTP authentication when sending email, configurable on the Module Config page.
+---- Changes since 1.160 ----
+Added buttons for reporting a message as spam and adding the sender to the global SpamAssassin blacklist.
+---- Changes since 1.170 ----
+Added button for deleting all mail in a folder (disabled by default in the Module Config).
+---- Changes since 1.180 ----
+Added support for two new mail systems - VPopMail and Qmail+LDAP. Both of these access mail for users in external databases, rather than Unix users.
+---- Changes since 1.200 ----
+Added a Module Config option (on by default) to get the From: address for users from Virtualmin, if installed.
+---- Changes since 1.210 ----
+Added Module Config options to show the number of messages in each users' inbox and sent-mail folder, and to attached a signature to sent mail.
+---- Changes since 1.220 ----
+All Webmin users can now report spam, rather than just those that have access to the SpamAssassin module.
+---- Changes since 1.240 ----
+Output from sa-learn or spamassassin is now show when reporting multiple messages as spam.
+---- Changes since 1.250 ----
+Added a Module Config option for selecting users to not display, thanks to Brad Kester.
+The displayed size of mailboxes in Maildir format includes all files in the directory, rather than just those in the cur, tmp and new subdirectories in older Webmin releases.
+---- Changes since 1.260 ----
+When the module is configured to display only users with mail and no users have mail, then nothing will be displayed.
+---- Changes since 1.270 ----
+Added a Module Config option for setting the date format.
+Added a Module Config option to enable pager arrows at the bottom of mail list and individual message pages.
+Placed a Delete All button at the bottom of each user's mail list, for deleting all mail in a folder.
+Added a Module Config option for setting the timezone for dates.
+The simple search box accepts inputs like from: jcameron or size: 10000 to search on a single specific field.
+Added a Module Config option for opening user email messages in separate windows.
+---- Changes since 1.300 ----
+The number of folders each user has is displayed in the user list.
+---- Changes since 1.310 ----
+Use ~username instead of the full home directory path in the folders list.
+---- Changes since 1.330 ----
+Replaced the HTMLarea widget for composing email with Xinha.
diff --git a/mailboxes/Makefile b/mailboxes/Makefile
new file mode 100644 (file)
index 0000000..d76398d
--- /dev/null
@@ -0,0 +1,2 @@
+HTMLEditor.class:      HTMLEditor.java
+                       CLASSPATH=/usr/local/netscape7/plugins/java2/lib/javaplugin.jar:. javac HTMLEditor.java
diff --git a/mailboxes/acl_security.pl b/mailboxes/acl_security.pl
new file mode 100644 (file)
index 0000000..8c6f09e
--- /dev/null
@@ -0,0 +1,124 @@
+
+require 'mailboxes-lib.pl';
+
+# acl_security_form(&options)
+# Output HTML for editing security options for the sendmail module
+sub acl_security_form
+{
+# Users whose mail can be read
+print "<tr> <td valign=top><b>$text{'acl_read'}</b></td> <td colspan=3>\n";
+printf "<input type=radio name=mmode value=0 %s> $text{'acl_none'}\n",
+       $_[0]->{'mmode'} == 0 ? "checked" : "";
+
+printf "<input type=radio name=mmode value=4 %s> $text{'acl_same'}\n",
+       $_[0]->{'mmode'} == 4 ? "checked" : "";
+
+printf "<input type=radio name=mmode value=1 %s> $text{'acl_all'}<br>\n",
+       $_[0]->{'mmode'} == 1 ? "checked" : "";
+
+printf "<input type=radio name=mmode value=2 %s> $text{'acl_users'}\n",
+       $_[0]->{'mmode'} == 2 ? "checked" : "";
+printf "<input name=musers size=40 value='%s'> %s<br>\n",
+       $_[0]->{'mmode'} == 2 ? $_[0]->{'musers'} : "",
+       &user_chooser_button("musers", 1);
+
+printf "<input type=radio name=mmode value=3 %s> $text{'acl_userse'}\n",
+       $_[0]->{'mmode'} == 3 ? "checked" : "";
+printf "<input name=muserse size=40 value='%s'> %s<br>\n",
+       $_[0]->{'mmode'} == 3 ? $_[0]->{'musers'} : "",
+       &user_chooser_button("muserse", 1);
+
+printf "<input type=radio name=mmode value=5 %s> $text{'acl_usersg'}\n",
+       $_[0]->{'mmode'} == 5 ? "checked" : "";
+printf "<input name=musersg size=30 value='%s'> %s\n",
+       $_[0]->{'mmode'} == 5 ? join(" ", map { scalar(getgrgid($_)) }
+                                    split(/\s+/, $_[0]->{'musers'})) : "",
+       &group_chooser_button("musersg", 1);
+printf "<input type=checkbox name=msec value=1 %s> %s<br>\n",
+       $_[0]->{'msec'} ? "checked" : "", $text{'acl_sec'};
+
+printf "<input type=radio name=mmode value=7 %s> $text{'acl_usersu'}\n",
+       $_[0]->{'mmode'} == 7 ? "checked" : "";
+printf "<input name=musersu1 size=6 value='%s'> -\n",
+       $_[0]->{'mmode'} == 7 ? $_[0]->{'musers'} : "";
+printf "<input name=musersu2 size=6 value='%s'><br>\n",
+       $_[0]->{'mmode'} == 7 ? $_[0]->{'musers2'} : "";
+
+printf "<input type=radio name=mmode value=6 %s> $text{'acl_usersm'}\n",
+       $_[0]->{'mmode'} == 6 ? "checked" : "";
+printf "<input name=musersm size=15 value='%s'></td> </tr>\n",
+       $_[0]->{'mmode'} == 6 ? $_[0]->{'musers'} : "";
+
+# Directory for arbitrary files
+print "<tr> <td valign=top><b>$text{'acl_dir'}</b></td> <td colspan=3>\n";
+print &ui_opt_textbox("dir", $_[0]->{'dir'}, 40, $text{'acl_dirauto'}."<br>");
+print "</td> </tr>\n";
+
+# Allowed From: addresses
+print "<tr> <td valign=top><b>$text{'acl_from'}</b></td> <td colspan=3>\n";
+printf "<input type=radio name=fmode value=0 %s> $text{'acl_any'}<br>\n",
+       $_[0]->{'fmode'} == 0 ? "checked" : "";
+printf "<input type=radio name=fmode value=1 %s> $text{'acl_fdoms'}\n",
+       $_[0]->{'fmode'} == 1 ? "checked" : "";
+printf "<input name=fdoms size=40 value='%s'><br>\n",
+       $_[0]->{'fmode'} == 1 ? $_[0]->{'from'} : '';
+printf "<input type=radio name=fmode value=2 %s> $text{'acl_faddrs'}\n",
+       $_[0]->{'fmode'} == 2 ? "checked" : "";
+printf "<input name=faddrs size=40 value='%s'><br>\n",
+       $_[0]->{'fmode'} == 2 ? $_[0]->{'from'} : '';
+printf "<input type=radio name=fmode value=3 %s> $text{'acl_fdom'}\n",
+       $_[0]->{'fmode'} == 3 ? "checked" : "";
+printf "<input name=fdom size=20 value='%s'><br>\n",
+       $_[0]->{'fmode'} == 3 ? $_[0]->{'from'} : '';
+print "</td> </tr>\n";
+
+print "<tr> <td><b>$text{'acl_fromname'}</b></td>\n";
+print "<td colspan=3><input name=fromname size=40 ",
+      "value='$_[0]->{'fromname'}'></td> </tr>\n";
+
+print "<tr> <td><b>$text{'acl_attach'}</b></td> <td colspan=3>\n";
+printf "<input type=radio name=attach_def value=1 %s> %s\n",
+       $_[0]->{'attach'}<0 ? 'checked' : '', $text{'acl_unlimited'};
+printf "<input type=radio name=attach_def value=0 %s>\n",
+       $_[0]->{'attach'}<0 ? '' : 'checked';
+printf "<input name=attach size=5 value='%s'> kB\n",
+       $_[0]->{'attach'}<0 ? '' : $_[0]->{'attach'};
+print "</td> </tr>\n";
+
+print "<tr> <td><b>$text{'acl_canattach'}</b></td>\n";
+printf "<td><input type=radio name=canattach value=1 %s> %s\n",
+       $_[0]->{'canattach'} ? 'checked' : '', $text{'yes'};
+printf "<input type=radio name=canattach value=0 %s> %s</td>\n",
+       $_[0]->{'canattach'} ? '' : 'checked', $text{'no'};
+
+print "<td><b>$text{'acl_candetach'}</b></td>\n";
+printf "<td><input type=radio name=candetach value=1 %s> %s\n",
+       $_[0]->{'candetach'} ? 'checked' : '', $text{'yes'};
+printf "<input type=radio name=candetach value=0 %s> %s</td> </tr>\n",
+       $_[0]->{'candetach'} ? '' : 'checked', $text{'no'};
+}
+
+# acl_security_save(&options)
+# Parse the form for security options for the sendmail module
+sub acl_security_save
+{
+$_[0]->{'mmode'} = $in{'mmode'};
+$_[0]->{'musers'} = $in{'mmode'} == 2 ? $in{'musers'} :
+                   $in{'mmode'} == 3 ? $in{'muserse'} :
+                   $in{'mmode'} == 5 ? join(" ", map { scalar(getgrnam($_)) }
+                                            split(/\s+/, $in{'musersg'})) :
+                   $in{'mmode'} == 6 ? $in{'musersm'} :
+                   $in{'mmode'} == 7 ? $in{'musersu1'} : "";
+$_[0]->{'musers2'} = $in{'mmode'} == 7 ? $in{'musersu2'} : "";
+$_[0]->{'msec'} = $in{'msec'};
+$_[0]->{'fmode'} = $in{'fmode'};
+$_[0]->{'from'} = $in{'fmode'} == 0 ? undef :
+                 $in{'fmode'} == 1 ? $in{'fdoms'} :
+                 $in{'fmode'} == 2 ? $in{'faddrs'} : $in{'fdom'};
+$_[0]->{'fromname'} = $in{'fromname'};
+$_[0]->{'attach'} = $in{'attach_def'} ? -1 : $in{'attach'};
+$_[0]->{'canattach'} = $in{'canattach'};
+$_[0]->{'candetach'} = $in{'candetach'};
+$_[0]->{'dir'} = $in{'dir_def'} ? undef : $in{'dir'};
+}
+
diff --git a/mailboxes/boxes-lib.pl b/mailboxes/boxes-lib.pl
new file mode 100644 (file)
index 0000000..d1024cc
--- /dev/null
@@ -0,0 +1,2282 @@
+# boxes-lib.pl
+# Functions to parsing user mail files
+
+use POSIX;
+if ($userconfig{'date_tz'} || $config{'date_tz'}) {
+        # Set the timezone for all date calculations, and force a conversion
+        # now as in some cases the first on fails!
+        $ENV{'TZ'} = $userconfig{'date_tz'} ||
+                     $config{'date_tz'};
+        strftime('%H:%M', localtime(time()));
+        }
+use Time::Local;
+
+# Always use DBM indexing
+$config{'index_dbm'} = 2;
+$config{'index_min'} = 1000000;
+
+# list_mails(user|file, [start], [end])
+# Returns a subset of mail from a mbox format file
+sub list_mails
+{
+local (@rv, $h, $done);
+my (@index, %index, $itype);
+$itype = &index_type($_[0]);
+if ($itype == 0) {
+       @index = &build_index($_[0]);
+       }
+else {
+       &build_dbm_index($_[0], \%index);
+       }
+local ($start, $end);
+local $isize = $itype == 0 ? scalar(@index) : $index{'mailcount'};
+if (@_ == 1 || !defined($_[1]) && !defined($_[2])) {
+       $start = 0; $end = $isize-1;
+       }
+elsif ($_[2] < 0) {
+       $start = $isize+$_[2]-1; $end = $isize+$_[1]-1;
+       $start = $start<0 ? 0 : $start;
+       }
+else {
+       $start = $_[1]; $end = $_[2];
+       $end = $isize-1 if ($end >= $isize);
+       }
+$rv[$isize-1] = undef if ($isize);     # force array to right size
+local $dash = &dash_mode($_[0]);
+open(MAIL, &user_mail_file($_[0]));
+$start = 0 if ($start < 0);
+for($i=$start; $i<=$end; $i++) {
+       # Seek to mail position
+       local $startline;
+       if ($itype == 0) {
+               seek(MAIL, $index[$i]->[0], 0);
+               $startline = $index[$i]->[1];
+               }
+       else {
+               local @idx = split(/\0/, $index{$i});
+               seek(MAIL, $idx[0], 0);
+               $startline = $idx[1];
+               }
+
+       # Read the mail
+       local $mail = &read_mail_fh(MAIL, $dash ? 2 : 1, 0);
+       $mail->{'line'} = $startline;
+       $mail->{'eline'} = $startline + $mail->{'lines'} - 1;
+       $mail->{'idx'} = $i;
+       $rv[$i] = $mail;
+       }
+return @rv;
+}
+
+# select_mails(user|file, &indexes, headersonly)
+# Returns a list of messages with the given indexes
+sub select_mails
+{
+local (@rv);
+my (@index, %index, $itype);
+$itype = &index_type($_[0]);
+if ($itype == 0) {
+       @index = &build_index($_[0]);
+       }
+else {
+       &build_dbm_index($_[0], \%index);
+       }
+local $isize = $itype == 0 ? scalar(@index) : $index{'mailcount'};
+open(MAIL, &user_mail_file($_[0]));
+foreach my $i (@{$_[1]}) {
+       # Seek to mail position
+       local $startline;
+       if ($itype == 0) {
+               seek(MAIL, $index[$i]->[0], 0);
+               $startline = $index[$i]->[1];
+               }
+       else {
+               local @idx = split(/\0/, $index{$i});
+               seek(MAIL, $idx[0], 0);
+               $startline = $idx[1];
+               }
+
+       # Read the mail
+       local $mail = &read_mail_fh(MAIL, $dash ? 2 : 1, $_[2]);
+       $mail->{'line'} = $startline;
+       $mail->{'eline'} = $startline + $mail->{'lines'} - 1;
+       $mail->{'idx'} = $i;
+       push(@rv, $mail);
+       }
+close(MAIL);
+return @rv;
+}
+
+# search_mail(user, field, match)
+# Returns an array of messages matching some search
+sub search_mail
+{
+return &advanced_search_mail($_[0], [ [ $_[1], $_[2] ] ], 1);
+}
+
+# advanced_search_mail(user|file, &fields, andmode, [&limits])
+# Returns an array of messages matching some search
+sub advanced_search_mail
+{
+local $itype = &index_type($_[0]);
+local (@index, %index, @rv, $i);
+local $dash = &dash_mode($_[0]);
+local @possible;               # index positions of possible mails
+local $possible_certain = 0;   # is possible list authoratative?
+local ($min, $max);
+if ($itype == 0) {
+       # We have only a plain index ..
+       @index = &build_index($_[0]);
+       $min = 0;
+       $max = scalar(@index)-1;
+       if ($_[3] && $_[3]->{'latest'}) {
+               $min = $max - $_[3]->{'latest'};
+               }
+       @possible = ($min .. $max);
+       }
+else {
+       # We have a DBM index .. if the search includes the from and subject
+       # fields, scan it first to cut down on the total time
+       &build_dbm_index($_[0], \%index);
+
+       # Check which fields are used in search
+       local @dbmfields = grep { $_->[0] eq 'from' ||
+                                 $_->[0] eq 'subject' } @{$_[1]};
+       local $alldbm = (scalar(@dbmfields) == scalar(@{$_[1]}));
+
+       $min = 0;
+       $max = $index{'mailcount'}-1;
+       if ($_[3] && $_[3]->{'latest'}) {
+               $min = $max - $_[3]->{'latest'};
+               }
+
+       # Only check DBM if it contains some fields, and if it contains all
+       # fields when in 'or' mode.
+       if (@dbmfields && ($alldbm || $_[2])) {
+               # Scan the DBM to build up a list of 'possibles'
+               for($i=$min; $i<=$max; $i++) {
+                       local @idx = split(/\0/, $index{$i});
+                       local $fake = { 'header' => { 'from', $idx[2],
+                                                     'subject', $idx[3] } };
+                       local $m = &mail_matches(\@dbmfields, $_[2], $fake);
+                       push(@possible, $i) if ($m);
+                       }
+               $possible_certain = $alldbm;
+               }
+       else {
+               # None of the DBM fields are in the search .. have to scan all
+               @possible = ($min .. $max);
+               }
+       }
+
+# Need to scan through possible messages to find those that match
+open(MAIL, &user_mail_file($_[0]));
+foreach $i (@possible) {
+       # Seek to mail position
+       local $startline;
+       if ($itype == 0) {
+               seek(MAIL, $index[$i]->[0], 0);
+               $startline = $index[$i]->[1];
+               }
+       else {
+               local @idx = split(/\0/, $index{$i});
+               seek(MAIL, $idx[0], 0);
+               $startline = $idx[1];
+               }
+
+       # Read the mail
+       local $mail = &read_mail_fh(MAIL, $dash ? 2 : 1, 0);
+       $mail->{'line'} = $startline;
+       $mail->{'eline'} = $startline + $mail->{'lines'} - 1;
+       $mail->{'idx'} = $i;
+       push(@rv, $mail) if ($possible_certain ||
+                            &mail_matches($_[1], $_[2], $mail));
+       }
+return @rv;
+}
+
+# build_index(user|file)
+sub build_index
+{
+local @index;
+local $ifile = &user_index_file($_[0]);
+local $umf = &user_mail_file($_[0]);
+local @ist = stat($ifile);
+local @st = stat($umf);
+
+if (open(INDEX, $ifile)) {
+       @index = map { /(\d+)\s+(\d+)/; [ $1, $2 ] } <INDEX>;
+       close(INDEX);
+       }
+
+if (!@ist || !@st || $ist[9] < $st[9] || $st[7] < $config{'index_min'}) {
+       # The mail file is newer than the index, or we are always re-indexing
+       local $fromok = 1;
+       local ($l, $ll);
+       local $dash = &dash_mode($umf);
+       if ($st[7] < $config{'index_min'}) {
+               $fromok = 0;    # Always re-index
+               open(MAIL, $umf);
+               }
+       else {
+               if (open(MAIL, $umf)) {
+                       local $il = $#index;
+                       local $i;
+                       for($i=($il>100 ? 100 : $il); $i>=0; $i--) {
+                               $l = $index[$il-$i];
+                               seek(MAIL, $index[$il-$i]->[0], 0);
+                               $ll = <MAIL>;
+                               $fromok = 0 if ($ll !~ /^From\s+(\S+).*\d+\r?\n/ ||
+                                               ($1 eq '-' && !$dash));
+                               }
+                       }
+               else {
+                       $fromok = 0;    # No mail file yet
+                       }
+               }
+       local ($pos, $lnum);
+       if (scalar(@index) && $fromok && $st[7] > $l->[0]) {
+               # Mail file seems to have gotten bigger, most likely
+               # because new mail has arrived ... only reindex the new mails
+               $pos = $l->[0] + length($ll);
+               $lnum = $l->[1] + 1;
+               }
+       else {
+               # Mail file has changed in some other way ... do a rebuild
+               $pos = 0;
+               $lnum = 0;
+               undef(@index);
+               seek(MAIL, 0, 0);
+               }
+       while(<MAIL>) {
+               if (/^From\s+(\S+).*\d+\r?\n/ && ($1 ne '-' || $dash)) {
+                       push(@index, [ $pos, $lnum ]);
+                       }
+               $pos += length($_);
+               $lnum++;
+               }
+       close(MAIL);
+       open(INDEX, ">$ifile");
+       print INDEX map { $_->[0]." ".$_->[1]."\n" } @index;
+       close(INDEX);
+       }
+return @index;
+}
+
+# build_dbm_index(user|file, &index)
+# Returns a reference to a DBM hash that indexes the given mail file.
+# Hash contains keys 0, 1, 2 .. each of which has a value containing the
+# position of the mail in the file, line number, subject and sender.
+# Special key lastchange = time index was last updated
+#            mailcount = number of messages in index
+sub build_dbm_index
+{
+local $ifile = &user_index_file($_[0]);
+local $umf = &user_mail_file($_[0]);
+local @st = stat($umf);
+local $index = $_[1];
+
+dbmopen(%$index, $ifile, 0600);
+if (!@st || $index->{'lastchange'} < $st[9] || $st[7] < $config{'index_min'}) {
+       # The mail file is newer than the index, or we are always re-indexing
+       local $fromok = 1;
+       local ($ll, @idx);
+       local $dash = &dash_mode($umf);
+       if ($st[7] < $config{'index_min'}) {
+               $fromok = 0;    # Always re-index
+               open(MAIL, $umf);
+               }
+       else {
+               if (open(MAIL, $umf)) {
+                       # Check the last 100 messages (at most)
+                       local $il = $index->{'mailcount'}-1;
+                       local $i;
+                       for($i=($il>100 ? 100 : $il); $i>=0; $i--) {
+                               @idx = split(/\0/, $index->{$il-$i});
+                               seek(MAIL, $idx[0], 0);
+                               $ll = <MAIL>;
+                               $fromok = 0 if ($ll !~ /^From\s+(\S+).*\d+\r?\n/ ||
+                                               ($1 eq '-' && !$dash));
+                               }
+                       }
+               else {
+                       $fromok = 0;    # No mail file yet
+                       }
+               }
+       local ($pos, $lnum, $istart);
+       if ($index->{'mailcount'} && $fromok && $st[7] > $idx[0]) {
+               # Mail file seems to have gotten bigger, most likely
+               # because new mail has arrived ... only reindex the new mails
+               $pos = $idx[0] + length($ll);
+               $lnum = $idx[1] + 1;
+               $istart = $index->{'mailcount'};
+               }
+       else {
+               # Mail file has changed in some other way ... do a rebuild
+               $istart = 0;
+               $pos = 0;
+               $lnum = 0;
+               seek(MAIL, 0, 0);
+               }
+       local ($doingheaders, @nidx);
+       while(<MAIL>) {
+               if (/^From\s+(\S+).*\d+\r?\n/ && ($1 ne '-' || $dash)) {
+                       @nidx = ( $pos, $lnum );
+                       $index->{$istart++} = join("\0", @nidx);
+                       $doingheaders = 1;
+                       }
+               elsif ($_ eq "\n" || $_ eq "\r\n") {
+                       $doingheaders = 0;
+                       }
+               elsif ($doingheaders && /^From:\s*(.{0,255})/i) {
+                       $nidx[2] = $1;
+                       $index->{$istart-1} = join("\0", @nidx);
+                       }
+               elsif ($doingheaders && /^Subject:\s*(.{0,255})/i) {
+                       $nidx[3] = $1;
+                       $index->{$istart-1} = join("\0", @nidx);
+                       }
+               $pos += length($_);
+               $lnum++;
+               }
+       close(MAIL);
+       $index->{'lastchange'} = time();
+       $index->{'mailcount'} = $istart;
+       }
+}
+
+# index_type(user|file)
+# Returns 0 if an old-style index exists for some mailbox, 1 if not (indicating
+# that DBM indexing should be used)
+sub index_type
+{
+return 0 if (!$config{'index_dbm'});
+return 1 if ($config{'index_dbm'} == 2);
+local $ifile = &user_index_file($_[0]);
+return -r $ifile ? 0 : 1;
+}
+
+# empty_mail(user|file)
+# Truncate a mail file to nothing
+sub empty_mail
+{
+local $umf = &user_mail_file($_[0]);
+local $itype = &index_type($_[0]);
+local $ifile = &user_index_file($_[0]);
+open(TRUNC, ">$umf");
+close(TRUNC);
+if ($itype == 0) {
+       # Truncate index too
+       open(TRUNC, ">$ifile");
+       close(TRUNC);
+       }
+else {
+       # Set index size to 0
+       local %index;
+       dbmopen(%index, $ifile, 0600);
+       $index{'mailcount'} = 0;
+       $index{'lastchange'} = time();
+       dbmclose(%index);
+       }
+}
+
+# count_mail(user|file)
+# Returns the number of messages in some mail file
+sub count_mail
+{
+my (@index, %index, $itype);
+$itype = &index_type($_[0]);
+if ($itype == 0) {
+       @index = &build_index($_[0]);
+       return scalar(@index);
+       }
+else {
+       &build_dbm_index($_[0], \%index);
+       return $index{'mailcount'};
+       }
+}
+
+# parse_mail(&mail, [&parent], [savebody])
+# Extracts the attachments from the mail body
+sub parse_mail
+{
+return if ($_[0]->{'parsed'}++);
+local $ct = $_[0]->{'header'}->{'content-type'};
+local (@attach, $h, $a);
+if ($ct =~ /multipart\/(\S+)/i && ($ct =~ /boundary="([^"]+)"/i ||
+                                  $ct =~ /boundary=([^;\s]+)/i)) {
+       # Multipart MIME message
+       local $bound = "--".$1;
+       local @lines = split(/\r?\n/, $_[0]->{'body'});
+       local $l;
+       local $max = @lines;
+       while($l < $max && $lines[$l++] ne $bound) {
+               # skip to first boundary
+               }
+       while(1) {
+               # read attachment headers
+               local (@headers, $attach);
+               while($lines[$l]) {
+                       $attach->{'raw'} .= $lines[$l]."\n";
+                       $attach->{'rawheaders'} .= $lines[$l]."\n";
+                       if ($lines[$l] =~ /^(\S+):\s*(.*)/) {
+                               push(@headers, [ $1, $2 ]);
+                               }
+                       elsif ($lines[$l] =~ /^\s+(.*)/) {
+                               $headers[$#headers]->[1] .= $1
+                                       unless($#headers < 0);
+                               }
+                       $l++;
+                       }
+               $attach->{'raw'} .= $lines[$l]."\n";
+               $l++;
+               $attach->{'headers'} = \@headers;
+               foreach $h (@headers) {
+                       $attach->{'header'}->{lc($h->[0])} = $h->[1];
+                       }
+               if ($attach->{'header'}->{'content-type'} =~ /^([^;]+)/) {
+                       $attach->{'type'} = lc($1);
+                       }
+               else {
+                       $attach->{'type'} = 'text/plain';
+                       }
+               if ($attach->{'header'}->{'content-disposition'} =~
+                   /filename\s*=\s*"([^"]+)"/i) {
+                       $attach->{'filename'} = $1;
+                       }
+               elsif ($attach->{'header'}->{'content-disposition'} =~
+                      /filename\s*=\s*([^;\s]+)/i) {
+                       $attach->{'filename'} = $1;
+                       }
+               elsif ($attach->{'header'}->{'content-type'} =~
+                   /name\s*=\s*"([^"]+)"/i) {
+                       $attach->{'filename'} = $1;
+                       }
+
+               # read the attachment body
+               while($l < $max && $lines[$l] ne $bound && $lines[$l] ne "$bound--") {
+                       $attach->{'data'} .= $lines[$l]."\n";
+                       $attach->{'raw'} .= $lines[$l]."\n";
+                       $l++;
+                       }
+               $attach->{'data'} =~ s/\n\n$/\n/;       # Lose trailing blank line
+               $attach->{'raw'} =~ s/\n\n$/\n/;
+
+               # decode if necessary
+               if (lc($attach->{'header'}->{'content-transfer-encoding'}) eq
+                   'base64') {
+                       # Standard base64 encoded attachment
+                       $attach->{'data'} = &b64decode($attach->{'data'});
+                       }
+               elsif (lc($attach->{'header'}->{'content-transfer-encoding'}) eq
+                      'x-uue') {
+                       # UUencoded attachment
+                       $attach->{'data'} = &uudecode($attach->{'data'});
+                       }
+               elsif (lc($attach->{'header'}->{'content-transfer-encoding'}) eq
+                      'quoted-printable') {
+                       # Quoted-printable text attachment
+                       $attach->{'data'} = &quoted_decode($attach->{'data'});
+                       }
+               elsif (lc($attach->{'type'}) eq 'application/mac-binhex40' && &has_command("hexbin")) {
+                       # Macintosh binhex encoded attachment
+                       local $temp = &transname();
+                       mkdir($temp, 0700);
+                       open(HEXBIN, "| (cd $temp ; hexbin -n attach -d 2>/dev/null)");
+                       print HEXBIN $attach->{'data'};
+                       close(HEXBIN);
+                       if (!$?) {
+                               open(HEXBIN, "$temp/attach.data");
+                               local $/ = undef;
+                               $attach->{'data'} = <HEXBIN>;
+                               close(HEXBIN);
+                               local $ct = &guess_mime_type($attach->{'filename'});
+                               $attach->{'type'} = $ct;
+                               $attach->{'header'} = { 'content-type' => $ct };
+                               $attach->{'headers'} = [ [ 'Content-Type', $ct ] ];
+                               }
+                       unlink("$temp/attach.data");
+                       rmdir($temp);
+                       }
+
+               $attach->{'idx'} = scalar(@attach);
+               $attach->{'parent'} = $_[1] ? $_[1] : $_[0];
+               push(@attach, $attach) if (@headers || $attach->{'data'});
+               if ($attach->{'type'} =~ /multipart\/(\S+)/i) {
+                       # This attachment contains more attachments ..
+                       # expand them.
+                       local $amail = { 'header' => $attach->{'header'},
+                                        'body' => $attach->{'data'} };
+                       &parse_mail($amail, $attach);
+                       $attach->{'attach'} = [ @{$amail->{'attach'}} ];
+                       map { $_->{'idx'} += scalar(@attach) }
+                           @{$amail->{'attach'}};
+                       push(@attach, @{$amail->{'attach'}});
+                       }
+               elsif (lc($attach->{'type'}) eq 'application/ms-tnef') {
+                       # This attachment is a winmail.dat file, which may
+                       # contain multiple other attachments!
+                       local ($opentnef, $tnef);
+                       if (!($opentnef = &has_command("opentnef")) &&
+                           !($tnef = &has_command("tnef"))) {
+                               $attach->{'error'} = "tnef command not installed";
+                               }
+                       else {
+                               # Can actually decode
+                               local $tempfile = &transname();
+                               open(TEMPFILE, ">$tempfile");
+                               print TEMPFILE $attach->{'data'};
+                               close(TEMPFILE);
+                               local $tempdir = &transname();
+                               mkdir($tempdir, 0700);
+                               if ($opentnef) {
+                                       system("$opentnef -d $tempdir -i $tempfile >/dev/null 2>&1");
+                                       }
+                               else {
+                                       system("$tnef -C $tempdir -f $tempfile >/dev/null 2>&1");
+                                       }
+                               pop(@attach);   # lose winmail.dat
+                               opendir(DIR, $tempdir);
+                               while($f = readdir(DIR)) {
+                                       next if ($f eq '.' || $f eq '..');
+                                       local $data;
+                                       open(FILE, "$tempdir/$f");
+                                       while(<FILE>) {
+                                               $data .= $_;
+                                               }
+                                       close(FILE);
+                                       local $ct = &guess_mime_type($f);
+                                       push(@attach,
+                                         { 'type' => $ct,
+                                           'idx' => scalar(@attach),
+                                           'header' =>
+                                               { 'content-type' => $ct },
+                                           'headers' =>
+                                               [ [ 'Content-Type', $ct ] ],
+                                           'filename' => $f,
+                                           'data' => $data });
+                                       }
+                               closedir(DIR);
+                               unlink(glob("$tempdir/*"), $tempfile);
+                               rmdir($tempdir);
+                               }
+                       }
+               last if ($l >= $max || $lines[$l] eq "$bound--");
+               $l++;
+               }
+       $_[0]->{'attach'} = \@attach;
+       }
+elsif ($_[0]->{'body'} =~ /begin\s+([0-7]+)\s+(.*)/i) {
+       # Message contains uuencoded file(s)
+       local @lines = split(/\n/, $_[0]->{'body'});
+       local ($attach, $rest);
+       foreach $l (@lines) {
+               if ($l =~ /^begin\s+([0-7]+)\s+(.*)/i) {
+                       $attach = { 'type' => &guess_mime_type($2),
+                                   'idx' => scalar(@{$_[0]->{'attach'}}),
+                                   'parent' => $_[1],
+                                   'filename' => $2 };
+                       push(@{$_[0]->{'attach'}}, $attach);
+                       }
+               elsif ($l =~ /^end/ && $attach) {
+                       $attach = undef;
+                       }
+               elsif ($attach) {
+                       $attach->{'data'} .= unpack("u", $l);
+                       }
+               else {
+                       $rest .= $l."\n";
+                       }
+               }
+       if ($rest =~ /\S/) {
+               # Some leftover text
+               push(@{$_[0]->{'attach'}},
+                       { 'type' => "text/plain",
+                         'idx' => scalar(@{$_[0]->{'attach'}}),
+                         'parent' => $_[1],
+                         'data' => $rest });
+               }
+       }
+elsif (lc($_[0]->{'header'}->{'content-transfer-encoding'}) eq 'base64') {
+       # Signed body section
+       $ct =~ s/;.*$//;
+       $_[0]->{'attach'} = [ { 'type' => lc($ct),
+                               'idx' => 0,
+                               'parent' => $_[1],
+                               'data' => &b64decode($_[0]->{'body'}) } ];
+       }
+elsif (lc($_[0]->{'header'}->{'content-type'}) eq 'x-sun-attachment') {
+       # Sun attachment format, which can contain several sections
+       local $sun;
+       foreach $sun (split(/----------/, $_[0]->{'body'})) {
+               local ($headers, $rest) = split(/\r?\n\r?\n/, $sun, 2);
+               local $attach = { 'idx' => scalar(@{$_[0]->{'attach'}}),
+                                 'parent' => $_[1],
+                                 'data' => $rest };
+               if ($headers =~ /X-Sun-Data-Name:\s*(\S+)/) {
+                       $attach->{'filename'} = $1;
+                       }
+               if ($headers =~ /X-Sun-Data-Type:\s*(\S+)/) {
+                       local $st = $1;
+                       $attach->{'type'} = $st eq "text" ? "text/plain" :
+                                           $st eq "html" ? "text/html" :
+                                           $st =~ /\// ? $st : "application/octet-stream";
+                       }
+               elsif ($attach->{'filename'}) {
+                       $attach->{'type'} =
+                               &guess_mime_type($attach->{'filename'});
+                       }
+               else {
+                       $attach->{'type'} = "text/plain";       # fallback
+                       }
+               push(@{$_[0]->{'attach'}}, $attach);
+               }
+       }
+else {
+       # One big attachment (probably text)
+       local ($type, $body);
+       ($type = $ct) =~ s/;.*$//;
+       $type = 'text/plain' if (!$type);
+       if (lc($_[0]->{'header'}->{'content-transfer-encoding'}) eq 'base64') {
+               $body = &b64decode($_[0]->{'body'});
+               }
+       elsif (lc($_[0]->{'header'}->{'content-transfer-encoding'}) eq 
+              'quoted-printable') {
+               $body = &quoted_decode($_[0]->{'body'});
+               }
+       else {
+               $body = $_[0]->{'body'};
+               }
+       $_[0]->{'attach'} = [ { 'type' => lc($type),
+                               'idx' => 0,
+                               'parent' => $_[1],
+                               'data' => $body } ];
+       }
+delete($_[0]->{'body'}) if (!$_[2]);
+}
+
+# delete_mail(user|file, &mail, ...)
+# Delete mail messages from a user by copying the file and rebuilding the index
+sub delete_mail
+{
+local @m = sort { $a->{'line'} <=> $b->{'line'} } @_[1..@_-1];
+local $i = 0;
+local $f = &user_mail_file($_[0]);
+local $ifile = &user_index_file($_[0]);
+local $itype = &index_type($_[0]);
+local $lnum = 0;
+local %dline;
+local ($dpos = 0, $dlnum = 0);
+local (@index, %index);
+if ($itype == 1) {
+       &build_dbm_index($_[0], \%index);
+       }
+
+local $tmpf = $< == 0 ? "$f.del" :
+             $_[0] =~ /^\/.*\/([^\/]+)$/ ?
+               "$user_module_config_directory/$1.del" :
+             "$user_module_config_directory/$_[0].del";
+open(SOURCE, $f) || &error("Read failed : $!");
+open(DEST, ">$tmpf") || &error("Open of $tmpf failed : $!");
+while(<SOURCE>) {
+       if ($i >= @m || $lnum < $m[$i]->{'line'}) {
+               if ($itype == 0 && /^From\s+(\S+).*\d+\r?\n/ &&
+                                  ($1 ne '-' || $dash)) {
+                       push(@index, [ $dpos, $dlnum ]);
+                       }
+               $dpos += length($_);
+               $dlnum++;
+               local $w = (print DEST $_);
+               if (!$w) {
+                       local $e = "$!";
+                       close(DEST);
+                       close(SOURCE);
+                       unlink($tmpf);
+                       &error("Write to $tmpf failed : $e");
+                       }
+               }
+       elsif ($lnum == $m[$i]->{'eline'}) {
+               $dline{$m[$i]->{'line'}}++;
+               $i++;
+               }
+       $lnum++;
+       }
+close(SOURCE);
+close(DEST) || &error("Write to $tmpf failed : $?");
+local @st = stat($f);
+unlink($f) if ($< == 0);
+if ($itype == 0) {
+       open(INDEX, ">$ifile");
+       print INDEX map { $_->[0]." ".$_->[1]."\n" } @index;
+       close(INDEX);
+       }
+else {
+       # Just force a total index re-build (XXX lazy!)
+       $index{'mailcount'} = $in{'lastchange'} = 0;
+       }
+if ($< == 0) {
+       rename($tmpf, $f);
+       }
+else {
+       system("cat ".quotemeta($tmpf)." > ".quotemeta($f).
+              " && rm -f ".quotemeta($tmpf));
+       }
+chown($st[4], $st[5], $f);
+chmod($st[2], $f);
+}
+
+# modify_mail(user|file, old, new, textonly)
+# Modify one email message in a mailbox by copying the file and rebuilding
+# the index.
+sub modify_mail
+{
+local $f = &user_mail_file($_[0]);
+local $ifile = &user_index_file($_[0]);
+local $itype = &index_type($_[0]);
+local $lnum = 0;
+local ($sizediff, $linesdiff);
+local (@index, %index);
+if ($itype == 0) {
+       @index = &build_index($_[0]);
+       }
+else {
+       &build_dbm_index($_[0], \%index);
+       }
+
+# Replace the email that gets modified
+local $tmpf = $< == 0 ? "$f.del" :
+             $_[0] =~ /^\/.*\/([^\/]+)$/ ?
+               "$user_module_config_directory/$1.del" :
+             "$user_module_config_directory/$_[0].del";
+open(SOURCE, $f);
+open(DEST, ">$tmpf");
+while(<SOURCE>) {
+       if ($lnum < $_[1]->{'line'} || $lnum > $_[1]->{'eline'}) {
+               # before or after the message to change
+               local $w = (print DEST $_);
+               if (!$w) {
+                       local $e = "$?";
+                       close(DEST);
+                       close(SOURCE);
+                       unlink($tmpf);
+                       &error("Write to $tmpf failed : $e");
+                       }
+               }
+       elsif ($lnum == $_[1]->{'line'}) {
+               # found start of message to change .. put in the new one
+               close(DEST);
+               local @ost = stat($tmpf);
+               local $nlines = &send_mail($_[2], $tmpf, $_[3], 1);
+               local @nst = stat($tmpf);
+               local $newsize = $nst[7] - $ost[7];
+               $sizediff = $newsize - $_[1]->{'size'};
+               $linesdiff = $nlines - ($_[1]->{'eline'} - $_[1]->{'line'} + 1);
+               open(DEST, ">>$tmpf");
+               }
+       $lnum++;
+       }
+close(SOURCE);
+close(DEST) || &error("Write failed : $!");
+
+# Now update the index and delete the temp file
+if ($itype == 0) {
+       # Update old-style index
+       foreach $i (@index) {
+               if ($i->[1] > $_[1]->{'line'}) {
+                       # Shift mails after the modified
+                       $i->[0] += $sizediff;
+                       $i->[1] += $linesdiff;
+                       }
+               }
+       }
+else {
+       # Update DBM index
+       for($i=0; $i<$index{'mailcount'}; $i++) {
+               local @idx = split(/\0/, $index{$i});
+               if ($idx[1] > $_[1]->{'line'}) {
+                       $idx[0] += $sizediff;
+                       $idx[1] += $linesdiff;
+                       $index{$i} = join("\0", @idx);
+                       }
+               }
+       $index{'lastchange'} = time();
+       }
+local @st = stat($f);
+unlink($f);
+if ($itype == 0) {
+       open(INDEX, ">$ifile");
+       print INDEX map { $_->[0]." ".$_->[1]."\n" } @index;
+       close(INDEX);
+       }
+if ($< == 0) {
+       rename($tmpf, $f);
+       }
+else {
+       system("cat $tmpf >$f && rm -f $tmpf");
+       }
+chown($st[4], $st[5], $f);
+chmod($st[2], $f);
+
+}
+
+# send_mail(&mail, [file], [textonly], [nocr], [smtp-server],
+#          [smtp-user], [smtp-pass], [smtp-auth-mode],
+#          [&notify-flags], [port])
+# Send out some email message or append it to a file.
+# Returns the number of lines written.
+sub send_mail
+{
+return 0 if (&is_readonly_mode());
+local (%header, $h);
+local $lnum = 0;
+local $sm = $_[4] || $config{'send_mode'};
+local $eol = $_[3] || !$sm ? "\n" : "\r\n";
+local $port = $_[9] || $config{'smtp_port'} || 25;
+foreach $h (@{$_[0]->{'headers'}}) {
+       $header{lc($h->[0])} = $h->[1];
+       }
+local @tm = localtime(time());
+push(@{$_[0]->{'headers'}},
+     [ 'Date', strftime("%a, %d %b %Y %H:%M:%S %z (%Z)", @tm) ])
+       if (!$header{'date'});
+local @from = &address_parts($header{'from'});
+local $esmtp = $_[8] ? 1 : 0;
+if ($_[1]) {
+       # Just append the email to a file using mbox format
+       open(MAIL, ">>$_[1]") || &error("Write failed : $!");
+       $lnum++;
+       print MAIL $_[0]->{'fromline'} ? $_[0]->{'fromline'}."\n" :
+                  strftime("From $from[0] %a %b %e %H:%M:%S %Y\n", @tm);
+       }
+elsif ($sm) {
+       # Connect to SMTP server
+       &open_socket($sm, $port, MAIL);
+       &smtp_command(MAIL);
+       if ($esmtp) {
+               &smtp_command(MAIL, "ehlo ".&get_system_hostname()."\r\n");
+               }
+       else {
+               &smtp_command(MAIL, "helo ".&get_system_hostname()."\r\n");
+               }
+
+       # Get username and password from parameters, or from module config
+       local $user = $_[5] || $userconfig{'smtp_user'} || $config{'smtp_user'};
+       local $pass = $_[6] || $userconfig{'smtp_pass'} || $config{'smtp_pass'};
+       local $auth = $_[7] || $userconfig{'smtp_auth'} ||
+                     $config{'smtp_auth'} || "Cram-MD5";
+       if ($user) {
+               # Send authentication commands
+               eval "use Authen::SASL";
+               if ($@) {
+                       &error("Perl module <tt>Authen::SASL</tt> is needed for SMTP authentication");
+                       }
+               my $sasl = Authen::SASL->new('mechanism' => uc($auth),
+                                            'callback' => {
+                                               'auth' => $user,
+                                               'user' => $user,
+                                               'pass' => $pass } );
+               &error("Failed to create Authen::SASL object") if (!$sasl);
+               local $conn = $sasl->client_new("smtp", &get_system_hostname());
+               local $arv = &smtp_command(MAIL, "auth $auth\r\n", 1);
+               if ($arv =~ /^(334)\s+(.*)/) {
+                       # Server says to go ahead
+                       $extra = $2;
+                       local $initial = $conn->client_start();
+                       local $auth_ok;
+                       if ($initial) {
+                               local $enc = &encode_base64($initial);
+                               $enc =~ s/\r|\n//g;
+                               $arv = &smtp_command(MAIL, "$enc\r\n", 1);
+                               if ($arv =~ /^(\d+)\s+(.*)/) {
+                                       if ($1 == 235) {
+                                               $auth_ok = 1;
+                                               }
+                                       else {
+                                               &error("Unknown SMTP authentication response : $arv");
+                                               }
+                                       }
+                               $extra = $2;
+                               }
+                       while(!$auth_ok) {
+                               local $message = &decode_base64($extra);
+                               local $return = $conn->client_step($message);
+                               local $enc = &encode_base64($return);
+                               $enc =~ s/\r|\n//g;
+                               $arv = &smtp_command(MAIL, "$enc\r\n", 1);
+                               if ($arv =~ /^(\d+)\s+(.*)/) {
+                                       if ($1 == 235) {
+                                               $auth_ok = 1;
+                                               }
+                                       elsif ($1 == 535) {
+                                               &error("SMTP authentication failed : $arv");
+                                               }
+                                       $extra = $2;
+                                       }
+                               else {
+                                       &error("Unknown SMTP authentication response : $arv");
+                                       }
+                               }
+                       }
+               }
+
+       &smtp_command(MAIL, "mail from: <$from[0]>\r\n");
+       local $notify = $_[8] ? " NOTIFY=".join(",", @{$_[8]}) : "";
+       local $u;
+       foreach $u (&address_parts($header{'to'}.",".$header{'cc'}.
+                                                ",".$header{'bcc'})) {
+               &smtp_command(MAIL, "rcpt to: <$u>$notify\r\n");
+               }
+       &smtp_command(MAIL, "data\r\n");
+       }
+elsif (defined(&send_mail_program)) {
+       # Use specified mail injector
+       local $cmd = &send_mail_program($from[0]);
+       $cmd || &error("No mail program was found on your system!");
+       open(MAIL, "| $cmd >/dev/null 2>&1");
+       }
+elsif ($config{'qmail_dir'}) {
+       # Start qmail-inject
+       open(MAIL, "| $config{'qmail_dir'}/bin/qmail-inject");
+       }
+elsif ($config{'postfix_control_command'}) {
+       # Start postfix's sendmail wrapper
+       local $cmd = -x "/usr/lib/sendmail" ? "/usr/lib/sendmail" :
+                       &has_command("sendmail");
+       $cmd || &error($text{'send_ewrapper'});
+       open(MAIL, "| $cmd -t -f$from[0] >/dev/null 2>&1");
+       }
+else {
+       # Start sendmail
+       &has_command($config{'sendmail_path'}) ||
+           &error(&text('send_epath', "<tt>$config{'sendmail_path'}</tt>"));
+       open(MAIL, "| $config{'sendmail_path'} -t -f$from[0] >/dev/null 2>&1");
+       }
+local $ctype = "multipart/mixed";
+local $msg_id;
+foreach $h (@{$_[0]->{'headers'}}) {
+       if (defined($_[0]->{'body'}) || $_[2]) {
+               print MAIL $h->[0],": ",$h->[1],$eol;
+               $lnum++;
+               }
+       else {
+               if ($h->[0] !~ /^(MIME-Version|Content-Type)$/i) {
+                       print MAIL $h->[0],": ",$h->[1],$eol;
+                       $lnum++;
+                       }
+               elsif (lc($h->[0]) eq 'content-type') {
+                       $ctype = $h->[1];
+                       }
+               }
+       if (lc($h->[0]) eq 'message-id') {
+               $msg_id++;
+               }
+       }
+if (!$msg_id) {
+       # Add a message-id header if missing
+       print MAIL "Message-Id: <",time().".".$$."\@".
+                                 &get_system_hostname(),">",$eol;
+       }
+
+# Work out first attachment content type
+local ($ftype, $fenc);
+if (@{$_[0]->{'attach'}} >= 1) {
+       local $first = $_[0]->{'attach'}->[0];
+       $ftype = "text/plain";
+       foreach my $h (@{$first->{'headers'}}) {
+               if (lc($h->[0]) eq "content-type") {
+                       $ftype = $h->[1];
+                       }
+               if (lc($h->[0]) eq "content-transfer-encoding") {
+                       $fenc = $h->[1];
+                       }
+               }
+       }
+
+if (defined($_[0]->{'body'})) {
+       # Use original mail body
+       print MAIL $eol;
+       $lnum++;
+       $_[0]->{'body'} =~ s/\r//g;
+       $_[0]->{'body'} =~ s/\n\.\n/\n\. \n/g;
+       $_[0]->{'body'} =~ s/\n/$eol/g;
+       $_[0]->{'body'} .= $eol if ($_[0]->{'body'} !~ /\n$/);
+       (print MAIL $_[0]->{'body'}) || &error("Write failed : $!");
+       $lnum += ($_[0]->{'body'} =~ tr/\n/\n/);
+       }
+elsif (!$_[2] || $ftype !~ /text\/plain/i ||
+       $fenc =~ /quoted-printable|base64/) {
+       # Sending MIME-encoded email
+       if ($ctype !~ /multipart\/report/i) {
+               $ctype =~ s/;.*$//;
+               }
+       print MAIL "MIME-Version: 1.0",$eol;
+       local $bound = "bound".time();
+       print MAIL "Content-Type: $ctype; boundary=\"$bound\"",$eol;
+       print MAIL $eol;
+       $lnum += 3;
+
+       # Send attachments
+       print MAIL "This is a multi-part message in MIME format.",$eol;
+       $lnum++;
+       foreach $a (@{$_[0]->{'attach'}}) {
+               print MAIL $eol;
+               print MAIL "--",$bound,$eol;
+               $lnum += 2;
+               local $enc;
+               foreach $h (@{$a->{'headers'}}) {
+                       print MAIL $h->[0],": ",$h->[1],$eol;
+                       $enc = $h->[1]
+                               if (lc($h->[0]) eq 'content-transfer-encoding');
+                       $lnum++;
+                       }
+               print MAIL $eol;
+               $lnum++;
+               if (lc($enc) eq 'base64') {
+                       local $enc = &encode_base64($a->{'data'});
+                       $enc =~ s/\r//g;
+                       $enc =~ s/\n/$eol/g;
+                       print MAIL $enc;
+                       $lnum += ($enc =~ tr/\n/\n/);
+                       }
+               else {
+                       $a->{'data'} =~ s/\r//g;
+                       $a->{'data'} =~ s/\n\.\n/\n\. \n/g;
+                       $a->{'data'} =~ s/\n/$eol/g;
+                       print MAIL $a->{'data'};
+                       $lnum += ($a->{'data'} =~ tr/\n/\n/);
+                       if ($a->{'data'} !~ /\n$/) {
+                               print MAIL $eol;
+                               $lnum++;
+                               }
+                       }
+               }
+       print MAIL $eol;
+       (print MAIL "--",$bound,"--",$eol) || &error("Write failed : $!");
+       print MAIL $eol;
+       $lnum += 3;
+       }
+else {
+       # Sending text-only mail from first attachment
+       local $a = $_[0]->{'attach'}->[0];
+       print MAIL $eol;
+       $lnum++;
+       $a->{'data'} =~ s/\r//g;
+       $a->{'data'} =~ s/\n/$eol/g;
+       (print MAIL $a->{'data'}) || &error("Write failed : $!");
+       $lnum += ($a->{'data'} =~ tr/\n/\n/);
+       if ($a->{'data'} !~ /\n$/) {
+               print MAIL $eol;
+               $lnum++;
+               }
+       }
+if ($sm && !$_[1]) {
+       &smtp_command(MAIL, ".$eol");
+       &smtp_command(MAIL, "quit$eol");
+       }
+if (!close(MAIL)) {
+       # Only bother to report an error on close if writing to a file
+       if ($_[1]) {
+               &error("Write failed : $!");
+               }
+       }
+return $lnum;
+}
+
+# mail_size(&mail, [textonly])
+# Returns the size of an email message in bytes
+sub mail_size
+{
+local ($mail, $textonly) = @_;
+local $temp = &transname();
+&send_mail($mail, $temp, $textonly);
+local @st = stat($temp);
+unlink($temp);
+return $st[7];
+}
+
+# b64decode(string)
+# Converts a string from base64 format to normal
+sub b64decode
+{
+    local($str) = $_[0];
+    local($res);
+    $str =~ tr|A-Za-z0-9+=/||cd;
+    $str =~ s/=+$//;
+    $str =~ tr|A-Za-z0-9+/| -_|;
+    while ($str =~ /(.{1,60})/gs) {
+        my $len = chr(32 + length($1)*3/4);
+        $res .= unpack("u", $len . $1 );
+    }
+    return $res;
+}
+
+# can_read_mail(user)
+sub can_read_mail
+{
+return 1 if ($_[0] && $access{'sent'} eq $_[0]);
+local @u = getpwnam($_[0]);
+return 0 if (!@u);
+return 0 if ($_[0] =~ /\.\./);
+return 0 if ($access{'mmode'} == 0);
+return 1 if ($access{'mmode'} == 1);
+local $u;
+if ($access{'mmode'} == 2) {
+       foreach $u (split(/\s+/, $access{'musers'})) {
+               return 1 if ($u eq $_[0]);
+               }
+       return 0;
+       }
+elsif ($access{'mmode'} == 4) {
+       return 1 if ($_[0] eq $remote_user);
+       }
+elsif ($access{'mmode'} == 5) {
+       return $u[3] eq $access{'musers'};
+       }
+elsif ($access{'mmode'} == 3) {
+       foreach $u (split(/\s+/, $access{'musers'})) {
+               return 0 if ($u eq $_[0]);
+               }
+       return 1;
+       }
+elsif ($access{'mmode'} == 6) {
+       return ($_[0] =~ /^$access{'musers'}$/);
+       }
+elsif ($access{'mmode'} == 7) {
+       return (!$access{'musers'} || $u[2] >= $access{'musers'}) &&
+              (!$access{'musers2'} || $u[2] <= $access{'musers2'});
+       }
+return 0;      # can't happen!
+}
+
+# from_hostname()
+sub from_hostname
+{
+local ($d, $masq);
+local $conf = &get_sendmailcf();
+foreach $d (&find_type("D", $conf)) {
+       if ($d->{'value'} =~ /^M\s*(\S*)/) { $masq = $1; }
+       }
+return $masq ? $masq : &get_system_hostname();
+}
+
+# mail_from_queue(qfile, [dfile|"auto"])
+# Reads a message from the Sendmail mail queue
+sub mail_from_queue
+{
+local $mail = { 'file' => $_[0] };
+$mail->{'quar'} = $_[0] =~ /\/hf/;
+$mail->{'lost'} = $_[0] =~ /\/Qf/;
+if ($_[1] eq "auto") {
+       $mail->{'dfile'} = $_[0];
+       $mail->{'dfile'} =~ s/\/(qf|hf|Qf)/\/df/;
+       }
+elsif ($_[1]) {
+       $mail->{'dfile'} = $_[1];
+       }
+$mail->{'lfile'} = $_[0];
+$mail->{'lfile'} =~ s/\/(qf|hf|Qf)/\/xf/;
+local $_;
+local @headers;
+open(QF, $_[0]) || return undef;
+while(<QF>) {
+       s/\r|\n//g;
+       if (/^M(.*)/) {
+               $mail->{'status'} = $1;
+               }
+       elsif (/^H\?[^\?]*\?(\S+):\s+(.*)/ || /^H(\S+):\s+(.*)/) {
+               push(@headers, [ $1, $2 ]);
+               $mail->{'rawheaders'} .= "$1: $2\n";
+               }
+       elsif (/^\s+(.*)/) {
+               $headers[$#headers]->[1] .= $1 unless($#headers < 0);
+               $mail->{'rawheaders'} .= $_."\n";
+               }
+       }
+close(QF);
+$mail->{'headers'} = \@headers;
+foreach $h (@headers) {
+       $mail->{'header'}->{lc($h->[0])} = $h->[1];
+       }
+
+if ($mail->{'dfile'}) {
+       # Read the mail body
+       open(DF, $mail->{'dfile'});
+       while(<DF>) {
+               $mail->{'body'} .= $_;
+               }
+       close(DF);
+       }
+local $datafile = $mail->{'dfile'};
+if (!$datafile) {
+       ($datafile = $mail->{'file'}) =~ s/\/(qf|hf|Qf)/\/df/;
+       }
+local @st0 = stat($mail->{'file'});
+local @st1 = stat($datafile);
+$mail->{'size'} = $st0[7] + $st1[7];
+return $mail;
+}
+
+# wrap_lines(text, width)
+# Given a multi-line string, return an array of lines wrapped to
+# the given width
+sub wrap_lines
+{
+local @rv;
+local $w = $_[1];
+foreach $rest (split(/\n/, $_[0])) {
+       if ($rest =~ /\S/) {
+               while($rest =~ /^(.{1,$w}\S*)\s*([\0-\377]*)$/) {
+                       push(@rv, $1);
+                       $rest = $2;
+                       }
+               }
+       else {
+               # Empty line .. keep as it is
+               push(@rv, $rest);
+               }
+       }
+return @rv;
+}
+
+# smtp_command(handle, command, no-error)
+sub smtp_command
+{
+local ($m, $c) = @_;
+print $m $c;
+local $r = <$m>;
+if ($r !~ /^[23]\d+/ && !$_[2]) {
+       &error(&text('send_esmtp', "<tt>".&html_escape($c)."</tt>",
+                                  "<tt>".&html_escape($r)."</tt>"));
+       }
+$r =~ s/\r|\n//g;
+if ($r =~ /^(\d+)\-/) {
+       # multi-line ESMTP response!
+       while(1) {
+               local $nr = <$m>;
+               $nr =~ s/\r|\n//g;
+               if ($nr =~ /^(\d+)\-(.*)/) {
+                       $r .= "\n".$2;
+                       }
+               elsif ($nr =~ /^(\d+)\s+(.*)/) {
+                       $r .= "\n".$2;
+                       last;
+                       }
+               }
+       }
+return $r;
+}
+
+# address_parts(string)
+# Returns the email addresses in a string
+sub address_parts
+{
+local @rv;
+local $rest = $_[0];
+while($rest =~ /([^<>\s,'"\@]+\@[A-z0-9\-\.\!]+)(.*)/) {
+       push(@rv, $1);
+       $rest = $2;
+       }
+return wantarray ? @rv : $rv[0];
+}
+
+# link_urls(text, separate)
+sub link_urls
+{
+local $r = $_[0];
+local $tar = $_[1] ? "target=link".int(rand()*100000) : "";
+$r =~ s/((http|ftp|https|mailto):[^><"'\s]+[^><"'\s\.\)])/<a href="$1" $tar>$1<\/a>/g;
+return $r;
+}
+
+# link_urls_and_escape(text, separate)
+# HTML escapes some text, as well as properly linking URLs in it
+sub link_urls_and_escape
+{
+local $l = $_[0];
+local $rv;
+local $tar = $_[1] ? " target=link".int(rand()*100000) : "";
+while($l =~ /^(.*?)((http|ftp|https|mailto):[^><"'\s]+[^><"'\s\.\)])(.*)/) {
+       local ($before, $url, $after) = ($1, $2, $4);
+       $rv .= &eucconv_and_escape($before)."<a href='$url' $tar>".
+              &html_escape($url)."</a>";
+       $l = $after;
+       }
+$rv .= &eucconv_and_escape($l);
+return $rv;
+}
+
+# uudecode(text)
+sub uudecode
+{
+local @lines = split(/\n/, $_[0]);
+local ($l, $data);
+for($l=0; $lines[$l] !~ /begin\s+([0-7]+)\s/i; $l++) { }
+while($lines[++$l]) {
+       $data .= unpack("u", $lines[$l]);
+       }
+return $data;
+}
+
+sub simplify_date
+{
+local $u = &parse_mail_date($_[0]);
+if ($u) {
+       local $fmt = $userconfig{'date_fmt'} || $config{'date_fmt'} || "dmy";
+       local $strf = $fmt eq "dmy" ? "%d/%m/%Y" :
+                     $fmt eq "mdy" ? "%m/%d/%Y" :
+                                     "%Y/%m/%d";
+       return strftime("$strf %H:%M", localtime($u));
+        }
+elsif ($_[0] =~ /^(\S+),\s+0*(\d+)\s+(\S+)\s+(\d+)\s+(\d+):(\d+)/) {
+       return "$2/$3/$4 $5:$6";
+       }
+elsif ($_[0] =~ /^0*(\d+)\s+(\S+)\s+(\d+)\s+(\d+):(\d+)/) {
+       return "$1/$2/$3 $4:$5";
+       }
+return $_[0];
+}
+
+# simplify_from(from)
+# Simplifies a From: address for display in the mail list. Only the first
+# address is returned.
+sub simplify_from
+{
+local $rv = &eucconv(&decode_mimewords($_[0]));
+local @sp = &split_addresses($rv);
+if (!@sp) {
+       return $text{'mail_nonefrom'};
+       }
+else {
+       local $first = &html_escape($sp[0]->[1] ? $sp[0]->[1] : $sp[0]->[2]);
+       if (length($first) > 80) {
+               return substr($first, 0, 80)." ..";
+               }
+       else {
+               return $first.(@sp > 1 ? " , ..." : "");
+               }
+       }
+}
+
+# simplify_subject(subject)
+sub simplify_subject
+{
+local $rv = &eucconv(&decode_mimewords($_[0]));
+$rv = substr($rv, 0, 80)." .." if (length($rv) > 80);
+return $rv =~ /\S/ ? &html_escape($rv) : "<br>";
+}
+
+# quoted_decode(text)
+sub quoted_decode
+{
+local $t = $_[0];
+$t =~ s/=\n//g;
+$t =~ s/=([a-zA-Z0-9]{2})/pack("c",hex($1))/ge;
+return $t;
+}
+
+# quoted_encode(text)
+sub quoted_encode
+{
+local $t = $_[0];
+$t =~ s/([=\177-\377])/sprintf("=%2.2X",ord($1))/ge;
+return $t;
+}
+
+sub decode_mimewords {
+    my $encstr = shift;
+    my %params = @_;
+    my @tokens;
+    $@ = '';           ### error-return
+
+    ### Collapse boundaries between adjacent encoded words:
+    $encstr =~ s{(\?\=)\r?\n[ \t](\=\?)}{$1$2}gs;
+    pos($encstr) = 0;
+    ### print STDOUT "ENC = [", $encstr, "]\n";
+
+    ### Decode:
+    my ($charset, $encoding, $enc, $dec);
+    while (1) {
+       last if (pos($encstr) >= length($encstr));
+       my $pos = pos($encstr);               ### save it
+
+       ### Case 1: are we looking at "=?..?..?="?
+       if ($encstr =~    m{\G             # from where we left off..
+                           =\?([^?]*)     # "=?" + charset +
+                            \?([bq])      #  "?" + encoding +
+                            \?([^?]+)     #  "?" + data maybe with spcs +
+                            \?=           #  "?="
+                           }xgi) {
+           ($charset, $encoding, $enc) = ($1, lc($2), $3);
+           $dec = (($encoding eq 'q') ? _decode_Q($enc) : _decode_B($enc));
+           push @tokens, [$dec, $charset];
+           next;
+       }
+
+       ### Case 2: are we looking at a bad "=?..." prefix? 
+       ### We need this to detect problems for case 3, which stops at "=?":
+       pos($encstr) = $pos;               # reset the pointer.
+       if ($encstr =~ m{\G=\?}xg) {
+           $@ .= qq|unterminated "=?..?..?=" in "$encstr" (pos $pos)\n|;
+           push @tokens, ['=?'];
+           next;
+       }
+
+       ### Case 3: are we looking at ordinary text?
+       pos($encstr) = $pos;               # reset the pointer.
+       if ($encstr =~ m{\G                # from where we left off...
+                        ([\x00-\xFF]*?    #   shortest possible string,
+                         \n*)             #   followed by 0 or more NLs,
+                        (?=(\Z|=\?))      # terminated by "=?" or EOS
+                       }xg) {
+           length($1) or die "MIME::Words: internal logic err: empty token\n";
+           push @tokens, [$1];
+           next;
+       }
+
+       ### Case 4: bug!
+       die "MIME::Words: unexpected case:\n($encstr) pos $pos\n\t".
+           "Please alert developer.\n";
+    }
+    return join('',map {$_->[0]} @tokens);
+}
+
+# _decode_Q STRING
+#     Private: used by _decode_header() to decode "Q" encoding, which is
+#     almost, but not exactly, quoted-printable.  :-P
+sub _decode_Q {
+    my $str = shift;
+    $str =~ s/_/\x20/g;                                # RFC-1522, Q rule 2
+    $str =~ s/=([\da-fA-F]{2})/pack("C", hex($1))/ge;  # RFC-1522, Q rule 1
+    $str;
+}
+
+# _decode_B STRING
+#     Private: used by _decode_header() to decode "B" encoding.
+sub _decode_B {
+    my $str = shift;
+    &decode_base64($str);
+}
+
+# user_mail_file(user|file, [other details])
+sub user_mail_file
+{
+if ($_[0] =~ /^\//) {
+       return $_[0];
+       }
+elsif ($config{'mail_dir'}) {
+       return &mail_file_style($_[0], $config{'mail_dir'},
+                               $config{'mail_style'});
+       }
+elsif (@_ > 1) {
+       return "$_[7]/$config{'mail_file'}";
+       }
+else {
+       local @u = getpwnam($_[0]);
+       return "$u[7]/$config{'mail_file'}";
+       }
+}
+
+# mail_file_style(user, basedir, style)
+sub mail_file_style
+{
+if ($_[2] == 0) {
+       return "$_[1]/$_[0]";
+       }
+elsif ($_[2] == 1) {
+       return $_[1]."/".substr($_[0], 0, 1)."/".$_[0];
+       }
+elsif ($_[2] == 2) {
+       return $_[1]."/".substr($_[0], 0, 1)."/".
+               substr($_[0], 0, 2)."/".$_[0];
+       }
+else {
+       return $_[1]."/".substr($_[0], 0, 1)."/".
+               substr($_[0], 1, 1)."/".$_[0];
+       }
+}
+
+# user_index_file(user|file)
+sub user_index_file
+{
+local $us = $_[0];
+$us =~ s/\//_/g;
+local $f;
+local $hn = &get_system_hostname();
+if ($_[0] =~ /^\/.*\/([^\/]+)$/) {
+       # A file .. the index file is in ~/.usermin/mailbox or
+       # /etc/webmin/mailboxes
+       if ($user_module_config_directory && $config{'shortindex'}) {
+               # Use short name for index file
+               $f = "$user_module_config_directory/$1.findex";
+               }
+       else {
+               $f = $user_module_config_directory ?
+                       "$user_module_config_directory/$us.findex" :
+                       "$module_config_directory/$us.findex";
+               }
+       }
+else {
+       # A username .. the index file is in /etc/webmin/mailboxes
+        $f = $user_module_config_directory ?
+               "$user_module_config_directory/$_[0].index" :
+               "$module_config_directory/$_[0].index";
+       }
+return -r $f && !-r "$f.$hn" ? $f : "$f.$hn";
+}
+
+# extract_mail(data)
+# Converts the text of a message into mail object.
+sub extract_mail
+{
+local $text = $_[0];
+$text =~ s/^\s+//;
+local ($amail, @aheaders, $i);
+local @alines = split(/\n/, $text);
+while($i < @alines && $alines[$i]) {
+       if ($alines[$i] =~ /^(\S+):\s*(.*)/) {
+               push(@aheaders, [ $1, $2 ]);
+               $amail->{'rawheaders'} .= $alines[$i]."\n";
+               }
+       elsif ($alines[$i] =~ /^\s+(.*)/) {
+               $aheaders[$#aheaders]->[1] .= $1 unless($#aheaders < 0);
+               $amail->{'rawheaders'} .= $alines[$i]."\n";
+               }
+       $i++;
+       }
+$amail->{'headers'} = \@aheaders;
+foreach $h (@aheaders) {
+       $amail->{'header'}->{lc($h->[0])} = $h->[1];
+       }
+splice(@alines, 0, $i);
+$amail->{'body'} = join("\n", @alines)."\n";
+return $amail;
+}
+
+# split_addresses(string)
+# Splits a comma-separated list of addresses into [ email, real-name, original ]
+# triplets
+sub split_addresses
+{
+local (@rv, $str = $_[0]);
+while(1) {
+       if ($str =~ /^[\s,]*(([^<>\(\)\s]+)\s+\(([^\(\)]+)\))(.*)$/) {
+               # An address like  foo@bar.com (Fooey Bar)
+               push(@rv, [ $2, $3, $1 ]);
+               $str = $4;
+               }
+       elsif ($str =~ /^[\s,]*("([^"]+)"\s*<([^\s<>,]+)>)(.*)$/ ||
+              $str =~ /^[\s,]*(([^<>]+)\s+<([^\s<>,]+)>)(.*)$/ ||
+              $str =~ /^[\s,]*(([^<>]+)<([^\s<>,]+)>)(.*)$/ ||
+              $str =~ /^[\s,]*(([^<>\[\]]+)\s+\[mailto:([^\s\[\]]+)\])(.*)$/||
+              $str =~ /^[\s,]*(()<([^<>,]+)>)(.*)/ ||
+              $str =~ /^[\s,]*(()([^\s<>,]+))(.*)/) {
+               # Addresses like  "Fooey Bar" <foo@bar.com>
+               #                 Fooey Bar <foo@bar.com>
+               #                 Fooey Bar [mailto:foo@bar.com]
+               #                 <foo@bar.com>
+               #                 <group name>
+               #                 foo@bar.com
+               push(@rv, [ $3, $2, $1 ]);
+               $str = $4;
+               }
+       else {
+               last;
+               }
+       }
+return @rv;
+}
+
+$match_ascii = '\x1b\([BHJ]([\t\x20-\x7e]*)';
+$match_jis = '\x1b\$[@B](([\x21-\x7e]{2})*)';
+
+sub eucconv {
+       local($_) = @_;
+       if ($current_lang eq 'ja_JP.euc') {
+               s/$match_jis/&j2e($1)/geo;
+               s/$match_ascii/$1/go;
+               }
+       $_;
+}
+
+sub j2e {
+       local($_) = @_;
+       tr/\x21-\x7e/\xa1-\xfe/;
+       $_;
+}
+
+# eucconv_and_escape(string)
+sub eucconv_and_escape {
+       return &html_escape(&eucconv($_[0]));
+}
+
+# list_maildir(file, [start], [end])
+# Returns a subset of mail from a maildir format directory
+sub list_maildir
+{
+local (@rv, $i, $f);
+local @files = &get_maildir_files($_[0]);
+
+local ($start, $end);
+if (!defined($_[1])) {
+       $start = 0;
+       $end = @files - 1;
+       }
+elsif ($_[2] < 0) {
+       $start = @files + $_[2] - 1;
+       $end = @files + $_[1] - 1;
+       $start = 0 if ($start < 0);
+       }
+else {
+       $start = $_[1];
+       $end = $_[2];
+       $end = @files-1 if ($end >= @files);
+       }
+foreach $f (@files) {
+       if ($i < $start || $i > $end) {
+               # Skip files outside requested index range
+               push(@rv, undef);
+               $i++;
+               next;
+               }
+       local $mail = &read_mail_file($f);
+       $mail->{'idx'} = $i++;
+       push(@rv, $mail);
+       }
+return @rv;
+}
+
+# select_maildir(file, &indexes, headersonly)
+# Returns a list of messages with the given indexes, from a maildir directory
+sub select_maildir
+{
+local @files = &get_maildir_files($_[0]);
+local @rv;
+foreach my $i (@{$_[1]}) {
+       local $mail = &read_mail_file($files[$i], $_[2]);
+       $mail->{'idx'} = $i;
+       push(@rv, $mail);
+       }
+return @rv;
+}
+
+# Get ordered list of message files (with in-memory and on-disk caching, as
+# this can be slow)
+# get_maildir_files(directory)
+sub get_maildir_files
+{
+# Work out last modified time
+local $newest;
+foreach my $d ("$_[0]/cur", "$_[0]/new") {
+       local @dst = stat($d);
+       $newest = $dst[9] if ($dst[9] > $newest);
+       }
+
+local @files;
+if (defined($main::list_maildir_cache{$_[0]}) &&
+    $main::list_maildir_cache_time{$_[0]} == $newest) {
+       # Use the in-memory cache cache
+       @files = @{$main::list_maildir_cache{$_[0]}};
+       }
+else {
+       # Check the on-disk cache file
+       local $cachefile = &get_maildir_cachefile($_[0]);
+       local @cst = $cachefile ? stat($cachefile) : ( );
+       if ($cst[9] >= $newest) {
+               # Can read the cache
+               open(CACHE, $cachefile);
+               while(<CACHE>) {
+                       chop;
+                       push(@files, $_[0]."/".$_);
+                       }
+               close(CACHE);
+               }
+       else {
+               # Really read
+               local @shorts;
+               foreach my $d ("cur", "new") {
+                       opendir(DIR, "$_[0]/$d");
+                       while(my $f = readdir(DIR)) {
+                               push(@shorts, "$d/$f")
+                                       if ($f ne "." && $f ne "..");
+                               }
+                       closedir(DIR);
+                       }
+               @shorts = sort { substr($a, 4) cmp substr($b, 4) } @shorts;
+               @files = map { "$_[0]/$_" } @shorts;
+
+               # Write out the on-disk cache
+               if ($cachefile) {
+                       &open_tempfile(CACHE, ">$cachefile", 1);
+                       my $err;
+                       foreach my $f (@shorts) {
+                               my $ok = (print CACHE $f,"\n");
+                               $err++ if (!$ok);
+                               }
+                       &close_tempfile(CACHE) if (!$err);
+                       local @st = stat($_[0]);
+                       if ($< == 0) {
+                               # Cache should have some ownership as directory
+                               &set_ownership_permissions($st[4], $st[5],
+                                                          undef, $cachefile);
+                               }
+                       }
+               }
+       $main::list_maildir_cache{$_[0]} = \@files;
+       $main::list_maildir_cache_time{$_[0]} = $st[7];
+       }
+return @files;
+}
+
+# search_maildir(file, field, what)
+# Search for messages in a maildir directory, and return the results
+sub search_maildir
+{
+return &advanced_search_maildir($_[0], [ [ $_[1], $_[2] ] ], 1);
+}
+
+# advanced_search_maildir(user|file, &fields, andmode, [&limit])
+# Search for messages in a maildir directory, and return the results
+sub advanced_search_maildir
+{
+local @rv;
+local ($min, $max);
+if ($_[3] && $_[3]->{'latest'}) {
+       $min = -1;
+       $max = -$_[3]->{'latest'};
+       }
+foreach $mail (&list_maildir($_[0], $min, $max)) {
+       push(@rv, $mail) if ($mail &&
+                            &mail_matches($_[1], $_[2], $mail));
+       }
+return @rv;
+}
+
+# delete_maildir(&mail, ...)
+# Delete messages from a maildir directory
+sub delete_maildir
+{
+local $m;
+
+# Find all maildirs being deleted from
+local %dirs;
+foreach $m (@_) {
+       if ($m->{'file'} =~ /^(.*)\/(cur|new)\/([^\/]+)$/) {
+               $dirs{$1}->{"$2/$3"} = 1;
+               }
+       }
+
+# Delete from caches
+foreach my $dir (keys %dirs) {
+       local $cachefile = &get_maildir_cachefile($dir);
+       next if (!$cachefile);
+       local @cst = stat($cachefile);
+       next if (!@cst);
+
+       # Work out last modified time, and don't update cache if too new
+       local $newest;
+       foreach my $d ("$dir/cur", "$dir/new") {
+               local @dst = stat($d);
+               $newest = $dst[9] if ($dst[9] > $newest);
+               }
+       next if ($newest > $cst[9]);
+
+       local $lref = &read_file_lines($cachefile);
+       for(my $i=0; $i<@$lref; $i++) {
+               if ($dirs{$dir}->{$lref->[$i]}) {
+                       # Found an entry to remove
+                       splice(@$lref, $i--, 1);
+                       }
+               }
+       &flush_file_lines($cachefile);
+       }
+
+# Actually delete the files
+foreach $m (@_) {
+       unlink($m->{'file'});
+       }
+
+}
+
+# modify_maildir(&oldmail, &newmail, textonly)
+# Replaces a message in a maildir directory
+sub modify_maildir
+{
+unlink($_[0]->{'file'});
+&send_mail($_[1], $_[0]->{'file'}, $_[2], 1);
+}
+
+# write_maildir(&mail, directory, textonly)
+# Adds some message in maildir format to a directory
+sub write_maildir
+{
+# Work out last modified time, and don't update cache if too new
+local $cachefile = &get_maildir_cachefile($_[1]);
+local $up2date = 0;
+if ($cachefile) {
+       local @cst = stat($cachefile);
+       if (@cst) {
+               local $newest;
+               foreach my $d ("$dir/cur", "$dir/new") {
+                       local @dst = stat($d);
+                       $newest = $dst[9] if ($dst[9] > $newest);
+                       }
+               $up2date = 1 if ($newest <= $cst[9]);
+               }
+       }
+
+# Select a unique filename and write to it
+local $now = time();
+local $hn = &get_system_hostname();
+local $mf;
+mkdir($_[1], 0755);
+mkdir("$_[1]/cur", 0755);
+do {
+       $mf = "$_[1]/cur/$now.$$.$hn";
+       $now++;
+       } while(-r $mf);
+&send_mail($_[0], $mf, $_[2], 1);
+
+if ($up2date && $cachefile) {
+       # Bring cache up to date
+       $now--;
+       local $lref = &read_file_lines($cachefile);
+       push(@$lref, "cur/$now.$$.$hn");
+       &flush_file_lines($cachefile);
+       }
+}
+
+# empty_maildir(file)
+# Delete all messages in an maildir directory
+sub empty_maildir
+{
+local $d;
+foreach $d ("$_[0]/cur", "$_[0]/new") {
+       local $f;
+       opendir(DIR, $d);
+       while($f = readdir(DIR)) {
+               unlink("$d/$f") if ($f ne '.' && $f ne '..');
+               }
+       closedir(DIR);
+       }
+local $cachefile = &get_maildir_cachefile($_[0]);
+unlink($cachefile) if ($cachefile);
+}
+
+# get_maildir_cachefile(dir)
+# Returns the cache file for a maildir directory
+sub get_maildir_cachefile
+{
+local ($dir) = @_;
+local $oldcache = -r "$dir/maildircache" ? "$dir/maildircache"
+                                        : "$dir/.usermin-maildircache";
+unlink($oldcache); 
+local $cd = $user_module_config_directory || $module_config_directory;
+local $sd = "$cd/maildircache";
+if (!-d $sd) {
+       &make_dir($sd, 0755) || return undef;
+       }
+$dir =~ s/\//_/g;
+return "$sd/$dir";
+}
+
+# count_maildir(dir)
+# Returns the number of messages in a maildir directory
+sub count_maildir
+{
+local $d;
+local $count = 0;
+foreach $d ("$_[0]/cur", "$_[0]/new") {
+       opendir(DIR, $d);
+       local @files = grep { $_ !~ /^\./ } readdir(DIR);
+       $count += scalar(@files);
+       closedir(DIR);
+       }
+return $count;
+}
+
+# list_mhdir(file, [start], [end])
+# Returns a subset of mail from an MH format directory
+sub list_mhdir
+{
+local ($start, $end, $f, $i, @rv);
+opendir(DIR, $_[0]);
+local @files = map { "$_[0]/$_" }
+               sort { $a <=> $b }
+                grep { /^\d+$/ } readdir(DIR);
+closedir(DIR);
+if (!defined($_[1])) {
+       $start = 0;
+       $end = @files - 1;
+       }
+elsif ($_[2] < 0) {
+       $start = @files + $_[2] - 1;
+       $end = @files + $_[1] - 1;
+       $start = 0 if ($start < 0);
+       }
+else {
+       $start = $_[1];
+       $end = $_[2];
+       $end = @files-1 if ($end >= @files);
+       }
+foreach $f (@files) {
+       if ($i < $start || $i > $end) {
+               # Skip files outside requested index range
+               push(@rv, undef);
+               $i++;
+               next;
+               }
+       local $mail = &read_mail_file($f);
+       $mail->{'idx'} = $i++;
+       push(@rv, $mail);
+       }
+return @rv;
+}
+
+# select_mhdir(file, &indexes, headersonly)
+# Returns a list of messages with the given indexes, from an mhdir directory
+sub select_mhdir
+{
+local @rv;
+opendir(DIR, $_[0]);
+local @files = map { "$_[0]/$_" }
+               sort { $a <=> $b }
+                grep { /^\d+$/ } readdir(DIR);
+closedir(DIR);
+foreach my $i (@{$_[1]}) {
+       local $mail = &read_mail_file($files[$i], $_[2]);
+       $mail->{'idx'} = $i;
+       push(@rv, $mail);
+       }
+return @rv;
+}
+
+# search_mhdir(file|user, field, what)
+# Search for messages in an MH directory, and return the results
+sub search_mhdir
+{
+return &advanced_search_mhdir($_[0], [ [ $_[1], $_[2] ] ], 1);
+}
+
+# advanced_search_mhdir(file|user, &fields, andmode, &limit)
+# Search for messages in an MH directory, and return the results
+sub advanced_search_mhdir
+{
+local @rv;
+local ($min, $max);
+if ($_[3] && $_[3]->{'latest'}) {
+       $min = -1;
+       $max = -$_[3]->{'latest'};
+       }
+foreach $mail (&list_mhdir($_[0], $min, $max)) {
+       push(@rv, $mail) if ($mail && &mail_matches($_[1], $_[2], $mail));
+       }
+return @rv;
+}
+
+# delete_mhdir(&mail, ...)
+# Delete messages from an MH directory
+sub delete_mhdir
+{
+local $m;
+foreach $m (@_) {
+       unlink($m->{'file'});
+       }
+}
+
+# modify_mhdir(&oldmail, &newmail, textonly)
+# Replaces a message in a maildir directory
+sub modify_mhdir
+{
+unlink($_[0]->{'file'});
+&send_mail($_[1], $_[0]->{'file'}, $_[2], 1);
+}
+
+# max_mhdir(dir)
+# Returns the maximum message ID in the directory
+sub max_mhdir
+{
+local $max = 1;
+opendir(DIR, $_[0]);
+foreach $f (readdir(DIR)) {
+       $max = $f if ($f =~ /^\d+$/ && $f > $max);
+       }
+closedir(DIR);
+return $max;
+}
+
+# empty_mhdir(file)
+# Delete all messages in an MH format directory
+sub empty_mhdir
+{
+local $f;
+opendir(DIR, $_[0]);
+foreach $f (readdir(DIR)) {
+       unlink("$_[0]/$f") if ($f =~ /^\d+$/);
+       }
+closedir(DIR);
+}
+
+# count_mhdir(file)
+# Returns the number of messages in an MH directory
+sub count_mhdir
+{
+opendir(DIR, $_[0]);
+local @files = grep { /^\d+$/ } readdir(DIR);
+closedir(DIR);
+return scalar(@files);
+}
+
+# read_mail_file(file, [headersonly])
+# Read a single message from a file
+sub read_mail_file
+{
+local (@headers, $mail);
+
+# Open and read the mail file
+open(MAIL, $_[0]) || return undef;
+$mail = &read_mail_fh(MAIL, 0, $_[1]);
+$mail->{'file'} = $_[0];
+close(MAIL);
+
+local @st = stat($_[0]);
+$mail->{'size'} = $st[7];
+return $mail;
+}
+
+# read_mail_fh(handle, [end-mode], [headersonly])
+# Reads an email message from the given file handle, either up to end of
+# the file, or a From line. End mode 0 = EOF, 1 = From without -,
+#                                   2 = From possibly with -
+sub read_mail_fh
+{
+local ($fh, $endmode, $headeronly) = @_;
+local (@headers, $mail);
+
+# Read the headers
+local $lnum = 0;
+while(1) {
+       $lnum++;
+       local $line = <$fh>;
+       $mail->{'size'} += length($line);
+       $line =~ s/\r|\n//g;
+       last if ($line eq '');
+       if ($line =~ /^(\S+):\s*(.*)/) {
+               push(@headers, [ $1, $2 ]);
+               $mail->{'rawheaders'} .= $line."\n";
+               }
+       elsif ($line =~ /^\s+(.*)/) {
+               $headers[$#headers]->[1] .= $1 unless($#headers < 0);
+               $mail->{'rawheaders'} .= $line."\n";
+               }
+       elsif ($line =~ /^From\s+(\S+).*\d+/ &&
+              ($1 ne '-' || $endmode == 2)) {
+               $mail->{'fromline'} = $line;
+               }
+       }
+$mail->{'headers'} = \@headers;
+foreach $h (@headers) {
+       $mail->{'header'}->{lc($h->[0])} = $h->[1];
+       }
+
+if (!$headersonly) {
+       # Read the mail body
+       if ($endmode == 0) {
+               # Till EOF
+               while(read($fh, $buf, 1024) > 0) {
+                       $mail->{'size'} += length($buf);
+                       $mail->{'body'} .= $buf;
+                       }
+               close(MAIL);
+               }
+       else {
+               # Tell next From line
+               while(1) {
+                       $line = <$fh>;
+                       last if (!$line || $line =~ /^From\s+(\S+).*\d+\r?\n/ &&
+                                ($1 ne '-' || $endmode == 2));
+                       $lnum++;
+                       $mail->{'size'} += length($line);
+                       $mail->{'body'} .= $line;
+                       }
+               $mail->{'lines'} = $lnum;
+               }
+       }
+elsif ($endmode) {
+       # Not reading the body, but we still need to search till the next
+       # From: line in order to get the size 
+       while(1) {
+               $line = <$fh>;
+               last if (!$line || $line =~ /^From\s+(\S+).*\d+\r?\n/ &&
+                        ($1 ne '-' || $endmode == 2));
+               $mail->{'size'} += length($line);
+               }
+       }
+return $mail;
+}
+
+# dash_mode(user|file)
+# Returns 1 if the messages in this folder are separated by lines like
+# From - instead of the usual From foo@bar.com
+sub dash_mode
+{
+open(DASH, &user_mail_file($_[0])) || return 0;        # assume no
+local $line = <DASH>;
+close(DASH);
+return $line =~ /^From\s+(\S+).*\d/ && $1 eq '-';
+}
+
+# mail_matches(&fields, andmode, &mail)
+# Returns 1 if some message matches a search
+sub mail_matches
+{
+local $count = 0;
+local $f;
+foreach $f (@{$_[0]}) {
+       local $field = $f->[0];
+       local $what = $f->[1];
+       local $neg = ($field =~ s/^\!//);
+       if ($field eq 'body') {
+               $count++
+                   if (!$neg && $_[2]->{'body'} =~ /\Q$what\E/i ||
+                        $neg && $_[2]->{'body'} !~ /\Q$what\E/i);
+               }
+       elsif ($field eq 'size') {
+               $count++
+                   if (!$neg && $_[2]->{'size'} > $what ||
+                        $neg && $_[2]->{'size'} < $what);
+               }
+       elsif ($field eq 'headers') {
+               local $headers = $_[2]->{'rawheaders'} ||
+                       join("", map { $_->[0].": ".$_->[1]."\n" }
+                                    @{$_[2]->{'headers'}});
+               $count++
+                   if (!$neg && $headers =~ /\Q$what\E/i ||
+                        $neg && $headers !~ /\Q$what\E/i);
+               }
+       elsif ($field eq "status") {
+               $count++
+                   if (!$neg && $_[2]->{$field} =~ /\Q$what\E/i||
+                        $neg && $_[2]->{$field} !~ /\Q$what\E/i);
+               }
+       else {
+               $count++
+                   if (!$neg && $_[2]->{'header'}->{$field} =~ /\Q$what\E/i||
+                        $neg && $_[2]->{'header'}->{$field} !~ /\Q$what\E/i);
+               }
+       return 1 if ($count && !$_[1]);
+       }
+return $count == @{$_[0]};
+}
+
+# search_fields(&fields)
+# Returns an array of headers/fields from a search
+sub search_fields
+{
+local @rv;
+foreach $f (@{$_[0]}) {
+       $f->[0] =~ /^\!?(.*)$/;
+       push(@rv, $1);
+       }
+return &unique(@rv);
+}
+
+# parse_delivery_status(text)
+# Returns the fields from a message/delivery-status attachment
+sub parse_delivery_status
+{
+local @lines = split(/[\r\n]+/, $_[0]);
+local (%rv, $l);
+foreach $l (@lines) {
+       if ($l =~ /^(\S+):\s*(.*)/) {
+               $rv{lc($1)} = $2;
+               }
+       }
+return \%rv;
+}
+
+# parse_mail_date(string)
+# Converts a mail Date: header into a unix time
+sub parse_mail_date
+{
+open(OLDSTDERR, ">&STDERR");   # suppress STDERR from Time::Local
+close(STDERR);
+my $rv = eval {
+       if ($_[0] =~ /^\s*(\S+),\s+(\d+)\s+(\S+)\s+(\d+)\s+(\d+):\s?(\d+):\s?(\d+)\s+(\S+)/) {
+               # Format like Mon, 13 Dec 2004 14:40:41 +0100
+               # or          Mon, 13 Dec 2004 14:18:16 GMT
+               # or          Tue, 14 Sep 04 02:45:09 GMT
+               local $tm = timegm($7, $6, $5, $2, &month_to_number($3),
+                                  $4 < 50 ? $4+100 : $4 < 1000 ? $4 : $4-1900);
+               local $tz = $8;
+               if ($tz =~ /^(\-|\+)?\d+$/) {
+                       local $tz = int($tz);
+                       $tz = $tz/100 if ($tz >= 50 || $tz <= -50);
+                       $tm -= $tz*60*60;
+                       }
+               return $tm;
+               }
+       elsif ($_[0] =~ /^\s*(\S+),\s+(\d+)\s+(\S+)\s+(\d+)\s+(\d+):\s?(\d+):\s?(\d+)/) {
+               # Format like Mon, 13 Dec 2004 14:40:41
+               # No timezone, so assume local
+               local $tm = timegm($7, $6, $5, $2, &month_to_number($3),
+                                  $4 < 50 ? $4+100 : $4 < 1000 ? $4 : $4-1900);
+               return $tm;
+               }
+       elsif ($_[0] =~ /^\s*(\S+)\s+(\S+)\s+(\d+)\s+(\d+):(\d+):(\d+)\s+(\d+)/) {
+               # Format like Tue Dec  7 12:58:52 2004
+               local $tm = timelocal($6, $5, $4, $3, &month_to_number($2),
+                                     $7 < 50 ? $7+100 : $7 < 1000 ? $7 : $7-1900);
+               return $tm;
+               }
+       elsif ($_[0] =~ /^(\d{4})\-(\d+)\-(\d+)\s+(\d+):(\d+)/) {
+               # Format like 2004-12-07 12:53
+               local $tm = timelocal(0, $4, $4, $3, $2-1,
+                                     $1 < 50 ? $1+100 : $1 < 1000 ? $1 : $1-1900);
+               }
+       elsif ($_[0] =~ /^(\d+)\s+(\S+)\s+(\d+)\s+(\d+):(\d+):(\d+)\s+(\S+)/) {
+               # Format like 30 Jun 2005 21:01:01 -0000
+               local $tm = timegm($6, $5, $4, $1, &month_to_number($2),
+                                  $3 < 50 ? $3+100 : $3 < 1000 ? $3 : $3-1900);
+               local $tz = $7;
+               if ($tz =~ /^(\-|\+)?\d+$/) {
+                       $tz = int($tz);
+                       $tz = $tz/100 if ($tz >= 50 || $tz <= -50);
+                       $tm -= $tz*60*60;
+                       }
+               return $tm;
+               }
+       else {
+               return undef;
+               }
+       };
+open(STDERR, ">&OLDSTDERR");
+close(OLDSTDERR);
+if ($@) {
+       return undef;
+       }
+return $rv;
+}
+
+# send_text_mail(from, to, cc, subject, body, [smtp-server])
+# A convenience function for sending a email with just a text body
+sub send_text_mail
+{
+local ($from, $to, $cc, $subject, $body, $smtp) = @_;
+local $cs = &get_charset();
+local $attach = $body =~ /[\177-\377]/ ?
+       { 'headers' => [ [ 'Content-Type', 'text/plain; charset='.$cs ],
+                        [ 'Content-Transfer-Encoding', 'quoted-printable' ] ],
+          'data' => &quoted_encode($body) } :
+       { 'headers' => [ [ 'Content-type', 'text/plain' ] ],
+         'data' => &entities_to_ascii($body) };
+local $mail = { 'headers' =>
+               [ [ 'From', $from ],
+                 [ 'To', $to ],
+                 [ 'Cc', $cc ],
+                 [ 'Subject', $subject ] ],
+               'attach' => [ $attach ] };
+return &send_mail($mail, undef, 1, 0, $smtp);
+}
+
+
+1;
diff --git a/mailboxes/config b/mailboxes/config
new file mode 100644 (file)
index 0000000..c9ba8aa
--- /dev/null
@@ -0,0 +1,50 @@
+max_records=200
+show_size=1
+show_size_below=0
+mail_system=3
+auto=1
+mail_dir=/var/mail
+mail_style=0
+mail_file=Mailbox
+mail_dir=Maildir
+wrap_width=80
+perpage=20
+track_read=0
+show_to=0
+sort_mode=1
+index_min=1000000
+fwd_mode=0
+delete_warn=y
+index_dbm=2
+top_buttons=2
+view_html=0
+mail_usermin=mail
+sync_create=0
+sync_modify=1
+sync_delete=1
+size_mode=1
+no_crlf=0
+sync_perms=0600
+show_mail=0
+html_edit=0
+check_mod=1
+spam_buttons=mail
+show_delall=0
+spam_del=0
+spam_report=
+mailbox_user=.usermin/mailbox
+vpopmail_dir=/home/vpopmail
+html_quote=0
+log_read=0
+from_virtualmin=1
+show_count=0
+show_sent=0
+sig_file=*
+show_body=0
+column_count=4
+ignore_users=
+ignore_users_enabled=0
+link_mode=0
+date_fmt=dmy
+arrows=1
+open_mode=0
diff --git a/mailboxes/config.info b/mailboxes/config.info
new file mode 100644 (file)
index 0000000..a31bec0
--- /dev/null
@@ -0,0 +1,76 @@
+line0=Configurable options,11
+wrap_width=Width to wrap mail messages at,0,6
+perpage=Mail messages to display per page,0,6
+track_read=Keep track of read/unread emails,1,1-Yes,0-No
+show_to=Show To: address in mailboxes?,1,1-Yes,0-No
+max_records=Maximum number of users to display,0,6
+top_buttons=Show buttons at top for,1,2-Mailboxes and mails,1-Mailboxes only,0-Never
+arrows=Show pager arrows at bottom for,1,2-Mailboxes and mails,1-Mailboxes only,0-Never
+show_delall=Show button to delete entire mailbox?,1,1-Yes,0-No
+show_size=User display mode,1,0-Username only,1-Username and size,2-Full details
+show_size_below=&nbsp; &nbsp; &nbsp; &nbsp;Username and size - Where to display size,1,0-To the right of username,1-Below the username
+column_count=Number of columns in which to display usernames,1,3-3,4-4,5-5,6-6,7-7,8-8,9-9
+ignore_users=Ignore these usernames (do not show),15,userIgnoreList
+ignore_users_enabled=&nbsp; &nbsp; &nbsp; &nbsp; Ignore list status,1,1-Enabled,0-Disabled
+show_count=Show number of messages in inbox?,1,1-Yes,0-No
+show_sent=Show number of messages in sent mail folder?,1,1-Yes,0-No
+sort_mode=Sort mailboxes by,1,2-Size,1-Username,0-Order in password file
+show_mail=Only show users who have mail?,1,1-Yes,0-No
+size_mode=Include all folders in size?,1,1-Yes,0-No (first folder only)
+fwd_mode=Forward messages with quoting?,1,0-Yes,1-No
+delete_warn=Ask for confirmation before deleting?,10,y-Yes,n-No,For mbox files larger than
+view_html=Show message body as,4,0-Always plain text,1-Text if possible&#44; HTML otherwise,2-HTML if possible&#44; text otherwise,3-Convert HTML to plain text
+html_edit=Use HTML editor for composing?,1,2-Always,1-When replying to HTML email,0-Never
+html_quote=HTML quoting mode,1,1-Message below &lt;hr&gt;,0-Message inside &lt;blockquote&gt;
+check_mod=Check for mailbox modification when deleting mail?,1,1-Yes,0-No
+log_read=Record the reading of mail in the Webmin Actions Log?,1,1-Yes,0-No
+bcc_to=Bcc: sent messages to,0
+sig_file=Signature file,10,*-None,.signature-~/.signature,Other file
+show_body=Show message body previews in list?,1,1-Yes,0-No
+open_mode=Open messages in,1,1-New window,0-List window
+link_mode=Open links in,1,1-New window,0-Same window
+download=Attachment MIME types to always download,9,20,4,\t
+date_fmt=Date format in mail list,1,dmy-DD/MM/YYYY,mdy-MM/DD/YYYY,ymd-YYYY/MM/DD
+date_tz=Timezone for date displays,3,System default
+
+line3=Spam options,11
+spam_buttons=Show spam reporting buttons for,2,list-Mailboxes,mail-Messages
+spam_del=Delete spam when reporting?,1,1-Yes,0-No
+spam_report=Report spam using,1,sa_learn-sa&#45;learn --spam,spamassassin-spamassasin -r,-Decide automatically
+
+line3.5=From address options,11
+from_addr=From: address to use when sending email manually,3,From mailbox username
+webmin_from=From: address to use when Webmin sends email,3,Default (webmin@<i>yourhost</i>)
+from_virtualmin=Get From: address from Virtualmin?,1,1-Yes,0-No
+from_dom=Domain to use in From: address,3,System hostname
+
+line1=System configuration,11
+mail_system=Mail server installed,4,1-Sendmail,0-Postfix,2-Qmail,4-Qmail+LDAP,5-Qmail+VPopMail,3-Detect automatically
+send_mode=Send mail using,10,-Mail server program,SMTP server
+no_crlf=Add carriage return ( \r ) to each line?,1,0-Yes,1-No
+smtp_user=SMTP login name for mail server,3,None
+smtp_pass=SMTP password for mail server,3,None
+smtp_auth=SMTP authentication method,4,-Default,Cram-MD5-Cram&#45;MD5,Digest-MD5-Digest&#45;MD5,Plain-Plain,Login-Login
+auto=Detect location of mail files automatically?,1,1-Yes&#44; based on mail server,0-No&#44; use settings below ..
+mail_dir=User mail file directory,3,None
+mail_style=Mail file directory style,4,0-mail/username,1-mail/u/username,2-mail/u/us/username,3-mail/u/s/username
+mail_file=Mail file in user home directories,3,None
+mail_sub=Mail directory in user home directories,3,None
+mail_usermin=Usermin-style folders subdirectory in home directory,3,None
+mailbox_user=Usermin Read Mail configuration directory in home directory,3,None
+
+line2=User synchronization,11
+sync_create=Create mailbox when user is created?,1,1-Yes,0-No
+sync_modify=Rename mailbox when user is renamed?,1,1-Yes,0-No
+sync_delete=Delete mailbox when user is deleted?,1,1-Yes,0-No
+sync_perms=Permissions for new mailboxes,0,4
+
+line4=VPOPMail options,11
+vpopmail_dir=Base directory for VPOPMail,0
+
+line5=Qmail+LDAP options,11
+ldap_host=LDAP server,0
+ldap_port=LDAP port,3,Default
+ldap_login=Login for LDAP server,0
+ldap_pass=Password for LDAP server,0
+ldap_base=Base for mail users,0
diff --git a/mailboxes/config.info.ca b/mailboxes/config.info.ca
new file mode 100644 (file)
index 0000000..f6d2e45
--- /dev/null
@@ -0,0 +1,76 @@
+line0=Opcions configurables,11
+wrap_width=Amplada dels missatges de correu,0,6
+perpage=Nombre de missatges a mostrar per pàgina,0,6
+track_read=Porta el compte dels correus llegits/no llegits,1,1-Sí,0-No
+show_to=Mostra les adreces To: a les bústies,1,1-Sí,0-No
+max_records=Nombre màxim d'usuaris a mostrar,0,6
+top_buttons=Mostra els botons a dalt per a,1,2-Bústies i correus,1-Només les bústies,0-Mai
+arrows=Mostra les fletxes de paginació al peu,1,2-Per a les bústies i els correus,1-Només per a les bústies,0-Mai
+show_delall=Mostra el botó per suprimir tota la bústia,1,1-Sí,0-No
+show_size=Mode de mostrar els usuaris,1,0-Només el nom d'usuari,1-Nom d'usuari i mida,2-Tots els detalls
+show_size_below=&nbsp; &nbsp; &nbsp; &nbsp;Usuari i mida - On es mostra la mida,1,0-A la dreta de l'usuari,1-A sota de l'usuari
+column_count=Nombre de columnes per mostrar-hi els noms d'usuari,1,3-3,4-4,5-5,6-6,7-7,8-8,9-9
+ignore_users=Ignora aquests noms d'usuari (no els mostris),15,userIgnoreList
+ignore_users_enabled=&nbsp; &nbsp; &nbsp; &nbsp; Ignora l'estat de la llista,1,1-Activat,0-Desactivat
+show_count=Mostra el nombre de missatges d'entrada,1,1-Sí,0-No
+show_sent=Mostra el nombre de missatges de la carpeta de coreru enviat,1,1-Sí,0-No
+sort_mode=Ordena les bústies per,1,2-Mida,1-Nom d'usuari,0-Ordre del fitxer de contrasenyes
+show_mail=Mostra només els usuaris que tenen correu,1,1-Sí,0-No
+size_mode=Inclou totes les carpetes a la mida,1,1-Sí,0-No (Només la primera carpeta)
+fwd_mode=Reenvia els missatges citats,1,0-Sí,1-No
+delete_warn=Demana confirmació abans d'esborrar,10,y-Sí,n-No,Per a bústies més grans de
+view_html=Mostra el cos del missatge,4,0-Sempre en text planer,1-En text si és possible&#44; altrament en HTML,2-HTML si és possible&#44; altrament en text,3-Converteix HTML a text planer
+html_edit=Utilitza l'editor HTML per a redactar,1,2-Sempre,1-En respondre correus HTML,0-Mai
+html_quote=Mode de citat HTML,1,1-Missatge sota &lt;hr&gt;,0-Missatge dins de &lt;blockquote&gt;
+check_mod=Comprova la modificació de la bústia en esborrar correu,1,1-Sí,0-No
+log_read=Registra la lectura del correu al Fitxer d'Accions de Webmin,1,1-Sí,0-No
+bcc_to=Fés còpia cega dels missatges enviats a,0
+sig_file=Fitxer de signatura,10,*-Cap,.signature-~/.signature,Un altre fitxer
+show_body=Mostra previsualitzacions del cos del missatge a la llista,1,1-Sí,0-No
+open_mode=Obre els missatges,1,1-En una finestra nova,0-En una finestra de la llista
+link_mode=Obre els enllaços a,1,1-Finestra nova,0-La mateixa finestra
+download=Tipus MIME d'adjuncions que sempres s'han de descarrregar,9,20,4,\t
+date_fmt=iFormat de la data a la llista de correu,1,dmy-DD/MM/AAAA,mdy-MM/DD/AAAA,ymd-AAAA/MM/DD
+date_tz=Zona horària de la data,3,Valor per defecte del sistema
+
+line3=Opcions de spam,11
+spam_buttons=Mostra els botons d'informe de spam de,2,list-Les bústies,mail-Els missatges
+spam_del=Suprimeix el spam en fer l'informe,1,1-Sí,0-No
+spam_report=Informa del spam utilitzant,1,sa_learn-sa&#45;learn --spam,spamassassin-spamassasin -r,-Decideix-ho automàticament
+
+line3.5=De les opcions de l'adreça,11
+from_addr=Adreça From: a utilitzar quan s'envia correu manualmen,3,Del nom d'usuari de la bústia
+webmin_from=Adreça From: a utilitzar quan el Webmin envia correu,3,per defecte (webmin@<i>elteuhost</i>)
+from_virtualmin=Obtingues l'adreça From: de Virtualmin,1,1-Sí,0-No
+from_dom=Domini a utilitzar a les adreces From:,3,Nom de host del sistema
+
+line1=Configuració del sistema,11
+mail_system=Servidor de correu instal·lat,1,1-Sendmail,0-Postfix,2-Qmail,3-Detecta'l automàticament,4-Qmail+LDAP,5-Qmail+VPopMail
+send_mode=Envia el correu emprant,10,-El programa servidor de correu,El servidor SMTP
+no_crlf=Afegeix un retorn de carro ( \r ) a cada línia,1,0-Sí,1-No
+smtp_user=Nom d'usuari SMTP al servidor de correu,3,Cap
+smtp_pass=Contrasenya SMTP al servidor de correu,3,Cap
+smtp_auth=Mètode d'autenticacio SMTP,4,-Defecte,Cram-MD5-Cram&#45;MD5,Digest-MD5-Digest&#45;MD5,Planer-Planer,Login-Login
+auto=Detecta la ubicació dels fitxers de correu automàticament,1,1-Sí&#44; basant-se en el servidor de correu,0-No&#44; utilitza la configuració de sota...
+mail_dir=Directori del fitxer de correu de l'usuari,3,Cap
+mail_style=Estil del directori del fitxer de correu,4,0-mail/usuari,1-mail/u/usuari,2-mail/u/us/usuari,3-mail/u/s/usuari
+mail_file=Fitxer de correu en els directoris arrel dels usuaris,3,Cap
+mail_sub=Directori de correu en els directoris arrel dels usuaris,3,Cap
+mail_usermin=SUbdirectori de carpetes estil Usermin al directori arrel,3,Cap
+mailbox_user=Directori de configuració de Correu Llegit Usermin al directori arrel,3,Cap
+
+line2=Sincronització d'usuaris,11
+sync_create=Crea la bústia quan es creï l'usuari,1,1-Sí,0-No
+sync_modify=Renomena la bústia quan es renomeni l'usuari,1,1-Sí,0-No
+sync_delete=Suprimeix la bústia quan se suprimeixi l'usuari,1,1-Sí,0-No
+sync_perms=Permisos de les bústies noves,0,4
+
+line4=Opcions VPOPMail,11
+vpopmail_dir=Directori base de VPOPMail,0
+
+line5=Opcions Qmail+LDAP,11
+ldap_host=Servidor LDAP,0
+ldap_port=Port LDAP,3,Defecte
+ldap_login=Usuari del servidor LDAP,0
+ldap_pass=Contrasenya del servidor LDAP,0
+ldap_base=Base d'usuaris de correu,0
diff --git a/mailboxes/config.info.de b/mailboxes/config.info.de
new file mode 100644 (file)
index 0000000..f6ca902
--- /dev/null
@@ -0,0 +1,54 @@
+line0=Konfigurierbare Optionen,11
+wrap_width=Zeilenumbruch bei Zeichen (70 empfohlen),0,6
+perpage=Anzahl von anzuzeigenden E&#45;Mails pro Seite,0,6
+track_read=E&#45;Mails als gelesen/ungelesen markieren?,1,1-Ja,0-Nein
+show_to=Zeige To:&#45;E&#45;Mail&#45;Adressen in E&#45;Mailboxen?,1,1-Ja,0-Nein
+max_records=Maximale Anzahl anzuzeigender Benutzer,0,6
+top_buttons=Zeige Buttons oben an f&#252;r,1,2-E&#45;Mailboxen und E&#45;Mails,1-Nur f&#252;r E&#45;Mailboxen,0-Niemals
+show_delall=Zeige Button an&#44; um die gesammte E&#45;Mailbox l&#246;schen zu k&#246;nnen?,1,1-Ja (empfohlen),0-Nein
+show_size=Benutzeranzeige&#45;Modus,1,0-Nur Benutzername,1-Benutzername und Gr&#246;&#223;e,2-Alle Einzelheiten
+sort_mode=Sortiere E&#45;Mailboxen nach,1,2-Gr&#246;&#223;e,1-Benutzername,0-Reihenfolge in Passwortdatei
+show_mail=Zeige nur die Benutzer an&#44; die E&#45;Mails haben?,1,1-Ja (empfohlen),0-Nein
+size_mode=Alle Ordner in Gr&#246;&#223;enanzeigen einrechnen?,1,1-Ja,0-Nein (nur der oberste Ordner)
+fwd_mode=Zitierte Weiterleitung von E&#45;Mails?,1,0-Ja (empfohlen),1-Nein
+delete_warn=Best&#228;tigungsabfrage vor dem L&#246;schen von E&#45;Mails?,10,y-Ja (empfohlen),n-Nein,F&#252;r mbox&#45;Dateien gr&#246;&#223;er als
+index_dbm=Indexierungsart,1,2-DBM,0-Textdatei
+index_min=Minimale E&#45;Maildateigr&#246;&#223;e zum Indexieren,3,Immer indexieren
+view_html=Zeige E&#45;Mails an als,4,0-Immer text/plain (empfohlen),1-Text wenn m&#246;glich (ansonsten HTML),2-HTML wenn m&#246;glich (ansonsten text/plain),3-Konvertiere HTML nach text/plain (geht nicht immer)
+html_edit=Benutze den HTML&#45;Editor f&#252;r das Schreiben von E&#45;Mails?,1,2-Immer (nicht empfohlen),1-Wenn auf HTML&#45;E&#45;Mails geantwortet wird (nicht empfohlen),0-Niemals (sehr empfohlen)
+html_quote=HTML-Zitierverhalten,1,1-Nachricht unterhalb &lt;hr&gt;,0-Nachricht innerhalb &lt;blockquote&gt;
+check_mod=Auf E&#45;Mailbox&#228;nderungen pr&#252;fen&#44; wenn E&#45;Mails gel&#246;scht werden?,1,1-Ja,0-Nein
+line3=Spam&#45;Optionen,11
+spam_buttons=Zeige Spam&#45;Report&#45;Buttons an f&#252;r,2,list-E&#45;Mailboxen,mail-E&#45;Mails
+spam_del=L&#246;sche Spam nach dem Report?,1,1-Ja,0-Nein
+spam_report=Spam&#45;Report&#45;Methode,1,sa_learn-sa&#45;learn &#45;&#45;spam,spamassassin-spamassassin &#45;r,-Entscheide Automatisch
+line1=Systemkonfiguration,11
+mail_system=Installierter E&#45;Mail&#45;Server,1,1-Sendmail,0-Postfix,2-Qmail
+send_mode=Versende E&#45;Mails unter Benutzung von,10,-Mailserver&#45;Binary,SMTP&#45;Server
+no_crlf=Einen Wagenr&#252;cklauf ( \r ) jeder Zeile hinzuf&#252;gen,1,0-Ja,1-Nein
+smtp_user=SMTP&#45;Login f&#252;r E&#45;Mailserver,3,kein
+smtp_pass=SMTP&#45;Passwort f&#252;r E&#45;Mailserver,3,Kein
+smtp_auth=SMTP&#45;Authentisierungsmethode,4,-Standard,Cram-MD5-Cram&#45;MD5,Digest-MD5-Digest&#45;MD5,Plain-Plain,Login-Login
+auto=Erkenne den Speicherort der E&#45;Maildateien automatisch?,1,1-Ja&#44; basierend auf dem Mailserver,0-Nein&#44;benutze folgende Einstellungen ..
+mail_dir=Benutzer&#45;E&#45;Mail&#45;Dateiverzeichnis,3,Kein
+mail_style=E&#45;Mail&#45;Dateiverzeichnisart,4,0-mail/benutzername,1-mail/u/benutzername,2-mail/u/us/benutzername,3-mail/u/s/benutzername
+mail_file=E&#45;Mail&#45;Datei in Benutzer&#45;Heimatverzeichnis,3,Kein
+mail_sub=E&#45;Mail&#45;Verzeichnis in Benutzer&#45;Heimatverzeichnis,3,Kein
+mail_usermin=Unterverzeichnis in Heimat&#45;Verzeichnis f&#252;r Usermin&#45;&#228;hnliche Ordner,3,Kein
+mailbox_user=Usermin &quot;Lese Benutzer&#45;E&#45;Mail&#45;Konfiguration&quot; im Heimatverzeichnis,3,Keines
+from_addr=From:&#45;E&#45;Mail&#45;Adresse&#44; die f&#252;r das Versenden manuell verschickter E-&#45;Mails benutzt werden soll,3,Aus dem E&#45;Mailbox&#45;Benutzernamen generieren
+webmin_from=Wenn Webmin E&#45;Mails versendet benutze diese &quot;From&quot;&#45;E&#45;Mail&#45;Adresse,3,Standard (webmin@<i>yourhost</i>)
+from_dom=Domaine&#44; die f&#252;r die From:&#45;E&#45;Mail&#45;Adresse genutzt werden soll,3,System&#45;Hostname
+line2=Benutzersynchronisation,11
+sync_create=E&#45;Mailbox beim Anlegen eines Benutzers ebenfalls einrichten?,1,1-Ja,0-Nein
+sync_modify=E&#45;Mailbox beim Umbenennen eines Benutzers ebenfalls umbenennen?,1,1-Ja,0-Nein
+sync_delete=E&#45;Mailbox beim L&#246;schen eines Benutzers ebenfalls l&#246;schen?,1,1-Ja,0-Nein
+sync_perms=Dateirechte f&#252;r neue E&#45;Mailboxen,0,4
+line4=VPOPMail&#45;Optionen,11
+vpopmail_dir=Base&#45;Verzeichnis f&#252;r VPOPMail,0
+line5=Qmail+LDAP&#45;Optionen,11
+ldap_host=LDAP&#45;Server,0
+ldap_port=LDAP&#45;Port,3,Standard
+ldap_login=Login f&#252;r LDAP&#45;Server,0
+ldap_pass=Passwort f&#252;r LDAP&#45;Server,0
+ldap_base=LDAP&#45;Base f&#252;r E&#45;Mail&#45;Benutzer,0
diff --git a/mailboxes/config_info.pl b/mailboxes/config_info.pl
new file mode 100644 (file)
index 0000000..a9d0312
--- /dev/null
@@ -0,0 +1,20 @@
+do '../web-lib-funcs.pl';
+
+sub show_userIgnoreList
+{
+  my($ig_usr) = shift(@_) || '';
+  my($preta)  = '<TEXTAREA NAME="ignore_users" COLS="35" ROWS="4">';
+  my($postta) = '</TEXTAREA>';
+
+  return
+      $preta .
+      $ig_usr .
+      $postta .
+      '&nbsp;' .
+      &user_chooser_button("ignore_users", 1);
+}
+
+sub parse_userIgnoreList
+{
+  return $in{'ignore_users'};
+}
diff --git a/mailboxes/defaultacl b/mailboxes/defaultacl
new file mode 100644 (file)
index 0000000..3bab5aa
--- /dev/null
@@ -0,0 +1,5 @@
+mmode=1
+fmode=0
+attach=-1
+canattach=1
+candetach=1
diff --git a/mailboxes/delete_all.cgi b/mailboxes/delete_all.cgi
new file mode 100755 (executable)
index 0000000..da7e724
--- /dev/null
@@ -0,0 +1,38 @@
+#!/usr/local/bin/perl
+# Delete all mail in some mailbox, after asking for confirmation
+
+require './mailboxes-lib.pl';
+&ReadParse();
+&can_user($in{'user'}) || &error($text{'mail_ecannot'});
+&is_user($in{'user'}) || -e $in{'user'} || &error($text{'mail_efile'});
+@folders = &list_user_folders_sorted($in{'user'});
+($folder) = grep { $_->{'index'} == $in{'folder'} } @folders;
+
+if ($in{'confirm'}) {
+       # Do it!
+       $sz = &mailbox_folder_size($folder);
+       &mailbox_empty_folder($folder);
+       &webmin_log("delmail", undef, undef, { 'from' => $folder->{'file'},
+                                              'count' => $sz });
+       &redirect("list_mail.cgi?user=$in{'user'}&folder=$in{'folder'}");
+       }
+else {
+       # Ask first
+       &ui_print_header(undef, $text{'delall_title'}, "");
+       print &ui_form_start("delete_all.cgi");
+       print &ui_hidden("user", $in{'user'}),"\n";
+       print &ui_hidden("folder", $in{'folder'}),"\n";
+       print "<center>\n";
+       print &text('delall_rusure',
+                   "<tt>$folder->{'file'}</tt>",
+                   &mailbox_folder_size($folder), 
+                   &nice_size(&folder_size($folder))),"<p>\n";
+       print &ui_submit($text{'delall_ok'}, "confirm"),"\n";
+       print &ui_form_end();
+       print "</center>\n";
+       &ui_print_footer("list_mail.cgi?user=$in{'user'}&folder=$in{'folder'}",
+                        $text{'mail_return'},
+                        "", $text{'index_return'});
+       }
+
+
diff --git a/mailboxes/delete_mail.cgi b/mailboxes/delete_mail.cgi
new file mode 100755 (executable)
index 0000000..221bc3a
--- /dev/null
@@ -0,0 +1,213 @@
+#!/usr/local/bin/perl
+# delete_mail.cgi
+# Delete, mark, move or copy multiple messages
+
+require './mailboxes-lib.pl';
+&ReadParse();
+&can_user($in{'user'}) || &error($text{'mail_ecannot'});
+@delete = sort { $a <=> $b } split(/\0/, $in{'d'});
+@folders = &list_user_folders($in{'user'});
+$folder = $folders[$in{'folder'}];
+
+if ($in{'mark1'} || $in{'mark2'}) {
+       # Marking emails with some status
+       @delete || &error($text{'delete_emnone'});
+       @mail = &mailbox_list_mails($delete[0], $delete[@delete-1], $folder);
+       dbmopen(%read, "$module_config_directory/$in{'user'}.read", 0600);
+       local $m = $in{'mark1'} ? $in{'mode1'} : $in{'mode2'};
+       foreach $d (@delete) {
+               local $hid = $mail[$d]->{'header'}->{'message-id'};
+               if ($m) {
+                       $read{$hid} = $m;
+                       }
+               else {
+                       delete($read{$hid});
+                       }
+               }
+       dbmclose(%read);
+       $perpage = $folder->{'perpage'} || $config{'perpage'};
+       &redirect("list_mail.cgi?start=$in{'start'}&folder=$in{'folder'}&user=$in{'user'}");
+       }
+elsif ($in{'move1'} || $in{'move2'}) {
+       # Moving mails to some other user's inbox
+       &check_modification($folder);
+       @delete || &error($text{'delete_emovenone'});
+       $muser = $in{'move1'} ? $in{'mfolder1'} : $in{'mfolder2'};
+       &can_user($muser) || &error($text{'delete_emovecannot'});
+       @mfolders = &list_user_folders($muser);
+       @mfolders || &error($text{'delete_emoveuser'});
+
+       @mail = &mailbox_list_mails($delete[0], $delete[@delete-1], $folder);
+       foreach $d (@delete) {
+               $mail[$d] || &error($text{'mail_eexists'});
+               push(@movemail, $mail[$d]);
+               }
+       &lock_folder($folder);
+       &lock_folder($mfolder);
+       &mailbox_move_mail($folder, $mfolders[0], @movemail);
+       &unlock_folder($mfolder);
+       &unlock_folder($folder);
+       &webmin_log("movemail", undef, undef, { 'from' => $folder->{'file'},
+                                               'to' => $mfolders[0]->{'file'},
+                                               'count' => scalar(@delete) } );
+       &redirect("list_mail.cgi?start=$in{'start'}&folder=$in{'folder'}&user=$in{'user'}");
+       }
+elsif ($in{'copy1'} || $in{'copy2'}) {
+       # Copying mails to some other folder
+       @delete || &error($text{'delete_ecopynone'});
+       $cuser = $in{'copy1'} ? $in{'mfolder1'} : $in{'mfolder2'};
+       &can_user($cuser) || &error($text{'delete_ecopycannot'});
+       @cfolders = &list_user_folders($cuser);
+       @cfolders || &error($text{'delete_ecopyuser'});
+
+       @mail = &mailbox_list_mails($delete[0], $delete[@delete-1], $folder);
+       foreach $d (@delete) {
+               $mail[$d] || &error($text{'mail_eexists'});
+               push(@copymail, $mail[$d]);
+               }
+       &lock_folder($cfolder);
+       &mailbox_copy_mail($folder, $cfolders[0], @copymail);
+       &unlock_folder($cfolder);
+       &webmin_log("copymail", undef, undef, { 'from' => $folder->{'file'},
+                                               'to' => $cfolders[0]->{'file'},
+                                               'count' => scalar(@delete) } );
+       &redirect("list_mail.cgi?start=$in{'start'}&folder=$in{'folder'}&user=$in{'user'}");
+       }
+elsif ($in{'forward'}) {
+       # Forwarding selected mails .. redirect
+       @delete || &error($text{'delete_efnone'});
+       &redirect("reply_mail.cgi?folder=$in{'folder'}&user=$in{'user'}&".
+                 join("&", map { "mailforward=$_" } @delete));
+       }
+elsif ($in{'new'}) {
+       # Need to redirect to compose form
+       &redirect("reply_mail.cgi?new=1&folder=$in{'folder'}&user=$in{'user'}");
+       }
+elsif ($in{'black'}) {
+       # Deny all senders
+       @delete || &error($text{'delete_ebnone'});
+       @mail = &mailbox_list_mails($delete[0], $delete[@delete-1], $folder);
+       foreach $d (@delete) {
+               push(@addrs, map { $_->[0] } &split_addresses($mail[$d]->{'header'}->{'from'}));
+               }
+       &foreign_require("spam", "spam-lib.pl");
+       local $conf = &spam::get_config();
+       local @from = map { @{$_->{'words'}} }
+                         &spam::find("blacklist_from", $conf);
+       local %already = map { $_, 1 } @from;
+       @newaddrs = grep { !$already{$_} } &unique(@addrs);
+       push(@from, @newaddrs);
+       &spam::save_directives($conf, 'blacklist_from',
+                              \@from, 1);
+       &flush_file_lines();
+       &redirect("list_mail.cgi?start=$in{'start'}&folder=$in{'folder'}&user=$in{'user'}");
+       }
+elsif ($in{'razor'}) {
+       # Report all messages, and show output to the user
+       @delete || &error($text{'delete_ebnone'});
+
+       &ui_print_header(undef, $text{'razor_title'}, "");
+       print "<b>$text{'razor_report2'}</b>\n";
+       print "<pre>";
+
+       # Write all messages to a temp file
+       @mail = &mailbox_list_mails($delete[0], $delete[@delete-1], $folder);
+       $temp = &transname();
+       $cmd = &spam_report_cmd($in{'user'});
+       foreach $d (@delete) {
+               $mail[$d] || &error($text{'mail_eexists'});
+               &send_mail($mail[$d], $temp);
+               push(@delmail, $mail[$d]);
+               }
+
+       # Call reporting command on them
+       &open_execute_command(OUT, "$cmd <$temp 2>&1", 1);
+       local $error;
+       while(<OUT>) {
+               print &html_escape($_);
+               $error++ if (/failed/i);
+               }
+       close(OUT);
+       unlink($temp);
+       print "</pre>\n";
+       if ($? || $error) {
+               print "<b>$text{'razor_err'}</b><p>\n";
+               }
+       else {
+               if ($config{'spam_del'}) {
+                       # Delete spam too
+                       &lock_folder($folder);
+                       &mailbox_delete_mail($folder, @delmail);
+                       &unlock_folder($folder);
+                       print "<b>$text{'razor_deleted'}</b><p>\n";
+                       }
+               else {
+                       print "<b>$text{'razor_done'}</b><p>\n";
+                       }
+               }
+       &ui_print_footer("list_mail.cgi?folder=$in{'folder'}&user=$in{'user'}", $text{'mail_return'}, "", $text{'index_return'});
+       }
+elsif ($in{'delete'} || $in{'deleteall'}) {
+       # Just deleting emails
+       &check_modification($folder);
+       @delete || $in{'deleteall'} || &error($text{'delete_enone'});
+       if (!$in{'confirm'} && &need_delete_warn($folder)) {
+               # Need to ask for confirmation before deleting
+               &ui_print_header(undef, $text{'confirm_title'}, "");
+               print &check_clicks_function();
+
+               print "<form action=delete_mail.cgi method=post>\n";
+               foreach $i (keys %in) {
+                       foreach $v (split(/\0/, $in{$i})) {
+                               print "<input type=hidden name=$i value='",
+                                     &html_escape($v),"'>\n";
+                               }
+                       }
+               print "<center><b>\n";
+               if ($in{'deleteall'}) {
+                       print &text('confirm_warnall'),"<br>\n";
+                       }
+               else {
+                       print &text('confirm_warn', scalar(@delete)),"<br>\n";
+                       }
+               if ($config{'delete_warn'} ne 'y') {
+                       print "$text{'confirm_warn2'}<p>\n"
+                       }
+               else {
+                       print "$text{'confirm_warn4'}<p>\n"
+                       }
+               print "</b><p><input type=submit name=confirm ",
+                     "value='$text{'confirm_ok'}' ",
+                     "onClick='return check_clicks(form)'></center></form>\n";
+               
+               &ui_print_footer("list_mail.cgi?start=$in{'start'}&folder=$in{'folder'}&user=$in{'user'}", $text{'mail_return'});
+               }
+       else {
+               # Go ahead and delete
+               &lock_folder($folder);
+               if ($in{'deleteall'}) {
+                       # Clear the whole folder
+                       $delcount = &mailbox_folder_size($folder);
+                       &mailbox_empty_folder($folder);
+                       }
+               else {
+                       # Just delete selected messages
+                       @mail = &mailbox_list_mails($delete[0],
+                                                   $delete[@delete-1],
+                                                   $folder);
+                       foreach $d (@delete) {
+                               $mail[$d] || &error($text{'mail_eexists'});
+                               push(@delmail, $mail[$d]);
+                               }
+                       &mailbox_delete_mail($folder, @delmail);
+                       $delcount = scalar(@delmail);
+                       }
+               &unlock_folder($folder);
+               &webmin_log("delmail", undef, undef,
+                           { 'from' => $folder->{'file'},
+                             'all' => $in{'deleteall'},
+                             'count' => $delcount } );
+               &redirect("list_mail.cgi?start=$in{'start'}&folder=$in{'folder'}&user=$in{'user'}");
+               }
+       }
+&pop3_logout_all();
diff --git a/mailboxes/detach.cgi b/mailboxes/detach.cgi
new file mode 100755 (executable)
index 0000000..41b444b
--- /dev/null
@@ -0,0 +1,123 @@
+#!/usr/local/bin/perl
+# detach.cgi
+# View one attachment from a message
+
+use Socket;
+require './mailboxes-lib.pl';
+&ReadParse();
+&can_user($in{'user'}) || &error($text{'mail_ecannot'});
+
+@folders = &list_user_folders($in{'user'});
+$folder = $folders[$in{'folder'}];
+@mail = &mailbox_list_mails($in{'idx'}, $in{'idx'}, $folder);
+$mail = $mail[$in{'idx'}];
+&parse_mail($mail);
+@sub = split(/\0/, $in{'sub'});
+foreach $s (@sub) {
+        # We are looking at a mail within a mail ..
+        local $amail = &extract_mail($mail->{'attach'}->[$s]->{'data'});
+        &parse_mail($amail);
+        $mail = $amail;
+        }
+$attach = $mail->{'attach'}->[$in{'attach'}];
+
+if ($in{'scale'}) {
+       # Scale the gif or jpeg image to 48 pixels high
+       local $temp = &transname();
+       open(TEMP, ">$temp");
+       print TEMP $attach->{'data'};
+       close(TEMP);
+       $SIG{'CHLD'} = sub { wait; };
+       if ($attach->{'type'} eq 'image/gif') {
+               ($pnmin, $pnmout) = &pipeopen("giftopnm $temp");
+               }
+       elsif ($attach->{'type'} eq 'image/jpeg') {
+               ($pnmin, $pnmout) = &pipeopen("djpeg -fast $temp");
+               }
+       else {
+               &dump_erroricon();
+               }
+       close($pnmin);
+       $type = <$pnmout>;
+       $size = <$pnmout>;
+       unlink($temp);
+       $type =~ /^P[0-9]/ || &dump_erroricon();
+       $size =~ /(\d+)\s+(\d+)/ || &dump_erroricon();
+       ($w, $h) = ($1, $2);
+       if ($w > 48) {
+               $scale = 48.0 / $w;
+               }
+       else {
+               $scale = 48.0 / $h;
+               }
+       ($jpegin, $jpegout) = &pipeopen("pnmscale $scale 2>/dev/null | cjpeg");
+       print $jpegin $type;
+       print $jpegin $size;
+       while(read($pnmout, $buf, 1024)) {
+               print $jpegin $buf;
+               }
+       close($jpegin);
+       close($pnmout);
+       print "Content-type: image/jpeg\n\n";
+       while(read($jpegout, $buf, 1024)) {
+               print $buf;
+               }
+       close($jpegout);
+       }
+else {
+       # Just output the attachment
+       print "X-no-links: 1\n";
+       @download = split(/\t+/, $config{'download'});
+       if (&indexof($attach->{'type'}, @download) >= 0) {
+               # Force download in IE
+               print "Content-Disposition: Attachment\n";
+               }
+       if ($attach->{'type'} eq 'message/delivery-status') {
+               print "Content-type: text/plain\n\n";
+               }
+       else {
+               print "Content-type: $attach->{'type'}\n\n";
+               }
+       if ($attach->{'type'} =~ /^text\/html/i) {
+               print &safe_urls(&filter_javascript($attach->{'data'}));
+               }
+       else {
+               print $attach->{'data'};
+               }
+       }
+&pop3_logout_all();
+
+sub dump_erroricon
+{
+print "Content-type: image/gif\n\n";
+open(ICON, "images/error.gif");
+while(<ICON>) { print; }
+close(ICON);
+exit;
+}
+
+# pipeopen(command)
+sub pipeopen
+{
+$pipe++;
+local $inr = "INr$pipe";
+local $inw = "INw$pipe";
+local $outr = "OUTr$pipe";
+local $outw = "OUTw$pipe";
+pipe($inr, $inw);
+pipe($outr, $outw);
+if (!fork()) {
+       untie(*STDIN);
+       untie(*STDOUT);
+       open(STDIN, "<&$inr");
+       open(STDOUT, ">&$outw");
+       close($inw);
+       close($outr);
+       exec($_[0]);
+       print STDERR "exec failed : $!\n";
+       exit 1;
+       }
+close($inr);
+close($outw);
+return ($inw, $outr);
+}
diff --git a/mailboxes/find.cgi b/mailboxes/find.cgi
new file mode 100755 (executable)
index 0000000..a86a5d3
--- /dev/null
@@ -0,0 +1,34 @@
+#!/usr/local/bin/perl
+# find.cgi
+# Display users matching some criteria
+
+require './mailboxes-lib.pl';
+&ReadParse();
+
+# Build a list of all matching users
+foreach $uinfo (&list_mail_users()) {
+       if (&can_user(@$uinfo)) {
+               if ($in{'match'} == 0 && lc($in{'user'}) eq $uinfo->[0] ||
+                   $in{'match'} == 1 && $uinfo->[0] =~ /\Q$in{'user'}\E/i) {
+                       push(@users, $uinfo);
+                       }
+               }
+       }
+
+if (@users == 1) {
+       # Can go direct to user
+       &redirect("list_mail.cgi?user=$users[0]->[0]");
+       }
+elsif (@users == 0) {
+       # No matches
+       &error($text{'find_enone'});
+       }
+else {
+       # Show table of matches
+       &ui_print_header(undef, $text{'find_title'}, "");
+       print &text('find_results', $in{'user'}),"<p>\n";
+       &show_users_table(\@users);
+       &ui_print_footer();
+       }
+
+
diff --git a/mailboxes/folders-lib.pl b/mailboxes/folders-lib.pl
new file mode 100644 (file)
index 0000000..1b66b7a
--- /dev/null
@@ -0,0 +1,2450 @@
+# folders-lib.pl
+# Functions for dealing with mail folders in various formats
+
+$pop3_port = 110;
+$imap_port = 143;
+$cache_directory = $user_module_config_directory || $module_config_directory;
+
+# mailbox_list_mails(start, end, &folder, [headersonly], [&error])
+# Returns an array whose size is that of the entire folder, with messages
+# in the specified range filled in.
+sub mailbox_list_mails
+{
+if ($_[2]->{'type'} == 0) {
+       # List a single mbox formatted file
+       return &list_mails($_[2]->{'file'}, $_[0], $_[1]);
+       }
+elsif ($_[2]->{'type'} == 1) {
+       # List a qmail maildir
+       local $md = $_[2]->{'file'};
+       return &list_maildir($md, $_[0], $_[1]);
+       }
+elsif ($_[2]->{'type'} == 2) {
+       # Get mail headers/body from a remote POP3 server
+
+       # Login first
+       local @rv = &pop3_login($_[2]);
+       if ($rv[0] != 1) {
+               # Failed to connect or login
+               if ($_[4]) {
+                       @{$_[4]} = @rv;
+                       return ();
+                       }
+               elsif ($rv[0] == 0) { &error($rv[1]); }
+               else { &error(&text('save_elogin', $rv[1])); }
+               }
+       local $h = $rv[1];
+       local @uidl = &pop3_uidl($h);
+       local %onserver = map { &safe_uidl($_), 1 } @uidl;
+
+       # Work out what range we want
+       local ($start, $end) = &compute_start_end($_[0], $_[1], scalar(@uidl));
+       local @rv = map { undef } @uidl;
+
+       # For each message in the range, get the headers or body
+       local ($i, $f, %cached, %sizeneed);
+       local $cd = "$cache_directory/$_[2]->{'id'}.cache";
+       if (opendir(CACHE, $cd)) {
+               while($f = readdir(CACHE)) {
+                       if ($f =~ /^(\S+)\.body$/) {
+                               $cached{$1} = 2;
+                               }
+                       elsif ($f =~ /^(\S+)\.headers$/) {
+                               $cached{$1} = 1;
+                               }
+                       }
+               closedir(CACHE);
+               }
+       else {
+               mkdir($cd, 0700);
+               }
+       for($i=$start; $i<=$end; $i++) {
+               local $u = &safe_uidl($uidl[$i]);
+               if ($cached{$u} == 2 || $cached{$u} == 1 && $_[3]) {
+                       # We already have everything that we need
+                       }
+               elsif ($cached{$u} == 1 || !$_[3]) {
+                       # We need to get the entire mail
+                       &pop3_command($h, "retr ".($i+1));
+                       open(CACHE, ">$cd/$u.body");
+                       while(<$h>) {
+                               s/\r//g;
+                               last if ($_ eq ".\n");
+                               print CACHE $_;
+                               }
+                       close(CACHE);
+                       unlink("$cd/$u.headers");
+                       $cached{$u} = 2;
+                       }
+               else {
+                       # We just need the headers
+                       &pop3_command($h, "top ".($i+1)." 0");
+                       open(CACHE, ">$cd/$u.headers");
+                       while(<$h>) {
+                               s/\r//g;
+                               last if ($_ eq ".\n");
+                               print CACHE $_;
+                               }
+                       close(CACHE);
+                       $cached{$u} = 1;
+                       }
+               local $mail = &read_mail_file($cached{$u} == 2 ?
+                               "$cd/$u.body" : "$cd/$u.headers");
+               if ($cached{$u} == 1) {
+                       if ($mail->{'body'} ne "") {
+                               $mail->{'size'} = int($mail->{'body'});
+                               }
+                       else {
+                               $sizeneed{$i} = 1;
+                               }
+                       }
+               $mail->{'uidl'} = $uidl[$i];
+               $mail->{'idx'} = $i;
+               $rv[$i] = $mail;
+               }
+
+       # Get sizes for mails if needed
+       if (%sizeneed) {
+               &pop3_command($h, "list");
+               while(<$h>) {
+                       s/\r//g;
+                       last if ($_ eq ".\n");
+                       if (/^(\d+)\s+(\d+)/ && $sizeneed{$1-1}) {
+                               # Add size to the mail cache
+                               $rv[$1-1]->{'size'} = $2;
+                               local $u = &safe_uidl($uidl[$1-1]);
+                               open(CACHE, ">>$cd/$u.headers");
+                               print CACHE $2,"\n";
+                               close(CACHE);
+                               }
+                       }
+               }
+
+       # Clean up any cached mails that no longer exist on the server
+       foreach $f (keys %cached) {
+               if (!$onserver{$f}) {
+                       unlink($cached{$f} == 1 ? "$cd/$f.headers"
+                                               : "$cd/$f.body");
+                       }
+               }
+
+       return @rv;
+       }
+elsif ($_[2]->{'type'} == 3) {
+       # List an MH directory
+       local $md = $_[2]->{'file'};
+       return &list_mhdir($md, $_[0], $_[1]);
+       }
+elsif ($_[2]->{'type'} == 4) {
+       # Get headers and possibly bodies from an IMAP server
+
+       # Login and select the specified mailbox
+       local @rv = &imap_login($_[2]);
+       if ($rv[0] != 1) {
+               # Something went wrong
+               if ($_[4]) {
+                       @{$_[4]} = @rv;
+                       return ();
+                       }
+               elsif ($rv[0] == 0) { &error($rv[1]); }
+               elsif ($rv[0] == 3) { &error(&text('save_emailbox', $rv[1])); }
+               elsif ($rv[0] == 2) { &error(&text('save_elogin2', $rv[1])); }
+               }
+       local $h = $rv[1];
+       local $count = $rv[2];
+       return () if (!$count);
+
+       # Work out what range we want
+       local ($start, $end) = &compute_start_end($_[0], $_[1], $count);
+       local @mail = map { undef } (0 .. $count-1);
+
+       # Get the headers or body of messages in the specified range
+       local @rv;
+       if ($_[3]) {
+               # Just the headers
+               @rv = &imap_command($h,
+                       sprintf "FETCH %d:%d (RFC822.SIZE RFC822.HEADER)",
+                               $start+1, $end+1);
+               }
+       else {
+               # Whole messages
+               @rv = &imap_command($h,
+                       sprintf "FETCH %d:%d RFC822", $start+1, $end+1);
+               }
+
+       # Parse the headers or whole messages that came back
+       local $i;
+       for($i=0; $i<@{$rv[1]}; $i++) {
+               # Extract the actual mail part
+               local $mail = &parse_imap_mail($rv[1]->[$i]);
+               if ($mail) {
+                       $mail->{'idx'} = $start+$i;
+                       $mail[$start+$i] = $mail;
+                       }
+               }
+
+       return @mail;
+       }
+elsif ($_[2]->{'type'} == 5) {
+       # Just all of the constituent folders
+       local @mail;
+
+       # Work out exactly how big the total is
+       local ($sf, %len, $count);
+       foreach $sf (@{$_[2]->{'subfolders'}}) {
+               $len{$sf} = &mailbox_folder_size($sf);
+               $count += $len{$sf};
+               }
+
+       # Work out what range we need
+       local ($start, $end) = &compute_start_end($_[0], $_[1], $count);
+
+       # Fetch the needed part of each sub-folder
+       local $pos = 0;
+       foreach $sf (@{$_[2]->{'subfolders'}}) {
+               local ($sfstart, $sfend);
+               $sfstart = $start - $pos;
+               $sfend = $end - $pos;
+               $sfstart = $sfstart < 0 ? 0 :
+                          $sfstart >= $len{$sf} ? $len{$sf}-1 : $sfstart;
+               $sfend = $sfend < 0 ? 0 :
+                        $sfend >= $len{$sf} ? $len{$sf}-1 : $sfend;
+               local @submail =
+                       &mailbox_list_mails($sfstart, $sfend, $sf, $_[3]);
+               local $sm;
+               foreach $sm (@submail) {
+                       if ($sm) {
+                               &push_index($sm, $sf, $sm->{'idx'}+$pos);
+                               }
+                       }
+               push(@mail, @submail);
+               $pos += $len{$sf};
+               }
+
+       return @mail;
+       }
+elsif ($_[2]->{'type'} == 6) {
+       # A virtual folder, which just contains indexes into other folders
+       local $mems = $folder->{'members'};
+       local ($start, $end) = &compute_start_end($_[0], $_[1], scalar(@$mems));
+
+       # Work out which folders we need, and how much
+       local (%frange, %namemap);
+       local $i;
+       for($i=$start; $i<=$end; $i++) {
+               local $sf = $mems->[$i]->[0];
+               local $sfn = &folder_name($sf);
+               local $idx = $mems->[$i]->[1];
+               if ($frange{$sfn}) {
+                       $frange{$sfn}->[0] = $idx if ($idx < $frange{$sfn}->[0]);
+                       $frange{$sfn}->[1] = $idx if ($idx > $frange{$sfn}->[1]);
+                       }
+               else {
+                       $frange{$sfn} = [ $idx, $idx ];
+                       }
+               $namemap{$sfn} = $sf;
+               }
+
+       # Get all the needed folders
+       local %sfs;
+       local $sfn;
+       foreach $sfn (keys %frange) {
+               local $sf = $namemap{$sfn};
+               local @submail = &mailbox_list_mails($frange{$sfn}->[0],
+                                                    $frange{$sfn}->[1],
+                                                    $sf, $_[3]);
+               $sfs{$sfn} = \@submail;
+               }
+
+       # Construct the results
+       local @mail = map { undef } (0 .. @$mems-1);
+       local $need_save = 0;
+       local @newmems = @$mems;
+       for($i=$start; $i<=$end && $i<=@$mems; $i++) {
+               local $sf = $mems->[$i]->[0];
+               local $sfn = &folder_name($sf);
+               local $idx = $mems->[$i]->[1];
+               $mail[$i] = $sfs{$sfn}->[$idx];
+               if ($mems->[$i]->[2] &&
+                   $mail[$i]->{'header'}->{'message-id'} ne $mems->[$i]->[2]) {
+                       # Argh .. index is wrong! Find message by ID
+                       local $real = &find_by_message_id($sf,$mems->[$i]->[2]);
+                       if ($real) {
+                               $mems->[$i]->[1] = $real->{'idx'};
+                               $mail[$i] = $real;
+                               $need_save++;
+                               }
+                       else {
+                               # Doesn't exist! Lose from index..
+                               @newmems = grep { $_ ne $mems->[$i] } @newmems;
+                               $need_save++;
+                               next;
+                               }
+                       }
+               &push_index($mail[$i], $sf, $i);
+               }
+
+       if ($need_save) {
+               $_[2]->{'members'} = \@newmems;
+               &save_folder($_[2]);
+               }
+       return @mail;
+       }
+}
+
+# mailbox_select_mails(&folder, &indexes, headersonly)
+# Returns only messages from a folder with indexes in the given array
+sub mailbox_select_mails
+{
+local ($folder, $indexes, $headersonly) = @_;
+if ($folder->{'type'} == 0) {
+       # mbox folder
+       return &select_mails($folder->{'file'}, $indexes, $headersonly);
+       }
+elsif ($folder->{'type'} == 1) {
+       # Maildir folder
+       return &select_maildir($folder->{'file'}, $indexes, $headersonly);
+       }
+elsif ($folder->{'type'} == 3) {
+       # MH folder
+       return &select_mhdir($folder->{'file'}, $indexes, $headersonly);
+       }
+elsif ($folder->{'type'} == 2) {
+       # POP folder
+
+       # Login first
+       local @rv = &pop3_login($folder);
+       if ($rv[0] != 1) {
+               # Failed to connect or login
+               if ($_[4]) {
+                       @{$_[4]} = @rv;
+                       return ();
+                       }
+               elsif ($rv[0] == 0) { &error($rv[1]); }
+               else { &error(&text('save_elogin', $rv[1])); }
+               }
+       local $h = $rv[1];
+       local @uidl = &pop3_uidl($h);
+
+       # Work out what we have cached
+       local ($i, $f, %cached, %sizeneed);
+       local @rv;
+       local $cd = "$cache_directory/$_[2]->{'id'}.cache";
+       if (opendir(CACHE, $cd)) {
+               while($f = readdir(CACHE)) {
+                       if ($f =~ /^(\S+)\.body$/) {
+                               $cached{$1} = 2;
+                               }
+                       elsif ($f =~ /^(\S+)\.headers$/) {
+                               $cached{$1} = 1;
+                               }
+                       }
+               closedir(CACHE);
+               }
+       else {
+               mkdir($cd, 0700);
+               }
+
+       # For each requested index, get the headers or body
+       foreach my $i (@$indexes) {
+               local $u = &safe_uidl($uidl[$i]);
+               if ($cached{$u} == 2 || $cached{$u} == 1 && $headersonly) {
+                       # We already have everything that we need
+                       }
+               elsif ($cached{$u} == 1 || !$headersonly) {
+                       # We need to get the entire mail
+                       &pop3_command($h, "retr ".($i+1));
+                       open(CACHE, ">$cd/$u.body");
+                       while(<$h>) {
+                               s/\r//g;
+                               last if ($_ eq ".\n");
+                               print CACHE $_;
+                               }
+                       close(CACHE);
+                       unlink("$cd/$u.headers");
+                       $cached{$u} = 2;
+                       }
+               else {
+                       # We just need the headers
+                       &pop3_command($h, "top ".($i+1)." 0");
+                       open(CACHE, ">$cd/$u.headers");
+                       while(<$h>) {
+                               s/\r//g;
+                               last if ($_ eq ".\n");
+                               print CACHE $_;
+                               }
+                       close(CACHE);
+                       $cached{$u} = 1;
+                       }
+               local $mail = &read_mail_file($cached{$u} == 2 ?
+                               "$cd/$u.body" : "$cd/$u.headers");
+               if ($cached{$u} == 1) {
+                       if ($mail->{'body'} ne "") {
+                               $mail->{'size'} = int($mail->{'body'});
+                               }
+                       else {
+                               $sizeneed{$i} = 1;
+                               }
+                       }
+               $mail->{'uidl'} = $uidl[$i];
+               $mail->{'idx'} = $i;
+               push(@rv, $mail);
+               }
+
+       # Get sizes for mails if needed
+       if (%sizeneed) {
+               &pop3_command($h, "list");
+               while(<$h>) {
+                       s/\r//g;
+                       last if ($_ eq ".\n");
+                       if (/^(\d+)\s+(\d+)/ && $sizeneed{$1-1}) {
+                               # Find mail in results, and set its size
+                               local ($ns) = grep { $_->{'idx'} == $1-1 } @rv;
+                               next if (!$ns);
+                               $ns->{'size'} = $2;
+                               local $u = &safe_uidl($uidl[$1-1]);
+                               open(CACHE, ">>$cd/$u.headers");
+                               print CACHE $2,"\n";
+                               close(CACHE);
+                               }
+                       }
+               }
+
+       return @rv;
+       }
+elsif ($folder->{'type'} == 4) {
+       # IMAP folder
+
+       # Login and select the specified mailbox
+       local @irv = &imap_login($folder);
+       if ($irv[0] != 1) {
+               # Something went wrong
+               if ($_[4]) {
+                       @{$_[4]} = @irv;
+                       return ();
+                       }
+               elsif ($irv[0] == 0) { &error($irv[1]); }
+               elsif ($irv[0] == 3) { &error(&text('save_emailbox', $irv[1])); }
+               elsif ($irv[0] == 2) { &error(&text('save_elogin2', $irv[1])); }
+               }
+       local $h = $irv[1];
+       local $count = $irv[2];
+       return () if (!$count);
+
+       # Fetch each mail by index
+       local @rv;
+       local $wanted = $headersonly ? "(RFC822.SIZE RFC822.HEADER)"
+                                    : "RFC822";
+       local @idxrv = &imap_command($h,
+               "FETCH ".join(",", map { $_+1 } @$indexes)." $wanted");
+       local $i = 0;
+       foreach my $idxrv (@{idxrv->[1]}) {
+               local $mail = &parse_imap_mail($idxrv);
+               if ($mail) {
+                       $mail->{'idx'} = $indexes->[$i++];
+                       push(@rv, $mail);
+                       }
+               }
+
+       return @rv;
+       }
+elsif ($folder->{'type'} == 5) {
+       # Composite folder .. need to convert each index to a position
+       # in a sub-folder
+       # XXX could be faster
+       my $pos = 0;
+       my @ranges;
+       foreach my $sf (@{$folder->{'subfolders'}}) {
+               my $len = &mailbox_folder_size($sf);
+               push(@ranges, [ $pos, $pos+$len, $sf ]);
+               $pos += $len;
+               }
+       local @rv;
+       foreach my $i (@$indexes) {
+               # Find out which sub-folder this index is in
+               foreach my $r (@ranges) {
+                       if ($i >= $r->[0] && $i < $r->[1]) {
+                               # Found it!
+                               local ($mail) = &mailbox_select_mails(
+                                          $r->[2], [ $i - $r->[0] ],
+                                          $headersonly);
+                               &push_index($mail, $r->[2], $i);
+                               push(@rv, $mail);
+                               last;
+                               }
+                       }
+               }
+       return @rv;
+       }
+elsif ($folder->{'type'} == 6) {
+       # Virtual folder .. translate each index to sub-folder index
+       # XXX could be faster
+       local $mems = $folder->{'members'};
+       local @rv;
+       local $need_save = 0;
+       local @newmems = @$mems;
+
+       # Work out how many different sub-folders we have
+       local $sf;
+       foreach my $i (@$indexes) {
+               $sf = $mems->[$i]->[0];
+               $sfcount{&folder_name($sf)} = 1;
+               }
+       if ((keys %sfcount) == 1) {
+               # Just one, so we can do one big select
+               local (@sfindexes, @sfmems);
+               foreach my $i (@$indexes) {
+                       push(@sfindexes, $mems->[$i]->[1]);
+                       push(@sfmems, $i);
+                       }
+               local $i = 0;
+               foreach my $mail (&mailbox_select_mails($sf, \@sfindexes,
+                                                       $headersonly)) {
+                       $mail->{'mem'} = $mems->[$sfmems[$i]];
+                       &push_index($mail, $sf, $sfmems[$i]);
+                       push(@rv, $mail);
+                       $i++;
+                       }
+               }
+       elsif ((keys %sfcount) > 0) {
+               # Several sub-folders, so we need to select from each
+               foreach my $i (@$indexes) {
+                       local $sf = $mems->[$i]->[0];
+                       local $idx = $mems->[$i]->[1];
+                       local ($mail) = &mailbox_select_mails($sf, [ $idx ],
+                                                             $headersonly);
+                       $mail->{'mem'} = $mems->[$i];
+                       &push_index($mail, $sf, $i);
+                       push(@rv, $mail);
+                       }
+               }
+
+       # Fix up any mistmatches between indexes and message IDs
+       foreach $mail (@rv) {
+               local $mem = $mail->{'mem'};
+               if ($mem->[2] &&
+                   $mail->{'header'}->{'message-id'} ne $mem->[2]) {
+                       # Message ID mismatch! Fix it..
+                       local $real = &find_by_message_id(
+                                       $mail->{'subfolder'}, $mem->[2]);
+                       if ($real) {
+                               $mem->[1] = $real->{'idx'};
+                               $mail = $real;
+                               $need_save++;
+                               }
+                       else {
+                               # Doesn't exist! Lose from index..
+                               @newmems = grep { $_ ne $mem } @newmems;
+                               $need_save++;
+                               }
+                       }
+               }
+       if ($need_save) {
+               $folder->{'members'} = \@newmems;
+               &save_folder($folder);
+               }
+       return @rv;
+       }
+}
+
+# compute_start_end(start, end, count)
+# Given start and end indexes (which may be negative or undef), returns the
+# real mail file indexes.
+sub compute_start_end
+{
+local ($start, $end, $count) = @_;
+if (!defined($start)) {
+       return (0, $count-1);
+       }
+elsif ($end < 0) {
+       local $rstart = $count+$_[1]-1;
+       local $rend = $count+$_[0]-1;
+       $rstart = $rstart < 0 ? 0 : $rstart;
+       return ($rstart, $rend);
+       }
+else {
+       local $rend = $_[1];
+       $rend = $count - 1 if ($rend >= $count);
+       return ($start, $rend);
+       }
+}
+
+# mailbox_list_mails_sorted(start, end, &folder, [headeronly], [&error],
+#                          [sort-field, sort-dir])
+# Returns messages in a folder within the given range, but sorted by the
+# given field and condition.
+sub mailbox_list_mails_sorted
+{
+local ($start, $end, $folder, $headers, $error, $field, $dir) = @_;
+if (!$field) {
+       # Default to current ordering
+       ($field, $dir) = &get_sort_field($folder);
+       }
+if (!$field || !$folder->{'sortable'}) {
+       # No sorting .. just return newest first
+       local @rv = reverse(&mailbox_list_mails(
+               -$start, -$end-1, $folder, $headers, $error));
+       local $i = 0;
+       foreach my $m (@rv) {
+               $m->{'sortidx'} = $i++;
+               }
+       return @rv;
+       }
+
+# Build the appropriate sort index, if missing
+local %index;
+&build_sort_index($folder, $field, \%index);
+
+# Get message indexes, sorted by the field
+my @sorter;
+while(my ($k, $v) = each %index) {
+       if ($k =~ /^(\d+)_\Q$field\E$/) {
+               push(@sorter, [ $1, lc($v) ]);
+               }
+       }
+if ($field eq "size" || $field eq "date" || $field eq "x-spam-status") {
+       # Numeric sort
+       @sorter = sort { my $s = $a->[1] <=> $b->[1]; $dir ? $s : -$s } @sorter;
+       }
+else {
+       # Alpha sort
+       @sorter = sort { my $s = $a->[1] cmp $b->[1]; $dir ? $s : -$s } @sorter;
+       }
+
+# Find those mails within the requested range
+($start, $end) = &compute_start_end($start, $end, scalar(@sorter));
+local @rv = map { undef } (0 .. scalar(@sorter)-1);
+local @wantindexes = map { $sorter[$_]->[0] } ($start .. $end);
+local @mails = &mailbox_select_mails($folder, \@wantindexes, $headers);
+for(my $i=0; $i<@mails; $i++) {
+       $rv[$start+$i] = $mails[$i];
+       $mails[$i]->{'sortidx'} = $start+$i;
+       }
+return @rv;
+}
+
+# set_sort_indexes(&folder, &mails, [sort-field, sort-dir])
+# Given a list of messages, sets the sortidx field based on their indexes
+# that would be used if the folder was sorted
+sub set_sort_indexes
+{
+local ($folder, $mails, $field, $dir) = @_;
+if (!$field) {
+       # Default to current ordering
+       ($field, $dir) = &get_sort_field($folder);
+       }
+if (!$field || !$folder->{'sortable'}) {
+       # Sort index is the same as the normal index reversed
+       my $count = &mailbox_folder_size($folder);
+       foreach my $m (@$mails) {
+               $m->{'sortidx'} = $count-$m->{'idx'}-1;
+               }
+       return;
+       }
+
+# Build the appropriate sort index, if missing
+local %index;
+&build_sort_index($folder, $field, \%index);
+
+# Get message indexes, sorted by the field
+my @sorter;
+while(my ($k, $v) = each %index) {
+       if ($k =~ /^(\d+)_\Q$field\E$/) {
+               push(@sorter, [ $1, lc($v) ]);
+               }
+       }
+if ($field eq "size" || $field eq "date") {
+       # Numeric sort
+       @sorter = sort { my $s = $a->[1] <=> $b->[1]; $dir ? $s : -$s } @sorter;
+       }
+else {
+       # Alpha sort
+       @sorter = sort { my $s = $a->[1] cmp $b->[1]; $dir ? $s : -$s } @sorter;
+       }
+
+# Update sort indexes for mails in list
+my $i = 0;
+my %idxmap = map { $_->{'idx'}, $_ } @$mails;
+foreach my $s (@sorter) {
+       my $m = $idxmap{$s->[0]};
+       if ($m) {
+               $m->{'sortidx'} = $i;
+               }
+       $i++;
+       }
+}
+
+# build_sort_index(&folder, field, &index)
+# Builds and/or loads the index for sorting a folder on some field. The
+# index uses the mail number as the key, and the field value as the value.
+sub build_sort_index
+{
+local ($folder, $field, $index) = @_;
+return 0 if (!$folder->{'sortable'});
+local $ifile = &folder_sort_index_file($folder);
+
+&open_dbm_db($index, $ifile, 0600);
+if ($index->{'lastchange'} < $folder->{'lastchange'} ||
+    !$folder->{'lastchange'}) {
+       # The mail file is newer than the index .. add messages not in the index
+       # to it.
+       local $realcount = $folder->{'type'} == 6 ?
+                       scalar(@{$folder->{'members'}}) :
+                       &mailbox_folder_size($folder);
+       local $indexcount = int($index->{'mailcount'});
+       if ($realcount < $indexcount) {
+               # Mail size has decreased! Need total rebuild
+               $indexcount = 0;
+               %$index = ( );
+               }
+       local @mails = $realcount ? &mailbox_select_mails($folder,
+                                       [ $indexcount .. $realcount-1 ], 1)
+                                 : ( );
+       my @index_fields = ( "subject", "from", "to", "date", "size", "x-spam-status", "message-id" );
+       foreach my $mail (@mails) {
+               foreach my $f (@index_fields) {
+                       if ($f eq "date") {
+                               # Convert date to Unix time
+                               $index->{$mail->{'idx'}."_date"} =
+                                 &parse_mail_date($mail->{'header'}->{'date'});
+                               }
+                       elsif ($f eq "size") {
+                               # Get mail size
+                               $index->{$mail->{'idx'}."_size"} =
+                                       $mail->{'size'};
+                               }
+                       elsif ($f eq "from" || $f eq "to") {
+                               # From: header .. convert to display version
+                               $index->{$mail->{'idx'}."_".$f} =
+                                       &simplify_from($mail->{'header'}->{$f});
+                               }
+                       elsif ($f eq "subject") {
+                               # Convert subject to display version
+                               $index->{$mail->{'idx'}."_".$f} =
+                                   &simplify_subject($mail->{'header'}->{$f});
+                               }
+                       elsif ($f eq "x-spam-status") {
+                               # Extract spam score
+                               $index->{$mail->{'idx'}."_".$f} =
+                                       $mail->{'header'}->{$f} =~ /(hits|score)=([0-9\.]+)/ ? $2 : undef;
+                               }
+                       else {
+                               # Just a header
+                               $index->{$mail->{'idx'}."_".$f} =
+                                       $mail->{'header'}->{$f};
+                               }
+                       }
+               }
+       $index->{'lastchange'} = time();
+       $index->{'mailcount'} = $realcount;
+       }
+return 1;
+}
+
+# delete_sort_index(&folder)
+# Trashes the sort index for a folder, to force a rebuild
+sub delete_sort_index
+{
+local ($folder) = @_;
+local $ifile = &folder_sort_index_file($folder);
+
+my %index;
+&open_dbm_db(\%index, $ifile, 0600);
+%index = ( );
+}
+
+# folder_sort_index_file(&folder)
+# Returns the index file to use for some folder
+sub folder_sort_index_file
+{
+local ($folder) = @_;
+return &user_index_file(($folder->{'file'} || $folder->{'id'}).".sort");
+}
+
+# find_by_message_id(&folder, id)
+# Finds a message by ID, and returns the mail object
+sub find_by_message_id
+{
+local ($folder, $mid) = @_;
+local %index;
+if (&build_sort_index($folder, undef, \%index)) {
+       while(my ($k, $v) = each %index) {
+               if ($k =~ /^(\d+)_message-id$/ && $v eq $mid) {
+                       # Got it!
+                       local ($rv) = &mailbox_select_mails($folder, [ $1 ]);
+                       &set_sort_indexes($folder, [ $rv ]);
+                       return $rv;
+                       }
+               }
+       }
+return undef;
+}
+
+# find_message_by_index(&mails, &folder, index, mid)
+# Finds a message by index (in a sorted folder) or message ID
+sub find_message_by_index
+{
+local ($mails, $folder, $idx, $mid) = @_;
+local $mail = $mails->[$idx];
+if ($mid && (!$mail || $mail->{'header'}->{'message-id'} ne $mid)) {
+       $mail = &find_by_message_id($folder, $mid);
+       }
+return $mail;
+}
+
+# mailbox_search_mail(&fields, andmode, &folder, [&limit])
+# Search a mailbox for multiple matching fields
+sub mailbox_search_mail
+{
+# For folders other than mbox and IMAP, build a sort index and use that for
+# the search, if it is simple enough (Subject, From and To only)
+local @idxfields = grep { $_->[0] eq 'from' || $_->[0] eq 'to' ||
+                          $_->[0] eq 'subject' } @{$_[0]};
+if ($_[2]->{'type'} != 0 && $_[2]->{'type'} != 4 && $_[2]->{'type'} != 5 &&
+    scalar(@idxfields) == scalar(@{$_[0]}) && @idxfields &&
+    &get_product_name() eq 'usermin') {
+       local %index;
+       &build_sort_index($_[2], undef, \%index);
+       local @rv;
+
+       # Work out which mail indexes match the requested headers
+       local %idxmatches = map { ("$_->[0]/$_->[1]", [ ]) } @idxfields;
+       while(my ($k, $v) = each %index) {
+               local ($ki, $kf) = split(/_/, $k, 2);
+               next if (!$kf || $ki eq '');
+
+               # Check all of the fields to see which ones match
+               foreach my $if (@idxfields) {
+                       local $iff = $if->[0];
+                       local ($neg) = ($iff =~ s/^\!//);
+                       next if ($kf ne $iff);
+                       if (!$neg && $v =~ /\Q$if->[1]\E/i ||
+                           $neg && $v !~ /\Q$if->[1]\E/i) {
+                               push(@{$idxmatches{"$if->[0]/$if->[1]"}}, $ki);
+                               }
+                       }
+               }
+       local @matches;
+       if ($_[1]) {
+               # Find indexes in all arrays
+               local %icount;
+               foreach my $if (keys %idxmatches) {
+                       foreach my $i (@{$idxmatches{$if}}) {
+                               $icount{$i}++;
+                               }
+                       }
+               foreach my $i (keys %icount) {
+                       }
+               local $fif = $idxfields[0];
+               @matches = grep { $icount{$_} == scalar(@idxfields) }
+                               @{$idxmatches{"$fif->[0]/$fif->[1]"}};
+               }
+       else {
+               # Find indexes in any array
+               foreach my $if (keys %idxmatches) {
+                       push(@matches, @{$idxmatches{$if}});
+                       }
+               @matches = &unique(@matches);
+               }
+       @matches = sort { $a <=> $b } @matches;
+
+       # Select the actual mails
+       return &mailbox_select_mails($_[2], \@matches, 0);
+       }
+
+if ($_[2]->{'type'} == 0) {
+       # Just search an mbox format file (which will use its own special
+       # field-level index)
+       return &advanced_search_mail($_[2]->{'file'}, $_[0], $_[1], $_[3]);
+       }
+elsif ($_[2]->{'type'} == 1) {
+       # Search a maildir directory
+       local $md = $_[2]->{'file'};
+       return &advanced_search_maildir($md, $_[0], $_[1], $_[3]);
+       }
+elsif ($_[2]->{'type'} == 2) {
+       # Get all of the mail from the POP3 server and search it
+       local ($min, $max);
+       if ($_[3] && $_[3]->{'latest'}) {
+               $min = -1;
+               $max = -$_[3]->{'latest'};
+               }
+       local @mails = &mailbox_list_mails($min, $max, $_[2],
+                  &indexof('body', &search_fields($_[0])) >= 0 ? 0 : 1);
+       local @rv = grep { $_ && &mail_matches($_[0], $_[1], $_) } @mails;
+       }
+elsif ($_[2]->{'type'} == 3) {
+       # Search an MH directory
+       local $md = $_[2]->{'file'};
+       return &advanced_search_mhdir($md, $_[0], $_[1], $_[3]);
+       }
+elsif ($_[2]->{'type'} == 4) {
+       # Use IMAP's remote search feature
+       # XXX broken!
+       local @rv = &imap_login($_[2]);
+       if ($rv[0] == 0) { &error($rv[1]); }
+       elsif ($rv[0] == 3) { &error(&text('save_emailbox', $rv[1])); }
+       elsif ($rv[0] == 2) { &error(&text('save_elogin2', $rv[1])); }
+       local $h = $rv[1];
+
+       # Do the search to get back a list of matching numbers
+       local @search;
+       foreach $f (@{$_[0]}) {
+               local $field = $f->[0];
+               local $neg = ($field =~ s/^\!//);
+               local $what = $f->[1];
+               $what = "\"$what\"" if ($field ne "size");
+               $field = "LARGER" if ($field eq "size");
+               local $search = uc($field)." ".$what."";
+               $search = "NOT $search" if ($neg);
+               push(@searches, $search);
+               }
+       local $searches;
+       if (@searches == 1) {
+               $searches = $searches[0];
+               }
+       elsif ($_[1]) {
+               $searches = join(" ", @searches);
+               }
+       else {
+               $searches = $searches[$#searches];
+               for($i=$#searches-1; $i>=0; $i--) {
+                       $searches = "or $searches[$i] ($searches)";
+                       }
+               }
+       @rv = &imap_command($h, "SEARCH $searches");
+       &error(&text('save_esearch', $rv[3])) if (!$rv[0]); 
+
+       # Get and parse those specific messages
+       local ($srch) = grep { $_ =~ /^\*\s+SEARCH/i } @{$rv[1]};
+       local @ids = split(/\s+/, $srch);
+       shift(@ids); shift(@ids);       # lose * SEARCH
+       local (@mail, $idx);
+       foreach $idx (@ids) {
+               local $realidx = $idx-1;
+               if ($_[3] && $_[3]->{'latest'}) {
+                       # Don't get message if outside search range
+                       next if ($realidx < $rv[3]-$_[3]->{'latest'});
+                       }
+               local @rv = &imap_command($h,
+                       "FETCH $idx (RFC822.SIZE RFC822.HEADER)");
+               &error(&text('save_esearch', $rv[3])) if (!$rv[0]); 
+               local $mail = &parse_imap_mail($rv[1]->[0]);
+               if ($mail) {
+                       $mail->{'idx'} = $realidx;
+                       push(@mail, $mail);
+                       }
+               }
+       return reverse(@mail);
+       }
+elsif ($_[2]->{'type'} == 5) {
+       # Search each sub-folder and combine the results - taking any count
+       # limits into effect
+       local $sf;
+       local $pos = 0;
+       local @mail;
+       local (%start, %len);
+       foreach $sf (@{$_[2]->{'subfolders'}}) {
+               $len{$sf} = &mailbox_folder_size($sf);
+               $start{$sf} = $pos;
+               $pos += $len{$sf};
+               }
+       local $limit = $_[3] ? { %{$_[3]} } : undef;
+       foreach $sf (reverse(@{$_[2]->{'subfolders'}})) {
+               local @submail = &mailbox_search_mail($_[0], $_[1], $sf, $limit);
+               foreach $sm (@submail) {
+                       &push_index($sm, $sf, $sm->{'idx'}+$start{$sf});
+                       }
+               push(@mail, reverse(@submail));
+               if ($limit && $limit->{'latest'}) {
+                       # Adjust latest down by size of this folder
+                       $limit->{'latest'} -= $len{$sf};
+                       last if ($limit->{'latest'} <= 0);
+                       }
+               }
+       return reverse(@mail);
+       }
+elsif ($_[2]->{'type'} == 6) {
+       # Just run a search on the sub-mails
+       local @rv;
+       local ($min, $max);
+       if ($_[3] && $_[3]->{'latest'}) {
+               $min = -1;
+               $max = -$_[3]->{'latest'};
+               }
+       local $mail;
+       foreach $mail (&mailbox_list_mails($min, $max, $_[2])) {
+               push(@rv, $mail) if ($mail && &mail_matches($_[0],$_[1],$mail));
+               }
+       return @rv;
+       }
+}
+
+# mailbox_delete_mail(&folder, mail, ...)
+# Delete multiple messages from some folder
+sub mailbox_delete_mail
+{
+return undef if (&is_readonly_mode());
+local $f = shift(@_);
+if ($userconfig{'delete_mode'} == 1 && !$f->{'trash'} && !$f->{'spam'}) {
+       # Copy to trash folder first
+       local ($trash) = grep { $_->{'trash'} } &list_folders();
+       local $m;
+       foreach $m (@_) {
+               &write_mail_folder($m, $trash);
+               }
+       }
+
+if ($f->{'type'} == 0) {
+       # Delete from mbox
+       &delete_mail($f->{'file'}, @_);
+       }
+elsif ($f->{'type'} == 1) {
+       # Delete from Maildir
+       &delete_maildir(@_);
+       }
+elsif ($f->{'type'} == 2) {
+       # Login and delete from the POP3 server
+       local @rv = &pop3_login($f);
+       if ($rv[0] == 0) { &error($rv[1]); }
+       elsif ($rv[0] == 2) { &error(&text('save_elogin', $rv[1])); }
+       local $h = $rv[1];
+       local @uidl = &pop3_uidl($h);
+       local $m;
+       local $cd = "$cache_directory/$f->{'id'}.cache";
+       foreach $m (@_) {
+               local $idx = &indexof($m->{'uidl'}, @uidl);
+               if ($idx >= 0) {
+                       &pop3_command($h, "dele ".($idx+1));
+                       local $u = &safe_uidl($m->{'uidl'});
+                       unlink("$cd/$u.headers",
+                              "$cd/$u.body");
+                       }
+               }
+       }
+elsif ($f->{'type'} == 3) {
+       # Delete from MH dir
+       &delete_mhdir(@_);
+       }
+elsif ($f->{'type'} == 4) {
+       # Delete from the IMAP server
+       local @rv = &imap_login($f);
+       if ($rv[0] == 0) { &error($rv[1]); }
+       elsif ($rv[0] == 3) { &error(&text('save_emailbox', $rv[1])); }
+       elsif ($rv[0] == 2) { &error(&text('save_elogin2', $rv[1])); }
+       local $h = $rv[1];
+
+       local $m;
+       foreach $m (@_) {
+               @rv = &imap_command($h, "STORE ".($m->{'idx'}+1).
+                                       " +FLAGS (\\Deleted)");
+               &error(&text('save_edelete', $rv[3])) if (!$rv[0]); 
+               }
+       @rv = &imap_command($h, "EXPUNGE");
+       &error(&text('save_edelete', $rv[3])) if (!$rv[0]); 
+       }
+elsif ($f->{'type'} == 5) {
+       # Delete from underlying folder(s)
+       local $sm;
+       foreach $sm (@_) {
+               local ($subfolder, $idx) = &pop_index($sm);
+               &mailbox_delete_mail($subfolder, $sm);
+               &push_index($sm, $subfolder, $idx);
+               }
+       }
+elsif ($f->{'type'} == 6) {
+       # Delete from virtual folder
+       local $sm;
+       if ($f->{'delete'}) {
+               # Delete the underlying messages, and remove from index
+               local %folderdel;
+               foreach $sm (sort { $b->{'subs'}->[0]->[1] <=>
+                                   $a->{'subs'}->[0]->[1] } @_) {
+                       local ($subfolder, $idx) = &pop_index($sm);
+                       &mailbox_delete_mail($subfolder, $sm);
+                       &push_index($sm, $subfolder, $idx);
+
+                       # Adjust indexes in virtual list that refer to same
+                       # sub-folder, and have higher numbers
+                       for(my $i=0; $i<@{$f->{'members'}}; $i++) {
+                               my $m = $f->{'members'}->[$i];
+                               my $subidx = $sm->{'subs'}->[0]->[1];
+                               if ($m->[0] eq $sm->{'subfolder'}) {
+                                       if ($m->[1] == $subidx) {
+                                               splice(@{$f->{'members'}},
+                                                      $i--, 1);
+                                               }
+                                       elsif ($m->[1] > $subidx) {
+                                               $m->[1]--;
+                                               }
+                                       }
+                               }
+                       }
+               }
+       else {
+               # Just take out of the virtual index
+               foreach $sm (sort { $b->{'idx'} <=> $a->{'idx'} } @_) {
+                       splice(@{$f->{'members'}}, $sm->{'idx'}, 1);
+                       }
+               }
+       &save_folder($f, $f);
+       }
+
+# Update folder index to remove indexes for deleted messages
+if ($f->{'sortable'}) {
+       local %index;
+       &build_sort_index($f, undef, \%index);
+       foreach my $m (sort { $b->{'idx'} <=> $a->{'idx'} } @_) {
+               foreach my $k (sort { $a <=> $b } (keys %index)) {
+                       local ($ki, $kf) = split(/_/, $k, 2);
+                       if ($m->{'idx'} == $ki) {
+                               # Delete this actual mail from the index
+                               delete($index{$k});
+                               }
+                       elsif ($m->{'idx'} < $ki) {
+                               # Mail is after one being deleted .. shift down
+                               $index{($ki-1)."_".$kf} = $index{$k};
+                               delete($index{$k});
+                               }
+                       }
+               }
+       $index{'mailcount'} -= scalar(@_);
+       $index{'lastchange'} = time();
+       dbmclose(%index);
+       }
+}
+
+# mailbox_empty_folder(&folder)
+# Remove the entire contents of a mail folder
+sub mailbox_empty_folder
+{
+return undef if (&is_readonly_mode());
+local $f = $_[0];
+if ($f->{'type'} == 0) {
+       # mbox format mail file
+       &empty_mail($f->{'file'});
+       }
+elsif ($f->{'type'} == 1) {
+       # qmail format maildir
+       &empty_maildir($f->{'file'});
+       }
+elsif ($f->{'type'} == 2) {
+       # POP3 server .. delete all messages
+       local @rv = &pop3_login($f);
+       if ($rv[0] == 0) { &error($rv[1]); }
+       elsif ($rv[0] == 2) { &error(&text('save_elogin', $rv[1])); }
+       local $h = $rv[1];
+       @rv = &pop3_command($h, "stat");
+       $rv[1] =~ /^(\d+)/ || return;
+       local $count = $1;
+       local $i;
+       for($i=1; $i<=$count; $i++) {
+               &pop3_command($h, "dele ".$i);
+               }
+       }
+elsif ($f->{'type'} == 3) {
+       # mh format maildir
+       &empty_mhdir($f->{'file'});
+       }
+elsif ($f->{'type'} == 4) {
+       # IMAP server .. delete all messages
+       local @rv = &imap_login($f);
+       if ($rv[0] == 0) { &error($rv[1]); }
+       elsif ($rv[0] == 3) { &error(&text('save_emailbox', $rv[1])); }
+       elsif ($rv[0] == 2) { &error(&text('save_elogin2', $rv[1])); }
+       local $h = $rv[1];
+       local $count = $rv[2];
+       local $i;
+       for($i=1; $i<=$count; $i++) {
+               @rv = &imap_command($h, "STORE ".$i.
+                                       " +FLAGS (\\Deleted)");
+               &error(&text('save_edelete', $rv[3])) if (!$rv[0]); 
+               }
+       @rv = &imap_command($h, "EXPUNGE");
+       &error(&text('save_edelete', $rv[3])) if (!$rv[0]); 
+       }
+elsif ($f->{'type'} == 5) {
+       # Empty each sub-folder
+       local $sf;
+       foreach $sf (@{$f->{'subfolders'}}) {
+               &mailbox_empty_folder($sf);
+               }
+       }
+elsif ($f->{'type'} == 6) {
+       # Just clear the virtual index
+       $f->{'members'} = [ ];
+       &save_folder($f);
+       }
+
+# Trash the folder index
+if ($folder->{'sortable'}) {
+       &delete_sort_index($folder);
+       }
+}
+
+# mailbox_copy_folder(&source, &dest)
+# Copy all messages from one folder to another. This is done in an optimized
+# way if possible.
+sub mailbox_copy_folder
+{
+local ($src, $dest) = @_;
+if ($src->{'type'} == 0 && $dest->{'type'} == 0) {
+       # mbox to mbox .. just read and write the files
+       &open_readfile(SOURCE, $src->{'file'});
+       &open_tempfile(DEST, ">>$dest->{'file'}");
+       while(read(SOURCE, $buf, 1024) > 0) {
+               &print_tempfile(DEST, $buf);
+               }
+       &close_tempfile(DEST);
+       close(SOURCE);
+       }
+elsif ($src->{'type'} == 1 && $dest->{'type'} == 1) {
+       # maildir to maildir .. just copy the files
+       local @files = &get_maildir_files($src->{'file'});
+       foreach my $f (@files) {
+               local $fn = $f;
+               $fn =~ s/^.*\///;
+               &copy_source_dest($f, "$dest->{'file'}/$fn");
+               }
+       }
+elsif ($src->{'type'} == 1 && $dest->{'type'} == 0) {
+       # maildir to mbox .. append all the files
+       local @files = &get_maildir_files($src->{'file'});
+       &open_tempfile(DEST, ">>$dest->{'file'}");
+       foreach my $f (@files) {
+               &open_readfile(SOURCE, $f);
+               while(read(SOURCE, $buf, 1024) > 0) {
+                       &print_tempfile(DEST, $buf);
+                       }
+               close(SOURCE);
+               }
+       &close_tempfile(DEST);
+       }
+else {
+       # read in all mail and write out, in 100 message blocks
+       local $max = &mailbox_folder_size($src);
+       for(my $s=0; $s<$max; $s+=100) {
+               local $e = $s+99;
+               $e = $max-1 if ($e >= $max);
+               local @mail = &mailbox_list_mails($s, $e, $src);
+               local @want = @mail[$s..$e];
+               &mailbox_copy_mail($src, $dest, @want);
+               }
+       }
+}
+
+# mailbox_move_mail(&source, &dest, mail, ...)
+# Move mail from one folder to another
+sub mailbox_move_mail
+{
+return undef if (&is_readonly_mode());
+local $src = shift(@_);
+local $dst = shift(@_);
+local $now = time();
+local $hn = &get_system_hostname();
+&create_folder_maildir($dst);
+if (($src->{'type'} == 1 || $src->{'type'} == 3) && $dst->{'type'} == 1) {
+       # Can just move mail files
+       local $dd = $dst->{'file'};
+       &create_folder_maildir($dst);
+       foreach $m (@_) {
+               rename($m->{'file'}, "$dd/cur/$now.$$.$hn");
+               $now++;
+               }
+       }
+elsif (($src->{'type'} == 1 || $src->{'type'} == 3) && $dst->{'type'} == 3) {
+       # Can move and rename to MH numbering
+       local $dd = $dst->{'file'};
+       local $num = &max_mhdir($dst->{'file'}) + 1;
+       foreach $m (@_) {
+               rename($m->{'file'}, "$dd/$num");
+               $num++;
+               }
+       }
+else {
+       # Append to new folder file, or create in folder directory
+       local $m;
+       foreach $m (@_) {
+               &write_mail_folder($m, $dst);
+               }
+       &mailbox_delete_mail($src, @_);
+       }
+
+# Force re-generation of source folder index
+if ($src->{'sortable'}) {
+       &delete_sort_index($src);
+       # XXX could be faster
+       }
+}
+
+# mailbox_move_folder(&source, &dest)
+# Moves all mail from one folder to another, possibly converting the type
+sub mailbox_move_folder
+{
+return undef if (&is_readonly_mode());
+local ($src, $dst) = @_;
+if ($src->{'type'} == $dst->{'type'}) {
+       # Can just move the file or dir
+       system("rm -rf ".quotemeta($dst->{'file'}));
+       system("mv ".quotemeta($src->{'file'})." ".quotemeta($dst->{'file'}));
+       }
+else {
+       # Need to copy one by one :(
+       local @mails = &mailbox_list_mails(undef, undef, $src);
+       &mailbox_move_mail($src, $dst, @mails);
+       }
+
+# Delete source folder index
+if ($src->{'sortable'}) {
+       &delete_sort_index($src);
+       }
+}
+
+# mailbox_copy_mail(&source, &dest, mail, ...)
+# Copy mail from one folder to another
+sub mailbox_copy_mail
+{
+return undef if (&is_readonly_mode());
+local $src = shift(@_);
+local $dst = shift(@_);
+local $now = time();
+&create_folder_maildir($dst);
+local $m;
+if ($src->{'type'} == 6 && $dst->{'type'} == 6) {
+       # Copying from one virtual folder to another, so just copy the
+       # reference
+       foreach $m (@_) {
+               push(@{$dst->{'members'}}, [ $m->{'subfolder'}, $m->{'subid'},
+                                            $m->{'header'}->{'message-id'} ]);
+               }
+       }
+elsif ($dst->{'type'} == 6) {
+       # Add this mail to the index of the virtual folder
+       foreach $m (@_) {
+               push(@{$dst->{'members'}}, [ $src, $m->{'idx'},
+                                            $m->{'header'}->{'message-id'} ]);
+               }
+       &save_folder($dst);
+       }
+else {
+       # Just write to destination folder
+       foreach $m (@_) {
+               &write_mail_folder($m, $dst);
+               }
+       }
+}
+
+# folder_type(file_or_dir)
+sub folder_type
+{
+return -d "$_[0]/cur" ? 1 : -d $_[0] ? 3 : 0;
+}
+
+# create_folder_maildir(&folder)
+# Ensure that a maildir folder has the needed new, cur and tmp directories
+sub create_folder_maildir
+{
+mkdir($folders_dir, 0700);
+if ($_[0]->{'type'} == 1) {
+       local $id = $_[0]->{'file'};
+       mkdir("$id/cur", 0700);
+       mkdir("$id/new", 0700);
+       mkdir("$id/tmp", 0700);
+       }
+}
+
+# write_mail_folder(&mail, &folder, textonly)
+# Writes some mail message to a folder
+sub write_mail_folder
+{
+return undef if (&is_readonly_mode());
+&create_folder_maildir($_[1]);
+if ($_[1]->{'type'} == 1) {
+       # Add to a maildir directory
+       local $md = $_[1]->{'file'};
+       &write_maildir($_[0], $md, $_[2]);
+       }
+elsif ($_[1]->{'type'} == 3) {
+       # Create a new MH file
+       local $num = &max_mhdir($_[1]->{'file'}) + 1;
+       local $md = $_[1]->{'file'};
+       &send_mail($_[0], "$md/$num", $_[2], 1);
+       }
+elsif ($_[1]->{'type'} == 0) {
+       # Just append to the folder file
+       &send_mail($_[0], $_[1]->{'file'}, $_[2], 1);
+       }
+elsif ($_[1]->{'type'} == 4) {
+       # Upload to the IMAP server
+       local @rv = &imap_login($_[1]);
+       if ($rv[0] == 0) { &error($rv[1]); }
+       elsif ($rv[0] == 3) { &error(&text('save_emailbox', $rv[1])); }
+       elsif ($rv[0] == 2) { &error(&text('save_elogin2', $rv[1])); }
+       local $h = $rv[1];
+
+       # Create a temp file and use it to create the IMAP command
+       local $temp = &transname();
+       &send_mail($_[0], $temp, $_[2], 1);
+       open(TEMP, $temp);
+       local $text;
+       while(<TEMP>) { $text .= $_; }
+       close(TEMP);
+       unlink($temp);
+       @rv = &imap_command($h, sprintf "APPEND %s {%d}\r\n%s",
+                       $_[1]->{'mailbox'} || "INBOX", length($text), $text);
+       &error(&text('save_eappend', $rv[3])) if (!$rv[0]); 
+       }
+elsif ($_[1]->{'type'} == 5) {
+       # Just append to the last subfolder
+       local @sf = @{$_[1]->{'subfolders'}};
+       &write_mail_folder($_[0], $sf[$#sf], $_[2]);
+       }
+elsif ($_[1]->{'type'} == 6) {
+       # Add mail to first sub-folder, and to virtual index
+       &error("Cannot add mail to virtual folders");
+       }
+}
+
+# mailbox_modify_mail(&oldmail, &newmail, &folder, textonly)
+# Replaces some mail message with a new one
+sub mailbox_modify_mail
+{
+return undef if (&is_readonly_mode());
+if ($_[2]->{'type'} == 1) {
+       # Just replace the existing file
+       &modify_maildir($_[0], $_[1], $_[3]);
+       }
+elsif ($_[2]->{'type'} == 3) {
+       # Just replace the existing file
+       &modify_mhdir($_[0], $_[1], $_[3]);
+       }
+elsif ($_[2]->{'type'} == 0) {
+       # Modify the mail file
+       &modify_mail($_[2]->{'file'}, $_[0], $_[1], $_[3]);
+       }
+elsif ($_[2]->{'type'} == 5 || $_[2]->{'type'} == 6) {
+       # Modify in the appropriate folder
+       local ($oldsubfolder, $oldidx) = &pop_index($_[0]);
+       local ($newsubfolder, $newidx) = &pop_index($_[1]);
+       &mailbox_modify_mail($_[0], $_[1], $oldsubfolder, $_[3]);
+       &push_index($_[0], $oldsubfolder, $oldidx);
+       &push_index($_[1], $newsubfolder, $newidx);
+       }
+else {
+       &error("Cannot modify mail in this type of folder!");
+       }
+
+# Force re-generation of folder index
+if ($_[2]->{'sortable'}) {
+       &delete_sort_index($_[2]);
+       # XXX could be faster
+       }
+}
+
+# mailbox_folder_size(&folder, [estimate])
+# Returns the number of messages in some folder
+sub mailbox_folder_size
+{
+if ($_[0]->{'type'} == 0) {
+       # A mbox formatted file
+       return &count_mail($_[0]->{'file'});
+       }
+elsif ($_[0]->{'type'} == 1) {
+       # A qmail maildir
+       return &count_maildir($_[0]->{'file'});
+       }
+elsif ($_[0]->{'type'} == 2) {
+       # A POP3 server
+       local @rv = &pop3_login($_[0]);
+       if ($rv[0] != 1) {
+               if ($rv[0] == 0) { &error($rv[1]); }
+               else { &error(&text('save_elogin', $rv[1])); }
+               }
+       local @st = &pop3_command($rv[1], "stat");
+       if ($st[0] == 1) {
+               local ($count, $size) = split(/\s+/, $st[1]);
+               return $count;
+               }
+       else {
+               &error($st[1]);
+               }
+       }
+elsif ($_[0]->{'type'} == 3) {
+       # An MH directory
+       return &count_mhdir($_[0]->{'file'});
+       }
+elsif ($_[0]->{'type'} == 4) {
+       # An IMAP server
+       local @rv = &imap_login($_[0]);
+       if ($rv[0] != 1) {
+               if ($rv[0] == 0) { &error($rv[1]); }
+               elsif ($rv[0] == 3) { &error(&text('save_emailbox', $rv[1])); }
+               elsif ($rv[0] == 2) { &error(&text('save_elogin2', $rv[1])); }
+               }
+       return $rv[2];
+       }
+elsif ($_[0]->{'type'} == 5) {
+       # A composite folder - the size is just that of the sub-folders
+       my $rv = 0;
+       foreach my $sf (@{$_[0]->{'subfolders'}}) {
+               $rv += &mailbox_folder_size($sf);
+               }
+       return $rv;
+       }
+elsif ($_[0]->{'type'} == 6 && !$_[1]) {
+       # A virtual folder .. we need to exclude messages that no longer
+       # exist in the parent folders
+       my $rv = 0;
+       foreach my $msg (@{$_[0]->{'members'}}) {
+               if (!$msg->[2] || &find_by_message_id($msg->[0], $msg->[2])) {
+                       $rv++;
+                       }
+               }
+       return $rv;
+       }
+elsif ($_[0]->{'type'} == 6 && $_[1]) {
+       # A virtual folder .. but we can just use the last member count
+       return scalar(@{$_[0]->{'members'}});
+       }
+}
+
+# pop3_login(&folder)
+# Logs into a POP3 server and returns a status (1=ok, 0=connect failed,
+# 2=login failed) and handle or error message
+sub pop3_login
+{
+local $h = $pop3_login_handle{$_[0]->{'id'}};
+return (1, $h) if ($h);
+$h = time().++$pop3_login_count;
+local $error;
+&open_socket($_[0]->{'server'}, $_[0]->{'port'} || 110, $h, \$error);
+return (0, $error) if ($error);
+local $os = select($h); $| = 1; select($os);
+local @rv = &pop3_command($h);
+return (0, $rv[1]) if (!$rv[0]);
+@rv = &pop3_command($h, "user $_[0]->{'user'}");
+return (2, $rv[1]) if (!$rv[0]);
+@rv = &pop3_command($h, "pass $_[0]->{'pass'}");
+return (2, $rv[1]) if (!$rv[0]);
+return (1, $pop3_login_handle{$_[0]->{'id'}} = $h);
+}
+
+# pop3_command(handle, command)
+# Executes a command and returns the status (1 or 0 for OK or ERR) and message
+sub pop3_command
+{
+local ($h, $c) = @_;
+print $h "$c\r\n" if ($c);
+local $rv = <$h>;
+$rv =~ s/\r|\n//g;
+return !$rv ? ( 0, "Connection closed" ) :
+       $rv =~ /^\+OK\s*(.*)/ ? ( 1, $1 ) :
+       $rv =~ /^\-ERR\s*(.*)/ ? ( 0, $1 ) : ( 0, $rv );
+}
+
+# pop3_logout(handle, doquit)
+sub pop3_logout
+{
+local @rv = $_[1] ? &pop3_command($_[0], "quit") : (1, undef);
+local $f;
+foreach $f (keys %pop3_login_handle) {
+       delete($pop3_login_handle{$f}) if ($pop3_login_handle{$f} eq $_[0]);
+       }
+close($_[0]);
+return @rv;
+}
+
+# pop3_uidl(handle)
+# Returns the uidl list
+sub pop3_uidl
+{
+local @rv;
+local $h = $_[0];
+local @urv = &pop3_command($h, "uidl");
+if (!$urv[0] && $urv[1] =~ /not\s+implemented/i) {
+       # UIDL is not available?! Use numeric list instead
+       &pop3_command($h, "list");
+       while(<$h>) {
+               s/\r//g;
+               last if ($_ eq ".\n");
+               if (/^(\d+)\s+(\d+)/) {
+                       push(@rv, "size$2");
+                       }
+               }
+       }
+elsif (!$urv[0]) {
+       &error("uidl failed! $urv[1]") if (!$urv[0]);
+       }
+else {
+       # Can get normal UIDL list
+       while(<$h>) {
+               s/\r//g;
+               last if ($_ eq ".\n");
+               if (/^(\d+)\s+(\S+)/) {
+                       push(@rv, $2);
+                       }
+               }
+       }
+return @rv;
+}
+
+# pop3_logout_all()
+# Properly closes all open POP3 and IMAP sessions
+sub pop3_logout_all
+{
+local $f;
+foreach $f (keys %pop3_login_handle) {
+       &pop3_logout($pop3_login_handle{$f}, 1);
+       }
+foreach $f (keys %imap_login_handle) {
+       &imap_logout($imap_login_handle{$f}, 1);
+       }
+}
+
+# imap_login(&folder)
+# Logs into a POP3 server, selects a mailbox and returns a status
+# (1=ok, 0=connect failed, 2=login failed, 3=mailbox error), a handle or error
+# message, and the number of messages in the mailbox.
+sub imap_login
+{
+local $h = $imap_login_handle{$_[0]->{'id'}};
+local @rv;
+if (!$h) {
+       # Need to open socket
+       $h = time().++$imap_login_count;
+       local $error;
+       &open_socket($_[0]->{'server'}, $_[0]->{'port'} ||
+               $imap_port, $h, \$error);
+       return (0, $error) if ($error);
+       local $os = select($h); $| = 1; select($os);
+
+       # Login normally
+       @rv = &imap_command($h);
+       return (0, $rv[3]) if (!$rv[0]);
+       @rv = &imap_command($h,"login \"$_[0]->{'user'}\" \"$_[0]->{'pass'}\"");
+       return (2, $rv[3]) if (!$rv[0]);
+
+       $imap_login_handle{$_[0]->{'id'}} = $h;
+       }
+
+# Select the right folder (if one was given)
+@rv = &imap_command($h, "select ".($_[0]->{'mailbox'} || "INBOX"));
+return (3, $rv[3]) if (!$rv[0]);
+local $count = $rv[2] =~ /\*\s+(\d+)\s+EXISTS/i ? $1 : undef;
+return (1, $h, $count);
+}
+
+# imap_command(handle, command)
+# Executes an IMAP command and returns 1 for success or 0 for failure, and
+# a reference to an array of results (some of which may be multiline), and
+# all of the results joined together, and the stuff after OK/BAD
+sub imap_command
+{
+local ($h, $c) = @_;
+local @rv;
+
+# Send the command, and read lines until a non-* one is found
+local $id = $$."-".$imap_command_count++;
+if ($c) {
+       print $h "$id $c\r\n";
+       }
+while(1) {
+       local $l = <$h>;
+       last if (!$l);
+       if ($l =~ /^(\*|\+)/) {
+               # Another response, and possibly the only one if no command
+               # was sent.
+               push(@rv, $l);
+               last if (!$c);
+               if ($l =~ /\{(\d+)\}\s*$/) {
+                       # Start of multi-line text .. read the specified size
+                       local $size = $1;
+                       local $got;
+                       local $err = "Error reading email";
+                       while($got < $size) {
+                               local $buf;
+                               local $r = read($h, $buf, $size-$got);
+                               return (0, [ $err ], $err, $err) if ($r < 0);
+                               $rv[$#rv] .= $buf;
+                               $got += $r;
+                               }
+                       }
+               }
+       elsif ($l =~ /^(\S+)\s+/ && $1 eq $id) {
+               # End of responses
+               push(@rv, $l);
+               last;
+               }
+       else {
+               # Part of last response
+               if (!@rv) {
+                       local $err = "Got unknown line $l";
+                       return (0, [ $err ], $err, $err);
+                       }
+               $rv[$#rv] .= $l;
+               }
+       }
+local $j = join("", @rv);
+local $lline = $rv[$#rv];
+if ($lline =~ /^(\S+)\s+OK\s*(.*)/) {
+       # Looks like the command worked
+       return (1, \@rv, $j, $2);
+       }
+else {
+       # Command failed!
+       return (0, \@rv, $j, $lline =~ /^(\S+)\s+(\S+)\s*(.*)/ ? $3 : undef);
+       }
+}
+
+# imap_logout(handle, doquit)
+sub imap_logout
+{
+local @rv = $_[1] ? &imap_command($_[0], "close") : (1, undef);
+local $f;
+foreach $f (keys %imap_login_handle) {
+       delete($imap_login_handle{$f}) if ($imap_login_handle{$f} eq $_[0]);
+       }
+close($_[0]);
+return @rv;
+}
+
+# lock_folder(&folder)
+sub lock_folder
+{
+return if ($_[0]->{'remote'} || $_[0]->{'type'} == 5 || $_[0]->{'type'} == 6);
+local $f = $_[0]->{'file'} ? $_[0]->{'file'} :
+          $_[0]->{'type'} == 0 ? &user_mail_file($remote_user) :
+                                 $qmail_maildir;
+if (&lock_file($f)) {
+       $_[0]->{'lock'} = $f;
+       }
+else {
+       # Cannot lock if in /var/mail
+       local $ff = $f;
+       $ff =~ s/\//_/g;
+       $ff = "/tmp/$ff";
+       $_[0]->{'lock'} = $ff;
+       &lock_file($ff);
+       }
+
+# Also, check for a .filename.pop3 file
+if ($config{'pop_locks'} && $f =~ /^(\S+)\/([^\/]+)$/) {
+       local $poplf = "$1/.$2.pop";
+       local $count = 0;
+       while(-r $poplf) {
+               sleep(1);
+               if ($count++ > 5*60) {
+                       # Give up after 5 minutes
+                       &error(&text('epop3lock_tries', "<tt>$f</tt>", 5));
+                       }
+               }
+       }
+}
+
+# unlock_folder(&folder)
+sub unlock_folder
+{
+return if ($_[0]->{'remote'});
+&unlock_file($_[0]->{'lock'});
+}
+
+# folder_file(&folder)
+# Returns the full path to the file or directory containing the folder's mail,
+# or undef if not appropriate (such as for POP3)
+sub folder_file
+{
+return $_[0]->{'remote'} ? undef : $_[0]->{'file'};
+}
+
+# parse_imap_mail(response)
+# Parses a response from the IMAP server into a standard mail structure
+sub parse_imap_mail
+{
+# Extract the actual mail part
+local $mail = { };
+local $realsize;
+local $imap = $_[0];
+if ($imap =~ /RFC822.SIZE\s+(\d+)/) {
+       $realsize = $1;
+       }
+$imap =~ s/^\*\s+\d+\s+FETCH.*\{(\d+)\}\r?\n// || return undef;
+local $size = $1;
+local @lines = split(/\n/, substr($imap, 0, $size));
+
+# Parse the headers
+local $lnum = 0;
+local @headers;
+while(1) {
+       local $line = $lines[$lnum++];
+       $mail->{'size'} += length($line);
+       $line =~ s/\r//g;
+       last if ($line eq '');
+       if ($line =~ /^(\S+):\s*(.*)/) {
+               push(@headers, [ $1, $2 ]);
+               }
+       elsif ($line =~ /^(\s+.*)/) {
+               $headers[$#headers]->[1] .= $1
+                       unless($#headers < 0);
+               }
+       }
+$mail->{'headers'} = \@headers;
+foreach $h (@headers) {
+       $mail->{'header'}->{lc($h->[0])} = $h->[1];
+       }
+
+# Parse the body
+while($lnum < @lines) {
+       $mail->{'size'} += length($lines[$lnum]+1);
+       $mail->{'body'} .= $lines[$lnum]."\n";
+       $lnum++;
+       }
+$mail->{'size'} = $realsize if ($realsize);
+return $mail;
+}
+
+# find_body(&mail, mode)
+# Returns the plain text body, html body and the one to use
+sub find_body
+{
+local ($a, $body, $textbody, $htmlbody);
+foreach $a (@{$_[0]->{'attach'}}) {
+       if ($a->{'type'} =~ /^text\/plain/i || $a->{'type'} eq 'text') {
+               $textbody = $a if (!$textbody && $a->{'data'} =~ /\S/);
+               }
+       elsif ($a->{'type'} =~ /^text\/html/i) {
+               $htmlbody = $a if (!$htmlbody && $a->{'data'} =~ /\S/);
+               }
+       }
+if ($_[1] == 0) {
+       $body = $textbody;
+       }
+elsif ($_[1] == 1) {
+       $body = $textbody || $htmlbody;
+       }
+elsif ($_[1] == 2) {
+       $body = $htmlbody || $textbody;
+       }
+elsif ($_[1] == 3) {
+       # Convert HTML to text if needed
+       if ($textbody) {
+               $body = $textbody;
+               }
+       else {
+               local $text = &html_to_text($htmlbody->{'data'});
+               $body = $textbody = 
+                       { 'data' => $text };
+               }
+       }
+return ($textbody, $htmlbody, $body);
+}
+
+# safe_html(html)
+# Converts HTML to a form safe for inclusion in a page
+sub safe_html
+{
+local $html = $_[0];
+local $bodystuff;
+if ($html =~ s/^[\000-\377]*<BODY([^>]*)>//i) {
+       $bodystuff = $1;
+       }
+$html =~ s/<\/BODY>[\000-\377]*$//i;
+$html =~ s/<base[^>]*>//i;
+$html = &filter_javascript($html);
+$html = &safe_urls($html);
+$bodystuff = &safe_html($bodystuff) if ($bodystuff);
+return wantarray ? ($html, $bodystuff) : $html;
+}
+
+# head_html(html)
+# Returns HTML in the <head> section of a document
+sub head_html
+{
+local $html = $_[0];
+return undef if ($html !~ /<HEAD[^>]*>/i || $html !~ /<\/HEAD[^>]*>/i);
+$html =~ s/^[\000-\377]*<HEAD[^>]*>//gi || &error("Failed to filter <pre>".&html_escape($html)."</pre>");
+$html =~ s/<\/HEAD[^>]*>[\000-\377]*//gi || &error("Failed to filter <pre>".&html_escape($html)."</pre>");
+$html =~ s/<base[^>]*>//i;
+return &filter_javascript($html);
+}
+
+# safe_urls(html)
+# Replaces dangerous-looking URLs in HTML
+sub safe_urls
+{
+local $html = $_[0];
+$html =~ s/((src|href|background)\s*=\s*)([^ '">]+)()/&safe_url($1, $3, $4)/gei;
+$html =~ s/((src|href|background)\s*=\s*')([^']+)(')/&safe_url($1, $3, $4)/gei;
+$html =~ s/((src|href|background)\s*=\s*")([^"]+)(")/&safe_url($1, $3, $4)/gei;
+return $html;
+}
+
+# safe_url(before, url, after)
+sub safe_url
+{
+local ($before, $url, $after) = @_;
+if ($url =~ /^#/) {
+       # Relative link - harmless
+       return $before.$url.$after;
+       }
+elsif ($url =~ /^cid:/i) {
+       # Definately safe (CIDs are harmless)
+       return $before.$url.$after;
+       }
+elsif ($url =~ /^(http:|https:)/) {
+       # Possibly safe, unless refers to local
+       local ($host, $port, $page, $ssl) = &parse_http_url($url);
+       local ($hhost, $hport) = split(/:/, $ENV{'HTTP_HOST'});
+       $hport ||= $ENV{'SERVER_PORT'};
+       if ($host ne $hhost ||
+           $port != $hport ||
+           $ssl != (uc($ENV{'HTTPS'}) eq 'ON' ? 1 : 0)) {
+               return $before.$url.$after;
+               }
+       else {
+               return $before."_unsafe_link_".$after;
+               }
+       }
+elsif ($url =~ /^mailto:([a-z0-9\.\-\_\@]+)/i) {
+       # A mailto link, which we can convert
+       return $before."reply_mail.cgi?new=1&to=".&urlize($1).$after;
+       }
+else {
+       # Relative URL like foo.cgi or /foo.cgi or ../foo.cgi - unsafe!
+       return $before."_unsafe_link_".$after;
+       }
+}
+
+# safe_uidl(string)
+sub safe_uidl
+{
+local $rv = $_[0];
+$rv =~ s/\/|\./_/g;
+return $rv;
+}
+
+# html_to_text(html)
+# Attempts to convert some HTML to text form
+sub html_to_text
+{
+local ($h2, $lynx);
+if (($h2 = &has_command("html2text")) || ($lynx = &has_command("lynx"))) {
+       # Can use a commonly available external program
+       local $temp = &transname().".html";
+       open(TEMP, ">$temp");
+       print TEMP $_[0];
+       close(TEMP);
+       open(OUT, ($lynx ? "$lynx -dump $temp" : "$h2 $temp")." 2>/dev/null |");
+       while(<OUT>) {
+               if ($lynx && $_ =~ /^\s*References\s*$/) {
+                       # Start of Lynx references output
+                       $gotrefs++;
+                       }
+               elsif ($lynx && $gotrefs &&
+                      $_ =~ /^\s*(\d+)\.\s+(http|https|ftp|mailto)/) {
+                       # Skip this URL reference line
+                       }
+               else {
+                       $text .= $_;
+                       }
+               }
+       close(OUT);
+       unlink($temp);
+       return $text;
+       }
+else {
+       # Do conversion manually :(
+       local $html = $_[0];
+       $html =~ s/\s+/ /g;
+       $html =~ s/<p>/\n\n/gi;
+       $html =~ s/<br>/\n/gi;
+       $html =~ s/<[^>]+>//g;
+       $html = &entities_to_ascii($html);
+       return $html;
+       }
+}
+
+# folder_select(&folders, selected-folder, name, [extra-options], [by-id])
+# Returns HTML for selecting a folder
+sub folder_select
+{
+local $sel = "<select name=$_[2]>\n";
+$sel .= $_[3];
+local $f;
+foreach $f (@{$_[0]}) {
+       next if ($f->{'hide'} && $f ne $_[1]);
+       $sel .= sprintf "<option value=%s %s>%s\n",
+               $_[4] ? &folder_name($f) : $f->{'index'},
+               $f eq $_[1] ? "selected" : "",
+               $f->{'name'};
+       }
+$sel .= "</select>\n";
+return $sel;
+}
+
+# folder_size(&folder, ...)
+# Sets the 'size' field of one or more folders, and returns the total
+sub folder_size
+{
+local ($f, $total);
+foreach $f (@_) {
+       if ($f->{'type'} == 0) {
+               # Single mail file - size is easy
+               local @st = stat($f->{'file'});
+               $f->{'size'} = $st[7];
+               }
+       elsif ($f->{'type'} == 1) {
+               # Maildir folder size is that of all files in it
+               $f->{'size'} = &recursive_disk_usage($f->{'file'});
+               }
+       elsif ($f->{'type'} == 3) {
+               # MH folder size is that of all mail files
+               local $mf;
+               $f->{'size'} = 0;
+               opendir(MHDIR, $f->{'file'});
+               while($mf = readdir(MHDIR)) {
+                       next if ($mf eq "." || $mf eq "..");
+                       local @st = stat("$f->{'file'}/$mf");
+                       $f->{'size'} += $st[7];
+                       }
+               closedir(MHDIR);
+               }
+       elsif ($f->{'type'} == 4) {
+               # Get size of IMAP folder
+               local ($ok, $h, $count) = &imap_login($f);
+               if ($ok) {
+                       $f->{'size'} = 0;
+                       local @rv = &imap_command($h,
+                               "FETCH 1:$count (RFC822.SIZE)");
+                       foreach my $r (@{$rv[1]}) {
+                               if ($r =~ /RFC822.SIZE\s+(\d+)/) {
+                                       $f->{'size'} += $1;
+                                       }
+                               }
+                       }
+               }
+       elsif ($f->{'type'} == 5) {
+               # Size of a combined folder is the size of all sub-folders
+               return &folder_size(@{$f->{'subfolders'}});
+               }
+       else {
+               # Cannot get size of a POP3 folder
+               $f->{'size'} = undef;
+               }
+       $total += $f->{'size'};
+       }
+return $total;
+}
+
+# parse_boolean(string)
+# Separates a string into a series of and/or separated values. Returns a
+# mode number (0=or, 1=and, 2=both) and a list of words
+sub parse_boolean
+{
+local @rv;
+local $str = $_[0];
+local $mode = -1;
+local $lastandor = 0;
+while($str =~ /^\s*"([^"]*)"(.*)$/ ||
+      $str =~ /^\s*"([^"]*)"(.*)$/ ||
+      $str =~ /^\s*(\S+)(.*)$/) {
+       local $word = $1;
+       $str = $2;
+       if (lc($word) eq "and") {
+               if ($mode < 0) { $mode = 1; }
+               elsif ($mode != 1) { $mode = 2; }
+               $lastandor = 1;
+               }
+       elsif (lc($word) eq "or") {
+               if ($mode < 0) { $mode = 0; }
+               elsif ($mode != 0) { $mode = 2; }
+               $lastandor = 1;
+               }
+       else {
+               if (!$lastandor && @rv) {
+                       $rv[$#rv] .= " ".$word;
+                       }
+               else {
+                       push(@rv, $word);
+                       }
+               $lastandor = 0;
+               }
+       }
+$mode = 0 if ($mode < 0);
+return ($mode, \@rv);
+}
+
+# recursive_files(dir, treat-dirs-as-folders)
+sub recursive_files
+{
+local ($f, @rv);
+opendir(DIR, $_[0]);
+local @files = readdir(DIR);
+closedir(DIR);
+foreach $f (@files) {
+       next if ($f eq "." || $f eq ".." || $f =~ /\.lock$/i ||
+                $f eq "cur" || $f eq "tmp" || $f eq "new" ||
+                $f =~ /^\.imap/i || $f eq ".customflags" ||
+                $f eq "dovecot-uidlist" || $f =~ /^courierimap/ ||
+                $f eq "maildirfolder" || $f eq "maildirsize" ||
+                $f eq "maildircache" || $f eq ".subscriptions" ||
+                 $f eq ".usermin-maildircache" || $f =~ /^dovecot\.index/ ||
+                $f =~ /\.webmintmp(\.\d+)$/);
+       local $p = "$_[0]/$f";
+       local $added = 0;
+       if ($_[1] || !-d $p || -d "$p/cur") {
+               push(@rv, $p);
+               $added = 1;
+               }
+       # If this directory wasn't a folder (or it it in Maildir format),
+       # search it too.
+       if (-d "$p/cur" || !$added) {
+               push(@rv, &recursive_files($p));
+               }
+       }
+return @rv;
+}
+
+# editable_mail(&mail)
+# Returns 0 if some mail message should not be editable (ie. internal folder)
+sub editable_mail
+{
+return $_[0]->{'header'}->{'subject'} !~ /DON'T DELETE THIS MESSAGE.*FOLDER INTERNAL DATA/;
+}
+
+# fix_cids(html, &attachments, url-prefix, &cid-list)
+# Replaces HTML like img src=cid:XXX with img src=detach.cgi?whatever
+sub fix_cids
+{
+local $rv = $_[0];
+$rv =~ s/(src="|href=")cid:([^"]+)(")/$1.&fix_cid($2,$_[1],$_[2],$_[3]).$3/gei;
+$rv =~ s/(src='|href=')cid:([^']+)(')/$1.&fix_cid($2,$_[1],$_[2],$_[3]).$3/gei;
+$rv =~ s/(src=|href=)cid:([^\s>]+)()/$1.&fix_cid($2,$_[1],$_[2],$_[3]).$3/gei;
+return $rv;
+}
+
+# fix_cid(cid, &attachments, url-prefix, &cid-list)
+sub fix_cid
+{
+local ($cont) = grep { $_->{'header'}->{'content-id'} eq $_[0] ||
+                      $_->{'header'}->{'content-id'} eq "<$_[0]>" } @{$_[1]};
+return "cid:$_[0]" if (!$cont);
+push(@{$_[3]}, $cont) if ($_[3]);
+return "$_[2]&attach=$cont->{'idx'}";
+}
+
+# quoted_message(&mail, quote-mode, sig, 0=any,1=text,2=html)
+# Returns the quoted text, html-flag and body attachment
+sub quoted_message
+{
+local ($mail, $qu, $sig, $bodymode) = @_;
+local $mode = $bodymode == 1 ? 1 :
+             $bodymode == 2 ? 2 :
+             defined(%userconfig) ? $userconfig{'view_html'} :
+                                    $config{'view_html'};
+local ($plainbody, $htmlbody) = &find_body($mail, $mode);
+local ($quote, $html_edit, $body);
+local $cfg = defined(%userconfig) ? \%userconfig : \%config;
+local @writers = &split_addresses($mail->{'header'}->{'from'});
+local $writer = &decode_mimewords($writers[0]->[1] || $writers[0]->[0]).
+               " wrote ..";
+local $tm;
+if ($cfg->{'reply_date'} &&
+    ($tm = &parse_mail_date($_[0]->{'header'}->{'date'}))) {
+       local $tmstr = &make_date($tm);
+       $writer = "On $tmstr $writer";
+       }
+local $qm = defined(%userconfig) ? $userconfig{'html_quote'}
+                                : $config{'html_quote'};
+if (($cfg->{'html_edit'} == 2 ||
+     $cfg->{'html_edit'} == 1 && $htmlbody) &&
+     $bodymode != 1) {
+       # Create quoted body HTML
+       if ($htmlbody) {
+               $body = $htmlbody;
+               $sig =~ s/\n/<br>\n/g;
+               if ($qu && $qm == 0) {
+                       # Quoted HTML as cite
+                       $quote = "$writer\n".
+                                "<blockquote type=cite>\n".
+                                &safe_html($htmlbody->{'data'}).
+                                "</blockquote>".$sig."<br>\n";
+                       }
+               elsif ($qu && $qm == 1) {
+                       # Quoted HTML below line
+                       $quote = "<br>$sig<hr>".
+                                "$writer<br>\n".
+                                &safe_html($htmlbody->{'data'});
+                       }
+               else {
+                       # Un-quoted HTML
+                       $quote = &safe_html($htmlbody->{'data'}).
+                                $sig."<br>\n";
+                       }
+               }
+       elsif ($plainbody) {
+               $body = $plainbody;
+               local $pd = $plainbody->{'data'};
+               $pd =~ s/^\s+//g;
+               $pd =~ s/\s+$//g;
+               if ($qu && $qm == 0) {
+                       # Quoted plain text as HTML as cite
+                       $quote = "$writer\n".
+                                "<blockquote type=cite>\n".
+                                "<pre>$pd</pre>".
+                                "</blockquote>".$sig."<br>\n";
+                       }
+               elsif ($qu && $qm == 1) {
+                       # Quoted plain text as HTML below line
+                       $quote = "<br>$sig<hr>".
+                                "$writer<br>\n".
+                                "<pre>$pd</pre><br>\n";
+                       }
+               else {
+                       # Un-quoted plain text as HTML
+                       $quote = "<pre>$pd</pre>".
+                                $sig."<br>\n";
+                       }
+               }
+       $html_edit = 1;
+       }
+else {
+       # Create quoted body text
+       if ($plainbody) {
+               $body = $plainbody;
+               $quote = $plainbody->{'data'};
+               }
+       elsif ($htmlbody) {
+               $body = $htmlbody;
+               $quote = &html_to_text($htmlbody->{'data'});
+               }
+       if ($quote && $qu) {
+               $quote = join("", map { "> $_\n" }
+                       &wrap_lines($quote, 70));
+               }
+       $quote = $writer."\n".$quote if ($quote && $qu);
+       $quote .= "$sig\n" if ($sig);
+       }
+return ($quote, $html_edit, $body);
+}
+
+# modification_time(&folder)
+# Returns the unix time on which this folder was last modified, or 0 if unknown
+sub modification_time
+{
+if ($_[0]->{'type'} == 0) {
+       # Modification time of file
+       local @st = stat($_[0]->{'file'});
+       return $st[9];
+       }
+elsif ($_[0]->{'type'} == 1) {
+       # Greatest modification time of cur/new directory
+       local @stcur = stat("$_[0]->{'file'}/cur");
+       local @stnew = stat("$_[0]->{'file'}/new");
+       return $stcur[9] > $stnew[9] ? $stcur[9] : $stnew[9];
+       }
+elsif ($_[0]->{'type'} == 2 || $_[0]->{'type'} == 4) {
+       # Cannot know for POP3 or IMAP folders
+       return 0;
+       }
+elsif ($_[0]->{'type'} == 3) {
+       # Modification time of MH folder
+       local @st = stat($_[0]->{'file'});
+       return $st[9];
+       }
+else {
+       # Huh?
+       return 0;
+       }
+}
+
+# requires_delivery_notification(&mail)
+sub requires_delivery_notification
+{
+return $_[0]->{'header'}->{'disposition-notification-to'} ||
+       $_[0]->{'header'}->{'read-reciept-to'};
+}
+
+# send_delivery_notification(&mail, [from-addr], manual)
+# Send an email containing delivery status information
+sub send_delivery_notification
+{
+local ($mail, $from) = @_;
+$from ||= $mail->{'header'}->{'to'};
+local $host = &get_display_hostname();
+local $to = &requires_delivery_notification($mail);
+local $product = &get_product_name();
+$product = ucfirst($product);
+local $version = &get_webmin_version();
+local ($taddr) = &split_addresses($mail->{'header'}->{'to'});
+local $disp = $manual ? "manual-action/MDN-sent-manually"
+                     : "automatic-action/MDN-sent-automatically";
+local $dsn = <<EOF;
+Reporting-UA: $host; $product $version
+Original-Recipient: rfc822;$taddr->[0]
+Final-Recipient: rfc822;$taddr->[0]
+Original-Message-ID: $mail->{'header'}->{'message-id'}
+Disposition: $disp; displayed
+EOF
+local $dmail = {
+       'headers' =>
+          [ [ 'From' => $from ],
+            [ 'To' => $to ],
+            [ 'Subject' => 'Delivery notification' ],
+            [ 'Content-type' => 'multipart/report; report-type=disposition-notification' ],
+            [ 'Content-Transfer-Encoding' => '7bit' ] ],
+       'attach' => [
+          { 'headers' => [ [ 'Content-type' => 'text/plain' ] ],
+            'data' => "This is a delivery status notification for the email sent to:\n$mail->{'header'}->{'to'}\non the date:\n$mail->{'header'}->{'date'}\nwith the subject:\n$mail->{'header'}->{'subject'}\n" },
+          { 'headers' => [ [ 'Content-type' =>
+                               'message/disposition-notification' ],
+                           [ 'Content-Transfer-Encoding' => '7bit' ] ],
+            'data' => $dsn }
+               ] };
+eval { local $main::errors_must_die = 1; &send_mail($dmail); };
+return $to;
+}
+
+# find_named_folder(name, &folders, [&cache])
+# Finds a folder by ID, filename, server name or displayed name
+sub find_named_folder
+{
+local $rv;
+if ($_[2] && exists($_[2]->{$_[0]})) {
+       # In cache
+       $rv = $_[2]->{$_[0]};
+       }
+else {
+       # Need to lookup
+       ($rv) = grep { $_->{'id'} eq $_[0] } @{$_[1]} if (!$rv);
+       ($rv) = grep { my $escfile = $_->{'file'};
+                      $escfile =~ s/\s/_/g;
+                      $escfile eq $_[0] ||
+                      $_->{'file'} eq $_[0] ||
+                      $_->{'server'} eq $_[0] } @{$_[1]} if (!$rv);
+       ($rv) = grep { my $escname = $_->{'name'};
+                      $escname =~ s/\s/_/g;
+                      $escname eq $_[0] ||
+                      $_->{'name'} eq $_[0] } @{$_[1]} if (!$rv);
+       $_[2]->{$_[0]} = $rv if ($_[2]);
+       }
+return $rv;
+}
+
+# folder_name(&folder)
+# Returns a unique identifier for a folder, based on it's filename or ID
+sub folder_name
+{
+my $rv = $_[0]->{'id'} ||
+         $_[0]->{'file'} ||
+         $_[0]->{'server'} ||
+         $_[0]->{'name'};
+$rv =~ s/\s/_/g;
+return $rv;
+}
+
+# set_folder_lastmodified(&folders)
+# Sets the last-modified time and sortable flag on all given folders
+sub set_folder_lastmodified
+{
+local ($folders) = @_;
+foreach my $folder (@$folders) {
+       if ($folder->{'type'} == 0 || $folder->{'type'} == 3) {
+               # For an mbox or MH folder, the last modified date is just that
+               # of the file or directory itself
+               local @st = stat($folder->{'file'});
+               $folder->{'lastchange'} = $st[9];
+               $folder->{'sortable'} = 1;
+               }
+       elsif ($folder->{'type'} == 1) {
+               # For a Maildir folder, the date is that of the newest
+               # sub-directory (cur, tmp or new)
+               $folder->{'lastchange'} = 0;
+               foreach my $sf ("cur", "tmp", "new") {
+                       local @st = stat("$folder->{'file'}/$sf");
+                       $folder->{'lastchange'} = $st[9]
+                               if ($st[9] > $folder->{'lastchange'});
+                       }
+               $folder->{'sortable'} = 1;
+               }
+       elsif ($folder->{'type'} == 6) {
+               # For a virtual folder, the date is that of the newest
+               # sub-folder, OR the folder file itself
+               local @st = stat($folder->{'folderfile'});
+               $folder->{'lastchange'} = $st[9];
+               my %done;
+               foreach my $m (@{$folder->{'members'}}) {
+                       if (!$done{$m->[0]}++) {
+                               &set_folder_lastmodified([ $m->[0] ]);
+                               $folder->{'lastchange'} =
+                                       $m->[0]->{'lastchange'}
+                                       if ($m->[0]->{'lastchange'} >
+                                           $folder->{'lastchange'});
+                               }
+                       }
+               $folder->{'sortable'} = 1;
+               }
+       else {
+               # For POP3 and IMAP folders, we don't know the last change
+               $folder->{'lastchange'} = undef;
+               $folder->{'sortable'} = 1;
+               }
+       }
+}
+
+# push_index(&mail, &folder, new-index)
+# Adjusts the index of some email to the new one, and stores the old
+# index and folder
+sub push_index
+{
+local ($mail, $folder, $newidx) = @_;
+unshift(@{$mail->{'subs'}}, [ $mail->{'subfolder'}, $mail->{'idx'} ]);
+$mail->{'subidx'} = $mail->{'subs'}->[0]->[1];
+$mail->{'subfolder'} = $folder;
+$mail->{'idx'} = $newidx;
+}
+
+# pop_index(&mail)
+# Removes the stored sub-folder and index, and restores the original index.
+# Returns the original sub-folder and index.
+sub pop_index
+{
+local ($mail) = @_;
+local $old = shift(@{$mail->{'subs'}});
+$mail->{'subidx'} = $mail->{'subs'}->[0]->[1];
+local @rv = ( $mail->{'subfolder'}, $mail->{'idx'} );
+$mail->{'subfolder'} = $old->[0];
+$mail->{'idx'} = $old->[1];
+return @rv;
+}
+
+# mail_preview(&mail)
+# Returns a short text preview of a message body
+sub mail_preview
+{
+local ($textbody, $htmlbody, $body) = &find_body($_[0], 0);
+local $data = $body->{'data'};
+$data =~ s/\r?\n/ /g;
+$data = substr($data, 0, 100);
+if ($data =~ /\S/) {
+       return $data;
+       }
+return undef;
+}
+
+# open_dbm_db(&hash, file, mode)
+# Attempts to open a DBM, first using SDBM_File, and then NDBM_File
+sub open_dbm_db
+{
+local ($hash, $file, $mode) = @_;
+eval "use SDBM_File";
+dbmopen(%$hash, $file, $mode);
+eval { $hash->{'1111111111'} = 'foo bar' };
+if ($@) {
+       dbmclose(%$hash);
+       eval "use NDBM_File";
+       dbmopen(%$hash, $file, $mode);
+       }
+}
+
+1;
+
diff --git a/mailboxes/images/attach.gif b/mailboxes/images/attach.gif
new file mode 100644 (file)
index 0000000..8cba6f2
Binary files /dev/null and b/mailboxes/images/attach.gif differ
diff --git a/mailboxes/images/boxes.gif b/mailboxes/images/boxes.gif
new file mode 100644 (file)
index 0000000..7416f19
Binary files /dev/null and b/mailboxes/images/boxes.gif differ
diff --git a/mailboxes/images/error.gif b/mailboxes/images/error.gif
new file mode 100644 (file)
index 0000000..df4546a
Binary files /dev/null and b/mailboxes/images/error.gif differ
diff --git a/mailboxes/images/icon.gif b/mailboxes/images/icon.gif
new file mode 100644 (file)
index 0000000..7416f19
Binary files /dev/null and b/mailboxes/images/icon.gif differ
diff --git a/mailboxes/images/p1.gif b/mailboxes/images/p1.gif
new file mode 100644 (file)
index 0000000..5fd162f
Binary files /dev/null and b/mailboxes/images/p1.gif differ
diff --git a/mailboxes/images/p2.gif b/mailboxes/images/p2.gif
new file mode 100644 (file)
index 0000000..010cab5
Binary files /dev/null and b/mailboxes/images/p2.gif differ
diff --git a/mailboxes/images/read.gif b/mailboxes/images/read.gif
new file mode 100644 (file)
index 0000000..fb48928
Binary files /dev/null and b/mailboxes/images/read.gif differ
diff --git a/mailboxes/images/smallicon.gif b/mailboxes/images/smallicon.gif
new file mode 100644 (file)
index 0000000..de0ee2d
Binary files /dev/null and b/mailboxes/images/smallicon.gif differ
diff --git a/mailboxes/images/special.gif b/mailboxes/images/special.gif
new file mode 100644 (file)
index 0000000..2bd81f6
Binary files /dev/null and b/mailboxes/images/special.gif differ
diff --git a/mailboxes/index.cgi b/mailboxes/index.cgi
new file mode 100755 (executable)
index 0000000..bd445a1
--- /dev/null
@@ -0,0 +1,100 @@
+#!/usr/local/bin/perl
+# index.cgi
+# Display all users on the system
+
+require './mailboxes-lib.pl';
+
+if ($config{'mail_system'} == 3) {
+       # Need to detect mail server
+       $ms = &detect_mail_system();
+       if ($ms == 3) {
+               &ui_print_header(undef, $text{'index_title'}, "", undef, 1, 1);
+               &ui_print_endpage(&text('index_esystem',
+                                       "../config.cgi?$module_name"));
+               }
+       else {
+               $config{'mail_system'} = $ms;
+               &save_module_config();
+               }
+       }
+elsif (!$config{'send_mode'} || !$config{'auto'}) {
+       # Make sure mail system is valid
+       local ($ms) = grep { $_->[1] == $config{'mail_system'} }
+                          @mail_system_modules;
+       if (!&check_mail_system($ms)) {
+               &ui_print_header(undef, $text{'index_title'}, "", undef, 1, 1);
+               &ui_print_endpage(&text('index_esystem2',
+                                       "../config.cgi?$module_name"));
+               }
+       }
+
+# Make sure mail system is running
+$err = &test_mail_system();
+if ($err) {
+       &ui_print_header(undef, $text{'index_title'}, "", undef, 1, 1);
+       &ui_print_endpage(&text('index_esystem3',
+                               "../config.cgi?$module_name", $err));
+       }
+
+# Show main page header
+&ui_print_header(undef, $text{'index_title'}, "", undef, 1, 1, 0,
+                undef, undef, undef,
+                $text{'index_system'.$config{'mail_system'}});
+
+# Check for Perl modules for SMTP authentication
+if ($config{'smtp_user'}) {
+       local @needed = ( "Authen::SASL" );
+       local $smode = $config{'smtp_auth'} || "Cram-MD5";
+       $smode = uc($smode);
+       $smode =~ s/-/_/g;
+       push(@needed, "Authen::SASL::Perl::$smode");
+       foreach $n (@needed) {
+               eval "use $n";
+               if ($@) {
+                       &ui_print_endpage(
+                               "<p>".&text('index_eperl', "<tt>$n</tt>",
+                                 "/cpan/download.cgi?source=3&cpan=$n&mode=2&".
+                                 "return=/$module_name/&returndesc=".
+                                 &urlize($text{'index_return'}))."<p>\n".
+                               "$text{'index_eperl2'}\n".
+                               "<pre>$@</pre>\n");
+                       }
+               }
+       }
+
+# Build a list of all available users
+@users = &list_mail_users($config{'max_records'}, \&can_user);
+
+$form = 0;
+if (!@users) {
+       # No users!
+       print "<b>$text{'index_none'}</b> <p>\n";
+       }
+elsif ($config{'max_records'} && @users > $config{'max_records'}) {
+       # Show input for searching for a user
+       print $text{'index_toomany'},"<p>\n";
+       print &ui_form_start("find.cgi");
+       print &ui_submit($text{'index_find'}),"\n";
+       print &ui_select("match", undef, [ [ "0", $text{'index_equals'} ],
+                                          [ "1", $text{'index_contains'} ] ]),"\n";
+       print &ui_user_textbox("user"),"\n";
+       print &ui_form_end();
+       $form++;
+       }
+else {
+       # Show using selected mode and sort
+       &show_users_table(\@users, $config{'show_mail'});
+       }
+
+if (&allowed_directory()) {
+       # Show form to view any mail file
+       print "<p>\n";
+       print &ui_form_start("list_mail.cgi");
+       print &ui_submit($text{'index_file'}),"\n";
+       print &ui_textbox("user", undef, 40),"\n",
+             &file_chooser_button("user", $form),"<br>\n";
+       print &ui_form_end();
+       }
+
+&ui_print_footer("/", $text{'index'});
+
diff --git a/mailboxes/lang/ca b/mailboxes/lang/ca
new file mode 100644 (file)
index 0000000..95e6b8a
--- /dev/null
@@ -0,0 +1,383 @@
+index_title=Lectura del Correu d'Usuaris
+index_none=NO tens permís per llegir el correu de cap usuari d'aquest sistema.
+index_header=Bústies d'usuaris
+index_empty=No hi ha correu
+index_return=a la llista d'usuaris
+index_esystem=No s'ha detectat al sistema cap dels servidors de correu suportats (Qmail, Postfix i Sendmail). Hauràs d'ajustar la <a href='$1'>configuració del mòdul</a> per configurar manualment el servidor de correu i possiblement els camins del correu.
+index_esystem2=No s'ha trobat al sistema el servidor de correu establert a la <a href='$1'>configuració del mòdul</a>. Hauràs de modificar la configuració per que faci servir el servidor correcte.
+index_esystem3=S'ha produït un error en contactar el sistema de correu establert a la <a href='$1'>configuració del mòdul</a>: $2.
+index_system2=Servidor de correu: Qmail
+index_system1=Servidor de correu: Sendmail
+index_system0=Servidor de correu: Postfix
+index_toomany=Hi ha massa usuaris al sistema com per mostrar-los en una sola pàgina.
+index_find=Busca usuaris tals que el nom d'usuari
+index_equals=sigui igual que
+index_contains=contingui
+index_eperl=El mòdul Perl $1 necessari per al mode d'autenticació SMTP seleccionat no està instal·lat o està mancat d'un mòdul dependent. <a href='$2'>Fes clic aquí</a> per instal·lar-lo ara.
+index_file=Llegeix el Correu del Fitxer:
+index_nousers=No s'ha trobat cap usuari!
+index_nousersmail=No s'ha trobat cap usuari amb correu.
+
+mail_title=Correu de l'Usuari
+mail_from=De
+mail_date=Data
+mail_subject=Tema
+mail_to=A
+mail_cc=Cc
+mail_bcc=Bcc
+mail_pri=Prioritat
+mail_highest=Altíssima
+mail_high=Alta
+mail_normal=Normal
+mail_low=Baixa
+mail_lowest=Baixíssima
+mail_for=A $1
+mail_for2=Per a l'usuari $1
+mail_sent=A la llista de correu enviat
+mail_size=Mida
+mail_level=Puntuació
+mail_delete=Suprimeix
+mail_compose=Redacta
+mail_open=Obre
+mail_return=a la bústia de l'usuari
+mail_pos=Missatges $1 a $2 de $3 a $4
+mail_none=Aquest usuari no té cap missatge a $1
+mail_ecannot=No tens permís per llegir el correu d'aquest usuari
+mail_all=Selecciona-ho tot.
+mail_invert=Inverteix la selecció.
+mail_nosort=Reinicia l'ordenació.
+mail_search=Busca missatges tals que
+mail_body=El cos
+mail_match=coincideixi amb
+mail_ok=Busca
+mail_nonefrom=Cap
+mail_mark=Marca com:
+mail_mark0=No llegit
+mail_mark1=Llegit
+mail_mark2=Especial
+mail_forward=Reenvia
+mail_move=Desplaça a:
+mail_copy=Copia a:
+mail_rfc=Des de la línia
+mail_eexists=El missatge ja no existeix!
+mail_fchange=Canvia
+mail_indexlink=Torna a la bústia
+mail_deleteall=Suprimeix-los Tots
+mail_black=Denega els Remitents
+mail_white=Permet els Remitents
+mail_efile=El fitxer de correu no existeix
+mail_fromsrch=El mateix remitent...
+mail_subsrch=El mateix assumpte...
+mail_tosrch=El mateix destinatari...
+mail_unknown=Desconegut/da
+
+mail_sign=Signa amb la clau
+mail_nosign=&lt;No el signis&gt;
+mail_crypt=Xifra per a
+mail_nocrypt=&lt;No el xifris&gt;
+mail_samecrypt=&lt;Claus de les adreces de destinació&gt;
+mail_addresses=Gestiona la Llibreta d'Adreces
+mail_folders=Gestiona les Carpetes
+mail_err=S'ha produït un error en llistar el correu d'aquesta carpeta: $1
+mail_loginheader=Entrada al servidor POP3
+mail_loginheader2=Entrada al servidor IMAP
+mail_logindesc=Has d'introduir un usuari i una contrasenya per accedir al correu<br>d'entrada del servidor de correu $1.
+mail_loginuser=Usuari
+mail_loginpass=Contrasenya
+mail_loginmailbox=Bústia IMAP
+mail_login=Entrada
+mail_reset=Neteja
+mail_logout=Canvia l'entrada POP3
+mail_logout2=Canvia l'entrada IMAP
+mail_sig=Edita la Signatura
+mail_jump=Salta a la pàgina:
+mail_of=de
+mail_replyto=Respon a
+mail_folder=Carpeta
+mail_delall=Suprimeix-ho tot
+mail_deltrash=Buida la Paperera
+mail_search2=Busca:
+mail_search3=Busca amb la puntuació de sobre:
+mail_advanced=Recerca Avançada
+mail_return2=al Correu de l'Usuari
+mail_esystem=S'ha produït un error en contactar amb el sistema de correu: $1. Això ha de ser corregit per l'administrador del sistema.
+
+view_title=Lectura del Correu
+view_desc=Missatge $1 a $2
+view_desc2=Missatge $1 per a l'usuari $2
+view_desc3=Missatge $1
+view_sent=Missatge $1 al la llista de correu enviat
+view_qdesc=Missatge $1 en cua
+view_headers=Capçaleres de correu
+view_body=Text del missatge
+view_allheaders=Mostra totes les capçaleres
+view_noheaders=Mostra les capçaleres bàsiques
+view_attach=Adjuncions
+view_reply=Respon
+view_reply2=Respon a tots
+view_enew=Edita coma nou
+view_forward=Reenvia
+view_delete=Suprimeix
+view_print=Imprimeix
+view_strip=Treu les Adjuncions
+view_ecannot=No tens permís per llegir el correu d'aquest usuari
+view_mark=Marca com:
+view_mark0=No llegit
+view_mark1=Llegit
+view_mark2=Especial
+view_return=al correu original
+view_sub=Correu Adjunt
+view_egone=Aquest missatge ja no existeix
+view_eugone=Aquest usuari no existeix
+
+view_gnupg=Verificació de la signatura GnuPG
+view_gnupg_0=La signatura de $1 és vàlida.
+view_gnupg_1=La signatura de $1 és vàlida, però no s'ha pogut establir la cadena de fiabilitat.
+view_gnupg_2=La signatura de $1 <b>NO</b> és vàlida.
+view_gnupg_3=L'ID de clau $1 no és a la teva llista, així que no es pot verificar la signatura.
+view_gnupg_4=No he pogut verificar la signatura: $1
+view_crypt=Desxifratge de correu GnuPG
+view_crypt_1=El missatge està xifrat, però el suport GnuPG no està instal·lat.
+view_crypt_2=No he pogut desxifrar el missatge: $1
+view_crypt_3=El correu s'ha desxifrat correctament.
+view_crypt_4=La part xifrada del missatge s'ha desxifrat correctament.
+view_recv=<a href='$2'>Obtingues l'ID de clau $1 del servidor de claus</a>.
+view_folder=Torna a la bústia
+view_detach=Separa el fitxer:
+view_dall=&lt;Tots els fitxers&gt;
+view_dir=al fitxer o directori del servidor:
+view_black=Denega el Remitent
+view_white=Permet el Remitent
+view_razor=Informa com a Spam
+view_ham=Informa com a Ham
+view_razordel=Informa i Suprimeix Spam
+view_dstatus=L'estat del lliurament ha fallat
+view_dstatusok=Estat de lliurament correcte
+view_final-recipient=Destinatari final
+view_diagnostic-code=Motiu de l'error
+view_remote-mta=Servidor de correu remot
+view_reporting-mta=Servidor de correu d'informes
+view_astext=Mostra com a text
+view_ashtml=Mostra com a HTML
+view_raw=Mostra el missatge tal com és
+
+compose_title=Redacció de Correu
+
+reply_title=Resposta de Correu
+forward_title=Reenviament de Correu
+enew_title=Edició de Correu
+reply_headers=Capçaleres de correu
+reply_attach=Adjuncions reenviades
+reply_mailforward=Missatges reenviats
+reply_attach2=Adjuncions de la part del client i del servidor
+reply_attach3=Adjuncions pujades
+reply_send=Envia el Correu
+reply_ecannot=No tens permís per enviar correu amb aquest usuari
+reply_body=Text del missatge
+reply_errc=No he pogut copiar el correu
+reply_errm=No he pogut desplaçar el correu
+reply_return=a la redacció del correu
+reply_efwdnone=No existeix cap dels missatges reenviats
+
+reply_spell=Comprova l'ortografia
+reply_draft=Desa com a Esborrany
+
+send_err=No he pogut enviar el correu
+send_eto=Hi falta l'adreça To
+send_efrom=Hi falta l'adreça From
+send_esubject=Hi falta l'asssumpte del correu
+send_title=Correu Enviat
+send_ok=Correu enviat correctament a to $1
+send_sending=Enviant el correu a $1...
+send_ecannot=No tens permís per enviar correu amb aquest usuari
+send_esmtp=L'ordre SMTP $1 ha fallat: $2
+send_eattach=Les adjuncions no poden fer més de $1 Kb.
+send_eperms=L'usuari $1 no pot llegir $2
+send_eperms2=No tens permís per enviar el fitxer $1
+send_epath=L'executable de Sendmail $1 no existeix.
+send_efile=No he pogut llegir l'adjunció $1: $2
+send_done=...fet.
+
+send_epass=No pots signar cap missatge perquè la teva frase de contrasenya encara no està configurada al mòdul de GnuPG.
+send_esign=No he pogut signar el missatge: $1
+send_ekey=No he pogut trobar la clau per a l'adreça de correu $1
+send_ecrypt=No he pogut xifrar el missatge: $1
+send_eword=Paraula '$1' incorrecta
+send_eword2=Paraula '$1' incorrecta - possible correcció: $2
+send_eline=A la línia $1:
+send_espell=S'han trobat els error ortogràfics següents al missatge...
+send_draft=El correu per a $1 s'ha desat a la carpeta d'esborranys.
+send_drafting=Desant el correu per a $1 a la carpeta d'esborranys...
+send_eattachsize=L'adjunció de correu excedeix la mida màxima permesa de $1 bytes
+
+delete_title=Supressió de Correu
+delete_rusure=Segur que vols suprimir les $1 missatges seleccionats de $2? Això pot trigar una mica si el fitxer de correu és gros. No es podrà realitzar cap altra acció fins que no s'hagi acabat la supressió.
+delete_rusure2=Segur que voleu suprimir aquest missatge de $1? Això pot trigar una mica si el fitxer de correu és gros. No es podrà realitzar cap altra acció fins que no s'hagi acabat la supressió.
+delete_ok=Suprimeix Ara
+delete_ecannot=No tens permís per suprimir correus d'aquest usuari
+delete_enone=No has seleccionat cap correu per suprimir
+delete_emnone=No has seleccionat cap correu per marcar
+delete_efnone=No has seleccionat cap correu per reenviar
+delete_ebnone=No has seleccionat cap correu per denegar
+delete_ewnone=No has seleccionat cap correu per permetre
+delete_ernone=No has seleccionat cap correu per informar-lo com a spam
+delete_ehnone=No has seleccionat cap correu per informar-lo com a ham
+delete_emoveuser=L'usuari per desplaçar-hi el correu no existeix
+delete_ecopyuser=L'usuari per copiar-hi el correu no existeix
+delete_emovecannot=No tens permís per desplaçar els correus a l'usuari especificat
+delete_ecopycannot=No tens permís per copiar els correus a l'usuari especificat
+delete_emovenone=No has seleccionat cap correu per desplaçar
+delete_ecopynone=No has seleccionat cap correu per copiar
+delete_nobutton=No has fet clic a cap botó
+delete_ereport=No he pogut informar com a spam: $1
+delete_errc=No he pogut copiar el correu
+delete_errm=No he pogut desplaçar el correu
+
+confirm_title=Confirmació de Supressió
+confirm_warn=Segur que vols suprimir els $1 missatges seleccionats?
+confirm_warn2=Degut a la mida i el format de la bústia, això pot trigar una mica. No es podrà realitzar cap altra acció fins que no s'hagi acabat la supressió.
+confirm_warn3=Segur que vols suprimir aquest missatge?
+confirm_warn4=No es podrà realitzar cap altra acció fins que no s'hagi acabat la supressió.
+confirm_ok=Suprimeix Ara
+confirm_warnall=Segur que vols suprimir tots els missatges d'aquesta carpeta?
+
+search_title=Resultats de la Recerca
+search_ecannot=No tens permís per buscar dins del correu d'aquest usuari
+search_ematch=Has d'introduir un text de recerca.
+search_escore=Hi falta la puntuació de spam o bé és invàlida
+search_efield=Has de seleccionar un tipus de recerca.
+search_ewhat=No has introduït cap text de recerca per a la fila $1
+search_enone=No has introduït cap criteri de recerca
+search_none=No s'ha trobat cap missatge.
+search_results2=$1 missatges de correu que coincideixen amb $2
+search_results3=$1 missatges de correu que no coincideixen amb $2
+search_results4=$1 missatges de correu que coincideixen amb la teva recerca
+search_results5=$1 missatges de correu on $2 coincideix amb $3
+search_msg2=Resultats de la recerca per a $1
+search_msg4=Resultats de la recerca
+search_msg5=Resultats de la recerca de spam amb puntuació $1
+search_local=A les carpetes locals
+search_all=A totes les carpetes
+search_limit=(dels darrers $1 missatges)
+search_status=Amb estat
+search_allstatus=Qualsevol
+search_onestatus=Només l'estat
+search_latest=Missatges a buscar
+search_nolatest=Tots els de la carpeta
+search_latestnum=Només els darrers
+search_elatest=Hi falta el nombre de missatges a buscar  o bé és invàlid
+search_withstatus=, amb estat $1
+
+folder_inbox=Entrada
+folder_sent=Enviat
+folder_drafts=Drafts
+folder_trash=Trash
+
+detach_err=No he pogut separar el fitxer
+detach_edir=No has introduït cap fitxer o directori per desar
+detach_eopen=No he pogut obrir $1: $2
+detach_ewrite=No he pogut gravar a $1: $2
+detach_title=Alliberament de Fitxer
+detach_ok=He gravat l'adjunció al fitxer del servidor $1 ($2).
+
+sform_title=Recerca Avançada
+sform_and=Busca missatges que coincideixin amb els criteris de recerca de sota...
+sform_or=Busca missatges que coincideixin amb qualsevol dels criteris de recerca de sota...
+sform_neg0=conté
+sform_neg1=no conté
+sform_ok=Busca Ara
+sform_folder=a les carpetes
+sform_all=&lt;Totes les carpetes&gt;
+sform_local=&lt;Carpetes locals&gt;
+sform_where=Tal que
+sform_text=el text
+sform_from=la capçalera From:
+sform_subject=la capçalera Subject:
+sform_to=la capçalera To:
+sform_cc=la capçalera Cc:
+sform_bcc=la capçalera Bcc:
+sform_date=la capçalera Date:
+sform_body=el cos del missatge
+sform_headers=qualsevol capçalera
+sform_size=la mida del missatge
+sform_return=al formulari de recerca avançada
+
+find_enone=No s'ha trobat cap usuari que coincideixi amb la teva recerca
+find_title=Resultats de la Recerca
+find_results=Usuaris que coincideixin amb la recerca per a $1...
+find_user=Usuari
+find_real=Nom real
+find_group=Grup
+find_home=Director i arrel
+find_size=Mida del correu
+find_incount=Correus
+find_sentcount=Enviats
+find_fcount=Carpetes
+find_in=$1 a $2
+
+acl_none=Cap
+acl_same=Usuari amb el mateix nom
+acl_all=Tots
+acl_read=Usuaris el correu dels quals es pot llegir
+acl_users=Només els usuaris
+acl_userse=Tots excepte els usuaris
+acl_usersg=Membres dels grups
+acl_from=Adreces From permissibles
+acl_any=Qualsevol adreça
+acl_fdoms=Dominis @ de bústies
+acl_faddrs=Adreces llistades
+acl_fdom=Qualsevol domini @ d'adreça
+acl_fromname=Nom real per a l'adreça From
+acl_apath=Limita els fitxers i el programa al directori
+acl_attach=Mida màxima total de les adjuncions
+acl_unlimited=Il·limitada
+acl_sent=Emmagatzema el correu enviat a la bústia
+acl_canattach=Pot adjuntar fitxers del servidor
+acl_candetach=Pot separar fitxers al servidor
+acl_usersm=Usuaris que coincideixin amb
+acl_asame=Igual que el nom d'usuari
+acl_usersu=Amb UID en el rang
+acl_sec=Inclou grups secundaris
+acl_dir=Pot llegir els fitxer de correu del directori
+acl_dirauto=Decideix automàticament (qualsevol lloc si tots els usuaris són visibles, enlloc altrament)
+
+log_delmail=He suprimit $1 missatges de $2
+log_movemail=He desplaçat $1 missatges de $2 a $3
+log_copymail=He copiat $1 missatges de $2 a $3
+log_send=He enviat el correu a $1
+log_read=He llegit el correu de $1
+
+emodified=Aquesta carpeta s'ha modificat des de la darrera visualització! Torna a la <a href='$1'>llista de correu</a> i torna-ho a provar.
+
+razor_title=Informant com a Spam
+razor_title2=Informant com a Ham
+razor_report=Informant aquest missatge a Razor i altres bases de dades de blocatge de spam de SpamAssassin...
+razor_report2=Informant dels missatges seleccionats a Razor i altres bases de dades SpamAssassin de blocatge de spam...
+razor_report3=Desinformant els missatges seleccionats a Razor i altres bases de dades SpamAssassin de blocatge de spam...
+razor_done=... fet.
+razor_err=... ha fallat! Mira el missatge d'error de sobre per saber-ne el motiu.
+razor_deleted=...fet, i també he suprimit el missatge.
+
+ham_title=Informant com a Ham
+ham_report=Informant aquest missatges com a no spam a Razor i altres base de dades SpamAssassin...
+
+black_title=Denegant el Remitent
+black_done=He afegit l'adreça de correu $1 a la llista d'adreces denegades de SpamAssassin.
+black_already=L'adreça de correu $1 ja és a la llista d'adreces denegades de SpamAssassin.
+
+white_title=Permetent Remitent
+white_done=He afegit l'adreça de correu $1 a la llista d'adreces permeses de SpamAssassin.
+white_already=L'adreça de correu $1 ja és a la llista d'adreces permeses de SpamAssassin.
+
+ldap_emod=Falta el mòdul Perl $1 necessari per connectar amb LDAP
+ldap_econn=No he pogut connectar amb el servidor LDAP $1 port $2
+ldap_elogin=No he pogut lligar el servidor LDAP $1 com a $2: $3
+ldap_ehost=No hi ha cap servidor LDAP establert a la configuració del mòdul
+ldap_eport=No hi ha cap port de servidor vàlid LDAP establert a la configuració del mòdul
+ldap_euser=No hi ha cap entrada LDAP establerta a la configuració del mòdul
+ldap_ebase=No hi ha cap DN base establert a la configuració del mòdul
+
+delall_title=Suprimeix Tot el Correu
+delall_rusure=Segur que vols suprimir tot el correu de $1? $2 missatges amb un total de $3 seran suprimit per sempre més.
+delall_ok=Suprimeix-los Ara
+
diff --git a/mailboxes/lang/de b/mailboxes/lang/de
new file mode 100644 (file)
index 0000000..94fb639
--- /dev/null
@@ -0,0 +1,318 @@
+acl_all=Alle
+acl_any=Jede Adresse
+acl_apath=Beschr&#228;nke Dateien und Programme auf Verzeichnis
+acl_asame=Verwende Benutzernamen
+acl_attach=Maximale Gesamtdateianhanggr&#246;&#223;e
+acl_canattach=Darf Dateien vom Serverdateisystem anh&#228;ngen?
+acl_candetach=Darf Dateianh&#228;nge im Serverdateisystem speichern?
+acl_dir=Darf E-Mail-Dateien lesen in Verzeichnis
+acl_dirauto=Entscheide automatisch (&#252;berall, wenn alle Benutzer sichtbar sind, ansonsten nirgends)
+acl_faddrs=Aufgef&#252;hrte Adressen
+acl_fdom=Jede Adresse @ Domain
+acl_fdoms=Mailbox @ Domains
+acl_from=Adressen, die f&#252;r Absenderadressen (FROM) erlaubt sind
+acl_fromname=Realname f&#252;r die Absenderadresse (FROM)
+acl_none=Kein
+acl_read=Benutzer, deren Post gelesen werden kann
+acl_same=Benutzer mit dem gleichen Namen
+acl_sec=Sekund&#228;re Gruppen einbeziehen?
+acl_sent=Speichere versendete E-Mail in E-Mailbox
+acl_users=Nur diese Benutzer
+acl_userse=Alle, au&#223;er diese Benutzer
+acl_usersg=Mitglieder der Gruppen
+acl_usersm=Benutzer passend zu/auf
+acl_usersu=Mit UID im Bereich
+black_already=Die E-Mail-Adresse $1 befindet sich bereits auf der Liste der durch SpamAssassin abgewiesenen Adressen
+black_done=Die E-Mail-Adresse wurde der Liste der durch SpamAssassin abgewiesenen Adressen hinzugef&#252;gt.
+black_title=Absender verbieten
+compose_title=Schreibe E-Mail
+confirm_ok=L&#246;sche jetzt
+confirm_title=L&#246;schen best&#228;tigen
+confirm_warn=Sind Sie sicher, da&#223; Sie die ausgew&#228;hlten $1 E-Mails l&#246;schen wollen?
+confirm_warn2=Aufgrund der Gr&#246;&#223;e und des Formates Ihrer E-Mailbox wird dies ein wenig dauern. W&#228;hrend des L&#246;schvorganges sollten keine anderen Aktionen stattfinden. Bitte ein wenig Geduld ...
+confirm_warn3=Sind Sie sicher, da&#223; Sie diese E-Mail l&#246;schen wollen?
+confirm_warn4=W&#228;hrend des L&#246;schvorganges sollten keine anderen Aktionen stattfinden. Bitte ein wenig Geduld ...
+confirm_warnall=Sind Sie sicher, da&#223; Sie alle E-Mails in diesem Ordner l&#246;schen wollen?
+delete_ebnone=Es wurde keine E-Mail zum Ablehnen ausgew&#228;hlt
+delete_ecannot=Sie d&#252;rfen keine E-Mails von diesem Benutzer l&#246;schen
+delete_ecopycannot=Sie d&#252;rfen keine E-Mails zu dem ausgew&#228;hlten Benutzer kopieren
+delete_ecopynone=Es wurde keine E-Mail f&#252;r den Kopiervorgang ausgew&#228;hlt.
+delete_ecopyuser=Der Benutzer, zu dem Sie E-Mails kopieren wollen, existiert nicht
+delete_efnone=Es wurde keine E-Mail f&#252;r den Weiterleitungsvorgang ausgew&#228;hlt.
+delete_ehnone=Keine E-Mail wurde f&#252;r den Report als Nicht-Spam ausgew&#228;hlt
+delete_emnone=Es wurde keine E-Mail f&#252;r den Markierungsvorgang ausgew&#228;hlt.
+delete_emovecannot=Sie d&#252;rfen keine E-Mails zu dem ausgew&#228;hlten Benutzer verschieben
+delete_emovenone=Es wurde keine E-Mail f&#252;r den Verschiebevorgang ausgew&#228;hlt.
+delete_emoveuser=Der Benutzer, zu dem Sie E-Mails verschieben wollen, existiert nicht
+delete_enone=Es wurde keine E-Mail f&#252;r den L&#246;schvorgang ausgew&#228;hlt.
+delete_ereport=Konnte nicht als Spam melden : $1
+delete_ernone=Es wurde keine E-Mail f&#252;r den Spam-Report ausgew&#228;hlt
+delete_errc=Konte E-Mail nicht kopieren
+delete_errm=Konnte E-Mail nicht verschieben
+delete_ewnone=Keine E-Mail wurde f&#252;r die Whitelist ausgew&#228;hlt
+delete_nobutton=Es wurde kein Button angeklickt
+delete_ok=Jetzt l&#246;schen
+delete_rusure=Sind Sie sicher, da&#223; Sie die ausgew&#228;hlten $1 E-Mails aus/von $2 l&#246;schen wollen? Aufgrund der Gr&#246;&#223;e und des Formates dieser E-Mailbox wird dies ein wenig dauern. W&#228;hrend des L&#246;schvorganges sollten keine anderen Aktionen stattfinden. Bitte ein wenig Geduld ...
+delete_rusure2=Sind Sie sicher, da&#223; Sie diese E-Mail aus/von $1 l&#246;schen wollen? Aufgrund der Gr&#246;&#223;e und des Formates dieser E-Mailbox wird dies ein wenig dauern. W&#228;hrend des L&#246;schvorganges sollten keine anderen Aktionen stattfinden. Bitte ein wenig Geduld ...
+delete_title=E-Mail l&#246;schen
+detach_edir=Es wurden weder Datei noch ein Verzeichnis f&#252;r den Speichervorgang ausgew&#228;hlt.
+detach_eopen=Fehler beim &#214;ffnen von $1 : $2
+detach_err=Konnte Datei nicht abh&#228;ngen/l&#246;schen
+detach_ewrite=Fehler beim Schreibvorgang zu $1 : $2
+detach_ok=Der Dateianhang wurde auf dem Server in die folgende Datei gespeichert $1 ($2).
+detach_title=Dateianhang speichern
+emodified=Dieser Order wurde seit dem aktuellen Einlesen ver&#228;ndert! Kehren Sie zur <a href='$1'>Liste der E-Mails</a> zur&#252;ck und starten Sie erneut.
+enew_title=E-Mail bearbeiten
+find_enone=Es wurden keine Benutzer gefunden, die der Suchanfrage entsprechen
+find_group=Gruppe
+find_home=Heimatverzeichnis
+find_real=Realname
+find_results=Benutzer, die auf das Suchmuster $1 passen ...
+find_size=E-Mail-Gr&#246;&#223;e
+find_title=Suchergebnisse
+find_user=Benutzername
+folder_drafts=Entw&#252;rfe
+folder_inbox=Posteingang
+folder_sent=Versendete E-Mail
+folder_trash=Papierkorb
+forward_title=E-Mail weiterleiten
+ham_report=Diese Nachricht wird als Nicht-Spam an Razor und andere SpamAssassin-Datenbanken gemeldet ..
+ham_title=Als Nicht-Spam melden
+index_contains=beinhaltet
+index_empty=Keine E-Mail
+index_eperl=Das Perl-Modul $1, welches f&#252;r den ausgew&#228;hlten SMTP-Authentisierungsmodus n&#246;tig ist, ist nicht installiert oder es fehlt ein zur Ausf&#252;hrung abh&#228;ngiges Modul. <a href='$2'>Klicken Sie hier</a> um dieses zu installieren.
+index_equals=ist gleich
+index_esystem=Keiner der unterst&#252;tzten E-Mailserver (Qmail, Postfix und Sendmail) wurde auf Ihrem System gefunden. Sie m&#252;ssen die <a href='$1'>Modulkonfiguration</a> manuell anpassen und dort den E-Mailserver und die zugeh&#246;rigen Serverpfade eintragen.
+index_esystem2=Der in der <a href='$1'>Modulkonfiguration</a> gesetzte E-Mailserver wurde auf ihrem System nicht gefunden. Sie m&#252;ssen dort die Konfiguration so anpassen, da&#223; der richtige E-Mailserver benutzt wird.
+index_file=Lese E-Mail in Datei:
+index_find=Finde Benutzer deren Benutzername
+index_header=Benutzer-E-Mailboxen
+index_none=Sie d&#252;rfen die E-Mails der Benutzer auf diesem System nicht lesen.
+index_return=Benutzerliste
+index_system0=E-Mailserver: Postfix
+index_system1=E-Mailserver: Sendmail
+index_system2=E-Mailserver: Qmail
+index_title=Lese Benutzer-E-Mail
+index_toomany=Es gibt mehr Benutzer auf Ihrem System, als hier angezeigt werden k&#246;nnen.
+ldap_econn=Fehler beim Verbinden zum LDAP-Server $1 Port $2
+ldap_elogin=Konnte nicht zum LDAP-Server $1 als $2 verbinden : $3
+ldap_emod=Das fehlende Perl-Modul $1 wird ben&#246;tigt, um sich via LDAP zu verbinden
+log_copymail=Es wurden $1 E-Mails von $2 nach $3 kopiert
+log_delmail=Es wurden $1 E-Mails von $2 gel&#246;scht
+log_movemail=Es wurden $1 E-Mails von $2 nach $3 kopiert
+log_read=Lese E-Mail f&#252;r $1
+log_send=Es wurde eine E-Mail nach $1 versandt.
+mail_addresses=Adressbuch bearbeiten
+mail_advanced=Erweiterte Suche
+mail_all=Alle ausw&#228;hlen
+mail_bcc=Bcc
+mail_black=Absender blockieren
+mail_body=Textk&#246;rper
+mail_cc=Cc
+mail_compose=Schreiben
+mail_copy=Kopieren nach:
+mail_crypt=GnuPG-Verschl&#252;sselung f&#252;r:
+mail_date=Datum
+mail_delall=Alle l&#246;schen
+mail_delete=L&#246;schen
+mail_deleteall=Alles l&#246;schen
+mail_deltrash=Papierkorb leeren
+mail_ecannot=Sie d&#252;rfen die E-Mails von diesem Benutzer nicht lesen
+mail_eexists=Die E-Mail existiert nicht mehr. Eventuell wurde Sie in der Zwischenzeit via POP3 abgerufen oder anderweitig von einem berechtigtem Benutzer gel&#246;scht/verschoben.
+mail_efile=Die E-Mail-Datei existiert nicht
+mail_err=Beim Auflisten der E-Mails in diesem Ordner ist ein Fehler aufgetreten : $1
+mail_fchange=&#196;ndern
+mail_folder=Ordner
+mail_folders=Ordner bearbeiten
+mail_for=In $1
+mail_for2=F&#252;r Benutzer $1
+mail_forward=Weiterleiten
+mail_from=Von
+mail_fromsrch=Gleicher Absender ..
+mail_high=Hoch
+mail_highest=H&#246;chste
+mail_indexlink=Zur&#252;ck zur E-Mailbox
+mail_invert=Auswahl umkehren
+mail_jump=Springe zu Seite :
+mail_login=Login
+mail_logindesc=Sie m&#252;ssen Benutzername und Passwort eingeben,<br>um Ihren Posteingang auf dem E-Mailserver $1 einsehen zu k&#246;nnen.
+mail_loginheader=POP3-Server-Login
+mail_loginmailbox=IMAP E-Mailbox
+mail_loginpass=Passwort
+mail_loginuser=Benutzername
+mail_logout=&#196;ndere POP3-Login
+mail_logout2=&#196;ndere IMAP-Login
+mail_low=Niedrig
+mail_lowest=Niedrigste
+mail_mark=Markiere als:
+mail_mark0=Ungelesen
+mail_mark1=Gelesen
+mail_mark2=Spezial
+mail_match=passt auf
+mail_move=Verschieben nach:
+mail_nocrypt=&lt;Nicht verschl&#252;sseln&gt;
+mail_none=Dieser Benutzer hat keine E-Mails in $1
+mail_nonefrom=Kein
+mail_normal=Normal
+mail_nosign=&lt;Nicht signieren&gt;
+mail_of=von
+mail_ok=Suche
+mail_pos=E-Mails $1 bis $2 von $3 in $4
+mail_pri=Priorit&#228;t
+mail_replyto=Antworten an
+mail_reset=L&#246;schen
+mail_return=Benutzer-E-Mailbox
+mail_return2=Benutzer-E-Mail
+mail_rfc=From-Zeile
+mail_samecrypt=&lt;Schl&#252;ssel der Zieladressen&gt;
+mail_search=Finde E-Mails wo
+mail_search2=Suche nach:
+mail_sent=In &lt;Versendete E-Mail&lt;-Liste
+mail_sig=Bearbeite Signatur
+mail_sign=Unterschreibe mit GnuPG-Schl&#252;ssel
+mail_size=Gr&#246;&#223;e
+mail_subject=Betreff
+mail_subsrch=Gleicher Betreff ..
+mail_title=Benutzer-E-Mail
+mail_to=An
+mail_tosrch=Gleicher Empf&#228;nger ..
+mail_white=Erlaube Absender
+razor_deleted=.. erledigt. Die E-Mail wurde gel&#246;scht.
+razor_done=.. erledigt
+razor_err=.. gescheitert! Oben sehen Sie warum.
+razor_report=Diese Nachricht wird sowohl der Razor- als auch anderen durch SpamAssassin genutzten Anti-Spam-Datenbanken gemeldet.
+razor_title=Als Spam melden
+reply_attach=Weitergeleitete Anh&#228;nge
+reply_attach2=Client- und serverseitige Anh&#228;nge
+reply_body=E-Mail-Text
+reply_draft=Speichern als Entwurf
+reply_ecannot=Sie d&#252;rfen keine E-Mails im Namen dieses Benutzers versenden
+reply_errc=Konnte E-Mail nicht kopieren
+reply_errm=Konnte E-Mail nicht verschieben
+reply_headers=E-Mail-Header
+reply_mailforward=Weitergeleitete E-Mails
+reply_send=E-Mail versenden
+reply_spell=Auf Rechtschreibfehler pr&#252;fen?
+reply_title=Auf E-Mail antworten
+search_all=In allen Ordnern
+search_allstatus=Jede
+search_ecannot=Sie d&#252;rfen die E-Mail dieses Benutzers nicht durchsuchen
+search_efield=Sie m&#252;ssen eine Such-/Abfrageart angeben.
+search_elatest=Fehlende oder ung&#252;ltige Anzahl an E-Mails f&#252;r die Suche
+search_ematch=Sie m&#252;ssen einen Text f&#252;r die Suche/Abfrage angeben.
+search_enone=Sie haben keinerlei Suchkriterien angegeben
+search_ewhat=Sie haben keinen Text f&#252;r die Suche/Abfrage der Spalte $1 angeben.
+search_latest=Zu durchsuchende E-Mails
+search_latestnum=Nur die neuesten
+search_limit=(der letzten $1 E-Mails)
+search_local=In lokalen Ordnern
+search_nolatest=Alle im Ordner
+search_none=Es wurden keine E-Mails gefunden.
+search_onestatus=Nur Status
+search_results2=$1 E-Mails passen auf $2
+search_results3=$1 E-Mails passen nicht auf $2
+search_results4=$1 E-Mails passen auf Ihre Suchanfrage
+search_status=Mit Status
+search_title=Suchergebnisse
+search_withstatus=, mit Status $1
+send_draft=Die E-Mail an $1 wurde als Entwurf gespeichert.
+send_eattach=Die Dateianh&#228;nge d&#252;rfen zusammen nicht mehr als $1 KiB gro&#223; sein.
+send_eattachsize=Die Dateianh&#228;nge &#252;berschreiten die maximal erlaubte Gr&#246;&#223;e von $1 bytes.
+send_ecannot=Sie d&#252;rfen im Namen dieses Benutzers keine E-Mail versenden
+send_ecrypt=Konnte E-Mail nicht verschl&#252;sseln : $1
+send_efile=Konnte Dateianh&#228;nge nicht lesen $1 : $2
+send_efrom=Fehlende Absenderadresse (FROM)
+send_ekey=Konnte den Schl&#252;ssel f&#252;r die E-Mail-Adresse $1 nicht finden
+send_eline=In Zeile $1:
+send_epass=Sie k&#246;nnen diese E-Mail nicht signieren, da Sie noch keine Passphrase im GnuPG-Modul hinterlegt haben.
+send_epath=Das Sendmail-Binary $1 existiert nicht.
+send_eperms=Benutzer $1 kann $2 nicht lesen
+send_eperms2=Sie d&#252;rfen die Datei $1 nicht versenden
+send_err=Konnte E-Mail nicht versenden
+send_esign=Konnte E-Mail nicht signieren : $1
+send_esmtp=SMTP-Kommando $1 gescheitert : $2
+send_espell=Die folgenden Rechtschreibefehler wurden in Ihrer E-Mail gefunden ..
+send_eto=Fehlende Empf&#228;ngeradresse (TO)
+send_eword=Falsch geschriebenes Wort $1
+send_eword2=Falsch geschriebenes Wort $1 - M&#246;gliche Korrektur $2
+send_ok=E-Mail erfolgreich versandt an $1
+send_title=E-Mail versandt
+sform_all=&lt;Alle Ordner&gt;
+sform_and=Finde E-Mails bei denen folgende Kriterien zutreffen ...
+sform_body=E-Mail-Textk&#246;rper
+sform_cc=Cc:-Zeile
+sform_date=Datum:-Zeile
+sform_folder=in Ordner(n)
+sform_from=From:-Zeile
+sform_headers=Jegliche Header-Zeile
+sform_local=&lt;Lokale Ordner&gt;
+sform_neg0=beinhaltet
+sform_neg1=beinhaltet nicht
+sform_ok=Jetzt suchen
+sform_or=Finde E-Mails, wo irgendeine der folgenden Kritierien zutreffen ..
+sform_return=Erweitertes Suchformular
+sform_size=Nachrichtengr&#246;&#223;e
+sform_subject=Betreff:-Zeile
+sform_text=den Text
+sform_title=Erweiterte Suche
+sform_to=To:-Zeile
+sform_where=Wo
+view_allheaders=Zeige alle E-Mail-Header an
+view_ashtml=Zeige E-Mail als HTML an (Kann gef&#228;hrlich sein!)
+view_astext=Zeige E-Mail als Text an (Explizit empfohlen)
+view_attach=Dateianh&#228;nge
+view_black=Diesen Absender in die Blacklist aufnehmen!
+view_body=E-Mail-Text
+view_crypt=GnuPG-E-Mail-Entschl&#252;sselung
+view_crypt_1=Diese E-Mail ist verschl&#252;sselt, jedoch wurde GnuPG nicht gefunden oder es ist nicht richtig installiert.
+view_crypt_2=Konnte E-Mail nicht entschl&#252;sseln : $1
+view_crypt_3=E-Mail erfolgreich entschl&#252;sselt.
+view_crypt_4=Der verschl&#252;sselte Anteil dieser E-Mail wurde erfolgreich entschl&#252;sselt.
+view_dall=&lt;Alle Dateien&gt;
+view_delete=L&#246;schen
+view_desc=E-Mail $1 in $2
+view_desc2=E-Mail $1 des Benutzers $2
+view_desc3=E-Mails $1
+view_detach=Dateianhang speichern:
+view_diagnostic-code=Grund des Fehlers
+view_dir=&nbsp;in eine Serverdatei oder -Verzeichnis:
+view_dstatus=Status des gescheiterten Versands
+view_dstatusok=Erfolgreicher &#220;bertragungsstatus
+view_ecannot=Sie d&#252;rfen die E-Mails dieses Benutzers nicht lesen
+view_egone=Die E-Mail existiert nicht mehr. Eventuell wurde Sie in der Zwischenzeit via POP3 abgerufen oder anderweitig von einem berechtigtem Benutzer gel&#246;scht/verschoben.
+view_enew=Als neu bearbeiten
+view_final-recipient=Entg&#252;ltiger Empf&#228;nger
+view_folder=Zur&#252;ck zur E-Mailbox
+view_forward=Weiterleiten
+view_gnupg=GnuPG-Signatur-&#220;berpr&#252;fung
+view_gnupg_0=Die Signatur von $1 ist g&#252;ltig.
+view_gnupg_1=Die Signatur von $1 ist g&#252;ltig, aber es konnte kein Vertrauensstatus erkannt werden.
+view_gnupg_2=Die Signatur von $1 ist <b>NICHT</b> g&#252;ltig.
+view_gnupg_3=Die Schl&#252;ssel-ID $1 ist nicht in Ihrer Liste, ergo kann keine GnuPG-Signatur-&#220;berpr&#252;fung statfinden.
+view_gnupg_4=Konnte Signatur nicht &#252;berpr&#252;fen : $1
+view_ham=Als Nicht-Spam melden
+view_headers=E-Mail-Header
+view_mark=Markiere als:
+view_mark0=Ungelesen
+view_mark1=Gelesen
+view_mark2=Spezial
+view_noheaders=Einfache Headeranzeige
+view_print=Drucken
+view_qdesc=E-Mail $1 in Warteschlange
+view_raw=Zeige Nachricht im Rohformat an
+view_razor=Als Spam melden
+view_razordel=Spam-Report und L&#246;schen
+view_recv=<a href='$2'>Hole Schl&#252;ssel-ID $1 vom Keyserver</a>.
+view_remote-mta=Entfernter E-Mail-Server
+view_reply=Anworten
+view_reply2=Allen antworten
+view_reporting-mta=Berichtender E-Mail-Server
+view_return=Original-E-Mail
+view_sent=E-Mail $1 in "Versendete E-Mail"-Liste
+view_strip=Entferne Dateianh&#228;nge
+view_sub=Angeh&#228;ngte E-Mail(s)
+view_title=E-Mail lesen
+view_white=Erlaube Absender
+white_already=Die E-Mail-Adresse $1 befindet sich bereits in der Whitelist von SpamAssassin.
+white_done=Die E-Mail-Adresse $1 wurde der Whitelist von SpamAssassin hinzugef&#252;gt.
+white_title=Erlaube Absender
diff --git a/mailboxes/lang/en b/mailboxes/lang/en
new file mode 100644 (file)
index 0000000..bdb5b96
--- /dev/null
@@ -0,0 +1,383 @@
+index_title=Read User Mail
+index_none=You are not allowed to read email for any users on this system.
+index_header=User mailboxes
+index_empty=No mail
+index_return=user list
+index_esystem=None of the supported mail servers (Qmail, Postfix and Sendmail) were detected on your system. You will need to adjust the <a href='$1'>module configuration</a> to set the mail server and possibly mail paths manually.
+index_esystem2=The mail server set in the <a href='$1'>module configuration</a> was not found on your system. You will need to adjust the configuration to use the correct server.
+index_esystem3=An error occurred contacting the mail system set in the <a href='$1'>module configuration</a> : $2.
+index_system2=Mail server: Qmail
+index_system1=Mail server: Sendmail
+index_system0=Mail server: Postfix
+index_toomany=There are too many users on your system to display on one page.
+index_find=Find users where username
+index_equals=equals
+index_contains=contains
+index_eperl=The Perl module $1 needed for the selected SMTP authentication mode is not installed or is missing a dependent module. <a href='$2'>Click here</a> to install it now.
+index_file=Read Mail in File:
+index_nousers=No users were found!
+index_nousersmail=No users with email were found.
+
+mail_title=User Email
+mail_from=From
+mail_date=Date
+mail_subject=Subject
+mail_to=To
+mail_cc=Cc
+mail_bcc=Bcc
+mail_pri=Priority
+mail_highest=Highest
+mail_high=High
+mail_normal=Normal
+mail_low=Low
+mail_lowest=Lowest
+mail_for=In $1
+mail_for2=For user $1
+mail_sent=In sent mail list
+mail_size=Size
+mail_level=Score
+mail_delete=Delete
+mail_compose=Compose
+mail_open=Open
+mail_return=user mailbox
+mail_pos=Messages $1 to $2 of $3 in $4
+mail_none=This user has no messages in $1
+mail_ecannot=You are not allowed to read this user's email
+mail_all=Select all.
+mail_invert=Invert selection.
+mail_nosort=Reset sorting.
+mail_search=Find messages where
+mail_body=Body
+mail_match=matches
+mail_ok=Search
+mail_nonefrom=None
+mail_mark=Mark as:
+mail_mark0=Unread
+mail_mark1=Read
+mail_mark2=Special
+mail_forward=Forward
+mail_move=Move to:
+mail_copy=Copy to:
+mail_rfc=From line
+mail_eexists=Message no longer exists!
+mail_fchange=Change
+mail_indexlink=Return to mailbox
+mail_deleteall=Delete All
+mail_black=Deny Senders
+mail_white=Allow Senders
+mail_efile=Mail file does not exist
+mail_fromsrch=Same sender..
+mail_subsrch=Same subject..
+mail_tosrch=Same recipient..
+mail_unknown=Unknown
+
+mail_sign=Sign with key
+mail_nosign=&lt;Don't sign&gt;
+mail_crypt=Encrypt for
+mail_nocrypt=&lt;Don't encrypt&gt;
+mail_samecrypt=&lt;Keys from destination addresses&gt;
+mail_addresses=Manage Address Book
+mail_folders=Manage Folders
+mail_err=An error occurred listing mail in this folder : $1
+mail_loginheader=POP3 server login
+mail_loginheader2=IMAP server login
+mail_logindesc=You must enter a username and password to access mail<br>in your inbox on the mail server $1.
+mail_loginuser=Username
+mail_loginpass=Password
+mail_loginmailbox=IMAP mailbox
+mail_login=Login
+mail_reset=Clear
+mail_logout=Change POP3 login
+mail_logout2=Change IMAP login
+mail_sig=Edit Signature
+mail_jump=Jump to page :
+mail_of=of
+mail_replyto=Reply to
+mail_folder=Folder
+mail_delall=Delete All
+mail_deltrash=Empty Trash
+mail_search2=Search for:
+mail_search3=Find with score above:
+mail_advanced=Advanced Search
+mail_return2=User Email
+mail_esystem=An error occurred contacting the mail system : $1. This must be fixied by the system administrator.
+
+view_title=Read Email
+view_desc=Message $1 in $2
+view_desc2=Message $1 for user $2
+view_desc3=Message $1
+view_sent=Message $1 in sent mail list
+view_qdesc=Queued message $1
+view_headers=Mail headers
+view_body=Message text
+view_allheaders=View all headers
+view_noheaders=View basic headers
+view_attach=Attachments
+view_reply=Reply
+view_reply2=Reply to all
+view_enew=Edit as new
+view_forward=Forward
+view_delete=Delete
+view_print=Print
+view_strip=Remove Attachments
+view_ecannot=You are not allowed to read this user's email
+view_mark=Mark as:
+view_mark0=Unread
+view_mark1=Read
+view_mark2=Special
+view_return=original email
+view_sub=Attached Email
+view_egone=This message no longer exists
+view_eugone=This user does not exist
+
+view_gnupg=GnuPG signature verification
+view_gnupg_0=Signature by $1 is valid.
+view_gnupg_1=Signature by $1 is valid, but trust chain could not be established.
+view_gnupg_2=Signature by $1 is <b>NOT</b> valid.
+view_gnupg_3=Key ID $1 is not in your list, so signature could not be verified.
+view_gnupg_4=Failed to verify signature : $1
+view_crypt=GnuPG mail decryption
+view_crypt_1=Message is encrypted, but GnuPG support is not installed.
+view_crypt_2=Failed to decrypt message : $1
+view_crypt_3=Mail was successfully decrypted.
+view_crypt_4=Encrypted portion of message was successfully decrypted.
+view_recv=<a href='$2'>Fetch key ID $1 from keyserver</a>.
+view_folder=Return to mailbox
+view_detach=Detach file:
+view_dall=&lt;All files&gt;
+view_dir=to server file or directory:
+view_black=Deny Sender
+view_white=Allow Sender
+view_razor=Report As Spam
+view_ham=Report As Ham
+view_razordel=Report and Delete Spam
+view_dstatus=Failed delivery status
+view_dstatusok=Successful delivery status
+view_final-recipient=Final recipient
+view_diagnostic-code=Reason for failure
+view_remote-mta=Remote mail server
+view_reporting-mta=Reporting mail server
+view_astext=View as text
+view_ashtml=View as HTML
+view_raw=View raw message
+
+compose_title=Compose Email
+
+reply_title=Reply to Email
+forward_title=Forward Email
+enew_title=Edit Email
+reply_headers=Mail headers
+reply_attach=Forwarded attachments
+reply_mailforward=Forwarded messages
+reply_attach2=Client and server-side attachments
+reply_attach3=Uploaded attachments
+reply_send=Send Mail
+reply_ecannot=You are not allowed to send mail as this user
+reply_body=Message text
+reply_errc=Failed to copy mail
+reply_errm=Failed to move mail
+reply_return=compose mail
+reply_efwdnone=None of the forwarded messages exist
+
+reply_spell=Check for spelling errors?
+reply_draft=Save as Draft
+
+send_err=Failed to send mail
+send_eto=Missing To address
+send_efrom=Missing From address
+send_esubject=Missing email subject
+send_title=Mail Sent
+send_ok=Mail sent successfully to $1
+send_sending=Sending mail to $1 ..
+send_ecannot=You are not allowed to send mail as this user
+send_esmtp=SMTP command $1 failed : $2
+send_eattach=Attachments cannot total more that $1 kB in size.
+send_eperms=User $1 cannot read $2
+send_eperms2=You are not allowed to send file $1
+send_epath=Sendmail executable $1 does not exist.
+send_efile=Failed to read attachment $1 : $2
+send_done=.. done.
+
+send_epass=You cannot sign a message because your passphrase has not be setup yet in the GnuPG module.
+send_esign=Failed to sign message : $1
+send_ekey=Couldn't find key for email address $1
+send_ecrypt=Failed to encrypt message : $1
+send_eword=Misspelt word $1
+send_eword2=Misspelt word $1 - possible corrections $2
+send_eline=In line $1 :
+send_espell=The following spelling errors were found in your message ..
+send_draft=Mail to $1 saved in drafts folder.
+send_drafting=Saving mail to $1 in drafts folder ..
+send_eattachsize=The mail attachment exceeded the maximum allowed size of $1 bytes
+
+delete_title=Delete Mail
+delete_rusure=Are you sure you want to delete the $1 selected messages from $2? This may take some time for a large mail file. Until the deletion has finished, no other action should be performed.
+delete_rusure2=Are you sure you want to delete this message from $1? This may take some time for a large mail file. Until the deletion has finished, no other action should be performed.
+delete_ok=Delete Now
+delete_ecannot=You are now allowed to delete mail from this user
+delete_enone=No mail selected to delete
+delete_emnone=No mail selected to mark
+delete_efnone=No mail selected to forward
+delete_ebnone=No mail selected to deny
+delete_ewnone=No mail selected to allow
+delete_ernone=No mail selected to report as spam
+delete_ehnone=No mail selected to report as ham
+delete_emoveuser=User to move mail to does not exist
+delete_ecopyuser=User to copy mail to does not exist
+delete_emovecannot=You are not allowed to move mail to the specified user
+delete_ecopycannot=You are not allowed to copy mail to the specified user
+delete_emovenone=No mail selected to move
+delete_ecopynone=No mail selected to copy
+delete_nobutton=No button clicked
+delete_ereport=Failed to report as spam : $1
+delete_errc=Failed to copy mail
+delete_errm=Failed to move mail
+
+confirm_title=Confirm Delete
+confirm_warn=Are you sure you want to delete the $1 selected messages?
+confirm_warn2=Because of the size and format of your mailbox, this may take some time. Until the deletion has finished, no other action should be performed.
+confirm_warn3=Are you sure you want to delete this message?
+confirm_warn4=Until the deletion has finished, no other action should be performed.
+confirm_ok=Delete Now
+confirm_warnall=Are you sure you want to delete all of the messages in this folder?
+
+search_title=Search Results
+search_ecannot=You are not allowed to search this user's email
+search_ematch=You must enter text to match against.
+search_escore=Missing or invalid spam score
+search_efield=You must select a search type.
+search_ewhat=No text to match against entered for row $1
+search_enone=No search criteria entered
+search_none=No messages found.
+search_results2=$1 mail messages matching $2
+search_results3=$1 mail messages not matching $2
+search_results4=$1 mail messages matching your search
+search_results5=$1 mail messages where $2 matches $3
+search_msg2=Search results for $1
+search_msg4=Search results
+search_msg5=Search results for spam with score $1
+search_local=In local folders
+search_all=In all folders
+search_limit=(from last $1 messages)
+search_status=With status
+search_allstatus=Any
+search_onestatus=Only status
+search_latest=Messages to search
+search_nolatest=All in folder
+search_latestnum=Only latest
+search_elatest=Missing or invalid number of messages to search
+search_withstatus=, with status $1
+
+folder_inbox=Inbox
+folder_sent=Sent mail
+folder_drafts=Drafts
+folder_trash=Trash
+
+detach_err=Failed to detach file
+detach_edir=No file or directory to save to entered
+detach_eopen=Failed to open $1 : $2
+detach_ewrite=Failed to write to $1 : $2
+detach_title=Detach File
+detach_ok=Wrote attachment to server-side file $1 ($2).
+
+sform_title=Advanced Search
+sform_and=Find messages matching all criteria below ..
+sform_or=Find messages matches any criteria below ..
+sform_neg0=contains
+sform_neg1=doesn't contain
+sform_ok=Search Now
+sform_folder=in folder(s)
+sform_all=&lt;All folders&gt;
+sform_local=&lt;Local folders&gt;
+sform_where=Where
+sform_text=the text
+sform_from=From: header
+sform_subject=Subject: header
+sform_to=To: header
+sform_cc=Cc: header
+sform_bcc=Bcc: header
+sform_date=Date: header
+sform_body=message body
+sform_headers=any header
+sform_size=message size
+sform_return=advanced search form
+
+find_enone=No users matching your search were found
+find_title=Search Results
+find_results=Users matching search for $1 ..
+find_user=Username
+find_real=Real name
+find_group=Group
+find_home=Home directory
+find_size=Mail size
+find_incount=Emails
+find_sentcount=Sent
+find_fcount=Folders
+find_in=$1 in $2
+
+acl_none=None
+acl_same=User with same name
+acl_all=All
+acl_read=Users whose mail can be read
+acl_users=Only users
+acl_userse=All except users
+acl_usersg=Members of groups
+acl_from=Allowable From addresses
+acl_any=Any address
+acl_fdoms=Mailbox @ domains
+acl_faddrs=Listed addresses
+acl_fdom=Any address @ domain
+acl_fromname=Real name for From address
+acl_apath=Limit files and program to directory
+acl_attach=Maximum total attachments size
+acl_unlimited=Unlimited
+acl_sent=Store sent mail in mailbox
+acl_canattach=Can attach server-side files?
+acl_candetach=Can detach files to server?
+acl_usersm=Users matching
+acl_asame=Same as username
+acl_usersu=With UID in range
+acl_sec=Include secondary groups?
+acl_dir=Can read mail files in directory
+acl_dirauto=Decide automatically (anywhere if all users are visible, nowhere otherwise)
+
+log_delmail=Deleted $1 messages from $2
+log_movemail=Moved $1 messages from $2 to $3
+log_copymail=Copied $1 messages from $2 to $3
+log_send=Sent mail to $1
+log_read=Read mail for $1
+
+emodified=This folder has been modified since it was last viewed! Return to the <a href='$1'>mail list</a> and try again.
+
+razor_title=Reporting As Spam
+razor_title2=Reporting As Ham
+razor_report=Reporting this message to Razor and other SpamAssassin spam-blocking databases ..
+razor_report2=Reporting the selected messages to Razor and other SpamAssassin spam-blocking databases ..
+razor_report3=Un-reporting the seleected messages to Razor and other SpamAssassin spam-blocking databases ..
+razor_done=.. done
+razor_err=.. failed! See the error message above for the reason why.
+razor_deleted=.. done, and deleted message too.
+
+ham_title=Reporting As Ham
+ham_report=Reporting this message as non-spam to Razor and other SpamAssassin databases ..
+
+black_title=Denying Sender
+black_done=Added the email address $1 to SpamAssassin's denied addresses list.
+black_already=The email address $1 is already on SpamAssassin's denied addresses list.
+
+white_title=Allowing Sender
+white_done=Added the email address $1 to SpamAssassin's allowed addresses list.
+white_already=The email address $1 is already on SpamAssassin's allowed addresses list.
+
+ldap_emod=Missing Perl module $1 needed for connecting to LDAP
+ldap_econn=Failed to connect to LDAP server $1 port $2
+ldap_elogin=Failed to bind to LDAP server $1 as $2 : $3
+ldap_ehost=No LDAP server set in module configuration
+ldap_eport=No valid LDAP server port set in module configuration
+ldap_euser=No LDAP login set in module configuration
+ldap_ebase=No LDAP base DN set in module configuration
+
+delall_title=Delete All Mail
+delall_rusure=Are you sure you want to delete all email from $1? $2 messages totalling $3 will be deleted forever.
+delall_ok=Delete Now
+
diff --git a/mailboxes/lang/es b/mailboxes/lang/es
new file mode 100644 (file)
index 0000000..671c51d
--- /dev/null
@@ -0,0 +1,102 @@
+mail_title=Correo de Usuario
+mail_from=Desde
+mail_date=Fecha
+mail_subject=Asunto
+mail_to=Para
+mail_pri=Prioridad
+mail_highest=La mayor
+mail_high=Alta
+mail_low=Baja
+mail_lowest=La m&#225;s baja
+mail_for=En $1
+mail_for2=Para usuario $1
+mail_sent=En lista de correo enviado
+mail_size=Medida
+mail_delete=Borrar mensajes seleccionados
+mail_compose=Componer nuevo correo
+mail_return=correo de usuario
+mail_ecannot=No est&#225;s autorizado a leer el correo de este usuario
+mail_all=Seleccionar todos
+mail_invert=Invertir selecci&#243;n
+mail_search=Hallar mensajes donde
+mail_body=Cuerpo
+mail_match=que coincida con
+mail_ok=Buscar
+mail_nonefrom=Ninguno
+mail_mark=Marcar los seleccionados como:
+mail_mark0=No le&#237;dos
+mail_mark1=Le&#237;dos
+mail_mark2=Especiales
+mail_forward=Remitir seleccionado
+mail_rfc=Desde l&#237;nea
+view_title=Leer Correo
+view_desc=Mensaje $1 en $2
+view_desc2=Mensaje $1 para usuario $2
+view_desc3=Mensaje $1
+view_sent=Mensaje $1 en lista de corre enviado
+view_qdesc=Mensaje en cola $1
+view_headers=Cabeceras de Correo
+view_allheaders=Ver todas las cabeceras
+view_noheaders=Ver cabeceras b&#225;sicas
+view_attach=Adjuntados
+view_reply=Responder
+view_reply2=Reponder a todos
+view_enew=Editar como nuevo
+view_forward=Remitir a
+view_delete=Borrar
+view_strip=Quitar Adjuntados
+view_ecannot=No est&#225;s autorizado a leer el correo de este usuario
+view_mark0=No le&#237;do
+view_mark1=Le&#237;do
+view_mark2=Especial
+view_return=correo original
+view_sub=Correo Adjunto
+compose_title=Componer Correo
+reply_title=Responder a Correo
+forward_title=Remitir Correo
+reply_headers=Cabeceras de Correo
+reply_attach=Adjuntados remitidos
+reply_mailforward=Mensajes remitidos
+reply_attach2=Adjuntados desde cliente y servidor
+reply_send=Enviar Correo
+reply_ecannot=No est&#225;s autorizado a enviar correo a este usuario
+send_err=Error al enviar correo
+send_eto=Direcci&#243;n 'A' sin poner
+send_efrom=Direcci&#243;n 'De' sin poner
+send_title=Correo enviado
+send_ok=Correo enviado correctamente a $1
+send_ecannot=No est&#225;s autorizado a enviar correo como este usuario
+send_esmtp=Ha fallado el comando SMTP $1 : $2
+send_eattach=Los archivos inclu&#237;dos no pueden exceder m&#225;s de 1kB de medida
+send_eperms=El usuario $1 no puede leer $2
+send_eperms2=No est&#225;s autorizado a enviar archivo $1
+send_epath=El ejecutable de Sendmail $1 no existe.
+delete_ecannot=No est&#225;s autorizado a borrar correo de este usuario
+delete_enone=No se ha seleccionado correo para ser borrado
+delete_emnone=No hay correo seleccionado para marcar
+search_title=Resultados de la b&#250;squeda
+search_ecannot=No est&#225;s autorizado a buscar en este correo de usuario
+search_ematch=Debes de digitar un texto que coincida con algo.
+search_none=No se han encontrado mensajes.
+search_results3=$1 mensajes de correo no coinciden con $2
+acl_none=Ninguno
+acl_same=Usuario con mismo nombre
+acl_all=Todos
+acl_read=Usuarios cuyo correo puede ser le&#237;do
+acl_users=Usuarios
+acl_userse=Todos excepto los usuarios
+acl_usersg=Miembros de grupo
+acl_from=Direcciones 'desde' autorizadas
+acl_any=Cualquier direcci&#243;n
+acl_fdoms=Buzones en dominios
+acl_faddrs=Direcciones listadas
+acl_fdom=Cualquier direcci&#243;n en dominio
+acl_fromname=Nombre real para direcci&#243;n remitente
+acl_apath=Limitar archivos y programa a directorio
+acl_attach=Medida m&#225;xima total de archivos a incluir
+acl_sent=Almacenar correo enviado en buz&#243;n
+acl_canattach=&#191;Puede adjuntar archivos del lado servidor?
+acl_usersm=Usuarios que coincidan
+acl_asame=Igual que nombre de usuario
+log_delmail=Borrados $1 mensajes de $2
+log_send=Enviado correo a $1
diff --git a/mailboxes/lang/fr b/mailboxes/lang/fr
new file mode 100644 (file)
index 0000000..dd1bffb
--- /dev/null
@@ -0,0 +1,105 @@
+acl_all=Tous
+acl_any=De toutes les adresses
+acl_faddrs=Adresses list&#233;es
+acl_fdom=Toutes les adresses d'un domaines
+acl_fdoms=Bo&#238;te aux lettres des domaines
+acl_from=Des adresses
+acl_none=Aucun
+acl_read=Usagers dont les courriers peuvent &#234;tre lu
+acl_users=Seulement les usagers
+acl_userse=Tous
+compose_title=&#201;crire un Courrier
+confirm_ok=Effacer maintenant
+confirm_title=Confirmer la suppression
+confirm_warn=Etes vous sur de vouloir supprimer les $1 messages s&#233;lectionn&#233;es?
+confirm_warn3=Etes vous sur de vouloir supprimer ce message?
+confirm_warn4=Jusqu'&#224; la fin de l'effacement, aucune autre action ne peut &#234;tes faite.
+confirm_warnall=Etes vous s&#251;r de vouloir supprimer tous les messages de ce dossier?
+delete_ecannot=Vous n'&#234;tes pas autoris&#233; &#224; supprimer les courriers de cet usager
+delete_ecopynone=Aucun message &#224; copier s&#233;lectionner
+delete_efnone=Aucun message &#224; transmettre s&#233;lectionn&#233;
+delete_emnone=Aucun message &#224; marquer s&#233;lectionn&#233;
+delete_emovenone=Aucun message &#224; d&#233;placer s&#233;lectionn&#233;
+delete_enone=Aucun mail &#224; effacer s&#233;lectionn&#233;
+delete_ernone=Aucun mail &#224; raporter comme spam s&#233;lectionn&#233;
+delete_ok=Effacer maintenant
+delete_title=Effacer le message
+detach_title=Sauver les fichiers
+enew_title=Editer le message
+find_size=Taille du message
+forward_title=Rediriger un Courrier
+index_header=Bo&#238;tes aux lettres des usagers
+index_return=liste des utilisateurs
+index_system0=Serveur mail : Postfix
+index_system1=Serveur mail : Sendmail
+index_system2=Serveur mail : Qmail
+index_title=Lire les mails des usagers
+mail_all=Selectionner tout
+mail_cc=Copie carbone
+mail_compose=&#201;crire un nouveau courrier
+mail_copy=Copier vers:
+mail_delall=Tout supprimer
+mail_delete=Supprimer les messages s&#233;lectionn&#233;s
+mail_deleteall=Tout supprimer
+mail_ecannot=Vous n'&#234;tes pas autoris&#233; &#224; lire les courriers des usagers
+mail_for=Dans $1
+mail_forward=Transmettre
+mail_from=De
+mail_invert=Selectioner l'inverse
+mail_mark=Marqu&#233; comme:
+mail_mark0=Non lu
+mail_mark1=Lu
+mail_mark2=Sp&#233;cial
+mail_move=D&#233;placer vers:
+mail_of=sur
+mail_ok=Chercher
+mail_pri=Priorit&#233;
+mail_replyto=R&#233;pondre &#224;
+mail_reset=Effacer
+mail_return=courrier de l'usager
+mail_size=Taille
+mail_subject=Sujet
+mail_title=Courrier de l'Usager
+mail_to=&#192;
+razor_done=.. fait
+razor_title=Rapporter comme Spam
+reply_attach=Attachement envoy&#233;
+reply_attach2=Attachements
+reply_body=Corps du message
+reply_draft=Sauver comme brouillon
+reply_ecannot=Vous n'&#234;tes pas autoris&#233; &#224; envoyer un courrier de cet usager
+reply_headers=Ent&#234;tes du courrier
+reply_send=Envoyer
+reply_title=R&#233;pondre au Courrier
+send_ecannot=Vous n'&#234;tes pas autoris&#233; &#224; envoyer un courrier de cet usager
+send_efrom=Adresse de l'&#233;metteur manquant
+send_err=Impossible d'envoyer le courrier
+send_esmtp=Impossible d'ex&#233;cuter la commande SMTP $1 : $2
+send_eto=Adresse du destinataire manquante
+send_ok=Courrier envoy&#233; avec succ&#232;s &#224; $1
+send_title=Courrier Envoy&#233;
+view_allheaders=Voir toutes les ent&#234;tes
+view_ashtml=Voir en HTML
+view_astext=Voir en Texte
+view_attach=Attachements
+view_black=Refuser l'exp&#233;diteur
+view_delete=Supprimer
+view_desc=Courrier $1 dans $2
+view_detach=Sauver les fichiers:
+view_dir=dans le r&#233;pertoire:
+view_ecannot=Vous n'&#234;tes pas autoris&#233; &#224; lire les courriers des usagers
+view_forward=Renvoyer
+view_headers=Ent&#234;tes du courrier
+view_mark=Marqu&#233; comme:
+view_mark0=Non lu
+view_mark1=Lu
+view_mark2=Sp&#233;cial
+view_print=Imprimer
+view_qdesc=Courrier $1 de la file d'attente
+view_razor=Rapporter comme Spam
+view_razordel=Rapporter et Supprimer
+view_reply=R&#233;pondre
+view_reply2=R&#233;pondre &#224; tous
+view_return=email original
+view_strip=Supprimer les pi&#232;ces jointes
+view_title=Lire un Courrier
diff --git a/mailboxes/lang/it b/mailboxes/lang/it
new file mode 100755 (executable)
index 0000000..4205b95
--- /dev/null
@@ -0,0 +1,353 @@
+index_title=Read Utente Mail
+index_none=You are not allowed to read email for any utenti on questo system.
+index_header=Utente mailboxes
+index_empty=No mail
+index_return=utente list
+index_esystem=None of the supported mail servers (Qmail, Postfix and Sendmail) were detected on your system. You will need to adjust the <a href='$1'>module configuration</a> to set the mail server and possibly mail paths manually.
+index_esystem2=The mail server set in the <a href='$1'>module configuration</a> was not found on your system. You will need to adjust the configuration to use the correct server.
+index_system2=Mail server: Qmail
+index_system1=Mail server: Sendmail
+index_system0=Mail server: Postfix
+index_toomany=There are too many utenti on your system to display on one pagina.
+index_find=Cerca utenti where nome utente
+index_equals=uguale
+index_contains=contiene
+index_eperl=The Perl module $1 needed for the selected SMTP authentication mode is not installed or is missing a dependent module. <a href='$2'>Click here</a> to install it now.
+index_file=Leggi posta nel file:
+
+mail_title=Utente Email
+mail_from=Da
+mail_date=Data
+mail_subject=Oggetto
+mail_to=To
+mail_cc=Cc
+mail_bcc=Bcc
+mail_pri=Priorit&#224;
+mail_highest=Più alta
+mail_high=Alta
+mail_normal=Normale
+mail_low=Bassa
+mail_lowest=Più bassa
+mail_for=In $1
+mail_for2=For utente $1
+mail_sent=Nella lista inviate
+mail_size=Dimensione
+mail_delete=Elimina
+mail_compose=Scrivi
+mail_open=Apri
+mail_return=utente mailbox
+mail_pos=Messaggi da $1 a $2 di  $3 in  $4
+mail_none=questo utente non ha messaggi in  $1
+mail_ecannot=non hai i permessi per leggere la posta di  questo utente
+mail_all=Seleziona tutti
+mail_invert=Selezione invertita
+mail_nosort=Annulla ordinamento
+mail_search=Cerca messaggi dove
+mail_body=Corpo
+mail_match=corrisponde
+mail_ok=Cerca
+mail_nonefrom=nessuno
+mail_mark=Segna come:
+mail_mark0=Non letto
+mail_mark1=Letto
+mail_mark2=Speciale
+mail_forward=inoltra
+mail_move=sposta in:
+mail_copy=copia in :
+mail_rfc=da linea
+mail_eexists=il messaggio non esiste più!
+mail_fchange=cambia
+mail_indexlink=torna alla casella di posta
+mail_deleteall=elimina tutto
+mail_black=blocca mittenti
+mail_white=accetta mittenti
+mail_efile=il file di Mail non esiste
+mail_fromsrch=stesso mittente..
+mail_subsrch=stesso oggetto..
+mail_tosrch=stesso destinatario
+
+mail_sign=firma con chiave:
+mail_nosign=&lt;non firmare&gt;
+mail_crypt=crpita per:
+mail_nocrypt=&lt;non criptare&gt;
+mail_samecrypt=&lt;chiave per destinatari&gt;
+mail_addresses=gestione Rubrica
+mail_folders=gestione cartelle
+mail_err=errore elencando i mesaggi  in questa cartella : $1
+mail_loginheader=POP3 server login
+mail_logindesc=inserisci nome utente e password per accedere alla posta<br>nella tua casella del server mail $1.
+mail_loginuser=nome utente
+mail_loginpass=password
+mail_loginmailbox=casella di posta  IMAP
+mail_login=login
+mail_reset=pulisci
+mail_logout=cambia POP3 login
+mail_logout2=cambia IMAP login
+mail_sig=modifica firma
+mail_jump=vai a pagina:
+mail_of=of
+mail_replyto=rispondi a
+mail_folder=cartella
+mail_delall=elimina tutti
+mail_deltrash=svuota cestino
+mail_search2=cerca :
+mail_advanced=ricerca Avanzata
+mail_return2=utente Email
+
+view_title=leggi email
+view_desc=messaggio $1 di  $2
+view_desc2=messaggio $1 per utente $2
+view_desc3=messaggio $1
+view_sent=messaggio  $1 in elenco posta inviata
+view_qdesc=messaggio accodato $1
+view_headers=intestazione mail
+view_body=testo del messaggio
+view_allheaders=vedi tutte le intestazioni
+view_noheaders=vedi intestazioni base
+view_attach=allegati
+view_reply=rispondi
+view_reply2=rispondi a tutti
+view_enew=modifica come nuovo
+view_forward=inoltra
+view_delete=elimina
+view_print=stampa
+view_strip=elimina allegati
+view_ecannot=non hai i permessi di leggere la posta di questo utente
+view_mark=segna come
+view_mark0=non letto
+view_mark1=letto
+view_mark2=speciale
+view_return=email originale
+view_sub=email allegata
+view_egone=questo messaggio non esiste più
+
+view_gnupg=verifica firma GnuPG
+view_gnupg_0=la firma di  $1 è valida
+view_gnupg_1=la firma di $1 è valida, ma il  trust chain no è stato stabilito
+view_gnupg_2=la firma di  $1 <b>NON &#200;</b> valida
+view_gnupg_3=Key ID $1 is not in your list, so signature could not be verified.
+view_gnupg_4=Failed to verify signature : $1
+view_crypt=GnuPG mail decryption
+view_crypt_1=Message is encrypted, but GnuPG support is not installed.
+view_crypt_2=Failed to decrypt messaggio : $1
+view_crypt_3=Mail was successfully decrypted.
+view_crypt_4=Encrypted portion of messaggio was successfully decrypted.
+view_recv=<a href='$2'>Fetch key ID $1 da keyserver</a>.
+view_folder=torna a mailbox
+view_detach=Detach file:
+view_dall=&lt;All files&gt;
+view_dir=a file server o directory
+view_black=blocca mittente
+view_white=sblocca mittente
+view_razor=segnala come spam
+view_ham=seganana come ham
+view_razordel=segnana ed  elimina
+view_dstatus=errore stato di consegna
+view_dstatusok=stato di consegna ok 
+view_final-recipient=Final recipient
+view_diagnostic-code=dettagli errore
+view_remote-mta=mail server remoto
+view_reporting-mta=segnalazione a mail server
+view_astext=vedi messaggio formato raw testo
+view_ashtml=vedi messaggio formato HTML
+view_raw=vedi messaggio formato raw
+
+compose_title=scrivi Email
+
+reply_title=rispondi a email
+forward_title=inoltra email
+enew_title=Modifica Email
+reply_headers=intestazioni Mail 
+reply_attach=Inoltrato allegati
+reply_mailforward=Inoltrato messaggi
+reply_attach2=Client and server-side allegati
+reply_send=invia email
+reply_ecannot=non hai i permessi per inviare messaggi come  questo utente
+reply_body=testo del messaggio
+reply_errc=errore nella copia 
+reply_errm=errore nello spostare il messaggio 
+reply_return=scrivi mail
+
+reply_spell=controllo ortografico?
+reply_draft=salva come bozza
+
+send_err=errore nell'invio email
+send_eto=inserire il destinatario
+send_efrom=inserire il mittente
+send_title=Mail inviata
+send_ok=Mail inviata a  $1
+send_sending=invio posta a   $1 ..
+send_ecannot=Non hai i permessi per inviare mail a questo utente
+send_esmtp=SMTP command $1 failed : $2
+send_eattach=gli allegati non possono essere superiori a  $1 kB in dimensione.
+send_eperms=utente $1 non può leggere $2
+send_eperms2=non hai i permessi per inviare file  $1
+send_epath=Sendmail executable $1 does not exist.
+send_efile=errore nel leggere allegato $1 : $2
+send_done=.. fatto
+
+send_epass=non è possibile firmare il messaggio perchè la tua  passphrase non risulta imposta nel modulo  GnuPG
+send_esign=errore nella firma del messaggio : $1
+send_ekey=Couldn't find key for email indirizzo $1
+send_ecrypt=Failed to encrypt messaggio : $1
+send_eword=errore grammaticale $1
+send_eword2=errore grammaticale $1 - correzioni possibili  $2
+send_eline=a riga $1 :
+send_espell=i seguenti errori grammaticali sono stati trovati nel messaggio ..
+send_draft=messaggio a  $1 salvato nella cartella bozze
+send_drafting=salvataggio mail to $1 nella cartella bozze
+send_eattachsize=l'allegato di posta  supera il limite massimo consentito di $1 bytes
+
+delete_title=elimina Mail
+delete_rusure=confermi eliminazione dei  $1 messaggi selezionati  da $2? Questa operazione potrebbe richiedere tempo. Non eseguire altre operazioni fino al messaggio di conferma. 
+delete_rusure2=confermi eliminazione di questo messaggio da $1?  Questa operazione potrebbe richiedere tempo. Non eseguire altre operazioni fino al messaggio di conferma. 
+delete_ok=elimina ora
+delete_ecannot=non hai i permessi per eliminare  da questo utente
+delete_enone=non risultano selezionate  email da eliminare 
+delete_emnone=non risultano selezionate  email da segnare
+delete_efnone=non risultano selezionate  email da inoltrare
+delete_ebnone=non risultano selezionate  email da bloccare
+delete_ewnone=non risultano selezionate  email da sbloccare
+delete_ernone=non risultano selezionate  email da segnalare come span
+delete_ehnone=non risultano selezionate  email da segnalare come  ham
+delete_emoveuser=utente a cui spostare mail non esiste
+delete_ecopyuser=utente a cui copiare  mail non esiste
+delete_emovecannot=non hai i permessi per spostare mail all'utente indicato
+delete_ecopycannot=non hai i permessi per copiare mail all'utente indicato
+delete_emovenone=non risultano selezionate  email da spostare
+delete_ecopynone=non risultano selezionate  email da copiare
+delete_nobutton=nessun bottone clicked
+delete_ereport=errore nella segnalazione spam : $1
+delete_errc=errore nella copia mail
+delete_errm=errore nello spostamento mail
+
+confirm_title=conferma elimina
+confirm_warn=confermi eliminazione dei $1 messaggi selezionati?
+confirm_warn2=Because of the dimensione and format of your mailbox, questo may take some time. Until the deletion has finished, no other action should be performed.
+confirm_warn3=Are you sure you want to delete questo messaggio?
+confirm_warn4=Until the deletion has finished, no other action should be performed.
+confirm_ok=Elimina Now
+confirm_warnall=Are you sure you want to delete all of the messaggi in questo cartella?
+
+search_title=esito ricerca 
+search_ecannot=non hai i permessi per cercare mail di questo utente
+search_ematch=inserisci un testo per il confronto
+search_efield=seleziona tipo di ricerca
+search_ewhat=No text to match against entered for row $1
+search_enone=inserisci un criterio di ricerca
+search_none=nessun messaggio trovato
+search_results2=$1 messaggi corrispondenti a  $2
+search_results3=$1 messaggi non corrispondenti $2
+search_results4=$1 messaggi corrispondenti alla ricerca
+search_msg2=esito ricerca per  $1
+search_msg4=esito ricerca
+search_local=nella cartelle locali
+search_all=in tutte le cartelle
+search_limit=(da last $1 messaggi)
+search_status=con status
+search_allstatus=tutti
+search_onestatus=solo status
+search_latest=Messaggi da cercare
+search_nolatest=tutto in cartella
+search_latestnum=più recenti
+search_elatest=inserisci un numero valido di messaggio da cercare
+search_withstatus=, con status $1
+
+folder_inbox=arrivo
+folder_sent=inviata
+folder_drafts=bozze
+folder_trash=cestino
+
+detach_err=errore in  detach file
+detach_edir=No file or directory to save to entered
+detach_eopen=errore di lettura $1 : $2
+detach_ewrite=erroe di scrittura su  $1 : $2
+detach_title=Detach File
+detach_ok=scritto allegato al file  server-side  $1 ($2).
+
+sform_title=Ricerca avanzata
+sform_and=Cerca messaggi con tutti i criteri sotto
+sform_or=Cerca messaggi con ogni criterio sotto
+sform_neg0=contiene
+sform_neg1=non contiene
+sform_ok=Cerca ora
+sform_folder=in cartella(s)
+sform_all=&lt;tuttel el cartelle&gt;
+sform_local=&lt;cartelle locali&gt;
+sform_where=Dove
+sform_text=il testo
+sform_from=Da: intestazione
+sform_subject=Oggetto: intestazione
+sform_to=To: intestazione
+sform_cc=Cc: intestazione
+sform_date=Date: intestazione
+sform_body=messaggio corpo
+sform_headers=ogni intestazione
+sform_size=messaggio dimensione
+sform_return=form ricerca avanzata
+
+find_enone=Nessun utente corrisponde alla ricerca effettuata
+find_title=Cerca risultati
+find_results=utenti corrispondenti alla ricerca per  $1 ..
+find_user=Nome utente
+find_real=Nome reale
+find_group=Gruppo
+find_home=Home directory
+find_size=dimensione mail
+find_incount=Messaggi
+find_sentcount=Inviati
+
+acl_none=Nessuno
+acl_same=Utente con stesso nome
+acl_all=tutti
+acl_read=utenti ai quali la posta può essere letta
+acl_users=Solo utenti
+acl_userse=Tutto tranne utenti
+acl_usersg=Membri dei gruppi
+acl_from=Disponibile da indirizzi
+acl_any=Ogni indirizzo
+acl_fdoms=Mailbox @ domini
+acl_faddrs=indirizzi elencati
+acl_fdom=ogni indirizzo @ dominio
+acl_fromname=Nome reale  per  Da indirizzo
+acl_apath=Limite file e programi per directory
+acl_attach=dimesione totale massima degli  allegati
+acl_sent=archivia posta inviata in cartella mailbox
+acl_canattach=puoi/può allegare file server-side?
+acl_candetach=puoi/può detach files al server?
+acl_usersm=corrispondenza utente
+acl_asame=Stesso come nome utente
+acl_usersu=With UID in range
+acl_sec=Include secondary gruppi?
+acl_dir=puoi/può leggere mail nella directory
+acl_dirauto=scelta automatica  (ovunque se tutti gli  utenti sono visibili, al contrario da nessuna parte)
+
+log_delmail=eliminati  $1 messaggi da $2
+log_movemail=spostati  $1 messaggi da $2 a $3
+log_copymail=copiati $1 messaggi da $2 a $3
+log_send=mail inviata a to $1
+log_read=leggi/letta mail per $1
+
+emodified=questa cartella è stata modificata dall'ultima visita! torna a  <a href='$1'>lista mail</a> e riprova
+
+razor_title=segnala come spam
+razor_report=sto segnalando questo messaggio a Razor o altri SpamAssassin spam-blocking databases ..
+razor_done=.. fatto
+razor_err=.. errore! vedi dettagli sopra
+razor_deleted=.. fatto , il messaggio è stato eliminato
+
+ham_title=Reporting As Ham
+ham_report=Reporting questo messaggio as non-spam to Razor and other SpamAssassin databases ..
+
+black_title=Denying Sender
+black_done=Added the email indirizzo $1 to SpamAssassin's denied indirizzi list.
+black_already=The email indirizzo $1 is already on SpamAssassin's denied indirizzi list.
+
+white_title=Allowing Sender
+white_done=Added the email indirizzo $1 to SpamAssassin's allowed indirizzi list.
+white_already=The email indirizzo $1 is already on SpamAssassin's allowed indirizzi list.
+
+ldap_emod=Missing Perl module $1 needed for connecting to LDAP
+ldap_econn=Failed to connect to LDAP server $1 port $2
+ldap_elogin=Failed to bind to LDAP server $1 as $2 : $3
+
diff --git a/mailboxes/lang/ja_JP.UTF-8 b/mailboxes/lang/ja_JP.UTF-8
new file mode 100644 (file)
index 0000000..567d8e3
--- /dev/null
@@ -0,0 +1,78 @@
+mail_title=ユーザ E メール
+mail_from=送信元
+mail_date=日付
+mail_subject=件名
+mail_to=宛先
+mail_pri=優先度
+mail_highest=最優先
+mail_high=高
+mail_normal=標準
+mail_low=低
+mail_lowest=最後
+mail_for=$1 内
+mail_sent=送信済みメール リスト内
+mail_size=サイズ
+mail_delete=選択されたメッセージを削除
+mail_compose=新規メールを作成
+mail_return=ユーザ E メール
+mail_ecannot=このユーザーのメールは読めません
+mail_all=すべて選択
+mail_invert=選択の反転
+mail_search=メッセージの検索
+mail_body=本文
+mail_match=一致
+mail_ok=検索
+mail_nonefrom=なし
+view_title=E メールを読む
+view_desc=$2のメッセージ $1
+view_sent=送信済みメール リストのメッセージ $1
+view_qdesc=キューされたメッセージ $1
+view_headers=メール ヘッダ
+view_attach=添付ファイル
+view_reply=返信
+view_reply2=全員に返信
+view_enew=新規として編集
+view_forward=転送
+view_delete=削除
+view_ecannot=このユーザーのメールは読めません
+compose_title=E メールの作成
+reply_title=E メールへの返信
+forward_title=E メールの転送
+reply_headers=メール ヘッダ
+reply_attach=転送された添付ファイル
+reply_attach2=添付ファイル
+reply_send=送信
+reply_ecannot=このユーザとしてはメールを送信できません
+send_err=メールを送信できませんでした
+send_eto=To (宛先)アドレスがありません
+send_efrom=From (送信者) アドレスがありません
+send_title=送信されたメール
+send_ok=$1 へのメールの送信を完了しました
+send_ecannot=このユーザとしてはメールを送信できません
+send_esmtp=SMTP コマンド $1 が失敗しました: $2
+send_eattach=添付ファイルのサイズは $1 KB を越えられません。
+send_eperms=ユーザ $1 は $2 を読み取れません
+send_eperms2=ファイル $1 は送信できません
+delete_ecannot=このユーザからのメールを削除できます
+delete_enone=削除するメッセージが選択されていません
+search_title=検索結果
+search_ecannot=このユーザのメールは検索できません
+search_ematch=検索するにはテキストを入力する必要があります。
+search_none=メッセージが見つかりませんでした。
+acl_none=なし
+acl_same=同名のユーザ
+acl_all=すべて
+acl_read=メールを読み取れるユーザ
+acl_users=次のユーザのみ
+acl_userse=次のユーザ以外すべて
+acl_usersg=グループのメンバー
+acl_from=アドレスから許可
+acl_any=任意のアドレス
+acl_fdoms=メールボックス @ ドメイン
+acl_faddrs=リストされたアドレス
+acl_fdom=任意のアドレス @ ドメイン
+acl_apath=ディレクトリへのファイルとプログラムを制限
+acl_attach=添付ファイルの合計サイズの最大値
+acl_sent=送信済みメールをメールボックスに保存
+log_delmail=$1 メッセージを $2 から削除しました
+log_send=$1 にメールを送信しました
diff --git a/mailboxes/lang/ja_JP.euc b/mailboxes/lang/ja_JP.euc
new file mode 100644 (file)
index 0000000..9b74c6b
--- /dev/null
@@ -0,0 +1,78 @@
+mail_title=¥æ¡¼¥¶ E ¥á¡¼¥ë
+mail_from=Á÷¿®¸µ
+mail_date=ÆüÉÕ
+mail_subject=·ï̾
+mail_to=°¸Àè
+mail_pri=Í¥ÀèÅÙ
+mail_highest=ºÇÍ¥Àè
+mail_high=¹â
+mail_normal=ɸ½à
+mail_low=Äã
+mail_lowest=ºÇ¸å
+mail_for=$1 Æâ
+mail_sent=Á÷¿®ºÑ¤ß¥á¡¼¥ë ¥ê¥¹¥ÈÆâ
+mail_size=¥µ¥¤¥º
+mail_delete=ÁªÂò¤µ¤ì¤¿¥á¥Ã¥»¡¼¥¸¤òºï½ü
+mail_compose=¿·µ¬¥á¡¼¥ë¤òºîÀ®
+mail_return=¥æ¡¼¥¶ E ¥á¡¼¥ë
+mail_ecannot=¤³¤Î¥æ¡¼¥¶¡¼¤Î¥á¡¼¥ë¤ÏÆɤá¤Þ¤»¤ó
+mail_all=¤¹¤Ù¤ÆÁªÂò
+mail_invert=ÁªÂò¤Îȿž
+mail_search=¥á¥Ã¥»¡¼¥¸¤Î¸¡º÷
+mail_body=ËÜʸ
+mail_match=°ìÃ×
+mail_ok=¸¡º÷
+mail_nonefrom=¤Ê¤·
+view_title=E ¥á¡¼¥ë¤òÆɤà
+view_desc=$2¤Î¥á¥Ã¥»¡¼¥¸ $1
+view_sent=Á÷¿®ºÑ¤ß¥á¡¼¥ë ¥ê¥¹¥È¤Î¥á¥Ã¥»¡¼¥¸ $1
+view_qdesc=¥­¥å¡¼¤µ¤ì¤¿¥á¥Ã¥»¡¼¥¸ $1
+view_headers=¥á¡¼¥ë ¥Ø¥Ã¥À
+view_attach=źÉÕ¥Õ¥¡¥¤¥ë
+view_reply=ÊÖ¿®
+view_reply2=Á´°÷¤ËÊÖ¿®
+view_enew=¿·µ¬¤È¤·¤ÆÊÔ½¸
+view_forward=žÁ÷
+view_delete=ºï½ü
+view_ecannot=¤³¤Î¥æ¡¼¥¶¡¼¤Î¥á¡¼¥ë¤ÏÆɤá¤Þ¤»¤ó
+compose_title=E ¥á¡¼¥ë¤ÎºîÀ®
+reply_title=E ¥á¡¼¥ë¤Ø¤ÎÊÖ¿®
+forward_title=E ¥á¡¼¥ë¤ÎžÁ÷
+reply_headers=¥á¡¼¥ë ¥Ø¥Ã¥À
+reply_attach=žÁ÷¤µ¤ì¤¿ÅºÉÕ¥Õ¥¡¥¤¥ë
+reply_attach2=źÉÕ¥Õ¥¡¥¤¥ë
+reply_send=Á÷¿®
+reply_ecannot=¤³¤Î¥æ¡¼¥¶¤È¤·¤Æ¤Ï¥á¡¼¥ë¤òÁ÷¿®¤Ç¤­¤Þ¤»¤ó
+send_err=¥á¡¼¥ë¤òÁ÷¿®¤Ç¤­¤Þ¤»¤ó¤Ç¤·¤¿
+send_eto=To ¡Ê°¸Àè¡Ë¥¢¥É¥ì¥¹¤¬¤¢¤ê¤Þ¤»¤ó
+send_efrom=From (Á÷¿®¼Ô) ¥¢¥É¥ì¥¹¤¬¤¢¤ê¤Þ¤»¤ó
+send_title=Á÷¿®¤µ¤ì¤¿¥á¡¼¥ë
+send_ok=$1 ¤Ø¤Î¥á¡¼¥ë¤ÎÁ÷¿®¤ò´°Î»¤·¤Þ¤·¤¿
+send_ecannot=¤³¤Î¥æ¡¼¥¶¤È¤·¤Æ¤Ï¥á¡¼¥ë¤òÁ÷¿®¤Ç¤­¤Þ¤»¤ó
+send_esmtp=SMTP ¥³¥Þ¥ó¥É $1 ¤¬¼ºÇÔ¤·¤Þ¤·¤¿: $2
+send_eattach=źÉÕ¥Õ¥¡¥¤¥ë¤Î¥µ¥¤¥º¤Ï $1 KB ¤ò±Û¤¨¤é¤ì¤Þ¤»¤ó¡£
+send_eperms=¥æ¡¼¥¶ $1 ¤Ï $2 ¤òÆɤ߼è¤ì¤Þ¤»¤ó
+send_eperms2=¥Õ¥¡¥¤¥ë $1 ¤ÏÁ÷¿®¤Ç¤­¤Þ¤»¤ó
+delete_ecannot=¤³¤Î¥æ¡¼¥¶¤«¤é¤Î¥á¡¼¥ë¤òºï½ü¤Ç¤­¤Þ¤¹
+delete_enone=ºï½ü¤¹¤ë¥á¥Ã¥»¡¼¥¸¤¬ÁªÂò¤µ¤ì¤Æ¤¤¤Þ¤»¤ó
+search_title=¸¡º÷·ë²Ì
+search_ecannot=¤³¤Î¥æ¡¼¥¶¤Î¥á¡¼¥ë¤Ï¸¡º÷¤Ç¤­¤Þ¤»¤ó
+search_ematch=¸¡º÷¤¹¤ë¤Ë¤Ï¥Æ¥­¥¹¥È¤òÆþÎϤ¹¤ëɬÍפ¬¤¢¤ê¤Þ¤¹¡£
+search_none=¥á¥Ã¥»¡¼¥¸¤¬¸«¤Ä¤«¤ê¤Þ¤»¤ó¤Ç¤·¤¿¡£
+acl_none=¤Ê¤·
+acl_same=Ʊ̾¤Î¥æ¡¼¥¶
+acl_all=¤¹¤Ù¤Æ
+acl_read=¥á¡¼¥ë¤òÆɤ߼è¤ì¤ë¥æ¡¼¥¶
+acl_users=¼¡¤Î¥æ¡¼¥¶¤Î¤ß
+acl_userse=¼¡¤Î¥æ¡¼¥¶°Ê³°¤¹¤Ù¤Æ
+acl_usersg=¥°¥ë¡¼¥×¤Î¥á¥ó¥Ð¡¼
+acl_from=¥¢¥É¥ì¥¹¤«¤éµö²Ä
+acl_any=Ǥ°Õ¤Î¥¢¥É¥ì¥¹
+acl_fdoms=¥á¡¼¥ë¥Ü¥Ã¥¯¥¹ @ ¥É¥á¥¤¥ó
+acl_faddrs=¥ê¥¹¥È¤µ¤ì¤¿¥¢¥É¥ì¥¹
+acl_fdom=Ǥ°Õ¤Î¥¢¥É¥ì¥¹ @ ¥É¥á¥¤¥ó
+acl_apath=¥Ç¥£¥ì¥¯¥È¥ê¤Ø¤Î¥Õ¥¡¥¤¥ë¤È¥×¥í¥°¥é¥à¤òÀ©¸Â
+acl_attach=źÉÕ¥Õ¥¡¥¤¥ë¤Î¹ç·×¥µ¥¤¥º¤ÎºÇÂçÃÍ
+acl_sent=Á÷¿®ºÑ¤ß¥á¡¼¥ë¤ò¥á¡¼¥ë¥Ü¥Ã¥¯¥¹¤ËÊݸ
+log_delmail=$1 ¥á¥Ã¥»¡¼¥¸¤ò $2 ¤«¤éºï½ü¤·¤Þ¤·¤¿
+log_send=$1 ¤Ë¥á¡¼¥ë¤òÁ÷¿®¤·¤Þ¤·¤¿
diff --git a/mailboxes/lang/ja_JP.jis b/mailboxes/lang/ja_JP.jis
new file mode 100644 (file)
index 0000000..d146900
--- /dev/null
@@ -0,0 +1,78 @@
+mail_title=\83\86\81[\83U E \83\81\81[\83\8b
+mail_from=\91\97\90M\8c³
+mail_date=\93ú\95t
+mail_subject=\8c\8f\96¼
+mail_to=\88\90æ
+mail_pri=\97D\90æ\93x
+mail_highest=\8dÅ\97D\90æ
+mail_high=\8d\82
+mail_normal=\95W\8f\80
+mail_low=\92á
+mail_lowest=\8dÅ\8cã
+mail_for=$1 \93à
+mail_sent=\91\97\90M\8dÏ\82Ý\83\81\81[\83\8b \83\8a\83X\83g\93à
+mail_size=\83T\83C\83Y
+mail_delete=\91I\91ð\82³\82ê\82½\83\81\83b\83Z\81[\83W\82ð\8dí\8f\9c
+mail_compose=\90V\8bK\83\81\81[\83\8b\82ð\8dì\90¬
+mail_return=\83\86\81[\83U E \83\81\81[\83\8b
+mail_ecannot=\82±\82Ì\83\86\81[\83U\81[\82Ì\83\81\81[\83\8b\82Í\93Ç\82ß\82Ü\82¹\82ñ
+mail_all=\82·\82×\82Ä\91I\91ð
+mail_invert=\91I\91ð\82Ì\94½\93]
+mail_search=\83\81\83b\83Z\81[\83W\82Ì\8c\9f\8dõ
+mail_body=\96{\95
+mail_match=\88ê\92v
+mail_ok=\8c\9f\8dõ
+mail_nonefrom=\82È\82µ
+view_title=E \83\81\81[\83\8b\82ð\93Ç\82Þ
+view_desc=$2\82Ì\83\81\83b\83Z\81[\83W $1
+view_sent=\91\97\90M\8dÏ\82Ý\83\81\81[\83\8b \83\8a\83X\83g\82Ì\83\81\83b\83Z\81[\83W $1
+view_qdesc=\83L\83\85\81[\82³\82ê\82½\83\81\83b\83Z\81[\83W $1
+view_headers=\83\81\81[\83\8b \83w\83b\83_
+view_attach=\93Y\95t\83t\83@\83C\83\8b
+view_reply=\95Ô\90M
+view_reply2=\91S\88õ\82É\95Ô\90M
+view_enew=\90V\8bK\82Æ\82µ\82Ä\95Ò\8fW
+view_forward=\93]\91\97
+view_delete=\8dí\8f\9c
+view_ecannot=\82±\82Ì\83\86\81[\83U\81[\82Ì\83\81\81[\83\8b\82Í\93Ç\82ß\82Ü\82¹\82ñ
+compose_title=E \83\81\81[\83\8b\82Ì\8dì\90¬
+reply_title=E \83\81\81[\83\8b\82Ö\82Ì\95Ô\90M
+forward_title=E \83\81\81[\83\8b\82Ì\93]\91\97
+reply_headers=\83\81\81[\83\8b \83w\83b\83_
+reply_attach=\93]\91\97\82³\82ê\82½\93Y\95t\83t\83@\83C\83\8b
+reply_attach2=\93Y\95t\83t\83@\83C\83\8b
+reply_send=\91\97\90M
+reply_ecannot=\82±\82Ì\83\86\81[\83U\82Æ\82µ\82Ä\82Í\83\81\81[\83\8b\82ð\91\97\90M\82Å\82«\82Ü\82¹\82ñ
+send_err=\83\81\81[\83\8b\82ð\91\97\90M\82Å\82«\82Ü\82¹\82ñ\82Å\82µ\82½
+send_eto=To \81i\88\90æ\81j\83A\83h\83\8c\83X\82ª\82 \82è\82Ü\82¹\82ñ
+send_efrom=From (\91\97\90M\8eÒ) \83A\83h\83\8c\83X\82ª\82 \82è\82Ü\82¹\82ñ
+send_title=\91\97\90M\82³\82ê\82½\83\81\81[\83\8b
+send_ok=$1 \82Ö\82Ì\83\81\81[\83\8b\82Ì\91\97\90M\82ð\8a®\97¹\82µ\82Ü\82µ\82½
+send_ecannot=\82±\82Ì\83\86\81[\83U\82Æ\82µ\82Ä\82Í\83\81\81[\83\8b\82ð\91\97\90M\82Å\82«\82Ü\82¹\82ñ
+send_esmtp=SMTP \83R\83}\83\93\83h $1 \82ª\8e¸\94s\82µ\82Ü\82µ\82½: $2
+send_eattach=\93Y\95t\83t\83@\83C\83\8b\82Ì\83T\83C\83Y\82Í $1 KB \82ð\89z\82¦\82ç\82ê\82Ü\82¹\82ñ\81B
+send_eperms=\83\86\81[\83U $1 \82Í $2 \82ð\93Ç\82Ý\8eæ\82ê\82Ü\82¹\82ñ
+send_eperms2=\83t\83@\83C\83\8b $1 \82Í\91\97\90M\82Å\82«\82Ü\82¹\82ñ
+delete_ecannot=\82±\82Ì\83\86\81[\83U\82©\82ç\82Ì\83\81\81[\83\8b\82ð\8dí\8f\9c\82Å\82«\82Ü\82·
+delete_enone=\8dí\8f\9c\82·\82é\83\81\83b\83Z\81[\83W\82ª\91I\91ð\82³\82ê\82Ä\82¢\82Ü\82¹\82ñ
+search_title=\8c\9f\8dõ\8c\8b\89Ê
+search_ecannot=\82±\82Ì\83\86\81[\83U\82Ì\83\81\81[\83\8b\82Í\8c\9f\8dõ\82Å\82«\82Ü\82¹\82ñ
+search_ematch=\8c\9f\8dõ\82·\82é\82É\82Í\83e\83L\83X\83g\82ð\93ü\97Í\82·\82é\95K\97v\82ª\82 \82è\82Ü\82·\81B
+search_none=\83\81\83b\83Z\81[\83W\82ª\8c©\82Â\82©\82è\82Ü\82¹\82ñ\82Å\82µ\82½\81B
+acl_none=\82È\82µ
+acl_same=\93¯\96¼\82Ì\83\86\81[\83U
+acl_all=\82·\82×\82Ä
+acl_read=\83\81\81[\83\8b\82ð\93Ç\82Ý\8eæ\82ê\82é\83\86\81[\83U
+acl_users=\8e\9f\82Ì\83\86\81[\83U\82Ì\82Ý
+acl_userse=\8e\9f\82Ì\83\86\81[\83U\88È\8aO\82·\82×\82Ä
+acl_usersg=\83O\83\8b\81[\83v\82Ì\83\81\83\93\83o\81[
+acl_from=\83A\83h\83\8c\83X\82©\82ç\8b\96\89Â
+acl_any=\94C\88Ó\82Ì\83A\83h\83\8c\83X
+acl_fdoms=\83\81\81[\83\8b\83{\83b\83N\83X @ \83h\83\81\83C\83\93
+acl_faddrs=\83\8a\83X\83g\82³\82ê\82½\83A\83h\83\8c\83X
+acl_fdom=\94C\88Ó\82Ì\83A\83h\83\8c\83X @ \83h\83\81\83C\83\93
+acl_apath=\83f\83B\83\8c\83N\83g\83\8a\82Ö\82Ì\83t\83@\83C\83\8b\82Æ\83v\83\8d\83O\83\89\83\80\82ð\90§\8cÀ
+acl_attach=\93Y\95t\83t\83@\83C\83\8b\82Ì\8d\87\8cv\83T\83C\83Y\82Ì\8dÅ\91å\92l
+acl_sent=\91\97\90M\8dÏ\82Ý\83\81\81[\83\8b\82ð\83\81\81[\83\8b\83{\83b\83N\83X\82É\95Û\91
+log_delmail=$1 \83\81\83b\83Z\81[\83W\82ð $2 \82©\82ç\8dí\8f\9c\82µ\82Ü\82µ\82½
+log_send=$1 \82É\83\81\81[\83\8b\82ð\91\97\90M\82µ\82Ü\82µ\82½
diff --git a/mailboxes/lang/ko_KR.UTF-8 b/mailboxes/lang/ko_KR.UTF-8
new file mode 100644 (file)
index 0000000..81fcb00
--- /dev/null
@@ -0,0 +1,80 @@
+mail_title=紫遂切 穿切 五析
+mail_from=降重切
+mail_date=劾促
+mail_subject=薦鯉
+mail_to=呪重切
+mail_cc=凧繕
+mail_bcc=需精 凧繕
+mail_pri=酔識 授是
+mail_highest=亜舌 株製
+mail_high=株製
+mail_normal=左搭
+mail_low=碍製
+mail_lowest=亜舌 碍製
+mail_for=$1
+mail_sent=降重 五析 鯉系
+mail_size=滴奄
+mail_delete=識澱廃 五獣走 肢薦
+mail_compose=歯 五析 拙失
+mail_return=紫遂切 穿切 五析
+mail_ecannot=戚 紫遂切税 穿切 五析聖 石聖 呪 蒸柔艦陥
+mail_all=乞砧 識澱
+mail_invert=鋼企稽 識澱
+mail_search=五獣走 伊事 是帖
+mail_body=沙庚
+mail_match=析帖
+mail_ok=伊事
+mail_nonefrom=蒸製
+view_title=穿切 五析 石奄
+view_desc=$2拭 赤澗 五獣走 $1
+view_sent=降重 五析 鯉系拭 赤澗 五獣走 $1
+view_qdesc=企奄伸拭 赤澗 五獣走 $1
+view_headers=五析 伯希
+view_attach=歎採 督析
+view_reply=噺重
+view_reply2=穿端 噺重
+view_enew=歯 五析稽 畷増
+view_forward=穿含
+view_delete=肢薦
+view_ecannot=戚 紫遂切税 穿切 五析聖 石聖 呪 蒸柔艦陥
+compose_title=穿切 五析 拙失
+reply_title=穿切 五析拭 噺重
+forward_title=穿切 五析 穿含
+reply_headers=五析 伯希
+reply_attach=穿含吉 歎採 督析
+reply_attach2=歎採 督析
+reply_send=穿勺
+reply_ecannot=戚 紫遂切稽 五析聖 穿勺拝 呪 蒸柔艦陥
+send_err=五析聖 穿勺馬走 公梅柔艦陥
+send_eto=蒸澗 呪重切 爽社
+send_efrom=蒸澗 降重切 爽社
+send_title=五析 穿勺 刃戟
+send_ok=$1拭 五析聖 穿勺梅柔艦陥.
+send_ecannot=戚 紫遂切稽 五析聖 穿勺拝 呪 蒸柔艦陥
+send_esmtp=SMTP 誤敬 $1 叔鳶: $2
+send_eattach=歎採 督析税 恥 滴奄澗 $1KB研 段引拝 呪 蒸柔艦陥.
+send_eperms=紫遂切 $1精(澗) $2聖(研) 石聖 呪 蒸柔艦陥
+send_eperms2=督析 $1聖(研) 左馨 呪 蒸柔艦陥
+delete_ecannot=戚 紫遂切稽採斗 閤精 五析聖 肢薦拝 呪 蒸柔艦陥
+delete_enone=肢薦拝 五析聖 識澱馬走 省紹柔艦陥
+search_title=伊事 衣引
+search_ecannot=戚 紫遂切税 穿切 五析聖 伊事拝 呪 蒸柔艦陥 
+search_ematch=伊事拝 努什闘研 脊径背醤 杯艦陥.
+search_none=五獣走亜 蒸柔艦陥.
+acl_none=蒸製
+acl_same=戚硯戚 疑析廃 紫遂切
+acl_all=乞砧
+acl_read=石聖 呪 赤澗 五析聖 左鎧澗 紫遂切
+acl_users=紫遂切幻
+acl_userse=紫遂切研 薦須廃 乞砧
+acl_usersg=益血 姥失据
+acl_from=買遂 亜管廃 降重切 爽社
+acl_any=績税税 爽社
+acl_fdoms=紫辞敗@亀五昔
+acl_faddrs=蟹伸吉 爽社
+acl_fdom=績税税 爽社@亀五昔
+acl_apath=巨刑塘軒拭 企廃 督析 貢 覗稽益轡 薦廃
+acl_attach=穿端 歎採 督析税 置企 滴奄
+acl_sent=紫辞敗拭 降重 五析 煽舌
+log_delmail=$2拭辞 $1鯵 五獣走 肢薦喫
+log_send=$1拭惟 五析 穿勺 刃戟
diff --git a/mailboxes/lang/ko_KR.euc b/mailboxes/lang/ko_KR.euc
new file mode 100644 (file)
index 0000000..540dedf
--- /dev/null
@@ -0,0 +1,80 @@
+mail_title=»ç¿ëÀÚ ÀüÀÚ ¸ÞÀÏ
+mail_from=¹ß½ÅÀÚ
+mail_date=³¯Â¥
+mail_subject=Á¦¸ñ
+mail_to=¼ö½ÅÀÚ
+mail_cc=ÂüÁ¶
+mail_bcc=¼ûÀº ÂüÁ¶
+mail_pri=¿ì¼± ¼øÀ§
+mail_highest=°¡Àå ³ôÀ½
+mail_high=³ôÀ½
+mail_normal=º¸Åë
+mail_low=³·À½
+mail_lowest=°¡Àå ³·À½
+mail_for=$1
+mail_sent=¹ß½Å ¸ÞÀÏ ¸ñ·Ï
+mail_size=Å©±â
+mail_delete=¼±ÅÃÇÑ ¸Þ½ÃÁö »èÁ¦
+mail_compose=»õ ¸ÞÀÏ ÀÛ¼º
+mail_return=»ç¿ëÀÚ ÀüÀÚ ¸ÞÀÏ
+mail_ecannot=ÀÌ »ç¿ëÀÚÀÇ ÀüÀÚ ¸ÞÀÏÀ» ÀÐÀ» ¼ö ¾ø½À´Ï´Ù
+mail_all=¸ðµÎ ¼±ÅÃ
+mail_invert=¹Ý´ë·Î ¼±ÅÃ
+mail_search=¸Þ½ÃÁö °Ë»ö À§Ä¡
+mail_body=º»¹®
+mail_match=ÀÏÄ¡
+mail_ok=°Ë»ö
+mail_nonefrom=¾øÀ½
+view_title=ÀüÀÚ ¸ÞÀÏ Àбâ
+view_desc=$2¿¡ Àִ ¸Þ½ÃÁö $1
+view_sent=¹ß½Å ¸ÞÀÏ ¸ñ·Ï¿¡ Àִ ¸Þ½ÃÁö $1
+view_qdesc=´ë±â¿­¿¡ Àִ ¸Þ½ÃÁö $1
+view_headers=¸ÞÀÏ Çì´õ
+view_attach=÷ºÎ ÆÄÀÏ
+view_reply=ȸ½Å
+view_reply2=Àüü È¸½Å
+view_enew=»õ ¸ÞÀϷΠÆíÁý
+view_forward=Àü´Þ
+view_delete=»èÁ¦
+view_ecannot=ÀÌ »ç¿ëÀÚÀÇ ÀüÀÚ ¸ÞÀÏÀ» ÀÐÀ» ¼ö ¾ø½À´Ï´Ù
+compose_title=ÀüÀÚ ¸ÞÀÏ ÀÛ¼º
+reply_title=ÀüÀÚ ¸ÞÀÏ¿¡ È¸½Å
+forward_title=ÀüÀÚ ¸ÞÀÏ Àü´Þ
+reply_headers=¸ÞÀÏ Çì´õ
+reply_attach=Àü´ÞµÈ Ã·ºÎ ÆÄÀÏ
+reply_attach2=÷ºÎ ÆÄÀÏ
+reply_send=Àü¼Û
+reply_ecannot=ÀÌ »ç¿ëÀڷΠ¸ÞÀÏÀ» Àü¼ÛÇÒ ¼ö ¾ø½À´Ï´Ù
+send_err=¸ÞÀÏÀ» Àü¼ÛÇÏÁö ¸øÇß½À´Ï´Ù
+send_eto=¾ø´Â ¼ö½ÅÀÚ ÁÖ¼Ò
+send_efrom=¾ø´Â ¹ß½ÅÀÚ ÁÖ¼Ò
+send_title=¸ÞÀÏ Àü¼Û ¿Ï·á
+send_ok=$1¿¡ ¸ÞÀÏÀ» Àü¼ÛÇß½À´Ï´Ù.
+send_ecannot=ÀÌ »ç¿ëÀڷΠ¸ÞÀÏÀ» Àü¼ÛÇÒ ¼ö ¾ø½À´Ï´Ù
+send_esmtp=SMTP ¸í·É $1 ½ÇÆÐ: $2
+send_eattach=÷ºÎ ÆÄÀÏÀÇ ÃÑ Å©±â´Â $1KB¸¦ ÃÊ°úÇÒ ¼ö ¾ø½À´Ï´Ù.
+send_eperms=»ç¿ëÀÚ $1Àº(´Â) $2À»(¸¦) ÀÐÀ» ¼ö ¾ø½À´Ï´Ù
+send_eperms2=ÆÄÀÏ $1À»(¸¦) º¸³¾ ¼ö ¾ø½À´Ï´Ù
+delete_ecannot=ÀÌ »ç¿ëÀڷκÎÅÍ ¹ÞÀº ¸ÞÀÏÀ» »èÁ¦ÇÒ ¼ö ¾ø½À´Ï´Ù
+delete_enone=»èÁ¦ÇÒ ¸ÞÀÏÀ» ¼±ÅÃÇÏÁö ¾Ê¾Ò½À´Ï´Ù
+search_title=°Ë»ö °á°ú
+search_ecannot=ÀÌ »ç¿ëÀÚÀÇ ÀüÀÚ ¸ÞÀÏÀ» °Ë»öÇÒ ¼ö ¾ø½À´Ï´Ù 
+search_ematch=°Ë»öÇÒ ÅؽºÆ®¸¦ ÀÔ·ÂÇؾߠÇÕ´Ï´Ù.
+search_none=¸Þ½ÃÁö°¡ ¾ø½À´Ï´Ù.
+acl_none=¾øÀ½
+acl_same=À̸§ÀÌ µ¿ÀÏÇÑ »ç¿ëÀÚ
+acl_all=¸ðµÎ
+acl_read=ÀÐÀ» ¼ö Àִ ¸ÞÀÏÀ» º¸³»´Â »ç¿ëÀÚ
+acl_users=»ç¿ëÀÚ¸¸
+acl_userse=»ç¿ëÀÚ¸¦ Á¦¿ÜÇÑ ¸ðµÎ
+acl_usersg=±×·ì ±¸¼º¿ø
+acl_from=Çã¿ë °¡´ÉÇÑ ¹ß½ÅÀÚ ÁÖ¼Ò
+acl_any=ÀÓÀÇÀÇ ÁÖ¼Ò
+acl_fdoms=»ç¼­ÇÔ@µµ¸ÞÀÎ
+acl_faddrs=³ª¿­µÈ ÁÖ¼Ò
+acl_fdom=ÀÓÀÇÀÇ ÁÖ¼Ò@µµ¸ÞÀÎ
+acl_apath=µð·ºÅ丮¿¡ ´ëÇÑ ÆÄÀÏ ¹× ÇÁ·Î±×·¥ Á¦ÇÑ
+acl_attach=Àüü Ã·ºÎ ÆÄÀÏÀÇ ÃÖ´ë Å©±â
+acl_sent=»ç¼­ÇÔ¿¡ ¹ß½Å ¸ÞÀÏ ÀúÀå
+log_delmail=$2¿¡¼­ $1°³ ¸Þ½ÃÁö »èÁ¦µÊ
+log_send=$1¿¡°Ô ¸ÞÀÏ Àü¼Û ¿Ï·á
diff --git a/mailboxes/lang/pl b/mailboxes/lang/pl
new file mode 100644 (file)
index 0000000..7ec72b3
--- /dev/null
@@ -0,0 +1,95 @@
+mail_title=Poczta u¿ytkownika
+mail_from=Od
+mail_date=Wys³ano
+mail_subject=Temat
+mail_to=Do
+mail_cc=DW
+mail_bcc=UDW
+mail_pri=Priorytet
+mail_highest=Najwy¿szy
+mail_high=Wysoki
+mail_normal=Zwyk³y
+mail_low=Niski
+mail_lowest=Najni¿szy
+mail_for=W $1
+mail_for2=Dla u¿ytkownika $1
+mail_sent=Na li¶cie poczty wys³anej
+mail_size=Rozmiar
+mail_delete=Skasuj wybrane wiadomo¶ci
+mail_compose=Utwórz now± wiadomo¶æ
+mail_return=skrzynki u¿ytkownika
+mail_ecannot=Nie masz uprawnieñ do czytania poczty tego u¿ytkownika
+mail_all=Wybierz wszystko
+mail_invert=Odwróæ zaznaczenia
+mail_search=Znajd¼ wiadomo¶ci, w&nbsp;których
+mail_body=Tre¶æ
+mail_match=zawiera
+mail_ok=Szukaj
+mail_nonefrom=Brak
+mail_mark=Zaznacz wybrane jako:
+mail_mark0=Nie przeczytane
+mail_mark1=Przeczytane
+mail_mark2=Specjalne
+view_title=Czytaj pocztê
+view_desc=Wiadomo¶æ $1 w&nbsp;$2
+view_desc2=Wiadomo¶æ $1 dla u¿ytkownika $2
+view_desc3=Wiadomo¶æ $1
+view_sent=Wiadomo¶æ $1 na li¶cie poczty wys³anej
+view_qdesc=Wiadomo¶æ w&nbsp;kolejce $1
+view_headers=Nag³ówki wiadomo¶ci
+view_attach=Za³±czniki
+view_reply=Odpowiedz
+view_reply2=Odpowiedz wszystkim
+view_enew=Edytuj jako now±
+view_forward=Przeka¿ dalej
+view_delete=Skasuj
+view_strip=Usuñ za³±czniki
+view_ecannot=Nie masz uprawnieñ do czytania poczty tego u¿ytkownika
+view_mark0=Nie przeczytan±
+view_mark1=Przeczytan±
+view_mark2=Specjaln±
+view_return=pierwotnego adresu e-mail
+compose_title=Utwórz wiadomo¶æ
+reply_title=Odpowiedz na wiadomo¶æ
+forward_title=Przeka¿ dalej
+reply_headers=Nag³ówki wiadomo¶ci
+reply_attach=Przekazywane za³±czniki
+reply_attach2=Za³±czniki
+reply_send=Wy¶lij
+reply_ecannot=Nie masz uprawnieñ do czytania poczty jako ten u¿ytkownik
+send_err=Nie uda³o siê wys³aæ wiadomo¶ci
+send_eto=Brak adresu docelowego
+send_efrom=Brak adresu ¼ród³owego
+send_title=Wiadomo¶æ wys³ana
+send_ok=Wiadomo¶æ pomy¶lnie wys³ana do $1
+send_ecannot=Nie masz uprawnieñ do wysy³ania poczty jako ten u¿ytkownik
+send_esmtp=Polecenie SMTP $1 zakoñczy³o sie b³êdem : $2
+send_eattach=£±czny rozmiar za³±czników nie mo¿e przekraczaæ $1 kB.
+send_eperms=U¿ytkownik $1 nie ma uprawnieñ do czytania $2
+send_eperms2=Nie masz uprawnieñ do wys³ania pliku $1
+delete_ecannot=Nie masz uprawnieñ do kasowania poczty tego u¿ytkownika
+delete_enone=Nie wybrano wiadomo¶ci do skasowania
+delete_emnone=Nie wybrano wiadomo¶ci do zaznaczenia
+search_title=Wyniki poszukiwania
+search_ecannot=Nie masz uprawnieñ do przeszukiwania poczty tego u¿ytkownika
+search_ematch=Musisz wprowadziæ poszukiwany tekst
+search_none=Nie znaleziono ¿adnej wiadomo¶ci
+acl_none=¯adne
+acl_same=U¿ytkownika o&nbsp;tej samej nazwie
+acl_all=Wszystkie
+acl_read=U¿ytkownicy, których poczta mo¿e byæ czytana
+acl_users=Tylko u¿ytkownicy
+acl_userse=Oprócz u¿ytkowników
+acl_usersg=Cz³onkowie grupy 
+acl_from=Dozwolone adresy ¼ród³owe
+acl_any=Dowolny adres
+acl_fdoms=Skrzynka @ domeny
+acl_faddrs=Wymienione adresy
+acl_fdom=Dowolny adres @ domena
+acl_fromname=Rzeczywista nazwa dla adresu ¼ród³owego
+acl_apath=Ograniczyæ pliki i&npsp;programy do umieszczonych w&npsp;katalogu
+acl_attach=Maksymalny ³±czny rozmiar za³±czników
+acl_sent=Przechowywaæ wys³an± pocztê w&nbsp;skrzynce
+acl_canattach=Mo¿e do³±czaæ pliki z&nbsp;serwera?
+log_delmail=Usuniêto $1 wiadomo¶ci z&nbsp;$2
+log_send=Wys³ano wiadomo¶æ do $1
diff --git a/mailboxes/lang/pt b/mailboxes/lang/pt
new file mode 100644 (file)
index 0000000..a5daa25
--- /dev/null
@@ -0,0 +1,36 @@
+mail_title=Email do Utilizador
+mail_from=De
+mail_date=Data
+mail_subject=Assunto
+mail_to=Para
+mail_for=Em $1
+mail_compose=Compor novo correio
+mail_return=email do utilizador
+mail_ecannot=Você não está autorizado para ler o correio deste utilizador
+view_title=Ler Correio
+view_desc=Mensagem $1 em $2
+view_qdesc=Mensagem em lista de espera $1
+view_headers=Cabaçalhos de correio
+view_attach=Anexos
+view_reply=Responder ao autor
+view_reply2=Responder a todos
+view_forward=Reenviar
+view_delete=Apagar
+view_ecannot=Você não está autorizado para ler o correio deste utilizador
+compose_title=Compor Email
+reply_title=Responder ao Email
+forward_title=Reenviar Email
+reply_headers=Cabeçalhos de Correio
+reply_attach=Anexos Reenviados
+reply_attach2=Anexos
+reply_send=Enviar
+reply_ecannot=Você não está autorizado para enviar correio como sendo este utilizador
+send_err=Erro ao enviar correio
+send_eto=Falta o endereço do destinatário
+send_title=Correio enviado
+send_ok=O correio foi enviado com sucesso para $1
+send_ecannot=Você não está autorizado para enviar correio como sendo este utilizador
+acl_none=Nenhum
+acl_all=Todos
+acl_read=Utilizadores cujo correio pode ser lido
+acl_users=Utilizadores
diff --git a/mailboxes/lang/sv b/mailboxes/lang/sv
new file mode 100644 (file)
index 0000000..c181366
--- /dev/null
@@ -0,0 +1,75 @@
+mail_title=Användar-e-post
+mail_from=Från
+mail_date=Datum
+mail_subject=Ärende
+mail_to=Till
+mail_cc=Kopia till
+mail_bcc=Osynlig kopia till
+mail_pri=Prioritet
+mail_highest=Högsta
+mail_high=Hög
+mail_low=Låg
+mail_lowest=Lägsta
+mail_for=I $1
+mail_size=Storlek
+mail_delete=Radera angivna brev
+mail_compose=Skriv nytt brev
+mail_return=användarpost
+mail_ecannot=Du får inte läsa e-post till denna användare
+mail_all=Välj allt
+mail_invert=Välj allt utom redan valt
+mail_search=Sök meddelanden där
+mail_body=Brevkroppen
+mail_match=innehåller
+mail_ok=Sök
+mail_nonefrom=Ingen
+view_title=Läs e-post
+view_desc=Brev $1 i $2
+view_qdesc=Köad e-post $1
+view_headers=Rubriker
+view_attach=Bilagor
+view_reply=Svara
+view_reply2=Svara till alla
+view_forward=Skicka vidare
+view_delete=Radera
+view_ecannot=Du får inte läsa e-post till denna användare
+compose_title=Skriv brev
+reply_title=Svara på brev
+forward_title=Skicka vidare brev
+reply_headers=Rubriker
+reply_attach=Vidaresända bilagor
+reply_attach2=Bilagor
+reply_send=Skicka
+reply_ecannot=Du får inte skicka e-post som denna användare
+send_err=Det gick inte att skicka brevet
+send_eto=Mottagaradress saknas
+send_efrom=Avsändaradress saknas
+send_title=Skickat brev
+send_ok=Brevet skickat till $1
+send_ecannot=Du får inte skicka e-post som denna användare
+send_esmtp=SMTP-kommando $1 misslyckades: $2
+send_eattach=Den sammanlagda storleken på bilagorna får inte vara större än $1 kB
+send_eperms=Användare $1 kan inte läsa $2
+send_eperms2=Du får inte skicka filen $1
+delete_ecannot=Du får inte radera e-post från denna användare
+delete_enone=Du har inte angivit vilket meddelande som ska tas bort
+search_title=Sökresultat
+search_ecannot=Du får inte söka i denna användares e-post
+search_ematch=Du måste ange en text som brevet ska innehålla
+search_none=Inget brev passade in på angivna villkor
+acl_none=Inga
+acl_same=Användare med samma namn
+acl_all=Alla
+acl_read=Användare vars e-post får läsas
+acl_users=Endast användare
+acl_userse=Alla utom användare
+acl_usersg=Medlemmar i grupp
+acl_from=Tillåtna avsändaradresser
+acl_any=Alla
+acl_fdoms=brevlåda@domäner
+acl_faddrs=Angivna adresser
+acl_fdom=valfri adress@domän
+acl_apath=Begränsa filer och program till katalog
+acl_attach=Maximal sammanlagd storlek på bilagor 
+log_delmail=Tog bort $1 brev från $2
+log_send=Skickade e-post till $1
diff --git a/mailboxes/lang/tr b/mailboxes/lang/tr
new file mode 100644 (file)
index 0000000..9058a1c
--- /dev/null
@@ -0,0 +1,356 @@
+acl_all=Hepsi
+acl_any=Herhangi bir adres
+acl_apath=Dizin için program ve dosya limitleri
+acl_asame=Kullanýcý adýyla ayný
+acl_attach=En çok toplam posta ek dosyalarý boyutu
+acl_canattach=Sunucu-sayfalarý dosyalarýný ekleyebilir mi?
+acl_candetach=Dosyalarý sunucuya gönderebilir mi?
+acl_dir=Dizindeki posta dosyalarýný okuyabilir mi
+acl_dirauto=Otomatik olarak karar ver (bütün kullanýcýlarý burda veya baþka yerde görmek istemiyorsanýz)
+acl_faddrs=Listelenmiþ alanlar
+acl_fdom=Herhangi adres @ alan
+acl_fdoms=Posta kutusu @ alanlar
+acl_from=Ýzin verilebilir From: adresleri
+acl_fromname=Gönderen adresi için gerçek isim
+acl_none=Hiçbiri
+acl_read=Kullanýcýlarýn okuyabilecekleri postalar
+acl_same=Ayný isimli kullanýcý
+acl_sec=Ýkincil gruplarý içer?
+acl_sent=Gönderilen postalarý depola
+acl_unlimited=Sýnýrsýz
+acl_users=Sadece kullanýcýlar
+acl_userse=Kullanýcýlardan baþka herkes
+acl_usersg=Gruplarýn üyeleri
+acl_usersm=Kullanýcý eþlemeleri
+acl_usersu=Aralýkta UID'le beraber
+black_already=$1 e-posta adresi SpamAssassin'in yasaklý listesindedir.
+black_done=SpamAssassin tarafýndan yasaklanan adresler listesine $1 e-posta adresi eklendi.
+black_title=Göndereni Yasaklama
+compose_title=E-posta Gönder
+confirm_ok=Þimdi Sil
+confirm_title=Silmeyi Onayla
+confirm_warn=$1 seçilmiþ mesajý silmeyi istiyor musun?
+confirm_warn2=Posta kutusunun boyutu ve biçimi yüzünden, bu biraz zaman alýr. Silme bitene kadar, diger uygulamalar calýþtýrýlmamalýdýr.
+confirm_warn3=Bu mesajý silmek istediðinizden emin misiniz?
+confirm_warn4=Silme bitene kadar, diger uygulamalar calýþtýrýlmamalýdýr.
+confirm_warnall=Bu dosyadaki tüm e-postalarý silmek istediðinizden emin misiniz?
+delall_ok=Þimdi Sil
+delall_rusure=$1'dan gelen $2 adet, toplamý $3 olan postalarý silmek istediðinizden emin misiniz.
+delall_title=Tüm E-posta'yý Sil
+delete_ebnone=Yasaklama için hiçbir posta seçilmedi
+delete_ecannot=Bu kullanýcý olarak e-posta silmek için izininiz yoktur
+delete_ecopycannot=Bu kullanýcýya posta kopyalama izniniz yok
+delete_ecopynone=Kopyalama için seçilen posta yok
+delete_ecopyuser=Posta kopyalanacak kullanýcý mevcut deðildir
+delete_efnone=Yönlendirme hiçbir posta seçilmemiþ
+delete_ehnone=Rapor için seçilmiþ posta yoktur
+delete_emnone=Ýþaretlenecek posta yoktur
+delete_emovecannot=Postayý belirlenmiþ kullanýcýya taþýmanýza izin verilmiyor
+delete_emovenone=Taþýma için hiç posta seçilmemiþ
+delete_emoveuser=Taþýnan mail için kullanýcý mevcut deðildir
+delete_enone=Silmek için posta silinmedi
+delete_ereport=Spam olarak raporlanamadý : $1
+delete_ernone=Spam olarak rapor edilmiþ posta yoktur
+delete_errc=Posta kopyalama baþarýsýz
+delete_errm=Posta taþýma baþarýsýz
+delete_ewnone=Ýzin için seçilmiþ posta yoktur
+delete_nobutton=Hiçbir tuþa basýlmadý
+delete_ok=Þimdi Sil
+delete_rusure=$2'nin seçilmiþ $1 mesajýný silmek istediðinizden emin misiniz? Dosya büyük bir dosya olduðu için silme iþlemi biraz zaman alabilir. Silme iþlemi bitinceye kadar, baska bir program caliþtýrmayýn.
+delete_rusure2=$1'den gelen mesajý silmek istediðinizden emin misiniz? Dosya boyutunun büyüklüðü yüzünden silme iþlemi uzun sürebilir. Silme iþlemi bitene kadar baþka bir program caliþtýrmayýn.
+delete_title=Postayý  Sil
+detach_edir=Saklamak için dosya veya dizin girilmedi
+detach_eopen=$1 : $2 yi açmak baþarýsýz oldu
+detach_err=Dosyayý çýkartýrken hata oluþtu
+detach_ewrite=$1'e yazarken hata oluþtu : $2
+detach_ok=Sunucu taraflý $1 dosyasýna eklenti yaz ($2).
+detach_title=Dosyayý Çýkar
+emodified=Bu dizin son gözden geçirildiðinden beri deðiþtirildi! <a href='$1'>posta liste</a>'ye geri dön ve tekrar dene.
+enew_title=E-Postayý Düzenle
+find_enone=Aramanýza uyan kullanýcý bulunamadý
+find_fcount=Dizinler
+find_group=Grup
+find_home=Ev dizini
+find_in=$1 içinde $2
+find_incount=E-postalar
+find_real=Gerçek isim
+find_results=$1 için yapýlan aramaya uyan kullanýcýlar ..
+find_sentcount=Gönderildi
+find_size=Posta boyutu
+find_title=Arama Sonuçlarý
+find_user=Kullanýcý Adý
+folder_drafts=Taslaklar
+folder_inbox=Gelen Kutusu
+folder_sent=Gönderilen postalar
+folder_trash=Çop Kutusu
+forward_title=E-postayý Döndür
+ham_report=Bu mesajý Razor ve SpamAssassin veritabanlarýna "spam deðil" olarak raporla ..
+ham_title=Ýstenen Raporlama
+index_contains=içeriyor
+index_empty=Posta yok
+index_eperl=Seçilen STMP doðrulama modu için $1 Perl modülüne ihtiyaç. <a href='$2'>buraya týklayara yükleyebilirsiniz</a>
+index_equals=Eþitlik
+index_esystem=Sisteminizde desteklenen posta sunucularý (Qmail, Postfix ve Sendmail) yoktur. Posta sunucusunu ve mail yolunu açmak için <a href='$1'>modül yapýlandýrmasý</a>'ný elle ayarlamaya gereksinim duyacaksýnýz.
+index_esystem2=<a href='$1'>modül yapýlandýrma</a>'daki mail sunucu ayarý sisteminizde bulunamadý. Doðru sunucuyu kullanmak için bu yapýlandýrmayý ayarlamanýz gerekmektedir.
+index_esystem3=<a href='$1'>bu adresteki</a> posta sistem ayarýna baglanýrken hata oluþtu : $2
+index_file=Dosyadaki postayý oku:
+index_find=Kullanýcýlarý bul
+index_header=Kullanýcý posta kutularý
+index_none=Bu sistemde herhangi bir kullanýcýnýn e-posta'sýný okuma hakkýnýz yoktur.
+index_nousers=Hiçbir kullanýcý bulunamadý'
+index_nousersmail=E-posta'sý olan hiçbir kullanýcý bulunamadý.
+index_return=kullanýcý listesi
+index_system0=Mail sunucusu: Postfix
+index_system1=Mail sunucusu: Sendmail
+index_system2=Posta sunucusu: Qmail
+index_title=Kullanýcý Postasýný Oku
+index_toomany=Sisteminizde bir sayfada görüntülenemeyecek kadar çok kullanýcýnýz var.
+ldap_ebase=LDAP tabanlý DN ayarý modül yapýlandýrýlmasýnda yoktur
+ldap_econn=$2 portundaki LDAP sunucusu $1'e baðlanýrken hata oluþtu
+ldap_ehost=Modül yapýlandýrýlmasýnda LDAP sunucu ayarý yoktur
+ldap_elogin=LDAP sunucusu $1'e $2 olarak baðlanýrken hata oluþtu : $3
+ldap_emod=LDAP'a baðlanmak için gerekli Perl modülü $1 kayýp
+ldap_eport=Modül yapýlandýrýlmasýnda geçerli LDAP sunucu portu yok
+ldap_euser=Modül yapýlandýrýlmasýnda LDAP giriþ ayarý yoktur
+log_copymail=$1 adet mesaj $2' den $3' e kopyalandý
+log_delmail=$2'den $1 mesajý silindi
+log_movemail=$1 adet mesaj $2' den $3' e taþýndý
+log_read=$1 için postalarý oku
+log_send=$1'e posta gönderildi
+mail_addresses=Adres Defterini Yönet
+mail_advanced=Geliþmiþ Arama
+mail_all=Hepsini seç
+mail_bcc=Bcc
+mail_black=Gönderenleri Yasakla
+mail_body=Mesaj içeriði
+mail_cc=Cc
+mail_compose=Yeni posta oluþtur
+mail_copy=Kopyala:
+mail_crypt=Þifrele:
+mail_date=Tarih
+mail_delall=Hepsini Sil
+mail_delete=Seçilen mesajlarý sil
+mail_deleteall=Hepsini Sil
+mail_deltrash=Çöpü Boþalt
+mail_ecannot=Bu kullanýcýnýn e-postasýný okumak için izininiz yoktur
+mail_eexists=Mesaj artýk mevcut deðil!
+mail_efile=Posta dosyasý mevcut deðil
+mail_err=Bu klasörde listelenen maillerde bir hata oluþtu : $1
+mail_esystem=$1 posta sistemine baðlanýrken hata oluþtu. Bu hata sistem yöneticisi tarafýndan düzeltilmelidir.
+mail_fchange=Deðiþtir
+mail_folder=Dizin
+mail_folders=Dizinleri Yönet
+mail_for=$1'de
+mail_for2=Kullanýcý $1 için
+mail_forward=Yönlendir
+mail_from=Nereden
+mail_fromsrch=Ayný gönderici..
+mail_high=Yüksek
+mail_highest=En yüksek
+mail_indexlink=Posta kutusuna geri dön
+mail_invert=Seçimi ters çevir
+mail_jump=Sayfaya atla :
+mail_level=Skor
+mail_login=Sisteme Giriþ
+mail_logindesc=$1 posta sunucusunda posta kutunuzdaki<br>mailinize eriþmek için bir kullanýcý ve þifre girmelinýz.
+mail_loginheader=POP3 sunucusuna giriþ yap
+mail_loginheader2=IMAP sunucusuna giriþ yap
+mail_loginmailbox=IMAP posta kutusu
+mail_loginpass=Þifre
+mail_loginuser=Kullanýcý adý
+mail_logout=POP3 giriþini deðiþtir
+mail_logout2=IMAP giriþini deðiþtir
+mail_low=Düþük
+mail_lowest=en düþük
+mail_mark=Ýþaretle:
+mail_mark0=Okunmamýþ
+mail_mark1=Okunmuþ
+mail_mark2=Özel
+mail_match=eþleþmeler
+mail_move=Taþý:
+mail_nocrypt=&lt;Þifreleme yapma&gt;
+mail_none=$1'de bu kullanýcýnýn mesajý yoktur
+mail_nonefrom=Hiçbiri
+mail_normal=Normal
+mail_nosign=&lt;Ýmzalama&gt;
+mail_nosort=Yeniden sýrala.
+mail_of=için
+mail_ok=Ara
+mail_open=Aç
+mail_pos=$4 içerisinde $3 mesajdan $1'den $2'ye kadar olanlar
+mail_pri=Öncelik
+mail_replyto=Yanýt adresi
+mail_reset=Temizle
+mail_return=Kullanýcý e-posta
+mail_return2=Kullanýcý E-postasý
+mail_rfc=Satýrdan
+mail_samecrypt=&lt;Varýþ yerlerinden anahtarlar&gt;
+mail_search=Mesajlarýn aranýlacaðý yer
+mail_search2=Araþtýr:
+mail_search3=Yukarýdaki skorla bul:
+mail_sent=Gönderilmiþ mesaj listesinde
+mail_sig=Ýmzayý Düzenle
+mail_sign=Anahtarla iþaretle:
+mail_size=Boyut
+mail_subject=Konu
+mail_subsrch=Ayný konu..
+mail_title=Kullanýcý E-Posta
+mail_to=Nereye
+mail_tosrch=Ayný alýcý..
+mail_unknown=Bilinmeyen
+mail_white=Gönderenlere Ýzin Ver
+razor_deleted=.. yapýldý, ve mesaj silindi.
+razor_done=.. yapýldý
+razor_err=.. hata oluþtu! Hata mesajýnýn nedenini yukarýda görebilirsiniz.
+razor_report=Bu mesajý Razor ve diðer SpamAssassin spam-bloklama veritabanlarýna raporla ..
+razor_report2=Seçili mesajlarý Razor ve diðer SpamAssassin spam-bloklama veritabanlarýna raporla ..
+razor_report3=Seçili mesajlarý Razor ve diðer SpamAssassin spam-bloklama veritabanlarýndan kaldýr ..
+razor_title=Spam Olarak Raporla
+razor_title2=Seçilmiþ Rapor
+reply_attach=Döndürülmüþ ekler
+reply_attach2=Ekler
+reply_body=Mesaj metni
+reply_draft=Taslak olarak Kaydet
+reply_ecannot=Bu kullanýcý olarak e-posta göndermek için izininiz yoktur
+reply_efwdnone=Yönlendirilmiþ postalarýn hiçbiri mevcut deðil
+reply_errc=Posta kopyalama hatasý
+reply_errm=Posta taþýma hatasý
+reply_headers=Posta baþlýklarý
+reply_mailforward=Yönlendirilmiþ mesajlar
+reply_return=posta oluþtur
+reply_send=Gönder
+reply_spell=Heceleme hatalarýný kontrol et?
+reply_title=E-postayý Geri Gönder
+search_all=Bütün dizinlerde
+search_allstatus=Herhangi
+search_ecannot=Bu kullanýcý e-postalarýný aramak için izininiz yoktur
+search_efield=Bir arama tipi seçmelisiniz.
+search_elatest=Arama için hatalý veya gecersiz sayýda mesaj
+search_ematch=Takip eden kutuya bir yazý girmelisiniz
+search_enone=Arama kriteri girilmedi
+search_escore=Hatalý veya geçersiz spam skoru
+search_ewhat=$1 satýrý için karþýlaþtýrýlacak metin yok
+search_latest=Aranacak mesajlar
+search_latestnum=Sadece en son
+search_limit=(son $1 mesajdan)
+search_local=Yerel klasörlerde
+search_msg2=$1 için arama sonuçlarý
+search_msg4=Arama sonuçlarý
+search_msg5=Spam skoru $1 için arama sonuçlarý
+search_nolatest=Klasördekilerin hepsi
+search_none=Mesaj bulunamadý.
+search_onestatus=Sadece durum
+search_results2=$2'ye uyan $1 posta mesajý bulundu
+search_results3=$1 eposta mesajý $2'yi karþýlamadý
+search_results4=$1 posta mesajý aramanýzla uyumludur
+search_results5=$2'nin $3'ü karþýladýðý durumda $1 adet mesaj bulundu
+search_status=Durumla beraber
+search_title=Arama sonuçlarý
+search_withstatus=, durum kodu $1 olan
+send_done=.. yapýldý.
+send_draft=$1'e gönderilecek mesaj taslak dizinine kaydedildi.
+send_drafting=$1'e gönderilecek postayý taslak dizinine kaydediyor ..
+send_eattach=Ek dosyalarýn boyutu $1 KB'den daha büyük olmamalýdýrlar.
+send_eattachsize=Mail eklentisi maksimum boyut olan $1'i aþtý
+send_ecannot=Bu kullanýcý olarak e-posta göndermek için izininiz yoktur
+send_ecrypt=Mesajý þifrelerken hata oluþtu : $1
+send_efile=Eklentiyi okurken hata oluþtu $1 : $2
+send_efrom=Nereden adresini girmelisiniz
+send_ekey=$1 e-posta için anahtar bulunamamýþtýr
+send_eline=$1. satýrda :
+send_epass=Bu mesajý imzalayamazsýnýz cünkü geçerli parolanýz GnuPG modülünde tanýmlanmamýþtýr.
+send_epath=Sendmail çalýþtýrýlabilir dosyasý $1 mevcut deðildir.
+send_eperms=$1 kullanýcýsý $2'yi okuyamaz
+send_eperms2=$1 dosyasýný göndermeniz için izin verilmedi
+send_err=Postanýn gönderilmesinde hata oluþtu
+send_esign=Mesajý imzalarken hata oluþtu : $1
+send_esmtp=SMTP komutu $1'de hata oluþtu : $2
+send_espell=Mesajýnýzda heceleme hatalarý bulundu ..
+send_esubject=Hatalý eposta konusu
+send_eto=Nereye adresini girmelisiniz
+send_eword=$1 yanlýþ hecelendi
+send_eword2=$1 yanlýþ hecelendi. Olasý düzeltme $2'dir
+send_ok=Posta $1'e baþarýyla gönderildi
+send_sending=$1'e posta gönderiliyor ..
+send_title=Posta Gönder
+sform_all=&lt;Bütün klasörler&gt;
+sform_and=Aþaðýdaki tüm kriterleri karþýlayan mesajlarý bul ..
+sform_body=mesaj gövdesi
+sform_cc=Cc: baþlýk
+sform_date=Tarih: baþlýk
+sform_folder=dizin(ler)de
+sform_from=Kimden: baþlýk
+sform_headers=herhangi baþlýk
+sform_local=&lt;Yerel klasörler&gt;
+sform_neg0=içeriyor
+sform_neg1=içermez
+sform_ok=Þimdi Ara
+sform_or=Aþaðýdaki bazý kriterleri karþýlayan mesajlarý bul ..
+sform_return=geliþmiþ arama formu
+sform_size=mesaj boyutu
+sform_subject=Konu: Baþlýk
+sform_text=metin
+sform_title=Geliþmiþ Arama
+sform_to=Kime: baþlýk
+sform_where=Nerede
+view_allheaders=Bütün baþlýklarý gözden geçir
+view_ashtml=HTML olarak görüntüle
+view_astext=Metin olarak görüntüle
+view_attach=Ekler
+view_black=Göndereni Yasakla
+view_body=Mesaj metni
+view_crypt=GnuPG posta þifre açma
+view_crypt_1=Mesaj þifrelendi, fakat GnuPG desteði yüklenmedi.
+view_crypt_2=þifeyi açmak için hata oluþtu : $1
+view_crypt_3=Posta þifresi baþarýlý bir þekilde açýldý
+view_crypt_4=Mesajýn þifreli bölümü baþarýlý bir þekilde açýldý.
+view_dall=&lt;Bütün dosyalar&gt;
+view_delete=Sil
+view_desc=Mesaj $1, $2'de
+view_desc2=$2 kullanýcýsý için $1 mesajý
+view_desc3=Mesaj $1
+view_detach=Ayrýlan dosya:
+view_diagnostic-code=Hata nedeni
+view_dir=sunucu dosyasý ve ya dizini:
+view_dstatus=Hatalý teslimat durumu
+view_dstatusok=Baþarýlý teslimat durumu
+view_ecannot=Bu kullanýcýnýn e-postasýný okumak için izininiz yoktur
+view_egone=Bu mesaj artýk mevcut deðil
+view_enew=Yeni olarak düzenle
+view_eugone=Bu kullanýcý mevcut deðildir
+view_final-recipient=Son alýcý
+view_folder=Posta kutusuna geri dön
+view_forward=Döndür
+view_gnupg=GnuPG imza doðrulamasý
+view_gnupg_0=$1'in imzasý geçerli.
+view_gnupg_1=$1'in imzasý geçerlidir, fakat güven iliþkisi kurulamadý.
+view_gnupg_2=$1'in imzasý geçerli <b>DEÐÝLDÝR</b>.
+view_gnupg_3=Anahtar kodu $1 listenizde yoktur, bu yüzden imza dogrulanamadý.
+view_gnupg_4=Ýmzayý doðrularken hata oluþtu : $1
+view_ham=Ýstenen Rapor
+view_headers=Posta baþlýklarý
+view_mark=Ýþaretle:
+view_mark0=Okunmamýþ
+view_mark1=Oku
+view_mark2=Özel
+view_noheaders=Temel baþlýklarý gözden geçir
+view_print=Yazdýr\r
+
+view_qdesc=Kuyruða konulmuþ mesaj $1
+view_raw=Okunmamýþ mesaja gözat
+view_razor=Spam Olarak Raporla
+view_razordel=Çop Postalarý Raporlama ve Sil
+view_recv=<a href='$2'>Anahtar sunucusundan $1 kodlu anahtarý al</a>.
+view_remote-mta=Uzak posta sunucusu
+view_reply=Geri gönder
+view_reply2=Hepsine geri gönder
+view_reporting-mta=Raporlanan posta sunucusu
+view_return=orjinal eposta
+view_sent=$1 mesajý gönderilmiþ posta listesinde
+view_strip=Eklentileri Çýkar
+view_sub=Baðlý E-posta
+view_title=E-posta oku
+view_white=Gönderene Ýzin Ver
+white_already=$1 e-posta adresi SpamAssassin'in izinli adres listelerindedir.
+white_done=$1 e-posta adresi SpamAssassin'in izinli adres listelerine eklendi.
+white_title=Ýzinli Gönderici
diff --git a/mailboxes/lang/zh_CN b/mailboxes/lang/zh_CN
new file mode 100644 (file)
index 0000000..92b1163
--- /dev/null
@@ -0,0 +1,106 @@
+mail_title=Óû§Óʼþ
+mail_from=À´×Ô
+mail_date=ÈÕÆÚ
+mail_subject=Ö÷Ìâ
+mail_to=·¢Íù
+mail_cc=תËÍ
+mail_bcc=ÃÜËÍ
+mail_pri=ÓÅÏȼ¶
+mail_highest=×î¸ß
+mail_high=¸ß
+mail_normal=Ò»°ã
+mail_low=µÍ
+mail_lowest=×îµÍ
+mail_for=ÔÚ $1
+mail_for2=ÓÃÓÚÓû§$1
+mail_sent=ÔÚ·¢ËÍÓʼþÁбíÖÐ
+mail_size=´óС
+mail_delete=ɾ³ýÑ¡¶¨µÄÓʼþ
+mail_compose=±àдÐÂÓʼþ
+mail_return=Óû§ÓÊÏä
+mail_ecannot=ÄúûÓÐÔĶÁ¸ÃÓû§µÄÓʼþµÄȨÏÞ
+mail_all=È«²¿Ñ¡ÖÐ
+mail_invert=ÄæÏòÑ¡È¡
+mail_search=Ñ°ÕÒÓʼþ£¬ÆäÖÐ
+mail_body=ÕýÎÄ
+mail_match=Æ¥Åä
+mail_ok=ËÑË÷
+mail_nonefrom=ÎÞ
+mail_mark=½«ÒÑÑ¡ÖеÄÓʼþ±ê¼ÇΪ£º
+mail_mark0=δÔĶÁ
+mail_mark1=ÔĶÁ
+mail_mark2=Ìرð
+mail_forward=ת·¢ÒÑÑ¡ÔñµÄ
+mail_rfc=¼Ä¼þÈËÐÐ
+view_title=ÔĶÁÓʼþ
+view_desc=$2ÖеÄÓʼþ$1
+view_desc2=Óû§$2µÄÓʼþ$1
+view_desc3=Óʼþ$1
+view_sent=ÒÑ·¢ËÍÓʼþÁбíÖеÄÓʼþ$1
+view_qdesc=´ý·¢µÄÓʼþ $1
+view_headers=ÓʼþÍ·
+view_allheaders=²é¿´ËùÓÐÓʼþÍ·
+view_noheaders=²ì¿´»ù±¾ÓʼþÍ·
+view_attach=¸½¼þ
+view_reply=»Ø¸´
+view_reply2=»Ø¸´¸øËùÓÐ
+view_enew=±à¼­ÐÂÓʼþ
+view_forward=ת·¢
+view_delete=ɾ³ý
+view_strip=ÒƳý¸½¼þ
+view_ecannot=ÄúûÓÐÔĶÁ¸ÃÓû§µÄÓʼþµÄȨÏÞ
+view_mark0=δÔĶÁ
+view_mark1=ÔĶÁ
+view_mark2=Ìرð
+view_return=Ô­¼þ
+view_sub=´øÓи½¼þµÄÓʼþ
+compose_title=±àдÓʼþ
+reply_title=»Ø¸´Óʼþ
+forward_title=ת·¢Óʼþ
+reply_headers=ÓʼþÍ·
+reply_attach=ת·¢µÄ¸½¼þ
+reply_mailforward=ÒÑת·¢µÄÓʼþ
+reply_attach2=¿Í»§¶ËºÍ·þÎñÆ÷µÄ¸½¼þ
+reply_send=·¢ËÍ
+reply_ecannot=ÄúûÓÐÒÔÕâ¸öÓû§µÄÉí·Ý·¢ËÍÓʼþµÄȨÏÞ
+send_err=ÎÞ·¨·¢ËÍÓʼþ
+send_eto=δÊäÈë¡°·¢Íù¡±µØÖ·
+send_efrom=¶ªÊ§¡°À´×Ô¡±µØÖ·
+send_title=ÒÑ·¢Ë͵ÄÓʼþ
+send_ok=ÓʼþÒѳɹ¦·¢Ë͵½ $1
+send_ecannot=ÄúûÓÐÒÔÕâ¸öÓû§µÄÉí·Ý·¢ËÍÓʼþµÄȨÏÞ
+send_esmtp=SMTP ÃüÁî $1 Ê§°Ü£º$2
+send_eattach=¸½¼þ´óС×ÜÁ¿²»Äܳ¬¹ý $1 kB¡£
+send_eperms=Óû§ $1 ²»ÄÜÔĶÁ $2
+send_eperms2=ÄúûÓз¢ËÍÎļþ$1µÄȨÏÞ
+send_epath=Sendmail³ÌÐò $1²»´æÔÚ¡£
+delete_ecannot=ÄúûÓÐɾ³ýÕâ¸öÓû§µÄÓʼþµÄȨÏÞ
+delete_enone=ûѡÔñҪɾ³ýµÄÓʼþ
+delete_emnone=ûÓÐÑ¡ÔñÒª±ê¼ÇµÄÓʼþ
+search_title=ËÑË÷½á¹û
+search_ecannot=ÄúûÓÐËÑË÷¸ÃÓû§µÄÓʼþµÄȨÏÞ
+search_ematch=Äú±ØÐëÔÙ´ÎÊäÈëÆ¥ÅäµÄÎı¾¡£
+search_none=ÕÒ²»µ½Óʼþ¡£
+search_results2=$1ÓʼþÆ¥Åä$2
+search_results3=$1Óʼþ²»Æ¥Åä$2
+acl_none=ÎÞ
+acl_same=ͬÃûµÄÓû§
+acl_all=ËùÓÐ
+acl_read=¿ÉÔĶÁÆäÓʼþµÄÓû§
+acl_users=½öÏÞÓû§
+acl_userse=ËùÓÐÓû§³ýÁË
+acl_usersg=×éµÄ³ÉÔ±
+acl_from=ÔÊÐí¡°À´×Ô¡±µØÖ·
+acl_any=ÈκεØÖ·
+acl_fdoms=ÓÊÏä @ Óò
+acl_faddrs=ÁгöµÄµØÖ·
+acl_fdom=ÈκεØÖ· @ Óò
+acl_fromname=À´Ô´µØÖ·µÄÕæÃû
+acl_apath=ÏÞÖÆÎļþºÍ³ÌÐòµ½Ä¿Â¼
+acl_attach=×î´ó¸½¼þ×Ü´óС
+acl_sent=ÔÚÓÊÏäÖд¢´æÒÑ·¢Ë͵ÄÓʼþ
+acl_canattach=¿ÉÒÔÕ³¸½·þÎñÆ÷ÎļþÂð£¿
+acl_usersm=Æ¥ÅäµÄÓû§
+acl_asame=ÓëÓû§Ïàͬ
+log_delmail=ÒÑ´Ó $2 É¾³ý $1Óʼþ 
+log_send=ÒÑ·¢ËÍÓʼþµ½ $1
diff --git a/mailboxes/lang/zh_CN.UTF-8 b/mailboxes/lang/zh_CN.UTF-8
new file mode 100644 (file)
index 0000000..fe79678
--- /dev/null
@@ -0,0 +1,106 @@
+mail_title=用户邮件
+mail_from=来自
+mail_date=日期
+mail_subject=主题
+mail_to=发往
+mail_cc=转送
+mail_bcc=密送
+mail_pri=优先级
+mail_highest=最高
+mail_high=高
+mail_normal=一般
+mail_low=低
+mail_lowest=最低
+mail_for=在 $1
+mail_for2=用于用户$1
+mail_sent=在发送邮件列表中
+mail_size=大小
+mail_delete=删除选定的邮件
+mail_compose=编写新邮件
+mail_return=用户邮箱
+mail_ecannot=您没有阅读该用户的邮件的权限
+mail_all=全部选中
+mail_invert=逆向选取
+mail_search=寻找邮件,其中
+mail_body=正文
+mail_match=匹配
+mail_ok=搜索
+mail_nonefrom=无
+mail_mark=将已选中的邮件标记为:
+mail_mark0=未阅读
+mail_mark1=阅读
+mail_mark2=特别
+mail_forward=转发已选择的
+mail_rfc=寄件人行
+view_title=阅读邮件
+view_desc=$2中的邮件$1
+view_desc2=用户$2的邮件$1
+view_desc3=邮件$1
+view_sent=已发送邮件列表中的邮件$1
+view_qdesc=待发的邮件 $1
+view_headers=邮件头
+view_allheaders=查看所有邮件头
+view_noheaders=察看基本邮件头
+view_attach=附件
+view_reply=回复
+view_reply2=回复给所有
+view_enew=编辑新邮件
+view_forward=转发
+view_delete=删除
+view_strip=移除附件
+view_ecannot=您没有阅读该用户的邮件的权限
+view_mark0=未阅读
+view_mark1=阅读
+view_mark2=特别
+view_return=原件
+view_sub=带有附件的邮件
+compose_title=编写邮件
+reply_title=回复邮件
+forward_title=转发邮件
+reply_headers=邮件头
+reply_attach=转发的附件
+reply_mailforward=已转发的邮件
+reply_attach2=客户端和服务器的附件
+reply_send=发送
+reply_ecannot=您没有以这个用户的身份发送邮件的权限
+send_err=无法发送邮件
+send_eto=未输入“发往”地址
+send_efrom=丢失“来自”地址
+send_title=已发送的邮件
+send_ok=邮件已成功发送到 $1
+send_ecannot=您没有以这个用户的身份发送邮件的权限
+send_esmtp=SMTP 命令 $1 失败:$2
+send_eattach=附件大小总量不能超过 $1 kB。
+send_eperms=用户 $1 不能阅读 $2
+send_eperms2=您没有发送文件$1的权限
+send_epath=Sendmail程序 $1不存在。
+delete_ecannot=您没有删除这个用户的邮件的权限
+delete_enone=没选择要删除的邮件
+delete_emnone=没有选择要标记的邮件
+search_title=搜索结果
+search_ecannot=您没有搜索该用户的邮件的权限
+search_ematch=您必须再次输入匹配的文本。
+search_none=找不到邮件。
+search_results2=$1邮件匹配$2
+search_results3=$1邮件不匹配$2
+acl_none=无
+acl_same=同名的用户
+acl_all=所有
+acl_read=可阅读其邮件的用户
+acl_users=仅限用户
+acl_userse=所有用户除了
+acl_usersg=组的成员
+acl_from=允许“来自”地址
+acl_any=任何地址
+acl_fdoms=邮箱 @ 域
+acl_faddrs=列出的地址
+acl_fdom=任何地址 @ 域
+acl_fromname=来源地址的真名
+acl_apath=限制文件和程序到目录
+acl_attach=最大附件总大小
+acl_sent=在邮箱中储存已发送的邮件
+acl_canattach=可以粘附服务器文件吗?
+acl_usersm=匹配的用户
+acl_asame=与用户相同
+log_delmail=已从 $2 删除 $1邮件 
+log_send=已发送邮件到 $1
diff --git a/mailboxes/lang/zh_TW.Big5 b/mailboxes/lang/zh_TW.Big5
new file mode 100644 (file)
index 0000000..7530c38
--- /dev/null
@@ -0,0 +1,265 @@
+acl_all=¥þ³¡
+acl_any=¥ô¦ó¶l¥ó¦ì§}
+acl_apath=­­¨îÀɮשMµ{¦¡¨ì¥Ø¿ý
+acl_asame=»P¨Ï¥ÎªÌ¬Û¦P
+acl_attach=³Ì¤jªþ¥óÁ`¤j¤p
+acl_canattach=¥i¥Hªþ¥[¦øªA¾¹ÀɶܡH
+acl_candetach=¥i¥Hªþ±aÀɮרì¦øªA¾¹?
+acl_faddrs=¦C¥Xªº¶l¥ó¦ì§}
+acl_fdom=¥ô¦ó¶l¥ó¦ì§}¦bºô°ì
+acl_fdoms=«H½c¦bºô°ì
+acl_from=¨Ì¾Ú¶l¥ó¦ì§}¤¹³\
+acl_fromname=¨Ó·½¦a§}ªº¯u¹ê¦WºÙ
+acl_none=µL
+acl_read=¥i¥HŪ¨ú­þ¨Ç¨Ï¥ÎªÌªº¶l¥ó
+acl_same=¦P¦Wªº¨Ï¥ÎªÌ
+acl_sent=¦b¶l½c¤¤Àx¦s¤wµo°eªº¶l¥ó
+acl_users=¥u¦³¨Ï¥ÎªÌ
+acl_userse=¥þ³¡, °£¤F¨Ï¥ÎªÌ
+acl_usersg=²Õªº¦¨­û
+acl_usersm=²Å¦Xªº¨Ï¥ÎªÌ
+acl_usersu=¦bUID½d³ò¤º
+compose_title=¼g§@¹q¤l¶l¥ó
+confirm_ok=²{¦b§R°£
+confirm_title=½T»{§R°£
+confirm_warn=±z½T©w­n§R°£ $1 «Ê¿ï¨úªº°T®§?
+confirm_warn2=¦]¬°±zªº«H½c¤j¤p»P®æ¦¡¡A»Ý­n¤@ÂI®É¶¡¡Aª½¨ì§R°£§¹¦¨«á¡A¤£·|°õ¦æ¨ä¥L°Ê§@
+confirm_warn3=±z½T©w­n§R°£°T®§
+confirm_warn4=ª½¨ì§R°£§¹¦¨¡A¤£·|°õ¦æ¨ä¥L°Ê§@¡C
+confirm_warnall=±z½T©w­n§R°£¸ê®Æ§¨¤¤©Ò¦³°T®§?
+delete_ecannot=±z¤£³Q¤¹³\§R°£³o­Ó¨Ï¥ÎªÌªº¶l¥ó
+delete_ecopycannot=±z¤£³Q¤¹³\½Æ»s¶l¥ó¨ì¯S©wªº¨Ï¥ÎªÌ
+delete_ecopynone=¥¼¿ï¨ú½Æ»s¶l¥ó
+delete_ecopyuser=½Æ»s¶l¥ó¨Ï¥ÎªÌ¤£¦s¦b
+delete_efnone=¥¼¿ï¨úÂà±H¶l¥ó
+delete_emnone=¨S¦³¿ï¾Ü­n¼Ð°Oªº¶l¥ó
+delete_emovecannot=±z¤£³Q¤¹³\²¾°Ê¶l¥ó¨ì¯S©wªº¨Ï¥ÎªÌ
+delete_emovenone=¥¼¿ï²¾°Ê±H¶l¥ó
+delete_emoveuser=¨Ï¥ÎªÌ²¾°Ê¶l¥ó¤£¦s¦b
+delete_enone=¨S¦³¿ï¾Ü­n§R°£ªº¶l¥ó
+delete_nobutton=¥¼«ö«ö¶s
+delete_ok=²{¦b§R°£
+delete_rusure=±z½T©w­n±q$2§R°£ $1 «Ê¿ï¨úªº°T®§?¤jÀɮתº¶l¥ó»Ý­n¤@¨Ç®É¶¡¡Aª½¨ì§R°£§¹¦¨¡A¤£·|°õ¦æ¨ä¥L°Ê§@¡C
+delete_rusure2=±z½T©w­n§R°£ $1 «Ê¿ï¨úªº°T®§?¤jÀɮתº¶l¥ó»Ý­n¤@¨Ç®É¶¡¡Aª½¨ì§R°£§¹¦¨¡A¤£·|°õ¦æ¨ä¥L°Ê§@¡C
+delete_title=§R°£¶l¥ó
+detach_edir=¨S¦³¿é¤JÀx¦sªºÀɮשΥؿý
+detach_eopen=¶}©l$1¥¢±Ñ:$2
+detach_err=ªþ¥[ÀÉ®×¥¢±Ñ
+detach_ewrite=¼gµ¹$1¥¢±Ñ:$2
+detach_ok=±q¦øªAºÝªþ¥[ÀÉ®×$1($2)
+detach_title=ªþ¥[ÀÉ®×
+enew_title=½s¿è¶l¥ó
+find_enone=¨S¦³±z·j´M©Ò²Å¦Xªº¨Ï¥ÎªÌ
+find_group=¸s²Õ
+find_home=®a¥Ø¿ý
+find_real=¯u¹ê¦WºÙ
+find_results=·j´M$1²Å¦Xªº¨Ï¥ÎªÌ..
+find_size=¶l¥ó¤j¤p
+find_title=·j´Mµ²ªG
+find_user=¨Ï¥ÎªÌ¦WºÙ
+folder_drafts=¯ó½Z
+folder_inbox=¦¬¥ó§¨
+folder_sent=¶Ç°e¶l¥ó
+folder_trash=©U§£±í
+forward_title=Âà±H¨ì¹q¤l¶l¥ó
+index_contains=¥]§t
+index_empty=µL¶l¥ó
+index_equals=µ¥©ó
+index_esystem=¦b±zªº¨t²Î¤W§ä¤£¨ì¤ä´©ªº¶l¥ó¦øªA¾¹(Qmail, Postfix ©M Sendmail)¡A±z¥i¥H¤â°Ê½Õ¾ã<a href='$1'>¼Ò²Õ²ÕºA</a>¨Ó³]©w¶l¥ó¦øªA¾¹©M¶l¥ó¸ô®|
+index_esystem2=¦b±zªº¨t²Î¤W§ä¤£¨ì<a href='$1'>¼Ò²Õ²ÕºA</a>¤¤³]©wªº¶l¥ó¦øªA¾¹¡A±z»Ý­n½Õ¾ã¥¿½Tªº¦øªA¾¹²ÕºA¡C
+index_find=¦b¨Ï¥ÎªÌ¦WºÙ¤¤§ä´M¨Ï¥ÎªÌ
+index_header=¨Ï¥ÎªÌ«H½c
+index_none=±z¤£³Q¤¹³\¥ô¦ó¦¹¨t²Î¤W¨Ï¥ÎªÌŪ¨ú¶l¥ó
+index_return=Sendmail ²ÕºA
+index_system0=¶l¥ó¦øªA¾¹:Postfix
+index_system1=¶l¥ó¦øªA¾¹:Sendmail
+index_system2=¶l¥ó¦øªA¾¹:Qmail
+index_title=Sendmail ²ÕºA
+index_toomany=¦b±zªº¨t²Î¤W¦³¤Ó¦hªº¨Ï¥ÎªÌ¡A©Ò¥HµLªk¦P®ÉÅã¥Ü¦b¤@­¶¤W
+log_copymail=±q $2 ¨ì $3 ½Æ»s$1«Ê°T®§
+log_delmail=¤w±q $2 §R°£ $1¶l¥ó
+log_movemail=±q $2 ¨ì $3 ²¾°Ê$1«Ê°T®§
+log_send=¤wµo°e¶l¥ó¨ì $1
+mail_addresses=ºÞ²zÁpµ¸¤H
+mail_advanced=¶i¶¥·j´M
+mail_all=¿ï¾Ü¥þ³¡
+mail_bcc=ÁôÂðƥ»
+mail_body=¥»Åé
+mail_cc=°Æ¥»§Û°e
+mail_compose=¼g§@·s¶l¥ó
+mail_copy=½Æ»s¨ì:
+mail_crypt=GnuPG¥[±Kµ¹:
+mail_date=¤é´Á
+mail_delall=¥þ³¡§R°£
+mail_delete=§R°£¿ï¾Üªº¶l¥ó
+mail_deltrash=ªÅ©U§£±í
+mail_ecannot=±z¤£³Q¤¹³\Ū¨ú³o­Ó¨Ï¥ÎªÌªº¶l¥ó
+mail_eexists=°T®§¤£¦s¦b¤F!
+mail_err=¦¹¸ê®Æ§¨¤¤¶l¥ó¦Cªíµo¥Í¤@­Ó¿ù»~ : $1
+mail_fchange=Åܧó
+mail_folder=¸ê®Æ§¨
+mail_folders=ºÞ²z¸ê®Æ§¨
+mail_for=¦b $1
+mail_for2=¥Î©ó¨Ï¥ÎªÌ$1
+mail_forward=Âà±H¤w¿ï¾Üªº
+mail_from=±H¥ó¤H
+mail_high=°ª
+mail_highest=³Ì°ª
+mail_invert=¤Ï¦V¿ï¨ú
+mail_jump=¸õ¦Ü­¶­±:
+mail_login=µn¤J
+mail_logindesc=±z¥²¶·¦b¶l¥ó¥D¾÷$1¤W<br>¿é¤J¨Ï¥ÎªÌ¦WºÙ©M±K½X¨Ó¶i¤J«H½c
+mail_loginheader=POP3µn¤J¦øªA¾¹
+mail_loginmailbox=IMAP«H½c
+mail_loginpass=±K½X
+mail_loginuser=¨Ï¥ÎªÌ¦WºÙ
+mail_logout=ÅܧóPOP3µn¤J
+mail_logout2=ÅܧóIMAPµn¤J
+mail_low=§C
+mail_lowest=³Ì§C
+mail_mark=±N¿ï¨úªº¶l¥ó¼Ð°O¬°¡G
+mail_mark0=¥¼¾\Ū
+mail_mark1=¾\Ū
+mail_mark2=¯S§O
+mail_match=²Å¦X
+mail_move=²¾¦Ü:
+mail_nocrypt=&lt;¤£½s½X&gt;
+mail_none=«H½c¤¤¨S¦³¶l¥ó
+mail_nonefrom=µL
+mail_normal=¤@¯ë
+mail_nosign=&lt;¤£Ã±ÃÒ&gt;
+mail_of=ªº
+mail_ok=·j´M
+mail_pos=¶l¥ó $1 ¨ì $2 ¦@ $3
+mail_pri=Àu¥ý­È
+mail_replyto=¦^¦Ü
+mail_reset=²M°£
+mail_return=¨Ï¥ÎªÌ¹q¤l¶l¥ó
+mail_return2=¨Ï¥ÎªÌ¶l¥ó
+mail_rfc=±H¥ó¤H
+mail_samecrypt=&lt;ª÷Æ_±q¥Øªº¦ì¸m&gt;
+mail_search=§ä´M¶l¥ó, ¨ä¤¤
+mail_search2=·j´M:
+mail_sent=¦bµo°e¶l¥ó¦Cªí¤¤
+mail_sig=½s¿èñ¦W
+mail_sign=GnuPG¥[±K:
+mail_size=¤j¤p
+mail_subject=¥DÃD
+mail_title=¨Ï¥ÎªÌ¹q¤l¶l¥ó
+mail_to=¦¬¥ó¤H
+reply_attach=Âà±Hªþ¥ó§¨±a
+reply_attach2=ªþ¥ó§¨±a
+reply_body=°T®§¤å¦r
+reply_draft=¦s¦Ü¯ó½Z
+reply_ecannot=±z¤£³Q¤¹³\¥H³o­Ó¨Ï¥ÎªÌ¦WºÙ°e¥X¶l¥ó
+reply_headers=¶l¥ó¼ÐÀY
+reply_mailforward=¤wÂà±Hªº¶l¥ó
+reply_send=°e¥X
+reply_spell=«÷¦rÀˬd?
+reply_title=¦^ÂШì¹q¤l¶l¥ó
+search_all=¦b©Ò¦³¸ê®Æ§¨
+search_ecannot=±z¤£³Q¤¹³\·j´M³o­Ó¨Ï¥ÎªÌªº¶l¥ó
+search_efield=±z¥²¶·¿ï¨ú·j´MÃþ«¬
+search_ematch=±z¥²¶·¿é¤J·j´Mªº±ø¥ó
+search_enone=¥¼¿é¤J±ø¥ó
+search_ewhat=¥¼¿é¤J¦C$1²Å¦X¤å¦r
+search_local=¦b¥»¦a¸ê®Æ§¨
+search_none=§ä¤£¨ì²Å¦Xªº¶l¥ó.
+search_results2=$1¶l¥ó²Å¦X$2
+search_results3=$1¶l¥ó¤£²Å¦X$2
+search_results4=$1«Ê¶l¥ó°T®§²Å¦X±zªº·j´M
+search_title=·j´Mµ²ªG
+send_draft=±Hµ¹$1Àx¦s¨ì¯ó½Z¸ê®Æ§¨
+send_eattach=ªþ¥ó¤j¤pÁ`¦X¤£¯à¶W¹L $1 kB¡C
+send_eattachsize=¶l¥óªþ¥[ÀɶW¹L¤¹³\³Ì¤jªº¤j¤p$1 ¦ì¤¸²Õ
+send_ecannot=±z¤£³Q¤¹³\¥H³o­Ó¨Ï¥ÎªÌ¦WºÙ°e¥X¶l¥ó
+send_ecrypt=°T®§½s½X¥¢±Ñ:$1
+send_efile=¾\Ūªþ±aÀÉ$1¥¢±Ñ : $2
+send_efrom=¿ò¥¢±H¥ó¤H¶l¥ó¦ì§}
+send_ekey=¦b¶l¥ó$1§ä¤£¨ìª÷Æ_
+send_eline=¦b½u$1¤W:
+send_epass=±z¤£¯à¹ï°T®§§@¼Æ¦ìñ³¹¡A¦]¬°±z©|¥¼³]©wGunPG¼Ò²Õ¸Ìªº³q¦æ½X
+send_epath=Sendmailµ{¦¡ $1¤£¦s¦b¡C
+send_eperms=¨Ï¥ÎªÌ $1 ¤£¯à¾\Ū $2
+send_eperms2=±z¤£³Q¤¹³\µo°eÀÉ$1
+send_err=¶l¥ó°e¥X¥¢±Ñ
+send_esign=¼Æ¦ìñ³¹°T®§¥¢±Ñ: $1
+send_esmtp=SMTP ©R¥O $1 ¥¢±Ñ: $2
+send_espell=¦b±zªº°T®§¤¤§ä¨ì¤U¦C«÷¦r¿ù»~ ..
+send_eto=¿ò¥¢¦¬¥ó¤H¶l¥ó¦ì§}
+send_eword=¿ù¦r$1
+send_eword2=¿ù¦r$1 - ¥i¯à¥¿½Tªº¬O $2
+send_ok=¶l¥ó¦¨¥\ªº°eµ¹ $1
+send_title=°e¥X¶l¥ó
+sform_all=&lt;©Ò¦³¸ê®Æ§¨&gt;
+sform_and=§ä¨ì¤U¦C²Å¦X©Ò¦³±ø¥ó°T®§ ..
+sform_body=°T®§¤º®e
+sform_cc=°Æ¥»: ÀÉÀY
+sform_date=¤é´Á:ÀÉÀY
+sform_folder=¦b¸ê®Æ§¨
+sform_from=±q:ÀÉÀY
+sform_headers=¥ô¦óÀÉÀY
+sform_local=&lt;¥»¦a¸ê®Æ§¨&gt;
+sform_neg0=¥]§t
+sform_neg1=¤£¥]§t
+sform_ok=±µ¨ü
+sform_or=§ä¨ì¤U¦C²Å¦X¥ô¤@±ø¥ó°T®§ ..
+sform_return=¶i¶¥·j´M
+sform_size=°T®§¤j¤p
+sform_subject=¥D¦®: ÀÉÀY
+sform_text=¦b¤å¦r
+sform_title=¶i¶¥·j´M
+sform_to=¦¬¥ó¤H: ÀÉÀY
+sform_where=­þ¸Ì
+view_allheaders=¬d¬Ý©Ò¦³¶l¥óÀÉÀY
+view_ashtml=À˵øHTML
+view_astext=À˵ø¤å¦r
+view_attach=ªþ¥ó§¨±a
+view_black=«ÊÂê±H¥óªÌ
+view_body=°T®§¤å¦r
+view_crypt=GnuPG¶l¥ó¸Ñ±K
+view_crypt_1=°T®§¤w¥[±K¡A¦ýGnuPG¤ä´©©|¥¼¦w¸Ë
+view_crypt_2=¸Ñ±K°T®§¥¢±Ñ: $1
+view_crypt_3=¶l¥ó¦¨¥\¸Ñ±K
+view_crypt_4=¥[±K«OÅ@¶l¥ó¦¨¥\¸Ñ±K
+view_dall=&lt;©Ò¦³ÀÉ®×&gt;
+view_delete=§R°£
+view_desc=¶l¥ó $1 ¦b $2
+view_desc2=¨Ï¥ÎªÌ$2ªº¶l¥ó$1
+view_desc3=¶l¥ó$1
+view_detach=ªþ¥[ÀÉ®×:
+view_diagnostic-code=¥¢±Ñ­ì¦]
+view_dir=¨ì¦øªA¾¹ÀɮשΥؿý
+view_dstatus=¶Ç°eª¬ºA¥¢±Ñ
+view_ecannot=±z¤£³Q¤¹³\Ū¨ú³o­Ó¨Ï¥ÎªÌªº¶l¥ó
+view_egone=°T®§¤£¦s¦b
+view_enew=½s¿è·s¶l¥ó
+view_final-recipient=³Ì«á¦¬¥óªÌ
+view_folder=ªð¦^«H½c
+view_forward=Âà±H
+view_gnupg=GnuPG»{ÃÒñ³¹
+view_gnupg_0=$1ñ³¹½T»{
+view_gnupg_1=$1ñ³¹¦³®Ä¡A¦ýµLªk«Ø¥ß©U§£±í
+view_gnupg_2=$1ñ³¹<b>©|¥¼½T»{</b>
+view_gnupg_3=±zªº²M³æ¤¤µLª÷Æ_$1¡A©Ò¥Hñ³¹µL®Ä
+view_gnupg_4=½T»{ñ³¹
+view_headers=¶l¥ó¼ÐÀY
+view_mark=¼Ð°O¬°:
+view_mark0=¥¼¾\Ū
+view_mark1=¾\Ū
+view_mark2=¯S§O
+view_noheaders=¹î¬Ý°ò¥»¶l¥óÀÉÀY
+view_print=¦C¦L
+view_qdesc=¦î¦C¤¤ªº¶l¥ó $1
+view_raw=À˵ø­ì©l°T®§
+view_razor=¦^³øµ¹Razor
+view_recv=<a href='$2'>±qª÷Æ_¥D¾÷¨ú±oª÷Æ_$1</a>.
+view_remote-mta=»·ºÝ¶l¥ó¦øªA¾¹
+view_reply=¦^ÂÐ
+view_reply2=¦^Âе¹¥þ³¡
+view_reporting-mta=¦^³ø¶l¥ó¦øªA¾¹
+view_return=­ì©l¶l¥ó
+view_sent=¤wµo°e¶l¥ó¦Cªí¤¤ªº¶l¥ó$1
+view_strip=²¾°£ªþ¥ó
+view_sub=ªþ±aªº¶l¥ó
+view_title=Ū¨ú¹q¤l¶l¥ó
diff --git a/mailboxes/lang/zh_TW.UTF-8 b/mailboxes/lang/zh_TW.UTF-8
new file mode 100644 (file)
index 0000000..78be3cb
--- /dev/null
@@ -0,0 +1,265 @@
+acl_all=全部
+acl_any=任何郵件位址
+acl_apath=限制檔案和程式到目錄
+acl_asame=與使用者相同
+acl_attach=最大附件總大小
+acl_canattach=可以附加伺服器檔嗎?
+acl_candetach=可以附帶檔案到伺服器?
+acl_faddrs=列出的郵件位址
+acl_fdom=任何郵件位址在網域
+acl_fdoms=信箱在網域
+acl_from=依據郵件位址允許
+acl_fromname=來源地址的真實名稱
+acl_none=無
+acl_read=可以讀取哪些使用者的郵件
+acl_same=同名的使用者
+acl_sent=在郵箱中儲存已發送的郵件
+acl_users=只有使用者
+acl_userse=全部, 除了使用者
+acl_usersg=組的成員
+acl_usersm=符合的使用者
+acl_usersu=在UID範圍內
+compose_title=寫作電子郵件
+confirm_ok=現在刪除
+confirm_title=確認刪除
+confirm_warn=您確定要刪除 $1 封選取的訊息?
+confirm_warn2=因為您的信箱大小與格式,需要一點時間,直到刪除完成後,不會執行其他動作
+confirm_warn3=您確定要刪除訊息
+confirm_warn4=直到刪除完成,不會執行其他動作。
+confirm_warnall=您確定要刪除資料夾中所有訊息?
+delete_ecannot=您不被允許刪除這個使用者的郵件
+delete_ecopycannot=您不被允許複製郵件到特定的使用者
+delete_ecopynone=未選取複製郵件
+delete_ecopyuser=複製郵件使用者不存在
+delete_efnone=未選取轉寄郵件
+delete_emnone=沒有選擇要標記的郵件
+delete_emovecannot=您不被允許移動郵件到特定的使用者
+delete_emovenone=未選移動寄郵件
+delete_emoveuser=使用者移動郵件不存在
+delete_enone=沒有選擇要刪除的郵件
+delete_nobutton=未按按鈕
+delete_ok=現在刪除
+delete_rusure=您確定要從$2刪除 $1 封選取的訊息?大檔案的郵件需要一些時間,直到刪除完成,不會執行其他動作。
+delete_rusure2=您確定要刪除 $1 封選取的訊息?大檔案的郵件需要一些時間,直到刪除完成,不會執行其他動作。
+delete_title=刪除郵件
+detach_edir=沒有輸入儲存的檔案或目錄
+detach_eopen=開始$1失敗:$2
+detach_err=附加檔案失敗
+detach_ewrite=寫給$1失敗:$2
+detach_ok=從伺服端附加檔案$1($2)
+detach_title=附加檔案
+enew_title=編輯郵件
+find_enone=沒有您搜尋所符合的使用者
+find_group=群組
+find_home=家目錄
+find_real=真實名稱
+find_results=搜尋$1符合的使用者..
+find_size=郵件大小
+find_title=搜尋結果
+find_user=使用者名稱
+folder_drafts=草稿
+folder_inbox=收件夾
+folder_sent=傳送郵件
+folder_trash=垃圾桶
+forward_title=轉寄到電子郵件
+index_contains=包含
+index_empty=無郵件
+index_equals=等於
+index_esystem=在您的系統上找不到支援的郵件伺服器(Qmail, Postfix 和 Sendmail),您可以手動調整<a href='$1'>模組組態</a>來設定郵件伺服器和郵件路徑
+index_esystem2=在您的系統上找不到<a href='$1'>模組組態</a>中設定的郵件伺服器,您需要調整正確的伺服器組態。
+index_find=在使用者名稱中找尋使用者
+index_header=使用者信箱
+index_none=您不被允許任何此系統上使用者讀取郵件
+index_return=Sendmail 組態
+index_system0=郵件伺服器:Postfix
+index_system1=郵件伺服器:Sendmail
+index_system2=郵件伺服器:Qmail
+index_title=Sendmail 組態
+index_toomany=在您的系統上有太多的使用者,所以無法同時顯示在一頁上
+log_copymail=從 $2 到 $3 複製$1封訊息
+log_delmail=已從 $2 刪除 $1郵件
+log_movemail=從 $2 到 $3 移動$1封訊息
+log_send=已發送郵件到 $1
+mail_addresses=管理聯絡人
+mail_advanced=進階搜尋
+mail_all=選擇全部
+mail_bcc=隱藏副本
+mail_body=本體
+mail_cc=副本抄送
+mail_compose=寫作新郵件
+mail_copy=複製到:
+mail_crypt=GnuPG加密給:
+mail_date=日期
+mail_delall=全部刪除
+mail_delete=刪除選擇的郵件
+mail_deltrash=空垃圾桶
+mail_ecannot=您不被允許讀取這個使用者的郵件
+mail_eexists=訊息不存在了!
+mail_err=此資料夾中郵件列表發生一個錯誤 : $1
+mail_fchange=變更
+mail_folder=資料夾
+mail_folders=管理資料夾
+mail_for=在 $1
+mail_for2=用於使用者$1
+mail_forward=轉寄已選擇的
+mail_from=寄件人
+mail_high=高
+mail_highest=最高
+mail_invert=反向選取
+mail_jump=跳至頁面:
+mail_login=登入
+mail_logindesc=您必須在郵件主機$1上<br>輸入使用者名稱和密碼來進入信箱
+mail_loginheader=POP3登入伺服器
+mail_loginmailbox=IMAP信箱
+mail_loginpass=密碼
+mail_loginuser=使用者名稱
+mail_logout=變更POP3登入
+mail_logout2=變更IMAP登入
+mail_low=低
+mail_lowest=最低
+mail_mark=將選取的郵件標記為:
+mail_mark0=未閱讀
+mail_mark1=閱讀
+mail_mark2=特別
+mail_match=符合
+mail_move=移至:
+mail_nocrypt=&lt;不編碼&gt;
+mail_none=信箱中沒有郵件
+mail_nonefrom=無
+mail_normal=一般
+mail_nosign=&lt;不簽證&gt;
+mail_of=的
+mail_ok=搜尋
+mail_pos=郵件 $1 到 $2 共 $3
+mail_pri=優先值
+mail_replyto=回至
+mail_reset=清除
+mail_return=使用者電子郵件
+mail_return2=使用者郵件
+mail_rfc=寄件人
+mail_samecrypt=&lt;金鑰從目的位置&gt;
+mail_search=找尋郵件, 其中
+mail_search2=搜尋:
+mail_sent=在發送郵件列表中
+mail_sig=編輯簽名
+mail_sign=GnuPG加密:
+mail_size=大小
+mail_subject=主題
+mail_title=使用者電子郵件
+mail_to=收件人
+reply_attach=轉寄附件夾帶
+reply_attach2=附件夾帶
+reply_body=訊息文字
+reply_draft=存至草稿
+reply_ecannot=您不被允許以這個使用者名稱送出郵件
+reply_headers=郵件標頭
+reply_mailforward=已轉寄的郵件
+reply_send=送出
+reply_spell=拼字檢查?
+reply_title=回覆到電子郵件
+search_all=在所有資料夾
+search_ecannot=您不被允許搜尋這個使用者的郵件
+search_efield=您必須選取搜尋類型
+search_ematch=您必須輸入搜尋的條件
+search_enone=未輸入條件
+search_ewhat=未輸入列$1符合文字
+search_local=在本地資料夾
+search_none=找不到符合的郵件.
+search_results2=$1郵件符合$2
+search_results3=$1郵件不符合$2
+search_results4=$1封郵件訊息符合您的搜尋
+search_title=搜尋結果
+send_draft=寄給$1儲存到草稿資料夾
+send_eattach=附件大小總合不能超過 $1 kB。
+send_eattachsize=郵件附加檔超過允許最大的大小$1 位元組
+send_ecannot=您不被允許以這個使用者名稱送出郵件
+send_ecrypt=訊息編碼失敗:$1
+send_efile=閱讀附帶檔$1失敗 : $2
+send_efrom=遺失寄件人郵件位址
+send_ekey=在郵件$1找不到金鑰
+send_eline=在線$1上:
+send_epass=您不能對訊息作數位簽章,因為您尚未設定GunPG模組裡的通行碼
+send_epath=Sendmail程式 $1不存在。
+send_eperms=使用者 $1 不能閱讀 $2
+send_eperms2=您不被允許發送檔$1
+send_err=郵件送出失敗
+send_esign=數位簽章訊息失敗: $1
+send_esmtp=SMTP 命令 $1 失敗: $2
+send_espell=在您的訊息中找到下列拼字錯誤 ..
+send_eto=遺失收件人郵件位址
+send_eword=錯字$1
+send_eword2=錯字$1 - 可能正確的是 $2
+send_ok=郵件成功的送給 $1
+send_title=送出郵件
+sform_all=&lt;所有資料夾&gt;
+sform_and=找到下列符合所有條件訊息 ..
+sform_body=訊息內容
+sform_cc=副本: 檔頭
+sform_date=日期:檔頭
+sform_folder=在資料夾
+sform_from=從:檔頭
+sform_headers=任何檔頭
+sform_local=&lt;本地資料夾&gt;
+sform_neg0=包含
+sform_neg1=不包含
+sform_ok=接受
+sform_or=找到下列符合任一條件訊息 ..
+sform_return=進階搜尋
+sform_size=訊息大小
+sform_subject=主旨: 檔頭
+sform_text=在文字
+sform_title=進階搜尋
+sform_to=收件人: 檔頭
+sform_where=哪裡
+view_allheaders=查看所有郵件檔頭
+view_ashtml=檢視HTML
+view_astext=檢視文字
+view_attach=附件夾帶
+view_black=封鎖寄件者
+view_body=訊息文字
+view_crypt=GnuPG郵件解密
+view_crypt_1=訊息已加密,但GnuPG支援尚未安裝
+view_crypt_2=解密訊息失敗: $1
+view_crypt_3=郵件成功解密
+view_crypt_4=加密保護郵件成功解密
+view_dall=&lt;所有檔案&gt;
+view_delete=刪除
+view_desc=郵件 $1 在 $2
+view_desc2=使用者$2的郵件$1
+view_desc3=郵件$1
+view_detach=附加檔案:
+view_diagnostic-code=失敗原因
+view_dir=到伺服器檔案或目錄
+view_dstatus=傳送狀態失敗
+view_ecannot=您不被允許讀取這個使用者的郵件
+view_egone=訊息不存在
+view_enew=編輯新郵件
+view_final-recipient=最後收件者
+view_folder=返回信箱
+view_forward=轉寄
+view_gnupg=GnuPG認證簽章
+view_gnupg_0=$1簽章確認
+view_gnupg_1=$1簽章有效,但無法建立垃圾桶
+view_gnupg_2=$1簽章<b>尚未確認</b>
+view_gnupg_3=您的清單中無金鑰$1,所以簽章無效
+view_gnupg_4=確認簽章
+view_headers=郵件標頭
+view_mark=標記為:
+view_mark0=未閱讀
+view_mark1=閱讀
+view_mark2=特別
+view_noheaders=察看基本郵件檔頭
+view_print=列印
+view_qdesc=佇列中的郵件 $1
+view_raw=檢視原始訊息
+view_razor=回報給Razor
+view_recv=<a href='$2'>從金鑰主機取得金鑰$1</a>.
+view_remote-mta=遠端郵件伺服器
+view_reply=回覆
+view_reply2=回覆給全部
+view_reporting-mta=回報郵件伺服器
+view_return=原始郵件
+view_sent=已發送郵件列表中的郵件$1
+view_strip=移除附件
+view_sub=附帶的郵件
+view_title=讀取電子郵件
diff --git a/mailboxes/list_mail.cgi b/mailboxes/list_mail.cgi
new file mode 100755 (executable)
index 0000000..7012105
--- /dev/null
@@ -0,0 +1,245 @@
+#!/usr/local/bin/perl
+# list_mail.cgi
+# List the mail messages for some user in some folder
+
+require './mailboxes-lib.pl';
+&ReadParse();
+&can_user($in{'user'}) || &error($text{'mail_ecannot'});
+&is_user($in{'user'}) || -e $in{'user'} || &error($text{'mail_efile'});
+$uuser = &urlize($in{'user'});
+
+if ($config{'track_read'}) {
+       dbmopen(%read, "$module_config_directory/$in{'user'}.read", 0600);
+       }
+
+# Make sure the mail system is OK
+$err = &test_mail_system();
+if ($err) {
+       &ui_print_header(undef, $text{'index_title'}, "", undef, 1, 1);
+       if (!$access{'noconfig'}) {
+               &ui_print_endpage(&text('index_esystem3',
+                                       "../config.cgi?$module_name", $err));
+               }
+       else {
+               &ui_print_endpage(&text('mail_esystem', $err));
+               }
+       }
+
+&ui_print_header(undef, $text{'mail_title'}, "");
+print &check_clicks_function();
+@folders = &list_user_folders_sorted($in{'user'});
+($folder) = grep { $_->{'index'} == $in{'folder'} } @folders;
+
+# Get folder-selection HTML
+$sel = &folder_select(\@folders, $folder, "folder");
+
+# Work out start from jump page
+$perpage = $folder->{'perpage'} || $config{'perpage'};
+if ($in{'jump'} =~ /^\d+$/ && $in{'jump'} > 0) {
+       $in{'start'} = ($in{'jump'}-1)*$perpage;
+       }
+
+# View mail from the most recent
+@mail = reverse(&mailbox_list_mails(-$in{'start'},
+                                   -$in{'start'}-$perpage+1,
+                                   $folder, 1, \@error));
+if ($in{'start'} >= @mail && $in{'jump'}) {
+       # Jumped too far!
+       $in{'start'} = @mail - $perpage;
+       @mail = reverse(&mailbox_list_mails(-$in{'start'},
+                                           -$in{'start'}-$perpage+1,
+                                           $folder, 1, \@error));
+       }
+
+# Show page flipping arrows
+&show_arrows();
+
+print "<form action=delete_mail.cgi method=post>\n";
+print "<input type=hidden name=user value='$in{'user'}'>\n";
+print "<input type=hidden name=folder value='$folder->{'index'}'>\n";
+print "<input type=hidden name=mod value=",&modification_time($folder),">\n";
+print "<input type=hidden name=start value='$in{'start'}'>\n";
+if ($config{'top_buttons'} && @mail) {
+       &show_buttons(1, \@folders, $folder, \@mail, $in{'user'});
+       @links = ( &select_all_link("d", 1),
+                  &select_invert_link("d", 1) );
+       print &ui_links_row(\@links);
+       }
+
+# Show error opening folder
+if (@error) {
+       print "<center><b><font color=#ff0000>\n";
+       print &text('mail_err', $error[0] == 0 ? $error[1] :
+                             &text('save_elogin', $error[1])),"\n";
+       print "</font></b></center>\n";
+       }
+
+$showto = $folder->{'sent'} || $folder->{'drafts'};
+@tds = ( "width=5", "nowrap", "nowrap", "nowrap", "nowrap" );
+if (@mail) {
+       # Show mailbox headers
+       local @hcols;
+       push(@hcols, "");
+       push(@hcols, $showto ? $text{'mail_to'} : $text{'mail_from'});
+       push(@hcols, $config{'show_to'} ? $showto ? ( $text{'mail_from'} ) :
+                                                   ( $text{'mail_to'} ) : ());
+       push(@hcols, $text{'mail_date'});
+       push(@hcols, $text{'mail_size'});
+       push(@hcols, $text{'mail_subject'});
+       print &ui_columns_start(\@hcols, 100, 0, \@tds);
+       }
+
+# Show rows for actual mail messages
+for($i=$in{'start'}; $i<@mail && $i<$in{'start'}+$perpage; $i++) {
+       local $idx = $mail[$i]->{'idx'};
+       local $cols = 0;
+       local @cols;
+       local $from = $mail[$i]->{'header'}->{$showto ? 'to' : 'from'};
+       $from = $text{'mail_unknown'} if ($from !~ /\S/);
+       push(@cols, &view_mail_link($in{'user'}, $folder, $idx, $from));
+       if ($config{'show_to'}) {
+               push(@cols, &simplify_from(
+                       $mail[$i]->{'header'}->{$showto ? 'from' : 'to'}));
+               }
+       push(@cols, &simplify_date($mail[$i]->{'header'}->{'date'}));
+       push(@cols, &nice_size($mail[$i]->{'size'}, 1024));
+       local $tbl;
+       $tbl .= "<table border=0 cellpadding=0 cellspacing=0 width=100%>".
+             "<tr><td>".&simplify_subject($mail[$i]->{'header'}->{'subject'}).
+             "</td> <td align=right>";
+       if ($mail[$i]->{'header'}->{'content-type'} =~ /multipart\/\S+/i) {
+               $tbl .= "<img src=images/attach.gif>";
+               }
+       local $p = int($mail[$i]->{'header'}->{'x-priority'});
+       if ($p == 1) {
+               $tbl .= "&nbsp;<img src=images/p1.gif>";
+               }
+       elsif ($p == 2) {
+               $tbl .= "&nbsp;<img src=images/p2.gif>";
+               }
+       if (!$showto) {
+               if ($read{$mail[$i]->{'header'}->{'message-id'}} == 2) {
+                       $tbl .= "&nbsp;<img src=images/special.gif>";
+                       }
+               elsif ($read{$mail[$i]->{'header'}->{'message-id'}} == 1) {
+                       $tbl .= "&nbsp;<img src=images/read.gif>";
+                       }
+               }
+       $tbl .= "</td></tr></table>\n";
+       push(@cols, $tbl);
+
+       if (&editable_mail($mail[$i])) {
+               print &ui_checked_columns_row(\@cols, \@tds, "d", $idx);
+               }
+       else {
+               print &ui_columns_row([ "", @cols ], \@tds);
+               }
+
+       if ($config{'show_body'}) {
+                # Show part of the body too
+                &parse_mail($mail[$i]);
+               local $data = &mail_preview($mail[$i]);
+               if ($data) {
+                        print "<tr $cb> <td colspan=",(scalar(@cols)+1),"><tt>",
+                               &html_escape($data),"</tt></td> </tr>\n";
+                       }
+                }
+       }
+if (@mail) {
+       print &ui_columns_end();
+       print &ui_links_row(\@links);
+       }
+
+&show_buttons(2, \@folders, $folder, \@mail, $in{'user'});
+print "</form>\n";
+if ($config{'arrows'} && @mail) {
+        # Show page flipping arrows at the bottom
+        &show_arrows();
+        }
+
+if (@mail) {
+       print "<hr>\n";
+       print "<table width=100%><tr>\n";
+
+       # Show simple search form
+       print "<form action=mail_search.cgi><td width=30%>\n";
+       print "<input type=hidden name=user value='$in{'user'}'>\n";
+       print "<input type=hidden name=folder value='$folder->{'index'}'>\n";
+       print "<input type=hidden name=simple value=1>\n";
+       print "<input type=submit value='$text{'mail_search2'}'>\n";
+       print "<input name=search size=20></td></form>\n";
+
+       # Show advanced search button
+       print "<form action=search_form.cgi>\n";
+       print "<input type=hidden name=user value='$in{'user'}'>\n";
+       print "<input type=hidden name=folder value='$folder->{'index'}'>\n";
+       print "<td width=20% align=center><input type=submit name=advanced ",
+             "value='$text{'mail_advanced'}'></td>\n";
+       print "</form>\n";
+
+       # Show delete all button
+       print "<form action=delete_all.cgi>\n";
+       print "<input type=hidden name=user value='$in{'user'}'>\n";
+       print "<input type=hidden name=folder value='$folder->{'index'}'>\n";
+       print "<td width=20% align=center><input type=submit ",
+             "value='$text{'mail_delall'}'></td>\n";
+       print "</form>\n";
+       }
+
+# Show page jump form
+$jumpform = (@mail > $perpage);
+if ($jumpform) {
+       print "<form action=list_mail.cgi>\n";
+       print "<input type=hidden name=user value='$in{'user'}'>\n";
+       print "<input type=hidden name=folder value='$folder->{'index'}'>\n";
+       print "<td width=30% align=right>\n";
+       print "<input type=submit value='$text{'mail_jump'}'>\n";
+       printf "<input name=jump size=3 value='%s'> %s %s\n",
+               int($in{'start'} / $perpage)+1, $text{'mail_of'},
+               int(@mail / $perpage)+1;
+       print "</td></form>\n";
+       }
+elsif (@mail) {
+       print "<td width=30% align=right></td>\n";
+       }
+
+if (@mail) {
+       print "</tr>\n";
+       print "</table>\n";
+       }
+
+if ($config{'log_read'}) {
+       &webmin_log("read", undef, $in{'user'},
+                   { 'file' => $folder->{'file'} });
+       }
+&ui_print_footer("", $text{'index_return'});
+
+sub show_arrows
+{
+print "<center>\n";
+print "<form action=list_mail.cgi><font size=+1>\n";
+print "<input type=hidden name=user value='$in{'user'}'>\n";
+if ($in{'start'}+$perpage < @mail) {
+       printf "<a href='list_mail.cgi?start=%d&user=%s&folder=%d'>".
+              "<img src=/images/left.gif border=0 align=middle></a>\n",
+               $in{'start'}+$perpage, $uuser, $in{'folder'};
+       }
+
+local $s = @mail-$in{'start'};
+local $e = @mail-$in{'start'}-$perpage+1;
+if (@mail) {
+       print &text('mail_pos', $s, $e < 1 ? 1 : $e, scalar(@mail), $sel);
+       }
+else {
+       print &text('mail_none', $sel);
+       }
+print "</font><input type=submit value='$text{'mail_fchange'}'>\n";
+
+if ($in{'start'}) {
+       printf "<a href='list_mail.cgi?start=%d&user=%s&folder=%d'>".
+              "<img src=/images/right.gif border=0 align=middle></a>\n",
+               $in{'start'}-$perpage, $uuser, $in{'folder'};
+       }
+print "</form></center>\n";
+}
+
diff --git a/mailboxes/log_parser.pl b/mailboxes/log_parser.pl
new file mode 100644 (file)
index 0000000..b6d45b9
--- /dev/null
@@ -0,0 +1,45 @@
+# log_parser.pl
+# Functions for parsing this module's logs
+
+do 'mailboxes-lib.pl';
+
+# parse_webmin_log(user, script, action, type, object, &params)
+# Converts logged information from this module into human-readable form
+sub parse_webmin_log
+{
+local ($user, $script, $action, $type, $object, $p, $long) = @_;
+if ($action eq 'delmail') {
+       return &text("log_delmail", $p->{'count'}, "<tt>$p->{'from'}</tt>");
+       }
+elsif ($action eq 'movemail') {
+       return &text("log_movemail", $p->{'count'}, "<tt>$p->{'from'}</tt>",
+                    "<tt>$p->{'to'}</tt>");
+       }
+elsif ($action eq 'copymail') {
+       return &text("log_copymail", $p->{'count'}, "<tt>$p->{'from'}</tt>",
+                    "<tt>$p->{'to'}</tt>");
+       }
+elsif ($action eq 'send') {
+       return &text('log_send', &html_escape(&extract_email($p->{'to'})));
+       }
+elsif ($action eq 'read') {
+       return &text('log_read', &html_escape($object));
+       }
+else {
+       return undef;
+       }
+}
+
+sub extract_email
+{
+if ($_[0] =~ /([^<>"' \(\)]+\@[^<>"' \(\)]+)/) {
+       return $1;
+       }
+elsif ($_[0] =~ /<(\S+)>/) {
+       return $1;
+       }
+else {
+       return $_[0];
+       }
+}
+
diff --git a/mailboxes/mail_search.cgi b/mailboxes/mail_search.cgi
new file mode 100755 (executable)
index 0000000..77a9c0f
--- /dev/null
@@ -0,0 +1,207 @@
+#!/usr/local/bin/perl
+# mail_search.cgi
+# Find mail messages matching some pattern
+
+require './mailboxes-lib.pl';
+&ReadParse();
+&can_user($in{'user'}) || &error($text{'mail_ecannot'});
+@folders = &list_user_folders($in{'user'});
+$uuser = &urlize($in{'user'});
+
+if ($in{'simple'}) {
+       # Make sure a search was entered
+       $in{'search'} || &error($text{'search_ematch'});
+       $ofolder = $folders[$in{'folder'}];
+       }
+else {
+       # Validate search fields
+       for($i=0; defined($in{"field_$i"}); $i++) {
+               if ($in{"field_$i"}) {
+                       $in{"what_$i"} || &error(&text('search_ewhat', $i+1));
+                       $neg = $in{"neg_$i"} ? "!" : "";
+                       push(@fields, [ $neg.$in{"field_$i"}, $in{"what_$i"} ]);
+                       }
+               }
+       @fields || &error($text{'search_enone'});
+       $ofolder = $folders[$in{'ofolder'}];
+       }
+
+if ($in{'folder'} == -2) {
+       $desc = $text{'search_local'};
+       }
+elsif ($in{'folder'} == -1) {
+       $desc = $text{'search_all'};
+       }
+else {
+       $folder = $folders[$in{'folder'}];
+       $desc = &text('mail_for', $folder->{'name'});
+       }
+&ui_print_header($desc, $text{'search_title'}, "", undef, 0, 0, undef,
+       &folder_link($in{'user'}, $ofolder));
+
+if ($in{'simple'}) {
+       if ($in{'search'} =~ /^(\S+):\s*(.*)$/) {
+               # A specific field was entered
+               local ($field, $what) = ($1, $2);
+               @searchlist = ( [ $field, $what ] );
+               @rv = &mailbox_search_mail(\@searchlist, 0, $folder);
+               print "<p><b>",&text('search_results5', scalar(@rv),
+                           "<tt>$field</tt>", "<tt>$what</tt>")," ..</b><p>\n";
+               }
+       else {
+               # Just search by Subject and From in one folder
+               ($mode, $words) = &parse_boolean($in{'search'});
+               if ($mode == 0) {
+                       # Can just do a single 'or' search
+                       @searchlist = map { ( [ 'subject', $_ ],
+                                             [ 'from', $_ ] ) } @$words;
+                       @rv = &mailbox_search_mail(\@searchlist, 0, $folder);
+                       }
+               elsif ($mode == 1) {
+                       # Need to do two 'and' searches and combine
+                       @searchlist1 = map { ( [ 'subject', $_ ] ) } @$words;
+                       @rv1 = &mailbox_search_mail(\@searchlist1, 1, $folder);
+                       @searchlist2 = map { ( [ 'from', $_ ] ) } @$words;
+                       @rv2 = &mailbox_search_mail(\@searchlist2, 1, $folder);
+                       @rv = @rv1;
+                       %gotidx = map { $_->{'idx'}, 1 } @rv;
+                       foreach $mail (@rv2) {
+                               push(@rv, $mail) if (!$gotidx{$mail->{'idx'}});
+                               }
+                       }
+               else {
+                       &error($text{'search_eboolean'});
+                       }
+               print "<p><b>",&text('search_results2', scalar(@rv),
+                                    "<tt>$in{'search'}</tt>")," ..</b><p>\n";
+               }
+       foreach $mail (@rv) {
+               $mail->{'folder'} = $folder;
+               }
+       }
+else {
+       # Complex search, perhaps over multiple folders!
+       if ($in{'folder'} == -2) {
+               @sfolders = grep { !$_->{'remote'} } @folders;
+               $multi_folder = 1;
+               }
+       elsif ($in{'folder'} == -1) {
+               @sfolders = @folders;
+               $multi_folder = 1;
+               }
+       else {
+               @sfolders = ( $folder );
+               }
+       foreach $sf (@sfolders) {
+               local @frv = &mailbox_search_mail(\@fields, $in{'and'}, $sf);
+               foreach $mail (@frv) {
+                       $mail->{'folder'} = $sf;
+                       }
+               push(@rv, @frv);
+               }
+       print "<p><b>",&text('search_results4', scalar(@rv))," ..</b><p>\n";
+       }
+@rv = reverse(@rv);
+
+$showto = $folder->{'sent'} || $folder->{'drafts'};
+if (@rv) {
+       print "<form action=delete_mail.cgi method=post>\n";
+       print "<input type=hidden name=folder value='$in{'folder'}'>\n";
+       print "<input type=hidden name=user value='$in{'user'}'>\n";
+       if ($config{'top_buttons'}) {
+               if (!$multi_folder) {
+                       &show_buttons(1, \@folders, $folder, \@rv, $in{'user'},
+                                     1);
+                       @links = ( &select_all_link("d", 0),
+                                  &select_invert_link("d", 0) );
+                       print &ui_links_row(\@links);
+                       }
+               }
+
+       # Show mailbox headers
+       local @hcols;
+       push(@hcols, "");
+       push(@hcols, $showto ? $text{'mail_to'} : $text{'mail_from'});
+       push(@hcols, $config{'show_to'} ? $showto ? ( $text{'mail_from'} ) :
+                                                   ( $text{'mail_to'} ) : ());
+       push(@hcols, $text{'mail_date'});
+       push(@hcols, $text{'mail_size'});
+       push(@hcols, $text{'mail_subject'});
+       print &ui_columns_start(\@hcols, 100, 0, \@tds);
+       }
+foreach $m (@rv) {
+       local $idx = $m->{'idx'};
+       local $mf = $m->{'folder'};
+       local @cols;
+       local $from = &simplify_from($m->{'header'}->{
+                                       $showto ? 'to' : 'from'});
+       $from = $text{'mail_unknown'} if ($from !~ /\S/);
+       push(@cols, "<a href='view_mail.cgi?idx=$idx&user=$uuser&folder=$mf->{'index'}'>$from</a>");
+       if ($config{'show_to'}) {
+               push(@cols, &simplify_from(
+                       $m->{'header'}->{$showto ? 'from' : 'to'}));
+               }
+       push(@cols, &simplify_date($m->{'header'}->{'date'}));
+       push(@cols, &nice_size($m->{'size'}, 1024));
+       local $tbl;
+       $tbl .= "<table border=0 cellpadding=0 cellspacing=0 width=100%>".
+             "<tr><td>".&simplify_subject($m->{'header'}->{'subject'}).
+             "</td> <td align=right>";
+       if ($m->{'header'}->{'content-type'} =~ /multipart\/\S+/i) {
+               $tbl .= "<img src=images/attach.gif>";
+               }
+       local $p = int($m->{'header'}->{'x-priority'});
+       if ($p == 1) {
+               $tbl .= "&nbsp;<img src=images/p1.gif>";
+               }
+       elsif ($p == 2) {
+               $tbl .= "&nbsp;<img src=images/p2.gif>";
+               }
+       if (!$showto) {
+               if ($read{$m->{'header'}->{'message-id'}} == 2) {
+                       $tbl .= "&nbsp;<img src=images/special.gif>";
+                       }
+               elsif ($read{$m->{'header'}->{'message-id'}} == 1) {
+                       $tbl .= "&nbsp;<img src=images/read.gif>";
+                       }
+               }
+       $tbl .= "</td></tr></table>\n";
+       push(@cols, $tbl);
+
+       if (&editable_mail($m)) {
+               print &ui_checked_columns_row(\@cols, \@tds, "d", $idx);
+               }
+       elsif ($multi_folder) {
+               print &ui_columns_row([ $mf->{'name'}, @cols ], \@tds);
+               }
+       else {
+               print &ui_columns_row([ "", @cols ], \@tds);
+               }
+
+       if ($config{'show_body'}) {
+                # Show part of the body too
+                &parse_mail($m);
+               local $data = &mail_preview($m);
+               if ($data) {
+                        print "<tr $cb> <td colspan=",(scalar(@cols)+1),"><tt>",
+                               &html_escape($data),"</tt></td> </tr>\n";
+                       }
+                }
+       }
+if (@rv) {
+       print &ui_columns_end();
+       if (!$multi_folder) {
+               print &ui_links_row(\@links);
+               &show_buttons(2, \@folders, $folder, \@rv, $in{'user'}, 1);
+               }
+       print "</form><p>\n";
+       }
+else {
+       print "<b>$text{'search_none'}</b> <p>\n";
+       }
+
+&ui_print_footer($in{'simple'} ? ( ) : ( "search_form.cgi?folder=$in{'folder'}",
+                               $text{'sform_return'} ),
+       "list_mail.cgi?user=$in{'user'}&folder=$in{'folder'}", $text{'mail_return'},
+       "", $text{'index_return'});
+
diff --git a/mailboxes/mailboxes-lib.pl b/mailboxes/mailboxes-lib.pl
new file mode 100644 (file)
index 0000000..0ee3ebc
--- /dev/null
@@ -0,0 +1,1034 @@
+# mailboxes-lib.pl
+# Common functions for reading user mailboxes
+
+do '../web-lib.pl';
+do '../ui-lib.pl';
+do 'boxes-lib.pl';
+do 'folders-lib.pl';
+&init_config();
+%access = &get_module_acl();
+$config{'perpage'} ||= 20;
+$config{'column_count'} ||= 4;
+$gconfig{'logfiles'} = 0;      # file change logging never needs to be done
+
+# Always detect the mail system if not set
+if ($config{'mail_system'} == 3) {
+       $config{'mail_system'} = &detect_mail_system();
+       &save_module_config() if ($config{'mail_system'} != 3);
+       }
+
+# send_mail_program(from, to)
+# Returns the command for injecting email, based on the mail system in use
+sub send_mail_program
+{
+if ($config{'mail_system'} == 1 || $config{'mail_system'} == 0) {
+       # Use sendmail executable, or postfix wrapper
+       local %sconfig = &foreign_check("sendmail") &&
+                        $config{'mail_system'} == 1 ?
+                               &foreign_config("sendmail") : ( );
+       local $cmd = &has_command($sconfig{'sendmail_path'} || "sendmail");
+       return "$cmd -t -f".quotemeta($_[0]) if ($cmd);
+       }
+elsif ($config{'mail_system'} == 2) {
+       # Use qmail injector
+       local %qconfig = &foreign_check("qmailadmin") ?
+                               &foreign_config("qmailadmin") : ( );
+       local $cmd = ($qconfig{'qmail_dir'} || "/var/qmail").
+                    "/bin/qmail-inject";
+       return $cmd if (-x $cmd);
+       }
+else {
+       # Fallback - use sendmail command
+       local $cmd = &has_command("sendmail");
+       return "$cmd -t -f".quotemeta($_[0]) if ($cmd);
+       }
+return undef;
+}
+
+# list_folders()
+# Returns a list of all mailboxes for all users
+sub list_folders
+{
+local (@rv, $uinfo);
+foreach $uinfo (&list_mail_users()) {
+       push(@rv, &list_user_folders(@$uinfo));
+       }
+return @rv;
+}
+
+# list_user_folders(user, [other info])
+# Returns a list of folders for mailboxes belonging to some user
+sub list_user_folders
+{
+if ($_[0] =~ /^\//) {
+       # A path .. return a folder just for it
+       return ( { 'name' => $_[0],
+                  'file' => $_[0],
+                  'type' => &folder_type($_[0]),
+                  'mode' => 1,
+                  'index' => 0 } );
+       }
+else {
+       # Getting folders for a user
+       local @uinfo = @_ > 1 ? @_ : &get_mail_user($_[0]);
+       return ( ) if (!@uinfo);
+       local ($dir, $style, $mailbox, $maildir) = &get_mail_style();
+       local @rv;
+
+       # Check for user-specified mail store
+       if ($uinfo[10]) {
+               push(@rv, { 'name' => $uinfo[10],
+                           'file' => $uinfo[10],
+                           'type' => &folder_type($uinfo[10]),
+                           'mode' => 0,
+                           'index' => scalar(@rv) } );
+               }
+
+       # Check for /var/mail/USERNAME file
+       if ($dir) {
+               local $mf = &mail_file_style($uinfo[0], $dir, $style);
+               push(@rv, { 'type' => 0,
+                           'name' => $mf,
+                           'file' => $mf,
+                           'user' => $uinfo[0],
+                           'index' => scalar(@rv) });
+               }
+
+       # Check for file in home dir
+       if ($mailbox) {
+               local $mf = "$uinfo[7]/$mailbox";
+               if (-r $mf || !@rv) {
+                       push(@rv, { 'type' => 0,
+                                   'name' => "~$uinfo[0]/$mailbox",
+                                   'file' => $mf,
+                                   'user' => $uinfo[0],
+                                   'index' => scalar(@rv) });
+                       }
+               }
+
+       # Check for directory in home dir
+       if ($maildir) {
+               local $mf = "$uinfo[7]/$maildir";
+               if (-d $mf || !@rv) {
+                       push(@rv, { 'type' => 1,
+                                   'name' => "~$uinfo[0]/$maildir/",
+                                   'file' => $mf,
+                                   'user' => $uinfo[0],
+                                   'index' => scalar(@rv) });
+                       }
+               }
+
+       # Add any ~/mail files
+       if ($config{'mail_usermin'}) {
+               local $folders_dir = "$uinfo[7]/$config{'mail_usermin'}";
+               foreach $p (&recursive_files($folders_dir, 1)) {
+                       local $f = $p;
+                       $f =~ s/^\Q$folders_dir\E\///;
+                       push(@rv, { 'file' => $p,
+                                   'name' => "~$uinfo[0]/$config{'mail_usermin'}/$f",
+                                   'type' => &folder_type($p),
+                                   'mode' => 0,
+                                   'sent' => $f eq "sentmail",
+                                   'index' => scalar(@rv) } );
+                       }
+               }
+
+       # Add any Usermin external mail files
+       if ($config{'mailbox_user'}) {
+               local %userconfig;
+               &read_file_cached("$uinfo[7]/$config{'mailbox_user'}/config",
+                                 \%userconfig);
+               local $o;
+               foreach $o (split(/\t+/, $userconfig{'mailboxes'})) {
+                       $o =~ /\/([^\/]+)$/ || next;
+                       push(@rv, { 'name' => $o,
+                                   'file' => $o,
+                                   'type' => &folder_type($o),
+                                   'mode' => 1,
+                                   'index' => scalar(@rv) } );
+                       }
+               }
+
+       foreach my $f (@rv) {
+               $f->{'user'} = $_[0];
+               }
+       return @rv;
+       }
+}
+
+sub list_user_folders_sorted
+{
+return &list_user_folders(@_);
+}
+
+# get_mail_style()
+# Returns a list containing the mail base directory, directory style,
+# mail file in home dir, and maildir in home dir
+sub get_mail_style
+{
+if (!defined(@mail_style_cache)) {
+       if ($config{'auto'}) {
+               # Based on mail server
+               if ($config{'mail_system'} == 1) {
+                       # Can get paths from Sendmail module config
+                       local %sconfig = &foreign_config("sendmail");
+                       if ($sconfig{'mail_dir'}) {
+                               return ($sconfig{'mail_dir'}, $sconfig{'mail_style'}, undef, undef);
+                               }
+                       else {
+                               return (undef, $sconfig{'mail_style'}, $sconfig{'mail_file'}, undef);
+                               }
+                       }
+               elsif ($config{'mail_system'} == 0) {
+                       # Need to query Postfix module for paths
+                       &foreign_require("postfix", "postfix-lib.pl");
+                       local @s = &postfix::postfix_mail_system();
+                       if ($s[0] == 0) {
+                               return ($s[1], 0, undef, undef);
+                               }
+                       elsif ($s[0] == 1) {
+                               return (undef, 0, $s[1], undef);
+                               }
+                       elsif ($s[0] == 2) {
+                               return (undef, 0, undef, $s[1]);
+                               }
+                       }
+               elsif ($config{'mail_system'} == 2 ||
+                      $config{'mail_system'} == 4) {
+                       # Need to check qmail module config for paths
+                       local %qconfig = &foreign_config("qmailadmin");
+                       if ($qconfig{'mail_system'} == 1) {
+                               return (undef, 0, undef,
+                                       $qconfig{'mail_dir_qmail'});
+                               }
+                       elsif ($qconfig{'mail_dir'}) {
+                               return ($qconfig{'mail_dir'}, $qconfig{'mail_style'}, undef, undef);
+                               }
+                       else {
+                               return (undef, $qconfig{'mail_style'}, $qconfig{'mail_file'}, undef);
+                               }
+                       }
+               elsif ($config{'mail_system'} == 5) {
+                       # vpopmail always uses ~/Maildir
+                       return ( undef, 0, undef, "Maildir" );
+                       }
+               else {
+                       # No mail server set yet!
+                       return (undef, undef, undef, undef);
+                       }
+               }
+       else {
+               # Use config settings
+               @mail_style_cache = ($config{'mail_dir'}, $config{'mail_style'},
+                                    $config{'mail_file'}, $config{'mail_sub'});
+               }
+       }
+return @mail_style_cache;
+}
+
+# can_user(username, [other details])
+sub can_user
+{
+if (!&is_user($_[0])) {
+       # For external files, check if the file is under an allowed
+       # directory, or owned by an allowed user.
+       local @st = stat($_[0]);
+       local @uinfo = &get_mail_uid($st[4]);
+       return 1 if (@uinfo && &can_user(@uinfo));
+       local $dir = &allowed_directory();
+       return defined($dir) && &is_under_directory($dir, $_[0]);
+       }
+local @u = @_ > 1 ? @_ : &get_mail_user($_[0]);
+return 1 if ($_[0] && $access{'sent'} eq $_[0]);
+return 1 if ($access{'mmode'} == 1);
+return 0 if (!@u);
+return 0 if ($_[0] =~ /\.\./);
+return 0 if ($access{'mmode'} == 0);
+local $u;
+if ($access{'mmode'} == 2) {
+       # Is user in list of users?
+       foreach $u (split(/\s+/, $access{'musers'})) {
+               return 1 if ($u eq $_[0]);
+               }
+       return 0;
+       }
+elsif ($access{'mmode'} == 4) {
+       # Is user the current Webmin user?
+       return 1 if ($_[0] eq $remote_user);
+       }
+elsif ($access{'mmode'} == 5) {
+       # Is the user's gid in the list of groups?
+       local $gid;
+       foreach $gid (split(/\s+/, $access{'musers'})) {
+               return 1 if ($u[3] == $gid);
+               if ($access{'msec'}) {
+                       # Check user's secondary groups too
+                       local @ginfo = getgrgid($gid);
+                       local @m = split(/\s+/, $ginfo[3]);
+                       return 1 if (&indexof($_[0], @m) >= 0);
+                       }
+               }
+       }
+elsif ($access{'mmode'} == 3) {
+       # Is the user not in the list of denied users
+       foreach $u (split(/\s+/, $access{'musers'})) {
+               return 0 if ($u eq $_[0]);
+               }
+       return 1;
+       }
+elsif ($access{'mmode'} == 6) {
+       # Does the user match a regexp?
+       return ($_[0] =~ /^$access{'musers'}$/);
+       }
+elsif ($access{'mmode'} == 7) {
+       # Is the user's UID within the allowed range?
+       return (!$access{'musers'} || $u[2] >= $access{'musers'}) &&
+              (!$access{'musers2'} || $u[2] <= $access{'musers2'});
+       }
+return 0;      # can't happen!
+}
+
+# movecopy_user_select(number, folders, folder, form-no)
+# Returns HTML for entering a username to copy mail to
+sub movecopy_user_select
+{
+local $rv;
+$rv .= "<input type=submit name=move$_[0] value=\"$text{'mail_move'}\" ".
+       "onClick='return check_clicks(form)'>";
+$rv .= "<input type=submit name=copy$_[0] value=\"$text{'mail_copy'}\" ".
+       "onClick='return check_clicks(form)'>";
+$rv .= &ui_user_textbox("mfolder$_[0]", undef, $_[3]);
+return $rv;
+}
+
+# need_delete_warn(&folder)
+sub need_delete_warn
+{
+return 1 if ($config{'delete_warn'} eq 'y');
+return 0 if ($config{'delete_warn'} eq 'n');
+local $mf;
+return $_[0]->{'type'} == 0 &&
+       ($mf = &folder_file($_[0])) &&
+       &disk_usage_kb($mf)*1024 > $config{'delete_warn'};
+}
+
+@mail_system_modules = (
+                        [ undef, 4, \&check_qmail_ldap, \&test_qmail_ldap ],
+                        [ undef, 5, \&check_vpopmail ],
+                        [ "qmailadmin", 2 ],
+                        [ "postfix", 0 ],
+                        [ "sendmail", 1 ],
+                       );
+
+# detect_mail_system()
+# Works out which mail server is installed
+sub detect_mail_system
+{
+foreach $m (@mail_system_modules) {
+       return $m->[1] if (&check_mail_system($m));
+       }
+return 3;
+}
+
+# check_mail_system(&mailsystem)
+sub check_mail_system
+{
+if ($_[0]->[0]) {
+       # Just check module
+       return &foreign_installed($_[0]->[0]);
+       }
+else {
+       # Call function
+       local $func = $_[0]->[2];
+       return &$func($_[0]);
+       }
+}
+
+# test_mail_system([&mailsystem])
+# Returns an error message if the mail system is invalid
+sub test_mail_system
+{
+local $ms;
+if (!$ms) {
+       ($ms) = grep { $_->[1] == $config{'mail_system'} } @mail_system_modules;
+       }
+if ($ms->[3]) {
+       local $func = $ms->[3];
+       return &$func();
+       }
+return undef;
+}
+
+# check_qmail_ldap()
+# Make sure Qmail with LDAP extensions is installed
+sub check_qmail_ldap
+{
+return 0 if (&foreign_installed("qmailadmin", 1) != 2);
+local %qconfig = &foreign_config("qmailadmin");
+return 0 if (!-r "$qconfig{'qmail_dir'}/control/ldapserver");
+return 1;
+}
+
+# check_vpopmail()
+# Make sure Qmail with VPopMail extensions is installed
+sub check_vpopmail
+{
+return 0 if (&foreign_installed("qmailadmin", 1) != 2);
+return -x "$config{'vpopmail_dir'}/bin/vadddomain";
+}
+
+# test_qmail_ldap()
+# Returns undef the Qmail+LDAP database can be contacted OK, or an error message
+sub test_qmail_ldap
+{
+$config{'ldap_host'} || return $text{'ldap_ehost'};
+$config{'ldap_port'} =~ /^\d+$/ || return $text{'ldap_eport'};
+$config{'ldap_login'} || return $text{'ldap_euser'};
+$config{'ldap_base'} || return $text{'ldap_ebase'};
+local $err = &connect_qmail_ldap(1);
+return ref($err) ? undef : $err;
+}
+
+# show_users_table(&users-list, [only-with-mail])
+# Outputs HTML for a table of users, with the appropriate sorting and mode
+sub show_users_table
+{
+local @users = @{$_[0]};
+local ($u, %size, %incount, %sentcount, %foldercount);
+if ($config{'sort_mode'} == 2 || $config{'show_size'} > 0 || $_[1] ||
+    $config{'show_count'} || $config{'show_sent'}) {
+       # Need to check folders
+       foreach $u (@users) {
+               next if ($config{'ignore_users_enabled'} == 1 &&
+                        $config{'ignore_users'} =~ /$u->[0]/);
+               local @folders = &list_user_folders(@$u);
+               $foldercount{$u->[0]} = scalar(@folders);
+               if ($config{'sort_mode'} == 2 ||
+                   $config{'show_size'} > 0 || $_[1]) {
+                       # Compute size of folders
+                       $size{$u->[0]} = $config{'size_mode'} ?
+                                               &folder_size(@folders) :
+                                               &folder_size($folders[0]);
+                       }
+               if ($config{'show_count'}) {
+                       # Get number of mails in inbox
+                       $incount{$u->[0]} = &mailbox_folder_size($folders[0]);
+                       }
+               if ($config{'show_sent'}) {
+                       # Count number of messages in sent mail
+                       local ($sent) = grep { $_->{'sent'} } @folders;
+                       $sentcount{$u->[0]} = &mailbox_folder_size($sent)
+                               if ($sent);
+                       }
+               }
+       }
+
+# Sort by chosen mode
+if ($config{'sort_mode'} == 2) {
+       @users = sort { $size{$b->[0]} <=> $size{$a->[0]} } @users;
+       }
+elsif ($config{'sort_mode'} == 1) {
+       @users = sort { lc($a->[0]) cmp lc($b->[0]) } @users;
+       }
+
+local @allusers = @users;
+if ($_[1]) {
+       # Limit to those with mail
+       @users = grep { $size{$_->[0]} } @users;
+       }
+
+# Show table of users
+if (!@allusers) {
+       print "<b>$text{'index_nousers'}</b><p>\n";
+       }
+elsif (!@users) {
+       print "<b>$text{'index_nousersmail'}</b><p>\n";
+       }
+elsif ($config{'show_size'} == 2) {
+       # Show full user details
+       local %uconfig = &foreign_config("useradmin");
+       local @ccols;
+       push(@ccols, $text{'find_incount'}) if ($config{'show_count'});
+       push(@ccols, $text{'find_sentcount'}) if ($config{'show_sent'});
+       push(@ccols, $text{'find_fcount'}) if (%foldercount);
+       print &ui_columns_start( [ $text{'find_user'}, $text{'find_real'},
+                                  $text{'find_group'}, $text{'find_home'},
+                                  $text{'find_size'}, @ccols ], 100);
+       foreach $u (@users) {
+               local $g = getgrgid($u->[3]);
+               next if ($config{'ignore_users_enabled'} == 1 &&
+                        $config{'ignore_users'} =~ /$u->[0]/);
+               $u->[6] =~ s/,.*$// if ($uconfig{'extra_real'});
+               local $home = $u->[7];
+               if (length($home) > 30) {
+                       $home = "...".substr($home, -30);
+                       }
+               local @ccols;
+               if ($config{'show_count'}) {
+                       push(@ccols, int($incount{$u->[0]}))
+                       }
+               if ($config{'show_sent'}) {
+                       push(@ccols, int($sentcount{$u->[0]}))
+                       }
+               if (%foldercount) {
+                       push(@ccols, int($foldercount{$u->[0]}))
+                       }
+               print &ui_columns_row(
+                       [ "<a href='list_mail.cgi?user=$u->[0]'>$u->[0]</a>",
+                         $u->[6], $g, $home,
+                         $size{$u->[0]} == 0 ? $text{'index_empty'} :
+                               &nice_size($size{$u->[0]}),
+                         @ccols ],
+                       [ undef, undef, undef, undef, "nowrap" ]);
+               }
+       print &ui_columns_end();
+       }
+else {
+       # Just showing username (and maybe size)
+       print &ui_table_start($text{'index_header'}, "width=100%", $config{'column_count'});
+       local $i = 0;
+       foreach $u (@users) {
+               next if ($config{'ignore_users_enabled'} == 1 &&
+                        $config{'ignore_users'} =~ /$u->[0]/);
+               print "<tr>\n" if ($i % $config{'column_count'} == 0);
+               print "<td width=", int(100/$config{'column_count'}), "%><a href='list_mail.cgi?user=$u->[0]'>";
+               print $u->[0];
+               if ($config{'show_size'} == 1) {
+                       local @folders = &list_user_folders(@$u);
+                       local $total = &folder_size(@folders);
+                       if ($size{$u->[0]} > 0) {
+                               print $config{'show_size_below'} ? '<BR>' : ' ';
+                               print "(";
+                               if (%foldercount) {
+                                       print &text('find_in',
+                                               &nice_size($size{$u->[0]}),
+                                               $foldercount{$u->[0]});
+                                       }
+                               else {
+                                       print &nice_size($size{$u->[0]});
+                                       }
+                               print ")";
+                               }
+                       }
+               print "</a></td>\n";
+               print "</tr>\n" if ($i % $config{'column_count'} == ($config{'column_count'} - 1));
+               $i++;
+               }
+       if ($i % $config{'column_count'}) {
+               while($i++ % $config{'column_count'}) { print "<td width=", int(100/$config{'column_count'}), "%></td>\n"; }
+               print "</tr>\n";
+               }
+       print &ui_table_end();
+       }
+}
+
+# switch_to_user(user)
+# Switch to the Unix user that files are accessed as.
+sub switch_to_user
+{
+if (!defined($old_uid)) {
+        local @uinfo = &get_mail_user($_[0]);
+        $old_uid = $>;
+        $old_gid = $);
+        $) = "$uinfo[3] $uinfo[3]";
+        $> = $uinfo[2];
+        }
+}
+
+sub switch_user_back
+{
+if (defined($old_uid)) {
+        $> = $old_uid;
+        $) = $old_gid;
+        $old_uid = $old_gid = undef;
+        }
+}
+
+sub folder_link
+{
+return "<a href='list_mail.cgi?user=$_[0]&folder=$_[1]->{'index'}'>$text{'mail_return2'}</a>";
+}
+
+# get_from_address()
+# Returns the address to use when sending email from a script
+sub get_from_address
+{
+local $host = &get_from_domain();
+if ($config{'webmin_from'} =~ /\@/) {
+       return $config{'webmin_from'};
+       }
+elsif (!$config{'webmin_from'}) {
+       return "webmin\@$host";
+       }
+else {
+       return "$config{'webmin_from'}\@$host";
+       }
+}
+
+# get_from_domain()
+# Returns the default domain for From: addresses
+sub get_from_domain
+{
+return $config{'from_dom'} || &get_display_hostname();
+}
+
+# get_user_from_address(&uinfo)
+# Returns the default From: address for mail sent from some user's mailbox
+sub get_user_from_address
+{
+local $uinfo = $_[0];
+if ($config{'from_addr'}) {
+       return $config{'from_addr'};
+       }
+elsif ($uinfo->[11]) {
+       return $uinfo->[11];
+       }
+elsif ($config{'from_virtualmin'} && &foreign_check("virtual-server")) {
+       # Does Virtualmin manage this user?
+       &foreign_require("virtual-server", "virtual-server-lib.pl");
+       local $d;
+       foreach $d (&virtual_server::list_domains()) {
+               local @users = &virtual_server::list_domain_users($d, 0, 0, 1);
+               local $u;
+               foreach $u (@users) {
+                       if ($u->{'user'} eq $uinfo->[0] && $u->{'email'}) {
+                               # Found him!
+                               return $u->{'email'};
+                               }
+                       }
+               }
+       }
+if ($uinfo->[0] =~ /\@/) {
+       return $uinfo->[0];
+       }
+else {
+       return $uinfo->[0].'@'.&get_from_domain();
+       }
+}
+
+# check_modification(&folder)
+# Display an error message if a folder has been modified since the time
+# in $in{'mod'}
+sub check_modification
+{
+local $newmod = &modification_time($_[0]);
+if ($in{'mod'} && $in{'mod'} != $newmod && $config{'check_mod'}) {
+       # Changed!
+       &error(&text('emodified', "list_mail.cgi?user=$in{'user'}&folder=$_[0]->{'index'}"));
+       }
+}
+
+# spam_report_cmd(user)
+# Returns a command for reporting spam, or undef if none
+sub spam_report_cmd
+{
+local ($user) = @_;
+local %sconfig = &foreign_config("spam");
+local $cmd;
+if ($config{'spam_report'} eq 'sa_learn') {
+        $cmd = &has_command($sconfig{'sa_learn'}) ? "$sconfig{'sa_learn'} --spam --mbox" : undef;
+        }
+elsif ($config{'spam_report'} eq 'spamassassin') {
+        $cmd = &has_command($sconfig{'spamassassin'}) ? "$sconfig{'spamassassin'} --r" : undef;
+        }
+else {
+        $cmd = &has_command($sconfig{'sa_learn'}) ?
+                "$sconfig{'sa_learn'} --spam --mbox" :
+               &has_command($sconfig{'spamassassin'}) ?
+                "$sconfig{'spamassassin'} --r" : undef;
+        }
+return $user eq "root" ? $cmd :
+       $cmd ? &command_as_user($user, 0, $cmd) : undef;
+}
+
+# allowed_directory()
+# Returns base directory for mail files, or undef if not allowed
+sub allowed_directory
+{
+if ($access{'dir'}) {
+       return $access{'dir'};
+       }
+else {
+       return $access{'mmode'} == 1 ? "/" : undef;
+       }
+}
+
+# is_user(user)
+sub is_user
+{
+return $_[0] !~ /^\//;
+}
+
+# list_mail_users([max], [filterfunc])
+# Returns getpw* style structures for all users who can recieve mail. Those with
+# duplicate info are skipped.
+sub list_mail_users
+{
+local ($max, $filter) = @_;
+local @rv;
+if ($config{'mail_system'} < 3) {
+       # Postfix, Sendmail and Qmail all use Unix users
+       local %found;
+       local %hfound;
+       local ($dir, $style, $mailbox, $maildir) = &get_mail_style();
+       setpwent();
+       while(my (@uinfo) = getpwent()) {
+               next if ($found{$uinfo[0]}++);  # done this username already
+               next if (!$dir && $hfound{$uinfo[7]}++);
+               next if ($filter && !&$filter(@uinfo));
+               push(@rv, \@uinfo);
+               last if ($max && @rv > $max);
+               }
+       endpwent();
+       }
+elsif ($config{'mail_system'} == 4) {
+       # Qmail+LDAP uses an LDAP db
+       local $ldap = &connect_qmail_ldap();
+       local $rv = $ldap->search(base => $config{'ldap_base'},
+                         filter => "(objectClass=qmailUser)");
+       &error($rv->error) if ($rv->code);
+       local $u;
+       foreach $u ($rv->all_entries) {
+               local @uinfo = &qmail_dn_to_user($u);
+               next if (!$uinfo[10]);  # alias only
+               next if ($filter && !&$filter(@uinfo));
+               push(@rv, \@uinfo);
+               last if ($max && @rv > $max);
+               }
+       $ldap->unbind();
+       }
+elsif ($config{'mail_system'} == 5) {
+       # Get vpopmail user list for all domains
+       opendir(DOMS, "$config{'vpopmail_dir'}/domains");
+       foreach my $d (readdir(DOMS)) {
+               next if ($d =~ /^\./);
+               local @uinfos = &parse_vpopmail_users("-D $d", $d);
+               if ($filter) {
+                       @uinfos = grep { &$filter(@$_) } @uinfos;
+                       }
+               push(@rv, @uinfos);
+               last if ($max && @rv > $max);
+               }
+       closedir(DOMS);
+       }
+return @rv;
+}
+
+# parse_vpopmail_users(command, domain)
+sub parse_vpopmail_users
+{
+local %attr_map = ( "passwd" => 1,
+                   "gecos" => 5,
+                   "dir" => 7 );
+local (@rv, $user);
+open(UINFO, "$config{'vpopmail_dir'}/bin/vuserinfo $_[0] |");
+while(<UINFO>) {
+       s/\r|\n//g;
+       if (/^([^:]+):\s+(.*)$/) {
+               local ($attr, $value) = ($1, $2);
+               if ($attr eq "name") {
+                       # Start of a new user
+                       $user = [ "$value\@$_[1]" ];
+                       $user->[11] = $user->[0];
+                       push(@rv, $user);
+                       }
+               local $amapped = $attr_map{$attr};
+               $user->[$amapped] = $value if ($amapped);
+               }
+       }
+close(UINFO);
+return @rv;
+}
+
+# get_mail_user(name)
+# Looks up a user by name
+sub get_mail_user
+{
+if ($config{'mail_system'} < 3) {
+       # Just find Unix user
+       return getpwnam($_[0]);
+       }
+elsif ($config{'mail_system'} == 4) {
+       # Lookup in LDAP DB
+       local $ldap = &connect_qmail_ldap();
+       local $rv = $ldap->search(base => $config{'ldap_base'},
+                         filter => "(&(objectClass=qmailUser)(uid=$_[0]))");
+       &error($rv->error) if ($rv->code);
+       local ($u) = $rv->all_entries;
+       if ($u) {
+               # Found in LDAP
+               local @uinfo = &qmail_dn_to_user($u);
+               $ldap->unbind();
+               return @uinfo;
+               }
+       else {
+               # Fall back to Unix user
+               return getpwnam($_[0]);
+               }
+       }
+elsif ($config{'mail_system'} == 5) {
+       # Find in vpopmail
+       local ($box, $dom) = split(/\@/, $_[0]);
+       local @users = &parse_vpopmail_users($_[0], $dom);
+       return @{$users[0]};
+       }
+}
+
+# get_mail_uid(name)
+# Looks up a user by UID
+sub get_mail_uid
+{
+if ($config{'mail_system'} < 3) {
+       # Just find Unix user
+       return getpwuid($_[0]);
+       }
+elsif ($config{'mail_system'} == 4) {
+       # Lookup in LDAP DB
+       local $ldap = &connect_qmail_ldap();
+       local $rv = $ldap->search(base => $config{'ldap_base'},
+                 filter => "(&(objectClass=qmailUser)(uidNumber=$_[0]))");
+       &error($rv->error) if ($rv->code);
+       local ($u) = $rv->all_entries;
+       local @uinfo = &qmail_dn_to_user($u);
+       $ldap->unbind();
+       return @uinfo;
+       }
+elsif ($config{'mail_system'} == 5) {
+       # Find in vpopmail
+       return ( );     # not possible, since UIDs aren't used!
+       }
+}
+
+# connect_qmail_ldap([return-error])
+# Connect to the LDAP server used for Qmail. Returns an LDAP handle on success,
+# or an error message on failure.
+sub connect_qmail_ldap
+{
+eval "use Net::LDAP";
+if ($@) {
+       local $err = &text('ldap_emod', "<tt>Net::LDAP</tt>");
+       if ($_[0]) { return $err; }
+       else { &error($err); }
+       }
+
+# Connect to server
+local $port = $config{'ldap_port'} || 389;
+local $ldap = Net::LDAP->new($config{'ldap_host'}, port => $port);
+if (!$ldap) {
+       local $err = &text('ldap_econn',
+                          "<tt>$config{'ldap_host'}</tt>","<tt>$port</tt>");
+       if ($_[0]) { return $err; }
+       else { &error($err); }
+       }
+
+# Start TLS if configured
+if ($config{'ldap_tls'}) {
+       $ldap->start_tls();
+       }
+
+# Login
+local $mesg;
+if ($config{'ldap_login'}) {
+       $mesg = $ldap->bind(dn => $config{'ldap_login'},
+                           password => $config{'ldap_pass'});
+       }
+else {
+       $mesg = $ldap->bind(anonymous => 1);
+       }
+if (!$mesg || $mesg->code) {
+       local $err = &text('ldap_elogin', "<tt>$config{'ldap_host'}</tt>",
+                    $dn, $mesg ? $mesg->error : "Unknown error");
+       if ($_[0]) { return $err; }
+       else { &error($err); }
+       }
+return $ldap;
+}
+
+# qmail_dn_to_user(&dn)
+sub qmail_dn_to_user
+{
+local $mms = &add_ldapmessagestore(
+       scalar($_[0]->get_value("mailMessageStore")));
+if (-d "$mms/Maildir") {
+       $mms .= "/" if ($mms !~ /\/$/);
+       $mms .= "Maildir";
+       }
+return ( scalar($_[0]->get_value("uid")),
+        scalar($_[0]->get_value("userPassword")),
+        scalar($_[0]->get_value("uidNumber")),
+        scalar($_[0]->get_value("gidNumber")),
+        scalar($_[0]->get_value("mailQuotaSize")),
+        scalar($_[0]->get_value("cn")),
+        scalar($_[0]->get_value("cn")),
+        scalar($_[0]->get_value("homeDirectory")),
+        scalar($_[0]->get_value("loginShell")),
+        undef,
+        $mms,
+        scalar($_[0]->get_value("mail")),
+        );
+}
+
+# add_ldapmessagestore(path)
+sub add_ldapmessagestore
+{
+if (!$_[0]) {
+       return $_[0];
+       }
+elsif ($_[0] =~ /^\//) {
+       return $_[0];
+       }
+else {
+       &foreign_require("qmailadmin", "qmail-lib.pl");
+       local $pfx = &qmailadmin::get_control_file("ldapmessagestore");
+       return $pfx."/".$_[0];
+       }
+}
+
+# show_buttons(number, &folders, current-folder, &mail, user, search-mode)
+sub show_buttons
+{
+local ($num, $folders, $folder, $mail, $user, $search) = @_;
+local $uuser = &urlize($user);
+local $spacer = "&nbsp;\n";
+if (@$mail) {
+       # Delete
+       print "<input type=submit name=delete value=\"$text{'mail_delete'}\" ",
+             "onClick='return check_clicks(form)'>";
+       if ($config{'show_delall'} && !$search) {
+               print "<input type=submit name=deleteall value=\"$text{'mail_deleteall'}\">";
+               }
+       print $spacer;
+
+       # Mark as
+       print "<input type=submit name=mark$_[0] value=\"$text{'mail_mark'}\">";
+       print "<select name=mode$_[0]>\n";
+       print "<option value=1 checked>$text{'mail_mark1'}\n";
+       print "<option value=0>$text{'mail_mark0'}\n";
+       print "<option value=2>$text{'mail_mark2'}\n";
+       print "</select>";
+       print $spacer;
+
+       if (&is_user($user)) {
+               # Forward
+               if ($config{'open_mode'}) {
+                       # Forward messages in a separate window
+                       print "<input type=submit name=forward value=\"$text{'mail_forward'}\" onClick='args = \"user=$uuser&folder=$folder->{'index'}\"; for(i=0; i<form.d.length; i++) { if (form.d[i].checked) { args += \"&mailforward=\"+form.d[i].value; } } window.open(\"reply_mail.cgi?\"+args, \"compose\", \"toolbar=no,menubar=no,scrollbars=yes,width=1024,height=768\"); return false'>";
+
+                       }
+               else {
+                       print "<input type=submit name=forward value=\"$text{'mail_forward'}\">";
+                       }
+               print $spacer;
+               }
+
+       # Move/copy
+       print &movecopy_user_select($_[0], $folders, $folder, 1);
+       print $spacer;
+
+       # Show spam report buttons
+       local @modules = &get_available_module_infos(1);
+       local ($hasspam) = grep { $_->{'dir'} eq "spam" } @modules;
+       if (&foreign_installed("spam") &&
+           $config{'spam_buttons'} =~ /list/ &&
+           &spam_report_cmd($user)) {
+               if ($hasspam) {
+                       print "<input type=submit value=\"$text{'mail_black'}\" name=black>";
+                       }
+               if ($config{'spam_del'}) {
+                       print "<input type=submit value=\"$text{'view_razordel'}\" name=razor>\n";
+                       }
+               else {
+                       print "<input type=submit value=\"$text{'view_razor'}\" name=razor>";
+                       }
+               print $spacer;
+               }
+       }
+
+if ($config{'open_mode'}) {
+        # Show mass open button
+        print "<input type=submit name=new value=\"$text{'mail_open'}\" onClick='for(i=0; i<form.d.length; i++) { if (form.d[i].checked) { window.open(\"view_mail.cgi?user=$uuser&folder=$folder->{'index'}&idx=\"+form.d[i].value, \"view\"+i, \"toolbar=no,menubar=no,scrollbars=yes,width=1024,height=768\"); } }
+ return false'>";
+        print $spacer;
+        }
+
+# Compose
+if (&is_user($user)) {
+       if ($config{'open_mode'}) {
+               # In a separate window
+               print "<input type=submit name=new value=\"$text{'mail_compose'}\" onClick='window.open(\"reply_mail.cgi?user=$user&new=1\", \"compose\", \"toolbar=no,menubar=no,scrollbars=yes,width=1024,height=768\"); return false'>";
+               }
+       else {
+               print "<input type=submit name=new value=\"$text{'mail_compose'}\">";
+               }
+       }
+print "<br>\n";
+}
+
+# get_signature(user)
+# Returns the users signature, if any
+sub get_signature
+{
+local $sf = &get_signature_file($_[0]);
+$sf || return undef;
+local $sig;
+open(SIG, $sf) || return undef;
+while(<SIG>) {
+       $sig .= $_;
+       }
+close(SIG);
+return $sig;
+}
+
+# get_signature_file(user)
+# Returns the full path to the file that should contain the user's signature,
+# or undef if none is defined
+sub get_signature_file
+{
+return undef if ($config{'sig_file'} eq '*' || $config{'sig_file'} eq '');
+local $sf = $config{'sig_file'};
+if ($sf !~ /^\//) {
+       local @uinfo = getpwnam($_[0]);
+       $sf = "$uinfo[7]/$sf";
+       }
+return $sf;
+}
+
+# view_mail_link(user, &folder, index, from-to-text)
+sub view_mail_link
+{
+local ($user, $folder, $idx, $txt) = @_;
+local $uuser = &urlize($user);
+local $url = "view_mail.cgi?user=$uuser&idx=$idx&folder=$folder->{'index'}";
+if ($config{'open_mode'}) {
+        return "<a href='' onClick='window.open(\"$url\", \"viewmail\", \"toolbar=no,menubar=no,scrollbars=yes,width=1024,height=768\"); return false'>".
+               &simplify_from($txt)."</a>";
+        }
+else {
+        return "<a href='$url'>".&simplify_from($txt)."</a>";
+        }
+}
+
+# mail_page_header(title, headstuff, bodystuff, rightstuff)
+sub mail_page_header
+{
+if ($config{'open_mode'}) {
+        &popup_header($_[0], $_[1], $_[2]);
+        }
+else {
+        &ui_print_header(undef, $_[0], "", undef, 0, 0, 0, $_[3], $_[1], $_[2]);
+        }
+}
+
+# mail_page_footer(link, text, ...)
+sub mail_page_footer
+{
+if ($config{'open_mode'}) {
+        &popup_footer();
+        }
+else {
+        &ui_print_footer(@_);
+        }
+}
+
+1;
+
diff --git a/mailboxes/makelang.pl b/mailboxes/makelang.pl
new file mode 100755 (executable)
index 0000000..c753e13
--- /dev/null
@@ -0,0 +1,54 @@
+#!/usr/local/bin/perl
+
+@ARGV == 1 || die "usage: makelang.pl <language>";
+
+$usermin = "/usr/local/useradmin/mailbox/ulang";
+$mailboxes = "/usr/local/webadmin/mailboxes/lang";
+$sendmail = "/usr/local/webadmin/sendmail/lang";
+
+&read_file("$mailboxes/en", \%emailboxes, \@eorder);
+&read_file("$sendmail/en", \%esendmail);
+&read_file("$usermin/en", \%eusermin);
+
+&read_file("$mailboxes/$ARGV[0]", \%fmailboxes);
+&read_file("$sendmail/$ARGV[0]", \%fsendmail);
+&read_file("$usermin/$ARGV[0]", \%fusermin);
+
+foreach $k (@eorder) {
+       if ($emailboxes{$k} eq $esendmail{$k} &&
+           $fsendmail{$k} &&
+           $fsendmail{$k} ne $esendmail{$k}) {
+               print "$k=$fsendmail{$k}\n";
+               }
+       elsif ($emailboxes{$k} eq $eusermin{$k} &&
+              $fusermin{$k} &&
+              $fusermin{$k} ne $eusermin{$k}) {
+               print "$k=$fusermin{$k}\n";
+               }
+       }
+
+# read_file(file, &assoc, [&order], [lowercase])
+# Fill an associative array with name=value pairs from a file
+sub read_file
+{
+local $_;
+open(ARFILE, $_[0]) || return 0;
+while(<ARFILE>) {
+       chomp;
+       local $hash = index($_, "#");
+       local $eq = index($_, "=");
+       if ($hash != 0 && $eq >= 0) {
+               local $n = substr($_, 0, $eq);
+               local $v = substr($_, $eq+1);
+               $_[1]->{$_[3] ? lc($n) : $n} = $v;
+               push(@{$_[2]}, $n) if ($_[2]);
+               }
+        }
+close(ARFILE);
+if (defined($main::read_file_cache{$_[0]})) {
+       %{$main::read_file_cache{$_[0]}} = %{$_[1]};
+       }
+return 1;
+}
+
+
diff --git a/mailboxes/module.info b/mailboxes/module.info
new file mode 100644 (file)
index 0000000..9e4d03a
--- /dev/null
@@ -0,0 +1,11 @@
+desc=Read User Mail
+category=servers
+longdesc=Read email in users' mailboxes.
+desc_de=Lese Benutzer-E-Mail
+desc_zh_TW.Big5=Ū¨ú¨Ï¥ÎªÌ¶l¥ó
+desc_ca=Lectura del Correu d'Usuaris
+desc_fr=Lecture du Courrier des Usagers
+readonly=1
+os_support=!windows
+desc_es=Lectura de Correo de Usuarios
+desc_zh_TW.UTF-8=讀取使用者郵件
diff --git a/mailboxes/reply_mail.cgi b/mailboxes/reply_mail.cgi
new file mode 100755 (executable)
index 0000000..a0b2ebe
--- /dev/null
@@ -0,0 +1,555 @@
+#!/usr/local/bin/perl
+# Display a form for replying to or composing an email
+
+require './mailboxes-lib.pl';
+&ReadParse();
+&can_user($in{'user'}) || &error($text{'mail_ecannot'});
+@uinfo = &get_mail_user($in{'user'});
+@uinfo || &error($text{'view_eugone'});
+
+@folders = &list_user_folders($in{'user'});
+$folder = $folders[$in{'folder'}];
+if ($in{'new'}) {
+       # Composing a new email
+       $html_edit = 1 if ($config{'html_edit'} == 2);
+       $sig = &get_signature($in{'user'});
+       if ($html_edit) {
+               $sig =~ s/\n/<br>\n/g;
+               $quote = "<html><body></body></html>";
+               }
+       else {
+               $quote = "\n\n$sig" if ($sig);
+               }
+       $to = $in{'to'};
+       &mail_page_header($text{'compose_title'}, undef,
+                         $html_edit ? "onload='initEditor()'" : "",
+                         &folder_link($in{'user'}, $folder));
+       }
+else {
+       # Replying or forwarding
+       if ($in{'mailforward'} ne '') {
+               # Replying to multiple
+               @mailforward = sort { $a <=> $b }
+                                   split(/\0/, $in{'mailforward'});
+               @mails = &mailbox_list_mails(
+                            $mailforward[0], $mailforward[@mailforward-1],
+                            $folder);
+               $mail = $mails[$mailforward[0]];
+               }
+       else {
+               # Replying to one
+               @mails = &mailbox_list_mails($in{'idx'}, $in{'idx'},
+                                            $folder);
+               $mail = $mails[$in{'idx'}];
+               &decode_and_sub();
+               }
+       &check_modification($folder) if ($in{'delete'});
+       $mail || &error($text{'mail_eexists'});
+
+       if ($in{'delete'}) {
+               # Just delete the email
+               if (!$in{'confirm'} && &need_delete_warn($folder)) {
+                       # Need to ask for confirmation before deleting
+                       &mail_page_header($text{'confirm_title'}, undef, undef,
+                                         &folder_link($in{'user'}, $folder));
+                       print &check_clicks_function();
+
+                       print "<form action=reply_mail.cgi>\n";
+                       foreach $i (keys %in) {
+                               foreach $v (split(/\0/, $in{$i})) {
+                                       print "<input type=hidden name=$i ",
+                                             "value='",&html_escape($v),"'>\n";
+                                       }
+                               }
+                       print "<center><b>$text{'confirm_warn3'}<br>\n";
+                       if ($config{'delete_warn'} ne 'y') {
+                               print "$text{'confirm_warn2'}<p>\n"
+                               }
+                       else {
+                               print "$text{'confirm_warn4'}<p>\n"
+                               }
+                       print "</b><p><input type=submit name=confirm ",
+                             "value='$text{'confirm_ok'}' ",
+                             "onClick='return check_clicks(form)'></center></form>\n";
+                       
+                       &mail_page_footer("view_mail.cgi?idx=$in{'idx'}&folder=$in{'folder'}&user=$in{'user'}",
+                               $text{'view_return'},
+                               "list_mail.cgi?folder=$in{'folder'}&user=$in{'user'}",
+                               $text{'mail_return'},
+                               "", $text{'index_return'});
+                       exit;
+                       }
+               &lock_folder($folder);
+               &mailbox_delete_mail($folder, $mail);
+               &unlock_folder($folder);
+               &webmin_log("delmail", undef, undef,
+                           { 'from' => $folder->{'file'},
+                             'count' => 1 } );
+               &redirect("list_mail.cgi?folder=$in{'folder'}&user=$in{'user'}");
+               exit;
+               }
+       elsif ($in{'print'}) {
+               # Extract the mail body
+               &decode_and_sub();
+               ($textbody, $htmlbody, $body) =
+                       &find_body($mail, $config{'view_html'});
+
+               # Output HTML header
+               &PrintHeader();
+               print "<html><head>\n";
+               print "<title>",&html_escape(&decode_mimewords(
+                       $mail->{'header'}->{'subject'})),"</title></head>\n";
+               print "<body bgcolor=#ffffff onLoad='window.print()'>\n";
+
+               # Display the headers
+               print "<table width=100% border=1>\n";
+               print "<tr $tb> <td><b>$text{'view_headers'}</b></td> </tr>\n";
+               print "<tr $cb> <td><table width=100%>\n";
+               print "<tr> <td><b>$text{'mail_from'}</b></td> ",
+                     "<td>",&eucconv_and_escape($mail->{'header'}->{'from'}),"</td> </tr>\n";
+               print "<tr> <td><b>$text{'mail_to'}</b></td> ",
+                     "<td>",&eucconv_and_escape($mail->{'header'}->{'to'}),"</td> </tr>\n";
+               print "<tr> <td><b>$text{'mail_cc'}</b></td> ",
+                     "<td>",&eucconv_and_escape($mail->{'header'}->{'cc'}),"</td> </tr>\n"
+                       if ($mail->{'header'}->{'cc'});
+               print "<tr> <td><b>$text{'mail_date'}</b></td> ",
+                     "<td>",&eucconv_and_escape(&html_escape($mail->{'header'}->{'date'})),
+                     "</td> </tr>\n";
+               print "<tr> <td><b>$text{'mail_subject'}</b></td> ",
+                     "<td>",&eucconv_and_escape(&decode_mimewords(
+                       $mail->{'header'}->{'subject'})),"</td> </tr>\n";
+               print "</table></td></tr></table><p>\n";
+
+               # Just display the mail body for printing
+               if ($body eq $textbody) {
+                       print "<table border width=100%><tr $cb><td><pre>";
+                       foreach $l (&wrap_lines($body->{'data'},
+                                               $config{'wrap_width'})) {
+                               print &eucconv_and_escape($l),"\n";
+                               }
+                       print "</pre></td></tr></table>\n";
+                       }
+               elsif ($body eq $htmlbody) {
+                       print "<table border width=100%><tr><td>\n";
+                       print &safe_html($body->{'data'});
+                       print "</td></tr></table>\n";
+                       }
+
+               print "</body></html>\n";
+               exit;
+               }
+       elsif ($in{'mark1'} || $in{'mark2'}) {
+               # Just mark the message
+               dbmopen(%read, "$module_config_directory/$in{'user'}.read", 0600);
+               $mode = $in{'mark1'} ? $in{'mode1'} : $in{'mode2'};
+               if ($mode) {
+                       $read{$mail->{'header'}->{'message-id'}} = $mode;
+                       }
+               else {
+                       delete($read{$mail->{'header'}->{'message-id'}});
+                       }
+               $perpage = $folder->{'perpage'} || $config{'perpage'};
+               $s = int((@mails - $in{'idx'} - 1) / $perpage) * $perpage;
+               &redirect("list_mail.cgi?start=$s&folder=$in{'folder'}&user=$in{'user'}");
+               exit;
+               }
+       elsif ($in{'detach'}) {
+               # Detach some attachment to a directory on the server
+               &error_setup($text{'detach_err'});
+               $in{'dir'} || &error($text{'detach_edir'});
+               $in{'dir'} = "$uinfo[7]/$in{'dir'}"
+                       if ($in{'dir'} !~ /^\//);
+               &decode_and_sub();
+
+               if ($in{'attach'} eq '*') {
+                       # Detaching all attachments, under their filenames
+                       @dattach = grep { $_->{'idx'} ne $in{'bindex'} }
+                                       @{$mail->{'attach'}};
+                       }
+               else {
+                       # Just one attachment
+                       @dattach = ( $mail->{'attach'}->[$in{'attach'}] );
+                       }
+
+               local @paths;
+               foreach $attach (@dattach) {
+                       local $path;
+                       if (-d $in{'dir'}) {
+                               # Just write to the filename in the directory
+                               local $fn;
+                               if ($attach->{'filename'}) {
+                                       $fn = &decode_mimewords(
+                                               $attach->{'filename'});
+                                       }
+                               else {
+                                       $attach->{'type'} =~ /\/(\S+)$/;
+                                       $fn = "file.$1";
+                                       }
+                               $path = "$in{'dir'}/$fn";
+                               }
+                       else {
+                               # Assume a full path was given
+                               $path = $in{'dir'};
+                               }
+                       push(@paths, $path);
+                       }
+
+               &switch_to_user($in{'user'});
+               for($i=0; $i<@dattach; $i++) {
+                       # Try to write the files
+                       &open_tempfile(FILE, ">$paths[$i]", 1, 1) ||
+                               &error(&text('detach_eopen',
+                                            "<tt>$paths[$i]</tt>", $!));
+                       (print FILE $dattach[$i]->{'data'}) ||
+                               &error(&text('detach_ewrite',
+                                            "<tt>$paths[$i]</tt>", $!));
+                       close(FILE) ||
+                               &error(&text('detach_ewrite',
+                                            "<tt>$paths[$i]</tt>", $!));
+                       }
+               &switch_user_back();
+
+               # Show a message about the new files
+               &mail_page_header($text{'detach_title'}, undef, undef,
+                                 &folder_link($in{'user'}, $folder));
+
+               for($i=0; $i<@dattach; $i++) {
+                       local $sz = (int(length($dattach[$i]->{'data'}) /
+                                        1000)+1)." Kb";
+                       print "<p>",&text('detach_ok',
+                                         "<tt>$paths[$i]</tt>", $sz),"<p>\n";
+                       }
+
+               &mail_page_footer("view_mail.cgi?idx=$in{'idx'}&folder=$in{'folder'}&user=$in{'user'}", $text{'view_return'},
+                       "list_mail.cgi?folder=$in{'folder'}&user=$in{'user'}", $text{'mail_return'},
+                       "", $text{'index_return'});
+               exit;
+               }
+       elsif ($in{'black'}) {
+               # Add sender to global SpamAssassin blacklist, and tell user
+               &mail_page_header($text{'black_title'});
+
+               &foreign_require("spam", "spam-lib.pl");
+               local $conf = &spam::get_config();
+               local @from = map { @{$_->{'words'}} }
+                                 &spam::find("blacklist_from", $conf);
+               local %already = map { $_, 1 } @from;
+               local ($spamfrom) = &address_parts($mail->{'header'}->{'from'});
+               if ($already{$spamfrom}) {
+                       print "<b>",&text('black_already',
+                                         "<tt>$spamfrom</tt>"),"</b><p>\n";
+                       }
+               else {
+                       push(@from, $spamfrom);
+                       &spam::save_directives($conf, 'blacklist_from',
+                                              \@from, 1);
+                       &flush_file_lines();
+                       print "<b>",&text('black_done',
+                                         "<tt>$spamfrom</tt>"),"</b><p>\n";
+                       }
+
+               &mail_page_footer("list_mail.cgi?folder=$in{'folder'}&user=$in{'user'}", $text{'mail_return'}, "", $text{'index_return'});
+               exit;
+               }
+       elsif ($in{'razor'}) {
+               # Report message to Razor and tell user
+               &mail_page_header($text{'razor_title'});
+
+               print "<b>$text{'razor_report'}</b>\n";
+               print "<pre>";
+               local $cmd = &spam_report_cmd($in{'user'});
+               local $temp = &transname();
+               &send_mail($mail, $temp, 0, 1);
+               &open_execute_command(OUT, "$cmd <$temp 2>&1", 1);
+               local $error;
+               while(<OUT>) {
+                       print &html_escape($_);
+                       $error++ if (/failed/i);
+                       }
+               close(OUT);
+               unlink($temp);
+               print "</pre>\n";
+               if ($? || $error) {
+                       print "<b>$text{'razor_err'}</b><p>\n";
+                       }
+               else {
+                       if ($config{'spam_del'}) {
+                               # Delete message too
+                               &lock_folder($folder);
+                               &mailbox_delete_mail($folder, $mail);
+                               &unlock_folder($folder);
+                               print "<b>$text{'razor_deleted'}</b><p>\n";
+                               }
+                       else {
+                               print "<b>$text{'razor_done'}</b><p>\n";
+                               }
+                       }
+
+               &mail_page_footer("list_mail.cgi?folder=$in{'folder'}&user=$in{'user'}", $text{'mail_return'}, "", $text{'index_return'});
+               exit;
+               }
+
+       if (!@mailforward) {
+               &parse_mail($mail);
+               @attach = @{$mail->{'attach'}};
+               }
+
+       if ($in{'strip'}) {
+               # Remove all non-body attachments
+               local $newmail = { 'headers' => $mail->{'headers'},
+                                  'header' => $mail->{'header'},
+                                  'fromline' => $mail->{'fromline'} };
+               foreach $a (@attach) {
+                       if ($a->{'type'} eq 'text/plain' ||
+                           $a->{'type'} eq 'text') {
+                               $newmail->{'attach'} = [ $a ];
+                               last;
+                               }
+                       }
+               &lock_folder($folder);
+               &mailbox_modify_mail($mail, $newmail, $folder);
+               &unlock_folder($folder);
+               &redirect("list_mail.cgi?user=$in{'user'}&folder=$in{'folder'}");
+               exit;
+               }
+
+       if ($in{'enew'}) {
+               # Editing an existing message, so keep same fields
+               $to = $mail->{'header'}->{'to'};
+               $rto = $mail->{'header'}->{'reply-to'};
+               $from = $mail->{'header'}->{'from'};
+               $cc = $mail->{'header'}->{'cc'};
+               $ouser = $1 if ($from =~ /^(\S+)\@/);
+               }
+       else {
+               if (!$in{'forward'} && !@mailforward) {
+                       # Replying to a message, so set To: field
+                       $to = $mail->{'header'}->{'reply-to'};
+                       $to = $mail->{'header'}->{'from'} if (!$to);
+                       }
+               if ($in{'rall'}) {
+                       # If replying to all, add any addresses in the original
+                       # To: or Cc: to our new Cc: address.
+                       # XXX should strip own addresses
+                       $cc = $mail->{'header'}->{'to'};
+                       $cc .= ", ".$mail->{'header'}->{'cc'}
+                               if ($mail->{'header'}->{'cc'});
+                       }
+               }
+
+       # Work out new subject, depending on whether we are replying
+       # our forwarding a message (or neither)
+       local $qu = !$in{'enew'} &&
+                   (!$in{'forward'} || !$config{'fwd_mode'});
+       $subject = &html_escape(&decode_mimewords(
+                               $mail->{'header'}->{'subject'}));
+       $subject = "Re: ".$subject if ($subject !~ /^Re/i && !$in{'forward'} &&
+                                      !@mailforward && !$in{'enew'});
+       $subject = "Fwd: ".$subject if ($subject !~ /^Fwd/i &&
+                                       ($in{'forward'} || @mailforward));
+
+       # Construct the initial mail text
+       $sig = &get_signature($in{'user'});
+       ($quote, $html_edit, $body) = &quoted_message($mail, $qu, $sig);
+       if ($in{'forward'} || $in{'enew'}) {
+               @attach = grep { $_ ne $body } @attach;
+               }
+       else {
+               undef(@attach);
+               }
+
+       # Show header
+       &mail_page_header(
+               $in{'forward'} || @mailforward ? $text{'forward_title'} :
+               $in{'enew'} ? $text{'enew_title'} :
+                             $text{'reply_title'}, undef,
+               $html_edit ? "onload='initEditor()'" : "",
+               &folder_link($in{'user'}, $folder));
+       }
+
+print "<form action=send_mail.cgi method=post enctype=multipart/form-data>\n";
+
+# Output various hidden fields
+print "<input type=hidden name=user value='$in{'user'}'>\n";
+print "<input type=hidden name=ouser value='$ouser'>\n";
+print "<input type=hidden name=idx value='$in{'idx'}'>\n";
+print "<input type=hidden name=folder value='$in{'folder'}'>\n";
+print "<input type=hidden name=new value='$in{'new'}'>\n";
+print "<input type=hidden name=enew value='$in{'enew'}'>\n";
+foreach $s (@sub) {
+       print "<input type=hidden name=sub value='$s'>\n";
+       }
+
+print "<table width=100% border=1>\n";
+print "<tr> <td $tb><b>$text{'reply_headers'}</b></td> </tr>\n";
+print "<tr> <td $cb><table width=100%>\n";
+
+# Work out and show the From: address
+print "<tr> <td><b>$text{'mail_from'}</b></td>\n";
+$from ||= &get_user_from_address(\@uinfo);
+if ($access{'fmode'} == 0) {
+       # Any email addresss
+       print "<td><input name=from size=40 value='$from'></td>\n";
+       }
+elsif ($access{'fmode'} == 1) {
+       # User's name in selected domains
+       local $u = $from || $ouser || $in{'user'};
+       $u =~ s/\@.*$//;
+       print "<td><select name=from>\n";
+       foreach $f (split(/\s+/, $access{'from'})) {
+               printf "<option value=%s %s>%s\n",
+                   "$u\@$f", $from eq "$u\@$f" ? 'selected' : '',
+                   $access{'fromname'} ?
+                       "\"$access{'fromname'}\" &lt;$u\@$f&gt;" : "$u\@$f";
+               }
+       print "</select></td>\n";
+       }
+elsif ($access{'fmode'} == 2) {
+       # Listed from addresses
+       print "<td><select name=from>\n";
+       foreach $f (split(/\s+/, $access{'from'})) {
+               printf "<option value=%s %s>%s\n",
+                   $f, $from eq $f ? 'selected' : '',
+                   $access{'fromname'} ? "$access{'fromname'} &lt;$f&gt;" : $f;
+               }
+       print "</select></td>\n";
+       }
+elsif ($access{'fmode'} == 3) {
+       # Fixed address in fixed domain
+       print "<td><input name=from size=10>$ouser\@$access{'from'}</td>\n";
+       }
+
+$to = &html_escape($to);
+print "<td><b>$text{'mail_to'}</b></td> ",
+      "<td><input name=to size=40 value=\"$to\"></td> </tr>\n";
+
+$cc = &html_escape($cc);
+print "<tr> <td><b>$text{'mail_cc'}</b></td> ",
+      "<td><input name=cc size=40 value=\"$cc\"></td>\n";
+print "<td><b>$text{'mail_bcc'}</b></td> ",
+      "<td><input name=bcc size=40 value='$config{'bcc_to'}'></td> </tr>\n";
+print "<tr> <td><b>$text{'mail_subject'}</b></td> ",
+      "<td><input name=subject size=40 value=\"$subject\"></td>\n";
+print "<td><b>$text{'mail_pri'}</b></td> ",
+      "<td><table cellpadding=0 cellspacing=0 width=100%>\n",
+      "<tr><td align=left><select name=pri>\n",
+      "<option value=1>$text{'mail_highest'}\n",
+      "<option value=2>$text{'mail_high'}\n",
+      "<option value='' selected>$text{'mail_normal'}\n",
+      "<option value=4>$text{'mail_low'}\n",
+      "<option value=5>$text{'mail_lowest'}\n",
+      "</select></td>\n",
+      "<td align=right><input type=submit value=\"$text{'reply_send'}\">\n",
+      "</tr></table></td></tr>\n";
+print "</table></td></tr></table><p>\n";
+
+# Output message body input
+print "<table width=100% border=1>\n",
+      "<tr $tb> <td><b>$text{'reply_body'}</b></td> </tr>",
+      "<tr $cb> <td>";
+if ($html_edit) {
+       # Output HTML editor textarea
+       print <<EOF;
+<script type="text/javascript">
+  _editor_url = "$gconfig{'webprefix'}/$module_name/xinha/";
+  _editor_lang = "en";
+</script>
+<script type="text/javascript" src="xinha/htmlarea.js"></script>
+
+<script type="text/javascript">
+var editor = null;
+function initEditor() {
+  editor = new HTMLArea("body");
+  editor.generate();
+  return false;
+}
+</script>
+EOF
+       print "<textarea rows=20 cols=80 style='width:100%' name=body id=body>",
+             &html_escape($quote),"</textarea>\n";
+       }
+else {
+       # Show text editing area
+       print "<textarea rows=20 cols=80 name=body $config{'wrap_mode'}>",
+             &html_escape($quote),"</textarea>\n";
+       if (&has_command("ispell")) {
+               print "<br>\n";
+               print "<input type=checkbox name=spell value=1> $text{'reply_spell'}\n";
+               }
+       }
+print "</td></tr></table><p>\n";
+print "<input type=hidden name=html_edit value='$html_edit'>\n";
+
+# Display forwarded attachments
+if (@attach) {
+       print "<table width=100% border=1>\n";
+       print "<tr> <td $tb><b>$text{'reply_attach'}</b></td> </tr>\n";
+       print "<tr> <td $cb>\n";
+       foreach $a (@attach) {
+               push(@titles, "<input type=checkbox name=forward value=$a->{'idx'} checked> ".($a->{'filename'} ? $a->{'filename'} : $a->{'type'}));
+               push(@links, "detach.cgi?idx=$in{'idx'}&folder=$in{'folder'}&attach=$a->{'idx'}$subs");
+               push(@icons, "images/boxes.gif");
+               }
+       &icons_table(\@links, \@titles, \@icons, 8);
+       print "</td></tr></table><p>\n";
+       }
+
+# Display forwarded mails
+if (@mailforward) {
+       print "<table width=100% border=1>\n";
+       print "<tr> <td $tb><b>$text{'reply_mailforward'}</b></td> </tr>\n";
+       print "<tr> <td $cb>\n";
+       foreach $f (@mailforward) {
+               push(@titles, &simplify_subject($mails[$f]->{'header'}->{'subject'}));
+               push(@links, "view_mail.cgi?idx=$f&folder=$in{'folder'}&user=$in{'user'}");
+               push(@icons, "images/boxes.gif");
+               print "<input type=hidden name=mailforward value=$f>\n";
+               }
+       &icons_table(\@links, \@titles, \@icons, 8);
+       print "</td></tr></table><p>\n";
+       }
+
+# Add form for more attachments
+print "<table width=100% border=1>\n";
+print "<tr $tb> <td colspan=3><b>$text{'reply_attach2'}</b></td> </tr>\n";
+
+print "<tr $cb> <td><input type=file size=20 name=attach0></td>\n";
+print "<td><input type=file size=20 name=attach1></td>\n";
+print "<td><input type=file size=20 name=attach2></td> </tr>\n";
+
+print "<tr $cb> <td><input type=file size=20 name=attach3></td>\n";
+print "<td><input type=file size=20 name=attach4></td>\n";
+print "<td><input type=file size=20 name=attach5></td> </tr>\n";
+
+if ($access{'canattach'}) {
+       print "<tr $cb> <td><input name=file0 size=20> ",
+               &file_chooser_button("file0"),"</td>\n";
+       print "<td><input name=file1 size=20> ",
+               &file_chooser_button("file1"),"</td>\n";
+       print "<td><input name=file2 size=20> ",
+               &file_chooser_button("file2"),"</td> </tr>\n";
+       }
+
+print "</table><p>\n";
+print "<input type=submit value=\"$text{'reply_send'}\">\n";
+print "</form>\n";
+
+&mail_page_footer("list_mail.cgi?folder=$in{'folder'}&user=$in{'user'}",
+       $text{'mail_return'},
+       "", $text{'index_return'});
+
+sub decode_and_sub
+{
+return if (!$mail);
+&parse_mail($mail);
+@sub = split(/\0/, $in{'sub'});
+$subs = join("", map { "&sub=$_" } @sub);
+foreach $s (@sub) {
+       # We are looking at a mail within a mail ..
+       local $amail = &extract_mail(
+                       $mail->{'attach'}->[$s]->{'data'});
+       &parse_mail($amail);
+       $mail = $amail;
+       }
+}
+
diff --git a/mailboxes/search_form.cgi b/mailboxes/search_form.cgi
new file mode 100755 (executable)
index 0000000..7b48335
--- /dev/null
@@ -0,0 +1,54 @@
+#!/usr/local/bin/perl
+# search_form.cgi
+# Display a form for searching a mailbox
+
+require './mailboxes-lib.pl';
+&ReadParse();
+&can_user($in{'user'}) || &error($text{'mail_ecannot'});
+
+@folders = &list_user_folders_sorted($in{'user'});
+($folder) = grep { $_->{'index'} == $in{'folder'} } @folders;
+&ui_print_header(undef, $text{'sform_title'}, "", undef, 0, 0, undef,
+       &folder_link($in{'user'}, $folder));
+
+print "<form action=mail_search.cgi>\n";
+print "<input type=hidden name=user value='$in{'user'}'>\n";
+print "<input type=hidden name=ofolder value='$in{'folder'}'>\n";
+print "<input type=radio name=and value=1 checked> $text{'sform_and'}\n";
+print "<input type=radio name=and value=0> $text{'sform_or'}<p>\n";
+
+print "<table>\n";
+#print "<tr> <td><b>$text{'sform_field'}</b></td> ",
+#      "<td><b>$text{'sform_mode'}</b></td> ",
+#      "<td><b>$text{'sform_for'}</b></td> </tr>\n";
+for($i=0; $i<=9; $i++) {
+       print "<tr>\n";
+       print "<td>$text{'sform_where'}</td>\n";
+       print "<td><select name=field_$i>\n";
+       print "<option value=''>&nbsp;\n";
+       foreach $f ('from', 'subject', 'to', 'cc', 'date', 'body', 'headers', 'size') {
+               print "<option value=$f>",$text{"sform_$f"},"\n";
+               }
+       print "</select></td>\n";
+
+       print "<td><select name=neg_$i>\n";
+       print "<option value=0 checked>$text{'sform_neg0'}\n";
+       print "<option value=1>$text{'sform_neg1'}\n";
+       print "</select></td>\n";
+
+       print "<td>$text{'sform_text'}</td>\n";
+       print "<td><input name=what_$i size=30></td>\n";
+       print "</tr>\n";
+       }
+print "</table><br>\n";
+
+$extra = "<option value=-1>$text{'sform_all'}\n";
+print "<input type=submit value='$text{'sform_ok'}'>\n";
+print " $text{'sform_folder'} ",&folder_select(\@folders, $folder, "folder",
+                                              $extra);
+print "</form>\n";
+
+&ui_print_footer("list_mail.cgi?folder=$in{'folder'}&user=".
+                 &urlize($in{'user'}), $text{'mail_return'},
+                "", $text{'index_return'});
+
diff --git a/mailboxes/send_mail.cgi b/mailboxes/send_mail.cgi
new file mode 100755 (executable)
index 0000000..140d1e8
--- /dev/null
@@ -0,0 +1,288 @@
+#!/usr/local/bin/perl
+# send_mail.cgi
+# Send off an email message
+
+require './mailboxes-lib.pl';
+&ReadParseMime();
+&can_user($in{'user'}) || &error($text{'mail_ecannot'});
+@uinfo = &get_mail_user($in{'user'});
+@uinfo || &error($text{'view_eugone'});
+
+# Check inputs
+@folders = &list_user_folders($in{'user'});
+$folder = $folders[$in{'folder'}];
+&error_setup($text{'send_err'});
+$in{'to'} || &error($text{'send_eto'});
+if ($access{'fmode'} == 0) {
+       # Any from address allowed
+       $in{'from'} || &error($text{'send_efrom'});
+       }
+elsif ($access{'fmode'} == 1) {
+       # From address must be in an allowed domain, and match username
+       $validfrom = &get_user_from_address(\@uinfo);
+       foreach $f (split(/\s+/, $access{'from'})) {
+               $found++ if ("$in{'user'}\@$f" eq $in{'from'} ||
+                            "$in{'ouser'}\@$f" eq $in{'from'} ||
+                            $validfrom eq $in{'from'});
+               }
+       &error($text{'send_efrom'}) if (!$found);
+       }
+elsif ($access{'fmode'} == 2) {
+       # From address must be in allowed list
+       foreach $f (split(/\s+/, $access{'from'})) {
+               $found++ if ($f eq $in{'from'});
+               }
+       &error($text{'send_efrom'}) if (!$found);
+       }
+elsif ($access{'fmode'} == 3) {
+       $in{'from'} .= "\@$access{'from'}";
+       }
+if ($in{'from'} =~ /^(\S+)\@(\S+)$/ && $access{'fromname'}) {
+       $in{'from'} = "$access{'fromname'} <$in{'from'}>";
+       }
+@sub = split(/\0/, $in{'sub'});
+$subs = join("", map { "&sub=$_" } @sub);
+
+# Construct the email
+$in{'from'} || &error($text{'send_efrom'});
+$mail->{'headers'} = [ [ 'From', $in{'from'} ],
+                      [ 'Subject', $in{'subject'} ],
+                      [ 'To', $in{'to'} ],
+                      [ 'Cc', $in{'cc'} ],
+                      [ 'Bcc', $in{'bcc'} ],
+                      [ 'X-Originating-IP', $ENV{'REMOTE_ADDR'} ],
+                      [ 'X-Mailer', "Webmin ".&get_webmin_version() ] ];
+push(@{$mail->{'headers'}}, [ 'X-Priority', $in{'pri'} ]) if ($in{'pri'});
+$in{'body'} =~ s/\r//g;
+if ($in{'body'} =~ /\S/) {
+       if ($in{'spell'}) {
+               pipe(INr, INw);
+               pipe(OUTr, OUTw);
+               select(INw); $| = 1; select(OUTr); $| = 1; select(STDOUT);
+               if (!fork()) {
+                       close(INw);
+                       close(OUTr);
+                       untie(*STDIN);
+                       untie(*STDOUT);
+                       untie(*STDERR);
+                       open(STDOUT, ">&OUTw");
+                       open(STDERR, ">/dev/null");
+                       open(STDIN, "<&INr");
+                       exec("ispell -a");
+                       exit;
+                       }
+               close(INr);
+               close(OUTw);
+               local $indent = "&nbsp;" x 4;
+               local @errs;
+               foreach $line (split(/\n+/, $in{'body'})) {
+                       next if ($line !~ /\S/);
+                       print INw $line,"\n";
+                       local @lerrs;
+                       while(1) {
+                               ($spell = <OUTr>) =~ s/\r|\n//g;
+                               last if (!$spell);
+                               if ($spell =~ /^#\s+(\S+)/) {
+                                       # Totally unknown word
+                                       push(@lerrs, $indent.&text('send_eword', "<i>".&html_escape($1)."</i>"));
+                                       }
+                               elsif ($spell =~ /^&\s+(\S+)\s+(\d+)\s+(\d+):\s+(.*)/) {
+                                       # Maybe possible word, with options
+                                       push(@lerrs, $indent.&text('send_eword2', "<i>".&html_escape($1)."</i>", "<i>".&html_escape($4)."</i>"));
+                                       }
+                               elsif ($spell =~ /^\?\s+(\S+)/) {
+                                       # Maybe possible word
+                                       push(@lerrs, $indent.&text('send_eword', "<i>".&html_escape($1)."</i>"));
+                                       }
+                               }
+                       if (@lerrs) {
+                               push(@errs, &text('send_eline', "<tt>".&html_escape($line)."</tt>")."<br>".join("<br>", @lerrs)."<p>\n");
+                               }
+                       }
+               close(INw);
+               close(OUTr);
+               if (@errs) {
+                       # Spelling errors found!
+                       &mail_page_header($text{'compose_title'}, undef, undef,
+                                         &folder_link($in{'user'}, $folder));
+                       print "<b>$text{'send_espell'}</b><p>\n";
+                       print @errs;
+                       &mail_page_footer("index.cgi?user=$in{'user'}&folder=$in{'folder'}",
+                               $text{'mail_return'});
+                       exit;
+                       }
+               }
+       local $mt = $in{'html_edit'} ? "text/html" : "text/plain";
+       if ($in{'body'} =~ /[\177-\377]/) {
+               # Contains 8-bit characters .. need to make quoted-printable
+               $quoted_printable++;
+               @attach = ( { 'headers' => [ [ 'Content-Type', $mt ],
+                                            [ 'Content-Transfer-Encoding',
+                                              'quoted-printable' ] ],
+                             'data' => quoted_encode($in{'body'}) } );
+               }
+       else {
+               # Plain 7-bit ascii text
+               @attach = ( { 'headers' => [ [ 'Content-Type', $mt ],
+                                            [ 'Content-Transfer-Encoding',
+                                              '7bit' ] ],
+                             'data' => $in{'body'} } );
+               }
+       $bodyattach = $attach[0];
+       }
+$attachsize = 0;
+foreach $i (0 .. 5) {
+       # Add uploaded attachment
+       next if (!$in{"attach$i"});
+       &test_max_attach($attachsize);
+       local $filename = $in{"attach${i}_filename"};
+       $filename =~ s/^.*(\\|\/)//;
+       local $type = $in{"attach${i}_content_type"}."; name=\"".
+                     $filename."\"";
+       local $disp = "inline; filename=\"".$filename."\"";
+       push(@attach, { 'data' => $in{"attach${i}"},
+                       'headers' => [ [ 'Content-type', $type ],
+                                      [ 'Content-Disposition', $disp ],
+                                      [ 'Content-Transfer-Encoding',
+                                        'base64' ] ] });
+       $atotal += length($in{"attach${i}"});
+       }
+foreach $i (0 .. 2) {
+       # Add server-side attachment
+       next if (!$in{"file$i"} || !$access{'canattach'});
+       @uinfo = &get_mail_user($in{'user'});
+       @uinfo || &error($text{'view_eugone'});
+       if ($in{"file$i"} !~ /^\//) {
+               $in{"file$i"} = $uinfo[7]."/".$in{"file$i"};
+               }
+
+       local @st = stat($in{"file$i"});
+       &test_max_attach($st[7]);
+       local $data;
+       &switch_to_user($in{'user'});
+       open(DATA, $in{"file$i"}) || &error(&text('send_efile', $in{"file$i"}, $!));
+       while(<DATA>) {
+               $data .= $_;
+               }
+       close(DATA);
+       &switch_user_back();
+       $in{"file$i"} =~ s/^.*\///;
+       local $type = &guess_mime_type($in{"file$i"}).
+                     "; name=\"".$in{"file$i"}."\"";
+       local $disp = "inline; filename=\"".$in{"file$i"}."\"";
+       push(@attach, { 'data' => $data,
+                       'headers' => [ [ 'Content-type', $type ],
+                                      [ 'Content-Disposition', $disp ],
+                                      [ 'Content-Transfer-Encoding',
+                                        'base64' ] ] });
+       $atotal += length($data);
+       }
+@fwd = split(/\0/, $in{'forward'});
+if (@fwd) {
+       # Add forwarded attachments
+       @mail = &mailbox_list_mails($in{'idx'}, $in{'idx'}, $folder);
+       $fwdmail = $mail[$in{'idx'}];
+       &parse_mail($fwdmail);
+
+       foreach $s (@sub) {
+               # We are looking at a mail within a mail ..
+               local $amail = &extract_mail($fwdmail->{'attach'}->[$s]->{'data'});
+               &parse_mail($amail);
+               $fwdmail = $amail;
+               }
+
+       foreach $f (@fwd) {
+               &test_max_attach(length($fwdmail->{'attach'}->[$f]->{'data'}));
+               push(@attach, $fwdmail->{'attach'}->[$f]);
+               $atotal += length($fwdmail->{'attach'}->[$f]->{'data'});
+               }
+       }
+@mailfwd = split(/\0/, $in{'mailforward'});
+if (@mailfwd) {
+       # Add forwarded emails
+       @mail = &mailbox_list_mails($mailfwd[0], $mailfwd[@mailfwd-1], $folder);
+       foreach $f (@mailfwd) {
+               $fwdmail = $mail[$f];
+               local $headertext;
+               foreach $h (@{$fwdmail->{'headers'}}) {
+                       $headertext .= $h->[0].": ".$h->[1]."\n";
+                       }
+               push(@attach, { 'data' => $headertext."\n".$fwdmail->{'body'},
+                               'headers' => [ [ 'Content-type', 'message/rfc822' ],
+                                              [ 'Content-Description',
+                                                 $fwdmail->{'header'}->{'subject'} ] ]
+                             });
+               }
+       }
+$mail->{'attach'} = \@attach;
+if ($access{'attach'} >= 0 && $atotal > $access{'attach'}*1024) {
+       &error(&text('send_eattach', $access{'attach'}));
+       }
+
+# Check for text-only email
+$textonly = $config{'no_mime'} && !$quoted_printable &&
+           @{$mail->{'attach'}} == 1 &&
+           $mail->{'attach'}->[0] eq $bodyattach &&
+           !$in{'html_edit'};
+
+# Send it off
+&send_mail($mail, undef, $textonly, $config{'no_crlf'});
+&webmin_log("send", undef, undef, { 'from' => $in{'from'}, 'to' => $in{'to'} });
+
+# Tell the user that email as sent
+&mail_page_header($text{'send_title'}, undef, undef,
+                 &folder_link($in{'user'}, $folder));
+
+@tos = ( split(/,/, $in{'to'}), split(/,/, $in{'cc'}), split(/,/, $in{'bcc'}) );
+$tos = join(" , ", map { "<tt>".&html_escape($_)."</tt>" } @tos);
+print "<p>",&text($in{'draft'} ? 'send_draft' : 'send_ok', $tos),"<p>\n";
+
+if ($in{'idx'} ne '') {
+       &mail_page_footer("view_mail.cgi?idx=$in{'idx'}&folder=$in{'folder'}&user=$in{'user'}$subs",
+               $text{'view_return'},
+               "list_mail.cgi?folder=$in{'folder'}&user=$in{'user'}",
+               $text{'mail_return'},
+               "", $text{'index_return'});
+       }
+else {
+       &mail_page_footer("list_mail.cgi?folder=$in{'folder'}&user=$in{'user'}",
+               $text{'mail_return'},
+               "", $text{'index_return'});
+       }
+
+# write_attachment(&attach)
+sub write_attachment
+{
+local ($a) = @_;
+local ($enc, $rv);
+foreach $h (@{$a->{'headers'}}) {
+       $rv .= $h->[0].": ".$h->[1]."\r\n";
+       $enc = $h->[1]
+           if (lc($h->[0]) eq 'content-transfer-encoding');
+       }
+$rv .= "\r\n";
+if (lc($enc) eq 'base64') {
+       local $encoded = &encode_base64($a->{'data'});
+       $encoded =~ s/\r//g;
+       $encoded =~ s/\n/\r\n/g;
+       $rv .= $encoded;
+       }
+else {
+       $a->{'data'} =~ s/\r//g;
+       $a->{'data'} =~ s/\n/\r\n/g;
+       $rv .= $a->{'data'};
+       if ($a->{'data'} !~ /\n$/) {
+               $rv .= "\r\n";
+               }
+       }
+return $rv;
+}
+
+sub test_max_attach
+{
+$attachsize += $_[0];
+if ($access{'attach'} >= 0 && $attachsize > $access{'attach'}) {
+       &error(&text('send_eattachsize', $access{'attach'}));
+       }
+}
+
diff --git a/mailboxes/useradmin_update.pl b/mailboxes/useradmin_update.pl
new file mode 100644 (file)
index 0000000..e1a876f
--- /dev/null
@@ -0,0 +1,94 @@
+
+do 'mailboxes-lib.pl';
+
+# useradmin_create_user(&details)
+# Create a new empty mail file
+sub useradmin_create_user
+{
+if ($config{'sync_create'}) {
+       local ($dir, $style, $mailbox, $maildir) = &get_mail_style();
+       if ($dir && -d $dir) {
+               # Create mail file like /var/mail/USERNAME
+               local $mf = &mail_file_style($_[0]->{'user'}, $dir, $style);
+               if (!-e $mf) {
+                       &create_mail_file($_[0], $mf);
+                       }
+               }
+       if ($mailbox && !-e "$_[0]->{'home'}/$mailbox") {
+               # Create mail file ~USERNAME/Mailbox
+               &create_mail_file($_[0], "$_[0]->{'home'}/$mailbox");
+               }
+       if ($maildir && !-e "$_[0]->{'home'}/$maildir") {
+               # Create mail directory like ~USERNAME/Maildir
+               &create_mail_dir($_[0], "$_[0]->{'home'}/$maildir");
+               }
+       }
+}
+
+# create_mail_file(&user, file)
+sub create_mail_file
+{
+open(TOUCH, ">$_[1]");
+close(TOUCH);
+if ($config{'sync_perms'}) {
+       system("chmod ".
+              quotemeta($config{'sync_perms'})." ".
+              quotemeta($_[1]));
+       }
+chown($_[0]->{'uid'}, $_[0]->{'gid'}, $_[1]);
+}
+
+# create_mail_dir(&user, dir)
+sub create_mail_dir
+{
+local $d;
+foreach $d ($_[1], "$_[0]/cur", "$_[1]/tmp", "$_[1]/new") {
+       mkdir($d, 0700);
+       if ($config{'sync_perms'}) {
+               system("chmod ".
+                      quotemeta($config{'sync_perms'})." ".
+                      quotemeta($d));
+               }
+       chown($_[0]->{'uid'}, $_[0]->{'gid'}, $d);
+       }
+}
+
+
+
+# useradmin_delete_user(&details)
+# Delete the user's mail file
+sub useradmin_delete_user
+{
+if ($config{'sync_delete'}) {
+       local ($dir, $style, $mailbox, $maildir) = &get_mail_style();
+       if ($dir && -d $dir) {
+               local $mf = &mail_file_style($_[0]->{'user'}, $dir, $style);
+               unlink($mf);
+               unlink($mf.".pop");
+               }
+       }
+}
+
+# useradmin_modify_user(&details, &old)
+# Rename the user's mail file if necessary, and change it's UID
+sub useradmin_modify_user
+{
+if ($config{'sync_modify'} &&
+    ($_[0]->{'user'} ne $_[1]->{'user'} || $_[0]->{'uid'} != $_[1]->{'uid'})) {
+       local ($dir, $style, $mailbox, $maildir) = &get_mail_style();
+       if ($dir && -d $dir) {
+               local $omf = &mail_file_style($_[0]->{'olduser'}, $dir, $style);
+               local $nmf = &mail_file_style($_[0]->{'user'}, $dir, $style);
+               local @st = stat($omf);
+               if ($st[4] != $_[0]->{'uid'}) {
+                       chown($_[0]->{'uid'}, $st[5], $omf);
+                       }
+               if ($omf ne $nmf && -e $omf) {
+                       &rename_logged($omf, $nmf);
+                       }
+               }
+       }
+}
+
+1;
+
diff --git a/mailboxes/view_mail.cgi b/mailboxes/view_mail.cgi
new file mode 100755 (executable)
index 0000000..ff83e14
--- /dev/null
@@ -0,0 +1,367 @@
+#!/usr/local/bin/perl
+# view_mail.cgi
+# View a single email message 
+
+require './mailboxes-lib.pl';
+$force_charset = '';
+&ReadParse();
+&can_user($in{'user'}) || &error($text{'mail_ecannot'});
+if (&is_user($in{'user'})) {
+       @uinfo = &get_mail_user($in{'user'});
+       @uinfo || &error($text{'view_eugone'});
+       }
+
+$uuser = &urlize($in{'user'});
+@folders = &list_user_folders($in{'user'});
+$folder = $folders[$in{'folder'}];
+@mail = &mailbox_list_mails($in{'idx'}, $in{'idx'}, $folder);
+$mail = $mail[$in{'idx'}];
+&parse_mail($mail, undef, $in{'raw'});
+@sub = split(/\0/, $in{'sub'});
+$subs = join("", map { "&sub=$_" } @sub);
+foreach $s (@sub) {
+        # We are looking at a mail within a mail ..
+        local $amail = &extract_mail($mail->{'attach'}->[$s]->{'data'});
+        &parse_mail($amail, undef, $in{'raw'});
+        $mail = $amail;
+        }
+
+dbmopen(%read, "$module_config_directory/$in{'user'}.read", 0600);
+eval { $read{$mail->{'header'}->{'message-id'}} = 1 }
+       if (!$read{$mail->{'header'}->{'message-id'}});
+
+if ($in{'raw'}) {
+       # Special mode - viewing whole raw message
+       print "Content-type: text/plain\n\n";
+       if ($mail->{'fromline'}) {
+               print $mail->{'fromline'},"\n";
+               }
+       if (defined($mail->{'rawheaders'})) {
+               #$mail->{'rawheaders'} =~ s/(\S)\t/$1\n\t/g;
+               print $mail->{'rawheaders'};
+               }
+       else {
+               foreach $h (@{$mail->{'headers'}}) {
+                       #$h->[1] =~ s/(\S)\t/$1\n\t/g;
+                       print "$h->[0]: $h->[1]\n";
+                       }
+               }
+       print "\n";
+       print $mail->{'body'};
+       exit;
+       }
+
+# Find body attachment and type
+($textbody, $htmlbody, $body) = &find_body($mail, $config{'view_html'});
+$body = $htmlbody if ($in{'body'} == 2);
+$body = $textbody if ($in{'body'} == 1);
+@attach = @{$mail->{'attach'}};
+
+# Show pre-body HTML
+if ($body && $body eq $htmlbody) {
+       $headstuff = &head_html($body->{'data'});
+       }
+
+&mail_page_header($text{'view_title'}, $headstuff, undef,
+                 &folder_link($in{'user'}, $folder));
+print &check_clicks_function();
+&show_arrows();
+
+print "<form action=reply_mail.cgi>\n";
+print "<input type=hidden name=user value='$in{'user'}'>\n";
+print "<input type=hidden name=idx value='$in{'idx'}'>\n";
+print "<input type=hidden name=folder value='$in{'folder'}'>\n";
+print "<input type=hidden name=mod value=",&modification_time($folder),">\n";
+foreach $s (@sub) {
+       print "<input type=hidden name=sub value='$s'>\n";
+       }
+
+# Find any delivery status attachment
+($dstatus) = grep { $_->{'type'} eq 'message/delivery-status' } @attach;
+
+# Strip out attachments not to display as icons
+@attach = grep { $_ ne $body && $_ ne $dstatus } @attach;
+@attach = grep { !$_->{'attach'} } @attach;
+
+if ($config{'top_buttons'} == 2 && &editable_mail($mail)) {
+       &show_mail_buttons(1, scalar(@sub));
+       print "<p>\n";
+       }
+
+print "<table width=100% border=1>\n";
+print "<tr> <td $tb><table width=100% cellpadding=0 cellspacing=0><tr>",
+      "<td><b>$text{'view_headers'}</b></td> <td align=right>\n";
+if ($in{'headers'}) {
+       print "<a href='view_mail.cgi?idx=$in{'idx'}&body=$in{'body'}&folder=$in{'folder'}&user=$uuser&headers=0$subs'>$text{'view_noheaders'}</a>\n";
+       }
+else {
+       print "<a href='view_mail.cgi?idx=$in{'idx'}&body=$in{'body'}&folder=$in{'folder'}&user=$uuser&headers=1$subs'>$text{'view_allheaders'}</a>\n";
+       }
+print "&nbsp;&nbsp;<a href='view_mail.cgi?idx=$in{'idx'}&folder=$in{'folder'}&user=$uuser&raw=1$subs'>$text{'view_raw'}</a></td>\n";
+print "</tr></table></td> </tr>\n";
+
+print "<tr> <td $cb><table width=100%>\n";
+if ($in{'headers'}) {
+       # Show all the headers
+       if ($mail->{'fromline'}) {
+               print "<tr> <td><b>$text{'mail_rfc'}</b></td>",
+                     "<td>",&eucconv_and_escape($mail->{'fromline'}),
+                     "</td> </tr>\n";
+               }
+       foreach $h (@{$mail->{'headers'}}) {
+               print "<tr> <td><b>$h->[0]:</b></td> ",
+                     "<td>",&eucconv_and_escape(&decode_mimewords($h->[1])),
+                     "</td> </tr>\n";
+               }
+       }
+else {
+       # Just show the most useful headers
+       print "<tr> <td><b>$text{'mail_from'}</b></td> ",
+             "<td>",&address_link($mail->{'header'}->{'from'}),"</td> </tr>\n";
+       print "<tr> <td><b>$text{'mail_to'}</b></td> ",
+             "<td>",&address_link($mail->{'header'}->{'to'}),"</td> </tr>\n";
+       print "<tr> <td><b>$text{'mail_cc'}</b></td> ",
+             "<td>",&address_link($mail->{'header'}->{'cc'}),"</td> </tr>\n"
+               if ($mail->{'header'}->{'cc'});
+       print "<tr> <td><b>$text{'mail_date'}</b></td> ",
+             "<td>",&eucconv_and_escape($mail->{'header'}->{'date'}),
+             "</td> </tr>\n";
+       print "<tr> <td><b>$text{'mail_subject'}</b></td> ",
+             "<td>",&eucconv_and_escape(&decode_mimewords(
+                       $mail->{'header'}->{'subject'})),"</td> </tr>\n";
+       }
+print "</table></td></tr></table><p>\n";
+
+# Show body attachment, with properly linked URLs
+if ($body && $body->{'data'} =~ /\S/) {
+       if ($body eq $textbody) {
+               # Show plain text
+               $bodycontents = "<pre>";
+               foreach $l (&wrap_lines(&eucconv($body->{'data'}),
+                                       $config{'wrap_width'})) {
+                       $bodycontents .= &link_urls_and_escape($l,
+                                               $config{'link_mode'})."\n";
+                       }
+               $bodycontents .= "</pre>";
+               if ($htmlbody) {
+                       $bodyright = "<a href='view_mail.cgi?idx=$in{'idx'}&headers=$in{'headers'}&folder=$in{'folder'}&user=$uuser&body=2$subs'>$text{'view_ashtml'}</a>";
+                       }
+               }
+       elsif ($body eq $htmlbody) {
+               # Attempt to show HTML
+               $bodycontents = &safe_html($body->{'data'});
+               $bodycontents = &fix_cids($bodycontents, \@attach,
+                       "detach.cgi?user=$uuser&idx=$in{'idx'}&folder=$in{'folder'}$subs");
+               if ($textbody) {
+                       $bodyright = "<a href='view_mail.cgi?idx=$in{'idx'}&headers=$in{'headers'}&folder=$in{'folder'}&user=$uuser&body=1$subs'>$text{'view_astext'}</a>";
+                       }
+               }
+       }
+if ($bodycontents) {
+       print "<table width=100% border=1>\n";
+       print "<tr $tb> <td><table width=100% cellpadding=0 cellspacing=0><tr>",
+             "<td><b>$text{'view_body'}</b></td> ",
+             "<td align=right>$bodyright</td> </tr></table></td> </tr>\n";
+       print "<tr $cb> <td>\n";
+       print $bodycontents;
+       print "</pre></td></tr></table><p>\n";
+       }
+
+# Show delivery status
+if ($dstatus) {
+       print "<table width=100% border=1>\n";
+       print "<tr> <td $tb><b>$text{'view_dstatus'}</b></td> </tr>\n";
+       print "<tr> <td $cb><table>\n";
+
+       local $ds = &parse_delivery_status($dstatus->{'data'});
+       foreach $dsh ('final-recipient', 'diagnostic-code',
+                     'remote-mta', 'reporting-mta') {
+               if ($ds->{$dsh}) {
+                       $ds->{$dsh} =~ s/^\S+;//;
+                       print "<tr> <td nowrap valign=top><b>",
+                             $text{'view_'.$dsh},"</b></td>\n";
+                       print "<td>",&html_escape($ds->{$dsh}),"</td> </tr>\n";
+                       }
+               }
+
+       print "</table></td></tr></table><p>\n";
+       }
+
+# Display other attachments
+if (@attach) {
+       print "<table width=100% border=1>\n";
+       print "<tr> <td $tb><b>$text{'view_attach'}</b></td> </tr>\n";
+       print "<tr> <td $cb>\n";
+       foreach $a (@attach) {
+               local $fn;
+               $size = (int(length($a->{'data'})/1000)+1)." Kb";
+               local $cb;
+               if ($a->{'type'} eq 'message/rfc822') {
+                       push(@titles, "$text{'view_sub'}<br>$size");
+                       }
+               elsif ($a->{'filename'}) {
+                       push(@titles, &decode_mimewords($a->{'filename'}).
+                                     "<br>$size");
+                       $fn = &decode_mimewords($a->{'filename'});
+                       push(@detach, [ $a->{'idx'}, $fn ]);
+                       }
+               else {
+                       push(@titles, "$a->{'type'}<br>$size");
+                       $a->{'type'} =~ /\/(\S+)$/;
+                       $fn = "file.$1";
+                       push(@detach, [ $a->{'idx'}, $fn ]);
+                       }
+               if ($a->{'error'}) {
+                       $titles[$#titles] .= "<br><font size=-1>($a->{'error'})</font>";
+                       }
+               $fn =~ s/ /_/g;
+               $fn =~ s/\#/_/g;
+               $fn = &html_escape($fn);
+               if ($a->{'type'} eq 'message/rfc822') {
+                       push(@links, "view_mail.cgi?idx=$in{'idx'}&folder=$in{'folder'}&user=$uuser$subs&sub=$a->{'idx'}");
+                       }
+               else {
+                       push(@links, "detach.cgi/$fn?idx=$in{'idx'}&folder=$in{'folder'}&user=$uuser&attach=$a->{'idx'}$subs");
+                       }
+               if ($config{'thumbnails'} &&
+                   ($a->{'type'} =~ /image\/gif/i && &has_command("giftopnm")&&
+                    &has_command("pnmscale") && &has_command("cjpeg") ||
+                    $a->{'type'} =~ /image\/jpeg/i && &has_command("djpeg") &&
+                    &has_command("pnmscale") && &has_command("cjpeg"))) {
+                       # Can show an image icon
+                       push(@icons, "detach.cgi?scale=1&idx=$in{'idx'}&folder=$in{'folder'}&attach=$a->{'idx'}$subs");
+                       $imgicons++;
+                       }
+               else {
+                       push(@icons, "images/boxes.gif");
+                       }
+               }
+       &icons_table(\@links, \@titles, \@icons, 6, undef,
+                    $imgicons ? ( 0, 0 ) : ( ));
+       if ($access{'candetach'} && @detach && defined($uinfo[2])) {
+               print "<input type=submit name=detach value='$text{'view_detach'}'>\n";
+               print "<input type=hidden name=bindex value='$body->{'idx'}'>\n" if ($body);
+               print "<select name=attach>\n";
+               print "<option value=*>$text{'view_dall'}\n";
+               foreach $a (@detach) {
+                       printf "<option value=%s>%s\n",
+                               $a->[0], $a->[1];
+                       }
+               print "</select>\n";
+               print "<b>$text{'view_dir'}</b>\n";
+               print "<input name=dir size=40> ",
+                       &file_chooser_button("dir", 1),"\n";
+               }
+       print "</td></tr></table><p>\n";
+       }
+
+&show_mail_buttons(2, scalar(@sub)) if (&editable_mail($mail));
+if ($config{'arrows'} == 2 && !@sub) {
+        &show_arrows();
+        }
+print "</form>\n";
+
+dbmclose(%read);
+
+local @sr = !@sub ? ( ) :
+    ( "view_mail.cgi?idx=$in{'idx'}", $text{'view_return'} ),
+$s = int((@mail - $in{'idx'} - 1) / $config{'perpage'}) *
+       $config{'perpage'};
+&mail_page_footer(@sub ? ( "view_mail.cgi?idx=$in{'idx'}&folder=$in{'folder'}&user=$uuser",
+                $text{'view_return'} ) : ( ),
+       "list_mail.cgi?folder=$in{'folder'}&user=$uuser", $text{'mail_return'},
+       "", $text{'index_return'});
+
+# show_mail_buttons(pos, submode)
+sub show_mail_buttons
+{
+local $spacer = "&nbsp;\n";
+if (!$_[1]) {
+       print "<input type=submit value=\"$text{'view_delete'}\" name=delete ",
+             "onClick='return check_clicks(form)'>";
+       print $spacer;
+
+       if (!$folder->{'sent'} && !$folder->{'drafts'}) {
+               $m = $read{$mail->{'header'}->{'message-id'}};
+               print "<input name=mark$_[0] type=submit value=\"$text{'view_mark'}\">";
+               print "<select name=mode$_[0]>\n";
+               foreach $i (0 .. 2) {
+                       printf "<option value=%d %s>%s\n",
+                               $i, $m == $i ? 'selected' : '', $text{"view_mark$i"};
+                       }
+               print "</select>";
+               print $spacer;
+               }
+       }
+if (&is_user($in{'user'})) {
+       print "<input type=submit value=\"$text{'view_forward'}\" name=forward>";
+       print $spacer;
+       }
+
+print "<input type=submit value=\"$text{'view_print'}\" name=print>";
+print $spacer;
+
+if (&is_user($in{'user'})) {
+       print "<input type=submit value=\"$text{'view_reply'}\" name=reply>";
+       print "<input type=submit value=\"$text{'view_reply2'}\" name=rall>";
+       print $spacer;
+       }
+
+print "<input type=submit value=\"$text{'view_strip'}\" name=strip>";
+print $spacer;
+
+# Show spam report buttons
+@modules = &get_available_module_infos(1);
+($hasspam) = grep { $_->{'dir'} eq "spam" } @modules;
+if (&foreign_installed("spam") &&
+    $config{'spam_buttons'} =~ /mail/ &&
+    &spam_report_cmd($in{'user'})) {
+       if ($hasspam) {
+               print "<input type=submit value=\"$text{'view_black'}\" name=black>";
+               }
+       if ($config{'spam_del'}) {
+               print "<input type=submit value=\"$text{'view_razordel'}\" name=razor>\n";
+               }
+       else {
+               print "<input type=submit value=\"$text{'view_razor'}\" name=razor>\n";
+               }
+       }
+print "<br>\n";
+}
+
+sub show_arrows
+{
+print "<center>\n";
+if (!@sub) {
+       if ($in{'idx'}) {
+               print "<a href='view_mail.cgi?idx=",
+                     $in{'idx'}-1,"&folder=$in{'folder'}&user=$uuser'>",
+                     "<img src=/images/left.gif border=0 ",
+                     "align=middle></a>\n";
+               }
+       print "<font size=+1>",&text('view_desc', $in{'idx'}+1,
+                       $folder->{'name'}),"</font>\n";
+       if ($in{'idx'} < @mail-1) {
+               print "<a href='view_mail.cgi?idx=",
+                     $in{'idx'}+1,"&folder=$in{'folder'}&user=$uuser'>",
+                     "<img src=/images/right.gif border=0 ",
+                     "align=middle></a>\n";
+               }
+       }
+else {
+       print "<font size=+1>$text{'view_sub'}</font>\n";
+       }
+print "</center><br>\n";
+}
+
+# address_link(address)
+sub address_link
+{
+local @addrs = &split_addresses(&decode_mimewords($_[0]));
+local @rv;
+foreach $a (@addrs) {
+       push(@rv, &eucconv_and_escape($a->[2]));
+       }
+return join(" , ", @rv);
+}
+
diff --git a/mailboxes/xinha/Xinha.css b/mailboxes/xinha/Xinha.css
new file mode 100644 (file)
index 0000000..2b4d7d9
--- /dev/null
@@ -0,0 +1,257 @@
+.htmlarea { background: #fff; }
+
+.htmlarea .toolbar {
+  cursor: default;
+  background: ButtonFace;
+  padding: 3px;
+  border: 1px solid;
+  border-color: ButtonHighlight ButtonShadow ButtonShadow ButtonHighlight;
+}
+.htmlarea .toolbar table { margin: 0; font-family: tahoma,verdana,sans-serif; font-size: 11px; }
+.htmlarea .toolbar img { border: none; vertical-align: top; }
+.htmlarea .toolbar .label { padding: 0px 3px; }
+
+.htmlarea .toolbar .button {
+  background: ButtonFace;
+  color: ButtonText;
+  border: 1px solid ButtonFace;
+  padding: 1px;
+  margin: 0px;
+  width: 18px;
+  height: 18px;
+}
+.htmlarea .toolbar a.button:hover {
+  border: 1px solid;
+  border-color: ButtonHighlight ButtonShadow ButtonShadow ButtonHighlight;
+}
+.htmlarea .toolbar a.buttonDisabled:hover {
+  border-color: ButtonFace;
+}
+.htmlarea .toolbar .buttonActive,
+.htmlarea .toolbar .buttonPressed
+{
+  padding: 2px 0px 0px 2px;
+  border: 1px solid;
+  border-color: ButtonShadow ButtonHighlight ButtonHighlight ButtonShadow;
+}
+.htmlarea .toolbar .buttonPressed {
+  background: ButtonHighlight;
+}
+.htmlarea .toolbar .indicator {
+  padding: 0px 3px;
+  overflow: hidden;
+  width: 20px;
+  text-align: center;
+  cursor: default;
+  border: 1px solid ButtonShadow;
+}
+
+.htmlarea .toolbar .buttonDisabled img {
+  filter: gray() alpha(opacity = 25);
+  -moz-opacity: 0.25;
+  opacity: 0.25;
+}
+
+.htmlarea .toolbar .separator {
+  /*position: relative;*/
+  margin: 3px;
+  border-left: 1px solid ButtonShadow;
+  border-right: 1px solid ButtonHighlight;
+  width: 0px;
+  height: 18px;
+  padding: 0px;
+}
+
+.htmlarea .toolbar .space { width: 5px; }
+
+.htmlarea .toolbar select, .htmlarea .toolbar option { font: 11px Tahoma,Verdana,sans-serif;}
+
+.htmlarea .toolbar select,
+.htmlarea .toolbar select:hover,
+.htmlarea .toolbar select:active { 
+  margin-top: 2px;
+  margin-bottom: 1px;
+  color: ButtonText;
+}
+
+.htmlarea iframe.xinha_iframe, .htmlarea textarea.xinha_textarea
+{
+  border: none; /*1px solid;*/
+}
+
+.htmlarea .statusBar {
+  border: 1px solid;
+  border-color: ButtonShadow ButtonHighlight ButtonHighlight ButtonShadow;
+  padding: 2px 4px;
+  background-color: ButtonFace;
+  color: ButtonText;
+  font: 11px Tahoma,Verdana,sans-serif;
+  height:16px;
+}
+
+.htmlarea .statusBar .statusBarTree a {
+  padding: 2px 5px;
+  color: #00f;
+}
+
+.htmlarea .statusBar .statusBarTree a:visited { color: #00f; }
+.htmlarea .statusBar .statusBarTree a:hover {
+  background-color: Highlight;
+  color: HighlightText;
+  padding: 1px 4px;
+  border: 1px solid HighlightText;
+}
+
+
+/* popup dialogs */
+
+.dialog {
+  color: ButtonText;
+  background: ButtonFace;
+}
+
+.dialog .content { padding: 2px; }
+
+.dialog, .dialog button, .dialog input, .dialog select, .dialog textarea, .dialog table {
+  font: 11px Tahoma,Verdana,sans-serif;
+}
+
+.dialog table { border-collapse: collapse; }
+
+.dialog .title, .dialog h1
+{
+  background: #008;
+  color: #ff8;
+  border-bottom: 1px solid #000;
+  padding: 1px 0px 2px 5px;
+  font-size: 12px;
+  font-weight: bold;
+  cursor: default;
+}
+.dialog h1 { margin:0px;}
+.dialog .title .button {
+  float: right;
+  border: 1px solid #66a;
+  padding: 0px 1px 0px 2px;
+  margin-right: 1px;
+  color: #fff;
+  text-align: center;
+}
+
+.dialog .title .button-hilite { border-color: #88f; background: #44c; }
+
+.dialog button {
+  width: 5em;
+  padding: 0px;
+}
+
+.dialog .buttonColor {
+  padding: 1px;
+  cursor: default;
+  border: 1px solid;
+  border-color: ButtonHighlight ButtonShadow ButtonShadow ButtonHighlight;
+}
+
+.dialog .buttonColor-hilite {
+  border-color: #000;
+}
+
+.dialog .buttonColor .chooser, .dialog .buttonColor .nocolor {
+  height: 0.6em;
+  border: 1px solid;
+  padding: 0px 1em;
+  border-color: ButtonShadow ButtonHighlight ButtonHighlight ButtonShadow;
+}
+
+.dialog .buttonColor .nocolor { padding: 0px; }
+.dialog .buttonColor .nocolor-hilite { background-color: #fff; color: #f00; }
+
+.dialog .label { text-align: right; width: 6em; }
+.dialog .value input { width: 100%; }
+.dialog .buttons { text-align: right; padding: 2px 4px 0px 4px; }
+
+.dialog legend { font-weight: bold; }
+.dialog fieldset table { margin: 2px 0px; }
+
+.popupwin {
+  padding: 0px;
+  margin: 0px;
+}
+
+.popupwin .title {
+  background: #fff;
+  color: #000;
+  font-weight: bold;
+  font-size: 120%;
+  padding: 3px 10px;
+  margin-bottom: 10px;
+  border-bottom: 1px solid black;
+  letter-spacing: 2px;
+}
+
+form { margin: 0px; border: none; }
+
+
+/** Panels **/
+.htmlarea .panels.top
+{
+  border-bottom : 1px solid;
+  border-color: ButtonShadow;
+}
+
+.htmlarea .panels.right
+{
+  border-left : 1px solid;
+  border-color: ButtonShadow;
+}
+
+.htmlarea .panels.left
+{
+  border-right : 1px solid;
+  border-color: ButtonShadow;
+}
+
+.htmlarea .panels.bottom
+{
+  border-top : 1px solid;
+  border-color: ButtonShadow;
+}
+
+.htmlarea .panel h1 {
+  background: ButtonFace;
+  border: 1px solid;
+  border-color: ButtonHighlight ButtonShadow ButtonShadow ButtonHighlight;
+  margin:   0px;
+  padding:  0px;
+  font-size:100%;
+  font-weight:bold;
+  padding: 2px;
+  clear:left;
+  
+}
+
+.htmlarea .panel { overflow:auto; }
+.htmlarea .panels.left  .panel { border-right:none; border-left:none; }
+.htmlarea .panels.left  h1     { border-right:none;  }
+.htmlarea .panels.right .panel { border-right:none; border-left:none; }
+.htmlarea .panels.left  h1     { border-left:none;  }
+.htmlarea { border: 1px solid black; }
+
+.loading
+{
+  background-color:#666;
+  position:absolute;
+  z-index:998;
+}
+.loading_main
+{
+  font-size:1.6em;
+  color:#ff6;
+  text-align:center;
+}
+.loading_sub
+{
+  font-size:1.0em;
+  color:#fff;
+  text-align:center;
+}
diff --git a/mailboxes/xinha/XinhaCore.js b/mailboxes/xinha/XinhaCore.js
new file mode 100644 (file)
index 0000000..ef37adf
--- /dev/null
@@ -0,0 +1,3473 @@
+Xinha.version={"Release":"Trunk","Head":"$HeadURL: http://svn.xinha.python-hosting.com/tags/0.92beta/XinhaCore.js $".replace(/^[^:]*: (.*) \$$/,"$1"),"Date":"$LastChangedDate: 2007-02-22 02:11:56 +0100 (Do, 22 Feb 2007) $".replace(/^[^:]*: ([0-9-]*) ([0-9:]*) ([+0-9]*) \((.*)\) \$/,"$4 $2 $3"),"Revision":"$LastChangedRevision: 757 $".replace(/^[^:]*: (.*) \$$/,"$1"),"RevisionBy":"$LastChangedBy: ray $".replace(/^[^:]*: (.*) \$$/,"$1")};
+Xinha._resolveRelativeUrl=function(_1,_2){
+if(_2.match(/^([^:]+\:)?\//)){
+return _2;
+}else{
+var b=_1.split("/");
+if(b[b.length-1]==""){
+b.pop();
+}
+var p=_2.split("/");
+if(p[0]=="."){
+p.shift();
+}
+while(p[0]==".."){
+b.pop();
+p.shift();
+}
+return b.join("/")+"/"+p.join("/");
+}
+};
+if(typeof _editor_url=="string"){
+_editor_url=_editor_url.replace(/\x2f*$/,"/");
+if(!_editor_url.match(/^([^:]+\:)?\//)){
+var path=window.location.toString().split("/");
+path.pop();
+_editor_url=Xinha._resolveRelativeUrl(path.join("/"),_editor_url);
+}
+}else{
+alert("WARNING: _editor_url is not set!  You should set this variable to the editor files path; it should preferably be an absolute path, like in '/htmlarea/', but it can be relative if you prefer.  Further we will try to load the editor files correctly but we'll probably fail.");
+_editor_url="";
+}
+if(typeof _editor_lang=="string"){
+_editor_lang=_editor_lang.toLowerCase();
+}else{
+_editor_lang="en";
+}
+if(typeof _editor_skin!=="string"){
+_editor_skin="";
+}
+var __xinhas=[];
+Xinha.agt=navigator.userAgent.toLowerCase();
+Xinha.is_ie=((Xinha.agt.indexOf("msie")!=-1)&&(Xinha.agt.indexOf("opera")==-1));
+Xinha.ie_version=parseFloat(Xinha.agt.substring(Xinha.agt.indexOf("msie")+5));
+Xinha.is_opera=(Xinha.agt.indexOf("opera")!=-1);
+Xinha.is_mac=(Xinha.agt.indexOf("mac")!=-1);
+Xinha.is_mac_ie=(Xinha.is_ie&&Xinha.is_mac);
+Xinha.is_win_ie=(Xinha.is_ie&&!Xinha.is_mac);
+Xinha.is_gecko=(navigator.product=="Gecko");
+Xinha.isRunLocally=document.URL.toLowerCase().search(/^file:/)!=-1;
+if(Xinha.isRunLocally){
+alert("Xinha *must* be installed on a web server. Locally opened files (those that use the \"file://\" protocol) cannot properly function. Xinha will try to initialize but may not be correctly loaded.");
+}
+function Xinha(_5,_6){
+if(!_5){
+throw ("Tried to create Xinha without textarea specified.");
+}
+if(Xinha.checkSupportedBrowser()){
+if(typeof _6=="undefined"){
+this.config=new Xinha.Config();
+}else{
+this.config=_6;
+}
+this._htmlArea=null;
+if(typeof _5!="object"){
+_5=Xinha.getElementById("textarea",_5);
+}
+this._textArea=_5;
+this._textArea.spellcheck=false;
+this._initial_ta_size={w:_5.style.width?_5.style.width:(_5.offsetWidth?(_5.offsetWidth+"px"):(_5.cols+"em")),h:_5.style.height?_5.style.height:(_5.offsetHeight?(_5.offsetHeight+"px"):(_5.rows+"em"))};
+if(this.config.showLoading){
+var _7=document.createElement("div");
+_7.id="loading_"+_5.name;
+_7.className="loading";
+try{
+_7.style.width=_5.offsetWidth+"px";
+}
+catch(ex){
+_7.style.width=this._initial_ta_size.w;
+}
+_7.style.left=Xinha.findPosX(_5)+"px";
+_7.style.top=(Xinha.findPosY(_5)+parseInt(this._initial_ta_size.h,10)/2)+"px";
+var _8=document.createElement("div");
+_8.className="loading_main";
+_8.id="loading_main_"+_5.name;
+_8.appendChild(document.createTextNode(Xinha._lc("Loading in progress. Please wait !")));
+var _9=document.createElement("div");
+_9.className="loading_sub";
+_9.id="loading_sub_"+_5.name;
+_9.appendChild(document.createTextNode(Xinha._lc("Constructing main object")));
+_7.appendChild(_8);
+_7.appendChild(_9);
+document.body.appendChild(_7);
+this.setLoadingMessage("Constructing object");
+}
+this._editMode="wysiwyg";
+this.plugins={};
+this._timerToolbar=null;
+this._timerUndo=null;
+this._undoQueue=[this.config.undoSteps];
+this._undoPos=-1;
+this._customUndo=true;
+this._mdoc=document;
+this.doctype="";
+this.__htmlarea_id_num=__xinhas.length;
+__xinhas[this.__htmlarea_id_num]=this;
+this._notifyListeners={};
+var _a={right:{on:true,container:document.createElement("td"),panels:[]},left:{on:true,container:document.createElement("td"),panels:[]},top:{on:true,container:document.createElement("td"),panels:[]},bottom:{on:true,container:document.createElement("td"),panels:[]}};
+for(var i in _a){
+if(!_a[i].container){
+continue;
+}
+_a[i].div=_a[i].container;
+_a[i].container.className="panels "+i;
+Xinha.freeLater(_a[i],"container");
+Xinha.freeLater(_a[i],"div");
+}
+this._panels=_a;
+Xinha.freeLater(this,"_textArea");
+}
+}
+Xinha.onload=function(){
+};
+Xinha.init=function(){
+Xinha.onload();
+};
+Xinha.RE_tagName=/(<\/|<)\s*([^ \t\n>]+)/ig;
+Xinha.RE_doctype=/(<!doctype((.|\n)*?)>)\n?/i;
+Xinha.RE_head=/<head>((.|\n)*?)<\/head>/i;
+Xinha.RE_body=/<body[^>]*>((.|\n|\r|\t)*?)<\/body>/i;
+Xinha.RE_Specials=/([\/\^$*+?.()|{}[\]])/g;
+Xinha.RE_email=/[_a-zA-Z\d\-\.]{3,}@[_a-zA-Z\d\-]{2,}(\.[_a-zA-Z\d\-]{2,})+/i;
+Xinha.RE_url=/(https?:\/\/)?(([a-z0-9_]+:[a-z0-9_]+@)?[a-z0-9_-]{2,}(\.[a-z0-9_-]{2,}){2,}(:[0-9]+)?(\/\S+)*)/i;
+Xinha.Config=function(){
+var _c=this;
+this.version=Xinha.version.Revision;
+this.width="auto";
+this.height="auto";
+this.sizeIncludesBars=true;
+this.sizeIncludesPanels=true;
+this.panel_dimensions={left:"200px",right:"200px",top:"100px",bottom:"100px"};
+this.statusBar=true;
+this.htmlareaPaste=false;
+this.mozParaHandler="best";
+this.getHtmlMethod="DOMwalk";
+this.undoSteps=20;
+this.undoTimeout=500;
+this.changeJustifyWithDirection=false;
+this.fullPage=false;
+this.pageStyle="";
+this.pageStyleSheets=[];
+this.baseHref=null;
+this.expandRelativeUrl=true;
+this.stripBaseHref=true;
+this.stripSelfNamedAnchors=true;
+this.only7BitPrintablesInURLs=true;
+this.sevenBitClean=false;
+this.specialReplacements={};
+this.killWordOnPaste=true;
+this.makeLinkShowsTarget=true;
+this.charSet=Xinha.is_gecko?document.characterSet:document.charset;
+this.imgURL="images/";
+this.popupURL="popups/";
+this.htmlRemoveTags=null;
+this.flowToolbars=true;
+this.showLoading=false;
+this.stripScripts=true;
+this.convertUrlsToLinks=true;
+this.colorPickerCellSize="6px";
+this.colorPickerGranularity=18;
+this.colorPickerPosition="bottom,right";
+this.colorPickerWebSafe=false;
+this.colorPickerSaveColors=20;
+this.fullScreen=false;
+this.fullScreenMargins=[0,0,0,0];
+this.toolbar=[["popupeditor"],["separator","formatblock","fontname","fontsize","bold","italic","underline","strikethrough"],["separator","forecolor","hilitecolor","textindicator"],["separator","subscript","superscript"],["linebreak","separator","justifyleft","justifycenter","justifyright","justifyfull"],["separator","insertorderedlist","insertunorderedlist","outdent","indent"],["separator","inserthorizontalrule","createlink","insertimage","inserttable"],["linebreak","separator","undo","redo","selectall","print"],(Xinha.is_gecko?[]:["cut","copy","paste","overwrite","saveas"]),["separator","killword","clearfonts","removeformat","toggleborders","splitblock","lefttoright","righttoleft"],["separator","htmlmode","showhelp","about"]];
+this.fontname={"&mdash; font &mdash;":"","Arial":"arial,helvetica,sans-serif","Courier New":"courier new,courier,monospace","Georgia":"georgia,times new roman,times,serif","Tahoma":"tahoma,arial,helvetica,sans-serif","Times New Roman":"times new roman,times,serif","Verdana":"verdana,arial,helvetica,sans-serif","impact":"impact","WingDings":"wingdings"};
+this.fontsize={"&mdash; size &mdash;":"","1 (8 pt)":"1","2 (10 pt)":"2","3 (12 pt)":"3","4 (14 pt)":"4","5 (18 pt)":"5","6 (24 pt)":"6","7 (36 pt)":"7"};
+this.formatblock={"&mdash; format &mdash;":"","Heading 1":"h1","Heading 2":"h2","Heading 3":"h3","Heading 4":"h4","Heading 5":"h5","Heading 6":"h6","Normal":"p","Address":"address","Formatted":"pre"};
+this.customSelects={};
+function cut_copy_paste(e,_e,_f){
+e.execCommand(_e);
+}
+this.debug=true;
+this.URIs={"blank":"popups/blank.html","link":_editor_url+"modules/CreateLink/link.html","insert_image":_editor_url+"modules/InsertImage/insert_image.html","insert_table":_editor_url+"modules/InsertTable/insert_table.html","select_color":"select_color.html","about":"about.html","help":"editor_help.html"};
+this.btnList={bold:["Bold",Xinha._lc({key:"button_bold",string:["ed_buttons_main.gif",3,2]},"Xinha"),false,function(e){
+e.execCommand("bold");
+}],italic:["Italic",Xinha._lc({key:"button_italic",string:["ed_buttons_main.gif",2,2]},"Xinha"),false,function(e){
+e.execCommand("italic");
+}],underline:["Underline",Xinha._lc({key:"button_underline",string:["ed_buttons_main.gif",2,0]},"Xinha"),false,function(e){
+e.execCommand("underline");
+}],strikethrough:["Strikethrough",Xinha._lc({key:"button_strikethrough",string:["ed_buttons_main.gif",3,0]},"Xinha"),false,function(e){
+e.execCommand("strikethrough");
+}],subscript:["Subscript",Xinha._lc({key:"button_subscript",string:["ed_buttons_main.gif",3,1]},"Xinha"),false,function(e){
+e.execCommand("subscript");
+}],superscript:["Superscript",Xinha._lc({key:"button_superscript",string:["ed_buttons_main.gif",2,1]},"Xinha"),false,function(e){
+e.execCommand("superscript");
+}],justifyleft:["Justify Left",["ed_buttons_main.gif",0,0],false,function(e){
+e.execCommand("justifyleft");
+}],justifycenter:["Justify Center",["ed_buttons_main.gif",1,1],false,function(e){
+e.execCommand("justifycenter");
+}],justifyright:["Justify Right",["ed_buttons_main.gif",1,0],false,function(e){
+e.execCommand("justifyright");
+}],justifyfull:["Justify Full",["ed_buttons_main.gif",0,1],false,function(e){
+e.execCommand("justifyfull");
+}],orderedlist:["Ordered List",["ed_buttons_main.gif",0,3],false,function(e){
+e.execCommand("insertorderedlist");
+}],unorderedlist:["Bulleted List",["ed_buttons_main.gif",1,3],false,function(e){
+e.execCommand("insertunorderedlist");
+}],insertorderedlist:["Ordered List",["ed_buttons_main.gif",0,3],false,function(e){
+e.execCommand("insertorderedlist");
+}],insertunorderedlist:["Bulleted List",["ed_buttons_main.gif",1,3],false,function(e){
+e.execCommand("insertunorderedlist");
+}],outdent:["Decrease Indent",["ed_buttons_main.gif",1,2],false,function(e){
+e.execCommand("outdent");
+}],indent:["Increase Indent",["ed_buttons_main.gif",0,2],false,function(e){
+e.execCommand("indent");
+}],forecolor:["Font Color",["ed_buttons_main.gif",3,3],false,function(e){
+e.execCommand("forecolor");
+}],hilitecolor:["Background Color",["ed_buttons_main.gif",2,3],false,function(e){
+e.execCommand("hilitecolor");
+}],undo:["Undoes your last action",["ed_buttons_main.gif",4,2],false,function(e){
+e.execCommand("undo");
+}],redo:["Redoes your last action",["ed_buttons_main.gif",5,2],false,function(e){
+e.execCommand("redo");
+}],cut:["Cut selection",["ed_buttons_main.gif",5,0],false,cut_copy_paste],copy:["Copy selection",["ed_buttons_main.gif",4,0],false,cut_copy_paste],paste:["Paste from clipboard",["ed_buttons_main.gif",4,1],false,cut_copy_paste],selectall:["Select all","ed_selectall.gif",false,function(e){
+e.execCommand("selectall");
+}],inserthorizontalrule:["Horizontal Rule",["ed_buttons_main.gif",6,0],false,function(e){
+e.execCommand("inserthorizontalrule");
+}],createlink:["Insert Web Link",["ed_buttons_main.gif",6,1],false,function(e){
+e._createLink();
+}],insertimage:["Insert/Modify Image",["ed_buttons_main.gif",6,3],false,function(e){
+e.execCommand("insertimage");
+}],inserttable:["Insert Table",["ed_buttons_main.gif",6,2],false,function(e){
+e.execCommand("inserttable");
+}],htmlmode:["Toggle HTML Source",["ed_buttons_main.gif",7,0],true,function(e){
+e.execCommand("htmlmode");
+}],toggleborders:["Toggle Borders",["ed_buttons_main.gif",7,2],false,function(e){
+e._toggleBorders();
+}],print:["Print document",["ed_buttons_main.gif",8,1],false,function(e){
+if(Xinha.is_gecko){
+e._iframe.contentWindow.print();
+}else{
+e.focusEditor();
+print();
+}
+}],saveas:["Save as","ed_saveas.gif",false,function(e){
+e.execCommand("saveas",false,"noname.htm");
+}],about:["About this editor",["ed_buttons_main.gif",8,2],true,function(e){
+e.execCommand("about");
+}],showhelp:["Help using editor",["ed_buttons_main.gif",9,2],true,function(e){
+e.execCommand("showhelp");
+}],splitblock:["Split Block","ed_splitblock.gif",false,function(e){
+e._splitBlock();
+}],lefttoright:["Direction left to right",["ed_buttons_main.gif",0,4],false,function(e){
+e.execCommand("lefttoright");
+}],righttoleft:["Direction right to left",["ed_buttons_main.gif",1,4],false,function(e){
+e.execCommand("righttoleft");
+}],overwrite:["Insert/Overwrite","ed_overwrite.gif",false,function(e){
+e.execCommand("overwrite");
+}],wordclean:["MS Word Cleaner",["ed_buttons_main.gif",5,3],false,function(e){
+e._wordClean();
+}],clearfonts:["Clear Inline Font Specifications",["ed_buttons_main.gif",5,4],true,function(e){
+e._clearFonts();
+}],removeformat:["Remove formatting",["ed_buttons_main.gif",4,4],false,function(e){
+e.execCommand("removeformat");
+}],killword:["Clear MSOffice tags",["ed_buttons_main.gif",4,3],false,function(e){
+e.execCommand("killword");
+}]};
+for(var i in this.btnList){
+var btn=this.btnList[i];
+if(typeof btn!="object"){
+continue;
+}
+if(typeof btn[1]!="string"){
+btn[1][0]=_editor_url+this.imgURL+btn[1][0];
+}else{
+btn[1]=_editor_url+this.imgURL+btn[1];
+}
+btn[0]=Xinha._lc(btn[0]);
+}
+};
+Xinha.Config.prototype.registerButton=function(id,_3a,_3b,_3c,_3d,_3e){
+var _3f;
+if(typeof id=="string"){
+_3f=id;
+}else{
+if(typeof id=="object"){
+_3f=id.id;
+}else{
+alert("ERROR [Xinha.Config::registerButton]:\ninvalid arguments");
+return false;
+}
+}
+switch(typeof id){
+case "string":
+this.btnList[id]=[_3a,_3b,_3c,_3d,_3e];
+break;
+case "object":
+this.btnList[id.id]=[id.tooltip,id.image,id.textMode,id.action,id.context];
+break;
+}
+};
+Xinha.prototype.registerPanel=function(_40,_41){
+if(!_40){
+_40="right";
+}
+this.setLoadingMessage("Register panel "+_40);
+var _42=this.addPanel(_40);
+if(_41){
+_41.drawPanelIn(_42);
+}
+};
+Xinha.Config.prototype.registerDropdown=function(_43){
+this.customSelects[_43.id]=_43;
+};
+Xinha.Config.prototype.hideSomeButtons=function(_44){
+var _45=this.toolbar;
+for(var i=_45.length;--i>=0;){
+var _47=_45[i];
+for(var j=_47.length;--j>=0;){
+if(_44.indexOf(" "+_47[j]+" ")>=0){
+var len=1;
+if(/separator|space/.test(_47[j+1])){
+len=2;
+}
+_47.splice(j,len);
+}
+}
+}
+};
+Xinha.Config.prototype.addToolbarElement=function(id,_4b,_4c){
+var _4d=this.toolbar;
+var a,i,j,o,sid;
+var _4f=false;
+var _50=false;
+var _51=0;
+var _52=0;
+var _53=0;
+var _54=false;
+var _55=false;
+if((id&&typeof id=="object")&&(id.constructor==Array)){
+_4f=true;
+}
+if((_4b&&typeof _4b=="object")&&(_4b.constructor==Array)){
+_50=true;
+_51=_4b.length;
+}
+if(_4f){
+for(i=0;i<id.length;++i){
+if((id[i]!="separator")&&(id[i].indexOf("T[")!==0)){
+sid=id[i];
+}
+}
+}else{
+sid=id;
+}
+for(i=0;i<_4d.length;++i){
+a=_4d[i];
+for(j=0;j<a.length;++j){
+if(a[j]==sid){
+return;
+}
+}
+}
+for(i=0;!_55&&i<_4d.length;++i){
+a=_4d[i];
+for(j=0;!_55&&j<a.length;++j){
+if(_50){
+for(o=0;o<_51;++o){
+if(a[j]==_4b[o]){
+if(o===0){
+_55=true;
+j--;
+break;
+}else{
+_53=i;
+_52=j;
+_51=o;
+}
+}
+}
+}else{
+if(a[j]==_4b){
+_55=true;
+break;
+}
+}
+}
+}
+if(!_55&&_50){
+if(_4b.length!=_51){
+j=_52;
+a=_4d[_53];
+_55=true;
+}
+}
+if(_55){
+if(_4c===0){
+if(_4f){
+a[j]=id[id.length-1];
+for(i=id.length-1;--i>=0;){
+a.splice(j,0,id[i]);
+}
+}else{
+a[j]=id;
+}
+}else{
+if(_4c<0){
+j=j+_4c+1;
+}else{
+if(_4c>0){
+j=j+_4c;
+}
+}
+if(_4f){
+for(i=id.length;--i>=0;){
+a.splice(j,0,id[i]);
+}
+}else{
+a.splice(j,0,id);
+}
+}
+}else{
+_4d[0].splice(0,0,"separator");
+if(_4f){
+for(i=id.length;--i>=0;){
+_4d[0].splice(0,0,id[i]);
+}
+}else{
+_4d[0].splice(0,0,id);
+}
+}
+};
+Xinha.Config.prototype.removeToolbarElement=Xinha.Config.prototype.hideSomeButtons;
+Xinha.replaceAll=function(_56){
+var tas=document.getElementsByTagName("textarea");
+for(var i=tas.length;i>0;(new Xinha(tas[--i],_56)).generate()){
+}
+};
+Xinha.replace=function(id,_5a){
+var ta=Xinha.getElementById("textarea",id);
+return ta?(new Xinha(ta,_5a)).generate():null;
+};
+Xinha.prototype._createToolbar=function(){
+this.setLoadingMessage("Create Toolbar");
+var _5c=this;
+var _5d=document.createElement("div");
+this._toolBar=this._toolbar=_5d;
+_5d.className="toolbar";
+_5d.unselectable="1";
+Xinha.freeLater(this,"_toolBar");
+Xinha.freeLater(this,"_toolbar");
+var _5e=null;
+var _5f={};
+this._toolbarObjects=_5f;
+this._createToolbar1(_5c,_5d,_5f);
+this._htmlArea.appendChild(_5d);
+return _5d;
+};
+Xinha.prototype._setConfig=function(_60){
+this.config=_60;
+};
+Xinha.prototype._addToolbar=function(){
+this._createToolbar1(this,this._toolbar,this._toolbarObjects);
+};
+Xinha._createToolbarBreakingElement=function(){
+var brk=document.createElement("div");
+brk.style.height="1px";
+brk.style.width="1px";
+brk.style.lineHeight="1px";
+brk.style.fontSize="1px";
+brk.style.clear="both";
+return brk;
+};
+Xinha.prototype._createToolbar1=function(_62,_63,_64){
+var _65;
+if(_62.config.flowToolbars){
+_63.appendChild(Xinha._createToolbarBreakingElement());
+}
+function newLine(){
+if(typeof _65!="undefined"&&_65.childNodes.length===0){
+return;
+}
+var _66=document.createElement("table");
+_66.border="0px";
+_66.cellSpacing="0px";
+_66.cellPadding="0px";
+if(_62.config.flowToolbars){
+if(Xinha.is_ie){
+_66.style.styleFloat="left";
+}else{
+_66.style.cssFloat="left";
+}
+}
+_63.appendChild(_66);
+var _67=document.createElement("tbody");
+_66.appendChild(_67);
+_65=document.createElement("tr");
+_67.appendChild(_65);
+_66.className="toolbarRow";
+}
+newLine();
+function setButtonStatus(id,_69){
+var _6a=this[id];
+var el=this.element;
+if(_6a!=_69){
+switch(id){
+case "enabled":
+if(_69){
+Xinha._removeClass(el,"buttonDisabled");
+el.disabled=false;
+}else{
+Xinha._addClass(el,"buttonDisabled");
+el.disabled=true;
+}
+break;
+case "active":
+if(_69){
+Xinha._addClass(el,"buttonPressed");
+}else{
+Xinha._removeClass(el,"buttonPressed");
+}
+break;
+}
+this[id]=_69;
+}
+}
+function createSelect(txt){
+var _6d=null;
+var el=null;
+var cmd=null;
+var _70=_62.config.customSelects;
+var _71=null;
+var _72="";
+switch(txt){
+case "fontsize":
+case "fontname":
+case "formatblock":
+_6d=_62.config[txt];
+cmd=txt;
+break;
+default:
+cmd=txt;
+var _73=_70[cmd];
+if(typeof _73!="undefined"){
+_6d=_73.options;
+_71=_73.context;
+if(typeof _73.tooltip!="undefined"){
+_72=_73.tooltip;
+}
+}else{
+alert("ERROR [createSelect]:\nCan't find the requested dropdown definition");
+}
+break;
+}
+if(_6d){
+el=document.createElement("select");
+el.title=_72;
+var obj={name:txt,element:el,enabled:true,text:false,cmd:cmd,state:setButtonStatus,context:_71};
+Xinha.freeLater(obj);
+_64[txt]=obj;
+for(var i in _6d){
+if(typeof (_6d[i])!="string"){
+continue;
+}
+var op=document.createElement("option");
+op.innerHTML=Xinha._lc(i);
+op.value=_6d[i];
+el.appendChild(op);
+}
+Xinha._addEvent(el,"change",function(){
+_62._comboSelected(el,txt);
+});
+}
+return el;
+}
+function createButton(txt){
+var el,btn,obj=null;
+switch(txt){
+case "separator":
+if(_62.config.flowToolbars){
+newLine();
+}
+el=document.createElement("div");
+el.className="separator";
+break;
+case "space":
+el=document.createElement("div");
+el.className="space";
+break;
+case "linebreak":
+newLine();
+return false;
+case "textindicator":
+el=document.createElement("div");
+el.appendChild(document.createTextNode("A"));
+el.className="indicator";
+el.title=Xinha._lc("Current style");
+obj={name:txt,element:el,enabled:true,active:false,text:false,cmd:"textindicator",state:setButtonStatus};
+Xinha.freeLater(obj);
+_64[txt]=obj;
+break;
+default:
+btn=_62.config.btnList[txt];
+}
+if(!el&&btn){
+el=document.createElement("a");
+el.style.display="block";
+el.href="javascript:void(0)";
+el.style.textDecoration="none";
+el.title=btn[0];
+el.className="button";
+el.style.direction="ltr";
+obj={name:txt,element:el,enabled:true,active:false,text:btn[2],cmd:btn[3],state:setButtonStatus,context:btn[4]||null};
+Xinha.freeLater(el);
+Xinha.freeLater(obj);
+_64[txt]=obj;
+el.ondrag=function(){
+return false;
+};
+Xinha._addEvent(el,"mouseout",function(ev){
+if(obj.enabled){
+Xinha._removeClass(el,"buttonActive");
+if(obj.active){
+Xinha._addClass(el,"buttonPressed");
+}
+}
+});
+Xinha._addEvent(el,"mousedown",function(ev){
+if(obj.enabled){
+Xinha._addClass(el,"buttonActive");
+Xinha._removeClass(el,"buttonPressed");
+Xinha._stopEvent(Xinha.is_ie?window.event:ev);
+}
+});
+Xinha._addEvent(el,"click",function(ev){
+if(obj.enabled){
+Xinha._removeClass(el,"buttonActive");
+if(Xinha.is_gecko){
+_62.activateEditor();
+}
+obj.cmd(_62,obj.name,obj);
+Xinha._stopEvent(Xinha.is_ie?window.event:ev);
+}
+});
+var _7c=Xinha.makeBtnImg(btn[1]);
+var img=_7c.firstChild;
+el.appendChild(_7c);
+obj.imgel=img;
+obj.swapImage=function(_7e){
+if(typeof _7e!="string"){
+img.src=_7e[0];
+img.style.position="relative";
+img.style.top=_7e[2]?("-"+(18*(_7e[2]+1))+"px"):"-18px";
+img.style.left=_7e[1]?("-"+(18*(_7e[1]+1))+"px"):"-18px";
+}else{
+obj.imgel.src=_7e;
+img.style.top="0px";
+img.style.left="0px";
+}
+};
+}else{
+if(!el){
+el=createSelect(txt);
+}
+}
+return el;
+}
+var _7f=true;
+for(var i=0;i<this.config.toolbar.length;++i){
+if(!_7f){
+}else{
+_7f=false;
+}
+if(this.config.toolbar[i]===null){
+this.config.toolbar[i]=["separator"];
+}
+var _81=this.config.toolbar[i];
+for(var j=0;j<_81.length;++j){
+var _83=_81[j];
+var _84;
+if(/^([IT])\[(.*?)\]/.test(_83)){
+var _85=RegExp.$1=="I";
+var _86=RegExp.$2;
+if(_85){
+_86=Xinha._lc(_86);
+}
+_84=document.createElement("td");
+_65.appendChild(_84);
+_84.className="label";
+_84.innerHTML=_86;
+}else{
+if(typeof _83!="function"){
+var _87=createButton(_83);
+if(_87){
+_84=document.createElement("td");
+_84.className="toolbarElement";
+_65.appendChild(_84);
+_84.appendChild(_87);
+}else{
+if(_87===null){
+alert("FIXME: Unknown toolbar item: "+_83);
+}
+}
+}
+}
+}
+}
+if(_62.config.flowToolbars){
+_63.appendChild(Xinha._createToolbarBreakingElement());
+}
+return _63;
+};
+var use_clone_img=false;
+Xinha.makeBtnImg=function(_88,doc){
+if(!doc){
+doc=document;
+}
+if(!doc._xinhaImgCache){
+doc._xinhaImgCache={};
+Xinha.freeLater(doc._xinhaImgCache);
+}
+var _8a=null;
+if(Xinha.is_ie&&((!doc.compatMode)||(doc.compatMode&&doc.compatMode=="BackCompat"))){
+_8a=doc.createElement("span");
+}else{
+_8a=doc.createElement("div");
+_8a.style.position="relative";
+}
+_8a.style.overflow="hidden";
+_8a.style.width="18px";
+_8a.style.height="18px";
+_8a.className="buttonImageContainer";
+var img=null;
+if(typeof _88=="string"){
+if(doc._xinhaImgCache[_88]){
+img=doc._xinhaImgCache[_88].cloneNode();
+}else{
+img=doc.createElement("img");
+img.src=_88;
+img.style.width="18px";
+img.style.height="18px";
+if(use_clone_img){
+doc._xinhaImgCache[_88]=img.cloneNode();
+}
+}
+}else{
+if(doc._xinhaImgCache[_88[0]]){
+img=doc._xinhaImgCache[_88[0]].cloneNode();
+}else{
+img=doc.createElement("img");
+img.src=_88[0];
+img.style.position="relative";
+if(use_clone_img){
+doc._xinhaImgCache[_88[0]]=img.cloneNode();
+}
+}
+img.style.top=_88[2]?("-"+(18*(_88[2]+1))+"px"):"-18px";
+img.style.left=_88[1]?("-"+(18*(_88[1]+1))+"px"):"-18px";
+}
+_8a.appendChild(img);
+return _8a;
+};
+Xinha.prototype._createStatusBar=function(){
+this.setLoadingMessage("Create StatusBar");
+var _8c=document.createElement("div");
+_8c.className="statusBar";
+this._statusBar=_8c;
+Xinha.freeLater(this,"_statusBar");
+var div=document.createElement("span");
+div.className="statusBarTree";
+div.innerHTML=Xinha._lc("Path")+": ";
+this._statusBarTree=div;
+Xinha.freeLater(this,"_statusBarTree");
+this._statusBar.appendChild(div);
+div=document.createElement("span");
+div.innerHTML=Xinha._lc("You are in TEXT MODE.  Use the [<>] button to switch back to WYSIWYG.");
+div.style.display="none";
+this._statusBarTextMode=div;
+Xinha.freeLater(this,"_statusBarTextMode");
+this._statusBar.appendChild(div);
+if(!this.config.statusBar){
+_8c.style.display="none";
+}
+return _8c;
+};
+Xinha.prototype.generate=function(){
+var i;
+var _8f=this;
+if(Xinha.is_ie){
+if(typeof InternetExplorer=="undefined"){
+Xinha.loadPlugin("InternetExplorer",function(){
+_8f.generate();
+},_editor_url+"modules/InternetExplorer/InternetExplorer.js");
+return false;
+}
+_8f._browserSpecificPlugin=_8f.registerPlugin("InternetExplorer");
+}else{
+if(typeof Gecko=="undefined"){
+Xinha.loadPlugin("Gecko",function(){
+_8f.generate();
+},_editor_url+"modules/Gecko/Gecko.js");
+return false;
+}
+_8f._browserSpecificPlugin=_8f.registerPlugin("Gecko");
+}
+this.setLoadingMessage("Generate Xinha object");
+if(typeof Dialog=="undefined"){
+Xinha._loadback(_editor_url+"modules/Dialogs/dialog.js",this.generate,this);
+return false;
+}
+if(typeof Xinha.Dialog=="undefined"){
+Xinha._loadback(_editor_url+"modules/Dialogs/inline-dialog.js",this.generate,this);
+return false;
+}
+if(typeof FullScreen=="undefined"){
+Xinha.loadPlugin("FullScreen",function(){
+_8f.generate();
+},_editor_url+"modules/FullScreen/full-screen.js");
+return false;
+}
+var _90=_8f.config.toolbar;
+for(i=_90.length;--i>=0;){
+for(var j=_90[i].length;--j>=0;){
+switch(_90[i][j]){
+case "popupeditor":
+_8f.registerPlugin("FullScreen");
+break;
+case "insertimage":
+if(typeof InsertImage=="undefined"&&typeof Xinha.prototype._insertImage=="undefined"){
+Xinha.loadPlugin("InsertImage",function(){
+_8f.generate();
+},_editor_url+"modules/InsertImage/insert_image.js");
+return false;
+}else{
+if(typeof InsertImage!="undefined"){
+_8f.registerPlugin("InsertImage");
+}
+}
+break;
+case "createlink":
+if(typeof CreateLink=="undefined"&&typeof Xinha.prototype._createLink=="undefined"&&typeof Linker=="undefined"){
+Xinha.loadPlugin("CreateLink",function(){
+_8f.generate();
+},_editor_url+"modules/CreateLink/link.js");
+return false;
+}else{
+if(typeof CreateLink!="undefined"){
+_8f.registerPlugin("CreateLink");
+}
+}
+break;
+case "inserttable":
+if(typeof InsertTable=="undefined"&&typeof Xinha.prototype._insertTable=="undefined"){
+Xinha.loadPlugin("InsertTable",function(){
+_8f.generate();
+},_editor_url+"modules/InsertTable/insert_table.js");
+return false;
+}else{
+if(typeof InsertTable!="undefined"){
+_8f.registerPlugin("InsertTable");
+}
+}
+break;
+case "hilitecolor":
+case "forecolor":
+if(typeof ColorPicker=="undefined"){
+Xinha.loadPlugin("ColorPicker",function(){
+_8f.generate();
+},_editor_url+"modules/ColorPicker/ColorPicker.js");
+return false;
+}else{
+if(typeof ColorPicker!="undefined"){
+_8f.registerPlugin("ColorPicker");
+}
+}
+break;
+}
+}
+}
+if(Xinha.is_gecko&&(_8f.config.mozParaHandler=="best"||_8f.config.mozParaHandler=="dirty")){
+switch(this.config.mozParaHandler){
+case "dirty":
+var _92=_editor_url+"modules/Gecko/paraHandlerDirty.js";
+break;
+default:
+var _92=_editor_url+"modules/Gecko/paraHandlerBest.js";
+break;
+}
+if(typeof EnterParagraphs=="undefined"){
+Xinha.loadPlugin("EnterParagraphs",function(){
+_8f.generate();
+},_92);
+return false;
+}
+_8f.registerPlugin("EnterParagraphs");
+}
+switch(this.config.getHtmlMethod){
+case "TransformInnerHTML":
+var _93=_editor_url+"modules/GetHtml/TransformInnerHTML.js";
+break;
+default:
+var _93=_editor_url+"modules/GetHtml/DOMwalk.js";
+break;
+}
+if(typeof GetHtmlImplementation=="undefined"){
+Xinha.loadPlugin("GetHtmlImplementation",function(){
+_8f.generate();
+},_93);
+return false;
+}else{
+_8f.registerPlugin("GetHtmlImplementation");
+}
+if(_editor_skin!==""){
+var _94=false;
+var _95=document.getElementsByTagName("head")[0];
+var _96=document.getElementsByTagName("link");
+for(i=0;i<_96.length;i++){
+if((_96[i].rel=="stylesheet")&&(_96[i].href==_editor_url+"skins/"+_editor_skin+"/skin.css")){
+_94=true;
+}
+}
+if(!_94){
+var _97=document.createElement("link");
+_97.type="text/css";
+_97.href=_editor_url+"skins/"+_editor_skin+"/skin.css";
+_97.rel="stylesheet";
+_95.appendChild(_97);
+}
+}
+this._framework={"table":document.createElement("table"),"tbody":document.createElement("tbody"),"tb_row":document.createElement("tr"),"tb_cell":document.createElement("td"),"tp_row":document.createElement("tr"),"tp_cell":this._panels.top.container,"ler_row":document.createElement("tr"),"lp_cell":this._panels.left.container,"ed_cell":document.createElement("td"),"rp_cell":this._panels.right.container,"bp_row":document.createElement("tr"),"bp_cell":this._panels.bottom.container,"sb_row":document.createElement("tr"),"sb_cell":document.createElement("td")};
+Xinha.freeLater(this._framework);
+var fw=this._framework;
+fw.table.border="0";
+fw.table.cellPadding="0";
+fw.table.cellSpacing="0";
+fw.tb_row.style.verticalAlign="top";
+fw.tp_row.style.verticalAlign="top";
+fw.ler_row.style.verticalAlign="top";
+fw.bp_row.style.verticalAlign="top";
+fw.sb_row.style.verticalAlign="top";
+fw.ed_cell.style.position="relative";
+fw.tb_row.appendChild(fw.tb_cell);
+fw.tb_cell.colSpan=3;
+fw.tp_row.appendChild(fw.tp_cell);
+fw.tp_cell.colSpan=3;
+fw.ler_row.appendChild(fw.lp_cell);
+fw.ler_row.appendChild(fw.ed_cell);
+fw.ler_row.appendChild(fw.rp_cell);
+fw.bp_row.appendChild(fw.bp_cell);
+fw.bp_cell.colSpan=3;
+fw.sb_row.appendChild(fw.sb_cell);
+fw.sb_cell.colSpan=3;
+fw.tbody.appendChild(fw.tb_row);
+fw.tbody.appendChild(fw.tp_row);
+fw.tbody.appendChild(fw.ler_row);
+fw.tbody.appendChild(fw.bp_row);
+fw.tbody.appendChild(fw.sb_row);
+fw.table.appendChild(fw.tbody);
+var _99=this._framework.table;
+this._htmlArea=_99;
+Xinha.freeLater(this,"_htmlArea");
+_99.className="htmlarea";
+this._framework.tb_cell.appendChild(this._createToolbar());
+var _9a=document.createElement("iframe");
+_9a.src=_editor_url+_8f.config.URIs.blank;
+this._framework.ed_cell.appendChild(_9a);
+this._iframe=_9a;
+this._iframe.className="xinha_iframe";
+Xinha.freeLater(this,"_iframe");
+var _9b=this._createStatusBar();
+this._framework.sb_cell.appendChild(_9b);
+var _9c=this._textArea;
+_9c.parentNode.insertBefore(_99,_9c);
+_9c.className="xinha_textarea";
+Xinha.removeFromParent(_9c);
+this._framework.ed_cell.appendChild(_9c);
+if(_9c.form){
+Xinha.prependDom0Event(this._textArea.form,"submit",function(){
+_8f._textArea.value=_8f.outwardHtml(_8f.getHTML());
+return true;
+});
+var _9d=_9c.value;
+Xinha.prependDom0Event(this._textArea.form,"reset",function(){
+_8f.setHTML(_8f.inwardHtml(_9d));
+_8f.updateToolbar();
+return true;
+});
+if(!_9c.form.xinha_submit){
+try{
+_9c.form.xinha_submit=_9c.form.submit;
+_9c.form.submit=function(){
+this.onsubmit();
+this.xinha_submit();
+};
+}
+catch(ex){
+}
+}
+}
+Xinha.prependDom0Event(window,"unload",function(){
+_9c.value=_8f.outwardHtml(_8f.getHTML());
+return true;
+});
+_9c.style.display="none";
+_8f.initSize();
+_8f._iframeLoadDone=false;
+Xinha._addEvent(this._iframe,"load",function(e){
+if(!_8f._iframeLoadDone){
+_8f._iframeLoadDone=true;
+_8f.initIframe();
+}
+return true;
+});
+};
+Xinha.prototype.initSize=function(){
+this.setLoadingMessage("Init editor size");
+var _9f=this;
+var _a0=null;
+var _a1=null;
+switch(this.config.width){
+case "auto":
+_a0=this._initial_ta_size.w;
+break;
+case "toolbar":
+_a0=this._toolBar.offsetWidth+"px";
+break;
+default:
+_a0=/[^0-9]/.test(this.config.width)?this.config.width:this.config.width+"px";
+break;
+}
+switch(this.config.height){
+case "auto":
+_a1=this._initial_ta_size.h;
+break;
+default:
+_a1=/[^0-9]/.test(this.config.height)?this.config.height:this.config.height+"px";
+break;
+}
+this.sizeEditor(_a0,_a1,this.config.sizeIncludesBars,this.config.sizeIncludesPanels);
+this.notifyOn("panel_change",function(){
+_9f.sizeEditor();
+});
+};
+Xinha.prototype.sizeEditor=function(_a2,_a3,_a4,_a5){
+this._iframe.style.height="100%";
+this._textArea.style.height="100%";
+this._iframe.style.width="";
+this._textArea.style.width="";
+if(_a4!==null){
+this._htmlArea.sizeIncludesToolbars=_a4;
+}
+if(_a5!==null){
+this._htmlArea.sizeIncludesPanels=_a5;
+}
+if(_a2){
+this._htmlArea.style.width=_a2;
+if(!this._htmlArea.sizeIncludesPanels){
+var _a6=this._panels.right;
+if(_a6.on&&_a6.panels.length&&Xinha.hasDisplayedChildren(_a6.div)){
+this._htmlArea.style.width=(this._htmlArea.offsetWidth+parseInt(this.config.panel_dimensions.right,10))+"px";
+}
+var _a7=this._panels.left;
+if(_a7.on&&_a7.panels.length&&Xinha.hasDisplayedChildren(_a7.div)){
+this._htmlArea.style.width=(this._htmlArea.offsetWidth+parseInt(this.config.panel_dimensions.left,10))+"px";
+}
+}
+}
+if(_a3){
+this._htmlArea.style.height=_a3;
+if(!this._htmlArea.sizeIncludesToolbars){
+this._htmlArea.style.height=(this._htmlArea.offsetHeight+this._toolbar.offsetHeight+this._statusBar.offsetHeight)+"px";
+}
+if(!this._htmlArea.sizeIncludesPanels){
+var _a8=this._panels.top;
+if(_a8.on&&_a8.panels.length&&Xinha.hasDisplayedChildren(_a8.div)){
+this._htmlArea.style.height=(this._htmlArea.offsetHeight+parseInt(this.config.panel_dimensions.top,10))+"px";
+}
+var _a9=this._panels.bottom;
+if(_a9.on&&_a9.panels.length&&Xinha.hasDisplayedChildren(_a9.div)){
+this._htmlArea.style.height=(this._htmlArea.offsetHeight+parseInt(this.config.panel_dimensions.bottom,10))+"px";
+}
+}
+}
+_a2=this._htmlArea.offsetWidth;
+_a3=this._htmlArea.offsetHeight;
+var _aa=this._panels;
+var _ab=this;
+var _ac=1;
+function panel_is_alive(pan){
+if(_aa[pan].on&&_aa[pan].panels.length&&Xinha.hasDisplayedChildren(_aa[pan].container)){
+_aa[pan].container.style.display="";
+return true;
+}else{
+_aa[pan].container.style.display="none";
+return false;
+}
+}
+if(panel_is_alive("left")){
+_ac+=1;
+}
+if(panel_is_alive("right")){
+_ac+=1;
+}
+this._framework.tb_cell.colSpan=_ac;
+this._framework.tp_cell.colSpan=_ac;
+this._framework.bp_cell.colSpan=_ac;
+this._framework.sb_cell.colSpan=_ac;
+if(!this._framework.tp_row.childNodes.length){
+Xinha.removeFromParent(this._framework.tp_row);
+}else{
+if(!Xinha.hasParentNode(this._framework.tp_row)){
+this._framework.tbody.insertBefore(this._framework.tp_row,this._framework.ler_row);
+}
+}
+if(!this._framework.bp_row.childNodes.length){
+Xinha.removeFromParent(this._framework.bp_row);
+}else{
+if(!Xinha.hasParentNode(this._framework.bp_row)){
+this._framework.tbody.insertBefore(this._framework.bp_row,this._framework.ler_row.nextSibling);
+}
+}
+if(!this.config.statusBar){
+Xinha.removeFromParent(this._framework.sb_row);
+}else{
+if(!Xinha.hasParentNode(this._framework.sb_row)){
+this._framework.table.appendChild(this._framework.sb_row);
+}
+}
+this._framework.lp_cell.style.width=this.config.panel_dimensions.left;
+this._framework.rp_cell.style.width=this.config.panel_dimensions.right;
+this._framework.tp_cell.style.height=this.config.panel_dimensions.top;
+this._framework.bp_cell.style.height=this.config.panel_dimensions.bottom;
+this._framework.tb_cell.style.height=this._toolBar.offsetHeight+"px";
+this._framework.sb_cell.style.height=this._statusBar.offsetHeight+"px";
+var _ae=_a3-this._toolBar.offsetHeight-this._statusBar.offsetHeight;
+if(panel_is_alive("top")){
+_ae-=parseInt(this.config.panel_dimensions.top,10);
+}
+if(panel_is_alive("bottom")){
+_ae-=parseInt(this.config.panel_dimensions.bottom,10);
+}
+this._iframe.style.height=_ae+"px";
+var _af=_a2;
+if(panel_is_alive("left")){
+_af-=parseInt(this.config.panel_dimensions.left,10);
+}
+if(panel_is_alive("right")){
+_af-=parseInt(this.config.panel_dimensions.right,10);
+}
+this._iframe.style.width=_af+"px";
+this._textArea.style.height=this._iframe.style.height;
+this._textArea.style.width=this._iframe.style.width;
+this.notifyOf("resize",{width:this._htmlArea.offsetWidth,height:this._htmlArea.offsetHeight});
+};
+Xinha.prototype.addPanel=function(_b0){
+var div=document.createElement("div");
+div.side=_b0;
+if(_b0=="left"||_b0=="right"){
+div.style.width=this.config.panel_dimensions[_b0];
+if(this._iframe){
+div.style.height=this._iframe.style.height;
+}
+}
+Xinha.addClasses(div,"panel");
+this._panels[_b0].panels.push(div);
+this._panels[_b0].div.appendChild(div);
+this.notifyOf("panel_change",{"action":"add","panel":div});
+return div;
+};
+Xinha.prototype.removePanel=function(_b2){
+this._panels[_b2.side].div.removeChild(_b2);
+var _b3=[];
+for(var i=0;i<this._panels[_b2.side].panels.length;i++){
+if(this._panels[_b2.side].panels[i]!=_b2){
+_b3.push(this._panels[_b2.side].panels[i]);
+}
+}
+this._panels[_b2.side].panels=_b3;
+this.notifyOf("panel_change",{"action":"remove","panel":_b2});
+};
+Xinha.prototype.hidePanel=function(_b5){
+if(_b5&&_b5.style.display!="none"){
+try{
+var pos=this.scrollPos(this._iframe.contentWindow);
+}
+catch(e){
+}
+_b5.style.display="none";
+this.notifyOf("panel_change",{"action":"hide","panel":_b5});
+try{
+this._iframe.contentWindow.scrollTo(pos.x,pos.y);
+}
+catch(e){
+}
+}
+};
+Xinha.prototype.showPanel=function(_b7){
+if(_b7&&_b7.style.display=="none"){
+try{
+var pos=this.scrollPos(this._iframe.contentWindow);
+}
+catch(e){
+}
+_b7.style.display="";
+this.notifyOf("panel_change",{"action":"show","panel":_b7});
+try{
+this._iframe.contentWindow.scrollTo(pos.x,pos.y);
+}
+catch(e){
+}
+}
+};
+Xinha.prototype.hidePanels=function(_b9){
+if(typeof _b9=="undefined"){
+_b9=["left","right","top","bottom"];
+}
+var _ba=[];
+for(var i=0;i<_b9.length;i++){
+if(this._panels[_b9[i]].on){
+_ba.push(_b9[i]);
+this._panels[_b9[i]].on=false;
+}
+}
+this.notifyOf("panel_change",{"action":"multi_hide","sides":_b9});
+};
+Xinha.prototype.showPanels=function(_bc){
+if(typeof _bc=="undefined"){
+_bc=["left","right","top","bottom"];
+}
+var _bd=[];
+for(var i=0;i<_bc.length;i++){
+if(!this._panels[_bc[i]].on){
+_bd.push(_bc[i]);
+this._panels[_bc[i]].on=true;
+}
+}
+this.notifyOf("panel_change",{"action":"multi_show","sides":_bc});
+};
+Xinha.objectProperties=function(obj){
+var _c0=[];
+for(var x in obj){
+_c0[_c0.length]=x;
+}
+return _c0;
+};
+Xinha.prototype.editorIsActivated=function(){
+try{
+return Xinha.is_gecko?this._doc.designMode=="on":this._doc.body.contentEditable;
+}
+catch(ex){
+return false;
+}
+};
+Xinha._someEditorHasBeenActivated=false;
+Xinha._currentlyActiveEditor=false;
+Xinha.prototype.activateEditor=function(){
+if(Xinha._currentlyActiveEditor){
+if(Xinha._currentlyActiveEditor==this){
+return true;
+}
+Xinha._currentlyActiveEditor.deactivateEditor();
+}
+if(Xinha.is_gecko&&this._doc.designMode!="on"){
+try{
+if(this._iframe.style.display=="none"){
+this._iframe.style.display="";
+this._doc.designMode="on";
+this._iframe.style.display="none";
+}else{
+this._doc.designMode="on";
+}
+}
+catch(ex){
+}
+}else{
+if(!Xinha.is_gecko&&this._doc.body.contentEditable!==true){
+this._doc.body.contentEditable=true;
+}
+}
+Xinha._someEditorHasBeenActivated=true;
+Xinha._currentlyActiveEditor=this;
+var _c2=this;
+this.enableToolbar();
+};
+Xinha.prototype.deactivateEditor=function(){
+this.disableToolbar();
+if(Xinha.is_gecko&&this._doc.designMode!="off"){
+try{
+this._doc.designMode="off";
+}
+catch(ex){
+}
+}else{
+if(!Xinha.is_gecko&&this._doc.body.contentEditable!==false){
+this._doc.body.contentEditable=false;
+}
+}
+if(Xinha._currentlyActiveEditor!=this){
+return;
+}
+Xinha._currentlyActiveEditor=false;
+};
+Xinha.prototype.initIframe=function(){
+this.setLoadingMessage("Init IFrame");
+this.disableToolbar();
+var doc=null;
+var _c4=this;
+try{
+if(_c4._iframe.contentDocument){
+this._doc=_c4._iframe.contentDocument;
+}else{
+this._doc=_c4._iframe.contentWindow.document;
+}
+doc=this._doc;
+if(!doc){
+if(Xinha.is_gecko){
+setTimeout(function(){
+_c4.initIframe();
+},50);
+return false;
+}else{
+alert("ERROR: IFRAME can't be initialized.");
+}
+}
+}
+catch(ex){
+setTimeout(function(){
+_c4.initIframe();
+},50);
+}
+Xinha.freeLater(this,"_doc");
+doc.open("text/html","replace");
+var _c5="";
+if(!_c4.config.fullPage){
+_c5="<html>\n";
+_c5+="<head>\n";
+_c5+="<meta http-equiv=\"Content-Type\" content=\"text/html; charset="+_c4.config.charSet+"\">\n";
+if(typeof _c4.config.baseHref!="undefined"&&_c4.config.baseHref!==null){
+_c5+="<base href=\""+_c4.config.baseHref+"\"/>\n";
+}
+_c5+=Xinha.addCoreCSS();
+if(_c4.config.pageStyle){
+_c5+="<style type=\"text/css\">\n"+_c4.config.pageStyle+"\n</style>";
+}
+if(typeof _c4.config.pageStyleSheets!=="undefined"){
+for(var i=0;i<_c4.config.pageStyleSheets.length;i++){
+if(_c4.config.pageStyleSheets[i].length>0){
+_c5+="<link rel=\"stylesheet\" type=\"text/css\" href=\""+_c4.config.pageStyleSheets[i]+"\">";
+}
+}
+}
+_c5+="</head>\n";
+_c5+="<body>\n";
+_c5+=_c4.inwardHtml(_c4._textArea.value);
+_c5+="</body>\n";
+_c5+="</html>";
+}else{
+_c5=_c4.inwardHtml(_c4._textArea.value);
+if(_c5.match(Xinha.RE_doctype)){
+_c4.setDoctype(RegExp.$1);
+_c5=_c5.replace(Xinha.RE_doctype,"");
+}
+var _c7=_c5.match(/<link\s+[\s\S]*?["']\s*\/?>/gi);
+_c5=_c5.replace(/<link\s+[\s\S]*?["']\s*\/?>\s*/gi,"");
+_c7?_c5=_c5.replace(/<\/head>/i,_c7.join("\n")+"\n</head>"):null;
+}
+doc.write(_c5);
+doc.close();
+if(this.config.fullScreen){
+this._fullScreen();
+}
+this.setEditorEvents();
+};
+Xinha.prototype.whenDocReady=function(F){
+var E=this;
+if(this._doc&&this._doc.body){
+F();
+}else{
+setTimeout(function(){
+E.whenDocReady(F);
+},50);
+}
+};
+Xinha.prototype.setMode=function(_ca){
+var _cb;
+if(typeof _ca=="undefined"){
+_ca=this._editMode=="textmode"?"wysiwyg":"textmode";
+}
+switch(_ca){
+case "textmode":
+this.setCC("iframe");
+_cb=this.outwardHtml(this.getHTML());
+this.setHTML(_cb);
+this.deactivateEditor();
+this._iframe.style.display="none";
+this._textArea.style.display="";
+if(this.config.statusBar){
+this._statusBarTree.style.display="none";
+this._statusBarTextMode.style.display="";
+}
+this.notifyOf("modechange",{"mode":"text"});
+this.findCC("textarea");
+break;
+case "wysiwyg":
+this.setCC("textarea");
+_cb=this.inwardHtml(this.getHTML());
+this.deactivateEditor();
+this.setHTML(_cb);
+this._iframe.style.display="";
+this._textArea.style.display="none";
+this.activateEditor();
+if(this.config.statusBar){
+this._statusBarTree.style.display="";
+this._statusBarTextMode.style.display="none";
+}
+this.notifyOf("modechange",{"mode":"wysiwyg"});
+this.findCC("iframe");
+break;
+default:
+alert("Mode <"+_ca+"> not defined!");
+return false;
+}
+this._editMode=_ca;
+for(var i in this.plugins){
+var _cd=this.plugins[i].instance;
+if(_cd&&typeof _cd.onMode=="function"){
+_cd.onMode(_ca);
+}
+}
+};
+Xinha.prototype.setFullHTML=function(_ce){
+var _cf=RegExp.multiline;
+RegExp.multiline=true;
+if(_ce.match(Xinha.RE_doctype)){
+this.setDoctype(RegExp.$1);
+_ce=_ce.replace(Xinha.RE_doctype,"");
+}
+RegExp.multiline=_cf;
+if(0){
+if(_ce.match(Xinha.RE_head)){
+this._doc.getElementsByTagName("head")[0].innerHTML=RegExp.$1;
+}
+if(_ce.match(Xinha.RE_body)){
+this._doc.getElementsByTagName("body")[0].innerHTML=RegExp.$1;
+}
+}else{
+var _d0=this.editorIsActivated();
+if(_d0){
+this.deactivateEditor();
+}
+var _d1=/<html>((.|\n)*?)<\/html>/i;
+_ce=_ce.replace(_d1,"$1");
+this._doc.open("text/html","replace");
+this._doc.write(_ce);
+this._doc.close();
+if(_d0){
+this.activateEditor();
+}
+this.setEditorEvents();
+return true;
+}
+};
+Xinha.prototype.setEditorEvents=function(){
+var _d2=this;
+var doc=this._doc;
+_d2.whenDocReady(function(){
+Xinha._addEvents(doc,["mousedown"],function(){
+_d2.activateEditor();
+return true;
+});
+Xinha._addEvents(doc,["keydown","keypress","mousedown","mouseup","drag"],function(_d4){
+return _d2._editorEvent(Xinha.is_ie?_d2._iframe.contentWindow.event:_d4);
+});
+for(var i in _d2.plugins){
+var _d6=_d2.plugins[i].instance;
+Xinha.refreshPlugin(_d6);
+}
+if(typeof _d2._onGenerate=="function"){
+_d2._onGenerate();
+}
+Xinha.addDom0Event(window,"resize",function(e){
+_d2.sizeEditor();
+});
+_d2.removeLoadingMessage();
+});
+};
+Xinha.prototype.registerPlugin=function(){
+var _d8=arguments[0];
+if(_d8===null||typeof _d8=="undefined"||(typeof _d8=="string"&&eval("typeof "+_d8)=="undefined")){
+return false;
+}
+var _d9=[];
+for(var i=1;i<arguments.length;++i){
+_d9.push(arguments[i]);
+}
+return this.registerPlugin2(_d8,_d9);
+};
+Xinha.prototype.registerPlugin2=function(_db,_dc){
+if(typeof _db=="string"){
+_db=eval(_db);
+}
+if(typeof _db=="undefined"){
+return false;
+}
+var obj=new _db(this,_dc);
+if(obj){
+var _de={};
+var _df=_db._pluginInfo;
+for(var i in _df){
+_de[i]=_df[i];
+}
+_de.instance=obj;
+_de.args=_dc;
+this.plugins[_db._pluginInfo.name]=_de;
+return obj;
+}else{
+alert("Can't register plugin "+_db.toString()+".");
+}
+};
+Xinha.getPluginDir=function(_e1){
+return _editor_url+"plugins/"+_e1;
+};
+Xinha.loadPlugin=function(_e2,_e3,_e4){
+if(eval("typeof "+_e2)!="undefined"){
+if(_e3){
+_e3(_e2);
+}
+return true;
+}
+if(!_e4){
+var dir=this.getPluginDir(_e2);
+var _e6=_e2.replace(/([a-z])([A-Z])([a-z])/g,function(str,l1,l2,l3){
+return l1+"-"+l2.toLowerCase()+l3;
+}).toLowerCase()+".js";
+_e4=dir+"/"+_e6;
+}
+Xinha._loadback(_e4,_e3?function(){
+_e3(_e2);
+}:null);
+return false;
+};
+Xinha._pluginLoadStatus={};
+Xinha.loadPlugins=function(_eb,_ec){
+var _ed=true;
+var _ee=Xinha.cloneObject(_eb);
+while(_ee.length){
+var p=_ee.pop();
+if(typeof Xinha._pluginLoadStatus[p]=="undefined"){
+Xinha._pluginLoadStatus[p]="loading";
+Xinha.loadPlugin(p,function(_f0){
+if(eval("typeof "+_f0)!="undefined"){
+Xinha._pluginLoadStatus[_f0]="ready";
+}else{
+Xinha._pluginLoadStatus[_f0]="failed";
+}
+});
+_ed=false;
+}else{
+switch(Xinha._pluginLoadStatus[p]){
+case "failed":
+case "ready":
+break;
+default:
+_ed=false;
+break;
+}
+}
+}
+if(_ed){
+return true;
+}
+if(_ec){
+setTimeout(function(){
+if(Xinha.loadPlugins(_eb,_ec)){
+_ec();
+}
+},150);
+}
+return _ed;
+};
+Xinha.refreshPlugin=function(_f1){
+if(_f1&&typeof _f1.onGenerate=="function"){
+_f1.onGenerate();
+}
+if(_f1&&typeof _f1.onGenerateOnce=="function"){
+_f1.onGenerateOnce();
+_f1.onGenerateOnce=null;
+}
+};
+Xinha.prototype.firePluginEvent=function(_f2){
+var _f3=[];
+for(var i=1;i<arguments.length;i++){
+_f3[i-1]=arguments[i];
+}
+for(var i in this.plugins){
+var _f5=this.plugins[i].instance;
+if(_f5==this._browserSpecificPlugin){
+continue;
+}
+if(_f5&&typeof _f5[_f2]=="function"){
+if(_f5[_f2].apply(_f5,_f3)){
+return true;
+}
+}
+}
+var _f5=this._browserSpecificPlugin;
+if(_f5&&typeof _f5[_f2]=="function"){
+if(_f5[_f2].apply(_f5,_f3)){
+return true;
+}
+}
+return false;
+};
+Xinha.loadStyle=function(_f6,_f7){
+var url=_editor_url||"";
+if(typeof _f7!="undefined"){
+url+="plugins/"+_f7+"/";
+}
+url+=_f6;
+if(/^\//.test(_f6)){
+url=_f6;
+}
+var _f9=document.getElementsByTagName("head")[0];
+var _fa=document.createElement("link");
+_fa.rel="stylesheet";
+_fa.href=url;
+_f9.appendChild(_fa);
+};
+Xinha.loadStyle(typeof _editor_css=="string"?_editor_css:"Xinha.css");
+Xinha.prototype.debugTree=function(){
+var ta=document.createElement("textarea");
+ta.style.width="100%";
+ta.style.height="20em";
+ta.value="";
+function debug(_fc,str){
+for(;--_fc>=0;){
+ta.value+=" ";
+}
+ta.value+=str+"\n";
+}
+function _dt(_fe,_ff){
+var tag=_fe.tagName.toLowerCase(),i;
+var ns=Xinha.is_ie?_fe.scopeName:_fe.prefix;
+debug(_ff,"- "+tag+" ["+ns+"]");
+for(i=_fe.firstChild;i;i=i.nextSibling){
+if(i.nodeType==1){
+_dt(i,_ff+2);
+}
+}
+}
+_dt(this._doc.body,0);
+document.body.appendChild(ta);
+};
+Xinha.getInnerText=function(el){
+var txt="",i;
+for(i=el.firstChild;i;i=i.nextSibling){
+if(i.nodeType==3){
+txt+=i.data;
+}else{
+if(i.nodeType==1){
+txt+=Xinha.getInnerText(i);
+}
+}
+}
+return txt;
+};
+Xinha.prototype._wordClean=function(){
+var _104=this;
+var _105={empty_tags:0,mso_class:0,mso_style:0,mso_xmlel:0,orig_len:this._doc.body.innerHTML.length,T:(new Date()).getTime()};
+var _106={empty_tags:"Empty tags removed: ",mso_class:"MSO class names removed: ",mso_style:"MSO inline style removed: ",mso_xmlel:"MSO XML elements stripped: "};
+function showStats(){
+var txt="Xinha word cleaner stats: \n\n";
+for(var i in _105){
+if(_106[i]){
+txt+=_106[i]+_105[i]+"\n";
+}
+}
+txt+="\nInitial document length: "+_105.orig_len+"\n";
+txt+="Final document length: "+_104._doc.body.innerHTML.length+"\n";
+txt+="Clean-up took "+(((new Date()).getTime()-_105.T)/1000)+" seconds";
+alert(txt);
+}
+function clearClass(node){
+var newc=node.className.replace(/(^|\s)mso.*?(\s|$)/ig," ");
+if(newc!=node.className){
+node.className=newc;
+if(!(/\S/.test(node.className))){
+node.removeAttribute("className");
+++_105.mso_class;
+}
+}
+}
+function clearStyle(node){
+var _10c=node.style.cssText.split(/\s*;\s*/);
+for(var i=_10c.length;--i>=0;){
+if((/^mso|^tab-stops/i.test(_10c[i]))||(/^margin\s*:\s*0..\s+0..\s+0../i.test(_10c[i]))){
+++_105.mso_style;
+_10c.splice(i,1);
+}
+}
+node.style.cssText=_10c.join("; ");
+}
+var _10e=null;
+if(Xinha.is_ie){
+_10e=function(el){
+el.outerHTML=Xinha.htmlEncode(el.innerText);
+++_105.mso_xmlel;
+};
+}else{
+_10e=function(el){
+var txt=document.createTextNode(Xinha.getInnerText(el));
+el.parentNode.insertBefore(txt,el);
+Xinha.removeFromParent(el);
+++_105.mso_xmlel;
+};
+}
+function checkEmpty(el){
+if(/^(span|b|strong|i|em|font|div|p)$/i.test(el.tagName)&&!el.firstChild){
+Xinha.removeFromParent(el);
+++_105.empty_tags;
+}
+}
+function parseTree(root){
+var tag=root.tagName.toLowerCase(),i,next;
+if((Xinha.is_ie&&root.scopeName!="HTML")||(!Xinha.is_ie&&(/:/.test(tag)))){
+_10e(root);
+return false;
+}else{
+clearClass(root);
+clearStyle(root);
+for(i=root.firstChild;i;i=next){
+next=i.nextSibling;
+if(i.nodeType==1&&parseTree(i)){
+checkEmpty(i);
+}
+}
+}
+return true;
+}
+parseTree(this._doc.body);
+this.updateToolbar();
+};
+Xinha.prototype._clearFonts=function(){
+var D=this.getInnerHTML();
+if(confirm(Xinha._lc("Would you like to clear font typefaces?"))){
+D=D.replace(/face="[^"]*"/gi,"");
+D=D.replace(/font-family:[^;}"']+;?/gi,"");
+}
+if(confirm(Xinha._lc("Would you like to clear font sizes?"))){
+D=D.replace(/size="[^"]*"/gi,"");
+D=D.replace(/font-size:[^;}"']+;?/gi,"");
+}
+if(confirm(Xinha._lc("Would you like to clear font colours?"))){
+D=D.replace(/color="[^"]*"/gi,"");
+D=D.replace(/([^-])color:[^;}"']+;?/gi,"$1");
+}
+D=D.replace(/(style|class)="\s*"/gi,"");
+D=D.replace(/<(font|span)\s*>/gi,"");
+this.setHTML(D);
+this.updateToolbar();
+};
+Xinha.prototype._splitBlock=function(){
+this._doc.execCommand("formatblock",false,"div");
+};
+Xinha.prototype.forceRedraw=function(){
+this._doc.body.style.visibility="hidden";
+this._doc.body.style.visibility="";
+};
+Xinha.prototype.focusEditor=function(){
+switch(this._editMode){
+case "wysiwyg":
+try{
+if(Xinha._someEditorHasBeenActivated){
+this.activateEditor();
+this._iframe.contentWindow.focus();
+}
+}
+catch(ex){
+}
+break;
+case "textmode":
+try{
+this._textArea.focus();
+}
+catch(e){
+}
+break;
+default:
+alert("ERROR: mode "+this._editMode+" is not defined");
+}
+return this._doc;
+};
+Xinha.prototype._undoTakeSnapshot=function(){
+++this._undoPos;
+if(this._undoPos>=this.config.undoSteps){
+this._undoQueue.shift();
+--this._undoPos;
+}
+var take=true;
+var txt=this.getInnerHTML();
+if(this._undoPos>0){
+take=(this._undoQueue[this._undoPos-1]!=txt);
+}
+if(take){
+this._undoQueue[this._undoPos]=txt;
+}else{
+this._undoPos--;
+}
+};
+Xinha.prototype.undo=function(){
+if(this._undoPos>0){
+var txt=this._undoQueue[--this._undoPos];
+if(txt){
+this.setHTML(txt);
+}else{
+++this._undoPos;
+}
+}
+};
+Xinha.prototype.redo=function(){
+if(this._undoPos<this._undoQueue.length-1){
+var txt=this._undoQueue[++this._undoPos];
+if(txt){
+this.setHTML(txt);
+}else{
+--this._undoPos;
+}
+}
+};
+Xinha.prototype.disableToolbar=function(_11a){
+if(this._timerToolbar){
+clearTimeout(this._timerToolbar);
+}
+if(typeof _11a=="undefined"){
+_11a=[];
+}else{
+if(typeof _11a!="object"){
+_11a=[_11a];
+}
+}
+for(var i in this._toolbarObjects){
+var btn=this._toolbarObjects[i];
+if(_11a.contains(i)){
+continue;
+}
+if(typeof (btn.state)!="function"){
+continue;
+}
+btn.state("enabled",false);
+}
+};
+Xinha.prototype.enableToolbar=function(){
+this.updateToolbar();
+};
+if(!Array.prototype.contains){
+Array.prototype.contains=function(_11d){
+var _11e=this;
+for(var i=0;i<_11e.length;i++){
+if(_11d==_11e[i]){
+return true;
+}
+}
+return false;
+};
+}
+if(!Array.prototype.indexOf){
+Array.prototype.indexOf=function(_120){
+var _121=this;
+for(var i=0;i<_121.length;i++){
+if(_120==_121[i]){
+return i;
+}
+}
+return null;
+};
+}
+Xinha.prototype.updateToolbar=function(_123){
+var doc=this._doc;
+var text=(this._editMode=="textmode");
+var _126=null;
+if(!text){
+_126=this.getAllAncestors();
+if(this.config.statusBar&&!_123){
+this._statusBarTree.innerHTML=Xinha._lc("Path")+": ";
+for(var i=_126.length;--i>=0;){
+var el=_126[i];
+if(!el){
+continue;
+}
+var a=document.createElement("a");
+a.href="javascript:void(0)";
+a.el=el;
+a.editor=this;
+Xinha.addDom0Event(a,"click",function(){
+this.blur();
+this.editor.selectNodeContents(this.el);
+this.editor.updateToolbar(true);
+return false;
+});
+Xinha.addDom0Event(a,"contextmenu",function(){
+this.blur();
+var info="Inline style:\n\n";
+info+=this.el.style.cssText.split(/;\s*/).join(";\n");
+alert(info);
+return false;
+});
+var txt=el.tagName.toLowerCase();
+if(typeof el.style!="undefined"){
+a.title=el.style.cssText;
+}
+if(el.id){
+txt+="#"+el.id;
+}
+if(el.className){
+txt+="."+el.className;
+}
+a.appendChild(document.createTextNode(txt));
+this._statusBarTree.appendChild(a);
+if(i!==0){
+this._statusBarTree.appendChild(document.createTextNode(String.fromCharCode(187)));
+}
+}
+}
+}
+for(var cmd in this._toolbarObjects){
+var btn=this._toolbarObjects[cmd];
+var _12e=true;
+if(typeof (btn.state)!="function"){
+continue;
+}
+if(btn.context&&!text){
+_12e=false;
+var _12f=btn.context;
+var _130=[];
+if(/(.*)\[(.*?)\]/.test(_12f)){
+_12f=RegExp.$1;
+_130=RegExp.$2.split(",");
+}
+_12f=_12f.toLowerCase();
+var _131=(_12f=="*");
+for(var k=0;k<_126.length;++k){
+if(!_126[k]){
+continue;
+}
+if(_131||(_126[k].tagName.toLowerCase()==_12f)){
+_12e=true;
+var _133=null;
+var att=null;
+var comp=null;
+var _136=null;
+for(var ka=0;ka<_130.length;++ka){
+_133=_130[ka].match(/(.*)(==|!=|===|!==|>|>=|<|<=)(.*)/);
+att=_133[1];
+comp=_133[2];
+_136=_133[3];
+if(!eval(_126[k][att]+comp+_136)){
+_12e=false;
+break;
+}
+}
+if(_12e){
+break;
+}
+}
+}
+}
+btn.state("enabled",(!text||btn.text)&&_12e);
+if(typeof cmd=="function"){
+continue;
+}
+var _138=this.config.customSelects[cmd];
+if((!text||btn.text)&&(typeof _138!="undefined")){
+_138.refresh(this);
+continue;
+}
+switch(cmd){
+case "fontname":
+case "fontsize":
+if(!text){
+try{
+var _139=(""+doc.queryCommandValue(cmd)).toLowerCase();
+if(!_139){
+btn.element.selectedIndex=0;
+break;
+}
+var _13a=this.config[cmd];
+var _13b=0;
+for(var j in _13a){
+if((j.toLowerCase()==_139)||(_13a[j].substr(0,_139.length).toLowerCase()==_139)){
+btn.element.selectedIndex=_13b;
+throw "ok";
+}
+++_13b;
+}
+btn.element.selectedIndex=0;
+}
+catch(ex){
+}
+}
+break;
+case "formatblock":
+var _13d=[];
+for(var _13e in this.config.formatblock){
+if(typeof this.config.formatblock[_13e]=="string"){
+_13d[_13d.length]=this.config.formatblock[_13e];
+}
+}
+var _13f=this._getFirstAncestor(this.getSelection(),_13d);
+if(_13f){
+for(var x=0;x<_13d.length;x++){
+if(_13d[x].toLowerCase()==_13f.tagName.toLowerCase()){
+btn.element.selectedIndex=x;
+}
+}
+}else{
+btn.element.selectedIndex=0;
+}
+break;
+case "textindicator":
+if(!text){
+try{
+var _141=btn.element.style;
+_141.backgroundColor=Xinha._makeColor(doc.queryCommandValue(Xinha.is_ie?"backcolor":"hilitecolor"));
+if(/transparent/i.test(_141.backgroundColor)){
+_141.backgroundColor=Xinha._makeColor(doc.queryCommandValue("backcolor"));
+}
+_141.color=Xinha._makeColor(doc.queryCommandValue("forecolor"));
+_141.fontFamily=doc.queryCommandValue("fontname");
+_141.fontWeight=doc.queryCommandState("bold")?"bold":"normal";
+_141.fontStyle=doc.queryCommandState("italic")?"italic":"normal";
+}
+catch(ex){
+}
+}
+break;
+case "htmlmode":
+btn.state("active",text);
+break;
+case "lefttoright":
+case "righttoleft":
+var _142=this.getParentElement();
+while(_142&&!Xinha.isBlockElement(_142)){
+_142=_142.parentNode;
+}
+if(_142){
+btn.state("active",(_142.style.direction==((cmd=="righttoleft")?"rtl":"ltr")));
+}
+break;
+default:
+cmd=cmd.replace(/(un)?orderedlist/i,"insert$1orderedlist");
+try{
+btn.state("active",(!text&&doc.queryCommandState(cmd)));
+}
+catch(ex){
+}
+break;
+}
+}
+if(this._customUndo&&!this._timerUndo){
+this._undoTakeSnapshot();
+var _143=this;
+this._timerUndo=setTimeout(function(){
+_143._timerUndo=null;
+},this.config.undoTimeout);
+}
+if(0&&Xinha.is_gecko){
+var s=this.getSelection();
+if(s&&s.isCollapsed&&s.anchorNode&&s.anchorNode.parentNode.tagName.toLowerCase()!="body"&&s.anchorNode.nodeType==3&&s.anchorOffset==s.anchorNode.length&&!(s.anchorNode.parentNode.nextSibling&&s.anchorNode.parentNode.nextSibling.nodeType==3)&&!Xinha.isBlockElement(s.anchorNode.parentNode)){
+try{
+s.anchorNode.parentNode.parentNode.insertBefore(this._doc.createTextNode("\t"),s.anchorNode.parentNode.nextSibling);
+}
+catch(ex){
+}
+}
+}
+for(var _145 in this.plugins){
+var _146=this.plugins[_145].instance;
+if(_146&&typeof _146.onUpdateToolbar=="function"){
+_146.onUpdateToolbar();
+}
+}
+};
+Xinha.prototype.getAllAncestors=function(){
+var p=this.getParentElement();
+var a=[];
+while(p&&(p.nodeType==1)&&(p.tagName.toLowerCase()!="body")){
+a.push(p);
+p=p.parentNode;
+}
+a.push(this._doc.body);
+return a;
+};
+Xinha.prototype._getFirstAncestor=function(sel,_14a){
+var prnt=this.activeElement(sel);
+if(prnt===null){
+try{
+prnt=(Xinha.is_ie?this.createRange(sel).parentElement():this.createRange(sel).commonAncestorContainer);
+}
+catch(ex){
+return null;
+}
+}
+if(typeof _14a=="string"){
+_14a=[_14a];
+}
+while(prnt){
+if(prnt.nodeType==1){
+if(_14a===null){
+return prnt;
+}
+if(_14a.contains(prnt.tagName.toLowerCase())){
+return prnt;
+}
+if(prnt.tagName.toLowerCase()=="body"){
+break;
+}
+if(prnt.tagName.toLowerCase()=="table"){
+break;
+}
+}
+prnt=prnt.parentNode;
+}
+return null;
+};
+Xinha.prototype._getAncestorBlock=function(sel){
+var prnt=(Xinha.is_ie?this.createRange(sel).parentElement:this.createRange(sel).commonAncestorContainer);
+while(prnt&&(prnt.nodeType==1)){
+switch(prnt.tagName.toLowerCase()){
+case "div":
+case "p":
+case "address":
+case "blockquote":
+case "center":
+case "del":
+case "ins":
+case "pre":
+case "h1":
+case "h2":
+case "h3":
+case "h4":
+case "h5":
+case "h6":
+case "h7":
+return prnt;
+case "body":
+case "noframes":
+case "dd":
+case "li":
+case "th":
+case "td":
+case "noscript":
+return null;
+default:
+break;
+}
+}
+return null;
+};
+Xinha.prototype._createImplicitBlock=function(type){
+var sel=this.getSelection();
+if(Xinha.is_ie){
+sel.empty();
+}else{
+sel.collapseToStart();
+}
+var rng=this.createRange(sel);
+};
+Xinha.prototype.surroundHTML=function(_151,_152){
+var html=this.getSelectedHTML();
+this.insertHTML(_151+html+_152);
+};
+Xinha.prototype.hasSelectedText=function(){
+return this.getSelectedHTML()!=="";
+};
+Xinha.prototype._comboSelected=function(el,txt){
+this.focusEditor();
+var _156=el.options[el.selectedIndex].value;
+switch(txt){
+case "fontname":
+case "fontsize":
+this.execCommand(txt,false,_156);
+break;
+case "formatblock":
+if(!_156){
+this.updateToolbar();
+break;
+}
+if(!Xinha.is_gecko||_156!=="blockquote"){
+_156="<"+_156+">";
+}
+this.execCommand(txt,false,_156);
+break;
+default:
+var _157=this.config.customSelects[txt];
+if(typeof _157!="undefined"){
+_157.action(this);
+}else{
+alert("FIXME: combo box "+txt+" not implemented");
+}
+break;
+}
+};
+Xinha.prototype._colorSelector=function(_158){
+var _159=this;
+if(Xinha.is_gecko){
+try{
+_159._doc.execCommand("useCSS",false,false);
+_159._doc.execCommand("styleWithCSS",false,true);
+}
+catch(ex){
+}
+}
+var btn=_159._toolbarObjects[_158].element;
+var _15b;
+if(_158=="hilitecolor"){
+if(Xinha.is_ie){
+_158="backcolor";
+_15b=Xinha._colorToRgb(_159._doc.queryCommandValue("backcolor"));
+}else{
+_15b=Xinha._colorToRgb(_159._doc.queryCommandValue("hilitecolor"));
+}
+}else{
+_15b=Xinha._colorToRgb(_159._doc.queryCommandValue("forecolor"));
+}
+var _15c=function(_15d){
+_159._doc.execCommand(_158,false,_15d);
+};
+if(Xinha.is_ie){
+var _15e=_159.createRange(_159.getSelection());
+_15c=function(_15f){
+_15e.select();
+_159._doc.execCommand(_158,false,_15f);
+};
+}
+var _160=new Xinha.colorPicker({cellsize:_159.config.colorPickerCellSize,callback:_15c,granularity:_159.config.colorPickerGranularity,websafe:_159.config.colorPickerWebSafe,savecolors:_159.config.colorPickerSaveColors});
+_160.open(_159.config.colorPickerPosition,btn,_15b);
+};
+Xinha.prototype.execCommand=function(_161,UI,_163){
+var _164=this;
+this.focusEditor();
+_161=_161.toLowerCase();
+if(this.firePluginEvent("onExecCommand",_161,UI,_163)){
+this.updateToolbar();
+return false;
+}
+switch(_161){
+case "htmlmode":
+this.setMode();
+break;
+case "hilitecolor":
+case "forecolor":
+this._colorSelector(_161);
+break;
+case "createlink":
+this._createLink();
+break;
+case "undo":
+case "redo":
+if(this._customUndo){
+this[_161]();
+}else{
+this._doc.execCommand(_161,UI,_163);
+}
+break;
+case "inserttable":
+this._insertTable();
+break;
+case "insertimage":
+this._insertImage();
+break;
+case "about":
+this._popupDialog(_164.config.URIs.about,null,this);
+break;
+case "showhelp":
+this._popupDialog(_164.config.URIs.help,null,this);
+break;
+case "killword":
+this._wordClean();
+break;
+case "cut":
+case "copy":
+case "paste":
+this._doc.execCommand(_161,UI,_163);
+if(this.config.killWordOnPaste){
+this._wordClean();
+}
+break;
+case "lefttoright":
+case "righttoleft":
+if(this.config.changeJustifyWithDirection){
+this._doc.execCommand((_161=="righttoleft")?"justifyright":"justifyleft",UI,_163);
+}
+var dir=(_161=="righttoleft")?"rtl":"ltr";
+var el=this.getParentElement();
+while(el&&!Xinha.isBlockElement(el)){
+el=el.parentNode;
+}
+if(el){
+if(el.style.direction==dir){
+el.style.direction="";
+}else{
+el.style.direction=dir;
+}
+}
+break;
+case "justifyleft":
+case "justifyright":
+_161.match(/^justify(.*)$/);
+var ae=this.activeElement(this.getSelection());
+if(ae&&ae.tagName.toLowerCase()=="img"){
+ae.align=ae.align==RegExp.$1?"":RegExp.$1;
+}else{
+this._doc.execCommand(_161,UI,_163);
+}
+break;
+default:
+try{
+this._doc.execCommand(_161,UI,_163);
+}
+catch(ex){
+if(this.config.debug){
+alert(ex+"\n\nby execCommand("+_161+");");
+}
+}
+break;
+}
+this.updateToolbar();
+return false;
+};
+Xinha.prototype._editorEvent=function(ev){
+var _169=this;
+if(typeof _169._textArea["on"+ev.type]=="function"){
+_169._textArea["on"+ev.type]();
+}
+if(this.isKeyEvent(ev)){
+if(_169.firePluginEvent("onKeyPress",ev)){
+return false;
+}
+if(this.isShortCut(ev)){
+this._shortCuts(ev);
+}
+}
+if(ev.type=="mousedown"){
+if(_169.firePluginEvent("onMouseDown",ev)){
+return false;
+}
+}
+if(_169._timerToolbar){
+clearTimeout(_169._timerToolbar);
+}
+_169._timerToolbar=setTimeout(function(){
+_169.updateToolbar();
+_169._timerToolbar=null;
+},250);
+};
+Xinha.prototype._shortCuts=function(ev){
+var key=this.getKey(ev).toLowerCase();
+var cmd=null;
+var _16d=null;
+switch(key){
+case "b":
+cmd="bold";
+break;
+case "i":
+cmd="italic";
+break;
+case "u":
+cmd="underline";
+break;
+case "s":
+cmd="strikethrough";
+break;
+case "l":
+cmd="justifyleft";
+break;
+case "e":
+cmd="justifycenter";
+break;
+case "r":
+cmd="justifyright";
+break;
+case "j":
+cmd="justifyfull";
+break;
+case "z":
+cmd="undo";
+break;
+case "y":
+cmd="redo";
+break;
+case "v":
+cmd="paste";
+break;
+case "n":
+cmd="formatblock";
+_16d="p";
+break;
+case "0":
+cmd="killword";
+break;
+case "1":
+case "2":
+case "3":
+case "4":
+case "5":
+case "6":
+cmd="formatblock";
+_16d="h"+key;
+break;
+}
+if(cmd){
+this.execCommand(cmd,false,_16d);
+Xinha._stopEvent(ev);
+}
+};
+Xinha.prototype.convertNode=function(el,_16f){
+var _170=this._doc.createElement(_16f);
+while(el.firstChild){
+_170.appendChild(el.firstChild);
+}
+return _170;
+};
+Xinha.prototype.scrollToElement=function(e){
+if(!e){
+e=this.getParentElement();
+if(!e){
+return;
+}
+}
+var _172=Xinha.getElementTopLeft(e);
+this._iframe.contentWindow.scrollTo(_172.left,_172.top);
+};
+Xinha.prototype.getHTML=function(){
+var html="";
+switch(this._editMode){
+case "wysiwyg":
+if(!this.config.fullPage){
+html=Xinha.getHTML(this._doc.body,false,this);
+}else{
+html=this.doctype+"\n"+Xinha.getHTML(this._doc.documentElement,true,this);
+}
+break;
+case "textmode":
+html=this._textArea.value;
+break;
+default:
+alert("Mode <"+this._editMode+"> not defined!");
+return false;
+}
+return html;
+};
+Xinha.prototype.outwardHtml=function(html){
+for(var i in this.plugins){
+var _176=this.plugins[i].instance;
+if(_176&&typeof _176.outwardHtml=="function"){
+html=_176.outwardHtml(html);
+}
+}
+html=html.replace(/<(\/?)b(\s|>|\/)/ig,"<$1strong$2");
+html=html.replace(/<(\/?)i(\s|>|\/)/ig,"<$1em$2");
+html=html.replace(/<(\/?)strike(\s|>|\/)/ig,"<$1del$2");
+html=html.replace("onclick=\"try{if(document.designMode &amp;&amp; document.designMode == 'on') return false;}catch(e){} window.open(","onclick=\"window.open(");
+var _177=location.href.replace(/(https?:\/\/[^\/]*)\/.*/,"$1")+"/";
+html=html.replace(/https?:\/\/null\//g,_177);
+html=html.replace(/((href|src|background)=[\'\"])\/+/ig,"$1"+_177);
+html=this.outwardSpecialReplacements(html);
+html=this.fixRelativeLinks(html);
+if(this.config.sevenBitClean){
+html=html.replace(/[^ -~\r\n\t]/g,function(c){
+return "&#"+c.charCodeAt(0)+";";
+});
+}
+html=html.replace(/(<script[^>]*)(freezescript)/gi,"$1javascript");
+if(this.config.fullPage){
+html=Xinha.stripCoreCSS(html);
+}
+return html;
+};
+Xinha.prototype.inwardHtml=function(html){
+for(var i in this.plugins){
+var _17b=this.plugins[i].instance;
+if(_17b&&typeof _17b.inwardHtml=="function"){
+html=_17b.inwardHtml(html);
+}
+}
+html=html.replace(/<(\/?)del(\s|>|\/)/ig,"<$1strike$2");
+html=html.replace("onclick=\"window.open(","onclick=\"try{if(document.designMode &amp;&amp; document.designMode == 'on') return false;}catch(e){} window.open(");
+html=this.inwardSpecialReplacements(html);
+html=html.replace(/(<script[^>]*)(javascript)/gi,"$1freezescript");
+var _17c=new RegExp("((href|src|background)=['\"])/+","gi");
+html=html.replace(_17c,"$1"+location.href.replace(/(https?:\/\/[^\/]*)\/.*/,"$1")+"/");
+html=this.fixRelativeLinks(html);
+if(this.config.fullPage){
+html=Xinha.addCoreCSS(html);
+}
+return html;
+};
+Xinha.prototype.outwardSpecialReplacements=function(html){
+for(var i in this.config.specialReplacements){
+var from=this.config.specialReplacements[i];
+var to=i;
+if(typeof from.replace!="function"||typeof to.replace!="function"){
+continue;
+}
+var reg=new RegExp(from.replace(Xinha.RE_Specials,"\\$1"),"g");
+html=html.replace(reg,to.replace(/\$/g,"$$$$"));
+}
+return html;
+};
+Xinha.prototype.inwardSpecialReplacements=function(html){
+for(var i in this.config.specialReplacements){
+var from=i;
+var to=this.config.specialReplacements[i];
+if(typeof from.replace!="function"||typeof to.replace!="function"){
+continue;
+}
+var reg=new RegExp(from.replace(Xinha.RE_Specials,"\\$1"),"g");
+html=html.replace(reg,to.replace(/\$/g,"$$$$"));
+}
+return html;
+};
+Xinha.prototype.fixRelativeLinks=function(html){
+if(typeof this.config.expandRelativeUrl!="undefined"&&this.config.expandRelativeUrl){
+var src=html.match(/(src|href)="([^"]*)"/gi);
+}
+var b=document.location.href;
+if(src){
+var url,url_m,relPath,base_m,absPath;
+for(var i=0;i<src.length;++i){
+url=src[i].match(/(src|href)="([^"]*)"/i);
+url_m=url[2].match(/\.\.\//g);
+if(url_m){
+relPath=new RegExp("(.*?)(([^/]*/){"+url_m.length+"})[^/]*$");
+base_m=b.match(relPath);
+absPath=url[2].replace(/(\.\.\/)*/,base_m[1]);
+html=html.replace(new RegExp(url[2].replace(Xinha.RE_Specials,"\\$1")),absPath);
+}
+}
+}
+if(typeof this.config.stripSelfNamedAnchors!="undefined"&&this.config.stripSelfNamedAnchors){
+var _18c=new RegExp(document.location.href.replace(/&/g,"&amp;").replace(Xinha.RE_Specials,"\\$1")+"(#[^'\" ]*)","g");
+html=html.replace(_18c,"$1");
+}
+if(typeof this.config.stripBaseHref!="undefined"&&this.config.stripBaseHref){
+var _18d=null;
+if(typeof this.config.baseHref!="undefined"&&this.config.baseHref!==null){
+_18d=new RegExp("((href|src|background)=\")("+this.config.baseHref.replace(Xinha.RE_Specials,"\\$1")+")","g");
+}else{
+_18d=new RegExp("((href|src|background)=\")("+document.location.href.replace(/^(https?:\/\/[^\/]*)(.*)/,"$1").replace(Xinha.RE_Specials,"\\$1")+")","g");
+}
+html=html.replace(_18d,"$1");
+}
+return html;
+};
+Xinha.prototype.getInnerHTML=function(){
+if(!this._doc.body){
+return "";
+}
+var html="";
+switch(this._editMode){
+case "wysiwyg":
+if(!this.config.fullPage){
+html=this._doc.body.innerHTML;
+}else{
+html=this.doctype+"\n"+this._doc.documentElement.innerHTML;
+}
+break;
+case "textmode":
+html=this._textArea.value;
+break;
+default:
+alert("Mode <"+this._editMode+"> not defined!");
+return false;
+}
+return html;
+};
+Xinha.prototype.setHTML=function(html){
+if(!this.config.fullPage){
+this._doc.body.innerHTML=html;
+}else{
+this.setFullHTML(html);
+}
+this._textArea.value=html;
+};
+Xinha.prototype.setDoctype=function(_190){
+this.doctype=_190;
+};
+Xinha._object=null;
+Xinha.cloneObject=function(obj){
+if(!obj){
+return null;
+}
+var _192={};
+if(obj.constructor.toString().match(/\s*function Array\(/)){
+_192=obj.constructor();
+}
+if(obj.constructor.toString().match(/\s*function Function\(/)){
+_192=obj;
+}else{
+for(var n in obj){
+var node=obj[n];
+if(typeof node=="object"){
+_192[n]=Xinha.cloneObject(node);
+}else{
+_192[n]=node;
+}
+}
+}
+return _192;
+};
+Xinha.checkSupportedBrowser=function(){
+if(Xinha.is_gecko){
+if(navigator.productSub<20021201){
+alert("You need at least Mozilla-1.3 Alpha.\nSorry, your Gecko is not supported.");
+return false;
+}
+if(navigator.productSub<20030210){
+alert("Mozilla < 1.3 Beta is not supported!\nI'll try, though, but it might not work.");
+}
+}
+return Xinha.is_gecko||Xinha.ie_version>=5.5;
+};
+Xinha._eventFlushers=[];
+Xinha.flushEvents=function(){
+var x=0;
+var e=Xinha._eventFlushers.pop();
+while(e){
+try{
+if(e.length==3){
+Xinha._removeEvent(e[0],e[1],e[2]);
+x++;
+}else{
+if(e.length==2){
+e[0]["on"+e[1]]=null;
+e[0]._xinha_dom0Events[e[1]]=null;
+x++;
+}
+}
+}
+catch(ex){
+}
+e=Xinha._eventFlushers.pop();
+}
+};
+if(document.addEventListener){
+Xinha._addEvent=function(el,_198,func){
+el.addEventListener(_198,func,true);
+Xinha._eventFlushers.push([el,_198,func]);
+};
+Xinha._removeEvent=function(el,_19b,func){
+el.removeEventListener(_19b,func,true);
+};
+Xinha._stopEvent=function(ev){
+ev.preventDefault();
+ev.stopPropagation();
+};
+}else{
+if(document.attachEvent){
+Xinha._addEvent=function(el,_19f,func){
+el.attachEvent("on"+_19f,func);
+Xinha._eventFlushers.push([el,_19f,func]);
+};
+Xinha._removeEvent=function(el,_1a2,func){
+el.detachEvent("on"+_1a2,func);
+};
+Xinha._stopEvent=function(ev){
+try{
+ev.cancelBubble=true;
+ev.returnValue=false;
+}
+catch(ex){
+}
+};
+}else{
+Xinha._addEvent=function(el,_1a6,func){
+alert("_addEvent is not supported");
+};
+Xinha._removeEvent=function(el,_1a9,func){
+alert("_removeEvent is not supported");
+};
+Xinha._stopEvent=function(ev){
+alert("_stopEvent is not supported");
+};
+}
+}
+Xinha._addEvents=function(el,evs,func){
+for(var i=evs.length;--i>=0;){
+Xinha._addEvent(el,evs[i],func);
+}
+};
+Xinha._removeEvents=function(el,evs,func){
+for(var i=evs.length;--i>=0;){
+Xinha._removeEvent(el,evs[i],func);
+}
+};
+Xinha.addDom0Event=function(el,ev,fn){
+Xinha._prepareForDom0Events(el,ev);
+el._xinha_dom0Events[ev].unshift(fn);
+};
+Xinha.prependDom0Event=function(el,ev,fn){
+Xinha._prepareForDom0Events(el,ev);
+el._xinha_dom0Events[ev].push(fn);
+};
+Xinha._prepareForDom0Events=function(el,ev){
+if(typeof el._xinha_dom0Events=="undefined"){
+el._xinha_dom0Events={};
+Xinha.freeLater(el,"_xinha_dom0Events");
+}
+if(typeof el._xinha_dom0Events[ev]=="undefined"){
+el._xinha_dom0Events[ev]=[];
+if(typeof el["on"+ev]=="function"){
+el._xinha_dom0Events[ev].push(el["on"+ev]);
+}
+el["on"+ev]=function(_1bc){
+var a=el._xinha_dom0Events[ev];
+var _1be=true;
+for(var i=a.length;--i>=0;){
+el._xinha_tempEventHandler=a[i];
+if(el._xinha_tempEventHandler(_1bc)===false){
+el._xinha_tempEventHandler=null;
+_1be=false;
+break;
+}
+el._xinha_tempEventHandler=null;
+}
+return _1be;
+};
+Xinha._eventFlushers.push([el,ev]);
+}
+};
+Xinha.prototype.notifyOn=function(ev,fn){
+if(typeof this._notifyListeners[ev]=="undefined"){
+this._notifyListeners[ev]=[];
+Xinha.freeLater(this,"_notifyListeners");
+}
+this._notifyListeners[ev].push(fn);
+};
+Xinha.prototype.notifyOf=function(ev,args){
+if(this._notifyListeners[ev]){
+for(var i=0;i<this._notifyListeners[ev].length;i++){
+this._notifyListeners[ev][i](ev,args);
+}
+}
+};
+Xinha._removeClass=function(el,_1c6){
+if(!(el&&el.className)){
+return;
+}
+var cls=el.className.split(" ");
+var ar=[];
+for(var i=cls.length;i>0;){
+if(cls[--i]!=_1c6){
+ar[ar.length]=cls[i];
+}
+}
+el.className=ar.join(" ");
+};
+Xinha._addClass=function(el,_1cb){
+Xinha._removeClass(el,_1cb);
+el.className+=" "+_1cb;
+};
+Xinha._hasClass=function(el,_1cd){
+if(!(el&&el.className)){
+return false;
+}
+var cls=el.className.split(" ");
+for(var i=cls.length;i>0;){
+if(cls[--i]==_1cd){
+return true;
+}
+}
+return false;
+};
+Xinha._blockTags=" body form textarea fieldset ul ol dl li div "+"p h1 h2 h3 h4 h5 h6 quote pre table thead "+"tbody tfoot tr td th iframe address blockquote ";
+Xinha.isBlockElement=function(el){
+return el&&el.nodeType==1&&(Xinha._blockTags.indexOf(" "+el.tagName.toLowerCase()+" ")!=-1);
+};
+Xinha._paraContainerTags=" body td th caption fieldset div";
+Xinha.isParaContainer=function(el){
+return el&&el.nodeType==1&&(Xinha._paraContainerTags.indexOf(" "+el.tagName.toLowerCase()+" ")!=-1);
+};
+Xinha._closingTags=" a abbr acronym address applet b bdo big blockquote button caption center cite code del dfn dir div dl em fieldset font form frameset h1 h2 h3 h4 h5 h6 i iframe ins kbd label legend map menu noframes noscript object ol optgroup pre q s samp script select small span strike strong style sub sup table textarea title tt u ul var ";
+Xinha.needsClosingTag=function(el){
+return el&&el.nodeType==1&&(Xinha._closingTags.indexOf(" "+el.tagName.toLowerCase()+" ")!=-1);
+};
+Xinha.htmlEncode=function(str){
+if(typeof str.replace=="undefined"){
+str=str.toString();
+}
+str=str.replace(/&/ig,"&amp;");
+str=str.replace(/</ig,"&lt;");
+str=str.replace(/>/ig,"&gt;");
+str=str.replace(/\xA0/g,"&nbsp;");
+str=str.replace(/\x22/g,"&quot;");
+return str;
+};
+Xinha.prototype.stripBaseURL=function(_1d4){
+if(this.config.baseHref===null||!this.config.stripBaseHref){
+return _1d4;
+}
+var _1d5=this.config.baseHref.replace(/^(https?:\/\/[^\/]+)(.*)$/,"$1");
+var _1d6=new RegExp(_1d5);
+return _1d4.replace(_1d6,"");
+};
+String.prototype.trim=function(){
+return this.replace(/^\s+/,"").replace(/\s+$/,"");
+};
+Xinha._makeColor=function(v){
+if(typeof v!="number"){
+return v;
+}
+var r=v&255;
+var g=(v>>8)&255;
+var b=(v>>16)&255;
+return "rgb("+r+","+g+","+b+")";
+};
+Xinha._colorToRgb=function(v){
+if(!v){
+return "";
+}
+var r,g,b;
+function hex(d){
+return (d<16)?("0"+d.toString(16)):d.toString(16);
+}
+if(typeof v=="number"){
+r=v&255;
+g=(v>>8)&255;
+b=(v>>16)&255;
+return "#"+hex(r)+hex(g)+hex(b);
+}
+if(v.substr(0,3)=="rgb"){
+var re=/rgb\s*\(\s*([0-9]+)\s*,\s*([0-9]+)\s*,\s*([0-9]+)\s*\)/;
+if(v.match(re)){
+r=parseInt(RegExp.$1,10);
+g=parseInt(RegExp.$2,10);
+b=parseInt(RegExp.$3,10);
+return "#"+hex(r)+hex(g)+hex(b);
+}
+return null;
+}
+if(v.substr(0,1)=="#"){
+return v;
+}
+return null;
+};
+Xinha.prototype._popupDialog=function(url,_1e0,init){
+Dialog(this.popupURL(url),_1e0,init);
+};
+Xinha.prototype.imgURL=function(file,_1e3){
+if(typeof _1e3=="undefined"){
+return _editor_url+file;
+}else{
+return _editor_url+"plugins/"+_1e3+"/img/"+file;
+}
+};
+Xinha.prototype.popupURL=function(file){
+var url="";
+if(file.match(/^plugin:\/\/(.*?)\/(.*)/)){
+var _1e6=RegExp.$1;
+var _1e7=RegExp.$2;
+if(!(/\.html$/.test(_1e7))){
+_1e7+=".html";
+}
+url=_editor_url+"plugins/"+_1e6+"/popups/"+_1e7;
+}else{
+if(file.match(/^\/.*?/)){
+url=file;
+}else{
+url=_editor_url+this.config.popupURL+file;
+}
+}
+return url;
+};
+Xinha.getElementById=function(tag,id){
+var el,i,objs=document.getElementsByTagName(tag);
+for(i=objs.length;--i>=0&&(el=objs[i]);){
+if(el.id==id){
+return el;
+}
+}
+return null;
+};
+Xinha.prototype._toggleBorders=function(){
+var _1eb=this._doc.getElementsByTagName("TABLE");
+if(_1eb.length!==0){
+if(!this.borders){
+this.borders=true;
+}else{
+this.borders=false;
+}
+for(var i=0;i<_1eb.length;i++){
+if(this.borders){
+Xinha._addClass(_1eb[i],"htmtableborders");
+}else{
+Xinha._removeClass(_1eb[i],"htmtableborders");
+}
+}
+}
+return true;
+};
+Xinha.addCoreCSS=function(html){
+var _1ee="<style title=\"Xinha Internal CSS\" type=\"text/css\">"+".htmtableborders, .htmtableborders td, .htmtableborders th {border : 1px dashed lightgrey ! important;}\n"+"html, body { border: 0px; } \n"+"body { background-color: #ffffff; } \n"+"</style>\n";
+if(html&&/<head>/i.test(html)){
+return html.replace(/<head>/i,"<head>"+_1ee);
+}else{
+if(html){
+return _1ee+html;
+}else{
+return _1ee;
+}
+}
+};
+Xinha.stripCoreCSS=function(html){
+return html.replace(/<style[^>]+title="Xinha Internal CSS"(.|\n)*?<\/style>/i,"");
+};
+Xinha.addClasses=function(el,_1f1){
+if(el!==null){
+var _1f2=el.className.trim().split(" ");
+var ours=_1f1.split(" ");
+for(var x=0;x<ours.length;x++){
+var _1f5=false;
+for(var i=0;_1f5===false&&i<_1f2.length;i++){
+if(_1f2[i]==ours[x]){
+_1f5=true;
+}
+}
+if(_1f5===false){
+_1f2[_1f2.length]=ours[x];
+}
+}
+el.className=_1f2.join(" ").trim();
+}
+};
+Xinha.removeClasses=function(el,_1f8){
+var _1f9=el.className.trim().split();
+var _1fa=[];
+var _1fb=_1f8.trim().split();
+for(var i=0;i<_1f9.length;i++){
+var _1fd=false;
+for(var x=0;x<_1fb.length&&!_1fd;x++){
+if(_1f9[i]==_1fb[x]){
+_1fd=true;
+}
+}
+if(!_1fd){
+_1fa[_1fa.length]=_1f9[i];
+}
+}
+return _1fa.join(" ");
+};
+Xinha.addClass=Xinha._addClass;
+Xinha.removeClass=Xinha._removeClass;
+Xinha._addClasses=Xinha.addClasses;
+Xinha._removeClasses=Xinha.removeClasses;
+Xinha._postback=function(url,data,_201){
+var req=null;
+req=Xinha.getXMLHTTPRequestObject();
+var _203="";
+if(typeof data=="string"){
+_203=data;
+}else{
+if(typeof data=="object"){
+for(var i in data){
+_203+=(_203.length?"&":"")+i+"="+encodeURIComponent(data[i]);
+}
+}
+}
+function callBack(){
+if(req.readyState==4){
+if(req.status==200||Xinha.isRunLocally&&req.status==0){
+if(typeof _201=="function"){
+_201(req.responseText,req);
+}
+}else{
+alert("An error has occurred: "+req.statusText);
+}
+}
+}
+req.onreadystatechange=callBack;
+req.open("POST",url,true);
+req.setRequestHeader("Content-Type","application/x-www-form-urlencoded; charset=UTF-8");
+req.send(_203);
+};
+Xinha._getback=function(url,_206){
+var req=null;
+req=Xinha.getXMLHTTPRequestObject();
+function callBack(){
+if(req.readyState==4){
+if(req.status==200||Xinha.isRunLocally&&req.status==0){
+_206(req.responseText,req);
+}else{
+alert("An error has occurred: "+req.statusText);
+}
+}
+}
+req.onreadystatechange=callBack;
+req.open("GET",url,true);
+req.send(null);
+};
+Xinha._geturlcontent=function(url){
+var req=null;
+req=Xinha.getXMLHTTPRequestObject();
+req.open("GET",url,false);
+req.send(null);
+if(req.status==200||Xinha.isRunLocally&&req.status==0){
+return req.responseText;
+}else{
+return "";
+}
+};
+if(typeof dump=="undefined"){
+function dump(o){
+var s="";
+for(var prop in o){
+s+=prop+" = "+o[prop]+"\n";
+}
+var x=window.open("","debugger");
+x.document.write("<pre>"+s+"</pre>");
+}
+}
+Xinha.arrayContainsArray=function(a1,a2){
+var _210=true;
+for(var x=0;x<a2.length;x++){
+var _212=false;
+for(var i=0;i<a1.length;i++){
+if(a1[i]==a2[x]){
+_212=true;
+break;
+}
+}
+if(!_212){
+_210=false;
+break;
+}
+}
+return _210;
+};
+Xinha.arrayFilter=function(a1,_215){
+var _216=[];
+for(var x=0;x<a1.length;x++){
+if(_215(a1[x])){
+_216[_216.length]=a1[x];
+}
+}
+return _216;
+};
+Xinha.uniq_count=0;
+Xinha.uniq=function(_218){
+return _218+Xinha.uniq_count++;
+};
+Xinha._loadlang=function(_219,url){
+var lang;
+if(typeof _editor_lcbackend=="string"){
+url=_editor_lcbackend;
+url=url.replace(/%lang%/,_editor_lang);
+url=url.replace(/%context%/,_219);
+}else{
+if(!url){
+if(_219!="Xinha"){
+url=_editor_url+"plugins/"+_219+"/lang/"+_editor_lang+".js";
+}else{
+url=_editor_url+"lang/"+_editor_lang+".js";
+}
+}
+}
+var _21c=Xinha._geturlcontent(url);
+if(_21c!==""){
+try{
+eval("lang = "+_21c);
+}
+catch(ex){
+alert("Error reading Language-File ("+url+"):\n"+Error.toString());
+lang={};
+}
+}else{
+lang={};
+}
+return lang;
+};
+Xinha._lc=function(_21d,_21e,_21f){
+var url,ret;
+if(typeof _21e=="object"&&_21e.url&&_21e.context){
+url=_21e.url+_editor_lang+".js";
+_21e=_21e.context;
+}
+var m=null;
+if(typeof _21d=="string"){
+m=_21d.match(/\$(.*?)=(.*?)\$/g);
+}
+if(m){
+if(!_21f){
+_21f={};
+}
+for(var i=0;i<m.length;i++){
+var n=m[i].match(/\$(.*?)=(.*?)\$/);
+_21f[n[1]]=n[2];
+_21d=_21d.replace(n[0],"$"+n[1]);
+}
+}
+if(_editor_lang=="en"){
+if(typeof _21d=="object"&&_21d.string){
+ret=_21d.string;
+}else{
+ret=_21d;
+}
+}else{
+if(typeof Xinha._lc_catalog=="undefined"){
+Xinha._lc_catalog=[];
+}
+if(typeof _21e=="undefined"){
+_21e="Xinha";
+}
+if(typeof Xinha._lc_catalog[_21e]=="undefined"){
+Xinha._lc_catalog[_21e]=Xinha._loadlang(_21e,url);
+}
+var key;
+if(typeof _21d=="object"&&_21d.key){
+key=_21d.key;
+}else{
+if(typeof _21d=="object"&&_21d.string){
+key=_21d.string;
+}else{
+key=_21d;
+}
+}
+if(typeof Xinha._lc_catalog[_21e][key]=="undefined"){
+if(_21e=="Xinha"){
+if(typeof _21d=="object"&&_21d.string){
+ret=_21d.string;
+}else{
+ret=_21d;
+}
+}else{
+return Xinha._lc(_21d,"Xinha",_21f);
+}
+}else{
+ret=Xinha._lc_catalog[_21e][key];
+}
+}
+if(typeof _21d=="object"&&_21d.replace){
+_21f=_21d.replace;
+}
+if(typeof _21f!="undefined"){
+for(var i in _21f){
+ret=ret.replace("$"+i,_21f[i]);
+}
+}
+return ret;
+};
+Xinha.hasDisplayedChildren=function(el){
+var _226=el.childNodes;
+for(var i=0;i<_226.length;i++){
+if(_226[i].tagName){
+if(_226[i].style.display!="none"){
+return true;
+}
+}
+}
+return false;
+};
+Xinha._loadback=function(Url,_229,_22a,_22b){
+var T=!Xinha.is_ie?"onload":"onreadystatechange";
+var S=document.createElement("script");
+S.type="text/javascript";
+S.src=Url;
+if(_229){
+S[T]=function(){
+if(Xinha.is_ie&&(!(/loaded|complete/.test(window.event.srcElement.readyState)))){
+return;
+}
+_229.call(_22a?_22a:this,_22b);
+S[T]=null;
+};
+}
+document.getElementsByTagName("head")[0].appendChild(S);
+};
+Xinha.collectionToArray=function(_22e){
+var _22f=[];
+for(var i=0;i<_22e.length;i++){
+_22f.push(_22e.item(i));
+}
+return _22f;
+};
+if(!Array.prototype.append){
+Array.prototype.append=function(a){
+for(var i=0;i<a.length;i++){
+this.push(a[i]);
+}
+return this;
+};
+}
+Xinha.makeEditors=function(_233,_234,_235){
+if(typeof _234=="function"){
+_234=_234();
+}
+var _236={};
+for(var x=0;x<_233.length;x++){
+var _238=new Xinha(_233[x],Xinha.cloneObject(_234));
+_238.registerPlugins(_235);
+_236[_233[x]]=_238;
+}
+return _236;
+};
+Xinha.startEditors=function(_239){
+for(var i in _239){
+if(_239[i].generate){
+_239[i].generate();
+}
+}
+};
+Xinha.prototype.registerPlugins=function(_23b){
+if(_23b){
+for(var i=0;i<_23b.length;i++){
+this.setLoadingMessage("Register plugin $plugin","Xinha",{"plugin":_23b[i]});
+this.registerPlugin(eval(_23b[i]));
+}
+}
+};
+Xinha.base64_encode=function(_23d){
+var _23e="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
+var _23f="";
+var chr1,chr2,chr3;
+var enc1,enc2,enc3,enc4;
+var i=0;
+do{
+chr1=_23d.charCodeAt(i++);
+chr2=_23d.charCodeAt(i++);
+chr3=_23d.charCodeAt(i++);
+enc1=chr1>>2;
+enc2=((chr1&3)<<4)|(chr2>>4);
+enc3=((chr2&15)<<2)|(chr3>>6);
+enc4=chr3&63;
+if(isNaN(chr2)){
+enc3=enc4=64;
+}else{
+if(isNaN(chr3)){
+enc4=64;
+}
+}
+_23f=_23f+_23e.charAt(enc1)+_23e.charAt(enc2)+_23e.charAt(enc3)+_23e.charAt(enc4);
+}while(i<_23d.length);
+return _23f;
+};
+Xinha.base64_decode=function(_243){
+var _244="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
+var _245="";
+var chr1,chr2,chr3;
+var enc1,enc2,enc3,enc4;
+var i=0;
+_243=_243.replace(/[^A-Za-z0-9\+\/\=]/g,"");
+do{
+enc1=_244.indexOf(_243.charAt(i++));
+enc2=_244.indexOf(_243.charAt(i++));
+enc3=_244.indexOf(_243.charAt(i++));
+enc4=_244.indexOf(_243.charAt(i++));
+chr1=(enc1<<2)|(enc2>>4);
+chr2=((enc2&15)<<4)|(enc3>>2);
+chr3=((enc3&3)<<6)|enc4;
+_245=_245+String.fromCharCode(chr1);
+if(enc3!=64){
+_245=_245+String.fromCharCode(chr2);
+}
+if(enc4!=64){
+_245=_245+String.fromCharCode(chr3);
+}
+}while(i<_243.length);
+return _245;
+};
+Xinha.removeFromParent=function(el){
+if(!el.parentNode){
+return;
+}
+var pN=el.parentNode;
+pN.removeChild(el);
+return el;
+};
+Xinha.hasParentNode=function(el){
+if(el.parentNode){
+if(el.parentNode.nodeType==11){
+return false;
+}
+return true;
+}
+return false;
+};
+Xinha.viewportSize=function(_24c){
+_24c=(_24c)?_24c:window;
+var x,y;
+if(_24c.innerHeight){
+x=_24c.innerWidth;
+y=_24c.innerHeight;
+}else{
+if(_24c.document.documentElement&&_24c.document.documentElement.clientHeight){
+x=_24c.document.documentElement.clientWidth;
+y=_24c.document.documentElement.clientHeight;
+}else{
+if(_24c.document.body){
+x=_24c.document.body.clientWidth;
+y=_24c.document.body.clientHeight;
+}
+}
+}
+return {"x":x,"y":y};
+};
+Xinha.prototype.scrollPos=function(_24e){
+_24e=(_24e)?_24e:window;
+var x,y;
+if(_24e.pageYOffset){
+x=_24e.pageXOffset;
+y=_24e.pageYOffset;
+}else{
+if(_24e.document.documentElement&&document.documentElement.scrollTop){
+x=_24e.document.documentElement.scrollLeft;
+y=_24e.document.documentElement.scrollTop;
+}else{
+if(_24e.document.body){
+x=_24e.document.body.scrollLeft;
+y=_24e.document.body.scrollTop;
+}
+}
+}
+return {"x":x,"y":y};
+};
+Xinha.getElementTopLeft=function(_250){
+var _251={top:0,left:0};
+while(_250){
+_251.top+=_250.offsetTop;
+_251.left+=_250.offsetLeft;
+if(_250.offsetParent&&_250.offsetParent.tagName.toLowerCase()!="body"){
+_250=_250.offsetParent;
+}else{
+_250=null;
+}
+}
+return _251;
+};
+Xinha.findPosX=function(obj){
+var _253=0;
+if(obj.offsetParent){
+return Xinha.getElementTopLeft(obj).left;
+}else{
+if(obj.x){
+_253+=obj.x;
+}
+}
+return _253;
+};
+Xinha.findPosY=function(obj){
+var _255=0;
+if(obj.offsetParent){
+return Xinha.getElementTopLeft(obj).top;
+}else{
+if(obj.y){
+_255+=obj.y;
+}
+}
+return _255;
+};
+Xinha.prototype.setLoadingMessage=function(_256,_257,_258){
+if(!this.config.showLoading||!document.getElementById("loading_sub_"+this._textArea.name)){
+return;
+}
+var elt=document.getElementById("loading_sub_"+this._textArea.name);
+elt.innerHTML=Xinha._lc(_256,_257,_258);
+};
+Xinha.prototype.removeLoadingMessage=function(){
+if(!this.config.showLoading||!document.getElementById("loading_"+this._textArea.name)){
+return;
+}
+document.body.removeChild(document.getElementById("loading_"+this._textArea.name));
+};
+Xinha.toFree=[];
+Xinha.freeLater=function(obj,prop){
+Xinha.toFree.push({o:obj,p:prop});
+};
+Xinha.free=function(obj,prop){
+if(obj&&!prop){
+for(var p in obj){
+Xinha.free(obj,p);
+}
+}else{
+if(obj){
+try{
+obj[prop]=null;
+}
+catch(x){
+}
+}
+}
+};
+Xinha.collectGarbageForIE=function(){
+Xinha.flushEvents();
+for(var x=0;x<Xinha.toFree.length;x++){
+Xinha.free(Xinha.toFree[x].o,Xinha.toFree[x].p);
+Xinha.toFree[x].o=null;
+}
+};
+Xinha.prototype.insertNodeAtSelection=function(_260){
+Xinha.notImplemented("insertNodeAtSelection");
+};
+Xinha.prototype.getParentElement=function(sel){
+Xinha.notImplemented("getParentElement");
+};
+Xinha.prototype.activeElement=function(sel){
+Xinha.notImplemented("activeElement");
+};
+Xinha.prototype.selectionEmpty=function(sel){
+Xinha.notImplemented("selectionEmpty");
+};
+Xinha.prototype.selectNodeContents=function(node,pos){
+Xinha.notImplemented("selectNodeContents");
+};
+Xinha.prototype.insertHTML=function(html){
+Xinha.notImplemented("insertHTML");
+};
+Xinha.prototype.getSelectedHTML=function(){
+Xinha.notImplemented("getSelectedHTML");
+};
+Xinha.prototype.getSelection=function(){
+Xinha.notImplemented("getSelection");
+};
+Xinha.prototype.createRange=function(sel){
+Xinha.notImplemented("createRange");
+};
+Xinha.prototype.isKeyEvent=function(_268){
+Xinha.notImplemented("isKeyEvent");
+};
+Xinha.prototype.isShortCut=function(_269){
+if(_269.ctrlKey&&!_269.altKey){
+return true;
+}
+return false;
+};
+Xinha.prototype.getKey=function(_26a){
+Xinha.notImplemented("getKey");
+};
+Xinha.getOuterHTML=function(_26b){
+Xinha.notImplemented("getOuterHTML");
+};
+Xinha.getXMLHTTPRequestObject=function(){
+try{
+if(typeof XMLHttpRequest=="function"){
+return new XMLHttpRequest();
+}else{
+if(typeof ActiveXObject=="function"){
+return new ActiveXObject("Microsoft.XMLHTTP");
+}
+}
+}
+catch(e){
+Xinha.notImplemented("getXMLHTTPRequestObject");
+}
+};
+Xinha.prototype._activeElement=function(sel){
+return this.activeElement(sel);
+};
+Xinha.prototype._selectionEmpty=function(sel){
+return this.selectionEmpty(sel);
+};
+Xinha.prototype._getSelection=function(){
+return this.getSelection();
+};
+Xinha.prototype._createRange=function(sel){
+return this.createRange(sel);
+};
+HTMLArea=Xinha;
+Xinha.init();
+Xinha.addDom0Event(window,"unload",Xinha.collectGarbageForIE);
+Xinha.notImplemented=function(_26f){
+throw new Error("Method Not Implemented","Part of Xinha has tried to call the "+_26f+" method which has not been implemented.");
+};
+\r
diff --git a/mailboxes/xinha/contrib/lc_parse_strings.php b/mailboxes/xinha/contrib/lc_parse_strings.php
new file mode 100644 (file)
index 0000000..b22343b
--- /dev/null
@@ -0,0 +1,264 @@
+<?php
+die("this script is disabled for security");
+
+/**
+  * LC-Parse-Strings-Script
+  *
+  * This script parses all xinhas source-files and creates base lang-files
+  * in the lang-folders (one for base and one every plugin)
+  *
+  * How To use it: - remove the die() in line 2 (security)
+  *                - make sure all lang-folders are writeable for your webserver
+  *                - open the contrib/lc_parse_strings.php in your browser
+  *                - lang/base.js will be written
+  *                - open base.js, translate all strings into your language and save it
+  *                  as yourlangauge.js
+  *                - send the translated file to the xinha-team
+ **/
+
+
+
+error_reporting(E_ALL);
+
+$ret = array();
+$files = getFiles("../", "js$");
+foreach($files as $file)
+{
+    $fp = fopen($file, "r");
+    $data = "";
+    while(!feof($fp)) {
+        $data .= fread($fp, 1024);
+    }
+
+    preg_match_all('#_lc\("([^"]+)"\)|_lc\(\'([^\']+)\'\)#', $data, $m);
+    foreach($m[1] as $i) {
+        if(trim($i)=="") continue;
+        $ret[] = $i;
+    }
+    foreach($m[2] as $i) {
+        if(trim($i)=="") continue;
+        $ret[] = $i;
+    }
+
+    if(eregi('htmlarea\\.js$', $file)) {
+        //toolbar-buttons
+        //bold:          [ "Bold"
+        preg_match_all('#[a-z]+: *\[ * "([^"]+)"#', $data, $m);
+        foreach($m[1] as $i) {
+            if(trim($i)=="") continue;
+            $ret[] = $i;
+        }
+
+        //HTMLArea._lc({key: 'button_bold', string
+        preg_match_all('#HTMLArea\\._lc\\({key: \'([^\']*)\'#', $data, $m);
+        foreach($m[1] as $i) {
+            if(trim($i)=="") continue;
+            $ret[] = $i;
+        }
+
+        //config.fontname, fontsize and formatblock
+        $data = substr($data, strpos($data, "this.fontname = {"), strpos($data, "this.customSelects = {};")-strpos($data, "this.fontname = {"));
+        preg_match_all('#"([^"]+)"[ \t]*:[ \t]*["\'][^"\']*["\'],?#', $data, $m);
+        foreach($m[1] as $i) {
+            if(trim($i)=="") continue;
+            $ret[] = $i;
+        }
+    }
+}
+
+$files = getFiles("../popups/", "html$");
+foreach($files as $file)
+{
+    if(preg_match("#custom2.html$#", $file)) continue;
+    if(preg_match('#old_#', $file)) continue;
+    $ret = array_merge($ret, parseHtmlFile($file));
+}
+$ret = array_unique($ret);
+$langData['HTMLArea'] = $ret;
+
+
+
+$plugins = getFiles("../plugins/");
+foreach($plugins as $pluginDir)
+{
+    $plugin = substr($pluginDir, 12);
+    if($plugin=="ibrowser") continue;
+    $ret = array();
+
+    $files = getFiles("$pluginDir/", "js$");
+    $files = array_merge($files, getFiles("$pluginDir/popups/", "html$"));
+    $files = array_merge($files, getFiles("$pluginDir/", "php$"));
+    foreach($files as $file)
+    {
+        $fp = fopen($file, "r");
+        $data = "";
+        if($fp) {
+            echo "$file open...<br>";
+            while(!feof($fp)) {
+              $data .= fread($fp, 1024);
+            }
+            preg_match_all('#_lc\("([^"]+)"|_lc\(\'([^\']+)\'#', $data, $m);
+            foreach($m[1] as $i) {
+                if(trim(strip_tags($i))=="") continue;
+                $ret[] = $i;
+            }
+            foreach($m[2] as $i) {
+                if(trim(strip_tags($i))=="") continue;
+                $ret[] = $i;
+            }
+        }
+    }
+
+    if($plugin=="TableOperations")
+    {
+        preg_match_all('#options = \\[([^\\]]+)\\];#', $data, $m);
+        foreach($m[1] as $i) {
+            preg_match_all('#"([^"]+)"#', $i, $m1);
+            foreach($m1[1] as $i) {
+                $ret[] = $i;
+            }
+        }
+        
+        //["cell-delete",        "td", "Delete cell"],
+        preg_match_all('#\\["[^"]+",[ \t]*"[^"]+",[ \t]*"([^"]+)"\\]#', $data, $m);
+        foreach($m[1] as $i) {
+            $ret[] = $i;
+        }
+    }
+
+
+    $files = getFiles("$pluginDir/", "html$");
+    $files = array_merge($files, getFiles("$pluginDir/", "php$"));
+    foreach($files as $file)
+    {
+        $ret = array_merge($ret, parseHtmlFile($file, $plugin));
+    }
+    
+    $files = getFiles("$pluginDir/popups/", "html$");
+    foreach($files as $file)
+    {
+        $ret = array_merge($ret, parseHtmlFile($file, $plugin));
+    }
+    $ret = array_unique($ret);
+
+    $langData[$plugin] = $ret;
+}
+
+foreach($langData as $plugin=>$strings)
+{
+    if(sizeof($strings)==0) continue;
+    
+
+    $data = "// I18N constants\n";
+    $data .= "//\n";
+    $data .= "//LANG: \"base\", ENCODING: UTF-8\n";
+    $data .= "//Author: Translator-Name, <email@example.com>\n";
+    $data .= "// FOR TRANSLATORS:\n";
+    $data .= "//\n";
+    $data .= "//   1. PLEASE PUT YOUR CONTACT INFO IN THE ABOVE LINE\n";
+    $data .= "//      (at least a valid email address)\n";
+    $data .= "//\n";
+    $data .= "//   2. PLEASE TRY TO USE UTF-8 FOR ENCODING;\n";
+    $data .= "//      (if this is not possible, please include a comment\n";
+    $data .= "//       that states what encoding is necessary.)\n";
+    $data .= "\n";
+    $data .= "{\n";
+    sort($strings);
+    foreach($strings as $string) {
+        $string = str_replace(array('\\', '"'), array('\\\\', '\\"'), $string);
+        $data .= "  \"".$string."\": \"\",\n";
+    }
+    $data = substr($data, 0, -2);
+    $data .= "\n";
+    $data .= "}\n";
+
+    if($plugin=="HTMLArea")
+        $file = "../lang/base.js";
+    else
+        $file = "../plugins/$plugin/lang/base.js";
+    
+    $fp = fopen($file, "w");
+    if(!$fp) continue;
+    fwrite($fp, $data);
+    fclose($fp);
+    echo "$file written...<br>";
+}
+
+
+
+
+function parseHtmlFile($file, $plugin="")
+{
+    $ret = array();
+    
+    $fp = fopen($file, "r");
+    if(!$fp) {
+        die("invalid fp");
+    }
+    $data = "";
+    while(!feof($fp)) {
+        $data .= fread($fp, 1024);
+    }
+    
+    if($plugin=="FormOperations" || $plugin=="SuperClean" || $plugin=="Linker") {
+        //<l10n>-tags for inline-dialog or panel-dialog based dialogs
+        $elems = array("l10n");
+    } else {
+        $elems = array("title", "input", "select", "legend", "span", "option", "td", "button", "div", "label");
+    }
+    foreach($elems as $elem) {
+        preg_match_all("#<{$elem}[^>]*>([^<^\"]+)</$elem>#i", $data, $m);
+        foreach($m[1] as $i) {
+            if(trim(strip_tags($i))=="") continue;
+            if($i=="/") continue;
+            if($plugin=="ImageManager" && preg_match('#^--+$#', $i)) continue; //skip those ------
+            if($plugin=="CharacterMap" && preg_match('#&[a-z0-9]+;#i', trim($i)) || $i=="@") continue;
+            if($plugin=="SpellChecker" && preg_match('#^\'\\.\\$[a-z]+\\.\'$#', $i)) continue;
+            $ret[] = trim($i);
+        }
+    }
+    
+    if($plugin=="FormOperations" || $plugin=="SuperClean" || $plugin=="Linker")
+    {
+        //_( for inline-dialog or panel-dialog based dialogs
+        preg_match_all('#"_\(([^"]+)\)"#i', $data, $m);
+        foreach($m[1] as $i) {
+            if(trim($i)=="") continue;
+            $ret[] = $i;
+        }
+    }
+    else
+    {
+        preg_match_all('#title="([^"]+)"#i', $data, $m);
+        foreach($m[1] as $i) {
+            if(trim(strip_tags($i))=="") continue;
+            if(strip_tags($i)==" - ") continue; //skip those - (ImageManager)
+            $ret[] = $i;
+        }
+    }
+    return($ret);
+}
+
+
+function getFiles($rootdirpath, $eregi_match='') {
+ $array = array();
+ if ($dir = @opendir($rootdirpath)) {
+   $array = array();
+   while (($file = readdir($dir)) !== false) {
+     if($file=="." || $file==".." || $file==".svn") continue;
+      if($eregi_match=="")
+        $array[] = $rootdirpath."/".$file;
+      else if(eregi($eregi_match,$file))
+        $array[] = $rootdirpath."/".$file;
+      
+   }
+   closedir($dir);
+ }
+ return $array;
+}
+
+
+
+
+
+?>
diff --git a/mailboxes/xinha/contrib/php-xinha.php b/mailboxes/xinha/contrib/php-xinha.php
new file mode 100644 (file)
index 0000000..2fac5d2
--- /dev/null
@@ -0,0 +1,202 @@
+<?php
+  /** Write the appropriate xinha_config directives to pass data to a PHP (Plugin) backend file.
+   *
+   *  ImageManager Example:
+   *  The following would be placed in step 3 of your configuration (see the NewbieGuide 
+   *  (http://xinha.python-hosting.com/wiki/NewbieGuide)
+   *
+   * <script language="javascript">
+   *  with (xinha_config.ImageManager)
+   *  { 
+   *    <?php 
+   *      xinha_pass_to_php_backend
+   *      (       
+   *        array
+   *        (
+   *         'images_dir' => '/home/your/directory',
+   *         'images_url' => '/directory'
+   *        )
+   *      )
+   *    ?>
+   *  }
+   *  </script>
+   * 
+   */
+      
+  function xinha_pass_to_php_backend($Data, $KeyLocation = 'Xinha:BackendKey')
+  {
+   
+    $bk = array();
+    $bk['data']       = serialize($Data);
+    
+    @session_start();
+    if(!isset($_SESSION[$KeyLocation]))
+    {
+      $_SESSION[$KeyLocation] = uniqid('Key_');
+    }
+    
+    $bk['session_name'] = session_name();      
+    $bk['key_location'] = $KeyLocation;      
+    $bk['hash']         = 
+      function_exists('sha1') ? 
+        sha1($_SESSION[$KeyLocation] . $bk['data']) 
+      : md5($_SESSION[$KeyLocation] . $bk['data']);
+      
+      
+    // The data will be passed via a postback to the 
+    // backend, we want to make sure these are going to come
+    // out from the PHP as an array like $bk above, so 
+    // we need to adjust the keys.
+    $backend_data = array();
+    foreach($bk as $k => $v)
+    {
+      $backend_data["backend_data[$k]"] = $v; 
+    }
+    
+    // The session_start() above may have been after data was sent, so cookies
+    // wouldn't have worked.
+    $backend_data[session_name()] = session_id();
+    
+    echo 'backend_data = ' . xinha_to_js($backend_data) . "; \n";
+    
+  }  
+   
+  /** Convert PHP data structure to Javascript */
+  
+  function xinha_to_js($var, $tabs = 0)
+  {
+    if(is_numeric($var))
+    {
+      return $var;
+    }
+  
+    if(is_string($var))
+    {
+      return "'" . xinha_js_encode($var) . "'";
+    }
+  
+    if(is_array($var))
+    {
+      $useObject = false;
+      foreach(array_keys($var) as $k) {
+          if(!is_numeric($k)) $useObject = true;
+      }
+      $js = array();
+      foreach($var as $k => $v)
+      {
+        $i = "";
+        if($useObject) {
+          if(preg_match('#^[a-zA-Z]+[a-zA-Z0-9]*$#', $k)) {
+            $i .= "$k: ";
+          } else {
+            $i .= "'$k': ";
+          }
+        }
+        $i .= xinha_to_js($v, $tabs + 1);
+        $js[] = $i;
+      }
+      if($useObject) {
+          $ret = "{\n" . xinha_tabify(implode(",\n", $js), $tabs) . "\n}";
+      } else {
+          $ret = "[\n" . xinha_tabify(implode(",\n", $js), $tabs) . "\n]";
+      }
+      return $ret;
+    }
+  
+    return 'null';
+  }
+    
+  /** Like htmlspecialchars() except for javascript strings. */
+  
+  function xinha_js_encode($string)
+  {
+    static $strings = "\\,\",',%,&,<,>,{,},@,\n,\r";
+  
+    if(!is_array($strings))
+    {
+      $tr = array();
+      foreach(explode(',', $strings) as $chr)
+      {
+        $tr[$chr] = sprintf('\x%02X', ord($chr));
+      }
+      $strings = $tr;
+    }
+  
+    return strtr($string, $strings);
+  }
+        
+   
+  /** Used by plugins to get the config passed via 
+  *   xinha_pass_to_backend()
+  *  returns either the structure given, or NULL
+  *  if none was passed or a security error was encountered.
+  */
+  
+  function xinha_read_passed_data()
+  {
+   if(isset($_REQUEST['backend_data']) && is_array($_REQUEST['backend_data']))
+   {
+     $bk = $_REQUEST['backend_data'];
+     session_name($bk['session_name']);
+     @session_start();
+     if(!isset($_SESSION[$bk['key_location']])) return NULL;
+     
+     if($bk['hash']         === 
+        function_exists('sha1') ? 
+          sha1($_SESSION[$bk['key_location']] . $bk['data']) 
+        : md5($_SESSION[$bk['key_location']] . $bk['data']))
+     {
+       return unserialize(ini_get('magic_quotes_gpc') ? stripslashes($bk['data']) : $bk['data']);
+     }
+   }
+   
+   return NULL;
+  }
+   
+  /** Used by plugins to get a query string that can be sent to the backend 
+  * (or another part of the backend) to send the same data.
+  */
+  
+  function xinha_passed_data_querystring()
+  {
+   $qs = array();
+   if(isset($_REQUEST['backend_data']) && is_array($_REQUEST['backend_data']))
+   {
+     foreach($_REQUEST['backend_data'] as $k => $v)
+     {
+       $v =  ini_get('magic_quotes_gpc') ? stripslashes($v) : $v;
+       $qs[] = "backend_data[" . rawurlencode($k) . "]=" . rawurlencode($v);
+     }       
+   }
+   
+   $qs[] = session_name() . '=' . session_id();
+   return implode('&', $qs);
+  }
+   
+    
+  /** Just space-tab indent some text */
+  function xinha_tabify($text, $tabs)
+  {
+    if($text)
+    {
+      return str_repeat("  ", $tabs) . preg_replace('/\n(.)/', "\n" . str_repeat("  ", $tabs) . "\$1", $text);
+    }
+  }       
+
+  /** Return upload_max_filesize value from php.ini in kilobytes (function adapted from php.net)**/
+  function upload_max_filesize_kb() 
+  {
+    $val = ini_get('upload_max_filesize');
+    $val = trim($val);
+    $last = strtolower($val{strlen($val)-1});
+    switch($last) 
+    {
+      // The 'G' modifier is available since PHP 5.1.0
+      case 'g':
+        $val *= 1024;
+      case 'm':
+        $val *= 1024;
+   }
+   return $val;
+}
+?>
\ No newline at end of file
diff --git a/mailboxes/xinha/examples/Extended.html b/mailboxes/xinha/examples/Extended.html
new file mode 100644 (file)
index 0000000..69aedda
--- /dev/null
@@ -0,0 +1,299 @@
+<html>\r
+\r
+<head>\r
+  <title>Settings</title>\r
+  <link rel="stylesheet" type="text/css" href="../popups/popup.css" />\r
+  <script type="text/javascript">\r
+\r
+function getAbsolutePos(el) {\r
+       var r = { x: el.offsetLeft, y: el.offsetTop };\r
+       if (el.offsetParent) {\r
+               var tmp = getAbsolutePos(el.offsetParent);\r
+               r.x += tmp.x;\r
+               r.y += tmp.y;\r
+       }\r
+       return r;\r
+};\r
+\r
+function getSelectedValue(el) {\r
+  if(!el)\r
+    return "";\r
+  return el[el.selectedIndex].value;\r
+}\r
+\r
+function setSelectedValue(el, val) {\r
+  if(!el)\r
+    return "";\r
+  var ops = el.getElementsByTagName("option");\r
+  for (var i = ops.length; --i >= 0;) {\r
+    var op = ops[i];\r
+    op.selected = (op.value == val);\r
+  }\r
+  el.value = val;\r
+}\r
+\r
+function getCheckedValue(el) {\r
+  if(!el)\r
+    return "";\r
+  var radioLength = el.length;\r
+  if(radioLength == undefined)\r
+    if(el.checked)\r
+      return el.value;\r
+    else\r
+      return "false";\r
+  for(var i = 0; i < radioLength; i++) {\r
+    if(el[i].checked) {\r
+      return el[i].value;\r
+    }\r
+  }\r
+  return "";\r
+}\r
+\r
+function setCheckedValue(el, val) {\r
+  if(!el)\r
+    return;\r
+  var radioLength = el.length;\r
+  if(radioLength == undefined) {\r
+    el.checked = (el.value == val.toString());\r
+    return;\r
+  }\r
+  for(var i = 0; i < radioLength; i++) {\r
+    el[i].checked = false;\r
+    if(el[i].value == val.toString()) {\r
+      el[i].checked = true;\r
+    }\r
+  }\r
+}\r
+\r
+function __dlg_onclose() {\r
+       opener.Dialog._return(null);\r
+};\r
+\r
+// closes the dialog and passes the return info upper.\r
+function __dlg_close(val) {\r
+       opener.Dialog._return(val);\r
+       window.close();\r
+};\r
+\r
+function __dlg_close_on_esc(ev) {\r
+       ev || (ev = window.event);\r
+       if (ev.keyCode == 27) {\r
+               window.close();\r
+               return false;\r
+       }\r
+       return true;\r
+};\r
+\r
+function __dlg_init(bottom) {\r
+  var body = document.body;\r
+       var body_height = 0;\r
+       if (typeof bottom == "undefined") {\r
+               var div = document.createElement("div");\r
+               body.appendChild(div);\r
+               var pos = getAbsolutePos(div);\r
+               body_height = pos.y;\r
+       } else {\r
+               var pos = getAbsolutePos(bottom);\r
+               body_height = pos.y + bottom.offsetHeight;\r
+       }\r
+       window.dialogArguments = opener.Dialog._arguments;\r
+       if (!document.all) {\r
+               window.sizeToContent();\r
+               window.sizeToContent(); // for reasons beyond understanding,\r
+                                       // only if we call it twice we get the\r
+                                       // correct size.\r
+               window.addEventListener("unload", __dlg_onclose, true);\r
+               window.innerWidth = body.offsetWidth + 5;\r
+               window.innerHeight = body_height + 2;\r
+               // center on parent\r
+               var x = opener.screenX + (opener.outerWidth - window.outerWidth) / 2;\r
+               var y = opener.screenY + (opener.outerHeight - window.outerHeight) / 2;\r
+               window.moveTo(x, y);\r
+       } else {\r
+               // window.dialogHeight = body.offsetHeight + 50 + "px";\r
+               // window.dialogWidth = body.offsetWidth + "px";\r
+               window.resizeTo(body.offsetWidth, body_height);\r
+               var ch = body.clientHeight;\r
+               var cw = body.clientWidth;\r
+               window.resizeBy(body.offsetWidth - cw, body_height - ch);\r
+               var W = body.offsetWidth;\r
+               var H = 2 * body_height - ch;\r
+               var x = (screen.availWidth - W) / 2;\r
+               var y = (screen.availHeight - H) / 2;\r
+               window.moveTo(x, y);\r
+       }\r
+       document.body.onkeypress = __dlg_close_on_esc;\r
+};\r
+\r
+function placeFocus() {\r
+var bFound = false;\r
+  // for each form\r
+  for (f=0; f < document.forms.length; f++) {\r
+    // for each element in each form\r
+    for(i=0; i < document.forms[f].length; i++) {\r
+      // if it's not a hidden element\r
+      if (document.forms[f][i].type != "hidden") {\r
+        // and it's not disabled\r
+        if (document.forms[f][i].disabled != true) {\r
+            // set the focus to it\r
+            document.forms[f][i].focus();\r
+            var bFound = true;\r
+        }\r
+      }\r
+      // if found in this element, stop looking\r
+      if (bFound == true)\r
+        break;\r
+    }\r
+    // if found in this form, stop looking\r
+    if (bFound == true)\r
+      break;\r
+  }\r
+}\r
+\r
+function Init() {\r
+  __dlg_init();\r
+  var param = window.dialogArguments;\r
+  if(param) {\r
+    var el;\r
+    for (var field in param) {\r
+      //alert(field + '="' + param[field] + '"');\r
+      el = document.getElementById(field);\r
+      if (el.tagName.toLowerCase()=="input"){\r
+        if ((el.type.toLowerCase()=="radio") || (el.type.toLowerCase()=="checkbox")){\r
+          setCheckedValue(el, param[field]);\r
+        } else {\r
+          el.value = param[field];\r
+        }\r
+      } else if (el.tagName.toLowerCase()=="select"){\r
+        setSelectedValue(el, param[field]);\r
+      } else if (el.tagName.toLowerCase()=="textarea"){\r
+        el.value = param[field];\r
+      }\r
+    }\r
+  }\r
+  placeFocus();\r
+};\r
+\r
+// pass data back to the calling window\r
+function onOK() {\r
+  var param = new Object();\r
+  var el = document.getElementsByTagName('input');\r
+  for (var i=0; i<el.length;i++){\r
+    if ((el[i].type.toLowerCase()=="radio") || (el[i].type.toLowerCase()=="checkbox")){\r
+      if (getCheckedValue(el[i])!=''){\r
+        param[el[i].id] = getCheckedValue(el[i]);\r
+      }\r
+    } else {\r
+      param[el[i].id] = el[i].value;\r
+    }\r
+  }\r
+  el = document.getElementsByTagName('select');\r
+  for (var i=0; i<el.length;i++){\r
+    param[el[i].id] = getSelectedValue(el[i]);\r
+  }\r
+  el = document.getElementsByTagName('textarea');\r
+  for (var i=0; i<el.length;i++){\r
+    param[el[i].id] = el[i].value;\r
+  }\r
+  __dlg_close(param);\r
+  return false;\r
+};\r
+\r
+function onCancel() {\r
+  __dlg_close(null);\r
+  return false;\r
+};\r
+\r
+</script>\r
+\r
+<style type="text/css">\r
+       .fr { width: 16em; float: left; padding: 2px 5px; text-align: right; }\r
+</style>\r
+\r
+</head>\r
+\r
+<body class="dialog" onload="Init(); window.resizeTo(360, 590);">\r
+<div class="title">Settings</div>\r
+  <form action="" method="get">\r
+    <div class="fr">Editor width:</div>\r
+      <input type="text" name="width" id="width" title="" />\r
+    <p />\r
+    <div class="fr">Editor height:</div>\r
+      <input type="text" name="height" id="height" title="" />\r
+    <p />\r
+    <div class="fr">Size includes bars</div>\r
+      <input type="checkbox" name="sizeIncludesBars" id="sizeIncludesBars" value="true" />\r
+    <p />\r
+    <div class="fr">Status Bar</div>\r
+      <input type="checkbox" name="statusBar" id="statusBar" value="true" />\r
+    <p />\r
+    <div class="fr">Mozilla Parameter Handler:</div>\r
+    <select name="mozParaHandler" id="mozParaHandler">\r
+      <option value="built-in">built-in</option>\r
+      <option value="dirty">dirty</option>\r
+      <option value="best">best</option>\r
+    </select>\r
+    <div class="space"></div>\r
+    <div class="fr">Undo steps:</div>\r
+      <input type="text" name="undoSteps" id="undoSteps" title="" />\r
+    <p />\r
+    <div class="fr">Base href:</div>\r
+      <input type="text" name="baseHref" id="baseHref" title="" />\r
+    <p />\r
+    <div class="fr">Strip base href</div>\r
+      <input type="checkbox" name="stripBaseHref" id="stripBaseHref" value="true" />\r
+    <p />\r
+    <div class="fr">Strip self named anchors</div>\r
+      <input type="checkbox" name="stripSelfNamedAnchors" id="stripSelfNamedAnchors" value="true" />\r
+    <p />\r
+    <div class="fr">only 7bit printables in URLs</div>\r
+      <input type="checkbox" name="only7BitPrintablesInURLs" id="only7BitPrintablesInURLs" value="true" />\r
+    <p />\r
+    <div class="fr">7bit Clean</div>\r
+      <input type="checkbox" name="sevenBitClean" id="sevenBitClean" value="true" />\r
+    <p />\r
+    <div class="fr">kill Word on paste</div>\r
+      <input type="checkbox" name="killWordOnPaste" id="killWordOnPaste" value="true" />\r
+    <p />\r
+    <div class="fr">flow toolbars</div>\r
+      <input type="checkbox" name="flowToolbars" id="flowToolbars" value="true" />\r
+    <p />\r
+    <div class="fr">show loading</div>\r
+      <input type="checkbox" name="showLoading" id="showLoading" value="true" />\r
+    <p />\r
+\r
+    <div id="CharacterMapOptions" class="options">\r
+    <hr size="0.5">\r
+    <div class="fr">CharacterMap mode :</div>\r
+      <select id="CharacterMapMode" name="CharacterMapMode">\r
+        <option value="popup">popup</option>\r
+        <option value="panel">panel</option>\r
+      </select>\r
+    </div>\r
+    <p />\r
+\r
+    <div id="ListTypeOptions" class="options">\r
+    <hr size="0.5">\r
+    <div class="fr">ListType mode :</div>\r
+      <select id="ListTypeMode" name="ListTypeMode">\r
+        <option value="toolbar">toolbar</option>\r
+        <option value="panel">panel</option>\r
+      </select>\r
+    </div>\r
+    <p />\r
+\r
+    <div id="CharCounterOptions" class="options">\r
+    <hr size="0.5">\r
+    <div class="fr">CharCounter (showChar) :</div><input type="checkbox" name="showChar" id="showChar" value="true" /><br />\r
+    <div class="fr">CharCounter (showWord) :</div><input type="checkbox" name="showWord" id="showWord" value="true" /><br />\r
+    <div class="fr">CharCounter (showHtml) :</div><input type="checkbox" name="showHtml" id="showHtml" value="true" />\r
+    </div>\r
+    <p />\r
+\r
+  <div id="buttons">\r
+    <button type="submit" name="ok" onclick="return onOK();">OK</button>\r
+    <button type="button" name="cancel" onclick="return onCancel();">Cancel</button>\r
+  </div>\r
+</form>\r
+</body>\r
+</html>\r
diff --git a/mailboxes/xinha/examples/custom.css b/mailboxes/xinha/examples/custom.css
new file mode 100644 (file)
index 0000000..15530d5
--- /dev/null
@@ -0,0 +1,40 @@
+  /*--------------------------------------:noTabs=true:tabSize=2:indentSize=2:--
+    --  CSS plugin example CSS file.  This file is used by full_example.js
+    --  when the CSS plugin is included in an auto-generated example.
+    --  @TODO Make this CSS more useful.
+    --
+    --  $HeadURL: http://svn.xinha.python-hosting.com/tags/0.92beta/examples/custom.css $
+    --  $LastChangedDate: 2007-01-19 23:24:36 +0100 (Fr, 19 Jan 2007) $
+    --  $LastChangedRevision: 677 $
+    --  $LastChangedBy: ray $
+    --------------------------------------------------------------------------*/
+
+body { background-color: #234; color: #dd8; font-family: tahoma; font-size: 12px; }
+
+a:link, a:visited { color: #8cf; }
+a:hover { color: #ff8; }
+
+h1 { background-color: #456; color: #ff8; padding: 2px 5px; border: 1px solid; border-color: #678 #012 #012 #678; }
+
+/* syntax highlighting (used by the first combo defined for the CSS plugin) */
+
+pre { margin: 0px 1em; padding: 5px 1em; background-color: #000; border: 1px dotted #02d; border-left: 2px solid #04f; }
+.code { color: #f5deb3; }
+.string { color: #00ffff; }
+.comment { color: #8fbc8f; }
+.variable-name { color: #fa8072; }
+.type { color: #90ee90; font-weight: bold; }
+.reference { color: #ee82ee; }
+.preprocessor { color: #faf; }
+.keyword { color: #ffffff; font-weight: bold; }
+.function-name { color: #ace; }
+.html-tag { font-weight: bold; }
+.html-helper-italic { font-style: italic; }
+.warning { color: #ffa500; font-weight: bold; }
+.html-helper-bold { font-weight: bold; }
+
+/* info combo */
+
+.quote { font-style: italic; color: #ee9; }
+.highlight { background-color: yellow; color: #000; }
+.deprecated { text-decoration: line-through; color: #aaa; }
diff --git a/mailboxes/xinha/examples/dynamic.css b/mailboxes/xinha/examples/dynamic.css
new file mode 100644 (file)
index 0000000..58bd14b
--- /dev/null
@@ -0,0 +1,56 @@
+  /*--------------------------------------:noTabs=true:tabSize=2:indentSize=2:--
+    --  DynamicCSS plugin example CSS file.  Used by full_example.js
+    --  when the DynamicCSS plugin is included in an auto-generated example.
+    --  @TODO Make this CSS more useful.
+    --
+    --  $HeadURL: http://svn.xinha.python-hosting.com/tags/0.92beta/examples/dynamic.css $
+    --  $LastChangedDate: 2007-01-19 23:24:36 +0100 (Fr, 19 Jan 2007) $
+    --  $LastChangedRevision: 677 $
+    --  $LastChangedBy: ray $
+    --------------------------------------------------------------------------*/
+
+p {
+  FONT-FAMILY: Arial, Helvetica;
+  FONT-SIZE: 9pt;
+  FONT-WEIGHT: normal;
+  COLOR: #000000;
+}
+
+p.p1 {
+  FONT-FAMILY: Arial, Helvetica;
+  FONT-SIZE: 11pt;
+  FONT-WEIGHT: normal;
+  COLOR: #000000;
+}
+
+p.p2 {
+  FONT-FAMILY: Arial, Helvetica;
+  FONT-SIZE: 13pt;
+  FONT-WEIGHT: normal;
+  COLOR: #000000;
+}
+
+div {
+  FONT-FAMILY: Arial, Helvetica;
+  FONT-SIZE: 9pt;
+  FONT-WEIGHT: bold;
+  COLOR: #000000;
+}
+
+div.div1 {
+  FONT-FAMILY: Arial, Helvetica;
+  FONT-SIZE: 11pt;
+  FONT-WEIGHT: bold;
+  COLOR: #000000;
+}
+
+div.div2 {
+  FONT-FAMILY: Arial, Helvetica;
+  FONT-SIZE: 13pt;
+  FONT-WEIGHT: bold;
+  COLOR: #000000;
+}
+
+.quote { font-style: italic; color: #ee9; }
+.highlight { background-color: yellow; color: #000; }
+.deprecated { text-decoration: line-through; color: #aaa; }
diff --git a/mailboxes/xinha/examples/ext_example-body.html b/mailboxes/xinha/examples/ext_example-body.html
new file mode 100644 (file)
index 0000000..129aa38
--- /dev/null
@@ -0,0 +1,202 @@
+<!DOCTYPE BHTML PUBLIC "-//BC//DTD BHTML 3.2 Final//EN">\r
+<html>\r
+<head>\r
+\r
+  <!-- ---------------------------------------------------------------------\r
+    --  $HeadURL: http://svn.xinha.python-hosting.com/tags/0.92beta/examples/ext_example-body.html $\r
+    --  $LastChangedDate: 2007-01-22 16:06:18 +0100 (Mo, 22 Jan 2007) $\r
+    --  $LastChangedRevision: 686 $\r
+    --  $LastChangedBy: gocher $\r
+    ------------------------------------------------------------------------ -->\r
+\r
+  <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />\r
+  <title>Example of Xinha</title>\r
+  <link rel="stylesheet" type="text/css" href="full_example.css" />\r
+\r
+  <script type="text/javascript">\r
+    function showError( sMsg, sUrl, sLine){\r
+      document.getElementById('errors').value += 'Error: ' + sMsg + '\n' +\r
+                                                 'Source File: ' + sUrl + '\n' +\r
+                                                 'Line: ' + sLine + '\n';\r
+      return false;\r
+    }\r
+    // You must set _editor_url to the URL (including trailing slash) where\r
+    // where xinha is installed, it's highly recommended to use an absolute URL\r
+    //  eg: _editor_url = "/path/to/xinha/";\r
+    // You may try a relative URL if you wish]\r
+    //  eg: _editor_url = "../";\r
+    // in this example we do a little regular expression to find the absolute path.\r
+    _editor_url  = document.location.href.replace(/examples\/ext_example-body\.html.*/, '')\r
+    //moved _editor_lang & _editor_skin to init function because of error thrown when frame document not ready\r
+  </script>\r
+\r
+  <!-- Load up the actual editor core -->\r
+  <script type="text/javascript" src="../XinhaCore.js"></script>\r
+\r
+  <script type="text/javascript">\r
+    xinha_editors = null;\r
+    xinha_init    = null;\r
+    xinha_config  = null;\r
+    xinha_plugins = null;\r
+\r
+    xinha_init = xinha_init ? xinha_init : function() {\r
+      window.onerror = showError;\r
+      document.onerror = showError;\r
+\r
+      var f = top.frames["menu"].document.forms["fsettings"];\r
+      _editor_lang = f.lang[f.lang.selectedIndex].value; // the language we need to use in the editor.\r
+      _editor_skin = f.skin[f.skin.selectedIndex].value; // the skin we use in the editor\r
+// What are the plugins you will be using in the editors on this page.\r
+// List all the plugins you will need, even if not all the editors will use all the plugins.\r
+      xinha_plugins = [ ];\r
+      for(var x = 0; x < f.plugins.length; x++) {\r
+        if(f.plugins[x].checked) xinha_plugins.push(f.plugins[x].value);\r
+      }\r
+\r
+      // THIS BIT OF JAVASCRIPT LOADS THE PLUGINS, NO TOUCHING  :)\r
+      if(!Xinha.loadPlugins(xinha_plugins, xinha_init)) return;\r
+\r
+// What are the names of the textareas you will be turning into editors?\r
+      var num = 1;\r
+      num = parseInt(f.num.value);\r
+      if(isNaN(num)) {\r
+        num = 1;\r
+        f.num.value = 1;\r
+      }\r
+      var dest = document.getElementById("editors_here");\r
+      var lipsum = window.parent.menu.document.getElementById('myTextarea0').value;\r
+\r
+      xinha_editors = [ ]\r
+      for(var x = 0; x < num; x++) {\r
+        var ta = 'myTextarea' + x;\r
+        xinha_editors.push(ta);\r
+\r
+        var div = document.createElement('div');\r
+        div.className = 'area_holder';\r
+\r
+        var txta = document.createElement('textarea');\r
+        txta.id   = ta;\r
+        txta.name = ta;\r
+        txta.value = lipsum;\r
+        txta.style.width="100%";\r
+        txta.style.height="420px";\r
+\r
+        div.appendChild(txta);\r
+        dest.appendChild(div);\r
+      }\r
+\r
+// Create a default configuration to be used by all the editors.\r
+      settings = top.frames["menu"].settings;\r
+      xinha_config = new Xinha.Config();\r
+      xinha_config.width = settings.width;\r
+      xinha_config.height = settings.height;\r
+      xinha_config.sizeIncludesBars = settings.sizeIncludesBars;\r
+      xinha_config.statusBar = settings.statusBar;\r
+      xinha_config.mozParaHandler = settings.mozParaHandler;\r
+      xinha_config.undoSteps = settings.undoSteps;\r
+      xinha_config.baseHref = settings.baseHref;\r
+      xinha_config.stripBaseHref = settings.stripBaseHref;\r
+      xinha_config.stripSelfNamedAnchors = settings.stripSelfNamedAnchors;\r
+      xinha_config.only7BitPrintablesInURLs = settings.only7BitPrintablesInURLs;\r
+      xinha_config.sevenBitClean = settings.sevenBitClean;\r
+      xinha_config.killWordOnPaste = settings.killWordOnPaste;\r
+      xinha_config.flowToolbars = settings.flowToolbars;\r
+      xinha_config.showLoading = settings.showLoading;\r
+\r
+      if (typeof CharCounter != 'undefined') {\r
+        xinha_config.CharCounter.showChar = settings.showChar;\r
+        xinha_config.CharCounter.showWord = settings.showWord;\r
+        xinha_config.CharCounter.showHtml = settings.showHtml;\r
+      }\r
+\r
+      if (typeof CharacterMap != 'undefined') xinha_config.CharacterMap.mode = settings.CharacterMapMode;\r
+      if (typeof ListType != 'undefined') xinha_config.ListType.mode = settings.ListTypeMode;\r
+\r
+      if(typeof CSS != 'undefined') {\r
+        xinha_config.pageStyle = "@import url(custom.css);";\r
+      }\r
+\r
+      if(typeof Stylist != 'undefined') {\r
+        // We can load an external stylesheet like this - NOTE : YOU MUST GIVE AN ABSOLUTE URL\r
+        //  otherwise it won't work!\r
+        xinha_config.stylistLoadStylesheet(document.location.href.replace(/[^\/]*\.html/, 'stylist.css'));\r
+\r
+        // Or we can load styles directly\r
+        xinha_config.stylistLoadStyles('p.red_text { color:red }');\r
+\r
+        // If you want to provide "friendly" names you can do so like\r
+        // (you can do this for stylistLoadStylesheet as well)\r
+        xinha_config.stylistLoadStyles('p.pink_text { color:pink }', {'p.pink_text' : 'Pretty Pink'});\r
+      }\r
+\r
+      if(typeof DynamicCSS != 'undefined') {\r
+        xinha_config.pageStyle = "@import url(dynamic.css);";\r
+      }\r
+\r
+      if(typeof InsertWords != 'undefined') {\r
+        // Register the keyword/replacement list\r
+        var keywrds1 = new Object();\r
+        var keywrds2 = new Object();\r
+\r
+        keywrds1['-- Dropdown Label --'] = '';\r
+        keywrds1['onekey'] = 'onevalue';\r
+        keywrds1['twokey'] = 'twovalue';\r
+        keywrds1['threekey'] = 'threevalue';\r
+\r
+        keywrds2['-- Insert Keyword --'] = '';\r
+        keywrds2['Username'] = '%user%';\r
+        keywrds2['Last login date'] = '%last_login%';\r
+        xinha_config.InsertWords = {\r
+          combos : [ { options: keywrds1, context: "body" },\r
+                     { options: keywrds2, context: "li" } ]\r
+        }\r
+      }\r
+\r
+      if(typeof Filter != 'undefined') {\r
+        xinha_config.Filters = ["Word", "Paragraph"];\r
+      }\r
+\r
+// First create editors for the textareas.\r
+// You can do this in two ways, either\r
+//   xinha_editors   = Xinha.makeEditors(xinha_editors, xinha_config, xinha_plugins);\r
+// if you want all the editor objects to use the same set of plugins, OR;\r
+//   xinha_editors = Xinha.makeEditors(xinha_editors, xinha_config);\r
+//   xinha_editors['myTextarea0'].registerPlugins(['Stylist','FullScreen']);\r
+//   xinha_editors['myTextarea1'].registerPlugins(['CSS','SuperClean']);\r
+// if you want to use a different set of plugins for one or more of the editors.\r
+      xinha_editors = Xinha.makeEditors(xinha_editors, xinha_config, xinha_plugins);\r
+\r
+// If you want to change the configuration variables of any of the editors,\r
+// this is the place to do that, for example you might want to\r
+// change the width and height of one of the editors, like this...\r
+//   xinha_editors['myTextarea0'].config.width  = '640px';\r
+//   xinha_editors['myTextarea0'].config.height = '480px';\r
+\r
+// Finally we "start" the editors, this turns the textareas into Xinha editors.\r
+      Xinha.startEditors(xinha_editors);\r
+    }\r
+\r
+// javascript submit handler\r
+// this shows how to create a javascript submit button that works with the htmleditor.\r
+    submitHandler = function(formname) {\r
+      var form = document.getElementById(formname);\r
+      // in order for the submit to work both of these methods have to be called.\r
+      form.onsubmit();\r
+      window.parent.menu.document.getElementById('myTextarea0').value = document.getElementById('myTextarea0').value;\r
+      form.submit();\r
+      return true;\r
+    }\r
+\r
+    window.onload = xinha_init;\r
+//    window.onunload = Xinha.collectGarbageForIE;\r
+  </script>\r
+</head>\r
+\r
+<body>\r
+  <form id="to_submit" name="to_submit" method="post" action="ext_example-dest.php">\r
+  <div id="editors_here" name="editors_here"></div>\r
+  <button type="button" onclick="submitHandler('to_submit');">Submit</button>\r
+  <textarea id="errors" name="errors" style="width:100%; height:100px; background:silver;"></textarea><!-- style="display:none;" -->\r
+  </form>\r
+</body>\r
+</html>\r
diff --git a/mailboxes/xinha/examples/ext_example-dest.php b/mailboxes/xinha/examples/ext_example-dest.php
new file mode 100644 (file)
index 0000000..5a226e4
--- /dev/null
@@ -0,0 +1,23 @@
+<html>
+<head>
+  <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+  <title>Example of Xinha</title>
+  <link rel="stylesheet" href="full_example.css" />
+</head>
+</body>
+<?php
+if (get_magic_quotes_gpc()) {
+  $_REQUEST = array_map('stripslashes',$_REQUEST);
+}
+// or in php.ini
+//; Magic quotes for incoming GET/POST/Cookie data.
+//magic_quotes_gpc = Off
+  foreach($_REQUEST as $key=>$value){
+    if(substr($key,0,10) == 'myTextarea') {
+      echo '<h3 style="border-bottom:1px solid black;">'.$key.'(source):</h3><xmp style="border:1px solid black; width: 100%; height: 200px; overflow: auto;">'.$value.'</xmp><br/>';
+      echo '<h3 style="border-bottom:1px solid black;">'.$key.'(preview):</h3>'.$value;
+    }
+  }
+?>
+</body>
+</html>
diff --git a/mailboxes/xinha/examples/ext_example-menu.php b/mailboxes/xinha/examples/ext_example-menu.php
new file mode 100644 (file)
index 0000000..1ce1115
--- /dev/null
@@ -0,0 +1,331 @@
+<?PHP
+  $LocalPluginPath = dirname(dirname(__FILE__)).DIRECTORY_SEPARATOR.'plugins';
+  $LocalSkinPath = dirname(dirname(__File__)).DIRECTORY_SEPARATOR.'skins';
+?>
+<html>
+<head>
+
+  <!--------------------------------------:noTabs=true:tabSize=2:indentSize=2:--
+    --  Xinha example menu.  This file is used by full_example.html within a
+    --  frame to provide a menu for generating example editors using
+    --  full_example-body.html, and full_example.js.
+    --
+    --  $HeadURL: http://svn.xinha.python-hosting.com/tags/0.92beta/examples/ext_example-menu.php $
+    --  $LastChangedDate: 2007-02-07 20:12:42 +0100 (Mi, 07 Feb 2007) $
+    --  $LastChangedRevision: 715 $
+    --  $LastChangedBy: htanaka $
+    --------------------------------------------------------------------------->
+
+  <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+  <title>Example of Xinha</title>
+  <link rel="stylesheet" href="full_example.css" />
+  <style type="text/css">
+    h1 {font: bold 22px "Staccato222 BT", cursive;}
+    form, p {margin: 0px; padding: 0px;}
+    label { display:block;}
+  </style>
+  <script language="JavaScript" type="text/javascript">
+  var settings = null;
+  settings = {
+    width: "auto",
+    height: "auto",
+    sizeIncludesBars: true,
+    statusBar: true,
+    mozParaHandler: "best",
+    undoSteps: 20,
+    baseHref: null,
+    stripBaseHref: true,
+    stripSelfNamedAnchors: true,
+    only7BitPrintablesInURLs: true,
+    sevenBitClean: false,
+    killWordOnPaste: true,
+    flowToolbars: true,
+    CharacterMapMode: "popup",
+    ListTypeMode: "toolbar",
+    showLoading: false,
+    showChar: true,
+    showWord: true,
+    showHtml: true
+  };
+
+
+    function getCookieVal (offset) {
+      var endstr = document.cookie.indexOf (";", offset);
+      if (endstr == -1)
+        endstr = document.cookie.length;
+      return unescape(document.cookie.substring(offset, endstr));
+    }
+
+    function getCookie (name) {
+      var arg = name + "=";
+      var alen = arg.length;
+      var clen = document.cookie.length;
+      var i = 0;
+      while (i < clen) {
+        var j = i + alen;
+        if (document.cookie.substring(i, j) == arg)
+          return getCookieVal (j);
+        i = document.cookie.indexOf(" ", i) + 1;
+        if (i == 0) break;
+      }
+      return null;
+    }
+
+    function setCookie (name, value) {
+      var argv = setCookie.arguments;
+      var argc = setCookie.arguments.length;
+      var expires = (argc > 2) ? argv[2] : null;
+      var path = (argc > 3) ? argv[3] : null;
+      var domain = (argc > 4) ? argv[4] : null;
+      var secure = (argc > 5) ? argv[5] : false;
+      document.cookie = name + "=" + escape (value) +
+        ((expires == null) ? "" : ("; expires=" + expires.toGMTString())) +
+        ((path == null) ? "" : ("; path=" + path)) +
+        ((domain == null) ? "" : ("; domain=" + domain)) +
+        ((secure == true) ? "; secure" : "");
+    }
+
+  function _onResize() {
+    var sHeight;
+    if (window.innerHeight) sHeight = window.innerHeight;
+    else if (document.body && document.body.offsetHeight) sHeight = document.body.offsetHeight;
+    else return;
+    if (sHeight>270) {
+      sHeight = sHeight - 245;
+    } else {
+      sHeight = 30
+    }
+    var div = document.getElementById("div_plugins");
+    div.style.height = sHeight + "px";
+  }
+
+function Dialog(url, action, init) {
+       if (typeof init == "undefined") {
+               init = window;  // pass this window object by default
+       }
+       Dialog._geckoOpenModal(url, action, init);
+};
+
+Dialog._parentEvent = function(ev) {
+       setTimeout( function() { if (Dialog._modal && !Dialog._modal.closed) { Dialog._modal.focus() } }, 50);
+       if (Dialog._modal && !Dialog._modal.closed) {
+               agt = navigator.userAgent.toLowerCase();
+               is_ie = ((agt.indexOf("msie") != -1) && (agt.indexOf("opera") == -1));
+               if (is_ie) {
+                       ev.cancelBubble = true;
+                       ev.returnValue = false;
+               } else {
+                       ev.preventDefault();
+                       ev.stopPropagation();
+               }
+       }
+};
+
+
+// should be a function, the return handler of the currently opened dialog.
+Dialog._return = null;
+
+// constant, the currently opened dialog
+Dialog._modal = null;
+
+// the dialog will read it's args from this variable
+Dialog._arguments = null;
+
+Dialog._geckoOpenModal = function(url, action, init) {
+       var dlg = window.open(url, "hadialog",
+                             "toolbar=no,menubar=no,personalbar=no,width=10,height=10," +
+                             "scrollbars=no,resizable=yes,modal=yes,dependable=yes");
+       Dialog._modal = dlg;
+       Dialog._arguments = init;
+
+       // capture some window's events
+       function capwin(w) {
+//             Xinha._addEvent(w, "click", Dialog._parentEvent);
+//             Xinha._addEvent(w, "mousedown", Dialog._parentEvent);
+//             Xinha._addEvent(w, "focus", Dialog._parentEvent);
+       };
+       // release the captured events
+       function relwin(w) {
+//             Xinha._removeEvent(w, "click", Dialog._parentEvent);
+//             Xinha._removeEvent(w, "mousedown", Dialog._parentEvent);
+//             Xinha._removeEvent(w, "focus", Dialog._parentEvent);
+       };
+       capwin(window);
+       // capture other frames
+       for (var i = 0; i < window.frames.length; capwin(window.frames[i++]));
+       // make up a function to be called when the Dialog ends.
+       Dialog._return = function (val) {
+               if (val && action) {
+                       action(val);
+               }
+               relwin(window);
+               // capture other frames
+               for (var i = 0; i < window.frames.length; relwin(window.frames[i++]));
+               Dialog._modal = null;
+       };
+};
+
+  function fExtended () {
+    Dialog("Extended.html", function(param) {
+      if(param) {
+        settings.width = param["width"];
+        settings.height = param["height"];
+        settings.sizeIncludesBars = (param["sizeIncludesBars"]=="true");
+        settings.statusBar = (param["statusBar"]=="true");
+        settings.mozParaHandler = param["mozParaHandler"];
+        settings.undoSteps = param["undoSteps"];
+        settings.baseHref = param["baseHref"];
+        settings.stripBaseHref = (param["stripBaseHref"]=="true");
+        settings.stripSelfNamedAnchors = (param["stripSelfNamedAnchors"]=="true");
+        settings.only7BitPrintablesInURLs = (param["only7BitPrintablesInURLs"]=="true");
+        settings.sevenBitClean = (param["sevenBitClean"]=="true");
+        settings.killWordOnPaste = (param["killWordOnPaste"]=="true");
+        settings.flowToolbars = (param["flowToolbars"]=="true");
+        settings.CharacterMapMode = param["CharacterMapMode"];
+        settings.ListTypeMode = param["ListTypeMode"];
+        settings.showLoading = (param["showLoading"]=="true");
+        settings.showChar = (param["showChar"]=="true");
+        settings.showWord = (param["showWord"]=="true");
+        settings.showHtml = (param["showHtml"]=="true");
+      }
+    }, settings );
+  }
+
+  function init(){
+    var co = getCookie('co_ext_Xinha');
+    if(co!=null){
+      var co_values;
+      var co_entries = co.split('###');
+      for (var i in co_entries) {
+        co_values = co_entries[i].split('=');
+        if(co_values[0]=='plugins') {
+          for(var x = 0; x < document.forms[0].plugins.length; x++) {
+            if(co_values[1].indexOf(document.forms[0].plugins[x].value)!=-1) {
+              document.forms[0].plugins[x].checked = true;
+            }
+          }
+        } else if(co_values[0]!='') {
+          document.getElementById(co_values[0]).value = co_values[1];
+        }
+      }
+    }
+    _onResize();
+  };
+
+  window.onresize = _onResize;
+  window.onload = init;
+  </script>
+</head>
+
+<body>
+  <form action="ext_example-body.html" target="body" name="fsettings" id="fsettings">
+  <h1>Xinha Example</h1>
+    <fieldset>
+      <legend>Settings</legend>
+        <label>
+          Number of Editors: <input type="text" name="num" id="num" value="1" style="width:25;" maxlength="2"/>
+        </label>
+        <label>
+          Language:
+          <select name="lang" id="lang">
+          <option value="en">English</option>
+          <option value="de">German</option>
+          <option value="fr">French</option>
+          <option value="it">Italian</option>
+          <option value="no">Norwegian</option>
+          <option value="pl">Polish</option>
+          <option value="ja">Japanese</option>
+          </select>
+        </label>
+        <label>
+          Skin:
+          <select name="skin" id="skin">
+          <option value="">-- no skin --</option>
+<?php
+       $d = @dir($LocalSkinPath);
+       while (false !== ($entry = $d->read()))  //not a dot file or directory
+       {       if(substr($entry,0,1) != '.')
+               { echo '<option value="' . $entry . '"> ' . $entry . '</option>'."\n";
+               }
+       }
+       $d->close();
+?>
+          </select>
+        </label>
+        <center><input type="button" value="extended Settings" onClick="fExtended();" /></center>
+
+    </fieldset>
+    <fieldset>
+      <legend>Plugins</legend>
+      <div id="div_plugins" style="width:100%; overflow:auto">
+<?php
+       $d = @dir($LocalPluginPath);
+       $dir_array = array();
+       while (false !== ($entry = $d->read()))  //not a dot file or directory
+       {       if(substr($entry,0,1) != '.')
+               {
+                       $dir_array[] = $entry;
+               }
+       }
+       $d->close();
+       sort($dir_array);
+       foreach ($dir_array as $entry)
+       {
+               echo '<label><input type="checkbox" name="plugins" id="plugins" value="' . $entry . '"> ' . $entry . '</label>'."\n";
+       }
+
+?>
+      </div>
+    </fieldset>
+    <center><button type="submit">reload editor</button></center>
+
+        <textarea id="myTextarea0" style="display:none">
+          <p>Lorem ipsum dolor sit amet, consectetuer adipiscing elit.
+          Aliquam et tellus vitae justo varius placerat. Suspendisse iaculis
+          velit semper dolor. Donec gravida tincidunt mi. Curabitur tristique
+          ante elementum turpis. Aliquam nisl. Nulla posuere neque non
+          tellus. Morbi vel nibh. Cum sociis natoque penatibus et magnis dis
+          parturient montes, nascetur ridiculus mus. Nam nec wisi. In wisi.
+          Curabitur pharetra bibendum lectus.</p>
+
+          <ul>
+            <li> Phasellus et massa sed diam viverra semper.  </li>
+            <li> Mauris tincidunt felis in odio.              </li>
+            <li> Nulla placerat nunc ut pede.                 </li>
+            <li> Vivamus ultrices mi sit amet urna.           </li>
+            <li> Quisque sed augue quis nunc laoreet volutpat.</li>
+            <li> Nunc sit amet metus in tortor semper mattis. </li>
+          </ul>
+        </textarea>
+
+  </form>
+  <script type="text/javascript">
+    top.frames["body"].location.href = document.location.href.replace(/ext_example-menu\.php.*/, 'ext_example-body.html')
+    var _oldSubmitHandler = null;
+    if (document.forms[0].onsubmit != null) {
+      _oldSubmitHandler = document.forms[0].onsubmit;
+    }
+    function frame_onSubmit(){
+      var thenewdate = new Date ();
+      thenewdate.setTime(thenewdate.getTime() + (5*24*60*60*1000));
+      var co_value = 'skin=' + document.getElementById('skin').options[document.getElementById('skin').selectedIndex].value + '###' +
+                     'lang=' + document.getElementById('lang').options[document.getElementById('lang').selectedIndex].value + '###' +
+                     'num=' + document.getElementById('num').value + '###';
+      var s_value='';
+      for(var x = 0; x < document.forms[0].plugins.length; x++) {
+        if(document.forms[0].plugins[x].checked)
+          s_value += document.forms[0].plugins[x].value + '/';
+      }
+      if(s_value!='') {
+        co_value += 'plugins=' + s_value + '###'
+      }
+      setCookie('co_ext_Xinha', co_value, thenewdate);
+      if (_oldSubmitHandler != null) {
+        _oldSubmitHandler();
+      }
+    }
+    document.forms[0].onsubmit = frame_onSubmit;
+  </script>
+
+</body>
+</html>
diff --git a/mailboxes/xinha/examples/ext_example.html b/mailboxes/xinha/examples/ext_example.html
new file mode 100644 (file)
index 0000000..ae3e157
--- /dev/null
@@ -0,0 +1,16 @@
+<html>\r
+\r
+  <!--------------------------------------:noTabs=true:tabSize=2:indentSize=2:--\r
+    --  Xinha example frameset.\r
+    --\r
+    --  $HeadURL: http://svn.xinha.python-hosting.com/tags/0.92beta/examples/ext_example.html $\r
+    --  $LastChangedDate: 2007-01-19 23:24:36 +0100 (Fr, 19 Jan 2007) $\r
+    --  $LastChangedRevision: 677 $\r
+    --  $LastChangedBy: ray $\r
+    --------------------------------------------------------------------------->\r
+\r
+  <frameset cols="240,*">\r
+    <frame src="ext_example-menu.php" name="menu" id="menu">\r
+    <frame src="about:blank" name="body" id="body">\r
+  </frameset>\r
+</html>\r
diff --git a/mailboxes/xinha/examples/full_example.css b/mailboxes/xinha/examples/full_example.css
new file mode 100644 (file)
index 0000000..56bf254
--- /dev/null
@@ -0,0 +1,48 @@
+   /*--------------------------------------:noTabs=true:tabSize=2:indentSize=2:--
+    --  Xinha example CSS file.  This is ripped from Trac ;)
+    --
+    --  $HeadURL: http://svn.xinha.python-hosting.com/tags/0.92beta/examples/full_example.css $
+    --  $LastChangedDate: 2007-01-19 23:24:36 +0100 (Fr, 19 Jan 2007) $
+    --  $LastChangedRevision: 677 $
+    --  $LastChangedBy: ray $
+    --------------------------------------------------------------------------*/
+
+ body {
+   background: #fff;
+   color: #000;
+   margin: 10px;
+  }
+  body, th, td {
+   font: normal 13px verdana,arial,'Bitstream Vera Sans',helvetica,sans-serif;
+  }
+  h1, h2, h3, h4 {
+   font-family: arial,verdana,'Bitstream Vera Sans',helvetica,sans-serif;
+   font-weight: bold;
+   letter-spacing: -0.018em;
+  }
+  h1 { font-size: 21px; margin: .15em 1em 0 0 }
+  h2 { font-size: 16px; margin: 2em 0 .5em; }
+  h3 { font-size: 14px; margin: 1.5em 0 .5em; }
+  hr { border: none;  border-top: 1px solid #ccb; margin: 2em 0; }
+  address { font-style: normal }
+  img { border: none }
+
+  :link, :visited {
+   text-decoration: none;
+   color: #b00;
+   border-bottom: 1px dotted #bbb;
+  }
+  :link:hover, :visited:hover {
+   background-color: #eee;
+   color: #555;
+  }
+  h1 :link, h1 :visited ,h2 :link, h2 :visited, h3 :link, h3 :visited,
+  h4 :link, h4 :visited, h5 :link, h5 :visited, h6 :link, h6 :visited {
+   color: inherit;
+  }
+
+  .area_holder
+  {
+    margin:10px;
+  }
+  label {font-size: 11px;}
\ No newline at end of file
diff --git a/mailboxes/xinha/examples/full_example.js b/mailboxes/xinha/examples/full_example.js
new file mode 100644 (file)
index 0000000..64e4735
--- /dev/null
@@ -0,0 +1,97 @@
+var num=1;
+if(window.parent&&window.parent!=window){
+var f=window.parent.menu.document.forms[0];
+_editor_lang=f.lang[f.lang.selectedIndex].value;
+_editor_skin=f.skin[f.skin.selectedIndex].value;
+num=parseInt(f.num.value);
+if(isNaN(num)){
+num=1;
+f.num.value=1;
+}
+xinha_plugins=[];
+for(var x=0;x<f.plugins.length;x++){
+if(f.plugins[x].checked){
+xinha_plugins.push(f.plugins[x].value);
+}
+}
+}
+xinha_editors=[];
+for(var x=0;x<num;x++){
+var ta="myTextarea"+x;
+xinha_editors.push(ta);
+}
+xinha_config=function(){
+var _1=new HTMLArea.Config();
+if(typeof CSS!="undefined"){
+_1.pageStyle="@import url(custom.css);";
+}
+if(typeof Stylist!="undefined"){
+_1.stylistLoadStylesheet(document.location.href.replace(/[^\/]*\.html/,"stylist.css"));
+_1.stylistLoadStyles("p.red_text { color:red }");
+_1.stylistLoadStyles("p.pink_text { color:pink }",{"p.pink_text":"Pretty Pink"});
+}
+if(typeof DynamicCSS!="undefined"){
+_1.pageStyle="@import url(dynamic.css);";
+}
+if(typeof InsertWords!="undefined"){
+var _2=new Object();
+var _3=new Object();
+_2["-- Dropdown Label --"]="";
+_2["onekey"]="onevalue";
+_2["twokey"]="twovalue";
+_2["threekey"]="threevalue";
+_3["-- Insert Keyword --"]="";
+_3["Username"]="%user%";
+_3["Last login date"]="%last_login%";
+_1.InsertWords={combos:[{options:_2,context:"body"},{options:_3,context:"li"}]};
+}
+if(typeof ListType!="undefined"){
+if(window.parent&&window.parent!=window){
+var f=window.parent.menu.document.forms[0];
+_1.ListType.mode=f.elements["ListTypeMode"].options[f.elements["ListTypeMode"].selectedIndex].value;
+}
+}
+if(typeof CharacterMap!="undefined"){
+if(window.parent&&window.parent!=window){
+var f=window.parent.menu.document.forms[0];
+_1.CharacterMap.mode=f.elements["CharacterMapMode"].options[f.elements["CharacterMapMode"].selectedIndex].value;
+}
+}
+if(typeof Filter!="undefined"){
+xinha_config.Filters=["Word","Paragraph"];
+}
+return _1;
+};
+var f=document.forms[0];
+f.innerHTML="";
+var lipsum=document.getElementById("lipsum").innerHTML;
+for(var x=0;x<num;x++){
+var ta="myTextarea"+x;
+var div=document.createElement("div");
+div.className="area_holder";
+var txta=document.createElement("textarea");
+txta.id=ta;
+txta.name=ta;
+txta.value=lipsum;
+txta.style.width="100%";
+txta.style.height="420px";
+div.appendChild(txta);
+f.appendChild(div);
+}
+var submit=document.createElement("input");
+submit.type="submit";
+submit.id="submit";
+submit.value="submit";
+f.appendChild(submit);
+var _oldSubmitHandler=null;
+if(document.forms[0].onsubmit!=null){
+_oldSubmitHandler=document.forms[0].onsubmit;
+}
+function frame_onSubmit(){
+alert(document.getElementById("myTextarea0").value);
+if(_oldSubmitHandler!=null){
+_oldSubmitHandler();
+}
+}
+document.forms[0].onsubmit=frame_onSubmit;
+\r
diff --git a/mailboxes/xinha/examples/simple_example.html b/mailboxes/xinha/examples/simple_example.html
new file mode 100644 (file)
index 0000000..3e4bd9e
--- /dev/null
@@ -0,0 +1,138 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">\r
+<html>\r
+<head>\r
+<meta http-equiv="Content-Type" content="text/html; charset=utf-8">\r
+<title>Simple example of Xinha</title>\r
+<script type="text/javascript">\r
+/************************************************************************\r
+ * Please refer to http://xinha.python-hosting.com/wiki/NewbieGuide\r
+ ************************************************************************\r
+ * You must set _editor_url to the URL (including trailing slash) where\r
+ * where xinha is installed, it's highly recommended to use an absolute URL\r
+ *  eg: _editor_url = "/path/to/xinha/";\r
+ * You may try a relative URL if you wish]\r
+ *  eg: _editor_url = "../";  \r
+ * in this example we do a little regular expression to find the absolute path.\r
+ ************************************************************************/\r
+var _editor_url  = document.location.href.replace(/examples\/simple_example\.html.*/, '')\r
+// And the language we need to use in the editor.\r
+var _editor_lang = "en";\r
+</script>\r
+<!-- Load up the actual editor core -->\r
+<script type="text/javascript" src="../XinhaCore.js"></script>\r
+<script type="text/javascript">\r
+/************************************************************************\r
+ * Plugins you will be using in the editors on this page.\r
+ * List all the plugins you will need, even if not all the editors will\r
+ * use all the plugins.\r
+ ************************************************************************\r
+ * Please refer to http://xinha.python-hosting.com/wiki/Plugins for the\r
+ * list of availables plugins\r
+ ************************************************************************/\r
+var xinha_plugins =\r
+[\r
+ 'CharacterMap',\r
+ 'ContextMenu',\r
+ 'FullScreen',\r
+ 'ListType',\r
+ 'SpellChecker',\r
+ 'Stylist',\r
+ 'SuperClean',\r
+ 'TableOperations'\r
+];\r
+/************************************************************************\r
+ * Names of the textareas you will be turning into editors\r
+ ************************************************************************/\r
+var xinha_editors =\r
+[\r
+  'myTextArea',\r
+  'anotherOne'\r
+];\r
+/************************************************************************\r
+ * Initialisation function\r
+ ************************************************************************/\r
+function xinha_init()\r
+{\r
+  // THIS BIT OF JAVASCRIPT LOADS THE PLUGINS, NO TOUCHING  :)\r
+  if(!Xinha.loadPlugins(xinha_plugins, xinha_init)) return;\r
+  /*************************************************************************\r
+   * We create a default configuration to be used by all the editors.\r
+   * If you wish to configure some of the editors differently this will be\r
+   * done later after editors are initiated.\r
+   ************************************************************************\r
+   * Please refer to http://xinha.python-hosting.com/wiki/Documentation/Customise\r
+   * for the configuration parameters\r
+   ************************************************************************/\r
+  var xinha_config = new Xinha.Config();\r
+  /************************************************************************\r
+   * We first create editors for the textareas.\r
+   * You can do this in two ways, either\r
+   *\r
+   *   xinha_editors   = Xinha.makeEditors(xinha_editors, xinha_config, xinha_plugins);\r
+   *\r
+   * if you want all the editor objects to use the same set of plugins, OR;\r
+   *\r
+   *   xinha_editors = Xinha.makeEditors(xinha_editors, xinha_config);\r
+   *   xinha_editors['myTextArea'].registerPlugins(['Stylist','FullScreen']);\r
+   *   xinha_editors['anotherOne'].registerPlugins(['CSS','SuperClean']);\r
+   *\r
+   * if you want to use a different set of plugins for one or more of the\r
+   * editors.\r
+   ************************************************************************/\r
+  xinha_editors = Xinha.makeEditors(xinha_editors, xinha_config, xinha_plugins);\r
+  /************************************************************************\r
+   * If you want to change the configuration variables of any of the\r
+   * editors,  this is the place to do that, for example you might want to\r
+   * change the width and height of one of the editors, like this...\r
+   ************************************************************************/\r
+  xinha_editors.myTextArea.config.width = '640px';\r
+  xinha_editors.myTextArea.config.height = '480px';\r
+  /************************************************************************\r
+   * Or remove the statusbar on the other one, like this...\r
+   * For every possible configuration, please refer to\r
+   * http://xinha.python-hosting.com/wiki/Documentation/ConfigVariablesList\r
+   ************************************************************************/\r
+  xinha_editors.anotherOne.config.statusBar = false;\r
+  /************************************************************************\r
+   * Finally we "start" the editors, this turns the textareas into\r
+   * Xinha editors.\r
+   ************************************************************************/\r
+  Xinha.startEditors(xinha_editors);\r
+}\r
+window.onload = xinha_init;\r
+</script>\r
+<link type="text/css" rel="stylesheet" title="blue-look" href="../skins/blue-look/skin.css">\r
+<link type="text/css" rel="alternate stylesheet" title="green-look" href="../skins/green-look/skin.css">\r
+<link type="text/css" rel="alternate stylesheet" title="xp-blue" href="../skins/xp-blue/skin.css">\r
+<link type="text/css" rel="alternate stylesheet" title="xp-green" href="../skins/xp-green/skin.css">\r
+<link type="text/css" rel="alternate stylesheet" title="inditreuse" href="../skins/inditreuse/skin.css">\r
+<link type="text/css" rel="alternate stylesheet" title="blue-metallic" href="../skins/blue-metallic/skin.css">\r
+</head>\r
+\r
+<body>\r
+\r
+<form onsubmit="alert(this.myTextArea.value); alert(this.anotherOne.value); return false;">\r
+<textarea id="myTextArea" name="myTextArea" rows="10" cols="80" style="width:100%">\r
+&lt;p&gt;Lorem ipsum dolor sit amet, consectetuer adipiscing elit.\r
+Aliquam et tellus vitae justo varius placerat. Suspendisse iaculis\r
+velit semper dolor. Donec gravida tincidunt mi. Curabitur tristique\r
+ante elementum turpis. Aliquam nisl. Nulla posuere neque non\r
+tellus. Morbi vel nibh. Cum sociis natoque penatibus et magnis dis\r
+parturient montes, nascetur ridiculus mus. Nam nec wisi. In wisi.\r
+Curabitur pharetra bibendum lectus.&lt;/p&gt;\r
+</textarea>\r
+<textarea id="anotherOne" name="anotherOne" rows="10" cols="80" style="width:100%">\r
+&lt;ul&gt;\r
+&lt;li&gt; Phasellus et massa sed diam viverra semper.  &lt;/li&gt;\r
+&lt;li&gt; Mauris tincidunt felis in odio.              &lt;/li&gt;\r
+&lt;li&gt; Nulla placerat nunc ut pede.                 &lt;/li&gt;\r
+&lt;li&gt; Vivamus ultrices mi sit amet urna.           &lt;/li&gt;\r
+&lt;li&gt; Quisque sed augue quis nunc laoreet volutpat.&lt;/li&gt;\r
+&lt;li&gt; Nunc sit amet metus in tortor semper mattis. &lt;/li&gt;\r
+&lt;/ul&gt;\r
+</textarea>\r
+<input type="submit">\r
+</form>\r
+\r
+</body>\r
+</html>
\ No newline at end of file
diff --git a/mailboxes/xinha/examples/stylist.css b/mailboxes/xinha/examples/stylist.css
new file mode 100644 (file)
index 0000000..f648787
--- /dev/null
@@ -0,0 +1,31 @@
+  /*--------------------------------------:noTabs=true:tabSize=2:indentSize=2:--
+    --  Stylist plugin example CSS file.  Used by full_example.js
+    --  when the Stylist plugin is included in an auto-generated example.
+    --
+    --  $HeadURL: http://svn.xinha.python-hosting.com/tags/0.92beta/examples/stylist.css $
+    --  $LastChangedDate: 2007-01-19 23:24:36 +0100 (Fr, 19 Jan 2007) $
+    --  $LastChangedRevision: 677 $
+    --  $LastChangedBy: ray $
+    --------------------------------------------------------------------------*/
+
+.bluetext
+{
+  color:blue;
+}
+
+p.blue_paragraph
+{
+  color:darkblue;
+}
+
+li.green_list_item
+{
+  color:green;
+}
+
+h1.webdings_lvl_1
+{
+  font-family:webdings;
+}
+
+img.polaroid { border:1px solid black; background-color:white; padding:10px; padding-bottom:30px; }
\ No newline at end of file
diff --git a/mailboxes/xinha/examples/testbed.html b/mailboxes/xinha/examples/testbed.html
new file mode 100644 (file)
index 0000000..c58d4d3
--- /dev/null
@@ -0,0 +1,191 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE html
+     PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
+    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
+<head>
+
+  <!--------------------------------------:noTabs=true:tabSize=2:indentSize=2:--
+    --  Xinha example usage.  This file shows how a developer might make use of
+    --  Xinha, it forms the primary example file for the entire Xinha project.
+    --  This file can be copied and used as a template for development by the
+    --  end developer who should simply removed the area indicated at the bottom
+    --  of the file to remove the auto-example-generating code and allow for the
+    --  use of the file as a boilerplate.
+    --
+    --  $HeadURL: http://svn.xinha.python-hosting.com/tags/0.92beta/examples/testbed.html $
+    --  $LastChangedDate: 2007-01-19 23:24:36 +0100 (Fr, 19 Jan 2007) $
+    --  $LastChangedRevision: 677 $
+    --  $LastChangedBy: ray $
+    --------------------------------------------------------------------------->
+
+  <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+  <title>Example of Xinha</title>
+  <link rel="stylesheet" href="full_example.css" />
+
+  <script type="text/javascript">
+    // You must set _editor_url to the URL (including trailing slash) where
+    // where xinha is installed, it's highly recommended to use an absolute URL
+    //  eg: _editor_url = "/path/to/xinha/";
+    // You may try a relative URL if you wish]
+    //  eg: _editor_url = "../";
+    // in this example we do a little regular expression to find the absolute path.
+    _editor_url  = document.location.href.replace(/examples\/.*/, '')
+    _editor_lang = "en";      // And the language we need to use in the editor.
+  </script>
+
+  <!-- Load up the actual editor core -->
+  <script type="text/javascript" src="../htmlarea.js"></script>
+
+  <script type="text/javascript">
+    xinha_editors = null;
+    xinha_init    = null;
+    xinha_config  = null;
+    xinha_plugins = null;
+
+    // This contains the names of textareas we will make into Xinha editors
+    xinha_init = xinha_init ? xinha_init : function()
+    {
+      /** STEP 1 ***************************************************************
+       * First, what are the plugins you will be using in the editors on this
+       * page.  List all the plugins you will need, even if not all the editors
+       * will use all the plugins.
+       ************************************************************************/
+
+      xinha_plugins = xinha_plugins ? xinha_plugins :
+      [
+        'CharacterMap', 'SpellChecker', 'Linker'
+      ];
+             // THIS BIT OF JAVASCRIPT LOADS THE PLUGINS, NO TOUCHING  :)
+             if(!Xinha.loadPlugins(xinha_plugins, xinha_init)) return;
+
+      /** STEP 2 ***************************************************************
+       * Now, what are the names of the textareas you will be turning into
+       * editors?
+       ************************************************************************/
+
+      xinha_editors = xinha_editors ? xinha_editors :
+      [
+        'myTextArea'
+      ];
+
+      /** STEP 3 ***************************************************************
+       * We create a default configuration to be used by all the editors.
+       * If you wish to configure some of the editors differently this will be
+       * done in step 4.
+       *
+       * If you want to modify the default config you might do something like this.
+       *
+       *   xinha_config = new Xinha.Config();
+       *   xinha_config.width  = 640;
+       *   xinha_config.height = 420;
+       *
+       *************************************************************************/
+
+       xinha_config = xinha_config ? xinha_config : new Xinha.Config();
+       xinha_config.fullPage = true;
+       xinha_config.CharacterMap.mode = 'panel';
+/*
+       // We can load an external stylesheet like this - NOTE : YOU MUST GIVE AN ABSOLUTE URL
+      //  otherwise it won't work!
+      xinha_config.stylistLoadStylesheet(document.location.href.replace(/[^\/]*\.html/, 'stylist.css'));
+
+      // Or we can load styles directly
+      xinha_config.stylistLoadStyles('p.red_text { color:red }');
+
+      // If you want to provide "friendly" names you can do so like
+      // (you can do this for stylistLoadStylesheet as well)
+      xinha_config.stylistLoadStyles('p.pink_text { color:pink }', {'p.pink_text' : 'Pretty Pink'});
+*/
+      /** STEP 3 ***************************************************************
+       * We first create editors for the textareas.
+       *
+       * You can do this in two ways, either
+       *
+       *   xinha_editors   = Xinha.makeEditors(xinha_editors, xinha_config, xinha_plugins);
+       *
+       * if you want all the editor objects to use the same set of plugins, OR;
+       *
+       *   xinha_editors = Xinha.makeEditors(xinha_editors, xinha_config);
+       *   xinha_editors['myTextArea'].registerPlugins(['Stylist','FullScreen']);
+       *   xinha_editors['anotherOne'].registerPlugins(['CSS','SuperClean']);
+       *
+       * if you want to use a different set of plugins for one or more of the
+       * editors.
+       ************************************************************************/
+
+      xinha_editors   = Xinha.makeEditors(xinha_editors, xinha_config, xinha_plugins);
+
+      /** STEP 4 ***************************************************************
+       * If you want to change the configuration variables of any of the
+       * editors,  this is the place to do that, for example you might want to
+       * change the width and height of one of the editors, like this...
+       *
+       *   xinha_editors.myTextArea.config.width  = 640;
+       *   xinha_editors.myTextArea.config.height = 480;
+       *
+       ************************************************************************/
+
+
+      /** STEP 5 ***************************************************************
+       * Finally we "start" the editors, this turns the textareas into
+       * Xinha editors.
+       ************************************************************************/
+
+      Xinha.startEditors(xinha_editors);
+      window.onload = null;
+    }
+
+    window.onload   = xinha_init;
+    // window.onunload = Xinha.collectGarbageForIE;
+  </script>
+</head>
+
+<body>
+
+  <form action="javascript:var x = document.getElementById('editors_here');alert(x.myTextArea.value);" id="editors_here" onsubmit="alert(this.myTextArea.value);">
+    <textarea id="myTextArea" name="myTextArea" style="width:100%;height:320px;">
+      &lt;html&gt;
+      &lt;head&gt;
+        &lt;title&gt;Hello&lt;/title&gt;
+        &lt;style type="text/css"&gt;
+          li { color:red; }
+        &lt;/style&gt;
+      &lt;/head&gt;
+      &lt;body&gt;
+      &lt;img src="http://xinha.python-hosting.com/trac/logo.jpg" usemap="#m1"&gt;
+      &lt;map name="m1"&gt;
+      &lt;area shape="rect" coords="137,101,255,124" href="http://www.mydomain.com"&gt;
+      &lt;/map&gt;
+
+      &lt;p&gt;
+        Lorem ipsum dolor sit amet, consectetuer adipiscing elit.
+        Aliquam et tellus vitae justo varius placerat. Suspendisse iaculis
+        velit semper dolor. Donec gravida tincidunt mi. Curabitur tristique
+        ante elementum turpis. Aliquam nisl. Nulla posuere neque non
+        tellus. Morbi vel nibh. Cum sociis natoque penatibus et magnis dis
+        parturient montes, nascetur ridiculus mus. Nam nec wisi. In wisi.
+        Curabitur pharetra bibendum lectus.
+      &lt;/p&gt;
+
+      &lt;ul&gt;
+        &lt;li&gt; Phasellus et massa sed diam viverra semper.  &lt;/li&gt;
+        &lt;li&gt; Mauris tincidunt felis in odio.              &lt;/li&gt;
+        &lt;li&gt; Nulla placerat nunc ut pede.                 &lt;/li&gt;
+        &lt;li&gt; Vivamus ultrices mi sit amet urna.           &lt;/li&gt;
+        &lt;li&gt; Quisque sed augue quis nunc laoreet volutpat.&lt;/li&gt;
+        &lt;li&gt; Nunc sit amet metus in tortor semper mattis. &lt;/li&gt;
+      &lt;/ul&gt;
+      &lt;/body&gt;
+      &lt;/html&gt;
+    </textarea>
+
+    <input type="submit" /> <input type="reset" />
+  </form>
+  <script language="javascript">
+    document.write(document.compatMode);
+  </script>
+  <a href="#" onclick="xinha_editors.myTextArea.hidePanels();">Hide</a>
+  <a href="#" onclick="xinha_editors.myTextArea.showPanels();">Show</a>
+</body>
+</html>
\ No newline at end of file
diff --git a/mailboxes/xinha/htmlarea.js b/mailboxes/xinha/htmlarea.js
new file mode 100644 (file)
index 0000000..014c4db
--- /dev/null
@@ -0,0 +1,23 @@
\r
+  /*--------------------------------------:noTabs=true:tabSize=2:indentSize=2:--\r
+    --  COMPATABILITY FILE\r
+    --  htmlarea.js is now XinhaCore.js  \r
+    --\r
+    --  $HeadURL: http://svn.xinha.python-hosting.com/tags/0.92beta/htmlarea.js $\r
+    --  $LastChangedDate: 2007-01-15 15:28:57 +0100 (Mo, 15 Jan 2007) $\r
+    --  $LastChangedRevision: 659 $\r
+    --  $LastChangedBy: gogo $\r
+    --------------------------------------------------------------------------*/\r
+    \r
+if ( typeof _editor_url == "string" )\r
+{\r
+  // Leave exactly one backslash at the end of _editor_url\r
+  _editor_url = _editor_url.replace(/\x2f*$/, '/');\r
+}\r
+else\r
+{\r
+  alert("WARNING: _editor_url is not set!  You should set this variable to the editor files path; it should preferably be an absolute path, like in '/htmlarea/', but it can be relative if you prefer.  Further we will try to load the editor files correctly but we'll probably fail.");\r
+  _editor_url = '';\r
+}\r
+\r
+document.write('<script type="text/javascript" src="'+_editor_url+'XinhaCore.js"></script>');
\ No newline at end of file
diff --git a/mailboxes/xinha/images/de/bold.gif b/mailboxes/xinha/images/de/bold.gif
new file mode 100644 (file)
index 0000000..21d286f
Binary files /dev/null and b/mailboxes/xinha/images/de/bold.gif differ
diff --git a/mailboxes/xinha/images/de/italic.gif b/mailboxes/xinha/images/de/italic.gif
new file mode 100644 (file)
index 0000000..0bf7947
Binary files /dev/null and b/mailboxes/xinha/images/de/italic.gif differ
diff --git a/mailboxes/xinha/images/de/underline.gif b/mailboxes/xinha/images/de/underline.gif
new file mode 100644 (file)
index 0000000..0545489
Binary files /dev/null and b/mailboxes/xinha/images/de/underline.gif differ
diff --git a/mailboxes/xinha/images/ed_about.gif b/mailboxes/xinha/images/ed_about.gif
new file mode 100644 (file)
index 0000000..0f28d40
Binary files /dev/null and b/mailboxes/xinha/images/ed_about.gif differ
diff --git a/mailboxes/xinha/images/ed_align.gif b/mailboxes/xinha/images/ed_align.gif
new file mode 100644 (file)
index 0000000..6c60d50
Binary files /dev/null and b/mailboxes/xinha/images/ed_align.gif differ
diff --git a/mailboxes/xinha/images/ed_align_center.gif b/mailboxes/xinha/images/ed_align_center.gif
new file mode 100644 (file)
index 0000000..75845b7
Binary files /dev/null and b/mailboxes/xinha/images/ed_align_center.gif differ
diff --git a/mailboxes/xinha/images/ed_align_justify.gif b/mailboxes/xinha/images/ed_align_justify.gif
new file mode 100644 (file)
index 0000000..568c595
Binary files /dev/null and b/mailboxes/xinha/images/ed_align_justify.gif differ
diff --git a/mailboxes/xinha/images/ed_align_left.gif b/mailboxes/xinha/images/ed_align_left.gif
new file mode 100644 (file)
index 0000000..8320a2a
Binary files /dev/null and b/mailboxes/xinha/images/ed_align_left.gif differ
diff --git a/mailboxes/xinha/images/ed_align_right.gif b/mailboxes/xinha/images/ed_align_right.gif
new file mode 100644 (file)
index 0000000..bc6f651
Binary files /dev/null and b/mailboxes/xinha/images/ed_align_right.gif differ
diff --git a/mailboxes/xinha/images/ed_blank.gif b/mailboxes/xinha/images/ed_blank.gif
new file mode 100644 (file)
index 0000000..1ea396b
Binary files /dev/null and b/mailboxes/xinha/images/ed_blank.gif differ
diff --git a/mailboxes/xinha/images/ed_buttons_main.gif b/mailboxes/xinha/images/ed_buttons_main.gif
new file mode 100644 (file)
index 0000000..bc1962b
Binary files /dev/null and b/mailboxes/xinha/images/ed_buttons_main.gif differ
diff --git a/mailboxes/xinha/images/ed_charmap.gif b/mailboxes/xinha/images/ed_charmap.gif
new file mode 100644 (file)
index 0000000..289aa7c
Binary files /dev/null and b/mailboxes/xinha/images/ed_charmap.gif differ
diff --git a/mailboxes/xinha/images/ed_clearfonts.gif b/mailboxes/xinha/images/ed_clearfonts.gif
new file mode 100644 (file)
index 0000000..38c52a8
Binary files /dev/null and b/mailboxes/xinha/images/ed_clearfonts.gif differ
diff --git a/mailboxes/xinha/images/ed_color_bg.gif b/mailboxes/xinha/images/ed_color_bg.gif
new file mode 100644 (file)
index 0000000..899f133
Binary files /dev/null and b/mailboxes/xinha/images/ed_color_bg.gif differ
diff --git a/mailboxes/xinha/images/ed_color_fg.gif b/mailboxes/xinha/images/ed_color_fg.gif
new file mode 100644 (file)
index 0000000..292ab87
Binary files /dev/null and b/mailboxes/xinha/images/ed_color_fg.gif differ
diff --git a/mailboxes/xinha/images/ed_copy.gif b/mailboxes/xinha/images/ed_copy.gif
new file mode 100644 (file)
index 0000000..0e440eb
Binary files /dev/null and b/mailboxes/xinha/images/ed_copy.gif differ
diff --git a/mailboxes/xinha/images/ed_custom.gif b/mailboxes/xinha/images/ed_custom.gif
new file mode 100644 (file)
index 0000000..1444030
Binary files /dev/null and b/mailboxes/xinha/images/ed_custom.gif differ
diff --git a/mailboxes/xinha/images/ed_cut.gif b/mailboxes/xinha/images/ed_cut.gif
new file mode 100644 (file)
index 0000000..23fbf80
Binary files /dev/null and b/mailboxes/xinha/images/ed_cut.gif differ
diff --git a/mailboxes/xinha/images/ed_delete.gif b/mailboxes/xinha/images/ed_delete.gif
new file mode 100644 (file)
index 0000000..aff568e
Binary files /dev/null and b/mailboxes/xinha/images/ed_delete.gif differ
diff --git a/mailboxes/xinha/images/ed_format_bold.gif b/mailboxes/xinha/images/ed_format_bold.gif
new file mode 100644 (file)
index 0000000..78686d1
Binary files /dev/null and b/mailboxes/xinha/images/ed_format_bold.gif differ
diff --git a/mailboxes/xinha/images/ed_format_italic.gif b/mailboxes/xinha/images/ed_format_italic.gif
new file mode 100644 (file)
index 0000000..2d3baf2
Binary files /dev/null and b/mailboxes/xinha/images/ed_format_italic.gif differ
diff --git a/mailboxes/xinha/images/ed_format_strike.gif b/mailboxes/xinha/images/ed_format_strike.gif
new file mode 100644 (file)
index 0000000..a5b1429
Binary files /dev/null and b/mailboxes/xinha/images/ed_format_strike.gif differ
diff --git a/mailboxes/xinha/images/ed_format_sub.gif b/mailboxes/xinha/images/ed_format_sub.gif
new file mode 100644 (file)
index 0000000..6c77d74
Binary files /dev/null and b/mailboxes/xinha/images/ed_format_sub.gif differ
diff --git a/mailboxes/xinha/images/ed_format_sup.gif b/mailboxes/xinha/images/ed_format_sup.gif
new file mode 100644 (file)
index 0000000..4ecb9a1
Binary files /dev/null and b/mailboxes/xinha/images/ed_format_sup.gif differ
diff --git a/mailboxes/xinha/images/ed_format_underline.gif b/mailboxes/xinha/images/ed_format_underline.gif
new file mode 100644 (file)
index 0000000..4bc47a1
Binary files /dev/null and b/mailboxes/xinha/images/ed_format_underline.gif differ
diff --git a/mailboxes/xinha/images/ed_help.gif b/mailboxes/xinha/images/ed_help.gif
new file mode 100644 (file)
index 0000000..d5f7d63
Binary files /dev/null and b/mailboxes/xinha/images/ed_help.gif differ
diff --git a/mailboxes/xinha/images/ed_hr.gif b/mailboxes/xinha/images/ed_hr.gif
new file mode 100644 (file)
index 0000000..ec5c778
Binary files /dev/null and b/mailboxes/xinha/images/ed_hr.gif differ
diff --git a/mailboxes/xinha/images/ed_html.gif b/mailboxes/xinha/images/ed_html.gif
new file mode 100644 (file)
index 0000000..026da4e
Binary files /dev/null and b/mailboxes/xinha/images/ed_html.gif differ
diff --git a/mailboxes/xinha/images/ed_image.gif b/mailboxes/xinha/images/ed_image.gif
new file mode 100644 (file)
index 0000000..1af79c3
Binary files /dev/null and b/mailboxes/xinha/images/ed_image.gif differ
diff --git a/mailboxes/xinha/images/ed_indent_less.gif b/mailboxes/xinha/images/ed_indent_less.gif
new file mode 100644 (file)
index 0000000..7dda02a
Binary files /dev/null and b/mailboxes/xinha/images/ed_indent_less.gif differ
diff --git a/mailboxes/xinha/images/ed_indent_more.gif b/mailboxes/xinha/images/ed_indent_more.gif
new file mode 100644 (file)
index 0000000..c34b47e
Binary files /dev/null and b/mailboxes/xinha/images/ed_indent_more.gif differ
diff --git a/mailboxes/xinha/images/ed_killword.gif b/mailboxes/xinha/images/ed_killword.gif
new file mode 100644 (file)
index 0000000..fd934a7
Binary files /dev/null and b/mailboxes/xinha/images/ed_killword.gif differ
diff --git a/mailboxes/xinha/images/ed_left_to_right.gif b/mailboxes/xinha/images/ed_left_to_right.gif
new file mode 100644 (file)
index 0000000..5e95ea5
Binary files /dev/null and b/mailboxes/xinha/images/ed_left_to_right.gif differ
diff --git a/mailboxes/xinha/images/ed_link.gif b/mailboxes/xinha/images/ed_link.gif
new file mode 100644 (file)
index 0000000..76fd537
Binary files /dev/null and b/mailboxes/xinha/images/ed_link.gif differ
diff --git a/mailboxes/xinha/images/ed_list_bullet.gif b/mailboxes/xinha/images/ed_list_bullet.gif
new file mode 100644 (file)
index 0000000..e37e84b
Binary files /dev/null and b/mailboxes/xinha/images/ed_list_bullet.gif differ
diff --git a/mailboxes/xinha/images/ed_list_num.gif b/mailboxes/xinha/images/ed_list_num.gif
new file mode 100644 (file)
index 0000000..630cbfd
Binary files /dev/null and b/mailboxes/xinha/images/ed_list_num.gif differ
diff --git a/mailboxes/xinha/images/ed_overwrite.gif b/mailboxes/xinha/images/ed_overwrite.gif
new file mode 100644 (file)
index 0000000..e7a8914
Binary files /dev/null and b/mailboxes/xinha/images/ed_overwrite.gif differ
diff --git a/mailboxes/xinha/images/ed_paste.gif b/mailboxes/xinha/images/ed_paste.gif
new file mode 100644 (file)
index 0000000..81b53a0
Binary files /dev/null and b/mailboxes/xinha/images/ed_paste.gif differ
diff --git a/mailboxes/xinha/images/ed_print.gif b/mailboxes/xinha/images/ed_print.gif
new file mode 100644 (file)
index 0000000..fb2bf80
Binary files /dev/null and b/mailboxes/xinha/images/ed_print.gif differ
diff --git a/mailboxes/xinha/images/ed_redo.gif b/mailboxes/xinha/images/ed_redo.gif
new file mode 100644 (file)
index 0000000..3d73dfc
Binary files /dev/null and b/mailboxes/xinha/images/ed_redo.gif differ
diff --git a/mailboxes/xinha/images/ed_right_to_left.gif b/mailboxes/xinha/images/ed_right_to_left.gif
new file mode 100644 (file)
index 0000000..9b255bd
Binary files /dev/null and b/mailboxes/xinha/images/ed_right_to_left.gif differ
diff --git a/mailboxes/xinha/images/ed_rmformat.gif b/mailboxes/xinha/images/ed_rmformat.gif
new file mode 100644 (file)
index 0000000..09f102a
Binary files /dev/null and b/mailboxes/xinha/images/ed_rmformat.gif differ
diff --git a/mailboxes/xinha/images/ed_save.gif b/mailboxes/xinha/images/ed_save.gif
new file mode 100644 (file)
index 0000000..82262d0
Binary files /dev/null and b/mailboxes/xinha/images/ed_save.gif differ
diff --git a/mailboxes/xinha/images/ed_save.png b/mailboxes/xinha/images/ed_save.png
new file mode 100644 (file)
index 0000000..881fe5d
Binary files /dev/null and b/mailboxes/xinha/images/ed_save.png differ
diff --git a/mailboxes/xinha/images/ed_saveas.gif b/mailboxes/xinha/images/ed_saveas.gif
new file mode 100644 (file)
index 0000000..4edd988
Binary files /dev/null and b/mailboxes/xinha/images/ed_saveas.gif differ
diff --git a/mailboxes/xinha/images/ed_selectall.gif b/mailboxes/xinha/images/ed_selectall.gif
new file mode 100644 (file)
index 0000000..9acf0a0
Binary files /dev/null and b/mailboxes/xinha/images/ed_selectall.gif differ
diff --git a/mailboxes/xinha/images/ed_show_border.gif b/mailboxes/xinha/images/ed_show_border.gif
new file mode 100644 (file)
index 0000000..42849b7
Binary files /dev/null and b/mailboxes/xinha/images/ed_show_border.gif differ
diff --git a/mailboxes/xinha/images/ed_splitblock.gif b/mailboxes/xinha/images/ed_splitblock.gif
new file mode 100644 (file)
index 0000000..1f1582b
Binary files /dev/null and b/mailboxes/xinha/images/ed_splitblock.gif differ
diff --git a/mailboxes/xinha/images/ed_splitcel.gif b/mailboxes/xinha/images/ed_splitcel.gif
new file mode 100644 (file)
index 0000000..a6e5ab5
Binary files /dev/null and b/mailboxes/xinha/images/ed_splitcel.gif differ
diff --git a/mailboxes/xinha/images/ed_undo.gif b/mailboxes/xinha/images/ed_undo.gif
new file mode 100644 (file)
index 0000000..319242f
Binary files /dev/null and b/mailboxes/xinha/images/ed_undo.gif differ
diff --git a/mailboxes/xinha/images/ed_word_cleaner.gif b/mailboxes/xinha/images/ed_word_cleaner.gif
new file mode 100644 (file)
index 0000000..d9b0759
Binary files /dev/null and b/mailboxes/xinha/images/ed_word_cleaner.gif differ
diff --git a/mailboxes/xinha/images/fr/bold.gif b/mailboxes/xinha/images/fr/bold.gif
new file mode 100644 (file)
index 0000000..8a0f657
Binary files /dev/null and b/mailboxes/xinha/images/fr/bold.gif differ
diff --git a/mailboxes/xinha/images/fr/strikethrough.gif b/mailboxes/xinha/images/fr/strikethrough.gif
new file mode 100644 (file)
index 0000000..5707aef
Binary files /dev/null and b/mailboxes/xinha/images/fr/strikethrough.gif differ
diff --git a/mailboxes/xinha/images/fr/underline.gif b/mailboxes/xinha/images/fr/underline.gif
new file mode 100644 (file)
index 0000000..4ecaf22
Binary files /dev/null and b/mailboxes/xinha/images/fr/underline.gif differ
diff --git a/mailboxes/xinha/images/fullscreen_maximize.gif b/mailboxes/xinha/images/fullscreen_maximize.gif
new file mode 100644 (file)
index 0000000..211c1c5
Binary files /dev/null and b/mailboxes/xinha/images/fullscreen_maximize.gif differ
diff --git a/mailboxes/xinha/images/fullscreen_minimize.gif b/mailboxes/xinha/images/fullscreen_minimize.gif
new file mode 100644 (file)
index 0000000..f679e5a
Binary files /dev/null and b/mailboxes/xinha/images/fullscreen_minimize.gif differ
diff --git a/mailboxes/xinha/images/insert_table.gif b/mailboxes/xinha/images/insert_table.gif
new file mode 100644 (file)
index 0000000..a8f4253
Binary files /dev/null and b/mailboxes/xinha/images/insert_table.gif differ
diff --git a/mailboxes/xinha/images/insertfilelink.gif b/mailboxes/xinha/images/insertfilelink.gif
new file mode 100644 (file)
index 0000000..099f04d
Binary files /dev/null and b/mailboxes/xinha/images/insertfilelink.gif differ
diff --git a/mailboxes/xinha/images/insertmacro.png b/mailboxes/xinha/images/insertmacro.png
new file mode 100644 (file)
index 0000000..3c874be
Binary files /dev/null and b/mailboxes/xinha/images/insertmacro.png differ
diff --git a/mailboxes/xinha/images/tidy.gif b/mailboxes/xinha/images/tidy.gif
new file mode 100644 (file)
index 0000000..a881c64
Binary files /dev/null and b/mailboxes/xinha/images/tidy.gif differ
diff --git a/mailboxes/xinha/images/toggle_borders.gif b/mailboxes/xinha/images/toggle_borders.gif
new file mode 100644 (file)
index 0000000..95bc5be
Binary files /dev/null and b/mailboxes/xinha/images/toggle_borders.gif differ
diff --git a/mailboxes/xinha/images/xinha_logo.gif b/mailboxes/xinha/images/xinha_logo.gif
new file mode 100644 (file)
index 0000000..346e313
Binary files /dev/null and b/mailboxes/xinha/images/xinha_logo.gif differ
diff --git a/mailboxes/xinha/lang/b5.js b/mailboxes/xinha/lang/b5.js
new file mode 100644 (file)
index 0000000..f000699
--- /dev/null
@@ -0,0 +1,29 @@
+// I18N constants -- UTF-8\r
+// by Dave Lo -- dlo@interactivetools.com\r
+{\r
+  "Bold": "粗體",\r
+  "Italic": "斜體",\r
+  "Underline": "底線",\r
+  "Strikethrough": "刪除線",\r
+  "Subscript": "下標",\r
+  "Superscript": "上標",\r
+  "Justify Left": "位置靠左",\r
+  "Justify Center": "位置居中",\r
+  "Justify Right": "位置靠右",\r
+  "Justify Full": "位置左右平等",\r
+  "Ordered List": "順序清單",\r
+  "Bulleted List": "無序清單",\r
+  "Decrease Indent": "減小行前空白",\r
+  "Increase Indent": "加寬行前空白",\r
+  "Font Color": "文字顏色",\r
+  "Background Color": "背景顏色",\r
+  "Horizontal Rule": "水平線",\r
+  "Insert Web Link": "插入連結",\r
+  "Insert/Modify Image": "插入圖形",\r
+  "Insert Table": "插入表格",\r
+  "Toggle HTML Source": "切換HTML原始碼",\r
+  "Enlarge Editor": "放大",\r
+  "About this editor": "關於 HTMLArea",\r
+  "Help using editor": "說明",\r
+  "Current style": "字體例子"\r
+}\r
diff --git a/mailboxes/xinha/lang/ch.js b/mailboxes/xinha/lang/ch.js
new file mode 100644 (file)
index 0000000..4227fa4
--- /dev/null
@@ -0,0 +1,56 @@
+// I18N constants\r
+\r
+// LANG: "ch", ENCODING: UTF-8\r
+// Samuel Stone, http://stonemicro.com/\r
+\r
+{\r
+  "Bold": "粗體",\r
+  "Italic": "斜體",\r
+  "Underline": "底線",\r
+  "Strikethrough": "刪線",\r
+  "Subscript": "下標",\r
+  "Superscript": "上標",\r
+  "Justify Left": "靠左",\r
+  "Justify Center": "居中",\r
+  "Justify Right": "靠右",\r
+  "Justify Full": "整齊",\r
+  "Ordered List": "順序清單",\r
+  "Bulleted List": "無序清單",\r
+  "Decrease Indent": "伸排",\r
+  "Increase Indent": "縮排",\r
+  "Font Color": "文字顏色",\r
+  "Background Color": "背景顏色",\r
+  "Horizontal Rule": "水平線",\r
+  "Insert Web Link": "插入連結",\r
+  "Insert/Modify Image": "插入圖像",\r
+  "Insert Table": "插入表格",\r
+  "Toggle HTML Source": "切換HTML原始碼",\r
+  "Enlarge Editor": "伸出編輯系統",\r
+  "About this editor": "關於 HTMLArea",\r
+  "Help using editor": "說明",\r
+  "Current style": "字體例子",\r
+  "Undoes your last action": "回原",\r
+  "Redoes your last action": "重来",\r
+  "Cut selection": "剪制选项",\r
+  "Copy selection": "复制选项",\r
+  "Paste from clipboard": "贴上",\r
+  "Direction left to right": "从左到右",\r
+  "Direction right to left": "从右到左",\r
+  "OK": "好",\r
+  "Cancel": "取消",\r
+  "Path": "途徑",\r
+  "You are in TEXT MODE.  Use the [<>] button to switch back to WYSIWYG.": "你在用純字編輯方式.  用 [<>] 按鈕轉回 所見即所得 編輯方式.",\r
+  "The full screen mode is known to cause problems with Internet Explorer, due to browser bugs that we weren": "整頁式在Internet Explorer 上常出問題, 因為這是 Internet Explorer 的無名問題,我們無法解決。你可能看見一些垃圾,或遇到其他問題。我們已警告了你. 如果要轉到 正頁式 請按 好.",\r
+  "The Paste button does not work in Mozilla based web browsers (technical security reasons). Press CTRL-V on your keyboard to paste directly.": "The Paste button does not work in Mozilla based web browsers (technical security reasons). Press CTRL-V on your keyboard to paste directly.",\r
+  "Cancel": "取消",\r
+  "Insert/Modify Link": "插入/改寫連結",\r
+  "New window (_blank)": "新窗户(_blank)",\r
+  "None (use implicit)": "無(use implicit)",\r
+  "Other": "其他",\r
+  "Same frame (_self)": "本匡 (_self)",\r
+  "Target:": "目標匡:",\r
+  "Title (tooltip):": "主題 (tooltip):",\r
+  "Top frame (_top)": "上匡 (_top)",\r
+  "URL:": "網址:",\r
+  "You must enter the URL where this link points to": "你必須輸入你要连结的網址"\r
+}\r
diff --git a/mailboxes/xinha/lang/cz.js b/mailboxes/xinha/lang/cz.js
new file mode 100644 (file)
index 0000000..87faec8
--- /dev/null
@@ -0,0 +1,50 @@
+// I18N constants\r
+\r
+// LANG: "cz", ENCODING: UTF-8\r
+// Author: Jiri Löw, <jirilow@jirilow.com>\r
+\r
+// FOR TRANSLATORS:\r
+//\r
+//   1. PLEASE PUT YOUR CONTACT INFO IN THE ABOVE LINE\r
+//      (at least a valid email address)\r
+//\r
+//   2. PLEASE TRY TO USE UTF-8 FOR ENCODING;\r
+//      (if this is not possible, please include a comment\r
+//       that states what encoding is necessary.)\r
+\r
+{\r
+  "Bold": "Tučně",\r
+  "Italic": "Kurzíva",\r
+  "Underline": "Podtržení",\r
+  "Strikethrough": "Přeškrtnutí",\r
+  "Subscript": "Dolní index",\r
+  "Superscript": "Horní index",\r
+  "Justify Left": "Zarovnat doleva",\r
+  "Justify Center": "Na střed",\r
+  "Justify Right": "Zarovnat doprava",\r
+  "Justify Full": "Zarovnat do stran",\r
+  "Ordered List": "Seznam",\r
+  "Bulleted List": "Odrážky",\r
+  "Decrease Indent": "Předsadit",\r
+  "Increase Indent": "Odsadit",\r
+  "Font Color": "Barva písma",\r
+  "Background Color": "Barva pozadí",\r
+  "Horizontal Rule": "Vodorovná čára",\r
+  "Insert Web Link": "Vložit odkaz",\r
+  "Insert/Modify Image": "Vložit obrázek",\r
+  "Insert Table": "Vložit tabulku",\r
+  "Toggle HTML Source": "Přepnout HTML",\r
+  "Enlarge Editor": "Nové okno editoru",\r
+  "About this editor": "O této aplikaci",\r
+  "Help using editor": "Nápověda aplikace",\r
+  "Current style": "Zvolený styl",\r
+  "Undoes your last action": "Vrátí poslední akci",\r
+  "Redoes your last action": "Opakuje poslední akci",\r
+  "Cut selection": "Vyjmout",\r
+  "Copy selection": "Kopírovat",\r
+  "Paste from clipboard": "Vložit",\r
+  "OK": "OK",\r
+  "Cancel": "Zrušit",\r
+  "Path": "Cesta",\r
+  "You are in TEXT MODE.  Use the [<>] button to switch back to WYSIWYG.": "Jste v TEXTOVÉM REŽIMU.  Použijte tlačítko [<>] pro přepnutí do WYSIWIG."\r
+}\r
diff --git a/mailboxes/xinha/lang/da.js b/mailboxes/xinha/lang/da.js
new file mode 100644 (file)
index 0000000..5a14e2b
--- /dev/null
@@ -0,0 +1,30 @@
+// LANG: "da", ENCODING: UTF-8\r
+// Author: rene, <rene@laerke.net>\r
+\r
+{\r
+  "Bold": "Fed",\r
+  "Italic": "Kursiv",\r
+  "Underline": "Understregning",\r
+  "Strikethrough": "Overstregning ",\r
+  "Subscript": "Sænket skrift",\r
+  "Superscript": "Hævet skrift",\r
+  "Justify Left": "Venstrejuster",\r
+  "Justify Center": "Centrer",\r
+  "Justify Right": "Højrejuster",\r
+  "Justify Full": "Lige margener",\r
+  "Ordered List": "Opstilling med tal",\r
+  "Bulleted List": "Opstilling med punkttegn",\r
+  "Decrease Indent": "Formindsk indrykning",\r
+  "Increase Indent": "Forøg indrykning",\r
+  "Font Color": "Skriftfarve",\r
+  "Background Color": "Baggrundsfarve",\r
+  "Horizontal Rule": "Horisontal linie",\r
+  "Insert Web Link": "Indsæt hyperlink",\r
+  "Insert/Modify Image": "Indsæt billede",\r
+  "Insert Table": "Indsæt tabel",\r
+  "Toggle HTML Source": "HTML visning",\r
+  "Enlarge Editor": "Vis editor i popup",\r
+  "About this editor": "Om htmlarea",\r
+  "Help using editor": "Hjælp",\r
+  "Current style": "Anvendt stil"\r
+}\r
diff --git a/mailboxes/xinha/lang/de.js b/mailboxes/xinha/lang/de.js
new file mode 100644 (file)
index 0000000..b85f4a7
--- /dev/null
@@ -0,0 +1,163 @@
+// I18N constants\r
+// LANG: "de", ENCODING: UTF-8\r
+{\r
+  "Bold": "Fett",\r
+  "Italic": "Kursiv",\r
+  "Underline": "Unterstrichen",\r
+  "Strikethrough": "Durchgestrichen",\r
+  "Subscript": "Tiefgestellt",\r
+  "Superscript": "Hochgestellt",\r
+  "Justify Left": "Linksbündig",\r
+  "Justify Center": "Zentriert",\r
+  "Justify Right": "Rechtsbündig",\r
+  "Justify Full": "Blocksatz",\r
+  "Ordered List": "Nummerierte Liste",\r
+  "Bulleted List": "Aufzählungsliste",\r
+  "Decrease Indent": "Einzug verkleinern",\r
+  "Increase Indent": "Einzug vergrößern",\r
+  "Font Color": "Schriftfarbe",\r
+  "Background Color": "Hindergrundfarbe",\r
+  "Horizontal Rule": "Horizontale Linie",\r
+  "Insert Web Link": "Hyperlink einfügen",\r
+  "Insert/Modify Image": "Bild einfügen/verändern",\r
+  "Insert Table": "Tabelle einfügen",\r
+  "Toggle HTML Source": "HTML Quelltext ein/ausschalten",\r
+  "Enlarge Editor": "Editor vergrößern",\r
+  "About this editor": "Über diesen Editor",\r
+  "Help using editor": "Hilfe",\r
+  "Current style": "Derzeitiger Stil",\r
+  "Undoes your last action": "Rückgängig",\r
+  "Redoes your last action": "Wiederholen",\r
+  "Cut selection": "Ausschneiden",\r
+  "Copy selection": "Kopieren",\r
+  "Paste from clipboard": "Einfügen aus der Zwischenablage",\r
+  "Direction left to right": "Textrichtung von Links nach Rechts",\r
+  "Direction right to left": "Textrichtung von Rechts nach Links",\r
+  "Remove formatting": "Formatierung entfernen",\r
+  "Select all": "Alles markieren",\r
+  "Print document": "Dokument ausdrucken",\r
+  "Clear MSOffice tags": "MSOffice filter",\r
+  "Clear Inline Font Specifications": "Zeichensatz Formatierungen entfernen",\r
+  "Would you like to clear font typefaces?": "Wollen Sie Zeichensatztypen entfernen",\r
+  "Would you like to clear font sizes?": "Wollen Sie Zeichensatzgrößen entfernen",\r
+  "Would you like to clear font colours?": "Wollen sie Zeichensatzfarben entfernen",\r
+  "Split Block": "Block teilen",\r
+  "Toggle Borders": "Tabellenränder ein/ausblenden",\r
+  "Save as": "speichern unter",\r
+  "Insert/Overwrite": "Einfügen/Überschreiben",\r
+  "&mdash; format &mdash;": "&mdash; Format &mdash;",\r
+  "Heading 1": "Überschrift 1",\r
+  "Heading 2": "Überschrift 2",\r
+  "Heading 3": "Überschrift 3",\r
+  "Heading 4": "Überschrift 4",\r
+  "Heading 5": "Überschrift 5",\r
+  "Heading 6": "Überschrift 6",\r
+  "Normal": "Normal (Absatz)",\r
+  "Address": "Adresse",\r
+  "Formatted": "Formatiert",\r
+\r
+  //dialogs\r
+  "OK": "OK",\r
+  "Cancel": "Abbrechen",\r
+  "Path": "Pfad",\r
+  "You are in TEXT MODE.  Use the [<>] button to switch back to WYSIWYG.": "Sie sind im Text-Modus. Benutzen Sie den [<>] Button, um in den visuellen Modus (WYSIWIG) zu gelangen.",\r
+   "The Paste button does not work in Mozilla based web browsers (technical security reasons). Press CTRL-V on your keyboard to paste directly.": "Aus Sicherheitsgründen dürfen Skripte normalerweise nicht auf Ausschneiden/Kopieren/Einfügen zugreifen. Benutzen Sie bitte die entsprechenden Tastatur-Kommandos (Strg + x/c/v).",\r
+\r
+  "You need to select some text before create a link": "Sie müssen einen Text markieren, um einen Link zu erstellen",\r
+  "Your Document is not well formed. Check JavaScript console for details.": "Ihr Dokument ist in keinem sauberen Format. Benutzen Sie die Javascript Console für weitere Informationen.",\r
+\r
+  "Alignment:": "Ausrichtung:",\r
+  "Not set": "nicht eingestellt",\r
+  "Left": "links",\r
+  "Right": "rechts",\r
+  "Texttop": "oben bündig",\r
+  "Absmiddle": "mittig",\r
+  "Baseline": "Grundlinie",\r
+  "Absbottom": "unten bündig",\r
+  "Bottom": "unten",\r
+  "Middle": "zentriert",\r
+  "Top": "oben",\r
+\r
+  "Layout": "Layout",\r
+  "Spacing": "Abstand",\r
+  "Horizontal:": "horizontal:",\r
+  "Horizontal padding": "horizontaler Inhaltsabstand",\r
+  "Vertical:": "vertikal:",\r
+  "Vertical padding": "vertikaler Inhaltsabstand",\r
+  "Border thickness:": "Randstärke:",\r
+  "Leave empty for no border": "leer lassen für keinen Rand",\r
+\r
+  //Insert Link\r
+  "Insert/Modify Link": "Verknüpfung hinzufügen/ändern",\r
+  "None (use implicit)": "k.A. (implizit)",\r
+  "New window (_blank)": "Neues Fenster (_blank)",\r
+  "Same frame (_self)": "Selber Rahmen (_self)",\r
+  "Top frame (_top)": "Oberster Rahmen (_top)",\r
+  "Other": "Anderes",\r
+  "Target:": "Ziel:",\r
+  "Title (tooltip):": "Titel (Tooltip):",\r
+  "URL:": "URL:",\r
+  "You must enter the URL where this link points to": "Sie müssen eine Ziel-URL angeben für die Verknüpfung angeben",\r
+\r
+  // Insert Table\r
+  "Insert Table": "Table einfügen",\r
+  "Rows:": "Zeilen:",\r
+  "Number of rows": "Zeilenanzahl",\r
+  "Cols:": "Spalten:",\r
+  "Number of columns": "Spaltenanzahl",\r
+  "Width:": "Breite:",\r
+  "Width of the table": "Tabellenbreite",\r
+  "Percent": "Prozent",\r
+  "Pixels": "Pixel",\r
+  "Em": "Geviert",\r
+  "Width unit": "Größeneinheit",\r
+  "Fixed width columns": "Spalten mit fester Breite",\r
+  "Positioning of this table": "Positionierung der Tabelle",\r
+  "Cell spacing:": "Zellenabstand:",\r
+  "Space between adjacent cells": "Raum zwischen angrenzenden Zellen",\r
+  "Cell padding:": "Innenabstand:",\r
+  "Space between content and border in cell": "Raum zwischen Inhalt und Rand der Zelle",\r
+  "You must enter a number of rows": "Bitte geben Sie die Anzahl der Zeilen an",\r
+  "You must enter a number of columns": "Bitte geben Sie die Anzahl der Spalten an",\r
+\r
+  // Insert Image\r
+  "Insert Image": "Bild einfügen",\r
+  "Image URL:": "Bild URL:",\r
+  "Enter the image URL here": "Bitte geben sie hier die Bild URL ein",\r
+  "Preview": "Voransicht",\r
+  "Preview the image in a new window": "Voransicht des Bildes in einem neuen Fenster",\r
+  "Alternate text:": "Alternativer Text:",\r
+  "For browsers that don't support images": "für Browser, die keine Bilder unterstützen",\r
+  "Positioning of this image": "Positionierung dieses Bildes",\r
+  "Image Preview:": "Bild Voransicht:",\r
+  "You must enter the URL": "Bitte geben Sie die URL ein",\r
+\r
+  "button_bold": "de/bold.gif",\r
+  "button_italic": "de/italic.gif",\r
+  "button_underline": "de/underline.gif",\r
+\r
+  // Editor Help\r
+  "Keyboard shortcuts": "Tastaturkürzel",\r
+  "The editor provides the following key combinations:": "Der Editor unterstützt die folgenden kombinationen:",\r
+  "new paragraph": "Neuer Absatz(Paragraph)",\r
+  "insert linebreak": "Harter Umbruch einfügen",\r
+  "Set format to paragraph": "Setze Formatierung auf Absatz",\r
+  "Clean content pasted from Word": "Von Word eingefügter Text bereinigen",\r
+  "Headings": "Überschrift Typ 1 bis 6",\r
+  "Close": "Schließen",\r
+\r
+  // Loading messages\r
+  "Loading in progress. Please wait !": "Editor wird geladen. Bitte warten !",\r
+  "Constructing main object": "Hauptteil wird erzeugt",\r
+  "Create Toolbar": "Bearbeitungswerkzeuge werden angelegt",\r
+  "Register panel right": "Erzeugt rechte Leiste",\r
+  "Register panel left": "Erzeugt linke Leiste",\r
+  "Register panel top": "Erzeugt obere Leiste",\r
+  "Register panel bottom": "Erzeugt untere Leiste",\r
+  \r
+  // ColorPicker\r
+  "Click a color..." : "Farbe wählen",\r
+  "Sample" : "Beispiel",\r
+  "Web Safe: " : "Web Safe: ",\r
+  "Color: " : "Farbe: "\r
+};
\ No newline at end of file
diff --git a/mailboxes/xinha/lang/ee.js b/mailboxes/xinha/lang/ee.js
new file mode 100644 (file)
index 0000000..2c961e5
--- /dev/null
@@ -0,0 +1,50 @@
+// I18N constants\r
+\r
+// LANG: "ee", ENCODING: UTF-8\r
+// Author: Martin Raie, <albertvill@hot.ee>\r
+\r
+// FOR TRANSLATORS:\r
+//\r
+//   1. PLEASE PUT YOUR CONTACT INFO IN THE ABOVE LINE\r
+//      (at least a valid email address)\r
+//\r
+//   2. PLEASE TRY TO USE UTF-8 FOR ENCODING;\r
+//      (if this is not possible, please include a comment\r
+//       that states what encoding is necessary.)\r
+\r
+{\r
+  "Bold": "Paks",\r
+  "Italic": "Kursiiv",\r
+  "Underline": "Allakriipsutatud",\r
+  "Strikethrough": "Läbikriipsutatud",\r
+  "Subscript": "Allindeks",\r
+  "Superscript": "Ülaindeks",\r
+  "Justify Left": "Joonda vasakule",\r
+  "Justify Center": "Joonda keskele",\r
+  "Justify Right": "Joonda paremale",\r
+  "Justify Full": "Rööpjoonda",\r
+  "Ordered List": "Nummerdus",\r
+  "Bulleted List": "Täpploend",\r
+  "Decrease Indent": "Vähenda taanet",\r
+  "Increase Indent": "Suurenda taanet",\r
+  "Font Color": "Fondi värv",\r
+  "Background Color": "Tausta värv",\r
+  "Horizontal Rule": "Horisontaaljoon",\r
+  "Insert Web Link": "Lisa viit",\r
+  "Insert/Modify Image": "Lisa pilt",\r
+  "Insert Table": "Lisa tabel",\r
+  "Toggle HTML Source": "HTML/tavaline vaade",\r
+  "Enlarge Editor": "Suurenda toimeti aken",\r
+  "About this editor": "Teave toimeti kohta",\r
+  "Help using editor": "Spikker",\r
+  "Current style": "Kirjastiil",\r
+  "Undoes your last action": "Võta tagasi",\r
+  "Redoes your last action": "Tee uuesti",\r
+  "Cut selection": "Lõika",\r
+  "Copy selection": "Kopeeri",\r
+  "Paste from clipboard": "Kleebi",\r
+  "OK": "OK",\r
+  "Cancel": "Loobu",\r
+  "Path": "Path",\r
+  "You are in TEXT MODE.  Use the [<>] button to switch back to WYSIWYG.": "Sa oled tekstireziimis.  Kasuta nuppu [<>] lülitamaks tagasi WYSIWIG reziimi."\r
+}\r
diff --git a/mailboxes/xinha/lang/el.js b/mailboxes/xinha/lang/el.js
new file mode 100644 (file)
index 0000000..8476a4d
--- /dev/null
@@ -0,0 +1,55 @@
+// I18N constants\r
+\r
+// LANG: "el", ENCODING: UTF-8\r
+// Author: Dimitris Glezos, dimitris@glezos.com\r
+\r
+{\r
+  "Bold": "Ξ\88Ξ½Ο\84ΞΏΞ½Ξ±",\r
+  "Italic": "Πλάγια",\r
+  "Underline": "Ξ�Ο\80ΞΏΞ³Ο\81Ξ±ΞΌΞΌΞΉΟ\83ΞΌΞ­Ξ½Ξ±",\r
+  "Strikethrough": "Ξ\94ΞΉΞ±Ξ³Ο\81Ξ±ΞΌΞΌΞ­Ξ½Ξ±",\r
+  "Subscript": "Ξ\94ΡίκΟ\84Ξ·Ο\82",\r
+  "Superscript": "Ξ\94ΡίκΟ\84Ξ·Ο\82",\r
+  "Justify Left": "Ξ£Ο\84ΞΏΞ―Ο\87ΞΉΟ\83Ξ· Ξ\91Ο\81ΞΉΟ\83Ο\84Ξ΅Ο\81Ξ¬",\r
+  "Justify Center": "Ξ£Ο\84ΞΏΞ―Ο\87ΞΉΟ\83Ξ· Ξ\9aΞ­Ξ½Ο\84Ο\81ΞΏ",\r
+  "Justify Right": "Ξ£Ο\84ΞΏΞ―Ο\87ΞΉΟ\83Ξ· Ξ\94Ρξιά",\r
+  "Justify Full": "Ξ Ξ»Ξ�Ο\81Ξ·Ο\82 Ξ£Ο\84ΞΏΞ―Ο\87ΞΉΟ\83Ξ·",\r
+  "Ordered List": "Ξ\91Ο\81Ξ―ΞΈΞΌΞ·Ο\83Ξ·",\r
+  "Bulleted List": "Ξ\9aΞΏΟ\85κκίδΡΟ\82",\r
+  "Decrease Indent": "Ξ\9cΡίΟ\89Ο\83Ξ· Ξ\95Ο\83ΞΏΟ\87Ξ�Ο\82",\r
+  "Increase Indent": "Ξ\91Ο\8dΞΎΞ·Ο\83Ξ· Ξ\95Ο\83ΞΏΟ\87Ξ�Ο\82",\r
+  "Font Color": "Ξ§Ο\81Ο\8eΞΌΞ± Ξ\93Ο\81Ξ±ΞΌΞΌΞ±Ο\84ΞΏΟ\83ΡιΟ\81Ξ¬Ο\82",\r
+  "Background Color": "Ξ§Ο\81Ο\8eΞΌΞ± Ξ¦Ο\8cΞ½Ο\84ΞΏΟ\85",\r
+  "Horizontal Rule": "Ξ\9fΟ\81ΞΉΞΆΟ\8cΞ½Ο\84ΞΉΞ± Ξ\93Ο\81Ξ±ΞΌΞΌΞ�",\r
+  "Insert Web Link": "Ξ\95ΞΉΟ\83Ξ±Ξ³Ο\89Ξ³Ξ� Ξ£Ο\85νδέΟ\83ΞΌΞΏΟ\85",\r
+  "Insert/Modify Image": "Ξ\95ΞΉΟ\83Ξ±Ξ³Ο\89Ξ³Ξ�/Ξ�Ο\81ΞΏΟ\80ΞΏΟ\80ΞΏΞ―Ξ·Ο\83Ξ· Ξ\95ΞΉΞΊΟ\8cΞ½Ξ±Ο\82",\r
+  "Insert Table": "Ξ\95ΞΉΟ\83Ξ±Ξ³Ο\89Ξ³Ξ� Ξ Ξ―Ξ½Ξ±ΞΊΞ±",\r
+  "Toggle HTML Source": "Ξ\95ναλλαγΞ� Ο\83Ξ΅/Ξ±Ο\80Ο\8c HTML",\r
+  "Enlarge Editor": "Ξ\9cΡγένθΟ\85Ξ½Ο\83Ξ· Ξ΅Ο\80ΡξΡΟ\81Ξ³Ξ±Ο\83Ο\84Ξ�",\r
+  "About this editor": "ΠληΟ\81ΞΏΟ\86ΞΏΟ\81Ξ―Ξ΅Ο\82",\r
+  "Help using editor": "Ξ\92ΞΏΞ�θΡια",\r
+  "Current style": "Ξ Ξ±Ο\81Ο\8eΞ½ Ο\83Ο\84Ο\85Ξ»",\r
+  "Undoes your last action": "Ξ\91Ξ½Ξ±Ξ―Ο\81Ξ΅Ο\83Ξ· Ο\84ΡλΡΟ\85Ο\84Ξ±Ξ―Ξ±Ο\82 Ξ΅Ξ½Ξ­Ο\81γΡιαΟ\82",\r
+  "Redoes your last action": "Ξ\95Ο\80Ξ±Ξ½Ξ±Ο\86ΞΏΟ\81Ξ¬ Ξ±Ο\80Ο\8c Ξ±Ξ½Ξ±Ξ―Ο\81Ξ΅Ο\83Ξ·",\r
+  "Cut selection": "Ξ\91Ο\80ΞΏΞΊΞΏΟ\80Ξ�",\r
+  "Copy selection": "Ξ\91Ξ½Ο\84ΞΉΞ³Ο\81Ξ±Ο\86Ξ�",\r
+  "Paste from clipboard": "Ξ\95Ο\80ΞΉΞΊΟ\8cλληΟ\83Ξ·",\r
+  "Direction left to right": "Ξ\9aΞ±Ο\84Ξ΅Ο\8dΞΈΟ\85Ξ½Ο\83Ξ· Ξ±Ο\81ΞΉΟ\83Ο\84Ξ΅Ο\81Ξ¬ Ο\80Ο\81ΞΏΟ\82 Ξ΄Ξ΅ΞΎΞΉΞ¬",\r
+  "Direction right to left": "Ξ\9aΞ±Ο\84Ξ΅Ο\8dΞΈΟ\85Ξ½Ο\83Ξ· Ξ±Ο\80Ο\8c Ξ΄Ξ΅ΞΎΞΉΞ¬ Ο\80Ο\81ΞΏΟ\82 Ο\84Ξ± Ξ±Ο\81ΞΉΟ\83Ο\84Ξ΅Ο\81Ξ¬",\r
+  "OK": "OK",\r
+  "Cancel": "Ξ\91ΞΊΟ\8dΟ\81Ο\89Ο\83Ξ·",\r
+  "Path": "Ξ\94ΞΉΞ±Ξ΄Ο\81ΞΏΞΌΞ�",\r
+  "You are in TEXT MODE.  Use the [<>] button to switch back to WYSIWYG.": "Ξ\95Ξ―Ο\83Ο\84Ξ΅ Ο\83Ξ΅ TEXT MODE.  Ξ§Ο\81Ξ·Ο\83ΞΉΞΌΞΏΟ\80ΞΏΞΉΞ�Ο\83Ο\84Ξ΅ Ο\84ΞΏ ΞΊΞΏΟ\85ΞΌΟ\80Ξ― [<>] Ξ³ΞΉΞ± Ξ½Ξ± Ξ΅Ο\80Ξ±Ξ½Ξ­Ο\81ΞΈΞ΅Ο\84Ξ΅ Ο\83Ο\84ΞΏ WYSIWIG.",\r
+  "The full screen mode is known to cause problems with Internet Explorer, due to browser bugs that we weren": "Ξ\97 ΞΊΞ±Ο\84Ξ¬Ο\83Ο\84Ξ±Ο\83Ξ· Ο\80Ξ»Ξ�Ο\81Ξ·Ο\82 ΞΏΞΈΟ\8cΞ½Ξ·Ο\82 Ξ­Ο\87Ρι Ο\80Ο\81ΞΏΞ²Ξ»Ξ�ΞΌΞ±Ο\84Ξ± ΞΌΞ΅ Ο\84ΞΏΞ½ Internet Explorer, Ξ»Ο\8cΞ³Ο\89 Ο\83Ο\86αλμάΟ\84Ο\89Ξ½ Ο\83Ο\84ΞΏΞ½ Ξ―διο Ο\84ΞΏΞ½ browser.  Ξ\91Ξ½ Ο\84ΞΏ Ο\83Ο\8dΟ\83Ο\84Ξ·ΞΌΞ± Ο\83Ξ±Ο\82 Ξ΅Ξ―Ξ½Ξ±ΞΉ Windows 9x ΞΌΟ\80ΞΏΟ\81Ρί ΞΊΞ±ΞΉ Ξ½Ξ± Ο\87Ο\81ΡιαΟ\83Ο\84ΡίΟ\84Ξ΅ reboot. Ξ\91Ξ½ Ξ΅Ξ―Ο\83Ο\84Ξ΅ Ο\83Ξ―Ξ³ΞΏΟ\85Ο\81ΞΏΞΉ, Ο\80Ξ±Ο\84Ξ�Ο\83Ο\84Ξ΅ Ξ\9fΞ\9a.",\r
+  "Cancel": "Ξ\91ΞΊΟ\8dΟ\81Ο\89Ο\83Ξ·",\r
+  "Insert/Modify Link": "Ξ\95ΞΉΟ\83Ξ±Ξ³Ο\89Ξ³Ξ�/Ξ�Ο\81ΞΏΟ\80ΞΏΟ\80ΞΏΞ―Ξ·Ο\83Ξ· Ο\83Ο\8dνδΡΟ\83ΞΌΞΏΟ\85",\r
+  "New window (_blank)": "Ξ\9dΞ­ΞΏ Ο\80Ξ±Ο\81άθΟ\85Ο\81ΞΏ (_blank)",\r
+  "None (use implicit)": "Ξ\9aΞ±Ξ½Ξ­Ξ½Ξ± (Ο\87Ο\81Ξ�Ο\83Ξ· Ξ±Ο\80Ο\8cΞ»Ο\85Ο\84ΞΏΟ\85)",\r
+  "Other": "Ξ\91λλο",\r
+  "Same frame (_self)": "Ξ\8aδιο frame (_self)",\r
+  "Target:": "Target:",\r
+  "Title (tooltip):": "Ξ�Ξ―Ο\84λοΟ\82 (tooltip):",\r
+  "Top frame (_top)": "ΠάνΟ\89 frame (_top)",\r
+  "URL:": "URL:",\r
+  "You must enter the URL where this link points to": "Ξ Ο\81Ξ­Ο\80Ρι Ξ½Ξ± Ξ΅ΞΉΟ\83άγΡΟ\84Ξ΅ Ο\84ΞΏ URL Ο\80ΞΏΟ\85 ΞΏΞ΄Ξ·Ξ³Ξ΅Ξ― Ξ±Ο\85Ο\84Ο\8cΟ\82 ΞΏ Ο\83Ο\8dνδΡΟ\83ΞΌΞΏΟ\82"\r
+}\r
diff --git a/mailboxes/xinha/lang/es.js b/mailboxes/xinha/lang/es.js
new file mode 100644 (file)
index 0000000..e1ea5c1
--- /dev/null
@@ -0,0 +1,40 @@
+// I18N constants\r
+\r
+// LANG: "es", ENCODING: UTF-8\r
+\r
+{\r
+  "Bold": "Negrita",\r
+  "Italic": "Cursiva",\r
+  "Underline": "Subrayado",\r
+  "Strikethrough": "Tachado",\r
+  "Subscript": "Sub?ndice",\r
+  "Superscript": "Super?ndice",\r
+  "Justify Left": "Alinear a la Izquierda",\r
+  "Justify Center": "Centrar",\r
+  "Justify Right": "Alinear a la Derecha",\r
+  "Justify Full": "Justificar",\r
+  "Ordered List": "Lista Ordenada",\r
+  "Bulleted List": "Lista No Ordenada",\r
+  "Decrease Indent": "Aumentar Sangr?a",\r
+  "Increase Indent": "Disminuir Sangr?a",\r
+  "Font Color": "Color del Texto",\r
+  "Background Color": "Color del Fondo",\r
+  "Horizontal Rule": "L?nea Horizontal",\r
+  "Insert Web Link": "Insertar Enlace",\r
+  "Insert/Modify Image": "Insertar Imagen",\r
+  "Insert Table": "Insertar Tabla",\r
+  "Toggle HTML Source": "Ver Documento en HTML",\r
+  "Enlarge Editor": "Ampliar Editor",\r
+  "About this editor": "Acerca del Editor",\r
+  "Help using editor": "Ayuda",\r
+  "Current style": "Estilo Actual",\r
+  "Undoes your last action": "Deshacer",\r
+  "Redoes your last action": "Rehacer",\r
+  "Cut selection": "Cortar selecci?n",\r
+  "Copy selection": "Copiar selecci?n",\r
+  "Paste from clipboard": "Pegar desde el portapapeles",\r
+  "OK": "Aceptar",\r
+  "Cancel": "Cancelar",\r
+  "Path": "Ruta",\r
+  "You are in TEXT MODE.  Use the [<>] button to switch back to WYSIWYG.": "Esta en modo TEXTO. Use el boton [<>] para cambiar a WYSIWIG"\r
+}\r
diff --git a/mailboxes/xinha/lang/fa.js b/mailboxes/xinha/lang/fa.js
new file mode 100644 (file)
index 0000000..106e831
--- /dev/null
@@ -0,0 +1,167 @@
+// I18N constants\r
+// LANG: "fa", ENCODING: UTF-8\r
+{\r
+  "Bold": "ضخیم",\r
+  "Italic": "مورب",\r
+  "Underline": "زیر خط",\r
+  "Strikethrough": "رو خط",\r
+  "Subscript": "زیروند",\r
+  "Superscript": "بالاوند",\r
+  "Justify Left": "تراز از چپ",\r
+  "Justify Center": "تراز در وسط",\r
+  "Justify Right": "تراز در راست",\r
+  "Justify Full": "تراز از چپ و راست",\r
+  "Ordered List": "فهرست مرتب",\r
+  "Bulleted List": "فهرست گلوله ای",\r
+  "Decrease Indent": "کاهش سر خط",\r
+  "Increase Indent": "افزایش سر خط",\r
+  "Font Color": "رنگ فلم",\r
+  "Background Color": "رنگ پس زمینه",\r
+  "Horizontal Rule": "خط افقی",\r
+  "Insert Web Link": "افزودن لینک وب",\r
+  "Insert/Modify Image": "افزودن یا ویرایش تصویر",\r
+  "Insert Table": "افزودن جدول",\r
+  "Toggle HTML Source": "مشاهده یا عدم مشاهده متن در قالب HTML",\r
+  "Enlarge Editor": "بزرگ کردن ویرایش گر",\r
+  "About this editor": "درباره این ویرایش گر",\r
+  "Help using editor": "راهنمای استفاده ویرایش گر",\r
+  "Current style": "شیوه کنونی",\r
+  "Undoes your last action": "برگرداندن آخرین عمل",\r
+  "Redoes your last action": "انجام مجدد آخرین عمل",\r
+  "Cut selection": "بریدن انتخاب شده",\r
+  "Copy selection": "کپی انتخاب شده",\r
+  "Paste from clipboard": "چسباندن از تخته کار",\r
+  "Direction left to right": "جهت از چپ به راست",\r
+  "Direction right to left": "جهت از راست به چپ",\r
+  "Remove formatting": "حذف فرمت بندی",\r
+  "Select all": "انتخاب همه",\r
+  "Print document": "چاپ سند",\r
+  "Clear MSOffice tags": "پاک کردن متن از برچسب های MSOffice",\r
+  "Clear Inline Font Specifications": "پاک کردن متن از مشخصات فونت",\r
+  "Would you like to clear font typefaces?": "آیا تمایل دارید ظاهر فلم را پاک کنید؟",\r
+  "Would you like to clear font sizes?": "آیا تمایل دارید اندازه قلم را پاک کنید",\r
+  "Would you like to clear font colours?": "آیا تمایل دارید رنگ قلم را پاک کنید؟",\r
+  "Split Block": "بلاک جداسازی",\r
+  "Toggle Borders": "فعال/غیر فعال کردن لبه ها",\r
+  "Save as": "ذخیره مانند...",\r
+  "Insert/Overwrite": "افزودن/جانویسی",\r
+  "&mdash; format &mdash;": "&mdash; قالب &mdash;",\r
+  "Heading 1": "تیتر 1",\r
+  "Heading 2": "تیتر 2",\r
+  "Heading 3": "تیتر 3",\r
+  "Heading 4": "تیتر 4",\r
+  "Heading 5": "تیتر 5",\r
+  "Heading 6": "تیتر 6",\r
+  "Normal": "معمولی",\r
+  "Address": "آدرس",\r
+  "Formatted": "قالب بندی شده",\r
+\r
+  //dialogs\r
+  "OK": "بله",\r
+  "Cancel": "انصراف",\r
+  "Path": "مسیر",\r
+  "You are in TEXT MODE.  Use the [<>] button to switch back to WYSIWYG.": "در مد متنی هستید.  از دکمه [<>] استفاده نمایید تا به مد WYSIWYG برگردید.",\r
+  "The Paste button does not work in Mozilla based web browsers (technical security reasons). Press CTRL-V on your keyboard to paste directly.": "دکمه چسباندن در مرورگرهای سری Mozilla کار نمی کند (به دلایل فنی امنیتی).برای چسباندن مستقیم ، دکمه CTRL-V را در صفحه کلید بزنید.",\r
+  "Your Document is not well formed. Check JavaScript console for details.": "سند شما بدرستی قالب بندی نشده است. برای اطلاعات بیشتر پایانه نمایش جاوااسکریپت را بررسی کنید.",\r
+\r
+  "Alignment:": "تراز بندی",\r
+  "Not set": "تنظیم نشده",\r
+  "Left": "چپ",\r
+  "Right": "راست",\r
+  "Texttop": "بالای متن",\r
+  "Absmiddle": "دقیقا وسط",\r
+  "Baseline": "ابتدای خط",\r
+  "Absbottom": "دقیقا پایین",\r
+  "Bottom": "پایین",\r
+  "Middle": "وسط",\r
+  "Top": "بالا",\r
+\r
+  "Layout": "لایه",\r
+  "Spacing": "فاصله گذاری",\r
+  "Horizontal:": "افقی",\r
+  "Horizontal padding": "پرکننده افقی",\r
+  "Vertical:": "عمودی",\r
+  "Vertical padding": "پرکننده عمودی",\r
+  "Border thickness:": "ضخامت لبه",\r
+  "Leave empty for no border": "برای بدون لبه خالی رها کن",\r
+\r
+  //Insert Link\r
+  "Insert/Modify Link": "افزودن / ویرایش لینک",\r
+  "None (use implicit)": "هیچکدام (استفاده از بدون شرط)",\r
+  "New window (_blank)": "پنجره جدید (_blank)",\r
+  "Same frame (_self)": "فریم یکسان (_self)",\r
+  "Top frame (_top)": "فریم بالایی (_top)",\r
+  "Other": "سایر",\r
+  "Target:": "هدف",\r
+  "Title (tooltip):": "عنوان (راهنمای یک خطی)",\r
+  "URL:": "URL:",\r
+  "You must enter the URL where this link points to": "باید URLی که این لینک به آن اشاره دارد را وارد کنید",\r
+  "You need to select some text before creating a link": "باید قبل از ساخت لینک ، متنی را انتخاب نمایید",\r
+\r
+  // Insert Table\r
+  "Insert Table": "افزودن جدول",\r
+  "Rows:": "ردیف ها",\r
+  "Number of rows": "تعداد ردیف ها",\r
+  "Cols:": "ستون ها",\r
+  "Number of columns": "تعداد ستون ها",\r
+  "Width:": "طول",\r
+  "Width of the table": "طول جدول",\r
+  "Percent": "درصد",\r
+  "Pixels": "پیکسل ها",\r
+  "Em": "Em",\r
+  "Width unit": "واحد طول",\r
+  "Fixed width columns": "ستون های طول ثابت",\r
+  "Positioning of this table": "موقعیت یابی این جدول",\r
+  "Cell spacing:": "فاصله سلول ها",\r
+  "Space between adjacent cells": "فاصله بین سلول های همجوار",\r
+  "Cell padding:": "پر کننده سلول",\r
+  "Space between content and border in cell": "فاصله بین محتوا و لبه در سلول",\r
+  "You must enter a number of rows": "باید تعداد ردیف ها را وارد کنید",\r
+  "You must enter a number of columns": "باید تعداد ستون ها را وارد کنید",\r
+\r
+  // Insert Image\r
+  "Insert Image": "افزودن تصویر",\r
+  "Image URL:": "URL تصویر",\r
+  "Enter the image URL here": "URL تصویر را اینجا وارد کنید",\r
+  "Preview": "پیش نمایش",\r
+  "Preview the image in a new window": "پیش نمایش تصویر در پنجره ای جدید",\r
+  "Alternate text:": "متن جایگزین",\r
+  "For browsers that don't support images": "برای مرورگرهایی که از تصاویر پشتیبانی نمی کنند",\r
+  "Positioning of this image": "موقعیت یابی تصویر",\r
+  "Image Preview:": "پیش نمایش تصویر",\r
+  "You must enter the URL": "شما باید URL را وارد کنید",\r
+\r
+  // toolbar\r
+  "button_bold": "fr/bold.gif",\r
+  "button_underline": "fr/underline.gif",\r
+  "button_strikethrough": "fr/strikethrough.gif",\r
+\r
+  // Editor Help\r
+  "Xinha Help": "راهنمای Xinha",\r
+  "Editor Help": "راهنمای ویرایشگر",\r
+  "Keyboard shortcuts": "میانبرهای صفحه کلید",\r
+  "The editor provides the following key combinations:": "ویرایشگر استفاده از کلید های گروهی زیر را مسیر می سازد :",\r
+  "ENTER": "ENTREE",\r
+  "new paragraph": "پاراگراف جدید",\r
+  "SHIFT-ENTER": "SHIFT+ENTREE",\r
+  "insert linebreak": "افزودن جدا کننده خط",\r
+  "Set format to paragraph": "تغییر قالب به پاراگراف",\r
+  "Clean content pasted from Word": "تمیز کردن محتوای چسبانده شده از Word",\r
+  "Headings": "عنوان گذاری",\r
+  "Close": "بستن",\r
+\r
+  // Loading messages\r
+  "Loading in progress. Please wait !": "بارگذاری در حال انجام است. لطفا صبر کنید !",\r
+  "Constructing main object": "ساختن شیء اصلی",\r
+  "Constructing object": "ساختن شیء",\r
+  "Register panel right": "ثبت قاب راست",\r
+  "Register panel left": "ثبت قاب چپ",\r
+  "Register panel top": "ثبت قاب بالا",\r
+  "Register panel bottom": "ثبت قاب پایین",\r
+  "Create Toolbar": "ساخت نوار ابزار",\r
+  "Create StatusBar": "ساخت نوار وضعیت",\r
+  "Generate Xinha object": "تولید شیء Xinha",\r
+  "Init editor size": "مقدار دهی اندازه ویرایشگر",\r
+  "Init IFrame": "مقدار دهی IFrame",\r
+  "Register plugin $plugin": "ثبت پلاگین $plugin"\r
+};
\ No newline at end of file
diff --git a/mailboxes/xinha/lang/fi.js b/mailboxes/xinha/lang/fi.js
new file mode 100644 (file)
index 0000000..0297c78
--- /dev/null
@@ -0,0 +1,38 @@
+// I18N constants\r
+\r
+// LANG: "en", ENCODING: UTF-8\r
+\r
+{\r
+  "Bold": "Lihavoitu",\r
+  "Italic": "Kursivoitu",\r
+  "Underline": "Alleviivattu",\r
+  "Strikethrough": "Yliviivattu",\r
+  "Subscript": "Alaindeksi",\r
+  "Superscript": "Yläindeksi",\r
+  "Justify Left": "Tasaa vasemmat reunat",\r
+  "Justify Center": "Keskitä",\r
+  "Justify Right": "Tasaa oikeat reunat",\r
+  "Justify Full": "Tasaa molemmat reunat",\r
+  "Ordered List": "Numerointi",\r
+  "Bulleted List": "Luettelomerkit",\r
+  "Decrease Indent": "Pienennä sisennystä",\r
+  "Increase Indent": "Lisää sisennystä",\r
+  "Font Color": "Fontin väri",\r
+  "Background Color": "Taustaväri",\r
+  "Horizontal Rule": "Vaakaviiva",\r
+  "Insert Web Link": "Lisää linkki",\r
+  "Insert/Modify Image": "Lisää kuva",\r
+  "Insert Table": "Lisää taulukko",\r
+  "Toggle HTML Source": "HTML-lähdekoodi vs WYSIWYG",\r
+  "Enlarge Editor": "Suurenna editori",\r
+  "About this editor": "Tietoja editorista",\r
+  "Help using editor": "Näytä ohje",\r
+  "Current style": "Nykyinen tyyli",\r
+  "Undoes your last action": "Peruuta viimeinen toiminto",\r
+  "Redoes your last action": "Palauta viimeinen toiminto",\r
+  "Cut selection": "Leikkaa maalattu",\r
+  "Copy selection": "Kopioi maalattu",\r
+  "Paste from clipboard": "Liitä leikepyödältä",\r
+  "OK": "Hyväksy",\r
+  "Cancel": "Peruuta"\r
+}\r
diff --git a/mailboxes/xinha/lang/fr.js b/mailboxes/xinha/lang/fr.js
new file mode 100644 (file)
index 0000000..c338846
--- /dev/null
@@ -0,0 +1,167 @@
+// I18N constants\r
+// LANG: "fr", ENCODING: UTF-8\r
+{\r
+  "Bold": "Gras",\r
+  "Italic": "Italique",\r
+  "Underline": "Souligné",\r
+  "Strikethrough": "Barré",\r
+  "Subscript": "Indice",\r
+  "Superscript": "Exposant",\r
+  "Justify Left": "Aligner à gauche",\r
+  "Justify Center": "Centrer",\r
+  "Justify Right": "Aligner à droite",\r
+  "Justify Full": "Justifier",\r
+  "Ordered List": "Liste numérotée",\r
+  "Bulleted List": "Liste à puces",\r
+  "Decrease Indent": "Diminuer le retrait",\r
+  "Increase Indent": "Augmenter le retrait",\r
+  "Font Color": "Couleur de police",\r
+  "Background Color": "Surlignage",\r
+  "Horizontal Rule": "Ligne horizontale",\r
+  "Insert Web Link": "Insérer un lien",\r
+  "Insert/Modify Image": "Insérer / Modifier une image",\r
+  "Insert Table": "Insérer un tableau",\r
+  "Toggle HTML Source": "Afficher / Masquer code source",\r
+  "Enlarge Editor": "Agrandir l'éditeur",\r
+  "About this editor": "A propos",\r
+  "Help using editor": "Aide",\r
+  "Current style": "Style courant",\r
+  "Undoes your last action": "Annuler la dernière action",\r
+  "Redoes your last action": "Répéter la dernière action",\r
+  "Cut selection": "Couper la sélection",\r
+  "Copy selection": "Copier la sélection",\r
+  "Paste from clipboard": "Coller depuis le presse-papier",\r
+  "Direction left to right": "Direction de gauche à droite",\r
+  "Direction right to left": "Direction de droite à gauche",\r
+  "Remove formatting": "Supprimer mise en forme",\r
+  "Select all": "Tout sélectionner",\r
+  "Print document": "Imprimer document",\r
+  "Clear MSOffice tags": "Supprimer tags MSOffice",\r
+  "Clear Inline Font Specifications": "Supprimer paramètres inline de la police",\r
+  "Would you like to clear font typefaces?": "Voulez-vous supprimer les types ?",\r
+  "Would you like to clear font sizes?": "Voulez-vous supprimer les tailles ?",\r
+  "Would you like to clear font colours?": "Voulez-vous supprimer les couleurs ?",\r
+  "Split Block": "Séparer les blocs",\r
+  "Toggle Borders": "Afficher / Masquer les bordures",\r
+  "Save as": "Enregistrer sous",\r
+  "Insert/Overwrite": "Insertion / Remplacement",\r
+  "&mdash; format &mdash;": "&mdash; Format &mdash;",\r
+  "Heading 1": "Titre 1",\r
+  "Heading 2": "Titre 2",\r
+  "Heading 3": "Titre 3",\r
+  "Heading 4": "Titre 4",\r
+  "Heading 5": "Titre 5",\r
+  "Heading 6": "Titre 6",\r
+  "Normal": "Normal",\r
+  "Address": "Adresse",\r
+  "Formatted": "Formaté",\r
+\r
+  //dialogs\r
+  "OK": "OK",\r
+  "Cancel": "Annuler",\r
+  "Path": "Chemin",\r
+  "You are in TEXT MODE.  Use the [<>] button to switch back to WYSIWYG.": "Vous êtes en MODE TEXTE.  Appuyez sur le bouton [<>] pour retourner au mode WYSIWYG.",\r
+  "The Paste button does not work in Mozilla based web browsers (technical security reasons). Press CTRL-V on your keyboard to paste directly.": "Le bouton Coller ne fonctionne pas sur les navigateurs basés sur Mozilla (pour des raisons de sécurité). Pressez CTRL-V au clavier pour coller directement.",\r
+  "Your Document is not well formed. Check JavaScript console for details.": "Le document est mal formé. Vérifiez la console JavaScript pour plus de détails.",\r
+\r
+  "Alignment:": "Alignement",\r
+  "Not set": "Indéfini",\r
+  "Left": "Gauche",\r
+  "Right": "Droite",\r
+  "Texttop": "Texttop",\r
+  "Absmiddle": "Absmiddle",\r
+  "Baseline": "Baseline",\r
+  "Absbottom": "Absbottom",\r
+  "Bottom": "Bas",\r
+  "Middle": "Milieu",\r
+  "Top": "Haut",\r
+\r
+  "Layout": "Mise en page",\r
+  "Spacing": "Espacement",\r
+  "Horizontal:": "Horizontal",\r
+  "Horizontal padding": "Marge horizontale interne",\r
+  "Vertical:": "Vertical",\r
+  "Vertical padding": "Marge verticale interne",\r
+  "Border thickness:": "Epaisseur de bordure",\r
+  "Leave empty for no border": "Laisser vide pour pas de bordure",\r
+\r
+  //Insert Link\r
+  "Insert/Modify Link": "Insérer / Modifier un lien",\r
+  "None (use implicit)": "Aucune (implicite)",\r
+  "New window (_blank)": "Nouvelle fenêtre (_blank)",\r
+  "Same frame (_self)": "Même frame (_self)",\r
+  "Top frame (_top)": "Frame principale (_top)",\r
+  "Other": "Autre",\r
+  "Target:": "Cible",\r
+  "Title (tooltip):": "Texte alternatif",\r
+  "URL:": "URL:",\r
+  "You must enter the URL where this link points to": "Vous devez entrer l'URL de ce lien",\r
+  "You need to select some text before creating a link": "Vous devez sélectionner du texte avant de créer un lien",\r
+\r
+  // Insert Table\r
+  "Insert Table": "Insérer un tableau",\r
+  "Rows:": "Lignes",\r
+  "Number of rows": "Nombre de lignes",\r
+  "Cols:": "Colonnes",\r
+  "Number of columns": "Nombre de colonnes",\r
+  "Width:": "Largeur",\r
+  "Width of the table": "Largeur du tableau",\r
+  "Percent": "Pourcent",\r
+  "Pixels": "Pixels",\r
+  "Em": "Em",\r
+  "Width unit": "Unités de largeur",\r
+  "Fixed width columns": "Colonnes à taille fixe",\r
+  "Positioning of this table": "Position du tableau",\r
+  "Cell spacing:": "Espacement",\r
+  "Space between adjacent cells": "Espace entre les cellules adjacentes",\r
+  "Cell padding:": "Marge interne",\r
+  "Space between content and border in cell": "Espace entre le contenu et la bordure d'une cellule",\r
+  "You must enter a number of rows": "Vous devez entrer le nombre de lignes",\r
+  "You must enter a number of columns": "Vous devez entrer le nombre de colonnes",\r
+\r
+  // Insert Image\r
+  "Insert Image": "Insérer une image",\r
+  "Image URL:": "URL image",\r
+  "Enter the image URL here": "Entrer l'URL de l'image ici",\r
+  "Preview": "Prévisualiser",\r
+  "Preview the image in a new window": "Prévisualiser l'image dans une nouvelle fenêtre",\r
+  "Alternate text:": "Texte alternatif",\r
+  "For browsers that don't support images": "Pour les navigateurs qui ne supportent pas les images",\r
+  "Positioning of this image": "Position de l'image",\r
+  "Image Preview:": "Prévisualisation",\r
+  "You must enter the URL": "Vous devez entrer l'URL",\r
+\r
+  // toolbar\r
+  "button_bold": "fr/bold.gif",\r
+  "button_underline": "fr/underline.gif",\r
+  "button_strikethrough": "fr/strikethrough.gif",\r
+\r
+  // Editor Help\r
+  "Xinha Help": "Aide Xinha",\r
+  "Editor Help": "Aide de l'éditeur",\r
+  "Keyboard shortcuts": "Raccourcis clavier",\r
+  "The editor provides the following key combinations:": "L'éditeur fournit les combinaisons de touches suivantes :",\r
+  "ENTER": "ENTREE",\r
+  "new paragraph": "Nouveau paragraphe",\r
+  "SHIFT-ENTER": "SHIFT+ENTREE",\r
+  "insert linebreak": "Insère un saut de ligne",\r
+  "Set format to paragraph": "Applique le format paragraphe",\r
+  "Clean content pasted from Word": "Nettoyage du contenu copié depuis Word",\r
+  "Headings": "Titres",\r
+  "Close": "Fermer",\r
+\r
+  // Loading messages\r
+  "Loading in progress. Please wait !": "Chargement en cours. Veuillez patienter !",\r
+  "Constructing main object": "Construction de l'objet principal",\r
+  "Constructing object": "Construction de l'objet",\r
+  "Register panel right": "Enregistrement du panneau droit",\r
+  "Register panel left": "Enregistrement du panneau gauche",\r
+  "Register panel top": "Enregistrement du panneau supérieur",\r
+  "Register panel bottom": "Enregistrement du panneau inférieur",\r
+  "Create Toolbar": "Construction de la barre d'icones",\r
+  "Create StatusBar": "Construction de la barre de status",\r
+  "Generate Xinha object": "Génération de l'objet Xinha",\r
+  "Init editor size": "Initialisation de la taille d'édition",\r
+  "Init IFrame": "Initialisation de l'iframe",\r
+  "Register plugin $plugin": "Enregistrement du plugin $plugin"\r
+};
\ No newline at end of file
diff --git a/mailboxes/xinha/lang/gb.js b/mailboxes/xinha/lang/gb.js
new file mode 100644 (file)
index 0000000..d3c8df8
--- /dev/null
@@ -0,0 +1,29 @@
+// I18N constants -- Chinese GB\r
+// by Dave Lo -- dlo@interactivetools.com\r
+{\r
+  "Bold": "粗体",\r
+  "Italic": "斜体",\r
+  "Underline": "底线",\r
+  "Strikethrough": "删除线",\r
+  "Subscript": "下标",\r
+  "Superscript": "上标",\r
+  "Justify Left": "位置靠左",\r
+  "Justify Center": "位置居中",\r
+  "Justify Right": "位置靠右",\r
+  "Justify Full": "位置左右平等",\r
+  "Ordered List": "顺序清单",\r
+  "Bulleted List": "无序清单",\r
+  "Decrease Indent": "减小行前空白",\r
+  "Increase Indent": "加宽行前空白",\r
+  "Font Color": "文字颜色",\r
+  "Background Color": "背景颜色",\r
+  "Horizontal Rule": "水平线",\r
+  "Insert Web Link": "插入连结",\r
+  "Insert/Modify Image": "插入图形",\r
+  "Insert Table": "插入表格",\r
+  "Toggle HTML Source": "切换HTML原始码",\r
+  "Enlarge Editor": "放大",\r
+  "About this editor": "关於 HTMLArea",\r
+  "Help using editor": "说明",\r
+  "Current style": "字体例子"\r
+}\r
diff --git a/mailboxes/xinha/lang/he.js b/mailboxes/xinha/lang/he.js
new file mode 100644 (file)
index 0000000..60dbd76
--- /dev/null
@@ -0,0 +1,64 @@
+// I18N constants\r
+\r
+// LANG: "he", ENCODING: UTF-8\r
+// Author: Liron Newman, http://www.eesh.net, <plastish at ultinet dot org>\r
+\r
+// FOR TRANSLATORS:\r
+//\r
+//   1. PLEASE PUT YOUR CONTACT INFO IN THE ABOVE LINE\r
+//      (at least a valid email address)\r
+//\r
+//   2. PLEASE TRY TO USE UTF-8 FOR ENCODING;\r
+//      (if this is not possible, please include a comment\r
+//       that states what encoding is necessary.)\r
+\r
+{\r
+  "Bold": "מודגש",\r
+  "Italic": "נטוי",\r
+  "Underline": "קו תחתי",\r
+  "Strikethrough": "קו אמצע",\r
+  "Subscript": "כתב עילי",\r
+  "Superscript": "כתב תחתי",\r
+  "Justify Left": " ישור לשמאל",\r
+  "Justify Center": "ישור למרכז",\r
+  "Justify Right": "ישור לימין",\r
+  "Justify Full": "ישור לשורה מלאה",\r
+  "Ordered List": "רשימה ממוספרת",\r
+  "Bulleted List": "רשימה לא ממוספרת",\r
+  "Decrease Indent": "הקטן כניסה",\r
+  "Increase Indent": "הגדל כניסה",\r
+  "Font Color": "צבע גופן",\r
+  "Background Color": "צבע רקע",\r
+  "Horizontal Rule": "קו אנכי",\r
+  "Insert Web Link": "הכנס היפר-קישור",\r
+  "Insert/Modify Image": "הכנס/שנה תמונה",\r
+  "Insert Table": "הכנס טבלה",\r
+  "Toggle HTML Source": "שנה מצב קוד HTML",\r
+  "Enlarge Editor": "הגדל את העורך",\r
+  "About this editor": "אודות עורך זה",\r
+  "Help using editor": "עזרה לשימוש בעורך",\r
+  "Current style": "סגנון נוכחי",\r
+  "Undoes your last action": "מבטל את פעולתך האחרונה",\r
+  "Redoes your last action": "מבצע מחדש את הפעולה האחרונה שביטלת",\r
+  "Cut selection": "גזור בחירה",\r
+  "Copy selection": "העתק בחירה",\r
+  "Paste from clipboard": "הדבק מהלוח",\r
+  "Direction left to right": "כיוון משמאל לימין",\r
+  "Direction right to left": "כיוון מימין לשמאל",\r
+  "OK": "אישור",\r
+  "Cancel": "ביטול",\r
+  "Path": "נתיב עיצוב",\r
+  "You are in TEXT MODE.  Use the [<>] button to switch back to WYSIWYG.": "אתה במצב טקסט נקי (קוד). השתמש בכפתור [<>] כדי לחזור למצב WYSIWYG (תצוגת עיצוב).",\r
+  "The full screen mode is known to cause problems with Internet Explorer, due to browser bugs that we weren": "מצב מסך מלא יוצר בעיות בדפדפן Internet Explorer, עקב באגים בדפדפן לא יכולנו לפתור את זה.  את/ה עלול/ה לחוות תצוגת זבל,  בעיות בתפקוד העורך ו/או קריסה של הדפדפן.  אם המערכת שלך היא Windows 9x סביר להניח שתקבל/י ",\r
+  "Cancel": "ביטול",\r
+  "Insert/Modify Link": "הוסף/שנה קישור",\r
+  "New window (_blank)": "חלון חדש (_blank)",\r
+  "None (use implicit)": "ללא (השתמש ב-frame הקיים)",\r
+  "Other": "אחר",\r
+  "Same frame (_self)": "אותו frame (_self)",\r
+  "Target:": "יעד:",\r
+  "Title (tooltip):": "כותרת (tooltip):",\r
+  "Top frame (_top)": "Frame עליון (_top)",\r
+  "URL:": "URL:",\r
+  "You must enter the URL where this link points to": "חובה לכתוב URL שאליו קישור זה מצביע"\r
+}\r
diff --git a/mailboxes/xinha/lang/hu.js b/mailboxes/xinha/lang/hu.js
new file mode 100644 (file)
index 0000000..75c541c
--- /dev/null
@@ -0,0 +1,64 @@
+// I18N constants\r
+\r
+// LANG: "hu", ENCODING: UTF-8\r
+// Author: Miklós Somogyi, <somogyine@vnet.hu>\r
+\r
+// FOR TRANSLATORS:\r
+//\r
+//   1. PLEASE PUT YOUR CONTACT INFO IN THE ABOVE LINE\r
+//      (at least a valid email address)\r
+//\r
+//   2. PLEASE TRY TO USE UTF-8 FOR ENCODING;\r
+//      (if this is not possible, please include a comment\r
+//       that states what encoding is necessary.)\r
+\r
+{\r
+  "Bold": "Félkövér",\r
+  "Italic": "Dőlt",\r
+  "Underline": "Aláhúzott",\r
+  "Strikethrough": "Áthúzott",\r
+  "Subscript": "Alsó index",\r
+  "Superscript": "Felső index",\r
+  "Justify Left": "Balra zárt",\r
+  "Justify Center": "Középre zárt",\r
+  "Justify Right": "Jobbra zárt",\r
+  "Justify Full": "Sorkizárt",\r
+  "Ordered List": "Számozott lista",\r
+  "Bulleted List": "Számozatlan lista",\r
+  "Decrease Indent": "Behúzás csökkentése",\r
+  "Increase Indent": "Behúzás növelése",\r
+  "Font Color": "Karakterszín",\r
+  "Background Color": "Háttérszín",\r
+  "Horizontal Rule": "Elválasztó vonal",\r
+  "Insert Web Link": "Hiperhivatkozás beszúrása",\r
+  "Insert/Modify Image": "Kép beszúrása",\r
+  "Insert Table": "Táblázat beszúrása",\r
+  "Toggle HTML Source": "HTML forrás be/ki",\r
+  "Enlarge Editor": "Szerkesztő külön ablakban",\r
+  "About this editor": "Névjegy",\r
+  "Help using editor": "Súgó",\r
+  "Current style": "Aktuális stílus",\r
+  "Undoes your last action": "Visszavonás",\r
+  "Redoes your last action": "Újra végrehajtás",\r
+  "Cut selection": "Kivágás",\r
+  "Copy selection": "Másolás",\r
+  "Paste from clipboard": "Beillesztés",\r
+  "Direction left to right": "Irány balról jobbra",\r
+  "Direction right to left": "Irány jobbról balra",\r
+  "OK": "Rendben",\r
+  "Cancel": "Mégsem",\r
+  "Path": "Hierarchia",\r
+  "You are in TEXT MODE.  Use the [<>] button to switch back to WYSIWYG.": "Forrás mód. Visszaváltás [<>] gomb",\r
+  "The full screen mode is known to cause problems with Internet Explorer, due to browser bugs that we weren": "A teljesképrenyős szerkesztés hibát okozhat Internet Explorer használata esetén, ez a böngésző a hibája, amit nem tudunk kikerülni. Szemetet észlelhet a képrenyőn, illetve néhány funkció hiányozhat és/vagy véletlenszerűen lefagyhat a böngésző. Windows 9x operaciós futtatása esetén elég valószínű, hogy ",\r
+  "Cancel": "Mégsem",\r
+  "Insert/Modify Link": "Hivatkozás Beszúrása/Módosítása",\r
+  "New window (_blank)": "Új ablak (_blank)",\r
+  "None (use implicit)": "Nincs (use implicit)",\r
+  "Other": "Más",\r
+  "Same frame (_self)": "Ugyanabba a keretbe (_self)",\r
+  "Target:": "Cél:",\r
+  "Title (tooltip):": "Cím (tooltip):",\r
+  "Top frame (_top)": "Felső keret (_top)",\r
+  "URL:": "URL:",\r
+  "You must enter the URL where this link points to": "Be kell írnia az URL-t, ahova a hivatkozás mutasson"\r
+}\r
diff --git a/mailboxes/xinha/lang/it.js b/mailboxes/xinha/lang/it.js
new file mode 100644 (file)
index 0000000..426e74c
--- /dev/null
@@ -0,0 +1,55 @@
+// I18N constants\r
+\r
+// LANG: "it", ENCODING: UTF-8\r
+// Author: Mattia Landoni, http://www.webpresident.org/\r
+\r
+{\r
+  "Bold": "Grassetto",\r
+  "Italic": "Corsivo",\r
+  "Underline": "Sottolineato",\r
+  "Strikethrough": "Barrato",\r
+  "Subscript": "Pedice",\r
+  "Superscript": "Apice",\r
+  "Justify Left": "Sinistra",\r
+  "Justify Center": "Centrato",\r
+  "Justify Right": "Destra",\r
+  "Justify Full": "Giustificato",\r
+  "Ordered List": "Lista numerata",\r
+  "Bulleted List": "Lista non numerata",\r
+  "Decrease Indent": "Diminuisci indentazione",\r
+  "Increase Indent": "Aumenta indentazione",\r
+  "Font Color": "Colore font",\r
+  "Background Color": "Colore sfondo",\r
+  "Horizontal Rule": "Righello orizzontale",\r
+  "Insert Web Link": "Inserisci link",\r
+  "Insert/Modify Image": "Inserisci/modifica Immagine",\r
+  "Insert Table": "Inserisci tabella",\r
+  "Toggle HTML Source": "Visualizza/nascondi sorgente HTML",\r
+  "Enlarge Editor": "Allarga editor",\r
+  "About this editor": "Informazioni su HTMLArea",\r
+  "Help using editor": "Aiuto",\r
+  "Current style": "Stile corrente",\r
+  "Undoes your last action": "Annulla ultima azione",\r
+  "Redoes your last action": "Ripeti ultima azione",\r
+  "Cut selection": "Taglia",\r
+  "Copy selection": "Copia",\r
+  "Paste from clipboard": "Incolla",\r
+  "Direction left to right": "Testo da sx a dx",\r
+  "Direction right to left": "Testo da dx a sx",\r
+  "OK": "OK",\r
+  "Cancel": "Annulla",\r
+  "Path": "Percorso",\r
+  "You are in TEXT MODE.  Use the [<>] button to switch back to WYSIWYG.": "Sei in MODALITA",\r
+  "The full screen mode is known to cause problems with Internet Explorer, due to browser bugs that we weren": "E",\r
+  "Cancel": "Annulla",\r
+  "Insert/Modify Link": "Inserisci/modifica link",\r
+  "New window (_blank)": "Nuova finestra (_blank)",\r
+  "None (use implicit)": "Niente (usa implicito)",\r
+  "Other": "Altro",\r
+  "Same frame (_self)": "Stessa frame (_self)",\r
+  "Target:": "Target:",\r
+  "Title (tooltip):": "Title (suggerimento):",\r
+  "Top frame (_top)": "Pagina intera (_top)",\r
+  "URL:": "URL:",\r
+  "You must enter the URL where this link points to": "Devi inserire l'indirizzo a cui punta il link"\r
+}\r
diff --git a/mailboxes/xinha/lang/ja.js b/mailboxes/xinha/lang/ja.js
new file mode 100644 (file)
index 0000000..b2d927d
--- /dev/null
@@ -0,0 +1,169 @@
+// I18N constants
+// LANG: "ja", ENCODING: UTF-8N
+
+{
+  "Bold": "太字",
+  "Italic": "斜体",
+  "Underline": "下線",
+  "Strikethrough": "打ち消し線",
+  "Subscript": "下付き添え字",
+  "Superscript": "上付き添え字",
+  "Justify Left": "左寄せ",
+  "Justify Center": "中央寄せ",
+  "Justify Right": "右寄せ",
+  "Justify Full": "均等割付",
+  "Ordered List": "番号付き箇条書き",
+  "Bulleted List": "記号付き箇条書き",
+  "Decrease Indent": "インデント解除",
+  "Increase Indent": "インデント設定",
+  "Font Color": "文字色",
+  "Background Color": "背景色",
+  "Horizontal Rule": "水平線",
+  "Insert Web Link": "リンクの挿入",
+  "Insert/Modify Image": "画像の挿入/修正",
+  "Insert Table": "テーブルを挿入",
+  "Toggle HTML Source": "HTML編集モードを切替",
+  "Enlarge Editor": "エディタを最大化",
+  "About this editor": "バージョン情報",
+  "Help using editor": "ヘルプ",
+  "Current style": "現在のスタイル",
+  "Undoes your last action": "元に戻す",
+  "Redoes your last action": "やり直し",
+  "Cut selection": "切り取り",
+  "Copy selection": "コピー",
+  "Paste from clipboard": "貼り付け",
+  "Direction left to right": "左から右へ",
+  "Direction right to left": "右から左へ",
+  "Remove formatting": "書式削除",
+  "Select all": "すべて選択",
+  "Print document": "印刷",
+  "Clear MSOffice tags": "MSOfficeタグをクリア",
+  "Clear Inline Font Specifications": "インラインフォント指定をクリア",
+  "Would you like to clear font typefaces?": "フォント名をクリアしますか?",
+  "Would you like to clear font sizes?": "サイズをクリアしますか?",
+  "Would you like to clear font colours?": "色をクリアしますか?",
+  "Split Block": "領域分割",
+  "Toggle Borders": "境界線の切替",
+  "Save as": "名前をつけて保存",
+  "Insert/Overwrite": "挿入/上書き",
+  "&mdash; format &mdash;": "&mdash; 書式 &mdash;",
+  "Heading 1": "見出し1",
+  "Heading 2": "見出し2",
+  "Heading 3": "見出し3",
+  "Heading 4": "見出し4",
+  "Heading 5": "見出し5",
+  "Heading 6": "見出し6",
+  "Normal": "標準",
+  "Address": "アドレス",
+  "Formatted": "整形済み",
+  "&mdash; font &mdash;": "&mdash; フォント &mdash;",
+  "&mdash; size &mdash;": "&mdash; サイズ &mdash;",
+  
+  //dialogs
+  "OK": "OK",
+  "Cancel": "中止",
+  "Path": "パス",
+  "You are in TEXT MODE.  Use the [<>] button to switch back to WYSIWYG.": "テキストモードで操作しています。WYSIWYG編集に戻るには[<>]ボタンを使ってください。",
+  "The Paste button does not work in Mozilla based web browsers (technical security reasons). Press CTRL-V on your keyboard to paste directly.": "MozillaベースのWebブラウザでは、貼り付けボタンは機能しません(技術的なセキュリティ上の理由で)。Ctrl+Vキーを押して直接貼り付けてください。",
+  "Your Document is not well formed. Check JavaScript console for details.": "この文書には構文的な問題があります。詳細はJavaScriptコンソールを参照してください。",
+  "You need to select some text before creating a link": "リンクを作成するにはテキストを選択する必要があります",
+
+  "Alignment:": "行揃え:",
+  "Not set": "なし",
+  "Left": "左",
+  "Right": "右",
+  "Texttop": "テキスト上部",
+  "Absmiddle": "中央(絶対的)",
+  "Baseline": "ベースライン",
+  "Absbottom": "下(絶対的)",
+  "Bottom": "下",
+  "Middle": "中央",
+  "Top": "上",
+
+  "Layout": "レイアウト",
+  "Spacing": "間隔",
+  "Horizontal:": "水平:",
+  "Horizontal padding": "水平余白",
+  "Vertical:": "垂直:",
+  "Vertical padding": "垂直余白",
+  "Border thickness:": "境界線の太さ:",
+  "Leave empty for no border": "境界線がない場合は空のままにする",
+
+  //Insert Link
+  "Insert/Modify Link": "リンクの挿入/修正",
+  "None (use implicit)": "なし (デフォルトに任せる)",
+  "New window (_blank)": "新しいウィンドウ (_blank)",
+  "Same frame (_self)": "自己フレーム内 (_self)",
+  "Top frame (_top)": "最上位フレーム (_top)",
+  "Other": "その他",
+  "Target:": "ターゲット:",
+  "Title (tooltip):": "タイトル:",
+  "URL:": "URL:",
+  "You must enter the URL where this link points to": "このリンクが指し示すURLを入力してください",
+
+  // Insert Table
+  "Insert Table": "テーブルの挿入",
+  "Rows:": "行:",
+  "Number of rows": "行数",
+  "Cols:": "列:",
+  "Number of columns": "列数",
+  "Width:": "幅:",
+  "Width of the table": "テーブルの幅",
+  "Percent": "パーセント(%)",
+  "Pixels": "ピクセル(px)",
+  "Em": "相対値(em)",
+  "Width unit": "幅の単位",
+  "Fixed width columns": "列の幅を固定",
+  "Positioning of this table": "このテーブルの配置",
+  "Cell spacing:": "セル間隔:",
+  "Space between adjacent cells": "隣接するセル間の距離",
+  "Cell padding:": "セル余白:",
+  "Space between content and border in cell": "セル内における内容と境界線との距離",
+  "You must enter a number of rows": "行数を入力してください",
+  "You must enter a number of columns": "列数を入力してください",
+
+  // Insert Image
+  "Insert Image": "画像の挿入",
+  "Image URL:": "画像URL:",
+  "Enter the image URL here": "画像のURLをここに入力します",
+  "Preview": "表示",
+  "Preview the image in a new window": "ウィンドウで画像を表示",
+  "Alternate text:": "代替テキスト:",
+  "For browsers that don't support images": "画像表示をサポートしないブラウザに必要です",
+  "Positioning of this image": "画像の配置",
+  "Image Preview:": "画像表示:",
+  "You must enter the URL": "URLを入力する必要があります",
+
+  //"button_bold": "fr/bold.gif",
+  //"button_underline": "fr/underline.gif",
+  //"button_strikethrough": "fr/strikethrough.gif",
+
+  // Editor Help
+  "Xinha Help": "ヘルプ",
+  "Editor Help": "エディタのヘルプ",
+  "Keyboard shortcuts": "キーボードショートカット",
+  "The editor provides the following key combinations:": "エディタは以下のキー操作を提供しています:",
+  "ENTER": "ENTER",
+  "new paragraph": "新規段落",
+  "SHIFT-ENTER": "SHIFT+ENTER",
+  "insert linebreak": "段落内改行の挿入",
+  "Set format to paragraph": "段落書式の設定",
+  "Clean content pasted from Word": "Wordから貼り付けられた内容の清書",
+  "Headings": "見出し",
+  "Close": "閉じる",
+
+  // Loading messages
+  "Loading in progress. Please wait !": "ロード中です。しばらくお待ちください",
+  "Constructing main object": "構成中 main object",
+  "Constructing object": "構成中 object",
+  "Register panel right": "登録 右パネル",
+  "Register panel left": "登録 左パネル",
+  "Register panel top": "登録 上パネル",
+  "Register panel bottom": "登録 下パネル",
+  "Create Toolbar": "作成 ツールバー",
+  "Create StatusBar": "作成 ステータスバー",
+  "Generate Xinha object": "生成 Xinha object",
+  "Init editor size": "初期化 エディタのサイズ",
+  "Init IFrame": "初期化 IFrame",
+  "Register plugin $plugin": "プラグインの登録 $plugin"
+};
\ No newline at end of file
diff --git a/mailboxes/xinha/lang/lt.js b/mailboxes/xinha/lang/lt.js
new file mode 100644 (file)
index 0000000..1235676
--- /dev/null
@@ -0,0 +1,53 @@
+// I18N constants\r
+\r
+// LANG: "lt", ENCODING: UTF-8\r
+// Author: Jaroslav Šatkevič, <jaro@akl.lt>\r
+\r
+{\r
+  "Bold": "Paryškinti",\r
+  "Italic": "Kursyvas",\r
+  "Underline": "Pabraukti",\r
+  "Strikethrough": "Perbraukti",\r
+  "Subscript": "Apatinis indeksas",\r
+  "Superscript": "Viršutinis indeksas",\r
+  "Justify Left": "Lygiavimas pagal kairę",\r
+  "Justify Center": "Lygiavimas pagal centrą",\r
+  "Justify Right": "Lygiavimas pagal dešinę",\r
+  "Justify Full": "Lygiuoti pastraipą",\r
+  "Ordered List": "Numeruotas sąrašas",\r
+  "Bulleted List": "Suženklintas sąrašas",\r
+  "Decrease Indent": "Sumažinti paraštę",\r
+  "Increase Indent": "Padidinti paraštę",\r
+  "Font Color": "Šrifto spalva",\r
+  "Background Color": "Fono spalva",\r
+  "Horizontal Rule": "Horizontali linija",\r
+  "Insert Web Link": "Įterpti nuorodą",\r
+  "Insert/Modify Image": "Įterpti paveiksliuką",\r
+  "Insert Table": "Įterpti lentelę",\r
+  "Toggle HTML Source": "Perjungti į HTML/WYSIWYG",\r
+  "Enlarge Editor": "Išplėstas redagavimo ekranas/Enlarge Editor",\r
+  "About this editor": "Apie redaktorių",\r
+  "Help using editor": "Pagalba naudojant redaktorių",\r
+  "Current style": "Dabartinis stilius",\r
+  "Undoes your last action": "Atšaukia paskutini jūsų veiksmą",\r
+  "Redoes your last action": "Pakartoja paskutinį atšauktą jūsų veiksmą",\r
+  "Cut selection": "Iškirpti",\r
+  "Copy selection": "Kopijuoti",\r
+  "Paste from clipboard": "Įterpti",\r
+  "OK": "OK",\r
+  "Cancel": "Atšaukti",\r
+  "Path": "Kelias",\r
+  "You are in TEXT MODE.  Use the [<>] button to switch back to WYSIWYG.": "Jūs esete teksto režime.  Naudokite [<>] mygtuką grįžimui į WYSIWYG.",\r
+  "The full screen mode is known to cause problems with Internet Explorer, due to browser bugs that we weren": "The full screen mode is known to cause problems with Internet Explorer, due to browser bugs that we weren",\r
+  "Cancel": "Atšaukti",\r
+  "Insert/Modify Link": "Idėti/Modifikuoti",\r
+  "New window (_blank)": "Naujas langas (_blank)",\r
+  "None (use implicit)": "None (use implicit)",\r
+  "Other": "Kitas",\r
+  "Same frame (_self)": "Same frame (_self)",\r
+  "Target:": "Target:",\r
+  "Title (tooltip):": "Pavadinimas (tooltip):",\r
+  "Top frame (_top)": "Top frame (_top)",\r
+  "URL:": "URL:",\r
+  "You must enter the URL where this link points to": "Jus privalote nurodyti URL į kuri rodo šitą nuoroda"\r
+}\r
diff --git a/mailboxes/xinha/lang/lv.js b/mailboxes/xinha/lang/lv.js
new file mode 100644 (file)
index 0000000..b24593d
--- /dev/null
@@ -0,0 +1,42 @@
+// I18N constants\r
+\r
+// LANG: "lv", ENCODING: UTF-8\r
+// Author: Mihai Bazon, http://dynarch.com/mishoo\r
+// Translated by: Janis Klavins, <janis.klavins@devia.lv>\r
+\r
+{\r
+  "Bold": "Trekniem burtiem",\r
+  "Italic": "Kursîvâ",\r
+  "Underline": "Pasvîtrots",\r
+  "Strikethrough": "Pârsvîtrots",\r
+  "Subscript": "Novietot zem rindas",\r
+  "Superscript": "Novietot virs rindas",\r
+  "Justify Left": "Izlîdzinât pa kreisi",\r
+  "Justify Center": "Izlîdzinât centrâ",\r
+  "Justify Right": "Izlîdzinât pa labi",\r
+  "Justify Full": "Izlîdzinât pa visu lapu",\r
+  "Ordered List": "Numurçts saraksts",\r
+  "Bulleted List": "Saraksts",\r
+  "Decrease Indent": "Samazinât atkâpi",\r
+  "Increase Indent": "Palielinât atkâpi",\r
+  "Font Color": "Burtu krâsa",\r
+  "Background Color": "Fona krâsa",\r
+  "Horizontal Rule": "Horizontâla atdalîtâjsvîtra",\r
+  "Insert Web Link": "Ievietot hipersaiti",\r
+  "Insert/Modify Image": "Ievietot attçlu",\r
+  "Insert Table": "Ievietot tabulu",\r
+  "Toggle HTML Source": "Skatît HTML kodu",\r
+  "Enlarge Editor": "Palielinât Rediìçtâju",\r
+  "About this editor": "Par ðo rediìçtâju",\r
+  "Help using editor": "Rediìçtâja palîgs",\r
+  "Current style": "Patreizçjais stils",\r
+  "Undoes your last action": "Atcelt pçdçjo darbîbu",\r
+  "Redoes your last action": "Atkârtot pçdçjo darbîbu",\r
+  "Cut selection": "Izgriezt iezîmçto",\r
+  "Copy selection": "Kopçt iezîmçto",\r
+  "Paste from clipboard": "Ievietot iezîmçto",\r
+  "OK": "Labi",\r
+  "Cancel": "Atcelt",\r
+  "Path": "Ceïð",\r
+  "You are in TEXT MODE.  Use the [<>] button to switch back to WYSIWYG.": "Jûs patlaban darbojaties TEKSTA REÞÎMÂ. Lai pârietu atpakaï uz GRAFISKO REÞÎMU (WYSIWIG), lietojiet [<>] pogu."\r
+}\r
diff --git a/mailboxes/xinha/lang/nb.js b/mailboxes/xinha/lang/nb.js
new file mode 100644 (file)
index 0000000..6ae0451
--- /dev/null
@@ -0,0 +1,78 @@
+// I18N constants\r
+\r
+// LANG: "nb", ENCODING: UTF-8\r
+\r
+// - translated by ses<ses@online.no>\r
+// Additional translations by Håvard Wigtil <havardw@extend.no>\r
+// Additional translations by Kim Steinhaug <kim@steinhaug.com>\r
+\r
+{\r
+  "Bold": "Fet",\r
+  "Italic": "Kursiv",\r
+  "Underline": "Understreket",\r
+  "Strikethrough": "Gjennomstreket",\r
+  "Subscript": "Nedsenket",\r
+  "Superscript": "Opphøyet",\r
+  "Justify Left": "Venstrejuster",\r
+  "Justify Center": "Midtjuster",\r
+  "Justify Right": "Høyrejuster",\r
+  "Justify Full": "Blokkjuster",\r
+  "Ordered List": "Nummerert liste",\r
+  "Bulleted List": "Punktliste",\r
+  "Decrease Indent": "Reduser innrykk",\r
+  "Increase Indent": "Øke innrykk",\r
+  "Font Color": "Tekstfarge",\r
+  "Background Color": "Bakgrundsfarge",\r
+  "Horizontal Rule": "Vannrett linje",\r
+  "Insert Web Link": "Lag lenke",\r
+  "Insert/Modify Image": "Sett inn bilde",\r
+  "Insert Table": "Sett inn tabell",\r
+  "Toggle HTML Source": "Vis kildekode",\r
+  "Enlarge Editor": "Vis i eget vindu",\r
+  "About this editor": "Om denne editor",\r
+  "Help using editor": "Hjelp",\r
+  "Current style": "Nåværende stil",\r
+  "Undoes your last action": "Angrer siste redigering",\r
+  "Redoes your last action": "Gjør om siste angring",\r
+  "Cut selection": "Klipp ut område",\r
+  "Copy selection": "Kopier område",\r
+  "Save as": "Lagre som",\r
+  "Paste from clipboard": "Lim inn",\r
+  "Remove formatting": "Fjern formattering",\r
+  "Direction left to right": "Fra venstre mot høyre",\r
+  "Direction right to left": "Fra høyre mot venstre",\r
+  "Insert/Overwrite": "Sett inn/Overskriv",\r
+  "OK": "OK",\r
+  "Cancel": "Avbryt",\r
+  "Path": "Tekstvelger",\r
+  "You are in TEXT MODE.  Use the [<>] button to switch back to WYSIWYG.": "Du er i tekstmodus  Klikk på [<>] for å gå tilbake til WYSIWIG.",\r
+  "The full screen mode is known to cause problems with Internet Explorer, due to browser bugs that we weren": "Visning i eget vindu har kjente problemer med Internet Explorer, på grunn av problemer med denne nettleseren. Mulige problemer er et uryddig skjermbilde, manglende editorfunksjoner og/eller at nettleseren crasher. Hvis du bruker Windows 95 eller Windows 98 er det også muligheter for at Windows will crashe.\n\nTrykk ",\r
+  "Cancel": "Avbryt",\r
+  "Insert/Modify Link": "Rediger lenke",\r
+  "New window (_blank)": "Eget vindu (_blank)",\r
+  "None (use implicit)": "Ingen (bruk standardinnstilling)",\r
+  "Other": "Annen",\r
+  "Same frame (_self)": "Samme ramme (_self)",\r
+  "Target:": "Mål:",\r
+  "Title (tooltip):": "Tittel (tooltip):",\r
+  "Top frame (_top)": "Toppramme (_top)",\r
+  "URL:": "Adresse:",\r
+  "You must enter the URL where this link points to": "Du må skrive inn en adresse som denne lenken skal peke til",\r
+  "Clear Inline Font Specifications": "Fjerne inline font spesifikasjoner",\r
+  "Would you like to clear font typefaces?": "Ønsker du å fjerne skrifttyper",\r
+  "Would you like to clear font sizes?": "Ønsker du å fjerne skrift størrelser",\r
+  "Would you like to clear font colours?": "Ønsker du å fjerne farger på skriften",\r
+  "Print document": "Skriv ut dokumentet",\r
+  "Split Block": "Seperasjonsblokk",\r
+  "Toggle Borders": "Skru av/på hjelpelinjer på tabeller",\r
+  "Select all": "Merk alt",\r
+  // Loading messages\r
+  "Loading in progress. Please wait !": "WYSIWYG laster, vennligst vent!",\r
+  "Constructing main object": "Vennligst vent",\r
+  "Create Toolbar": "Lag verktøylinje",\r
+  "Register panel right": "Registrer høyrepanel",\r
+  "Register panel left": "Registrer venstrepanel",\r
+  "Register panel top": "Registrer toppanel",\r
+  "Register panel bottom": "Registrer bunnpanel"\r
+\r
+};\r
diff --git a/mailboxes/xinha/lang/nl.js b/mailboxes/xinha/lang/nl.js
new file mode 100644 (file)
index 0000000..3fe6b8c
--- /dev/null
@@ -0,0 +1,64 @@
+// I18N constants\r
+\r
+// LANG: "nl", ENCODING: UTF-8\r
+// Author: Michel Weegeerink (info@mmc-shop.nl), http://mmc-shop.nl\r
+\r
+// FOR TRANSLATORS:\r
+//\r
+//   1. PLEASE PUT YOUR CONTACT INFO IN THE ABOVE LINE\r
+//      (at least a valid email address)\r
+//\r
+//   2. PLEASE TRY TO USE UTF-8 FOR ENCODING;\r
+//      (if this is not possible, please include a comment\r
+//       that states what encoding is necessary.)\r
+\r
+{\r
+  "Bold": "Vet",\r
+  "Italic": "Cursief",\r
+  "Underline": "Onderstrepen",\r
+  "Strikethrough": "Doorhalen",\r
+  "Subscript": "Subscript",\r
+  "Superscript": "Superscript",\r
+  "Justify Left": "Links uitlijnen",\r
+  "Justify Center": "Centreren",\r
+  "Justify Right": "Rechts uitlijnen",\r
+  "Justify Full": "Uitvullen",\r
+  "Ordered List": "Nummering",\r
+  "Bulleted List": "Opsommingstekens",\r
+  "Decrease Indent": "Inspringing verkleinen",\r
+  "Increase Indent": "Inspringing vergroten",\r
+  "Font Color": "Tekstkleur",\r
+  "Background Color": "Achtergrondkleur",\r
+  "Horizontal Rule": "Horizontale lijn",\r
+  "Insert Web Link": "Hyperlink invoegen/aanpassen",\r
+  "Insert/Modify Image": "Afbeelding invoegen/aanpassen",\r
+  "Insert Table": "Tabel invoegen",\r
+  "Toggle HTML Source": "HTML broncode",\r
+  "Enlarge Editor": "Vergroot Editor",\r
+  "About this editor": "Over deze editor",\r
+  "Help using editor": "HTMLArea help",\r
+  "Current style": "Huidige stijl",\r
+  "Undoes your last action": "Ongedaan maken",\r
+  "Redoes your last action": "Herhalen",\r
+  "Cut selection": "Knippen",\r
+  "Copy selection": "Kopi?ren",\r
+  "Paste from clipboard": "Plakken",\r
+  "Direction left to right": "Tekstrichting links naar rechts",\r
+  "Direction right to left": "Tekstrichting rechts naar links",\r
+  "OK": "OK",\r
+  "Cancel": "Annuleren",\r
+  "Path": "Pad",\r
+  "You are in TEXT MODE.  Use the [<>] button to switch back to WYSIWYG.": "Je bent in TEKST-mode. Gebruik de [<>] knop om terug te keren naar WYSIWYG-mode.",\r
+  "The full screen mode is known to cause problems with Internet Explorer, due to browser bugs that we weren": "Fullscreen-mode veroorzaakt problemen met Internet Explorer door bugs in de webbrowser die we niet kunnen omzeilen. Hierdoor kunnen de volgende effecten optreden: verknoeide teksten, een verlies aan editor-functionaliteit en/of willekeurig vastlopen van de webbrowser. Als u Windows 95 of 98 gebruikt, is het zeer waarschijnlijk dat u een algemene beschermingsfout (",\r
+  "Cancel": "Annuleren",\r
+  "Insert/Modify Link": "Hyperlink invoegen/aanpassen",\r
+  "New window (_blank)": "Nieuw venster (_blank)",\r
+  "None (use implicit)": "Geen",\r
+  "Other": "Ander",\r
+  "Same frame (_self)": "Zelfde frame (_self)",\r
+  "Target:": "Doel:",\r
+  "Title (tooltip):": "Titel (tooltip):",\r
+  "Top frame (_top)": "Bovenste frame (_top)",\r
+  "URL:": "URL:",\r
+  "You must enter the URL where this link points to": "Geef de URL in waar de link naar verwijst"\r
+}\r
diff --git a/mailboxes/xinha/lang/pl.js b/mailboxes/xinha/lang/pl.js
new file mode 100644 (file)
index 0000000..b5bd5ce
--- /dev/null
@@ -0,0 +1,125 @@
+// I18N constants
+// LANG: "pl", ENCODING: UTF-8
+// translated: Krzysztof Kotowicz, http://www.eskot.krakow.pl/portfolio/, koto@webworkers.pl
+{
+  "Bold": "Pogrubienie",
+  "Italic": "Pochylenie",
+  "Underline": "Podkreślenie",
+  "Strikethrough": "Przekreślenie",
+  "Subscript": "Indeks dolny",
+  "Superscript": "Indeks górny",
+  "Justify Left": "Wyrównaj do lewej",
+  "Justify Center": "Wyśrodkuj",
+  "Justify Right": "Wyrównaj do prawej",
+  "Justify Full": "Wyjustuj",
+  "Ordered List": "Numerowanie",
+  "Bulleted List": "Wypunktowanie",
+  "Decrease Indent": "Zmniejsz wcięcie",
+  "Increase Indent": "Zwiększ wcięcie",
+  "Font Color": "Kolor czcionki",
+  "Background Color": "Kolor tła",
+  "Horizontal Rule": "Linia pozioma",
+  "Insert Web Link": "Wstaw adres sieci Web",
+  "Insert/Modify Image": "Wstaw obraz",
+  "Insert Table": "Wstaw tabelę",
+  "Toggle HTML Source": "Edycja WYSIWYG/w źródle strony",
+  "Enlarge Editor": "Pełny ekran",
+  "About this editor": "Informacje o tym edytorze",
+  "Help using editor": "Pomoc",
+  "Current style": "Obecny styl",
+  "Undoes your last action": "Cofa ostatnio wykonane polecenie",
+  "Redoes your last action": "Ponawia ostatnio wykonane polecenie",
+  "Cut selection": "Wycina zaznaczenie do schowka",
+  "Copy selection": "Kopiuje zaznaczenie do schowka",
+  "Paste from clipboard": "Wkleja zawartość schowka",
+  "Direction left to right": "Kierunek tekstu lewo-prawo",
+  "Direction right to left": "Kierunek tekstu prawo-lewo",
+  "Remove formatting": "Usuń formatowanie",
+  "Select all": "Zaznacz wszystko",
+  "Print document": "Drukuj dokument",
+  "Clear MSOffice tags": "Wyczyść tagi MSOffice",
+  "Clear Inline Font Specifications": "Wycisz bezpośrednie przypisania czcionek",
+  "Split Block": "Podziel blok",
+  "Toggle Borders": "Włącz / wyłącz ramki",
+
+  "&mdash; format &mdash;": "&mdash; Format &mdash;",
+  "Heading 1": "Nagłówek 1",
+  "Heading 2": "Nagłówek 2",
+  "Heading 3": "Nagłówek 3",
+  "Heading 4": "Nagłówek 4",
+  "Heading 5": "Nagłówek 5",
+  "Heading 6": "Nagłówek 6",
+  "Normal": "Normalny",
+  "Address": "Adres",
+  "Formatted": "Preformatowany",
+
+  //dialogs
+  "OK": "OK",
+  "Cancel": "Anuluj",
+  "Path": "Ścieżka",
+  "You are in TEXT MODE.  Use the [<>] button to switch back to WYSIWYG.": "Jesteś w TRYBIE TEKSTOWYM. Użyj przycisku [<>], aby przełączyć się na tryb WYSIWYG.",
+  "The Paste button does not work in Mozilla based web browsers (technical security reasons). Press CTRL-V on your keyboard to paste directly.": "Przycisk Wklej nie działa w przeglądarkach Mozilla z uwagi na ustawienia bezpieczeństwa. Naciśnij CRTL-V, aby wkleić zawartość schowka.",
+
+  "Alignment:": "Wyrównanie:",
+  "Not set": "Nie ustawione",
+  "Left": "Do lewej",
+  "Right": "Do prawej",
+  "Texttop": "Góra tekstu",
+  "Absmiddle": "Abs. środek",
+  "Baseline": "Linia bazowa",
+  "Absbottom": "Abs. dół",
+  "Bottom": "Dół",
+  "Middle": "Środek",
+  "Top": "Góra",
+
+  "Layout": "Layout",
+  "Spacing": "Spacjowanie",
+  "Horizontal:": "Poziome:",
+  "Horizontal padding": "Wcięcie poziome",
+  "Vertical:": "Pionowe:",
+  "Vertical padding": "Wcięcie pionowe",
+  "Border thickness:": "Grubość obramowania:",
+  "Leave empty for no border": "Bez ramek - zostaw puste",
+
+  //Insert Link
+  "Insert/Modify Link": "Wstaw/edytuj odnośnik",
+  "None (use implicit)": "Brak",
+  "New window (_blank)": "Nowe okno (_blank)",
+  "Same frame (_self)": "Ta sama ramka (_self)",
+  "Top frame (_top)": "Główna ramka (_top)",
+  "Other": "Inne",
+  "Target:": "Okno docelowe:",
+  "Title (tooltip):": "Tytuł (tooltip):",
+  "URL:": "URL:",
+  "You must enter the URL where this link points to": "Musisz podać URL, na jaki będzie wskazywał odnośnik",
+
+  // Insert Table
+  "Insert Table": "Wstaw tabelę",
+  "Rows:": "Wierszy:",
+  "Number of rows": "Liczba wierszy",
+  "Cols:": "Kolumn:",
+  "Number of columns": "Liczba kolumn",
+  "Width:": "Szerokość:",
+  "Width of the table": "Szerokość tabeli",
+  "Percent": "Procent",
+  "Pixels": "Pikseli",
+  "Em": "Em",
+  "Width unit": "Jednostka",
+  "Fixed width columns": "Kolumny o stałej szerokości",
+  "Positioning of this table": "Pozycjonowanie tabeli",
+  "Cell spacing:": "Odstęp komórek:",
+  "Space between adjacent cells": "Przestrzeń pomiędzy komórkami",
+  "Cell padding:": "Wcięcie komórek:",
+  "Space between content and border in cell": "Przestrzeń między krawędzią a zawartością komórki",
+
+  // Insert Image
+  "Insert Image": "Wstaw obrazek",
+  "Image URL:": "URL obrazka:",
+  "Enter the image URL here": "Podaj URL obrazka",
+  "Preview": "Podgląd",
+  "Preview the image in a new window": "Podgląd obrazka w nowym oknie",
+  "Alternate text:": "Tekst alternatywny:",
+  "For browsers that don't support images": "Dla przeglądarek, które nie obsługują obrazków",
+  "Positioning of this image": "Pozycjonowanie obrazka",
+  "Image Preview:": "Podgląd obrazka:"
+}
diff --git a/mailboxes/xinha/lang/pt_br.js b/mailboxes/xinha/lang/pt_br.js
new file mode 100644 (file)
index 0000000..156216d
--- /dev/null
@@ -0,0 +1,32 @@
+// I18N constants\r
+\r
+// LANG: "bt_br", ENCODING: UTF-8\r
+// Brazilian Portuguese Translation by Alex Piaz <webmaster@globalmap.com>\r
+\r
+{
+  "Bold": "Negrito",
+  "Italic": "Itálico",
+  "Underline": "Sublinhado",
+  "Strikethrough": "Tachado",
+  "Subscript": "Subescrito",
+  "Superscript": "Sobrescrito",
+  "Justify Left": "Alinhar à Esquerda",
+  "Justify Center": "Centralizar",
+  "Justify Right": "Alinhar à Direita",
+  "Justify Full": "Justificar",
+  "Ordered List": "Lista Numerada",
+  "Bulleted List": "Lista Marcadores",
+  "Decrease Indent": "Diminuir Indentação",
+  "Increase Indent": "Aumentar Indentação",
+  "Font Color": "Cor da Fonte",
+  "Background Color": "Cor do Fundo",
+  "Horizontal Rule": "Linha Horizontal",
+  "Insert Web Link": "Inserir Link",
+  "Insert/Modify Image": "Inserir Imagem",
+  "Insert Table": "Inserir Tabela",
+  "Toggle HTML Source": "Ver Código-Fonte",
+  "Enlarge Editor": "Expandir Editor",
+  "About this editor": "Sobre",
+  "Help using editor": "Ajuda",
+  "Current style": "Estilo Atual"
+}
diff --git a/mailboxes/xinha/lang/ro.js b/mailboxes/xinha/lang/ro.js
new file mode 100644 (file)
index 0000000..6565bd2
--- /dev/null
@@ -0,0 +1,63 @@
+// I18N constants\r
+\r
+// LANG: "ro", ENCODING: UTF-8\r
+// Author: Mihai Bazon, http://dynarch.com/mishoo\r
+\r
+// FOR TRANSLATORS:\r
+//\r
+//   1. PLEASE PUT YOUR CONTACT INFO IN THE ABOVE LINE\r
+//      (at least a valid email address)\r
+//\r
+//   2. PLEASE TRY TO USE UTF-8 FOR ENCODING;\r
+//      (if this is not possible, please include a comment\r
+//       that states what encoding is necessary.)\r
+\r
+{\r
+  "Bold": "Îngroşat",\r
+  "Italic": "Italic",\r
+  "Underline": "Subliniat",\r
+  "Strikethrough": "Tăiat",\r
+  "Subscript": "Indice jos",\r
+  "Superscript": "Indice sus",\r
+  "Justify Left": "Aliniere la stânga",\r
+  "Justify Center": "Aliniere pe centru",\r
+  "Justify Right": "Aliniere la dreapta",\r
+  "Justify Full": "Aliniere în ambele părţi",\r
+  "Ordered List": "Listă ordonată",\r
+  "Bulleted List": "Listă marcată",\r
+  "Decrease Indent": "Micşorează alineatul",\r
+  "Increase Indent": "Măreşte alineatul",\r
+  "Font Color": "Culoarea textului",\r
+  "Background Color": "Culoare de fundal",\r
+  "Horizontal Rule": "Linie orizontală",\r
+  "Insert Web Link": "Inserează/modifică link",\r
+  "Insert/Modify Image": "Inserează/modifică imagine",\r
+  "Insert Table": "Inserează un tabel",\r
+  "Toggle HTML Source": "Sursa HTML / WYSIWYG",\r
+  "Enlarge Editor": "Maximizează editorul",\r
+  "About this editor": "Despre editor",\r
+  "Help using editor": "Documentaţie (devel)",\r
+  "Current style": "Stilul curent",\r
+  "Undoes your last action": "Anulează ultima acţiune",\r
+  "Redoes your last action": "Reface ultima acţiune anulată",\r
+  "Cut selection": "Taie în clipboard",\r
+  "Copy selection": "Copie în clipboard",\r
+  "Paste from clipboard": "Aduce din clipboard",\r
+  "Direction left to right": "Direcţia de scriere: stânga - dreapta",\r
+  "Direction right to left": "Direcţia de scriere: dreapta - stânga",\r
+  "OK": "OK",\r
+  "Cancel": "Anulează",\r
+  "Path": "Calea",\r
+  "You are in TEXT MODE.  Use the [<>] button to switch back to WYSIWYG.": "Eşti în modul TEXT.  Apasă butonul [<>] pentru a te întoarce în modul WYSIWYG.",\r
+  "Cancel": "Renunţă",\r
+  "Insert/Modify Link": "Inserează/modifcă link",\r
+  "New window (_blank)": "Fereastră nouă (_blank)",\r
+  "None (use implicit)": "Nimic (foloseşte ce-i implicit)",\r
+  "Other": "Alt target",\r
+  "Same frame (_self)": "Aceeaşi fereastră (_self)",\r
+  "Target:": "Ţinta:",\r
+  "Title (tooltip):": "Titlul (tooltip):",\r
+  "Top frame (_top)": "Fereastra principală (_top)",\r
+  "URL:": "URL:",\r
+  "You must enter the URL where this link points to": "Trebuie să introduceţi un URL"\r
+}\r
diff --git a/mailboxes/xinha/lang/ru.js b/mailboxes/xinha/lang/ru.js
new file mode 100644 (file)
index 0000000..5290b7f
--- /dev/null
@@ -0,0 +1,185 @@
+// I18N constants\r
+\r
+// LANG: "ru", ENCODING: UTF-8\r
+// Author: Yulya Shtyryakova, <yulya@vdcom.ru>\r
+\r
+// Some additions by: Alexey Kirpichnikov, <alexkir@kiwistudio.ru>\r
+// I took French version as a source of English phrases because French version was the most comprehensive\r
+// (fr.js was the largest file, actually) %)\r
+\r
+// FOR TRANSLATORS:\r
+//\r
+//   1. PLEASE PUT YOUR CONTACT INFO IN THE ABOVE LINE\r
+//      (at least a valid email address)\r
+//\r
+//   2. PLEASE TRY TO USE UTF-8 FOR ENCODING;\r
+//      (if this is not possible, please include a comment\r
+//       that states what encoding is necessary.)\r
+\r
+{\r
+  "Bold": "Полужирный",\r
+  "Italic": "Наклонный",\r
+  "Underline": "Подчеркнутый",\r
+  "Strikethrough": "Перечеркнутый",\r
+  "Subscript": "Нижний индекс",\r
+  "Superscript": "Верхний индекс",\r
+  "Justify Left": "По левому краю",\r
+  "Justify Center": "По центру",\r
+  "Justify Right": "По правому краю",\r
+  "Justify Full": "По ширине",\r
+  "Ordered List": "Нумерованный список",\r
+  "Bulleted List": "Маркированный список",\r
+  "Decrease Indent": "Уменьшить отступ",\r
+  "Increase Indent": "Увеличить отступ",\r
+  "Font Color": "Цвет шрифта",\r
+  "Background Color": "Цвет фона",\r
+  "Horizontal Rule": "Горизонтальный разделитель",\r
+  "Insert Web Link": "Вставить гиперссылку",\r
+  "Insert/Modify Image": "Вставить изображение",\r
+  "Insert Table": "Вставить таблицу",\r
+  "Toggle HTML Source": "Показать Html-код",\r
+  "Enlarge Editor": "Увеличить редактор",\r
+  "About this editor": "О редакторе",\r
+  "Help using editor": "Помощь",\r
+  "Current style": "Текущий стиль",\r
+  "Undoes your last action": "Отменить",\r
+  "Redoes your last action": "Повторить",\r
+  "Cut selection": "Вырезать",\r
+  "Copy selection": "Копировать",\r
+  "Paste from clipboard": "Вставить",\r
+  "Direction left to right": "Направление слева направо",\r
+  "Direction right to left": "Направление справа налево",\r
+  "Remove formatting": "Убрать форматирование",\r
+  "Select all": "Выделить все",\r
+  "Print document": "Печать",\r
+  "Clear MSOffice tags": "Удалить разметку MSOffice",\r
+  "Clear Inline Font Specifications": "Удалить непосредственное задание шрифтов",\r
+  "Would you like to clear font typefaces?": "Удалить типы шрифтов?",\r
+  "Would you like to clear font sizes?": "Удалить размеры шрифтов ?",\r
+  "Would you like to clear font colours?": "Удалить цвета шрифтов ?",\r
+  "Split Block": "Разделить блок",\r
+  "Toggle Borders": "Включить/выключить отображение границ",\r
+  "Save as": "Сохранить как",\r
+  "Insert/Overwrite": "Вставка/замена",\r
+  "&mdash; format &mdash;": "&mdash; форматирование &mdash;",\r
+  "Heading 1": "Заголовок 1",\r
+  "Heading 2": "Заголовок 2",\r
+  "Heading 3": "Заголовок 3",\r
+  "Heading 4": "Заголовок 4",\r
+  "Heading 5": "Заголовок 5",\r
+  "Heading 6": "Заголовок 6",\r
+  "Normal": "Обычный текст",\r
+  "Address": "Адрес",\r
+  "Formatted": "Отформатированный текст",\r
+\r
+  "&mdash; font &mdash;": "&mdash; шрифт &mdash;",\r
+  "&mdash; size &mdash;": "&mdash; размер &mdash;",\r
+\r
+\r
+// Диалоги\r
+\r
+  "OK": "OK",\r
+  "Cancel": "Отмена",\r
+  "Path": "Путь",\r
+  "You are in TEXT MODE.  Use the [<>] button to switch back to WYSIWYG.": "Вы в режиме отображения Html-кода. нажмите кнопку [<>], чтобы переключиться в визуальный режим.",\r
+\r
+"The Paste button does not work in Mozilla based web browsers (technical security reasons). Press CTRL-V on your keyboard to paste directly.": "Кнопка Вставить не работает в браузерах на основе Mozilla (по техническим причинам, связанным с безопасностью). Нажмите Ctrl-V на клавиатуре, чтобы вставить.",\r
+\r
+  "Your Document is not well formed. Check JavaScript console for details.": "Ваш документ неправильно сформирован. Посмотрите Консоль JavaScript, чтобы узнать подробности.",\r
+\r
+  "Alignment:": "Выравнивание",\r
+  "Not set": "Не установлено",\r
+  "Left": "По левому краю",\r
+  "Right": "По правому краю",\r
+  "Texttop": "По верхней границе текста",\r
+  "Absmiddle": "По середине текста",\r
+  "Baseline": "По нижней границе текста",\r
+  "Absbottom": "По нижней границе",\r
+  "Bottom": "По нижнему краю",\r
+  "Middle": "Посредине",\r
+  "Top": "По верхнему краю",\r
+\r
+  "Layout": "Расположение",\r
+  "Spacing": "Поля",\r
+  "Horizontal:": "По горизонтали",\r
+  "Horizontal padding": "Горизонтальные поля",\r
+  "Vertical:": "По вертикали",\r
+  "Vertical padding": "Вертикальные поля",\r
+  "Border thickness:": "Толщина рамки",\r
+  "Leave empty for no border": "Оставьте пустым, чтобы убрать рамку",\r
+\r
+  //Insert Link\r
+  "Insert/Modify Link": "Вставка/изменение ссылки",\r
+  "None (use implicit)": "По умолчанию",\r
+  "New window (_blank)": "Новое окно (_blank)",\r
+  "Same frame (_self)": "То же окно (_self)",\r
+  "Top frame (_top)": "Родительское окно (_top)",\r
+  "Other": "Другое",\r
+  "Target:": "Открывать в окне:",\r
+  "Title (tooltip):": "Всплывающая подсказка",\r
+  "URL:": "URL:",\r
+  "You must enter the URL where this link points to": "Вы должны указать URL, на который будет указывать ссылка",\r
+  "You need to select some text before creating a link": "Вы должны выделить текст, который будет преобразован в ссылку",\r
+\r
+  // Insert Table\r
+  "Insert Table": "Вставка таблицы",\r
+  "Rows:": "Строки",\r
+  "Number of rows": "Количество строк",\r
+  "Cols:": "Столбцы",\r
+  "Number of columns": "Количество столбцов",\r
+  "Width:": "Ширина",\r
+  "Width of the table": "Ширина таблицы",\r
+  "Percent": "проценты",\r
+  "Pixels": "пикселы",\r
+  "Em": "em",\r
+  "Width unit": "Единицы измерения",\r
+  "Fixed width columns": "Столбцы фиксированной ширины",\r
+  "Positioning of this table": "Расположение таблицы",\r
+  "Cell spacing:": "Расстояние между ячейками",\r
+  "Space between adjacent cells": "Расстояние между соседними ячейками",\r
+  "Cell padding:": "Поля в ячейках",\r
+  "Space between content and border in cell": "Расстояние между границей ячейки и текстом",\r
+  "You must enter a number of rows": "Вы должны ввести количество строк",\r
+  "You must enter a number of columns": "Вы должны ввести количество столбцов",\r
+\r
+  // Insert Image\r
+  "Insert Image": "Вставка изображения",\r
+  "Image URL:": "URL изображения",\r
+  "Enter the image URL here": "Вставьте адрес изображения",\r
+  "Preview": "Предварительный просмотр",\r
+  "Preview the image in a new window": "Предварительный просмотр в отдельном окне",\r
+  "Alternate text:": "Альтернативный текст",\r
+  "For browsers that don't support images": "Для браузеров, которые не отображают картинки",\r
+  "Positioning of this image": "Расположение изображения",\r
+  "Image Preview:": "Предварительный просмотр",\r
+  "You must enter the URL": "Вы должны ввести URL",\r
+\r
+  // Editor Help\r
+  "Xinha Help": "Помощь",\r
+  "Editor Help": "Помощь",\r
+  "Keyboard shortcuts": "Горячие клавиши",\r
+  "The editor provides the following key combinations:": "Редактор поддерживает следующие комбинации клавиш:",\r
+  "ENTER": "ENTER",\r
+  "new paragraph": "новый абзац",\r
+  "SHIFT-ENTER": "SHIFT+ENTER",\r
+  "insert linebreak": "перенос строки",\r
+  "Set format to paragraph": "Отформатировать абзац",\r
+  "Clean content pasted from Word": "Очистить текст, вставленный из Word",\r
+  "Headings": "Заголовки",\r
+  "Close": "Закрыть",\r
+\r
+  // Loading messages\r
+  "Loading in progress. Please wait !": "Загрузка... Пожалуйста, подождите.",\r
+  "Constructing main object": "Создание главного объекта",\r
+  "Constructing object": "Создание объекта",\r
+  "Register panel right": "Регистрация правой панели",\r
+  "Register panel left": "Регистрация левой панели",\r
+  "Register panel top": "Регистрация верхней панели",\r
+  "Register panel bottom": "Регистрация нижней панели",\r
+  "Create Toolbar": "Создание панели инструментов",\r
+  "Create StatusBar": "Создание панели состояния",\r
+  "Generate Xinha object": "Создание объекта Xinha",\r
+  "Init editor size": "Инициализация размера редактора",\r
+  "Init IFrame": "инициализация iframe",\r
+  "Register plugin $plugin": "Регистрация $plugin"\r
+};\r
diff --git a/mailboxes/xinha/lang/sh.js b/mailboxes/xinha/lang/sh.js
new file mode 100644 (file)
index 0000000..2706304
--- /dev/null
@@ -0,0 +1,140 @@
+// I18N constants
+
+// LANG: "sh", ENCODING: UTF-8 | ISO-8859-2
+// Author: Ljuba Ranković, http://www.rankovic.net/ljubar
+
+// FOR TRANSLATORS:
+//
+//   1. PLEASE PUT YOUR CONTACT INFO IN THE ABOVE LINE
+//      (at least a valid email address)
+//
+//   2. PLEASE TRY TO USE UTF-8 FOR ENCODING;
+//      (if this is not possible, please include a comment
+//       that states what encoding is necessary.)
+
+{
+               "Bold": "Masno",
+               "Italic": "Kurziv",
+               "Underline": "Podvučeno",
+               "Strikethrough": "Precrtano",
+               "Subscript": "Indeks-tekst",
+               "Superscript": "Eksponent-tekst",
+               "Justify Left":"Ravnanje ulevo",
+               "Justify Center": "Ravnanje po simetrali",
+               "Justify Right": "Ravnanje udesno",
+               "Justify Full": "Puno ravnanje",
+               "Ordered List": "Lista sa rednim brojevima",
+               "Bulleted List": "Lista sa simbolima",
+               "Decrease Indent": "smanji uvlačenje",
+               "Increase Indent": "Povećaj uvlačenje",
+               "Font Color": "Boja slova",
+               "Background Color": "Boja pozadine",
+               "Horizontal Rule": "Horizontalna linija",
+               "Insert Web Link": "Dodaj web link",
+               "Insert/Modify Image": "Dodaj/promeni sliku",
+               "Insert Table": "Ubaci tabelu",
+               "Toggle HTML Source": "Prebaci na HTML kod",
+               "Enlarge Editor": "Povećaj editor",
+               "About this editor": "O ovom editoru",
+               "Help using editor": "Pomoć pri korišćenju editora",
+               "Current style": "Važeći stil",
+               "Undoes your last action": "Poništava poslednju radnju",
+               "Redoes your last action": "Vraća poslednju radnju",
+               "Cut selection": "Iseci izabrano",
+               "Copy selection": "Kopiraj izabrano",
+               "Paste from clipboard": "Zalepi iz klipborda",
+               "Direction left to right": "Pravac s leva na desno",
+               "Direction right to left": "Pravac s desna na levo",
+        "Remove formatting": "Ukoni formatiranje",
+        "Select all": "Izaberi sve",
+        "Print document": "Štampaj dokument",
+        "Clear MSOffice tags": "Obriši MSOffice tagove",
+        "Clear Inline Font Specifications": "Obriši dodeljene osobine fonta",
+        "Split Block": "Podeli blok",
+        "Toggle Borders": "Izmeni okvire",
+
+        "&mdash; format &mdash;": "&mdash; Format &mdash;",
+        "Heading 1": "Zaglavlje 1",
+        "Heading 2": "Zaglavlje 2",
+        "Heading 3": "Zaglavlje 3",
+        "Heading 4": "Zaglavlje 4",
+        "Heading 5": "Zaglavlje 5",
+        "Heading 6": "Zaglavlje 6",
+        "Normal": "Običan",
+        "Address": "Adresa",
+        "Formatted": "Formatiran",
+        
+        // dialogs
+               "OK": "OK",
+               "Cancel": "Poništi",
+               "Path": "Putanja",
+               "You are in TEXT MODE.  Use the [<>] button to switch back to WYSIWYG.": "Nalazite se u TEXT režimu.  Koristite [<>] dugme za povratak na WYSIWYG.",
+
+               "The Paste button does not work in Mozilla based web browsers (technical security reasons). Press CTRL-V on your keyboard to paste directly.": "",
+
+        "Alignment:": "Ravnanje",
+        "Not set": "Nije postavljeno",
+        "Left": "Levo",
+        "Right": "Desno",
+        "Texttop": "Vrh teksta",
+        "Absmiddle": "Apsolutna sredina",
+        "Baseline": "Donja linija",
+        "Absbottom": "Apsolutno dno",
+        "Bottom": "Dno",
+        "Middle": "Sredina",
+        "Top": "Vrh",
+
+        "Layout": "Prelom",
+        "Spacing": "Razmak",
+        "Horizontal:": "Po horizontali",
+        "Horizontal padding": "Horizontalno odstojanje",
+        "Vertical:": "Po vertikali",
+        "Vertical padding": "Vertikalno odstojanje",
+        "Border thickness:": "Debljina okvira",
+        "Leave empty for no border": "Ostavi prazno kad nema okvira",
+               
+        // Insert Link
+               "Insert/Modify Link": "Dodaj/promeni Link",
+               "None (use implicit)": "koristi podrazumevano",
+               "New window (_blank)": "Novom prozoru (_blank)",
+               "Same frame (_self)": "Isti frejm (_self)",
+               "Top frame (_top)": "Glavni frejm (_top)",
+               "Other": "Drugo",
+               "Target:": "Otvori u:",
+               "Title (tooltip):": "Naziv (tooltip):",
+               "URL:": "URL:",
+               "You must enter the URL where this link points to": "Morate uneti URL na koji vodi ovaj link",
+               
+        // Insert Table
+        "Insert Table": "Ubaci tabelu",
+        "Rows:": "Redovi",
+        "Number of rows": "Broj redova",
+        "Cols:": "Kolone",
+        "Number of columns": "Broj kolona",
+        "Width:": "Širina",
+        "Width of the table": "Širina tabele",
+        "Percent": "Procenat",
+        "Pixels": "Pikseli",
+        "Em": "Em",
+        "Width unit": "Jedinica širine",
+        "Fixed width columns": "Fiksirana širina kolona",
+        "Positioning of this table": "Postavljanje ove tabele",
+        "Cell spacing:": "Rastojanje ćelija",
+        "Space between adjacent cells": "Rastojanje naspramnih ćelija",
+        "Cell padding:": "Unutrašnja odstojanja u ćeliji",
+        "Space between content and border in cell": "Rastojanje između sadržaja i okvira ćelije",
+
+        // Insert Image
+        "Insert Image": "Ubaci sliku",
+        "Image URL:": "URL slike",
+        "Enter the image URL here": "Unesite URL slike ovde",
+        "Preview": "Pregled",
+        "Preview the image in a new window": "Pregledaj sliku u novom prozoru",
+        "Alternate text:": "Alternativni tekst",
+        "For browsers that don't support images": "Za pretraživače koji ne podržavaju slike",
+        "Positioning of this image": "Postavljanje ove slike",
+        "Image Preview:": "Pregled slike",
+        
+        // Select Color popup
+               "Select Color": "Izaberite boju"
+};
diff --git a/mailboxes/xinha/lang/si.js b/mailboxes/xinha/lang/si.js
new file mode 100644 (file)
index 0000000..90a69b5
--- /dev/null
@@ -0,0 +1,50 @@
+// I18N constants\r
+\r
+// LANG: "si", ENCODING: UTF-8\r
+// Author: Tomaz Kregar, x_tomo_x@email.si\r
+\r
+// FOR TRANSLATORS:\r
+//\r
+//   1. PLEASE PUT YOUR CONTACT INFO IN THE ABOVE LINE\r
+//      (at least a valid email address)\r
+//\r
+//   2. PLEASE TRY TO USE UTF-8 FOR ENCODING;\r
+//      (if this is not possible, please include a comment\r
+//       that states what encoding is necessary.)\r
+\r
+{\r
+  "Bold": "Krepko",\r
+  "Italic": "Ležeče",\r
+  "Underline": "Podčrtano",\r
+  "Strikethrough": "Prečrtano",\r
+  "Subscript": "Podpisano",\r
+  "Superscript": "Nadpisano",\r
+  "Justify Left": "Poravnaj levo",\r
+  "Justify Center": "Na sredino",\r
+  "Justify Right": "Poravnaj desno",\r
+  "Justify Full": "Porazdeli vsebino",\r
+  "Ordered List": "Oštevilčevanje",\r
+  "Bulleted List": "Označevanje",\r
+  "Decrease Indent": "Zmanjšaj zamik",\r
+  "Increase Indent": "Povečaj zamik",\r
+  "Font Color": "Barva pisave",\r
+  "Background Color": "Barva ozadja",\r
+  "Horizontal Rule": "Vodoravna črta",\r
+  "Insert Web Link": "Vstavi hiperpovezavo",\r
+  "Insert/Modify Image": "Vstavi sliko",\r
+  "Insert Table": "Vstavi tabelo",\r
+  "Toggle HTML Source": "Preklopi na HTML kodo",\r
+  "Enlarge Editor": "Povečaj urejevalnik",\r
+  "About this editor": "Vizitka za urejevalnik",\r
+  "Help using editor": "Pomoč za urejevalnik",\r
+  "Current style": "Trenutni slog",\r
+  "Undoes your last action": "Razveljavi zadnjo akcijo",\r
+  "Redoes your last action": "Uveljavi zadnjo akcijo",\r
+  "Cut selection": "Izreži",\r
+  "Copy selection": "Kopiraj",\r
+  "Paste from clipboard": "Prilepi",\r
+  "OK": "V redu",\r
+  "Cancel": "Prekliči",\r
+  "Path": "Pot",\r
+  "You are in TEXT MODE.  Use the [<>] button to switch back to WYSIWYG.": "Si v tekstovnem načinu.  Uporabi [<>] gumb za prklop nazaj na WYSIWYG."\r
+}\r
diff --git a/mailboxes/xinha/lang/sr.js b/mailboxes/xinha/lang/sr.js
new file mode 100644 (file)
index 0000000..06f78c4
--- /dev/null
@@ -0,0 +1,140 @@
+// I18N constants
+
+// LANG: "sh", ENCODING: UTF-8 | ISO-8859-5
+// Author: Ljuba Ranković, http://www.rankovic.net/ljubar
+
+// FOR TRANSLATORS:
+//
+//   1. PLEASE PUT YOUR CONTACT INFO IN THE ABOVE LINE
+//      (at least a valid email address)
+//
+//   2. PLEASE TRY TO USE UTF-8 FOR ENCODING;
+//      (if this is not possible, please include a comment
+//       that states what encoding is necessary.)
+
+{
+               "Bold": "Масно",
+               "Italic": "Курзив",
+               "Underline": "Подвучено",
+               "Strikethrough": "Прецртано",
+               "Subscript": "Индекс-текст",
+               "Superscript": "Експонент-текст",
+               "Justify Left": "Равнање улево",
+               "Justify Center": "Равнање по симетрали",
+               "Justify Right": "Равнање удесно",
+               "Justify Full": "Пуно равнање",
+               "Ordered List": "Листа са редним бројевима",
+               "Bulleted List": "Листа са симболима",
+               "Decrease Indent": "Смањи увлачење",
+               "Increase Indent": "Повећај увлачење",
+               "Font Color": "Боја слова",
+               "Background Color": "Боја позадине",
+               "Horizontal Rule": "Хоризонтална линија",
+               "Insert Web Link": "додај веб линк",
+               "Insert/Modify Image": "додај/промени слику",
+               "Insert Table": "Убаци табелу",
+               "Toggle HTML Source": "Пребаци на приказ ХТМЛ кода",
+               "Enlarge Editor": "Повећај едитор",
+               "About this editor": "О овом едитору",
+               "Help using editor": "Помоћ при коришћењу едитора",
+               "Current style": "Важећи стил",
+               "Undoes your last action": "Поништава последњу радњу",
+               "Redoes your last action": "Враћа последњу радњу",
+               "Cut selection": "Исеци изабрано",
+               "Copy selection": "Копирај изабрано",
+               "Paste from clipboard": "Залепи из клипборда",
+               "Direction left to right": "Правац с лева на десно",
+               "Direction right to left": "Правац с десна на лево",
+        "Remove formatting": "Уклони форматирање",
+        "Select all": "Изабери све",
+        "Print document": "Штампај документ",
+        "Clear MSOffice tags": "Обриши MSOffice тагове",
+        "Clear Inline Font Specifications": "Обриши примењене особине фонта",
+        "Split Block": "Подели блок",
+        "Toggle Borders": "Пребаци оквирне линије",
+
+        "&mdash; format &mdash;": "&mdash; Format &mdash;",
+        "Heading 1": "Заглавље 1",
+        "Heading 2": "Заглавље 2",
+        "Heading 3": "Заглавље 3",
+        "Heading 4": "Заглавље 4",
+        "Heading 5": "Заглавље 5",
+        "Heading 6": "Заглавље 6",
+        "Normal": "обичан",
+        "Address": "адреса",
+        "Formatted": "форматиран",           
+               
+        // dialogs
+               "OK": "OK",
+               "Cancel": "Поништи",
+               "Path": "Путања",
+               "You are in TEXT MODE.  Use the [<>] button to switch back to WYSIWYG.":    "Налазите се у ТЕКСТ режиму.  Користите [<>] дугме за повратак на ШВТИД (WYSIWYG).",
+
+               "The Paste button does not work in Mozilla based web browsers (technical security reasons). Press CTRL-V on your keyboard to paste directly.": "Дугме 'залепи' не ради у претраживачима породице Mozilla (из разлога сигурности). Притисните CTRL-V на тастатури да директно залепите.",
+               
+        "Alignment:": "Равнање",
+        "Not set": "Није постављено",
+        "Left": "Лево",
+        "Right": "Десно",
+        "Texttop": "Врх текста",
+        "Absmiddle": "Апсолутна средина",
+        "Baseline": "Доња линија",
+        "Absbottom": "Апсолутно дно",
+        "Bottom": "Дно",
+        "Middle": "Средина",
+        "Top": "Врх",
+
+        "Layout": "Прелом",
+        "Spacing": "Размак",
+        "Horizontal:": "По хоризонтали",
+        "Horizontal padding": "Хортизонтално одстојање",
+        "Vertical:": "По вертикали",
+        "Vertical padding": "Вертикално одстојање",
+        "Border thickness:": "Дебљина оквира",
+        "Leave empty for no border": "Остави празно кад нема оквира",
+
+        // Insert Link
+               "Insert/Modify Link": "додај/промени линк",
+               "None (use implicit)": "користи подразумевано",
+               "New window (_blank)": "Новом прозору (_blank)",
+               "Same frame (_self)": "Исти фрејм (_self)",
+               "Top frame (_top)": "Главни фрејм (_top)",
+               "Other": "Друго",
+               "Target:": "Отвори у:",
+               "Title (tooltip):": "Назив (tooltip):",
+               "URL:": "УРЛ:",
+               "You must enter the URL where this link points to": "Морате унети УРЛ на који води овај линк",
+
+        // Insert Table
+        "Insert Table": "Убаци табелу",
+        "Rows:": "Редови",
+        "Number of rows": "Број редова",
+        "Cols:": "Колоне",
+        "Number of columns": "Број колона",
+        "Width:": "Ширина",
+        "Width of the table": "Ширина табеле",
+        "Percent": "Процената",
+        "Pixels": "Пиксела",
+        "Em": "Ем",
+        "Width unit": "Јединица ширине",
+        "Fixed width columns": "Фиксирана ширина колоне",
+        "Positioning of this table": "Постављање ове табеле",
+        "Cell spacing:": "Размак између ћелија",
+        "Space between adjacent cells": "Размак између наспрамних ћелија",
+        "Cell padding:": "Унутрашња одстојања од ивица ћелије",
+        "Space between content and border in cell": "Растојање између садржаја у ћелији и њеног оквира",
+
+        // Insert Image
+        "Insert Image": "Убаци слику",
+        "Image URL:": "УРЛ слике",
+        "Enter the image URL here": "Унесите УРЛ слике овде",
+        "Preview": "Преглед",
+        "Preview the image in a new window": "Прегледај слику у новом прозору",
+        "Alternate text:": "алтернативни текст",
+        "For browsers that don't support images": "За претраживаче који не подржавају слике",
+        "Positioning of this image": "Постављање ове слике",
+        "Image Preview:": "Преглед слике",
+
+        // Select Color popup
+               "Select Color": "Изабери боју"
+};
diff --git a/mailboxes/xinha/lang/sv.js b/mailboxes/xinha/lang/sv.js
new file mode 100644 (file)
index 0000000..61af49e
--- /dev/null
@@ -0,0 +1,116 @@
+// I18N constants
+// LANG: "sv", ENCODING: UTF-8
+
+// Swedish version for htmlArea v3.0
+// Initital translation by pat <pat@engvall.nu>
+// Synced with additional contants in rev. 477 (Mar 2006) by Thomas Loo <tloo@saltstorm.net>
+
+{
+  "Bold": "Fet",
+  "Italic": "Kursiv",
+  "Underline": "Understruken",
+  "Strikethrough": "Genomstruken",
+  "Subscript": "Nedsänkt",
+  "Superscript": "Upphöjd",
+  "Justify Left": "Vänsterjustera",
+  "Justify Center": "Centrera",
+  "Justify Right": "Högerjustera",
+  "Justify Full": "Marginaljustera",
+  "Ordered List": "Numrerad lista",
+  "Bulleted List": "Punktlista",
+  "Decrease Indent": "Minska indrag",
+  "Increase Indent": "Öka indrag",
+  "Font Color": "Textfärg",
+  "Background Color": "Bakgrundsfärg",
+  "Horizontal Rule": "Vågrät linje",
+  "Insert Web Link": "Infoga länk",
+  "Insert/Modify Image": "Infoga bild",
+  "Toggle HTML Source": "Visa källkod",
+  "Enlarge Editor": "Visa i eget fönster",
+  "About this editor": "Om denna editor",
+  "Help using editor": "Hjälp",
+  "Current style": "Nuvarande stil",
+
+  "Undoes your last action": "Ångra kommando",
+  "Redoes your last action": "Upprepa kommando",
+  "Select all": "Markera allt",
+  "Print document": "Skriv ut",
+  "Clear MSOffice tags": "Städa bort MS Office taggar",
+  "Clear Inline Font Specifications": "Rensa inbäddad typsnittsinformation",
+  "Remove formatting": "Rensa formattering",
+  "Toggle Borders": "Objektramar",
+  "Split Block": "Dela block",
+  "Direction left to right": "Vänster till höger",
+  "Direction right to left": "Höger till vänster",
+
+  "Insert/Overwrite": "Infoga/Skriv över",
+  "OK": "OK",
+  "Cancel": "Avbryt",
+  "Path": "Objekt",
+  "You are in TEXT MODE.  Use the [<>] button to switch back to WYSIWYG.": "Du befinner dig i texläge. Klicka på ikonen [<>] ovan för att växla tillbaka till WYSIWIG läge",
+  "The full screen mode is known to cause problems with Internet Explorer, due to browser bugs that we weren": "Visning i fullskärmsläga fungerar dåligt i din webläsare. Möjliga problem resulterar i en ryckig editor, saknade editorfunktioner och/eller att webläsaren kraschar. Om du använder Windows 95/98 finns också möjligheten att Windows kraschar.\n\nTryck ",
+  "The Paste button does not work in Mozilla based web browsers (technical security reasons). Press CTRL-V on your keyboard to paste directly.": "Denna knapp fungerar ej i Mozillabaserad webläsare, använd istället snabbtangenterna CTRL-V på tangentbordet för att klistra in.",
+
+  "Insert/Modify Link": "Redigera länk",
+  "New window (_blank)": "Nytt fönster (_blank)",
+  "None (use implicit)": "Ingen (använd standardinställing)",
+  "Other": "Annan",
+  "Same frame (_self)": "Samma ram (_self)",
+  "Target:": "Mål:",
+  "Title (tooltip):": "Titel (tooltip):",
+  "Top frame (_top)": "Toppram (_top)",
+  "URL:": "Sökväg:",
+  "You must enter the URL where this link points to": "Du måsta ange en adress till vilken länken skall peka på",
+  "Would you like to clear font typefaces?": "Radera alla typsnittsinformation ?",
+  "Would you like to clear font sizes?": "Radera alla fontstorlekar ?",
+  "Would you like to clear font colours?": "Ta bort all textfärger ?",
+
+  "You need to select some text before creating a link": "Du måsta markera ett objekt att applicera länken på!",
+
+  // Insert Table
+  "Insert Table": "Infoga tabell",
+  "Rows:": "Rader:",
+  "Number of rows": "Antal rader",
+  "Cols:": "Kolumner:",
+  "Number of columns": "Antal kolumner",
+  "Width:": "Bredd:",
+  "Width of the table": "Tabellbredd",
+  "Percent": "Procent",
+  "Pixels": "Pixlar",
+  "Em": "",
+  "Width unit": "Breddenheter",
+  "Fixed width columns": "Fixerad bredd",
+  "Alignment:": "Marginaljustering",
+  "Positioning of this table": "Tabellposition",
+  "Border thickness:": "Ramtjocklek",
+  "Leave empty for no border": "Lämna fältet tomt för att undvika ramar",
+  "Spacing": "Cellegenskaper",
+  "Cell spacing:": "Cellmarginaler:",
+  "Space between adjacent cells": "Utrymme mellan celler",
+  "Cell padding:": "Cellindrag:",
+  "Space between content and border in cell": "Utrymme mellan ram och cellinnehåll",
+  "You must enter a number of rows": "Ange ental rader",
+  "You must enter a number of columns": "Ange antal kolumner",
+
+  // Editor Help
+  "Keyboard shortcuts": "Snabbtangenter",
+  "The editor provides the following key combinations:": "Editorn nyttjar följande kombinationer:",
+  "new paragraph": "Ny paragraf ",
+  "insert linebreak": "Infoga radbrytning ",
+  "Set format to paragraph": "Aktivera paragrafläge",
+  "Clean content pasted from Word": "Rensa innehåll inklistrat från MS Word",
+  "Headings": "Skapa standardrubrik",
+  "Cut selection": "Klipp ut markering",
+  "Copy selection": "Kopiera  markering",
+  "Paste from clipboard": "Klistra in",
+  "Close": "Stäng",
+
+  // Loading messages
+  "Loading in progress. Please wait !": "Editorn laddas. Vänta...",
+  "Constructing main object": "Skapar huvudobjekt",
+  "Create Toolbar": "Skapar verktygspanel",
+  "Register panel right": "Registerar panel höger",
+  "Register panel left": "Registerar panel vänster",
+  "Register panel top": "Registerar toppanel",
+  "Register panel bottom": "Registerar fotpanel"
+}
diff --git a/mailboxes/xinha/lang/vn.js b/mailboxes/xinha/lang/vn.js
new file mode 100644 (file)
index 0000000..4bdd0d3
--- /dev/null
@@ -0,0 +1,56 @@
+// I18N constants : Vietnamese\r
+// LANG: "en", ENCODING: UTF-8\r
+// Author: Nguyễn Đình Nam, <hncryptologist@yahoo.com>\r
+// Modified 21/07/2004 by Phạm Mai Quân <pmquan@4vn.org>\r
+\r
+{\r
+  "Bold": "Đậm",\r
+  "Italic": "Nghiêng",\r
+  "Underline": "Gạch Chân",\r
+  "Strikethrough": "Gạch Xóa",\r
+  "Subscript": "Viết Xuống Dưới",\r
+  "Superscript": "Viết Lên Trên",\r
+  "Justify Left": "Căn Trái",\r
+  "Justify Center": "Căn Giữa",\r
+  "Justify Right": "Căn Phải",\r
+  "Justify Full": "Căn Đều",\r
+  "Ordered List": "Danh Sách Có Thứ Tự (1, 2, 3)",\r
+  "Bulleted List": "Danh Sách Phi Thứ Tự (Chấm đầu dòng)",\r
+  "Decrease Indent": "Lùi Ra Ngoài",\r
+  "Increase Indent": "Thụt Vào Trong",\r
+  "Font Color": "Màu Chữ",\r
+  "Background Color": "Màu Nền",\r
+  "Horizontal Rule": "Dòng Kẻ Ngang",\r
+  "Insert Web Link": "Tạo Liên Kết",\r
+  "Insert/Modify Image": "Chèn Ảnh",\r
+  "Insert Table": "Chèn Bảng",\r
+  "Toggle HTML Source": "Chế Độ Mã HTML",\r
+  "Enlarge Editor": "Phóng To Ô Soạn Thảo",\r
+  "About this editor": "Tự Giới Thiệu",\r
+  "Help using editor": "Giúp Đỡ",\r
+  "Current style": "Định Dạng Hiện Thời",\r
+  "Undoes your last action": "Hủy thao tác trước",\r
+  "Redoes your last action": "Lấy lại thao tác vừa bỏ",\r
+  "Cut selection": "Cắt",\r
+  "Copy selection": "Sao chép",\r
+  "Paste from clipboard": "Dán",\r
+  "Direction left to right": "Viết từ trái sang phải",\r
+  "Direction right to left": "Viết từ phải sang trái",\r
+  "OK": "Đồng ý",\r
+  "Cancel": "Hủy",\r
+  "The full screen mode is known to cause problems with Internet Explorer, due to browser bugs that we weren": "Chế độ phóng to ô soạn thảo có thể gây lỗi với Internet Explorer vì một số lỗi của trình duyệt này, vì thế chế độ này có thể sẽ không chạy. Hiển thị không đúng, lộn xộn, không có đầy đủ chức năng, và cũng có thể làm trình duyệt của bạn bị tắt ngang. Nếu bạn đang sử dụng Windows 9x bạn có thể bị báo lỗi ",\r
+  "Path": "Đường Dẫn",\r
+  "You are in TEXT MODE.  Use the [<>] button to switch back to WYSIWYG.": "Bạn đang ở chế độ text.  Sử dụng nút [<>] để chuyển lại chế độ WYSIWIG.",\r
+  "Cancel": "Hủy",\r
+  "Insert/Modify Link": "Thêm/Chỉnh sửa đường dẫn",\r
+  "New window (_blank)": "Cửa sổ mới (_blank)",\r
+  "None (use implicit)": "Không (sử dụng implicit)",\r
+  "OK": "Đồng ý",\r
+  "Other": "Khác",\r
+  "Same frame (_self)": "Trên cùng khung (_self)",\r
+  "Target:": "Nơi hiện thị:",\r
+  "Title (tooltip):": "Tiêu đề (của hướng dẫn):",\r
+  "Top frame (_top)": "Khung trên cùng (_top)",\r
+  "URL:": "URL:",\r
+  "You must enter the URL where this link points to": "Bạn phải điền địa chỉ (URL) mà đường dẫn sẽ liên kết tới"\r
+}\r
diff --git a/mailboxes/xinha/license.txt b/mailboxes/xinha/license.txt
new file mode 100644 (file)
index 0000000..e7798cf
--- /dev/null
@@ -0,0 +1,30 @@
+htmlArea License (based on BSD license)\r
+Copyright (c) 2002-2004, interactivetools.com, inc.\r
+Copyright (c) 2003-2004 dynarch.com\r
+All rights reserved.\r
+\r
+Redistribution and use in source and binary forms, with or without\r
+modification, are permitted provided that the following conditions are met:\r
+\r
+1) Redistributions of source code must retain the above copyright notice,\r
+   this list of conditions and the following disclaimer.\r
+\r
+2) Redistributions in binary form must reproduce the above copyright notice,\r
+   this list of conditions and the following disclaimer in the documentation\r
+   and/or other materials provided with the distribution.\r
+\r
+3) Neither the name of interactivetools.com, inc. nor the names of its\r
+   contributors may be used to endorse or promote products derived from this\r
+   software without specific prior written permission.\r
+\r
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"\r
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\r
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\r
+ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE\r
+LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR\r
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF\r
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS\r
+INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN\r
+CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)\r
+ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE\r
+POSSIBILITY OF SUCH DAMAGE.\r
diff --git a/mailboxes/xinha/modules/ColorPicker/ColorPicker.js b/mailboxes/xinha/modules/ColorPicker/ColorPicker.js
new file mode 100644 (file)
index 0000000..dfdeb85
--- /dev/null
@@ -0,0 +1,522 @@
+ColorPicker._pluginInfo={name:"colorPicker",version:"1.0",developer:"James Sleeman",developer_url:"http://www.gogo.co.nz/",c_owner:"Gogo Internet Services",license:"htmlArea",sponsor:"Gogo Internet Services",sponsor_url:"http://www.gogo.co.nz/"};
+function ColorPicker(){
+}
+Xinha.colorPicker=function(_1){
+if(Xinha.colorPicker.savedColors.length===0){
+Xinha.colorPicker.loadColors();
+}
+var _2=this;
+var _3=false;
+var _4=false;
+var _5=0;
+var _6=0;
+this.callback=_1.callback?_1.callback:function(_7){
+alert("You picked "+_7);
+};
+this.websafe=_1.websafe?_1.websafe:false;
+this.savecolors=_1.savecolors?_1.savecolors:20;
+this.cellsize=parseInt(_1.cellsize?_1.cellsize:"10px",10);
+this.side=_1.granularity?_1.granularity:18;
+var _8=this.side+1;
+var _9=this.side-1;
+this.value=1;
+this.saved_cells=null;
+this.table=document.createElement("table");
+this.table.className="dialog";
+this.table.cellSpacing=this.table.cellPadding=0;
+this.table.onmouseup=function(){
+_3=false;
+_4=false;
+};
+this.tbody=document.createElement("tbody");
+this.table.appendChild(this.tbody);
+this.table.style.border="1px solid WindowFrame";
+this.table.style.zIndex="1000";
+var tr=document.createElement("tr");
+var td=document.createElement("td");
+td.colSpan=this.side;
+td.className="title";
+td.style.fontFamily="small-caption,caption,sans-serif";
+td.style.fontSize="x-small";
+td.appendChild(document.createTextNode(Xinha._lc("Click a color...")));
+td.style.borderBottom="1px solid WindowFrame";
+tr.appendChild(td);
+td=null;
+var td=document.createElement("td");
+td.className="title";
+td.colSpan=2;
+td.style.fontFamily="Tahoma,Verdana,sans-serif";
+td.style.borderBottom="1px solid WindowFrame";
+td.style.paddingRight="0";
+tr.appendChild(td);
+var _c=document.createElement("div");
+_c.title=Xinha._lc("Close");
+_c.className="buttonColor";
+_c.style.height="11px";
+_c.style.width="11px";
+_c.style.cursor="pointer";
+_c.onclick=function(){
+_2.close();
+};
+_c.appendChild(document.createTextNode("\xd7"));
+_c.align="center";
+_c.style.verticalAlign="top";
+_c.style.position="relative";
+_c.style.cssFloat="right";
+_c.style.styleFloat="right";
+_c.style.padding="0";
+_c.style.margin="2px";
+_c.style.backgroundColor="transparent";
+_c.style.fontSize="11px";
+if(!Xinha.is_ie){
+_c.style.lineHeight="9px";
+}
+_c.style.letterSpacing="0";
+td.appendChild(_c);
+this.tbody.appendChild(tr);
+_c=tr=td=null;
+this.constrain_cb=document.createElement("input");
+this.constrain_cb.type="checkbox";
+this.chosenColor=document.createElement("input");
+this.chosenColor.type="text";
+this.chosenColor.maxLength=7;
+this.chosenColor.style.width="50px";
+this.chosenColor.style.fontSize="11px";
+this.chosenColor.onchange=function(){
+if(/#[0-9a-f]{6,6}/i.test(this.value)){
+_2.backSample.style.backgroundColor=this.value;
+_2.foreSample.style.color=this.value;
+}
+};
+this.backSample=document.createElement("div");
+this.backSample.appendChild(document.createTextNode("\xa0"));
+this.backSample.style.fontWeight="bold";
+this.backSample.style.fontFamily="small-caption,caption,sans-serif";
+this.backSample.fontSize="x-small";
+this.foreSample=document.createElement("div");
+this.foreSample.appendChild(document.createTextNode(Xinha._lc("Sample")));
+this.foreSample.style.fontWeight="bold";
+this.foreSample.style.fontFamily="small-caption,caption,sans-serif";
+this.foreSample.fontSize="x-small";
+function toHex(_d){
+var h=_d.toString(16);
+if(h.length<2){
+h="0"+h;
+}
+return h;
+}
+function tupleToColor(_f){
+return "#"+toHex(_f.red)+toHex(_f.green)+toHex(_f.blue);
+}
+function nearestPowerOf(num,_11){
+return Math.round(Math.round(num/_11)*_11);
+}
+function doubleHexDec(dec){
+return parseInt(dec.toString(16)+dec.toString(16),16);
+}
+function rgbToWebsafe(_13){
+_13.red=doubleHexDec(nearestPowerOf(parseInt(toHex(_13.red).charAt(0),16),3));
+_13.blue=doubleHexDec(nearestPowerOf(parseInt(toHex(_13.blue).charAt(0),16),3));
+_13.green=doubleHexDec(nearestPowerOf(parseInt(toHex(_13.green).charAt(0),16),3));
+return _13;
+}
+function hsvToRGB(h,s,v){
+var _17;
+if(s===0){
+_17={red:v,green:v,blue:v};
+}else{
+h/=60;
+var i=Math.floor(h);
+var f=h-i;
+var p=v*(1-s);
+var q=v*(1-s*f);
+var t=v*(1-s*(1-f));
+switch(i){
+case 0:
+_17={red:v,green:t,blue:p};
+break;
+case 1:
+_17={red:q,green:v,blue:p};
+break;
+case 2:
+_17={red:p,green:v,blue:t};
+break;
+case 3:
+_17={red:p,green:q,blue:v};
+break;
+case 4:
+_17={red:t,green:p,blue:v};
+break;
+default:
+_17={red:v,green:p,blue:q};
+break;
+}
+}
+_17.red=Math.ceil(_17.red*255);
+_17.green=Math.ceil(_17.green*255);
+_17.blue=Math.ceil(_17.blue*255);
+return _17;
+}
+this.open=function(_1d,_1e,_1f){
+this.table.style.display="";
+this.pick_color();
+if(_1f&&/#[0-9a-f]{6,6}/i.test(_1f)){
+this.chosenColor.value=_1f;
+this.backSample.style.backgroundColor=_1f;
+this.foreSample.style.color=_1f;
+}
+this.table.style.position="absolute";
+var e=_1e;
+var top=0;
+var _22=0;
+do{
+top+=e.offsetTop;
+_22+=e.offsetLeft;
+e=e.offsetParent;
+}while(e);
+var x,y;
+if(/top/.test(_1d)){
+if(top-this.table.offsetHeight>0){
+this.table.style.top=(top-this.table.offsetHeight)+"px";
+}else{
+this.table.style.top=0;
+}
+}else{
+this.table.style.top=(top+_1e.offsetHeight)+"px";
+}
+if(/left/.test(_1d)){
+this.table.style.left=_22+"px";
+}else{
+if(_22-(this.table.offsetWidth-_1e.offsetWidth)>0){
+this.table.style.left=(_22-(this.table.offsetWidth-_1e.offsetWidth))+"px";
+}else{
+this.table.style.left=0;
+}
+}
+};
+function pickCell(_24){
+_2.chosenColor.value=_24.colorCode;
+_2.backSample.style.backgroundColor=_24.colorCode;
+_2.foreSample.style.color=_24.colorCode;
+if((_24.hue>=195&&_24.saturation>0.5)||(_24.hue===0&&_24.saturation===0&&_24.value<0.5)||(_24.hue!==0&&_2.value<0.75)){
+_24.style.borderColor="#fff";
+}else{
+_24.style.borderColor="#000";
+}
+_5=_24.thisrow;
+_6=_24.thiscol;
+}
+function pickValue(_25){
+if(_2.value<0.5){
+_25.style.borderColor="#fff";
+}else{
+_25.style.borderColor="#000";
+}
+_9=_25.thisrow;
+_8=_25.thiscol;
+_2.chosenColor.value=_2.saved_cells[_5][_6].colorCode;
+_2.backSample.style.backgroundColor=_2.saved_cells[_5][_6].colorCode;
+_2.foreSample.style.color=_2.saved_cells[_5][_6].colorCode;
+}
+function unpickCell(row,col){
+_2.saved_cells[row][col].style.borderColor=_2.saved_cells[row][col].colorCode;
+}
+this.pick_color=function(){
+var _28,cols;
+var _29=this;
+var _2a=359/(this.side);
+var _2b=1/(this.side-1);
+var _2c=1/(this.side-1);
+var _2d=this.constrain_cb.checked;
+if(this.saved_cells===null){
+this.saved_cells=[];
+for(var row=0;row<this.side;row++){
+var tr=document.createElement("tr");
+this.saved_cells[row]=[];
+for(var col=0;col<this.side;col++){
+var td=document.createElement("td");
+if(_2d){
+td.colorCode=tupleToColor(rgbToWebsafe(hsvToRGB(_2a*row,_2b*col,this.value)));
+}else{
+td.colorCode=tupleToColor(hsvToRGB(_2a*row,_2b*col,this.value));
+}
+this.saved_cells[row][col]=td;
+td.style.height=this.cellsize+"px";
+td.style.width=this.cellsize-2+"px";
+td.style.borderWidth="1px";
+td.style.borderStyle="solid";
+td.style.borderColor=td.colorCode;
+td.style.backgroundColor=td.colorCode;
+if(row==_5&&col==_6){
+td.style.borderColor="#000";
+this.chosenColor.value=td.colorCode;
+this.backSample.style.backgroundColor=td.colorCode;
+this.foreSample.style.color=td.colorCode;
+}
+td.hue=_2a*row;
+td.saturation=_2b*col;
+td.thisrow=row;
+td.thiscol=col;
+td.onmousedown=function(){
+_3=true;
+_29.saved_cells[_5][_6].style.borderColor=_29.saved_cells[_5][_6].colorCode;
+pickCell(this);
+};
+td.onmouseover=function(){
+if(_3){
+pickCell(this);
+}
+};
+td.onmouseout=function(){
+if(_3){
+this.style.borderColor=this.colorCode;
+}
+};
+td.ondblclick=function(){
+Xinha.colorPicker.remember(this.colorCode,_29.savecolors);
+_29.callback(this.colorCode);
+_29.close();
+};
+td.appendChild(document.createTextNode(" "));
+td.style.cursor="pointer";
+tr.appendChild(td);
+td=null;
+}
+var td=document.createElement("td");
+td.appendChild(document.createTextNode(" "));
+td.style.width=this.cellsize+"px";
+tr.appendChild(td);
+td=null;
+var td=document.createElement("td");
+this.saved_cells[row][col+1]=td;
+td.appendChild(document.createTextNode(" "));
+td.style.width=this.cellsize-2+"px";
+td.style.height=this.cellsize+"px";
+td.constrainedColorCode=tupleToColor(rgbToWebsafe(hsvToRGB(0,0,_2c*row)));
+td.style.backgroundColor=td.colorCode=tupleToColor(hsvToRGB(0,0,_2c*row));
+td.style.borderWidth="1px";
+td.style.borderStyle="solid";
+td.style.borderColor=td.colorCode;
+if(row==_9){
+td.style.borderColor="black";
+}
+td.hue=_2a*row;
+td.saturation=_2b*col;
+td.hsv_value=_2c*row;
+td.thisrow=row;
+td.thiscol=col+1;
+td.onmousedown=function(){
+_4=true;
+_29.saved_cells[_9][_8].style.borderColor=_29.saved_cells[_9][_8].colorCode;
+_29.value=this.hsv_value;
+_29.pick_color();
+pickValue(this);
+};
+td.onmouseover=function(){
+if(_4){
+_29.value=this.hsv_value;
+_29.pick_color();
+pickValue(this);
+}
+};
+td.onmouseout=function(){
+if(_4){
+this.style.borderColor=this.colorCode;
+}
+};
+td.style.cursor="pointer";
+tr.appendChild(td);
+td=null;
+this.tbody.appendChild(tr);
+tr=null;
+}
+var tr=document.createElement("tr");
+this.saved_cells[row]=[];
+for(var col=0;col<this.side;col++){
+var td=document.createElement("td");
+if(_2d){
+td.colorCode=tupleToColor(rgbToWebsafe(hsvToRGB(0,0,_2c*(this.side-col-1))));
+}else{
+td.colorCode=tupleToColor(hsvToRGB(0,0,_2c*(this.side-col-1)));
+}
+this.saved_cells[row][col]=td;
+td.style.height=this.cellsize+"px";
+td.style.width=this.cellsize-2+"px";
+td.style.borderWidth="1px";
+td.style.borderStyle="solid";
+td.style.borderColor=td.colorCode;
+td.style.backgroundColor=td.colorCode;
+td.hue=0;
+td.saturation=0;
+td.value=_2c*(this.side-col-1);
+td.thisrow=row;
+td.thiscol=col;
+td.onmousedown=function(){
+_3=true;
+_29.saved_cells[_5][_6].style.borderColor=_29.saved_cells[_5][_6].colorCode;
+pickCell(this);
+};
+td.onmouseover=function(){
+if(_3){
+pickCell(this);
+}
+};
+td.onmouseout=function(){
+if(_3){
+this.style.borderColor=this.colorCode;
+}
+};
+td.ondblclick=function(){
+Xinha.colorPicker.remember(this.colorCode,_29.savecolors);
+_29.callback(this.colorCode);
+_29.close();
+};
+td.appendChild(document.createTextNode(" "));
+td.style.cursor="pointer";
+tr.appendChild(td);
+td=null;
+}
+this.tbody.appendChild(tr);
+tr=null;
+var tr=document.createElement("tr");
+var td=document.createElement("td");
+tr.appendChild(td);
+td.colSpan=this.side+2;
+td.style.padding="3px";
+if(this.websafe){
+var div=document.createElement("div");
+var _33=document.createElement("label");
+_33.appendChild(document.createTextNode(Xinha._lc("Web Safe: ")));
+this.constrain_cb.onclick=function(){
+_29.pick_color();
+};
+_33.appendChild(this.constrain_cb);
+_33.style.fontFamily="small-caption,caption,sans-serif";
+_33.style.fontSize="x-small";
+div.appendChild(_33);
+td.appendChild(div);
+div=null;
+}
+var div=document.createElement("div");
+var _33=document.createElement("label");
+_33.style.fontFamily="small-caption,caption,sans-serif";
+_33.style.fontSize="x-small";
+_33.appendChild(document.createTextNode(Xinha._lc("Color: ")));
+_33.appendChild(this.chosenColor);
+div.appendChild(_33);
+var but=document.createElement("span");
+but.className="buttonColor ";
+but.style.fontSize="13px";
+but.style.width="24px";
+but.style.marginLeft="2px";
+but.style.padding="0px 4px";
+but.style.cursor="pointer";
+but.onclick=function(){
+Xinha.colorPicker.remember(_29.chosenColor.value,_29.savecolors);
+_29.callback(_29.chosenColor.value);
+_29.close();
+};
+but.appendChild(document.createTextNode("OK"));
+but.align="center";
+div.appendChild(but);
+td.appendChild(div);
+var _35=document.createElement("table");
+_35.style.width="100%";
+var _36=document.createElement("tbody");
+_35.appendChild(_36);
+var _37=document.createElement("tr");
+_36.appendChild(_37);
+var _38=document.createElement("td");
+_37.appendChild(_38);
+_38.appendChild(this.backSample);
+_38.style.width="50%";
+var _39=document.createElement("td");
+_37.appendChild(_39);
+_39.appendChild(this.foreSample);
+_39.style.width="50%";
+td.appendChild(_35);
+var _3a=document.createElement("div");
+_3a.style.clear="both";
+function createSavedColors(_3b){
+var _3c=false;
+var div=document.createElement("div");
+div.style.width=_29.cellsize+"px";
+div.style.height=_29.cellsize+"px";
+div.style.margin="1px";
+div.style.border="1px solid black";
+div.style.cursor="pointer";
+div.style.backgroundColor=_3b;
+div.style[_3c?"styleFloat":"cssFloat"]="left";
+div.ondblclick=function(){
+_29.callback(_3b);
+_29.close();
+};
+div.onclick=function(){
+_29.chosenColor.value=_3b;
+_29.backSample.style.backgroundColor=_3b;
+_29.foreSample.style.color=_3b;
+};
+_3a.appendChild(div);
+}
+for(var _3e=0;_3e<Xinha.colorPicker.savedColors.length;_3e++){
+createSavedColors(Xinha.colorPicker.savedColors[_3e]);
+}
+td.appendChild(_3a);
+this.tbody.appendChild(tr);
+document.body.appendChild(this.table);
+}else{
+for(var row=0;row<this.side;row++){
+for(var col=0;col<this.side;col++){
+if(_2d){
+this.saved_cells[row][col].colorCode=tupleToColor(rgbToWebsafe(hsvToRGB(_2a*row,_2b*col,this.value)));
+}else{
+this.saved_cells[row][col].colorCode=tupleToColor(hsvToRGB(_2a*row,_2b*col,this.value));
+}
+this.saved_cells[row][col].style.backgroundColor=this.saved_cells[row][col].colorCode;
+this.saved_cells[row][col].style.borderColor=this.saved_cells[row][col].colorCode;
+}
+}
+var _3f=this.saved_cells[_5][_6];
+this.chosenColor.value=_3f.colorCode;
+this.backSample.style.backgroundColor=_3f.colorCode;
+this.foreSample.style.color=_3f.colorCode;
+if((_3f.hue>=195&&_3f.saturation>0.5)||(_3f.hue===0&&_3f.saturation===0&&_3f.value<0.5)||(_3f.hue!==0&&_29.value<0.75)){
+_3f.style.borderColor="#fff";
+}else{
+_3f.style.borderColor="#000";
+}
+}
+};
+this.close=function(){
+this.table.style.display="none";
+};
+};
+Xinha.colorPicker.savedColors=[];
+Xinha.colorPicker.remember=function(_40,_41){
+for(var i=Xinha.colorPicker.savedColors.length;i--;){
+if(Xinha.colorPicker.savedColors[i]==_40){
+return false;
+}
+}
+Xinha.colorPicker.savedColors.splice(0,0,_40);
+Xinha.colorPicker.savedColors=Xinha.colorPicker.savedColors.slice(0,_41);
+var _43=new Date();
+_43.setMonth(_43.getMonth()+1);
+document.cookie="XinhaColorPicker="+escape(Xinha.colorPicker.savedColors.join("-"))+";expires="+_43.toGMTString();
+return true;
+};
+Xinha.colorPicker.loadColors=function(){
+var _44=document.cookie.indexOf("XinhaColorPicker");
+if(_44!=-1){
+var _45=(document.cookie.indexOf("=",_44)+1);
+var end=document.cookie.indexOf(";",_44);
+if(end==-1){
+end=document.cookie.length;
+}
+Xinha.colorPicker.savedColors=unescape(document.cookie.substring(_45,end)).split("-");
+}
+};
+Xinha.colorPicker._lc=function(_47){
+return Xinha._lc(_47);
+};
+\r
diff --git a/mailboxes/xinha/modules/CreateLink/link.html b/mailboxes/xinha/modules/CreateLink/link.html
new file mode 100644 (file)
index 0000000..adad400
--- /dev/null
@@ -0,0 +1,136 @@
+<html>
+
+<head>
+  <title>Insert/Modify Link</title>
+  <script type="text/javascript" src="../../popups/popup.js"></script>
+  <link rel="stylesheet" type="text/css" href="../../popups/popup.css" />
+
+  <script type="text/javascript">
+    window.resizeTo(400, 200);
+
+Xinha = window.opener.Xinha;
+
+function i18n(str) {
+  return (Xinha._lc(str, 'Xinha'));
+}
+
+function onTargetChanged() {
+  var f = document.getElementById("f_other_target");
+  if (this.value == "_other") {
+    f.style.visibility = "visible";
+    f.select();
+    f.focus();
+  } else f.style.visibility = "hidden";
+}
+
+function Init() {
+  __dlg_translate('Xinha');
+  __dlg_init();
+
+  // Make sure the translated string appears in the drop down. (for gecko)
+  document.getElementById("f_target").selectedIndex = 1;
+  document.getElementById("f_target").selectedIndex = 0;
+
+  var param = window.dialogArguments;
+  var target_select = document.getElementById("f_target");
+  var use_target = true;
+  if (param) {
+    if ( typeof param["f_usetarget"] != "undefined" ) {
+      use_target = param["f_usetarget"];
+    }
+    if ( typeof param["f_href"] != "undefined" ) {
+      document.getElementById("f_href").value = param["f_href"];
+      document.getElementById("f_title").value = param["f_title"];
+      comboSelectValue(target_select, param["f_target"]);
+      if (target_select.value != param.f_target) {
+        var opt = document.createElement("option");
+        opt.value = param.f_target;
+        opt.innerHTML = opt.value;
+        target_select.appendChild(opt);
+        opt.selected = true;
+      }
+    }
+  }
+  if (! use_target) {
+    document.getElementById("f_target_label").style.visibility = "hidden";
+    document.getElementById("f_target").style.visibility = "hidden";
+    document.getElementById("f_other_target").style.visibility = "hidden";
+  }
+  var opt = document.createElement("option");
+  opt.value = "_other";
+  opt.innerHTML = i18n("Other");
+  target_select.appendChild(opt);
+  target_select.onchange = onTargetChanged;
+  document.getElementById("f_href").focus();
+  document.getElementById("f_href").select();
+}
+
+function onOK() {
+  var required = {
+    // f_href shouldn't be required or otherwise removing the link by entering an empty
+    // url isn't possible anymore.
+    // "f_href": i18n("You must enter the URL where this link points to")
+  };
+  for (var i in required) {
+    var el = document.getElementById(i);
+    if (!el.value) {
+      alert(required[i]);
+      el.focus();
+      return false;
+    }
+  }
+  // pass data back to the calling window
+  var fields = ["f_href", "f_title", "f_target" ];
+  var param = new Object();
+  for (var i in fields) {
+    var id = fields[i];
+    var el = document.getElementById(id);
+    param[id] = el.value;
+  }
+  if (param.f_target == "_other")
+    param.f_target = document.getElementById("f_other_target").value;
+  __dlg_close(param);
+  return false;
+}
+
+function onCancel() {
+  __dlg_close(null);
+  return false;
+}
+
+</script>
+
+</head>
+
+<body class="dialog" onload="Init()">
+<div class="title">Insert/Modify Link</div>
+<form>
+<table border="0" style="width: 100%;">
+  <tr>
+    <td class="label">URL:</td>
+    <td><input type="text" id="f_href" style="width: 100%" /></td>
+  </tr>
+  <tr>
+    <td class="label">Title (tooltip):</td>
+    <td><input type="text" id="f_title" style="width: 100%" /></td>
+  </tr>
+  <tr>
+    <td class="label"><span id="f_target_label">Target:</span></td>
+    <td><select id="f_target">
+      <option value="">None (use implicit)</option>
+      <option value="_blank">New window (_blank)</option>
+      <option value="_self">Same frame (_self)</option>
+      <option value="_top">Top frame (_top)</option>
+    </select>
+    <input type="text" name="f_other_target" id="f_other_target" size="10" style="visibility: hidden" />
+    </td>
+  </tr>
+</table>
+
+<div id="buttons">
+  <button type="submit" name="ok" onclick="return onOK();">OK</button>
+  <button type="button" name="cancel" onclick="return onCancel();">Cancel</button>
+</div>
+</form>
+</body>
+</html>
\ No newline at end of file
diff --git a/mailboxes/xinha/modules/CreateLink/link.js b/mailboxes/xinha/modules/CreateLink/link.js
new file mode 100644 (file)
index 0000000..57d8c1b
--- /dev/null
@@ -0,0 +1,84 @@
+CreateLink._pluginInfo={name:"CreateLink",origin:"Xinha Core",version:"$LastChangedRevision: 694 $".replace(/^[^:]*: (.*) \$$/,"$1"),developer:"The Xinha Core Developer Team",developer_url:"$HeadURL: http://svn.xinha.python-hosting.com/tags/0.92beta/modules/CreateLink/link.js $".replace(/^[^:]*: (.*) \$$/,"$1"),sponsor:"",sponsor_url:"",license:"htmlArea"};
+function CreateLink(_1){
+}
+Xinha.prototype._createLink=function(_2){
+var _3=this;
+var _4=null;
+if(typeof _2=="undefined"){
+_2=this.getParentElement();
+if(_2){
+while(_2&&!/^a$/i.test(_2.tagName)){
+_2=_2.parentNode;
+}
+}
+}
+if(!_2){
+var _5=_3.getSelection();
+var _6=_3.createRange(_5);
+var _7=0;
+if(Xinha.is_ie){
+if(_5.type=="Control"){
+_7=_6.length;
+}else{
+_7=_6.compareEndPoints("StartToEnd",_6);
+}
+}else{
+_7=_6.compareBoundaryPoints(_6.START_TO_END,_6);
+}
+if(_7===0){
+alert(Xinha._lc("You need to select some text before creating a link"));
+return;
+}
+_4={f_href:"",f_title:"",f_target:"",f_usetarget:_3.config.makeLinkShowsTarget};
+}else{
+_4={f_href:Xinha.is_ie?_3.stripBaseURL(_2.href):_2.getAttribute("href"),f_title:_2.title,f_target:_2.target,f_usetarget:_3.config.makeLinkShowsTarget};
+}
+Dialog(_3.config.URIs.link,function(_8){
+if(!_8){
+return false;
+}
+var a=_2;
+if(!a){
+try{
+var _a=Xinha.uniq("http://www.example.com/Link");
+_3._doc.execCommand("createlink",false,_a);
+var _b=_3._doc.getElementsByTagName("a");
+for(var i=0;i<_b.length;i++){
+var _d=_b[i];
+if(_d.href==_a){
+if(!a){
+a=_d;
+}
+_d.href=_8.f_href;
+if(_8.f_target){
+_d.target=_8.f_target;
+}
+if(_8.f_title){
+_d.title=_8.f_title;
+}
+}
+}
+}
+catch(ex){
+}
+}else{
+var _e=_8.f_href.trim();
+_3.selectNodeContents(a);
+if(_e===""){
+_3._doc.execCommand("unlink",false,null);
+_3.updateToolbar();
+return false;
+}else{
+a.href=_e;
+}
+}
+if(!(a&&a.tagName.toLowerCase()=="a")){
+return false;
+}
+a.target=_8.f_target.trim();
+a.title=_8.f_title.trim();
+_3.selectNodeContents(a);
+_3.updateToolbar();
+},_4);
+};
+\r
diff --git a/mailboxes/xinha/modules/Dialogs/dialog.js b/mailboxes/xinha/modules/Dialogs/dialog.js
new file mode 100644 (file)
index 0000000..fcb57d5
--- /dev/null
@@ -0,0 +1,62 @@
+function Dialog(_1,_2,_3){
+if(typeof _3=="undefined"){
+_3=window;
+}
+Dialog._geckoOpenModal(_1,_2,_3);
+}
+Dialog._parentEvent=function(ev){
+setTimeout(function(){
+if(Dialog._modal&&!Dialog._modal.closed){
+Dialog._modal.focus();
+}
+},50);
+try{
+if(Dialog._modal&&!Dialog._modal.closed){
+Xinha._stopEvent(ev);
+}
+}
+catch(e){
+}
+};
+Dialog._return=null;
+Dialog._modal=null;
+Dialog._arguments=null;
+Dialog._geckoOpenModal=function(_5,_6,_7){
+var _8=window.open(_5,"hadialog","toolbar=no,menubar=no,personalbar=no,width=10,height=10,"+"scrollbars=no,resizable=yes,modal=yes,dependable=yes");
+Dialog._modal=_8;
+Dialog._arguments=_7;
+function capwin(w){
+Xinha._addEvent(w,"click",Dialog._parentEvent);
+Xinha._addEvent(w,"mousedown",Dialog._parentEvent);
+Xinha._addEvent(w,"focus",Dialog._parentEvent);
+}
+function relwin(w){
+Xinha._removeEvent(w,"click",Dialog._parentEvent);
+Xinha._removeEvent(w,"mousedown",Dialog._parentEvent);
+Xinha._removeEvent(w,"focus",Dialog._parentEvent);
+}
+capwin(window);
+for(var i=0;i<window.frames.length;i++){
+try{
+capwin(window.frames[i]);
+}
+catch(e){
+}
+}
+Dialog._return=function(_c){
+if(_c&&_6){
+_6(_c);
+}
+relwin(window);
+for(var i=0;i<window.frames.length;i++){
+try{
+relwin(window.frames[i]);
+}
+catch(e){
+}
+}
+Dialog._modal=null;
+};
+Dialog._modal.focus();
+};
+\r
diff --git a/mailboxes/xinha/modules/Dialogs/inline-dialog.js b/mailboxes/xinha/modules/Dialogs/inline-dialog.js
new file mode 100644 (file)
index 0000000..fb9129a
--- /dev/null
@@ -0,0 +1,204 @@
+Xinha.Dialog=function(_1,_2,_3){
+this.id={};
+this.r_id={};
+this.editor=_1;
+this.document=document;
+this.rootElem=document.createElement("div");
+this.rootElem.className="dialog";
+this.rootElem.style.position="absolute";
+this.rootElem.style.display="none";
+this.editor._framework.ed_cell.insertBefore(this.rootElem,this.editor._framework.ed_cell.firstChild);
+this.rootElem.style.width=this.width=this.editor._framework.ed_cell.offsetWidth+"px";
+this.rootElem.style.height=this.height=this.editor._framework.ed_cell.offsetHeight+"px";
+var _4=this;
+if(typeof _3=="function"){
+this._lc=_3;
+}else{
+if(_3){
+this._lc=function(_5){
+return Xinha._lc(_5,_3);
+};
+}else{
+this._lc=function(_6){
+return _6;
+};
+}
+}
+_2=_2.replace(/\[([a-z0-9_]+)\]/ig,function(_7,id){
+if(typeof _4.id[id]=="undefined"){
+_4.id[id]=Xinha.uniq("Dialog");
+_4.r_id[_4.id[id]]=id;
+}
+return _4.id[id];
+}).replace(/<l10n>(.*?)<\/l10n>/ig,function(_9,_a){
+return _4._lc(_a);
+}).replace(/="_\((.*?)\)"/g,function(_b,_c){
+return "=\""+_4._lc(_c)+"\"";
+});
+this.rootElem.innerHTML=_2;
+this.editor.notifyOn("resize",function(e,_e){
+_4.rootElem.style.width=_4.width=_4.editor._framework.ed_cell.offsetWidth+"px";
+_4.rootElem.style.height=_4.height=_4.editor._framework.ed_cell.offsetHeight+"px";
+_4.onresize();
+});
+};
+Xinha.Dialog.prototype.onresize=function(){
+return true;
+};
+Xinha.Dialog.prototype.show=function(_f){
+if(Xinha.is_ie){
+this._lastRange=this.editor._createRange(this.editor._getSelection());
+}
+if(typeof _f!="undefined"){
+this.setValues(_f);
+}
+this._restoreTo=[this.editor._textArea.style.display,this.editor._iframe.style.visibility,this.editor.hidePanels()];
+this.editor._textArea.style.display="none";
+this.editor._iframe.style.visibility="hidden";
+this.rootElem.style.display="";
+};
+Xinha.Dialog.prototype.hide=function(){
+this.rootElem.style.display="none";
+this.editor._textArea.style.display=this._restoreTo[0];
+this.editor._iframe.style.visibility=this._restoreTo[1];
+this.editor.showPanels(this._restoreTo[2]);
+if(Xinha.is_ie){
+this._lastRange.select();
+}
+this.editor.updateToolbar();
+return this.getValues();
+};
+Xinha.Dialog.prototype.toggle=function(){
+if(this.rootElem.style.display=="none"){
+this.show();
+}else{
+this.hide();
+}
+};
+Xinha.Dialog.prototype.setValues=function(_10){
+for(var i in _10){
+var _12=this.getElementsByName(i);
+if(!_12){
+continue;
+}
+for(var x=0;x<_12.length;x++){
+var e=_12[x];
+switch(e.tagName.toLowerCase()){
+case "select":
+for(var j=0;j<e.options.length;j++){
+if(typeof _10[i]=="object"){
+for(var k=0;k<_10[i].length;k++){
+if(_10[i][k]==e.options[j].value){
+e.options[j].selected=true;
+}
+}
+}else{
+if(_10[i]==e.options[j].value){
+e.options[j].selected=true;
+}
+}
+}
+break;
+case "textarea":
+case "input":
+switch(e.getAttribute("type")){
+case "radio":
+if(e.value==_10[i]){
+e.checked=true;
+}
+break;
+case "checkbox":
+if(typeof _10[i]=="object"){
+for(var j in _10[i]){
+if(_10[i][j]==e.value){
+e.checked=true;
+}
+}
+}else{
+if(_10[i]==e.value){
+e.checked=true;
+}
+}
+break;
+default:
+e.value=_10[i];
+}
+break;
+default:
+break;
+}
+}
+}
+};
+Xinha.Dialog.prototype.getValues=function(){
+var _17=[];
+var _18=Xinha.collectionToArray(this.rootElem.getElementsByTagName("input")).append(Xinha.collectionToArray(this.rootElem.getElementsByTagName("textarea"))).append(Xinha.collectionToArray(this.rootElem.getElementsByTagName("select")));
+for(var x=0;x<_18.length;x++){
+var i=_18[x];
+if(!(i.name&&this.r_id[i.name])){
+continue;
+}
+if(typeof _17[this.r_id[i.name]]=="undefined"){
+_17[this.r_id[i.name]]=null;
+}
+var v=_17[this.r_id[i.name]];
+switch(i.tagName.toLowerCase()){
+case "select":
+if(i.multiple){
+if(!v.push){
+if(v!=null){
+v=[v];
+}else{
+v=new Array();
+}
+}
+for(var j=0;j<i.options.length;j++){
+if(i.options[j].selected){
+v.push(i.options[j].value);
+}
+}
+}else{
+if(i.selectedIndex>=0){
+v=i.options[i.selectedIndex];
+}
+}
+break;
+case "textarea":
+case "input":
+default:
+switch(i.type.toLowerCase()){
+case "radio":
+if(i.checked){
+v=i.value;
+break;
+}
+case "checkbox":
+if(v==null){
+if(this.getElementsByName(this.r_id[i.name]).length>1){
+v=new Array();
+}
+}
+if(i.checked){
+if(v!=null&&typeof v=="object"&&v.push){
+v.push(i.value);
+}else{
+v=i.value;
+}
+}
+break;
+default:
+v=i.value;
+break;
+}
+}
+_17[this.r_id[i.name]]=v;
+}
+return _17;
+};
+Xinha.Dialog.prototype.getElementById=function(id){
+return this.document.getElementById(this.id[id]?this.id[id]:id);
+};
+Xinha.Dialog.prototype.getElementsByName=function(_1e){
+return this.document.getElementsByName(this.id[_1e]?this.id[_1e]:_1e);
+};
+\r
diff --git a/mailboxes/xinha/modules/Dialogs/panel-dialog.js b/mailboxes/xinha/modules/Dialogs/panel-dialog.js
new file mode 100644 (file)
index 0000000..321dac0
--- /dev/null
@@ -0,0 +1,76 @@
+
+Xinha.PanelDialog = function(editor, side, html, localizer)
+{
+  this.id    = { };
+  this.r_id  = { }; // reverse lookup id
+  this.editor   = editor;
+  this.document = document;
+  this.rootElem = editor.addPanel(side);
+
+  var dialog = this;
+  if(typeof localizer == 'function')
+  {
+    this._lc = localizer;
+  }
+  else if(localizer)
+  {
+    this._lc = function(string)
+    {
+      return Xinha._lc(string,localizer);
+    };
+  }
+  else
+  {
+    this._lc = function(string)
+    {
+      return string;
+    };
+  }
+
+  html = html.replace(/\[([a-z0-9_]+)\]/ig,
+                      function(fullString, id)
+                      {
+                        if(typeof dialog.id[id] == 'undefined')
+                        {
+                          dialog.id[id] = Xinha.uniq('Dialog');
+                          dialog.r_id[dialog.id[id]] = id;
+                        }
+                        return dialog.id[id];
+                      }
+             ).replace(/<l10n>(.*?)<\/l10n>/ig,
+                       function(fullString,translate)
+                       {
+                         return dialog._lc(translate) ;
+                       }
+             ).replace(/="_\((.*?)\)"/g,
+                       function(fullString, translate)
+                       {
+                         return '="' + dialog._lc(translate) + '"';
+                       }
+             );
+
+  this.rootElem.innerHTML = html;
+};
+
+Xinha.PanelDialog.prototype.show = function(values)
+{
+  this.editor.showPanel(this.rootElem);
+};
+
+Xinha.PanelDialog.prototype.hide = function()
+{
+  this.editor.hidePanel(this.rootElem);
+  return this.getValues();
+};
+
+Xinha.PanelDialog.prototype.onresize   = Xinha.Dialog.prototype.onresize;
+
+Xinha.PanelDialog.prototype.toggle     = Xinha.Dialog.prototype.toggle;
+
+Xinha.PanelDialog.prototype.setValues  = Xinha.Dialog.prototype.setValues;
+
+Xinha.PanelDialog.prototype.getValues  = Xinha.Dialog.prototype.getValues;
+
+Xinha.PanelDialog.prototype.getElementById    = Xinha.Dialog.prototype.getElementById;
+
+Xinha.PanelDialog.prototype.getElementsByName = Xinha.Dialog.prototype.getElementsByName;
\ No newline at end of file
diff --git a/mailboxes/xinha/modules/Dialogs/popupwin.js b/mailboxes/xinha/modules/Dialogs/popupwin.js
new file mode 100644 (file)
index 0000000..6fcffe4
--- /dev/null
@@ -0,0 +1,120 @@
+function PopupWin(_1,_2,_3,_4){
+this.editor=_1;
+this.handler=_3;
+var _5=window.open("","__ha_dialog","toolbar=no,menubar=no,personalbar=no,width=600,height=600,left=20,top=40,scrollbars=no,resizable=yes");
+this.window=_5;
+var _6=_5.document;
+this.doc=_6;
+var _7=this;
+var _8=document.baseURI||document.URL;
+if(_8&&_8.match(/(.*)\/([^\/]+)/)){
+_8=RegExp.$1+"/";
+}
+if(typeof _editor_url!="undefined"&&!(/^\//.test(_editor_url))&&!(/http:\/\//.test(_editor_url))){
+_8+=_editor_url;
+}else{
+_8=_editor_url;
+}
+if(!(/\/$/.test(_8))){
+_8+="/";
+}
+this.baseURL=_8;
+_6.open();
+var _9="<html><head><title>"+_2+"</title>\n";
+_9+="<style type=\"text/css\">@import url("+_editor_url+"Xinha.css);</style>\n";
+if(_editor_skin!=""){
+_9+="<style type=\"text/css\">@import url("+_editor_url+"skins/"+_editor_skin+"/skin.css);</style>\n";
+}
+_9+="</head>\n";
+_9+="<body class=\"dialog popupwin\" id=\"--HA-body\"></body></html>";
+_6.write(_9);
+_6.close();
+function init2(){
+var _a=_6.body;
+if(!_a){
+setTimeout(init2,25);
+return false;
+}
+_5.title=_2;
+_6.documentElement.style.padding="0px";
+_6.documentElement.style.margin="0px";
+var _b=_6.createElement("div");
+_b.className="content";
+_7.content=_b;
+_a.appendChild(_b);
+_7.element=_a;
+_4(_7);
+_5.focus();
+}
+init2();
+}
+PopupWin.prototype.callHandler=function(){
+var _c=["input","textarea","select"];
+var _d={};
+for(var ti=_c.length;--ti>=0;){
+var _f=_c[ti];
+var els=this.content.getElementsByTagName(_f);
+for(var j=0;j<els.length;++j){
+var el=els[j];
+var val=el.value;
+if(el.tagName.toLowerCase()=="input"){
+if(el.type=="checkbox"){
+val=el.checked;
+}
+}
+_d[el.name]=val;
+}
+}
+this.handler(this,_d);
+return false;
+};
+PopupWin.prototype.close=function(){
+this.window.close();
+};
+PopupWin.prototype.addButtons=function(){
+var _14=this;
+var div=this.doc.createElement("div");
+this.content.appendChild(div);
+div.id="buttons";
+div.className="buttons";
+for(var i=0;i<arguments.length;++i){
+var btn=arguments[i];
+var _18=this.doc.createElement("button");
+div.appendChild(_18);
+_18.innerHTML=HTMLArea._lc(btn,"HTMLArea");
+switch(btn.toLowerCase()){
+case "ok":
+HTMLArea.addDom0Event(_18,"click",function(){
+_14.callHandler();
+_14.close();
+return false;
+});
+break;
+case "cancel":
+HTMLArea.addDom0Event(_18,"click",function(){
+_14.close();
+return false;
+});
+break;
+}
+}
+};
+PopupWin.prototype.showAtElement=function(){
+var _19=this;
+setTimeout(function(){
+var w=_19.content.offsetWidth+4;
+var h=_19.content.offsetHeight+4;
+var el=_19.content;
+var s=el.style;
+s.position="absolute";
+s.left=parseInt((w-el.offsetWidth)/2,10)+"px";
+s.top=parseInt((h-el.offsetHeight)/2,10)+"px";
+if(HTMLArea.is_gecko){
+_19.window.innerWidth=w;
+_19.window.innerHeight=h;
+}else{
+_19.window.resizeTo(w+8,h+70);
+}
+},25);
+};
+\r
diff --git a/mailboxes/xinha/modules/FullScreen/full-screen.js b/mailboxes/xinha/modules/FullScreen/full-screen.js
new file mode 100644 (file)
index 0000000..7487aa8
--- /dev/null
@@ -0,0 +1,136 @@
+function FullScreen(_1,_2){
+this.editor=_1;
+_1._superclean_on=false;
+cfg=_1.config;
+cfg.registerButton("fullscreen",this._lc("Maximize/Minimize Editor"),[_editor_url+cfg.imgURL+"ed_buttons_main.gif",8,0],true,function(e,_4,_5){
+e._fullScreen();
+});
+cfg.addToolbarElement("fullscreen","popupeditor",0);
+}
+FullScreen._pluginInfo={name:"FullScreen",version:"1.0",developer:"James Sleeman",developer_url:"http://www.gogo.co.nz/",c_owner:"Gogo Internet Services",license:"htmlArea",sponsor:"Gogo Internet Services",sponsor_url:"http://www.gogo.co.nz/"};
+FullScreen.prototype._lc=function(_6){
+return Xinha._lc(_6,{url:_editor_url+"modules/FullScreen/lang/",context:"FullScreen"});
+};
+Xinha.prototype._fullScreen=function(){
+var e=this;
+function sizeItUp(){
+if(!e._isFullScreen||e._sizing){
+return false;
+}
+e._sizing=true;
+var _8=Xinha.viewportSize();
+var h=_8.y-e.config.fullScreenMargins[0]-e.config.fullScreenMargins[2];
+var w=_8.x-e.config.fullScreenMargins[1]-e.config.fullScreenMargins[3];
+e.sizeEditor(w+"px",h+"px",true,true);
+e._sizing=false;
+if(e._toolbarObjects.fullscreen){
+e._toolbarObjects.fullscreen.swapImage([_editor_url+cfg.imgURL+"ed_buttons_main.gif",9,0]);
+}
+}
+function sizeItDown(){
+if(e._isFullScreen||e._sizing){
+return false;
+}
+e._sizing=true;
+e.initSize();
+e._sizing=false;
+if(e._toolbarObjects.fullscreen){
+e._toolbarObjects.fullscreen.swapImage([_editor_url+cfg.imgURL+"ed_buttons_main.gif",8,0]);
+}
+}
+function resetScroll(){
+if(e._isFullScreen){
+window.scroll(0,0);
+window.setTimeout(resetScroll,150);
+}
+}
+if(typeof this._isFullScreen=="undefined"){
+this._isFullScreen=false;
+if(e.target!=e._iframe){
+Xinha._addEvent(window,"resize",sizeItUp);
+}
+}
+if(Xinha.is_gecko){
+this.deactivateEditor();
+}
+if(this._isFullScreen){
+this._htmlArea.style.position="";
+if(!Xinha.is_ie){
+this._htmlArea.style.border="";
+}
+try{
+if(Xinha.is_ie&&document.compatMode=="CSS1Compat"){
+var _b=document.getElementsByTagName("html");
+}else{
+var _b=document.getElementsByTagName("body");
+}
+_b[0].style.overflow="";
+}
+catch(e){
+}
+this._isFullScreen=false;
+sizeItDown();
+var _c=this._htmlArea;
+while((_c=_c.parentNode)&&_c.style){
+_c.style.position=_c._xinha_fullScreenOldPosition;
+_c._xinha_fullScreenOldPosition=null;
+}
+if(Xinha.ie_version<7){
+var _d=document.getElementsByTagName("select");
+for(var i=0;i<_d.length;++i){
+_d[i].style.visibility="visible";
+}
+}
+window.scroll(this._unScroll.x,this._unScroll.y);
+}else{
+this._unScroll={x:(window.pageXOffset)?(window.pageXOffset):(document.documentElement)?document.documentElement.scrollLeft:document.body.scrollLeft,y:(window.pageYOffset)?(window.pageYOffset):(document.documentElement)?document.documentElement.scrollTop:document.body.scrollTop};
+var _c=this._htmlArea;
+while((_c=_c.parentNode)&&_c.style){
+_c._xinha_fullScreenOldPosition=_c.style.position;
+_c.style.position="static";
+}
+if(Xinha.ie_version<7){
+var _d=document.getElementsByTagName("select");
+var s,currentEditor;
+for(var i=0;i<_d.length;++i){
+s=_d[i];
+currentEditor=false;
+while(s=s.parentNode){
+if(s==this._htmlArea){
+currentEditor=true;
+break;
+}
+}
+if(!currentEditor&&_d[i].style.visibility!="hidden"){
+_d[i].style.visibility="hidden";
+}
+}
+}
+window.scroll(0,0);
+this._htmlArea.style.position="absolute";
+this._htmlArea.style.zIndex=999;
+this._htmlArea.style.left=e.config.fullScreenMargins[3]+"px";
+this._htmlArea.style.top=e.config.fullScreenMargins[0]+"px";
+if(!Xinha.is_ie){
+this._htmlArea.style.border="none";
+}
+this._isFullScreen=true;
+resetScroll();
+try{
+if(Xinha.is_ie&&document.compatMode=="CSS1Compat"){
+var _b=document.getElementsByTagName("html");
+}else{
+var _b=document.getElementsByTagName("body");
+}
+_b[0].style.overflow="hidden";
+}
+catch(e){
+}
+sizeItUp();
+}
+if(Xinha.is_gecko){
+this.activateEditor();
+}
+this.focusEditor();
+};
+\r
diff --git a/mailboxes/xinha/modules/FullScreen/lang/de.js b/mailboxes/xinha/modules/FullScreen/lang/de.js
new file mode 100644 (file)
index 0000000..72ff5a3
--- /dev/null
@@ -0,0 +1,6 @@
+// I18N constants
+// LANG: "de", ENCODING: UTF-8
+// translated: Raimund Meyer xinha@ray-of-light.org
+{
+  "Maximize/Minimize Editor": "Editor maximieren/verkleinern"
+};
diff --git a/mailboxes/xinha/modules/FullScreen/lang/fr.js b/mailboxes/xinha/modules/FullScreen/lang/fr.js
new file mode 100644 (file)
index 0000000..af4b014
--- /dev/null
@@ -0,0 +1,5 @@
+// I18N constants
+// LANG: "fr", ENCODING: UTF-8
+{
+  "Maximize/Minimize Editor": "Agrandir/Réduire l'éditeur"
+};
\ No newline at end of file
diff --git a/mailboxes/xinha/modules/FullScreen/lang/ja.js b/mailboxes/xinha/modules/FullScreen/lang/ja.js
new file mode 100644 (file)
index 0000000..f10c4da
--- /dev/null
@@ -0,0 +1,5 @@
+// I18N constants
+// LANG: "ja", ENCODING: UTF-8
+{
+  "Maximize/Minimize Editor": "エディタの最大化/最小化"
+};
\ No newline at end of file
diff --git a/mailboxes/xinha/modules/FullScreen/lang/nb.js b/mailboxes/xinha/modules/FullScreen/lang/nb.js
new file mode 100644 (file)
index 0000000..aba7c2b
--- /dev/null
@@ -0,0 +1,6 @@
+// I18N constants
+// LANG: "nb", ENCODING: UTF-8
+// translated: Kim Steinhaug, http://www.steinhaug.com/, kim@steinhaug.com
+{
+  "Maximize/Minimize Editor": "Maksimer/Minimer WYSIWYG vindu"
+};
\ No newline at end of file
diff --git a/mailboxes/xinha/modules/FullScreen/lang/pl.js b/mailboxes/xinha/modules/FullScreen/lang/pl.js
new file mode 100644 (file)
index 0000000..0c639bb
--- /dev/null
@@ -0,0 +1,6 @@
+// I18N constants
+// LANG: "pl", ENCODING: UTF-8
+// translated: Krzysztof Kotowicz, koto1sa@o2.pl, http://www.eskot.krakow.pl/portfolio
+{
+  "Maximize/Minimize Editor": "Maksymalizuj/minimalizuj edytor"
+};
diff --git a/mailboxes/xinha/modules/FullScreen/lang/ru.js b/mailboxes/xinha/modules/FullScreen/lang/ru.js
new file mode 100644 (file)
index 0000000..b2248ef
--- /dev/null
@@ -0,0 +1,6 @@
+// I18N constants\r
+// LANG: "ru", ENCODING: UTF-8\r
+// Author: Andrei Blagorazumov, a@fnr.ru\r
+{\r
+  "Maximize/Minimize Editor": "Развернуть/Свернуть редактор"\r
+};
\ No newline at end of file
diff --git a/mailboxes/xinha/modules/FullScreen/lang/sv.js b/mailboxes/xinha/modules/FullScreen/lang/sv.js
new file mode 100644 (file)
index 0000000..c27fee5
--- /dev/null
@@ -0,0 +1,6 @@
+// I18N constants
+// LANG: "sv" (Swedish), ENCODING: UTF-8
+// translated: Erik Dalén, <dalen@jpl.se>
+{
+  "Maximize/Minimize Editor": "Maximera/Minimera WYSIWYG fönster"
+};
diff --git a/mailboxes/xinha/modules/Gecko/Gecko.js b/mailboxes/xinha/modules/Gecko/Gecko.js
new file mode 100644 (file)
index 0000000..1f03ef4
--- /dev/null
@@ -0,0 +1,386 @@
+Gecko._pluginInfo={name:"Gecko",origin:"Xinha Core",version:"$LastChangedRevision: 716 $".replace(/^[^:]*: (.*) \$$/,"$1"),developer:"The Xinha Core Developer Team",developer_url:"$HeadURL: http://svn.xinha.python-hosting.com/tags/0.92beta/modules/Gecko/Gecko.js $".replace(/^[^:]*: (.*) \$$/,"$1"),sponsor:"",sponsor_url:"",license:"htmlArea"};
+function Gecko(_1){
+this.editor=_1;
+_1.Gecko=this;
+}
+Gecko.prototype.onKeyPress=function(ev){
+var _3=this.editor;
+var s=_3.getSelection();
+if(_3.isShortCut(ev)){
+switch(_3.getKey(ev).toLowerCase()){
+case "z":
+if(_3._unLink&&_3._unlinkOnUndo){
+Xinha._stopEvent(ev);
+_3._unLink();
+_3.updateToolbar();
+return true;
+}
+break;
+case "a":
+sel=_3.getSelection();
+sel.removeAllRanges();
+range=_3.createRange();
+range.selectNodeContents(_3._doc.body);
+sel.addRange(range);
+Xinha._stopEvent(ev);
+return true;
+break;
+case "v":
+if(!_3.config.htmlareaPaste){
+return true;
+}
+break;
+}
+}
+switch(_3.getKey(ev)){
+case " ":
+var _5=function(_6,_7){
+var _8=_6.nextSibling;
+if(typeof _7=="string"){
+_7=_3._doc.createElement(_7);
+}
+var a=_6.parentNode.insertBefore(_7,_8);
+Xinha.removeFromParent(_6);
+a.appendChild(_6);
+_8.data=" "+_8.data;
+s.collapse(_8,1);
+_3._unLink=function(){
+var t=a.firstChild;
+a.removeChild(t);
+a.parentNode.insertBefore(t,a);
+Xinha.removeFromParent(a);
+_3._unLink=null;
+_3._unlinkOnUndo=false;
+};
+_3._unlinkOnUndo=true;
+return a;
+};
+if(_3.config.convertUrlsToLinks&&s&&s.isCollapsed&&s.anchorNode.nodeType==3&&s.anchorNode.data.length>3&&s.anchorNode.data.indexOf(".")>=0){
+var _b=s.anchorNode.data.substring(0,s.anchorOffset).search(/\S{4,}$/);
+if(_b==-1){
+break;
+}
+if(_3._getFirstAncestor(s,"a")){
+break;
+}
+var _c=s.anchorNode.data.substring(0,s.anchorOffset).replace(/^.*?(\S*)$/,"$1");
+var _d=_c.match(Xinha.RE_email);
+if(_d){
+var _e=s.anchorNode;
+var _f=_e.splitText(s.anchorOffset);
+var _10=_e.splitText(_b);
+_5(_10,"a").href="mailto:"+_d[0];
+break;
+}
+RE_date=/([0-9]+\.)+/;
+RE_ip=/(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)/;
+var _11=_c.match(Xinha.RE_url);
+if(_11){
+if(RE_date.test(_c)){
+if(!RE_ip.test(_c)){
+break;
+}
+}
+var _12=s.anchorNode;
+var _13=_12.splitText(s.anchorOffset);
+var _14=_12.splitText(_b);
+_5(_14,"a").href=(_11[1]?_11[1]:"http://")+_11[2];
+break;
+}
+}
+break;
+}
+switch(ev.keyCode){
+case 27:
+if(_3._unLink){
+_3._unLink();
+Xinha._stopEvent(ev);
+}
+break;
+break;
+case 8:
+case 46:
+if(!ev.shiftKey&&this.handleBackspace()){
+Xinha._stopEvent(ev);
+}
+default:
+_3._unlinkOnUndo=false;
+if(s.anchorNode&&s.anchorNode.nodeType==3){
+var a=_3._getFirstAncestor(s,"a");
+if(!a){
+break;
+}
+if(!a._updateAnchTimeout){
+if(s.anchorNode.data.match(Xinha.RE_email)&&a.href.match("mailto:"+s.anchorNode.data.trim())){
+var _16=s.anchorNode;
+var _17=function(){
+a.href="mailto:"+_16.data.trim();
+a._updateAnchTimeout=setTimeout(_17,250);
+};
+a._updateAnchTimeout=setTimeout(_17,1000);
+break;
+}
+var m=s.anchorNode.data.match(Xinha.RE_url);
+if(m&&a.href.match(s.anchorNode.data.trim())){
+var _19=s.anchorNode;
+var _1a=function(){
+m=_19.data.match(Xinha.RE_url);
+if(m){
+a.href=(m[1]?m[1]:"http://")+m[2];
+}
+a._updateAnchTimeout=setTimeout(_1a,250);
+};
+a._updateAnchTimeout=setTimeout(_1a,1000);
+}
+}
+}
+break;
+}
+return false;
+};
+Gecko.prototype.handleBackspace=function(){
+var _1b=this.editor;
+setTimeout(function(){
+var sel=_1b.getSelection();
+var _1d=_1b.createRange(sel);
+var SC=_1d.startContainer;
+var SO=_1d.startOffset;
+var EC=_1d.endContainer;
+var EO=_1d.endOffset;
+var _22=SC.nextSibling;
+if(SC.nodeType==3){
+SC=SC.parentNode;
+}
+if(!(/\S/.test(SC.tagName))){
+var p=document.createElement("p");
+while(SC.firstChild){
+p.appendChild(SC.firstChild);
+}
+SC.parentNode.insertBefore(p,SC);
+Xinha.removeFromParent(SC);
+var r=_1d.cloneRange();
+r.setStartBefore(_22);
+r.setEndAfter(_22);
+r.extractContents();
+sel.removeAllRanges();
+sel.addRange(r);
+}
+},10);
+};
+Gecko.prototype.inwardHtml=function(_25){
+_25=_25.replace(/<(\/?)strong(\s|>|\/)/ig,"<$1b$2");
+_25=_25.replace(/<(\/?)em(\s|>|\/)/ig,"<$1i$2");
+_25=_25.replace(/<(\/?)del(\s|>|\/)/ig,"<$1strike$2");
+return _25;
+};
+Gecko.prototype.outwardHtml=function(_26){
+_26=_26.replace(/<script[\s]*src[\s]*=[\s]*['"]chrome:\/\/.*?["']>[\s]*<\/script>/ig,"");
+return _26;
+};
+Gecko.prototype.onExecCommand=function(_27,UI,_29){
+try{
+this.editor._doc.execCommand("useCSS",false,true);
+this.editor._doc.execCommand("styleWithCSS",false,false);
+}
+catch(ex){
+}
+switch(_27){
+case "paste":
+alert(Xinha._lc("The Paste button does not work in Mozilla based web browsers (technical security reasons). Press CTRL-V on your keyboard to paste directly."));
+return true;
+}
+return false;
+};
+Gecko.prototype.onMouseDown=function(ev){
+if(ev.target.tagName.toLowerCase()=="hr"){
+var sel=this.editor.getSelection();
+var _2c=this.editor.createRange(sel);
+_2c.selectNode(ev.target);
+}
+};
+Xinha.prototype.insertNodeAtSelection=function(_2d){
+var sel=this.getSelection();
+var _2f=this.createRange(sel);
+sel.removeAllRanges();
+_2f.deleteContents();
+var _30=_2f.startContainer;
+var pos=_2f.startOffset;
+var _32=_2d;
+switch(_30.nodeType){
+case 3:
+if(_2d.nodeType==3){
+_30.insertData(pos,_2d.data);
+_2f=this.createRange();
+_2f.setEnd(_30,pos+_2d.length);
+_2f.setStart(_30,pos+_2d.length);
+sel.addRange(_2f);
+}else{
+_30=_30.splitText(pos);
+if(_2d.nodeType==11){
+_32=_32.firstChild;
+}
+_30.parentNode.insertBefore(_2d,_30);
+this.selectNodeContents(_32);
+this.updateToolbar();
+}
+break;
+case 1:
+if(_2d.nodeType==11){
+_32=_32.firstChild;
+}
+_30.insertBefore(_2d,_30.childNodes[pos]);
+this.selectNodeContents(_32);
+this.updateToolbar();
+break;
+}
+};
+Xinha.prototype.getParentElement=function(sel){
+if(typeof sel=="undefined"){
+sel=this.getSelection();
+}
+var _34=this.createRange(sel);
+try{
+var p=_34.commonAncestorContainer;
+if(!_34.collapsed&&_34.startContainer==_34.endContainer&&_34.startOffset-_34.endOffset<=1&&_34.startContainer.hasChildNodes()){
+p=_34.startContainer.childNodes[_34.startOffset];
+}
+while(p.nodeType==3){
+p=p.parentNode;
+}
+return p;
+}
+catch(ex){
+return null;
+}
+};
+Xinha.prototype.activeElement=function(sel){
+if((sel===null)||this.selectionEmpty(sel)){
+return null;
+}
+if(!sel.isCollapsed){
+if(sel.anchorNode.childNodes.length>sel.anchorOffset&&sel.anchorNode.childNodes[sel.anchorOffset].nodeType==1){
+return sel.anchorNode.childNodes[sel.anchorOffset];
+}else{
+if(sel.anchorNode.nodeType==1){
+return sel.anchorNode;
+}else{
+return null;
+}
+}
+}
+return null;
+};
+Xinha.prototype.selectionEmpty=function(sel){
+if(!sel){
+return true;
+}
+if(typeof sel.isCollapsed!="undefined"){
+return sel.isCollapsed;
+}
+return true;
+};
+Xinha.prototype.selectNodeContents=function(_38,pos){
+this.focusEditor();
+this.forceRedraw();
+var _3a;
+var _3b=typeof pos=="undefined"?true:false;
+var sel=this.getSelection();
+_3a=this._doc.createRange();
+if(_3b&&_38.tagName&&_38.tagName.toLowerCase().match(/table|img|input|textarea|select/)){
+_3a.selectNode(_38);
+}else{
+_3a.selectNodeContents(_38);
+}
+sel.removeAllRanges();
+sel.addRange(_3a);
+};
+Xinha.prototype.insertHTML=function(_3d){
+var sel=this.getSelection();
+var _3f=this.createRange(sel);
+this.focusEditor();
+var _40=this._doc.createDocumentFragment();
+var div=this._doc.createElement("div");
+div.innerHTML=_3d;
+while(div.firstChild){
+_40.appendChild(div.firstChild);
+}
+var _42=this.insertNodeAtSelection(_40);
+};
+Xinha.prototype.getSelectedHTML=function(){
+var sel=this.getSelection();
+var _44=this.createRange(sel);
+return Xinha.getHTML(_44.cloneContents(),false,this);
+};
+Xinha.prototype.getSelection=function(){
+return this._iframe.contentWindow.getSelection();
+};
+Xinha.prototype.createRange=function(sel){
+this.activateEditor();
+if(typeof sel!="undefined"){
+try{
+return sel.getRangeAt(0);
+}
+catch(ex){
+return this._doc.createRange();
+}
+}else{
+return this._doc.createRange();
+}
+};
+Xinha.prototype.isKeyEvent=function(_46){
+return _46.type=="keypress";
+};
+Xinha.prototype.getKey=function(_47){
+return String.fromCharCode(_47.charCode);
+};
+Xinha.getOuterHTML=function(_48){
+return (new XMLSerializer()).serializeToString(_48);
+};
+Xinha.prototype.cc=String.fromCharCode(173);
+Xinha.prototype.setCC=function(_49){
+if(_49=="textarea"){
+var ta=this._textArea;
+var _4b=ta.selectionStart;
+var _4c=ta.value.substring(0,_4b);
+var _4d=ta.value.substring(_4b,ta.value.length);
+if(_4d.match(/^[^<]*>/)){
+var _4e=_4d.indexOf(">")+1;
+ta.value=_4c+_4d.substring(0,_4e)+this.cc+_4d.substring(_4e,_4d.length);
+}else{
+ta.value=_4c+this.cc+_4d;
+}
+}else{
+var sel=this.getSelection();
+sel.getRangeAt(0).insertNode(document.createTextNode(this.cc));
+}
+};
+Xinha.prototype.findCC=function(_50){
+var _51=(_50=="textarea")?window:this._iframe.contentWindow;
+if(_51.find(this.cc)){
+if(_50=="textarea"){
+var ta=this._textArea;
+var _53=pos=ta.selectionStart;
+var end=ta.selectionEnd;
+var _55=ta.scrollTop;
+ta.value=ta.value.substring(0,_53)+ta.value.substring(end,ta.value.length);
+ta.selectionStart=pos;
+ta.selectionEnd=pos;
+ta.scrollTop=_55;
+ta.focus();
+}else{
+var sel=this.getSelection();
+sel.getRangeAt(0).deleteContents();
+}
+}
+};
+Xinha.prototype._standardToggleBorders=Xinha.prototype._toggleBorders;
+Xinha.prototype._toggleBorders=function(){
+var _57=this._standardToggleBorders();
+var _58=this._doc.getElementsByTagName("TABLE");
+for(var i=0;i<_58.length;i++){
+_58[i].style.display="none";
+_58[i].style.display="table";
+}
+return _57;
+};
+\r
diff --git a/mailboxes/xinha/modules/Gecko/paraHandlerBest.js b/mailboxes/xinha/modules/Gecko/paraHandlerBest.js
new file mode 100644 (file)
index 0000000..317fead
--- /dev/null
@@ -0,0 +1,293 @@
+EnterParagraphs._pluginInfo={name:"EnterParagraphs",version:"1.0",developer:"Adam Wright",developer_url:"http://www.hipikat.org/",sponsor:"The University of Western Australia",sponsor_url:"http://www.uwa.edu.au/",license:"htmlArea"};
+EnterParagraphs.prototype._whiteSpace=/^\s*$/;
+EnterParagraphs.prototype._pExclusions=/^(address|blockquote|body|dd|div|dl|dt|fieldset|form|h1|h2|h3|h4|h5|h6|hr|li|noscript|ol|p|pre|table|ul)$/i;
+EnterParagraphs.prototype._pContainers=/^(body|del|div|fieldset|form|ins|map|noscript|object|td|th)$/i;
+EnterParagraphs.prototype._pBreak=/^(address|pre|blockquote)$/i;
+EnterParagraphs.prototype._permEmpty=/^(area|base|basefont|br|col|frame|hr|img|input|isindex|link|meta|param)$/i;
+EnterParagraphs.prototype._elemSolid=/^(applet|br|button|hr|img|input|table)$/i;
+EnterParagraphs.prototype._pifySibling=/^(address|blockquote|del|div|dl|fieldset|form|h1|h2|h3|h4|h5|h6|hr|ins|map|noscript|object|ol|p|pre|table|ul|)$/i;
+EnterParagraphs.prototype._pifyForced=/^(ul|ol|dl|table)$/i;
+EnterParagraphs.prototype._pifyParent=/^(dd|dt|li|td|th|tr)$/i;
+function EnterParagraphs(_1){
+this.editor=_1;
+if(Xinha.is_gecko){
+this.onKeyPress=this.__onKeyPress;
+}
+}
+EnterParagraphs.prototype.name="EnterParagraphs";
+EnterParagraphs.prototype.insertAdjacentElement=function(_2,_3,el){
+if(_3=="BeforeBegin"){
+_2.parentNode.insertBefore(el,_2);
+}else{
+if(_3=="AfterEnd"){
+_2.nextSibling?_2.parentNode.insertBefore(el,_2.nextSibling):_2.parentNode.appendChild(el);
+}else{
+if(_3=="AfterBegin"&&_2.firstChild){
+_2.insertBefore(el,_2.firstChild);
+}else{
+if(_3=="BeforeEnd"||_3=="AfterBegin"){
+_2.appendChild(el);
+}
+}
+}
+}
+};
+EnterParagraphs.prototype.forEachNodeUnder=function(_5,_6,_7,_8){
+var _9,end;
+if(_5.nodeType==11&&_5.firstChild){
+_9=_5.firstChild;
+end=_5.lastChild;
+}else{
+_9=end=_5;
+}
+while(end.lastChild){
+end=end.lastChild;
+}
+return this.forEachNode(_9,end,_6,_7,_8);
+};
+EnterParagraphs.prototype.forEachNode=function(_a,_b,_c,_d,_e){
+var _f=function(_10,_11){
+return (_11=="ltr"?_10.nextSibling:_10.previousSibling);
+};
+var _12=function(_13,_14){
+return (_14=="ltr"?_13.firstChild:_13.lastChild);
+};
+var _15,lookup,fnReturnVal;
+var _16=_e;
+var _17=false;
+while(_15!=_d=="ltr"?_b:_a){
+if(!_15){
+_15=_d=="ltr"?_a:_b;
+}else{
+if(_12(_15,_d)){
+_15=_12(_15,_d);
+}else{
+if(_f(_15,_d)){
+_15=_f(_15,_d);
+}else{
+lookup=_15;
+while(!_f(lookup,_d)&&lookup!=(_d=="ltr"?_b:_a)){
+lookup=lookup.parentNode;
+}
+_15=(_f(lookup,_d)?_f(lookup,_d):lookup);
+}
+}
+}
+_17=(_15==(_d=="ltr"?_b:_a));
+switch(_c){
+case "cullids":
+fnReturnVal=this._fenCullIds(_15,_16);
+break;
+case "find_fill":
+fnReturnVal=this._fenEmptySet(_15,_16,_c,_17);
+break;
+case "find_cursorpoint":
+fnReturnVal=this._fenEmptySet(_15,_16,_c,_17);
+break;
+}
+if(fnReturnVal[0]){
+return fnReturnVal[1];
+}
+if(_17){
+break;
+}
+if(fnReturnVal[1]){
+_16=fnReturnVal[1];
+}
+}
+return false;
+};
+EnterParagraphs.prototype._fenEmptySet=function(_18,_19,_1a,_1b){
+if(!_19&&!_18.firstChild){
+_19=_18;
+}
+if((_18.nodeType==1&&this._elemSolid.test(_18.nodeName))||(_18.nodeType==3&&!this._whiteSpace.test(_18.nodeValue))||(_18.nodeType!=1&&_18.nodeType!=3)){
+switch(_1a){
+case "find_fill":
+return new Array(true,false);
+break;
+case "find_cursorpoint":
+return new Array(true,_18);
+break;
+}
+}
+if(_1b){
+return new Array(true,_19);
+}
+return new Array(false,_19);
+};
+EnterParagraphs.prototype._fenCullIds=function(_1c,_1d,_1e){
+if(_1d.id){
+_1e[_1d.id]?_1d.id="":_1e[_1d.id]=true;
+}
+return new Array(false,_1e);
+};
+EnterParagraphs.prototype.processSide=function(rng,_20){
+var _21=function(_22,_23){
+return (_23=="left"?_22.previousSibling:_22.nextSibling);
+};
+var _24=_20=="left"?rng.startContainer:rng.endContainer;
+var _25=_20=="left"?rng.startOffset:rng.endOffset;
+var _26,start=_24;
+while(start.nodeType==1&&!this._permEmpty.test(start.nodeName)){
+start=(_25?start.lastChild:start.firstChild);
+}
+while(_26=_26?(_21(_26,_20)?_21(_26,_20):_26.parentNode):start){
+if(_21(_26,_20)){
+if(this._pExclusions.test(_21(_26,_20).nodeName)){
+return this.processRng(rng,_20,_26,_21(_26,_20),(_20=="left"?"AfterEnd":"BeforeBegin"),true,false);
+}
+}else{
+if(this._pContainers.test(_26.parentNode.nodeName)){
+return this.processRng(rng,_20,_26,_26.parentNode,(_20=="left"?"AfterBegin":"BeforeEnd"),true,false);
+}else{
+if(this._pExclusions.test(_26.parentNode.nodeName)){
+if(this._pBreak.test(_26.parentNode.nodeName)){
+return this.processRng(rng,_20,_26,_26.parentNode,(_20=="left"?"AfterBegin":"BeforeEnd"),false,(_20=="left"?true:false));
+}else{
+return this.processRng(rng,_20,(_26=_26.parentNode),(_21(_26,_20)?_21(_26,_20):_26.parentNode),(_21(_26,_20)?(_20=="left"?"AfterEnd":"BeforeBegin"):(_20=="left"?"AfterBegin":"BeforeEnd")),false,false);
+}
+}
+}
+}
+}
+};
+EnterParagraphs.prototype.processRng=function(rng,_28,_29,_2a,_2b,_2c,_2d){
+var _2e=_28=="left"?rng.startContainer:rng.endContainer;
+var _2f=_28=="left"?rng.startOffset:rng.endOffset;
+var _30=this.editor;
+var _31=_30._doc.createRange();
+_31.selectNode(_29);
+if(_28=="left"){
+_31.setEnd(_2e,_2f);
+rng.setStart(_31.startContainer,_31.startOffset);
+}else{
+if(_28=="right"){
+_31.setStart(_2e,_2f);
+rng.setEnd(_31.endContainer,_31.endOffset);
+}
+}
+var cnt=_31.cloneContents();
+this.forEachNodeUnder(cnt,"cullids","ltr",this.takenIds,false,false);
+var _33,pifyOffset,fill;
+_33=_28=="left"?(_31.endContainer.nodeType==3?true:false):(_31.startContainer.nodeType==3?false:true);
+pifyOffset=_33?_31.startOffset:_31.endOffset;
+_33=_33?_31.startContainer:_31.endContainer;
+if(this._pifyParent.test(_33.nodeName)&&_33.parentNode.childNodes.item(0)==_33){
+while(!this._pifySibling.test(_33.nodeName)){
+_33=_33.parentNode;
+}
+}
+if(cnt.nodeType==11&&!cnt.firstChild){
+if(_33.nodeName!="BODY"||(_33.nodeName=="BODY"&&pifyOffset!=0)){
+cnt.appendChild(_30._doc.createElement(_33.nodeName));
+}
+}
+fill=this.forEachNodeUnder(cnt,"find_fill","ltr",false);
+if(fill&&this._pifySibling.test(_33.nodeName)&&((pifyOffset==0)||(pifyOffset==1&&this._pifyForced.test(_33.nodeName)))){
+_29=_30._doc.createElement("p");
+_29.innerHTML="&nbsp;";
+if((_28=="left")&&_33.previousSibling){
+return new Array(_33.previousSibling,"AfterEnd",_29);
+}else{
+if((_28=="right")&&_33.nextSibling){
+return new Array(_33.nextSibling,"BeforeBegin",_29);
+}else{
+return new Array(_33.parentNode,(_28=="left"?"AfterBegin":"BeforeEnd"),_29);
+}
+}
+}
+if(fill){
+if(fill.nodeType==3){
+fill=_30._doc.createDocumentFragment();
+}
+if((fill.nodeType==1&&!this._elemSolid.test())||fill.nodeType==11){
+var _34=_30._doc.createElement("p");
+_34.innerHTML="&nbsp;";
+fill.appendChild(_34);
+}else{
+var _34=_30._doc.createElement("p");
+_34.innerHTML="&nbsp;";
+fill.parentNode.insertBefore(parentNode,fill);
+}
+}
+if(fill){
+_29=fill;
+}else{
+_29=(_2c||(cnt.nodeType==11&&!cnt.firstChild))?_30._doc.createElement("p"):_30._doc.createDocumentFragment();
+_29.appendChild(cnt);
+}
+if(_2d){
+_29.appendChild(_30._doc.createElement("br"));
+}
+return new Array(_2a,_2b,_29);
+};
+EnterParagraphs.prototype.isNormalListItem=function(rng){
+var _36,listNode;
+_36=rng.startContainer;
+if((typeof _36.nodeName!="undefined")&&(_36.nodeName.toLowerCase()=="li")){
+listNode=_36;
+}else{
+if((typeof _36.parentNode!="undefined")&&(typeof _36.parentNode.nodeName!="undefined")&&(_36.parentNode.nodeName.toLowerCase()=="li")){
+listNode=_36.parentNode;
+}else{
+return false;
+}
+}
+if(!listNode.previousSibling){
+if(rng.startOffset==0){
+return false;
+}
+}
+return true;
+};
+EnterParagraphs.prototype.__onKeyPress=function(ev){
+if(ev.keyCode==13&&!ev.shiftKey&&this.editor._iframe.contentWindow.getSelection){
+return this.handleEnter(ev);
+}
+};
+EnterParagraphs.prototype.handleEnter=function(ev){
+var _39;
+var sel=this.editor.getSelection();
+var rng=this.editor.createRange(sel);
+if(this.isNormalListItem(rng)){
+return true;
+}
+this.takenIds=new Object();
+var _3c=this.processSide(rng,"left");
+var _3d=this.processSide(rng,"right");
+_39=_3d[2];
+sel.removeAllRanges();
+rng.deleteContents();
+var _3e=this.forEachNodeUnder(_39,"find_cursorpoint","ltr",false,true);
+if(!_3e){
+alert("INTERNAL ERROR - could not find place to put cursor after ENTER");
+}
+if(_3c){
+this.insertAdjacentElement(_3c[0],_3c[1],_3c[2]);
+}
+if(_3d&&_3d.nodeType!=1){
+this.insertAdjacentElement(_3d[0],_3d[1],_3d[2]);
+}
+if((_3e)&&(this._permEmpty.test(_3e.nodeName))){
+var _3f=0;
+while(_3e.parentNode.childNodes.item(_3f)!=_3e){
+_3f++;
+}
+sel.collapse(_3e.parentNode,_3f);
+}else{
+try{
+sel.collapse(_3e,0);
+if(_3e.nodeType==3){
+_3e=_3e.parentNode;
+}
+this.editor.scrollToElement(_3e);
+}
+catch(e){
+}
+}
+this.editor.updateToolbar();
+Xinha._stopEvent(ev);
+return true;
+};
+\r
diff --git a/mailboxes/xinha/modules/Gecko/paraHandlerDirty.js b/mailboxes/xinha/modules/Gecko/paraHandlerDirty.js
new file mode 100644 (file)
index 0000000..aef5722
--- /dev/null
@@ -0,0 +1,116 @@
+EnterParagraphs._pluginInfo={name:"EnterParagraphs",origin:"Xinha Core",version:"$LastChangedRevision: 688 $".replace(/^[^:]*: (.*) \$$/,"$1"),developer:"The Xinha Core Developer Team",developer_url:"$HeadURL: http://svn.xinha.python-hosting.com/trunk/modules/Gecko/paraHandlerDirty.js $".replace(/^[^:]*: (.*) \$$/,"$1"),sponsor:"",sponsor_url:"",license:"htmlArea"};
+function EnterParagraphs(_1){
+this.editor=_1;
+}
+EnterParagraphs.prototype.onKeyPress=function(ev){
+if(ev.keyCode==13&&!ev.shiftKey){
+this.dom_checkInsertP();
+Xinha._stopEvent(ev);
+}
+};
+EnterParagraphs.prototype.dom_checkInsertP=function(){
+var _3=this.editor;
+var p,body;
+var _5=_3.getSelection();
+var _6=_3.createRange(_5);
+if(!_6.collapsed){
+_6.deleteContents();
+}
+_3.deactivateEditor();
+var SC=_6.startContainer;
+var SO=_6.startOffset;
+var EC=_6.endContainer;
+var EO=_6.endOffset;
+if(SC==EC&&SC==body&&!SO&&!EO){
+p=_3._doc.createTextNode(" ");
+body.insertBefore(p,body.firstChild);
+_6.selectNodeContents(p);
+SC=_6.startContainer;
+SO=_6.startOffset;
+EC=_6.endContainer;
+EO=_6.endOffset;
+}
+p=_3.getAllAncestors();
+var _b=null;
+body=_3._doc.body;
+for(var i=0;i<p.length;++i){
+if(Xinha.isParaContainer(p[i])){
+break;
+}else{
+if(Xinha.isBlockElement(p[i])&&!(/body|html/i.test(p[i].tagName))){
+_b=p[i];
+break;
+}
+}
+}
+if(!_b){
+var _d=_6.startContainer;
+while(_d.parentNode&&!Xinha.isParaContainer(_d.parentNode)){
+_d=_d.parentNode;
+}
+var _e=_d;
+var _f=_d;
+while(_e.previousSibling){
+if(_e.previousSibling.tagName){
+if(!Xinha.isBlockElement(_e.previousSibling)){
+_e=_e.previousSibling;
+}else{
+break;
+}
+}else{
+_e=_e.previousSibling;
+}
+}
+while(_f.nextSibling){
+if(_f.nextSibling.tagName){
+if(!Xinha.isBlockElement(_f.nextSibling)){
+_f=_f.nextSibling;
+}else{
+break;
+}
+}else{
+_f=_f.nextSibling;
+}
+}
+_6.setStartBefore(_e);
+_6.setEndAfter(_f);
+_6.surroundContents(_3._doc.createElement("p"));
+_b=_6.startContainer.firstChild;
+_6.setStart(SC,SO);
+}
+_6.setEndAfter(_b);
+var r2=_6.cloneRange();
+_5.removeRange(_6);
+var df=r2.extractContents();
+if(df.childNodes.length===0){
+df.appendChild(_3._doc.createElement("p"));
+df.firstChild.appendChild(_3._doc.createElement("br"));
+}
+if(df.childNodes.length>1){
+var nb=_3._doc.createElement("p");
+while(df.firstChild){
+var s=df.firstChild;
+df.removeChild(s);
+nb.appendChild(s);
+}
+df.appendChild(nb);
+}
+if(!(/\S/.test(_b.innerHTML))){
+_b.innerHTML="&nbsp;";
+}
+p=df.firstChild;
+if(!(/\S/.test(p.innerHTML))){
+p.innerHTML="<br />";
+}
+if((/^\s*<br\s*\/?>\s*$/.test(p.innerHTML))&&(/^h[1-6]$/i.test(p.tagName))){
+df.appendChild(_3.convertNode(p,"p"));
+df.removeChild(p);
+}
+var _14=_b.parentNode.insertBefore(df.firstChild,_b.nextSibling);
+_3.activateEditor();
+_5=_3.getSelection();
+_5.removeAllRanges();
+_5.collapse(_14,0);
+_3.scrollToElement(_14);
+};
+\r
diff --git a/mailboxes/xinha/modules/GetHtml/DOMwalk.js b/mailboxes/xinha/modules/GetHtml/DOMwalk.js
new file mode 100644 (file)
index 0000000..ee47633
--- /dev/null
@@ -0,0 +1,168 @@
+function GetHtmlImplementation(_1){
+this.editor=_1;
+}
+GetHtmlImplementation._pluginInfo={name:"GetHtmlImplementation DOMwalk",origin:"Xinha Core",version:"$LastChangedRevision: 742 $".replace(/^[^:]*: (.*) \$$/,"$1"),developer:"The Xinha Core Developer Team",developer_url:"$HeadURL: http://svn.xinha.python-hosting.com/tags/0.92beta/modules/GetHtml/DOMwalk.js $".replace(/^[^:]*: (.*) \$$/,"$1"),sponsor:"",sponsor_url:"",license:"htmlArea"};
+Xinha.getHTML=function(_2,_3,_4){
+try{
+return Xinha.getHTMLWrapper(_2,_3,_4);
+}
+catch(ex){
+alert(Xinha._lc("Your Document is not well formed. Check JavaScript console for details."));
+return _4._iframe.contentWindow.document.body.innerHTML;
+}
+};
+Xinha.emptyAttributes=" checked disabled ismap readonly nowrap compact declare selected defer multiple noresize noshade ";
+Xinha.getHTMLWrapper=function(_5,_6,_7,_8){
+var _9="";
+if(!_8){
+_8="";
+}
+switch(_5.nodeType){
+case 10:
+case 6:
+case 12:
+break;
+case 2:
+break;
+case 4:
+_9+=(Xinha.is_ie?("\n"+_8):"")+"<![CDATA["+_5.data+"]]>";
+break;
+case 5:
+_9+="&"+_5.nodeValue+";";
+break;
+case 7:
+_9+=(Xinha.is_ie?("\n"+_8):"")+"<?"+_5.target+" "+_5.data+" ?>";
+break;
+case 1:
+case 11:
+case 9:
+var _a;
+var i;
+var _c=(_5.nodeType==1)?_5.tagName.toLowerCase():"";
+if((_c=="script"||_c=="noscript")&&_7.config.stripScripts){
+break;
+}
+if(_6){
+_6=!(_7.config.htmlRemoveTags&&_7.config.htmlRemoveTags.test(_c));
+}
+if(Xinha.is_ie&&_c=="head"){
+if(_6){
+_9+=(Xinha.is_ie?("\n"+_8):"")+"<head>";
+}
+var _d=RegExp.multiline;
+RegExp.multiline=true;
+var _e=_5.innerHTML.replace(Xinha.RE_tagName,function(_f,p1,p2){
+return p1+p2.toLowerCase();
+});
+RegExp.multiline=_d;
+_9+=_e+"\n";
+if(_6){
+_9+=(Xinha.is_ie?("\n"+_8):"")+"</head>";
+}
+break;
+}else{
+if(_6){
+_a=(!(_5.hasChildNodes()||Xinha.needsClosingTag(_5)));
+_9+=(Xinha.is_ie&&Xinha.isBlockElement(_5)?("\n"+_8):"")+"<"+_5.tagName.toLowerCase();
+var _12=_5.attributes;
+for(i=0;i<_12.length;++i){
+var a=_12.item(i);
+if(typeof a.nodeValue=="object"){
+continue;
+}
+if(_5.tagName.toLowerCase()=="input"&&_5.type.toLowerCase()=="checkbox"&&a.nodeName.toLowerCase()=="value"&&a.nodeValue.toLowerCase()=="on"){
+continue;
+}
+if(!a.specified&&!(_5.tagName.toLowerCase().match(/input|option/)&&a.nodeName=="value")&&!(_5.tagName.toLowerCase().match(/area/)&&a.nodeName.match(/shape|coords/i))){
+continue;
+}
+var _14=a.nodeName.toLowerCase();
+if(/_moz_editor_bogus_node/.test(_14)){
+_9="";
+break;
+}
+if(/(_moz)|(contenteditable)|(_msh)/.test(_14)){
+continue;
+}
+var _15;
+if(Xinha.emptyAttributes.indexOf(" "+_14+" ")!=-1){
+_15=_14;
+}else{
+if(_14!="style"){
+if(typeof _5[a.nodeName]!="undefined"&&_14!="href"&&_14!="src"&&!(/^on/.test(_14))){
+_15=_5[a.nodeName];
+}else{
+_15=a.nodeValue;
+if(Xinha.is_ie&&(_14=="href"||_14=="src")){
+_15=_7.stripBaseURL(_15);
+}
+if(_7.config.only7BitPrintablesInURLs&&(_14=="href"||_14=="src")){
+_15=_15.replace(/([^!-~]+)/g,function(_16){
+return escape(_16);
+});
+}
+}
+}else{
+if(!Xinha.is_ie){
+_15=_5.style.cssText.replace(/rgb\(.*?\)/ig,function(rgb){
+return Xinha._colorToRgb(rgb);
+});
+}
+}
+}
+if(/^(_moz)?$/.test(_15)){
+continue;
+}
+_9+=" "+_14+"=\""+Xinha.htmlEncode(_15)+"\"";
+}
+if(Xinha.is_ie&&_5.style.cssText){
+_9+=" style=\""+_5.style.cssText.toLowerCase()+"\"";
+}
+if(Xinha.is_ie&&_5.tagName.toLowerCase()=="option"&&_5.selected){
+_9+=" selected=\"selected\"";
+}
+if(_9!==""){
+if(_a&&_c=="p"){
+_9+=">&nbsp;</p>";
+}else{
+if(_a){
+_9+=" />";
+}else{
+_9+=">";
+}
+}
+}
+}
+}
+var _18=false;
+if(_c=="script"||_c=="noscript"){
+if(!_7.config.stripScripts){
+if(Xinha.is_ie){
+var _19="\n"+_5.innerHTML.replace(/^[\n\r]*/,"").replace(/\s+$/,"")+"\n"+_8;
+}else{
+var _19=(_5.hasChildNodes())?_5.firstChild.nodeValue:"";
+}
+_9+=_19+"</"+_c+">"+((Xinha.is_ie)?"\n":"");
+}
+}else{
+for(i=_5.firstChild;i;i=i.nextSibling){
+if(!_18&&i.nodeType==1&&Xinha.isBlockElement(i)){
+_18=true;
+}
+_9+=Xinha.getHTMLWrapper(i,true,_7,_8+"  ");
+}
+if(_6&&!_a){
+_9+=(Xinha.is_ie&&Xinha.isBlockElement(_5)&&_18?("\n"+_8):"")+"</"+_5.tagName.toLowerCase()+">";
+}
+}
+break;
+case 3:
+_9=/^script|noscript|style$/i.test(_5.parentNode.tagName)?_5.data:Xinha.htmlEncode(_5.data);
+break;
+case 8:
+_9="<!--"+_5.data+"-->";
+break;
+}
+return _9;
+};
+\r
diff --git a/mailboxes/xinha/modules/GetHtml/TransformInnerHTML.js b/mailboxes/xinha/modules/GetHtml/TransformInnerHTML.js
new file mode 100644 (file)
index 0000000..4043edb
--- /dev/null
@@ -0,0 +1,143 @@
+function GetHtmlImplementation(_1){
+this.editor=_1;
+}
+GetHtmlImplementation._pluginInfo={name:"GetHtmlImplementation TransformInnerHTML",version:"1.0",developer:"Nelson Bright",developer_url:"http://www.brightworkweb.com/",sponsor:"",sponsor_url:"",license:"htmlArea"};
+HTMLArea.RegExpCache=[new RegExp().compile(/<\s*\/?([^\s\/>]+)[\s*\/>]/gi),new RegExp().compile(/(\s+)_moz[^=>]*=[^\s>]*/gi),new RegExp().compile(/\s*=\s*(([^'"][^>\s]*)([>\s])|"([^"]+)"|'([^']+)')/g),new RegExp().compile(/\/>/g),new RegExp().compile(/<(br|hr|img|input|link|meta|param|embed|area)((\s*\S*="[^"]*")*)>/g),new RegExp().compile(/(checked|compact|declare|defer|disabled|ismap|multiple|no(href|resize|shade|wrap)|readonly|selected)([\s>])/gi),new RegExp().compile(/(="[^']*)'([^'"]*")/),new RegExp().compile(/&(?=[^<]*>)/g),new RegExp().compile(/<\s+/g),new RegExp().compile(/\s+(\/)?>/g),new RegExp().compile(/\s{2,}/g),new RegExp().compile(/\s+([^=\s]+)((="[^"]+")|([\s>]))/g),new RegExp().compile(/\s+contenteditable(=[^>\s\/]*)?/gi),new RegExp().compile(/((href|src)=")([^\s]*)"/g),new RegExp().compile(/<\/?(div|p|h[1-6]|table|tr|td|th|ul|ol|li|blockquote|object|br|hr|img|embed|param|pre|script|html|head|body|meta|link|title|area|input|form|textarea|select|option)[^>]*>/g),new RegExp().compile(/<\/(div|p|h[1-6]|table|tr|ul|ol|blockquote|object|html|head|body|script|form|select)( [^>]*)?>/g),new RegExp().compile(/<(div|p|h[1-6]|table|tr|ul|ol|blockquote|object|html|head|body|script|form|select)( [^>]*)?>/g),new RegExp().compile(/<(td|th|li|option|br|hr|embed|param|pre|meta|link|title|area|input|textarea)[^>]*>/g),new RegExp().compile(/(^|<\/(pre|script)>)(\s|[^\s])*?(<(pre|script)[^>]*>|$)/g),new RegExp().compile(/(<pre[^>]*>)([\s\S])*?(<\/pre>)/g),new RegExp().compile(/(^|<!--[\s\S]*?-->)([\s\S]*?)(?=<!--[\s\S]*?-->|$)/g),new RegExp().compile(/\S*=""/g),new RegExp().compile(/<!--[\s\S]*?-->|<\?[\s\S]*?\?>|<\/?\w[^>]*>/g),new RegExp().compile(/(^|<\/script>)[\s\S]*?(<script[^>]*>|$)/g)];
+HTMLArea.prototype.cleanHTML=function(_2){
+var c=HTMLArea.RegExpCache;
+_2=_2.replace(c[0],function(_4){
+return _4.toLowerCase();
+}).replace(c[1]," ").replace(c[12]," ").replace(c[2],"=\"$2$4$5\"$3").replace(c[21]," ").replace(c[11],function(_5,p1,p2){
+return " "+p1.toLowerCase()+p2;
+}).replace(c[3],">").replace(c[9],"$1>").replace(c[5],"$1=\"$1\"$3").replace(c[4],"<$1$2 />").replace(c[6],"$1$2").replace(c[8],"<").replace(c[10]," ");
+if(HTMLArea.is_ie&&c[13].test(_2)){
+_2=_2.replace(c[13],"$1"+this.stripBaseURL(RegExp.$3)+"\"");
+}
+if(this.config.only7BitPrintablesInURLs){
+if(HTMLArea.is_ie){
+c[13].test(_2);
+}
+if(c[13].test(_2)){
+try{
+_2=_2.replace(c[13],"$1"+decodeURIComponent(RegExp.$3).replace(/([^!-~]+)/g,function(_8){
+return escape(_8);
+})+"\"");
+}
+catch(e){
+_2=_2.replace(c[13],"$1"+RegExp.$3.replace(/([^!-~]+)/g,function(_9){
+return escape(_9);
+})+"\"");
+}
+}
+}
+return _2;
+};
+HTMLArea.indent=function(s,_b){
+HTMLArea.__nindent=0;
+HTMLArea.__sindent="";
+HTMLArea.__sindentChar=(typeof _b=="undefined")?"  ":_b;
+var c=HTMLArea.RegExpCache;
+if(HTMLArea.is_gecko){
+s=s.replace(c[19],function(_d){
+return _d.replace(/<br \/>/g,"\n");
+});
+}
+s=s.replace(c[18],function(_e){
+_e=_e.replace(c[20],function(st,$1,$2){
+string=$2.replace(/[\n\r]/gi," ").replace(/\s+/gi," ").replace(c[14],function(str){
+if(str.match(c[16])){
+var s="\n"+HTMLArea.__sindent+str;
+HTMLArea.__sindent+=HTMLArea.__sindentChar;
+++HTMLArea.__nindent;
+return s;
+}else{
+if(str.match(c[15])){
+--HTMLArea.__nindent;
+HTMLArea.__sindent="";
+for(var i=HTMLArea.__nindent;i>0;--i){
+HTMLArea.__sindent+=HTMLArea.__sindentChar;
+}
+return "\n"+HTMLArea.__sindent+str;
+}else{
+if(str.match(c[17])){
+return "\n"+HTMLArea.__sindent+str;
+}
+}
+}
+return str;
+});
+return $1+string;
+});
+return _e;
+});
+s=s.replace(/^\s*/,"").replace(/ +\n/g,"\n").replace(/[\r\n]+<\/script>/g,"\n</script>");
+return s;
+};
+HTMLArea.getHTML=function(_15,_16,_17){
+var _18="";
+var c=HTMLArea.RegExpCache;
+if(_15.nodeType==11){
+var div=document.createElement("div");
+var _1b=_15.insertBefore(div,_15.firstChild);
+for(j=_1b.nextSibling;j;j=j.nextSibling){
+_1b.appendChild(j.cloneNode(true));
+}
+_18+=_1b.innerHTML.replace(c[23],function(_1c){
+_1c=_1c.replace(c[22],function(tag){
+if(/^<[!\?]/.test(tag)){
+return tag;
+}else{
+return _17.cleanHTML(tag);
+}
+});
+return _1c;
+});
+}else{
+var _1e=(_15.nodeType==1)?_15.tagName.toLowerCase():"";
+if(_16){
+_18+="<"+_1e;
+var _1f=_15.attributes;
+for(i=0;i<_1f.length;++i){
+var a=_1f.item(i);
+if(!a.specified){
+continue;
+}
+var _21=a.nodeName.toLowerCase();
+var _22=a.nodeValue;
+_18+=" "+_21+"=\""+_22+"\"";
+}
+_18+=">";
+}
+if(_1e=="html"){
+innerhtml=_17._doc.documentElement.innerHTML;
+}else{
+innerhtml=_15.innerHTML;
+}
+_18+=innerhtml.replace(c[23],function(_23){
+_23=_23.replace(c[22],function(tag){
+if(/^<[!\?]/.test(tag)){
+return tag;
+}else{
+if(!(_17.config.htmlRemoveTags&&_17.config.htmlRemoveTags.test(tag.replace(/<([^\s>\/]+)/,"$1")))){
+return _17.cleanHTML(tag);
+}else{
+return "";
+}
+}
+});
+return _23;
+});
+if(HTMLArea.is_ie){
+_18=_18.replace(/<li( [^>]*)?>/g,"</li><li$1>").replace(/(<(ul|ol)[^>]*>)[\s\n]*<\/li>/g,"$1").replace(/<\/li>([\s\n]*<\/li>)+/g,"</li>");
+}
+if(HTMLArea.is_gecko){
+_18=_18.replace(/<br \/>\n$/,"");
+}
+if(_16){
+_18+="</"+_1e+">";
+}
+_18=HTMLArea.indent(_18);
+}
+return _18;
+};
+\r
diff --git a/mailboxes/xinha/modules/InsertImage/insert_image.html b/mailboxes/xinha/modules/InsertImage/insert_image.html
new file mode 100644 (file)
index 0000000..f4f81af
--- /dev/null
@@ -0,0 +1,174 @@
+<!DOCTYPE html\r
+     PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"\r
+    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">\r
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">\r
+  <title>Insert Image</title>\r
+\r
+<script type="text/javascript" src="../../popups/popup.js"></script>\r
+<link rel="stylesheet" type="text/css" href="../../popups/popup.css" />\r
+\r
+<script type="text/javascript">\r
+\r
+\r
+Xinha = window.opener.Xinha;\r
+function i18n(str) {\r
+  return (Xinha._lc(str, 'Xinha'));\r
+}\r
+\r
+function Init() {\r
+  __dlg_translate('Xinha');\r
+  __dlg_init(null,{width:410,height:400});\r
+  // Make sure the translated string appears in the drop down. (for gecko)\r
+  document.getElementById("f_align").selectedIndex = 1;\r
+  document.getElementById("f_align").selectedIndex = 5;\r
+  var param = window.dialogArguments;\r
+  if (param["f_base"]) {\r
+      document.getElementById("f_base").value = param["f_base"];\r
+  }\r
+  document.getElementById("f_url").value    = param["f_url"] ? param["f_url"] : "";\r
+  document.getElementById("f_alt").value    = param["f_alt"] ? param["f_alt"] : "";\r
+  document.getElementById("f_border").value = (typeof param["f_border"]!="undefined") ? param["f_border"] : "";\r
+  document.getElementById("f_align").value  = param["f_align"] ? param["f_align"] : "";\r
+  document.getElementById("f_vert").value   = (typeof param["f_vert"]!="undefined") ? param["f_vert"] : "";\r
+  document.getElementById("f_horiz").value  = (typeof param["f_horiz"]!="undefined") ? param["f_horiz"] : "";\r
+  if (param["f_url"]) {\r
+      window.ipreview.location.replace(Xinha._resolveRelativeUrl(param.f_base, param.f_url));\r
+  }\r
+  document.getElementById("f_url").focus();\r
+}\r
+\r
+function onOK() {\r
+  var required = {\r
+    "f_url": i18n("You must enter the URL")\r
+  };\r
+  for (var i in required) {\r
+    var el = document.getElementById(i);\r
+    if (!el.value) {\r
+      alert(required[i]);\r
+      el.focus();\r
+      return false;\r
+    }\r
+  }\r
+  // pass data back to the calling window\r
+  var fields = ["f_url", "f_alt", "f_align", "f_border",\r
+                "f_horiz", "f_vert"];\r
+  var param = new Object();\r
+  for (var i in fields) {\r
+    var id = fields[i];\r
+    var el = document.getElementById(id);\r
+    param[id] = el.value;\r
+  }\r
+  __dlg_close(param);\r
+  return false;\r
+}\r
+\r
+function onCancel() {\r
+  __dlg_close(null);\r
+  return false;\r
+}\r
+\r
+function onPreview() {\r
+  var f_url = document.getElementById("f_url");\r
+  var url = f_url.value;\r
+  var base = document.getElementById("f_base").value;\r
+  if (!url) {\r
+    alert(i18n("You must enter the URL"));\r
+    f_url.focus();\r
+    return false;\r
+  }\r
+  window.ipreview.location.replace(Xinha._resolveRelativeUrl(base, url));\r
+  return false;\r
+}\r
+</script>\r
+\r
+</head>\r
+\r
+<body class="dialog" onload="Init()">\r
+\r
+<div class="title">Insert Image</div>\r
+<!--- new stuff --->\r
+<form action="" method="get">\r
+<input type="hidden" name="base" id="f_base"/>\r
+<table border="0" width="100%" style="padding: 0px; margin: 0px">\r
+  <tbody>\r
+\r
+  <tr>\r
+    <td style="width: 7em; text-align: right">Image URL:</td>\r
+    <td><input type="text" name="url" id="f_url" style="width:75%"\r
+      title="Enter the image URL here" />\r
+      <button name="preview" onclick="return onPreview();"\r
+      title="Preview the image in a new window">Preview</button>\r
+    </td>\r
+  </tr>\r
+  <tr>\r
+    <td style="width: 7em; text-align: right">Alternate text:</td>\r
+    <td><input type="text" name="alt" id="f_alt" style="width:100%"\r
+      title="For browsers that don't support images" /></td>\r
+  </tr>\r
+\r
+  </tbody>\r
+</table>\r
+\r
+<br />\r
+\r
+<fieldset style="float: left; margin-left: 5px;">\r
+<legend>Layout</legend>\r
+\r
+<div class="space"></div>\r
+\r
+<div class="fl">Alignment:</div>\r
+<select size="1" name="align" id="f_align"\r
+  title="Positioning of this image">\r
+  <option value=""                             >Not set</option>\r
+  <option value="left"                         >Left</option>\r
+  <option value="right"                        >Right</option>\r
+  <option value="texttop"                      >Texttop</option>\r
+  <option value="absmiddle"                    >Absmiddle</option>\r
+  <option value="baseline" selected="1"        >Baseline</option>\r
+  <option value="absbottom"                    >Absbottom</option>\r
+  <option value="bottom"                       >Bottom</option>\r
+  <option value="middle"                       >Middle</option>\r
+  <option value="top"                          >Top</option>\r
+</select>\r
+\r
+<br />\r
+\r
+<div class="fl">Border thickness:</div>\r
+<input type="text" name="border" id="f_border" size="5"\r
+title="Leave empty for no border" />\r
+\r
+<div class="space"></div>\r
+\r
+</fieldset>\r
+\r
+<fieldset>\r
+<legend>Spacing</legend>\r
+\r
+<div class="space"></div>\r
+\r
+<div class="fr">Horizontal:</div>\r
+<input type="text" name="horiz" id="f_horiz" size="5"\r
+title="Horizontal padding" />\r
+\r
+<br />\r
+\r
+<div class="fr">Vertical:</div>\r
+<input type="text" name="vert" id="f_vert" size="5"\r
+title="Vertical padding" />\r
+\r
+<div class="space"></div>\r
+\r
+</fieldset>\r
+<br style="clear:all"/>\r
+<div>\r
+Image Preview:<br />\r
+    <iframe name="ipreview" id="ipreview" frameborder="0" style="border : 1px solid gray;" \r
+       height="200" width="100%" src="../../popups/blank.html"></iframe>\r
+</div>\r
+<div id="buttons">\r
+<button type="button" name="ok" onclick="return onOK();">OK</button>\r
+<button type="button" name="cancel" onclick="return onCancel();">Cancel</button>\r
+</div>\r
+</form>\r
+</body>\r
+</html>
\ No newline at end of file
diff --git a/mailboxes/xinha/modules/InsertImage/insert_image.js b/mailboxes/xinha/modules/InsertImage/insert_image.js
new file mode 100644 (file)
index 0000000..2af77df
--- /dev/null
@@ -0,0 +1,107 @@
+InsertImage._pluginInfo={name:"InsertImage",origin:"Xinha Core",version:"$LastChangedRevision: 733 $".replace(/^[^:]*: (.*) \$$/,"$1"),developer:"The Xinha Core Developer Team",developer_url:"$HeadURL: http://svn.xinha.python-hosting.com/tags/0.92beta/modules/InsertImage/insert_image.js $".replace(/^[^:]*: (.*) \$$/,"$1"),sponsor:"",sponsor_url:"",license:"htmlArea"};
+function InsertImage(_1){
+}
+Xinha.prototype._insertImage=function(_2){
+var _3=this;
+var _4;
+if(typeof _2=="undefined"){
+_2=this.getParentElement();
+if(_2&&_2.tagName.toLowerCase()!="img"){
+_2=null;
+}
+}
+var _5;
+if(typeof _3.config.baseHref!="undefined"&&_3.config.baseHref!==null){
+_5=_3.config.baseHref;
+}else{
+var _6=window.location.toString().split("/");
+_6.pop();
+_5=_6.join("/");
+}
+if(_2){
+_4={f_base:_5,f_url:Xinha.is_ie?_3.stripBaseURL(_2.src):_2.getAttribute("src"),f_alt:_2.alt,f_border:_2.border,f_align:_2.align,f_vert:(_2.vspace!=-1?_2.vspace:""),f_horiz:(_2.hspace!=-1?_2.hspace:""),f_width:_2.width,f_height:_2.height};
+}else{
+_4={f_base:_5,f_url:""};
+}
+Dialog(_3.config.URIs.insert_image,function(_7){
+if(!_7){
+return false;
+}
+var _8=_2;
+if(!_8){
+if(Xinha.is_ie){
+var _9=_3.getSelection();
+var _a=_3.createRange(_9);
+_3._doc.execCommand("insertimage",false,_7.f_url);
+_8=_a.parentElement();
+if(_8.tagName.toLowerCase()!="img"){
+_8=_8.previousSibling;
+}
+}else{
+_8=document.createElement("img");
+_8.src=_7.f_url;
+_3.insertNodeAtSelection(_8);
+if(!_8.tagName){
+_8=_a.startContainer.firstChild;
+}
+}
+}else{
+_8.src=_7.f_url;
+}
+for(var _b in _7){
+var _c=_7[_b];
+switch(_b){
+case "f_alt":
+if(_c){
+_8.alt=_c;
+}else{
+_8.removeAttribute("alt");
+}
+break;
+case "f_border":
+if(_c){
+_8.border=parseInt(_c||"0");
+}else{
+_8.removeAttribute("border");
+}
+break;
+case "f_align":
+if(_c){
+_8.align=_c;
+}else{
+_8.removeAttribute("align");
+}
+break;
+case "f_vert":
+if(_c){
+_8.vspace=parseInt(_c||"0");
+}else{
+_8.removeAttribute("vspace");
+}
+break;
+case "f_horiz":
+if(_c){
+_8.hspace=parseInt(_c||"0");
+}else{
+_8.removeAttribute("hspace");
+}
+break;
+case "f_width":
+if(_c){
+_8.width=parseInt(_c||"0");
+}else{
+_8.removeAttribute("width");
+}
+break;
+case "f_height":
+if(_c){
+_8.height=parseInt(_c||"0");
+}else{
+_8.removeAttribute("height");
+}
+break;
+}
+}
+},_4);
+};
+\r
diff --git a/mailboxes/xinha/modules/InsertTable/insert_table.html b/mailboxes/xinha/modules/InsertTable/insert_table.html
new file mode 100644 (file)
index 0000000..351a0a2
--- /dev/null
@@ -0,0 +1,157 @@
+<html>\r
+\r
+<head>\r
+  <title>Insert Table</title>\r
+\r
+<script type="text/javascript" src="../../popups/popup.js"></script>\r
+<link rel="stylesheet" type="text/css" href="../../popups/popup.css" />\r
+\r
+<script type="text/javascript">\r
+\r
+window.resizeTo(425, 250);\r
+\r
+Xinha = window.opener.Xinha;\r
+function i18n(str) {\r
+  return (Xinha._lc(str, 'Xinha'));\r
+}\r
+\r
+function Init() {\r
+  Xinha = window.opener.Xinha; // load the Xinha plugin and lang file\r
+  __dlg_translate('Xinha');\r
+  __dlg_init(null, Xinha.is_ie ? null : {width:425,height:250});\r
+  // Make sure the translated string appears in the drop down. (for gecko)\r
+  document.getElementById("f_unit").selectedIndex = 1;\r
+  document.getElementById("f_unit").selectedIndex = 0;\r
+  document.getElementById("f_align").selectedIndex = 1;\r
+  document.getElementById("f_align").selectedIndex = 0;\r
+  document.getElementById("f_rows").focus();\r
+}\r
+\r
+function onOK() {\r
+  var required = {\r
+    "f_rows": i18n("You must enter a number of rows"),\r
+    "f_cols": i18n("You must enter a number of columns")\r
+  };\r
+  for (var i in required) {\r
+    var el = document.getElementById(i);\r
+    if (!el.value) {\r
+      alert(required[i]);\r
+      el.focus();\r
+      return false;\r
+    }\r
+  }\r
+  var fields = ["f_rows", "f_cols", "f_width", "f_unit", "f_fixed",\r
+                "f_align", "f_border", "f_spacing", "f_padding"];\r
+  var param = new Object();\r
+  for (var i in fields) {\r
+    var id = fields[i];\r
+    var el = document.getElementById(id);\r
+    param[id] = (el.type == "checkbox") ? el.checked : el.value;\r
+  }\r
+  __dlg_close(param);\r
+  return false;\r
+}\r
+\r
+function onCancel() {\r
+  __dlg_close(null);\r
+  return false;\r
+}\r
+\r
+</script>\r
+\r
+</head>\r
+\r
+<body class="dialog" onload="Init()">\r
+\r
+<div class="title">Insert Table</div>\r
+\r
+<form action="" method="get">\r
+<table border="0" style="padding: 0px; margin: 0px">\r
+  <tbody>\r
+\r
+  <tr>\r
+    <td style="width: 4em; text-align: right">Rows:</td>\r
+    <td><input type="text" name="rows" id="f_rows" size="5" title="Number of rows" value="2" /></td>\r
+    <td style="width: 4em; text-align: right">Width:</td>\r
+    <td><input type="text" name="width" id="f_width" size="5" title="Width of the table" value="100" /></td>\r
+    <td><select size="1" name="unit" id="f_unit" title="Width unit">\r
+      <option value="%" selected="1"  >Percent</option>\r
+      <option value="px"              >Pixels</option>\r
+      <option value="em"              >Em</option>\r
+    </select></td>\r
+  </tr>\r
+  <tr>\r
+    <td style="width: 4em; text-align: right">Cols:</td>\r
+    <td><input type="text" name="cols" id="f_cols" size="5" title="Number of columns" value="4" /></td>\r
+    <td style="text-align: right"><input type="checkbox" checked="checked" name="fixed" id="f_fixed" /></td>\r
+    <td colspan="2"><label for="f_fixed"\r
+    >Fixed width columns</label></td>\r
+  </tr>\r
+  </tbody>\r
+</table>\r
+\r
+<p />\r
+\r
+<fieldset style="float: left; margin-left: 5px;">\r
+<legend>Layout</legend>\r
+\r
+<div class="space"></div>\r
+\r
+<div class="fl">Alignment:</div>\r
+<select size="1" name="align" id="f_align"\r
+  title="Positioning of this table">\r
+  <option value="" selected="1"                >Not set</option>\r
+  <option value="left"                         >Left</option>\r
+  <option value="right"                        >Right</option>\r
+  <option value="texttop"                      >Texttop</option>\r
+  <option value="absmiddle"                    >Absmiddle</option>\r
+  <option value="baseline"                     >Baseline</option>\r
+  <option value="absbottom"                    >Absbottom</option>\r
+  <option value="bottom"                       >Bottom</option>\r
+  <option value="middle"                       >Middle</option>\r
+  <option value="top"                          >Top</option>\r
+</select>\r
+\r
+<p />\r
+\r
+<div class="fl">Border thickness:</div>\r
+<input type="text" name="border" id="f_border" size="5" value="1"\r
+title="Leave empty for no border" />\r
+<!--\r
+<p />\r
+\r
+<div class="fl">Collapse borders:</div>\r
+<input type="checkbox" name="collapse" id="f_collapse" />\r
+-->\r
+<div class="space"></div>\r
+\r
+</fieldset>\r
+\r
+<fieldset style="float:right; margin-right: 5px;">\r
+<legend>Spacing</legend>\r
+\r
+<div class="space"></div>\r
+\r
+<div class="fr">Cell spacing:</div>\r
+<input type="text" name="spacing" id="f_spacing" size="5" value="1"\r
+title="Space between adjacent cells" />\r
+\r
+<p />\r
+\r
+<div class="fr">Cell padding:</div>\r
+<input type="text" name="padding" id="f_padding" size="5" value="1"\r
+title="Space between content and border in cell" />\r
+\r
+<div class="space"></div>\r
+\r
+</fieldset>\r
+\r
+<div style="margin-top: 85px; border-top: 1px solid #999; padding: 2px; text-align: right;">\r
+<button type="button" name="ok" onclick="return onOK();">OK</button>\r
+<button type="button" name="cancel" onclick="return onCancel();">Cancel</button>\r
+</div>\r
+\r
+</form>\r
+\r
+</body>\r
+</html>
\ No newline at end of file
diff --git a/mailboxes/xinha/modules/InsertTable/insert_table.js b/mailboxes/xinha/modules/InsertTable/insert_table.js
new file mode 100644 (file)
index 0000000..74183b2
--- /dev/null
@@ -0,0 +1,63 @@
+InsertTable._pluginInfo={name:"InsertTable",origin:"Xinha Core",version:"$LastChangedRevision: 688 $".replace(/^[^:]*: (.*) \$$/,"$1"),developer:"The Xinha Core Developer Team",developer_url:"$HeadURL: http://svn.xinha.python-hosting.com/trunk/modules/InsertTable/insert_table.js $".replace(/^[^:]*: (.*) \$$/,"$1"),sponsor:"",sponsor_url:"",license:"htmlArea"};
+function InsertTable(_1){
+}
+Xinha.prototype._insertTable=function(){
+var _2=this.getSelection();
+var _3=this.createRange(_2);
+var _4=this;
+Dialog(_4.config.URIs.insert_table,function(_5){
+if(!_5){
+return false;
+}
+var _6=_4._doc;
+var _7=_6.createElement("table");
+for(var _8 in _5){
+var _9=_5[_8];
+if(!_9){
+continue;
+}
+switch(_8){
+case "f_width":
+_7.style.width=_9+_5.f_unit;
+break;
+case "f_align":
+_7.align=_9;
+break;
+case "f_border":
+_7.border=parseInt(_9,10);
+break;
+case "f_spacing":
+_7.cellSpacing=parseInt(_9,10);
+break;
+case "f_padding":
+_7.cellPadding=parseInt(_9,10);
+break;
+}
+}
+var _a=0;
+if(_5.f_fixed){
+_a=Math.floor(100/parseInt(_5.f_cols,10));
+}
+var _b=_6.createElement("tbody");
+_7.appendChild(_b);
+for(var i=0;i<_5.f_rows;++i){
+var tr=_6.createElement("tr");
+_b.appendChild(tr);
+for(var j=0;j<_5.f_cols;++j){
+var td=_6.createElement("td");
+if(_a){
+td.style.width=_a+"%";
+}
+tr.appendChild(td);
+td.appendChild(_6.createTextNode("\xa0"));
+}
+}
+if(Xinha.is_ie){
+_3.pasteHTML(_7.outerHTML);
+}else{
+_4.insertNodeAtSelection(_7);
+}
+return true;
+},null);
+};
+\r
diff --git a/mailboxes/xinha/modules/InternetExplorer/InternetExplorer.js b/mailboxes/xinha/modules/InternetExplorer/InternetExplorer.js
new file mode 100644 (file)
index 0000000..a1d965e
--- /dev/null
@@ -0,0 +1,203 @@
+InternetExplorer._pluginInfo={name:"Internet Explorer",origin:"Xinha Core",version:"$LastChangedRevision: 737 $".replace(/^[^:]*: (.*) \$$/,"$1"),developer:"The Xinha Core Developer Team",developer_url:"$HeadURL: http://svn.xinha.python-hosting.com/tags/0.92beta/modules/InternetExplorer/InternetExplorer.js $".replace(/^[^:]*: (.*) \$$/,"$1"),sponsor:"",sponsor_url:"",license:"htmlArea"};
+function InternetExplorer(_1){
+this.editor=_1;
+_1.InternetExplorer=this;
+}
+InternetExplorer.prototype.onKeyPress=function(ev){
+if(this.editor.isShortCut(ev)){
+switch(this.editor.getKey(ev).toLowerCase()){
+case "n":
+this.editor.execCommand("formatblock",false,"<p>");
+Xinha._stopEvent(ev);
+return true;
+break;
+case "1":
+case "2":
+case "3":
+case "4":
+case "5":
+case "6":
+this.editor.execCommand("formatblock",false,"<h"+this.editor.getKey(ev).toLowerCase()+">");
+Xinha._stopEvent(ev);
+return true;
+break;
+}
+}
+switch(ev.keyCode){
+case 8:
+case 46:
+if(this.handleBackspace()){
+Xinha._stopEvent(ev);
+return true;
+}
+break;
+}
+return false;
+};
+InternetExplorer.prototype.handleBackspace=function(){
+var _3=this.editor;
+var _4=_3.getSelection();
+if(_4.type=="Control"){
+var _5=_3.activeElement(_4);
+Xinha.removeFromParent(_5);
+return true;
+}
+var _6=_3.createRange(_4);
+var r2=_6.duplicate();
+r2.moveStart("character",-1);
+var a=r2.parentElement();
+if(a!=_6.parentElement()&&(/^a$/i.test(a.tagName))){
+r2.collapse(true);
+r2.moveEnd("character",1);
+r2.pasteHTML("");
+r2.select();
+return true;
+}
+};
+InternetExplorer.prototype.inwardHtml=function(_9){
+_9=_9.replace(/<(\/?)del(\s|>|\/)/ig,"<$1strike$2");
+return _9;
+};
+Xinha.prototype.insertNodeAtSelection=function(_a){
+this.insertHTML(_a.outerHTML);
+};
+Xinha.prototype.getParentElement=function(_b){
+if(typeof _b=="undefined"){
+_b=this.getSelection();
+}
+var _c=this.createRange(_b);
+switch(_b.type){
+case "Text":
+var _d=_c.parentElement();
+while(true){
+var _e=_c.duplicate();
+_e.moveToElementText(_d);
+if(_e.inRange(_c)){
+break;
+}
+if((_d.nodeType!=1)||(_d.tagName.toLowerCase()=="body")){
+break;
+}
+_d=_d.parentElement;
+}
+return _d;
+case "None":
+return _c.parentElement();
+case "Control":
+return _c.item(0);
+default:
+return this._doc.body;
+}
+};
+Xinha.prototype.activeElement=function(_f){
+if((_f===null)||this.selectionEmpty(_f)){
+return null;
+}
+if(_f.type.toLowerCase()=="control"){
+return _f.createRange().item(0);
+}else{
+var _10=_f.createRange();
+var _11=this.getParentElement(_f);
+if(_11.innerHTML==_10.htmlText){
+return _11;
+}
+return null;
+}
+};
+Xinha.prototype.selectionEmpty=function(sel){
+if(!sel){
+return true;
+}
+return this.createRange(sel).htmlText==="";
+};
+Xinha.prototype.selectNodeContents=function(_13,pos){
+this.focusEditor();
+this.forceRedraw();
+var _15;
+var _16=typeof pos=="undefined"?true:false;
+if(_16&&_13.tagName&&_13.tagName.toLowerCase().match(/table|img|input|select|textarea/)){
+_15=this._doc.body.createControlRange();
+_15.add(_13);
+}else{
+_15=this._doc.body.createTextRange();
+_15.moveToElementText(_13);
+}
+_15.select();
+};
+Xinha.prototype.insertHTML=function(_17){
+this.focusEditor();
+var sel=this.getSelection();
+var _19=this.createRange(sel);
+_19.pasteHTML(_17);
+};
+Xinha.prototype.getSelectedHTML=function(){
+var sel=this.getSelection();
+var _1b=this.createRange(sel);
+if(_1b.htmlText){
+return _1b.htmlText;
+}else{
+if(_1b.length>=1){
+return _1b.item(0).outerHTML;
+}
+}
+return "";
+};
+Xinha.prototype.getSelection=function(){
+return this._doc.selection;
+};
+Xinha.prototype.createRange=function(sel){
+return sel.createRange();
+};
+Xinha.prototype.isKeyEvent=function(_1d){
+return _1d.type=="keydown";
+};
+Xinha.prototype.getKey=function(_1e){
+return String.fromCharCode(_1e.keyCode);
+};
+Xinha.getOuterHTML=function(_1f){
+return _1f.outerHTML;
+};
+Xinha.prototype.cc=String.fromCharCode(8201);
+Xinha.prototype.setCC=function(_20){
+if(_20=="textarea"){
+var ta=this._textArea;
+var pos=document.selection.createRange();
+pos.collapse();
+pos.text=this.cc;
+var _23=ta.value.indexOf(this.cc);
+var _24=ta.value.substring(0,_23);
+var _25=ta.value.substring(_23+this.cc.length,ta.value.length);
+if(_25.match(/^[^<]*>/)){
+var _26=_25.indexOf(">")+1;
+ta.value=_24+_25.substring(0,_26)+this.cc+_25.substring(_26,_25.length);
+}else{
+ta.value=_24+this.cc+_25;
+}
+}else{
+var sel=this.getSelection();
+var r=sel.createRange();
+if(sel.type=="Control"){
+var _29=r.item(0);
+_29.outerHTML+=this.cc;
+}else{
+r.collapse();
+r.text=this.cc;
+}
+}
+};
+Xinha.prototype.findCC=function(_2a){
+var _2b=(_2a=="textarea")?this._textArea:this._doc.body;
+range=_2b.createTextRange();
+if(range.findText(escape(this.cc))){
+range.select();
+range.text="";
+}
+if(range.findText(this.cc)){
+range.select();
+range.text="";
+}
+if(_2a=="textarea"){
+this._textArea.focus();
+}
+};
+\r
diff --git a/mailboxes/xinha/popups/about.html b/mailboxes/xinha/popups/about.html
new file mode 100644 (file)
index 0000000..2091075
--- /dev/null
@@ -0,0 +1,296 @@
+<!--\r
+\r
+(c) dynarch.com, 2003-2004\r
+Author: Mihai Bazon, http://dynarch.com/mishoo\r
+Distributed as part of HTMLArea 3.0\r
+\r
+"You are not expected to understand this...  I don't neither."\r
+\r
+                      (from The Linux Kernel Source Code,\r
+                            ./arch/x86_64/ia32/ptrace.c:90)\r
+\r
+;-)\r
+\r
+-->\r
+\r
+<html style="height: 100%">\r
+<head>\r
+<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />\r
+<title>About Xinha</title>\r
+<script type="text/javascript" src="popup.js"></script>\r
+<script type="text/javascript">\r
+window.resizeTo(450, 250);\r
+var TABS = [];\r
+var CURRENT_TAB = 0;\r
+var CONTENT_HEIGHT_DIFF = 0;\r
+var CONTENT_WIDTH_DIFF = 0;\r
+function selectTab(idx) {\r
+  var ct = TABS[CURRENT_TAB];\r
+  ct.className = ct.className.replace(/\s*tab-current\s*/, ' ');\r
+  ct = TABS[CURRENT_TAB = idx];\r
+  ct.className += ' tab-current';\r
+  for (var i = TABS.length; --i >= 0;) {\r
+    var area = document.getElementById("tab-area-" + i);\r
+    if (CURRENT_TAB == i) {\r
+      area.style.display = "block";\r
+    } else {\r
+      area.style.display = "none";\r
+    }\r
+  }\r
+  document.body.style.visibility = "hidden";\r
+  document.body.style.visibility = "visible";\r
+  document.cookie = "HTMLAREA-ABOUT-TAB=" + idx;\r
+}\r
+var editor = null;\r
+function initDocument() {\r
+  editor = window.dialogArguments;\r
+  Xinha = window.opener.Xinha;\r
+\r
+  var plugins = document.getElementById("plugins");\r
+  var j = 0;\r
+  var html = "<table width='99%' cellpadding='2' cellspacing=2 style='margin-top: 1em; collapse-borders: collapse; border: 1px solid black;'>" +\r
+    "<thead><tr>" +\r
+    "<td>Name</td>" +\r
+    "<td>Developer</td>" +\r
+    "<td>Sponsored by</td>" +\r
+    "<td>License</td>" +\r
+    "</tr></thead><tbody>";\r
+  for (var i in editor.plugins) {\r
+    var info = editor.plugins[i];\r
+    if (typeof info != 'object' || !info.name || typeof info.name !='string')   continue;\r
+    html += "<tr><td>" + info.name + " v" + info.version + "</td>" +\r
+            "<td><a href='" + info.developer_url + "' target='_blank' title='Visit developer website'>" +\r
+      info.developer + "</a></td>" +\r
+      "<td><a href='" + info.sponsor_url + "' target='_blank' title='Visit sponsor website'>" +\r
+      info.sponsor + "</a></td>" +\r
+      "<td>" + info.license + "</td></tr>";\r
+    ++j;\r
+  }\r
+\r
+  if (j) {\r
+          html += "</tbody></table>" +\r
+      "<p>License \"htmlArea\" means that the plugin is distributed under the same terms " +\r
+      "as Xinha itself.</p>";\r
+    plugins.innerHTML = "<p>The following plugins have been loaded.</p>" + html;\r
+  } else {\r
+    plugins.innerHTML = "<p>No plugins have been loaded</p>";\r
+  }\r
+\r
+  plugins.innerHTML += "<p>User agent reports:<br/>" + navigator.userAgent + "</p>";\r
+\r
+  var content = document.getElementById("content");\r
+  if (window.innerHeight) {\r
+    CONTENT_HEIGHT_DIFF = window.innerHeight - 250;\r
+    CONTENT_WIDTH_DIFF = window.innerWidth - content.offsetWidth;\r
+  } else {\r
+    CONTENT_HEIGHT_DIFF = document.body.offsetHeight - 250;\r
+    CONTENT_WIDTH_DIFF = document.body.offsetWidth - 400;\r
+  }\r
+  window.onresize();\r
+  var bar = document.getElementById("tabbar");\r
+  j = 0;\r
+  for (var i = bar.firstChild; i; i = i.nextSibling) {\r
+    TABS.push(i);\r
+    i.__msh_tab = j;\r
+    i.onmousedown = function(ev) { selectTab(this.__msh_tab); Xinha._stopEvent(ev || window.event); };\r
+    var area = document.getElementById("tab-area-" + j);\r
+    if (/tab-current/.test(i.className)) {\r
+      CURRENT_TAB = j;\r
+      area.style.display = "block";\r
+    } else {\r
+      area.style.display = "none";\r
+    }\r
+    ++j;\r
+  }\r
+  if (document.cookie.match(/HTMLAREA-ABOUT-TAB=([0-9]+)/))\r
+    selectTab(RegExp.$1);\r
+}\r
+window.onresize = function() {\r
+  var content = document.getElementById("content");\r
+  if (window.innerHeight) {\r
+    content.style.height = (window.innerHeight - CONTENT_HEIGHT_DIFF) + "px";\r
+    content.style.width = (window.innerWidth - CONTENT_WIDTH_DIFF) + "px";\r
+  } else {\r
+    content.style.height = (document.body.offsetHeight - CONTENT_HEIGHT_DIFF) + "px";\r
+    //content.style.width = (document.body.offsetWidth - CONTENT_WIDTH_DIFF) + "px";\r
+  }\r
+}\r
+</script>\r
+<style>\r
+  html,body,textarea,table\r
+  { font-family: helvetica,arial,sans-serif;\r
+    font-size: 11px; padding: 0px; margin: 0px;\r
+  }\r
+\r
+  body { padding: 0px; background: #f5f6f6; color: black; }\r
+  a:link, a:visited { color: #00f; }\r
+  a:hover           { color: #f00; }\r
+  a:active          { color: #f80; }\r
+  button            { font: 11px; border-width: 1px; background-color:#f5f6f6; color:black; }\r
+\r
+  p { margin: 0.5em 0px; }\r
+\r
+  #tdheader h1\r
+  { font: bold 40px "Staccato222 BT", cursive; margin: 0px; padding-left:4px; border-bottom: 1px solid #6a6; }\r
+  h1 { font: bold 20px; margin: 0px; border-bottom: 1px solid #6a6; }\r
+  h2 { font: bold 110%; margin: 0.7em 0px; }\r
+\r
+  thead {\r
+    font-weight: bold;\r
+    background-color: #CCC;\r
+  }\r
+\r
+  thead td { padding-left:4px; }\r
+\r
+  .buttons {\r
+    text-align: right; padding: 3px;\r
+    background-color: white;\r
+    border-top: 1px solid #555;\r
+  }\r
+\r
+  #tabbar {\r
+    position: relative;\r
+    left: 10px;\r
+  }\r
+  .tab {\r
+    color: #OOO;\r
+    cursor: pointer;\r
+    margin-left: -5px;\r
+    float: left; position: relative;\r
+    border: 1px solid #555;\r
+    top: -3px; left: -2px;\r
+    padding: 2px 10px 3px 10px;\r
+    border-top: none; background-color: #CCC;\r
+    -moz-border-radius: 0px 0px 4px 4px;\r
+    z-index: 0;\r
+  }\r
+  .tab-current\r
+  {\r
+    color: #000;\r
+    top: -4px;\r
+    background-color: #f5f6f6;\r
+    padding: 3px 10px 4px 10px;\r
+    z-index: 10;\r
+  }\r
+\r
+  li, ol, ul { margin-top: 0px; margin-bottom: 0px; }\r
+</style></head>\r
+<body onload="__dlg_init(); initDocument();"\r
+><table cellspacing="0" cellpadding="0" style="border-collapse: collapse;\r
+      width: 100%; height: 100%;">\r
+\r
+<tr style="height: 1em"><td id="tdheader">\r
+\r
+<h1><img src="../images/xinha_logo.gif" alt="Xinha" /></h1>\r
+\r
+</td></tr>\r
+<tr><td id="tdcontent" style="padding: 0.5em;">\r
+\r
+<div style="overflow: auto; height: 250px;" id="content">\r
+<div id="tab-areas">\r
+\r
+<div id="tab-area-0">\r
+\r
+  <p>A free WYSIWYG editor replacement for <tt>&lt;textarea&gt;</tt> fields.</p>\r
+  <p>Visit the <a href="http://xinha.gogo.co.nz/">Xinha Website</a> for more information.</p>\r
+\r
+  <p>\r
+   Use of Xinha is granted by the terms of the htmlArea License (based on BSD license)\r
+  </p>\r
+  <p>\r
+    Xinha was originally based on work by Mihai Bazon which is:\r
+  </p>\r
+  <blockquote><pre>Copyright (c) 2003-2004 dynarch.com.\r
+    Copyright (c) 2002-2003 interactivetools.com, inc.\r
+    This copyright notice MUST stay intact for use.</pre></blockquote>\r
+\r
+</div>\r
+\r
+<div id="tab-area-1">\r
+\r
+  <p>\r
+   The development of Xinha would not have been possible without the original work of <a href="http://dynarch.com/">Mihai Bazon</a>, <a href="http://interactivetools.com" target="_blank">InteractiveTools.com</a>, and the many sponsors and contributors from around the world.\r
+  </p>\r
+\r
+</div>\r
+\r
+<div id="tab-area-2">\r
+<pre>htmlArea License (based on BSD license)\r
+Copyright (c) 2002-2004, interactivetools.com, inc.\r
+Copyright (c) 2003-2004 dynarch.com\r
+All rights reserved.\r
+\r
+Redistribution and use in source and binary forms, with or without\r
+modification, are permitted provided that the following conditions are met:\r
+\r
+1) Redistributions of source code must retain the above copyright notice,\r
+   this list of conditions and the following disclaimer.\r
+\r
+2) Redistributions in binary form must reproduce the above copyright notice,\r
+   this list of conditions and the following disclaimer in the documentation\r
+   and/or other materials provided with the distribution.\r
+\r
+3) Neither the name of interactivetools.com, inc. nor the names of its\r
+   contributors may be used to endorse or promote products derived from this\r
+   software without specific prior written permission.\r
+\r
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"\r
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\r
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\r
+ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE\r
+LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR\r
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF\r
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS\r
+INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN\r
+CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)\r
+ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE\r
+POSSIBILITY OF SUCH DAMAGE.</pre>\r
+</div>\r
+\r
+<div id="tab-area-3">\r
+<div id="plugins">\r
+</div>\r
+</div>\r
+\r
+\r
+<div id="tab-area-4">\r
+  <script type="text/javascript">\r
+    if(window.opener && window.opener.Xinha)\r
+    {\r
+      var ver = window.opener.Xinha.version;\r
+      document.write('<pre>'\r
+                      + '\nRelease: ' + ver.Release + ' (' + ver.Date + ')'\r
+                      + '\nHead: ' + ver.Head\r
+                      + '\nRevision: ' + ver.Revision\r
+                      + '\nLast Changed By: ' + ver.RevisionBy\r
+                      + '\n' +\r
+                      '</pre>');\r
+    }\r
+    else\r
+    {\r
+      document.write('<pre>Version information unavailable.</pre>');\r
+    }\r
+  </script>\r
+</div>\r
+\r
+</div></div>\r
+\r
+\r
+</td></tr>\r
+<tr style="height: 1em"><td id="tdfooter">\r
+\r
+\r
+<div class="buttons">\r
+<div id="tabbar"\r
+><div class="tab tab-current"\r
+>About</div><div class="tab"\r
+>Thanks</div><div class="tab"\r
+>License</div><div class="tab"\r
+>Plugins</div><div class="tab"\r
+>Version</div></div>\r
+<button type="button" onclick="__dlg_close(null);">Close</button>\r
+</div>\r
+\r
+</td></tr></table>\r
+\r
+</body></html>\r
diff --git a/mailboxes/xinha/popups/blank.html b/mailboxes/xinha/popups/blank.html
new file mode 100644 (file)
index 0000000..5b1b16d
--- /dev/null
@@ -0,0 +1,2 @@
+<html>\r
+</html>
\ No newline at end of file
diff --git a/mailboxes/xinha/popups/editor_help.html b/mailboxes/xinha/popups/editor_help.html
new file mode 100644 (file)
index 0000000..ac4fb1a
--- /dev/null
@@ -0,0 +1,62 @@
+<html>\r
+ <head>\r
+  <title>Editor Help</title>\r
+  <script type="text/javascript" src="popup.js"></script>\r
+  <link rel="stylesheet" type="text/css" href="popup.css" />\r
+\r
+<script type="text/javascript">\r
+  window.resizeTo(400, 480);\r
+Xinha = window.opener.Xinha;\r
+function Init() {\r
+  __dlg_translate('Xinha');\r
+  __dlg_init();\r
+\r
+}\r
+\r
+\r
+function onCancel() {\r
+  __dlg_close(null);\r
+  return false;\r
+}\r
+\r
+</script>\r
+</head>\r
+\r
+<body class="dialog" onload="Init()">\r
+<div class="title">Xinha Help</div>\r
+<table width="100%" style="background: white none repeat scroll 0%;">\r
+  <thead style="border: 1px solid gray; background: silver none repeat scroll 0%;">\r
+  <tr>\r
+    <th colspan="2"> \r
+      <span style="text-align:center">Keyboard shortcuts</span><br />\r
+      <span>The editor provides the following key combinations:</span>\r
+    </th>\r
+  </tr>\r
+  </thead>\r
+  <tbody>\r
+  <tr><td>ENTER</td><td><span>new paragraph</span>(&lt;P&gt;)</td></tr>\r
+  <tr><td>SHIFT-ENTER</td><td><span>insert linebreak</span>(&lt;BR&gt;)</td></tr>\r
+  <tr><td>CTRL-A</td><td><span>Select all</span></td></tr>\r
+  <tr><td>CTRL-B</td><td><span>Bold</span></td></tr>\r
+  <tr><td>CTRL-I</td><td><span>Italic</span></td></tr>\r
+  <tr><td>CTRL-U</td><td><span>Underline</span></td></tr>\r
+  <tr><td>CTRL-S</td><td><span>Strikethrough</span></td></tr>\r
+  <tr><td>CTRL-L</td><td><span>Justify Left</span></td></tr>\r
+  <tr><td>CTRL-E</td><td><span>Justify Center</span></td></tr>\r
+  <tr><td>CTRL-R</td><td><span>Justify Right</span></td></tr>\r
+  <tr><td>CTRL-J</td><td><span>Justify Full</span></td></tr>\r
+  <tr><td>CTRL-Z</td><td><span>Undoes your last action</span></td></tr>\r
+  <tr><td>CTRL-Y</td><td><span>Redoes your last action</span></td></tr>\r
+  <tr><td>CTRL-N</td><td><span>Set format to paragraph</span></td></tr>\r
+  <tr><td>CTRL-0 (zero)</td><td><span>Clean content pasted from Word</span></td></tr>\r
+  <tr><td>CTRL-1 .. CTRL-6</td><td><span>Headings</span> (&lt;h1&gt; .. &lt;h6&gt;)</td></tr>\r
+  <tr><td>CTRL-X</td><td><span>Cut selection</span></td></tr>\r
+  <tr><td>CTRL-C</td><td><span>Copy selection</span></td></tr>\r
+  <tr><td>CTRL-V</td><td><span>Paste from clipboard</span></td></tr>\r
+  </tbody>\r
+</table>\r
+<div id="buttons">\r
+  <button type="button" name="cancel" onclick="return onCancel();">Close</button>\r
+</div>\r
+</body>\r
+</html>
\ No newline at end of file
diff --git a/mailboxes/xinha/popups/popup.css b/mailboxes/xinha/popups/popup.css
new file mode 100644 (file)
index 0000000..7bf9358
--- /dev/null
@@ -0,0 +1,39 @@
+html, body, .dialog {
+  background: ButtonFace;
+  color: ButtonText;
+  font: 11px Tahoma,Verdana,sans-serif;
+  margin: 0px;
+  padding: 0px;
+}
+body { padding: 5px; }
+form { padding: 0px; margin: 0px; }
+form p {
+  margin-top: 5px;
+  margin-bottom: 5px;
+}
+table {
+  font: 11px Tahoma,Verdana,sans-serif;
+}
+select, input, button { font: 11px Tahoma,Verdana,sans-serif; }
+button { width: 70px; }
+table .label { text-align: right; width: 8em; }
+
+.fl { width: 9em; float: left; padding: 2px 5px; text-align: right; }
+.fr { width: 7em; float: left; padding: 2px 5px; text-align: right; }
+fieldset { padding: 0px 10px 5px 5px; }
+.space { padding: 2px; }
+.title { background: #ddf; color: #000; font-weight: bold; font-size: 120%; padding: 3px 10px; margin-bottom: 10px;
+border-bottom: 1px solid black; letter-spacing: 2px;
+}
+.buttonColor {
+  padding: 1px;
+  cursor: default;
+  border: 1px solid;
+  border-color: ButtonHighlight ButtonShadow ButtonShadow ButtonHighlight;
+}
+#buttons {
+      margin-top: 1em; 
+      border-top: 1px solid #999;
+      padding: 1em; 
+      text-align: right;
+}
\ No newline at end of file
diff --git a/mailboxes/xinha/popups/popup.js b/mailboxes/xinha/popups/popup.js
new file mode 100644 (file)
index 0000000..9cb5210
--- /dev/null
@@ -0,0 +1,102 @@
+Xinha=window.opener.Xinha;
+HTMLArea=window.opener.Xinha;
+function getAbsolutePos(el){
+var r={x:el.offsetLeft,y:el.offsetTop};
+if(el.offsetParent){
+var _3=getAbsolutePos(el.offsetParent);
+r.x+=_3.x;
+r.y+=_3.y;
+}
+return r;
+}
+function comboSelectValue(c,_5){
+var _6=c.getElementsByTagName("option");
+for(var i=_6.length;--i>=0;){
+var op=_6[i];
+op.selected=(op.value==_5);
+}
+c.value=_5;
+}
+function __dlg_onclose(){
+opener.Dialog._return(null);
+}
+function __dlg_init(_9,_a){
+__xinha_dlg_init(_a);
+}
+function __xinha_dlg_init(_b){
+if(window.__dlg_init_done){
+return true;
+}
+if(window.opener._editor_skin!=""){
+var _c=document.getElementsByTagName("head")[0];
+var _d=document.createElement("link");
+_d.type="text/css";
+_d.href=window.opener._editor_url+"skins/"+window.opener._editor_skin+"/skin.css";
+_d.rel="stylesheet";
+_c.appendChild(_d);
+}
+window.dialogArguments=opener.Dialog._arguments;
+var _e=document.body;
+if(!_b){
+var _f=Xinha.viewportSize(window);
+_b={width:_f.x,height:_e.scrollHeight};
+}
+window.resizeTo(_b.width,_b.height);
+var _f=Xinha.viewportSize(window);
+window.resizeBy(0,_e.scrollHeight-_f.y);
+if(_b.top&&_b.left){
+window.moveTo(_b.left,_b.top);
+}else{
+if(!Xinha.is_ie){
+var x=opener.screenX+(opener.outerWidth-_b.width)/2;
+var y=opener.screenY+(opener.outerHeight-_b.height)/2;
+}else{
+var x=(self.screen.availWidth-_b.width)/2;
+var y=(self.screen.availHeight-_b.height)/2;
+}
+window.moveTo(x,y);
+}
+Xinha.addDom0Event(document.body,"keypress",__dlg_close_on_esc);
+window.__dlg_init_done=true;
+}
+function __dlg_translate(_12){
+var _13=["input","select","legend","span","option","td","th","button","div","label","a","img"];
+for(var _14=0;_14<_13.length;++_14){
+var _15=document.getElementsByTagName(_13[_14]);
+for(var i=_15.length;--i>=0;){
+var _17=_15[i];
+if(_17.firstChild&&_17.firstChild.data){
+var txt=Xinha._lc(_17.firstChild.data,_12);
+if(txt){
+_17.firstChild.data=txt;
+}
+}
+if(_17.title){
+var txt=Xinha._lc(_17.title,_12);
+if(txt){
+_17.title=txt;
+}
+}
+if(_17.tagName.toLowerCase()=="input"&&(/^(button|submit|reset)$/i.test(_17.type))){
+var txt=Xinha._lc(_17.value,_12);
+if(txt){
+_17.value=txt;
+}
+}
+}
+}
+document.title=Xinha._lc(document.title,_12);
+}
+function __dlg_close(val){
+opener.Dialog._return(val);
+window.close();
+}
+function __dlg_close_on_esc(ev){
+ev||(ev=window.event);
+if(ev.keyCode==27){
+__dlg_close(null);
+return false;
+}
+return true;
+}
+\r
diff --git a/mailboxes/xinha/popups/select_color.html b/mailboxes/xinha/popups/select_color.html
new file mode 100644 (file)
index 0000000..b64a484
--- /dev/null
@@ -0,0 +1,359 @@
+<!--<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">-->\r
+<html><head><title>Select Color</title>\r
+<meta http-equiv="Content-Type" content="text/html; charset=utf-8">\r
+<script type="text/javascript" src="popup.js"></script>\r
+<script type="text/javascript">\r
+window.resizeTo(240, 182);\r
+// run on page load\r
+function Init()\r
+{\r
+  __dlg_translate('Xinha');\r
+  __dlg_init();\r
+  Xinha.addDom0Event(document, 'keypress', __dlg_close_on_esc);\r
+\r
+  var color = window.dialogArguments;\r
+  color = ValidateColor(color) || '000000';\r
+  View(color);\r
+}\r
+// preview color\r
+function View(color)\r
+{\r
+  document.getElementById("ColorPreview").style.backgroundColor = '#' + color;\r
+  document.getElementById("ColorHex").value = '#' + color;\r
+}\r
+// select color\r
+function Set(string)\r
+{\r
+  var color = ValidateColor(string);\r
+  if (color == null)\r
+  {\r
+    alert("Invalid color code: " + string);\r
+  }\r
+  else\r
+  {\r
+    View(color);\r
+    __dlg_close(color);\r
+  }\r
+}\r
+// return valid color code\r
+function ValidateColor(string)\r
+{\r
+  string = string || '';\r
+  string = string + "";\r
+  string = string.toUpperCase();\r
+  var chars = '0123456789ABCDEF';\r
+  var out   = '';\r
+  // remove invalid color chars\r
+  for (var i=0; i<string.length; i++)\r
+  {\r
+    var schar = string.charAt(i);\r
+    if (chars.indexOf(schar) != -1) out += schar;\r
+  }\r
+  // check length\r
+  if (out.length != 6) return null;\r
+  return out;\r
+}\r
+</script>\r
+<style type="text/css">\r
+body { background:buttonface; margin:0; padding:0; }\r
+form { margin:0; padding:0; }\r
+#containerpreview td { background:buttonface; }\r
+#preview { background-color:#000000; padding:1px; height:21px; width:50px; }\r
+#ColorPreview { height:100%; width:100%; }\r
+#ColorHex { font-size:12px; background:buttonface; border:0; }\r
+#palettecolor { cursor:pointer; }\r
+#palettecolor td { width:10px; height:10px; }\r
+</style>\r
+</head>\r
+<body class="dialog" onload="Init()">\r
+\r
+<form action="#" method="get" onsubmit="Set(document.getElementById('ColorHex').value); return false;">\r
+<table border="0" cellspacing="0" cellpadding="4" width="100%" id="containerpreview">\r
+ <tr>\r
+  <td valign="middle"><div id="preview"><div id="ColorPreview"></div></div></td>\r
+  <td valign="middle" width="100%"><input type="text" name="ColorHex"\r
+    id="ColorHex" value="" size=15></td>\r
+ </tr>\r
+</table>\r
+</form>\r
+\r
+<table id="palettecolor" border="0" cellspacing="1" cellpadding="0" width="100%" bgcolor="#000000">\r
+<tr>\r
+<td bgcolor="#000000" onmouseover="View('000000')" onclick="Set('000000')"></td>\r
+<td bgcolor="#000000" onmouseover="View('000000')" onclick="Set('000000')"></td>\r
+<td bgcolor="#000000" onmouseover="View('000000')" onclick="Set('000000')"></td>\r
+<td bgcolor="#000000" onmouseover="View('000000')" onclick="Set('000000')"></td>\r
+<td bgcolor="#003300" onmouseover="View('003300')" onclick="Set('003300')"></td>\r
+<td bgcolor="#006600" onmouseover="View('006600')" onclick="Set('006600')"></td>\r
+<td bgcolor="#009900" onmouseover="View('009900')" onclick="Set('009900')"></td>\r
+<td bgcolor="#00CC00" onmouseover="View('00CC00')" onclick="Set('00CC00')"></td>\r
+<td bgcolor="#00FF00" onmouseover="View('00FF00')" onclick="Set('00FF00')"></td>\r
+<td bgcolor="#330000" onmouseover="View('330000')" onclick="Set('330000')"></td>\r
+<td bgcolor="#333300" onmouseover="View('333300')" onclick="Set('333300')"></td>\r
+<td bgcolor="#336600" onmouseover="View('336600')" onclick="Set('336600')"></td>\r
+<td bgcolor="#339900" onmouseover="View('339900')" onclick="Set('339900')"></td>\r
+<td bgcolor="#33CC00" onmouseover="View('33CC00')" onclick="Set('33CC00')"></td>\r
+<td bgcolor="#33FF00" onmouseover="View('33FF00')" onclick="Set('33FF00')"></td>\r
+<td bgcolor="#660000" onmouseover="View('660000')" onclick="Set('660000')"></td>\r
+<td bgcolor="#663300" onmouseover="View('663300')" onclick="Set('663300')"></td>\r
+<td bgcolor="#666600" onmouseover="View('666600')" onclick="Set('666600')"></td>\r
+<td bgcolor="#669900" onmouseover="View('669900')" onclick="Set('669900')"></td>\r
+<td bgcolor="#66CC00" onmouseover="View('66CC00')" onclick="Set('66CC00')"></td>\r
+<td bgcolor="#66FF00" onmouseover="View('66FF00')" onclick="Set('66FF00')"></td>\r
+</tr>\r
+<tr>\r
+<td bgcolor="#000000" onmouseover="View('000000')" onclick="Set('000000')"></td>\r
+<td bgcolor="#333333" onmouseover="View('333333')" onclick="Set('333333')"></td>\r
+<td bgcolor="#000000" onmouseover="View('000000')" onclick="Set('000000')"></td>\r
+<td bgcolor="#000033" onmouseover="View('000033')" onclick="Set('000033')"></td>\r
+<td bgcolor="#003333" onmouseover="View('003333')" onclick="Set('003333')"></td>\r
+<td bgcolor="#006633" onmouseover="View('006633')" onclick="Set('006633')"></td>\r
+<td bgcolor="#009933" onmouseover="View('009933')" onclick="Set('009933')"></td>\r
+<td bgcolor="#00CC33" onmouseover="View('00CC33')" onclick="Set('00CC33')"></td>\r
+<td bgcolor="#00FF33" onmouseover="View('00FF33')" onclick="Set('00FF33')"></td>\r
+<td bgcolor="#330033" onmouseover="View('330033')" onclick="Set('330033')"></td>\r
+<td bgcolor="#333333" onmouseover="View('333333')" onclick="Set('333333')"></td>\r
+<td bgcolor="#336633" onmouseover="View('336633')" onclick="Set('336633')"></td>\r
+<td bgcolor="#339933" onmouseover="View('339933')" onclick="Set('339933')"></td>\r
+<td bgcolor="#33CC33" onmouseover="View('33CC33')" onclick="Set('33CC33')"></td>\r
+<td bgcolor="#33FF33" onmouseover="View('33FF33')" onclick="Set('33FF33')"></td>\r
+<td bgcolor="#660033" onmouseover="View('660033')" onclick="Set('660033')"></td>\r
+<td bgcolor="#663333" onmouseover="View('663333')" onclick="Set('663333')"></td>\r
+<td bgcolor="#666633" onmouseover="View('666633')" onclick="Set('666633')"></td>\r
+<td bgcolor="#669933" onmouseover="View('669933')" onclick="Set('669933')"></td>\r
+<td bgcolor="#66CC33" onmouseover="View('66CC33')" onclick="Set('66CC33')"></td>\r
+<td bgcolor="#66FF33" onmouseover="View('66FF33')" onclick="Set('66FF33')"></td>\r
+</tr>\r
+<tr>\r
+<td bgcolor="#000000" onmouseover="View('000000')" onclick="Set('000000')"></td>\r
+<td bgcolor="#666666" onmouseover="View('666666')" onclick="Set('666666')"></td>\r
+<td bgcolor="#000000" onmouseover="View('000000')" onclick="Set('000000')"></td>\r
+<td bgcolor="#000066" onmouseover="View('000066')" onclick="Set('000066')"></td>\r
+<td bgcolor="#003366" onmouseover="View('003366')" onclick="Set('003366')"></td>\r
+<td bgcolor="#006666" onmouseover="View('006666')" onclick="Set('006666')"></td>\r
+<td bgcolor="#009966" onmouseover="View('009966')" onclick="Set('009966')"></td>\r
+<td bgcolor="#00CC66" onmouseover="View('00CC66')" onclick="Set('00CC66')"></td>\r
+<td bgcolor="#00FF66" onmouseover="View('00FF66')" onclick="Set('00FF66')"></td>\r
+<td bgcolor="#330066" onmouseover="View('330066')" onclick="Set('330066')"></td>\r
+<td bgcolor="#333366" onmouseover="View('333366')" onclick="Set('333366')"></td>\r
+<td bgcolor="#336666" onmouseover="View('336666')" onclick="Set('336666')"></td>\r
+<td bgcolor="#339966" onmouseover="View('339966')" onclick="Set('339966')"></td>\r
+<td bgcolor="#33CC66" onmouseover="View('33CC66')" onclick="Set('33CC66')"></td>\r
+<td bgcolor="#33FF66" onmouseover="View('33FF66')" onclick="Set('33FF66')"></td>\r
+<td bgcolor="#660066" onmouseover="View('660066')" onclick="Set('660066')"></td>\r
+<td bgcolor="#663366" onmouseover="View('663366')" onclick="Set('663366')"></td>\r
+<td bgcolor="#666666" onmouseover="View('666666')" onclick="Set('666666')"></td>\r
+<td bgcolor="#669966" onmouseover="View('669966')" onclick="Set('669966')"></td>\r
+<td bgcolor="#66CC66" onmouseover="View('66CC66')" onclick="Set('66CC66')"></td>\r
+<td bgcolor="#66FF66" onmouseover="View('66FF66')" onclick="Set('66FF66')"></td>\r
+</tr>\r
+<tr>\r
+<td bgcolor="#000000" onmouseover="View('000000')" onclick="Set('000000')"></td>\r
+<td bgcolor="#999999" onmouseover="View('999999')" onclick="Set('999999')"></td>\r
+<td bgcolor="#000000" onmouseover="View('000000')" onclick="Set('000000')"></td>\r
+<td bgcolor="#000099" onmouseover="View('000099')" onclick="Set('000099')"></td>\r
+<td bgcolor="#003399" onmouseover="View('003399')" onclick="Set('003399')"></td>\r
+<td bgcolor="#006699" onmouseover="View('006699')" onclick="Set('006699')"></td>\r
+<td bgcolor="#009999" onmouseover="View('009999')" onclick="Set('009999')"></td>\r
+<td bgcolor="#00CC99" onmouseover="View('00CC99')" onclick="Set('00CC99')"></td>\r
+<td bgcolor="#00FF99" onmouseover="View('00FF99')" onclick="Set('00FF99')"></td>\r
+<td bgcolor="#330099" onmouseover="View('330099')" onclick="Set('330099')"></td>\r
+<td bgcolor="#333399" onmouseover="View('333399')" onclick="Set('333399')"></td>\r
+<td bgcolor="#336699" onmouseover="View('336699')" onclick="Set('336699')"></td>\r
+<td bgcolor="#339999" onmouseover="View('339999')" onclick="Set('339999')"></td>\r
+<td bgcolor="#33CC99" onmouseover="View('33CC99')" onclick="Set('33CC99')"></td>\r
+<td bgcolor="#33FF99" onmouseover="View('33FF99')" onclick="Set('33FF99')"></td>\r
+<td bgcolor="#660099" onmouseover="View('660099')" onclick="Set('660099')"></td>\r
+<td bgcolor="#663399" onmouseover="View('663399')" onclick="Set('663399')"></td>\r
+<td bgcolor="#666699" onmouseover="View('666699')" onclick="Set('666699')"></td>\r
+<td bgcolor="#669999" onmouseover="View('669999')" onclick="Set('669999')"></td>\r
+<td bgcolor="#66CC99" onmouseover="View('66CC99')" onclick="Set('66CC99')"></td>\r
+<td bgcolor="#66FF99" onmouseover="View('66FF99')" onclick="Set('66FF99')"></td>\r
+</tr>\r
+<tr>\r
+<td bgcolor="#000000" onmouseover="View('000000')" onclick="Set('000000')"></td>\r
+<td bgcolor="#CCCCCC" onmouseover="View('CCCCCC')" onclick="Set('CCCCCC')"></td>\r
+<td bgcolor="#000000" onmouseover="View('000000')" onclick="Set('000000')"></td>\r
+<td bgcolor="#0000CC" onmouseover="View('0000CC')" onclick="Set('0000CC')"></td>\r
+<td bgcolor="#0033CC" onmouseover="View('0033CC')" onclick="Set('0033CC')"></td>\r
+<td bgcolor="#0066CC" onmouseover="View('0066CC')" onclick="Set('0066CC')"></td>\r
+<td bgcolor="#0099CC" onmouseover="View('0099CC')" onclick="Set('0099CC')"></td>\r
+<td bgcolor="#00CCCC" onmouseover="View('00CCCC')" onclick="Set('00CCCC')"></td>\r
+<td bgcolor="#00FFCC" onmouseover="View('00FFCC')" onclick="Set('00FFCC')"></td>\r
+<td bgcolor="#3300CC" onmouseover="View('3300CC')" onclick="Set('3300CC')"></td>\r
+<td bgcolor="#3333CC" onmouseover="View('3333CC')" onclick="Set('3333CC')"></td>\r
+<td bgcolor="#3366CC" onmouseover="View('3366CC')" onclick="Set('3366CC')"></td>\r
+<td bgcolor="#3399CC" onmouseover="View('3399CC')" onclick="Set('3399CC')"></td>\r
+<td bgcolor="#33CCCC" onmouseover="View('33CCCC')" onclick="Set('33CCCC')"></td>\r
+<td bgcolor="#33FFCC" onmouseover="View('33FFCC')" onclick="Set('33FFCC')"></td>\r
+<td bgcolor="#6600CC" onmouseover="View('6600CC')" onclick="Set('6600CC')"></td>\r
+<td bgcolor="#6633CC" onmouseover="View('6633CC')" onclick="Set('6633CC')"></td>\r
+<td bgcolor="#6666CC" onmouseover="View('6666CC')" onclick="Set('6666CC')"></td>\r
+<td bgcolor="#6699CC" onmouseover="View('6699CC')" onclick="Set('6699CC')"></td>\r
+<td bgcolor="#66CCCC" onmouseover="View('66CCCC')" onclick="Set('66CCCC')"></td>\r
+<td bgcolor="#66FFCC" onmouseover="View('66FFCC')" onclick="Set('66FFCC')"></td>\r
+</tr>\r
+<tr>\r
+<td bgcolor="#000000" onmouseover="View('000000')" onclick="Set('000000')"></td>\r
+<td bgcolor="#FFFFFF" onmouseover="View('FFFFFF')" onclick="Set('FFFFFF')"></td>\r
+<td bgcolor="#000000" onmouseover="View('000000')" onclick="Set('000000')"></td>\r
+<td bgcolor="#0000FF" onmouseover="View('0000FF')" onclick="Set('0000FF')"></td>\r
+<td bgcolor="#0033FF" onmouseover="View('0033FF')" onclick="Set('0033FF')"></td>\r
+<td bgcolor="#0066FF" onmouseover="View('0066FF')" onclick="Set('0066FF')"></td>\r
+<td bgcolor="#0099FF" onmouseover="View('0099FF')" onclick="Set('0099FF')"></td>\r
+<td bgcolor="#00CCFF" onmouseover="View('00CCFF')" onclick="Set('00CCFF')"></td>\r
+<td bgcolor="#00FFFF" onmouseover="View('00FFFF')" onclick="Set('00FFFF')"></td>\r
+<td bgcolor="#3300FF" onmouseover="View('3300FF')" onclick="Set('3300FF')"></td>\r
+<td bgcolor="#3333FF" onmouseover="View('3333FF')" onclick="Set('3333FF')"></td>\r
+<td bgcolor="#3366FF" onmouseover="View('3366FF')" onclick="Set('3366FF')"></td>\r
+<td bgcolor="#3399FF" onmouseover="View('3399FF')" onclick="Set('3399FF')"></td>\r
+<td bgcolor="#33CCFF" onmouseover="View('33CCFF')" onclick="Set('33CCFF')"></td>\r
+<td bgcolor="#33FFFF" onmouseover="View('33FFFF')" onclick="Set('33FFFF')"></td>\r
+<td bgcolor="#6600FF" onmouseover="View('6600FF')" onclick="Set('6600FF')"></td>\r
+<td bgcolor="#6633FF" onmouseover="View('6633FF')" onclick="Set('6633FF')"></td>\r
+<td bgcolor="#6666FF" onmouseover="View('6666FF')" onclick="Set('6666FF')"></td>\r
+<td bgcolor="#6699FF" onmouseover="View('6699FF')" onclick="Set('6699FF')"></td>\r
+<td bgcolor="#66CCFF" onmouseover="View('66CCFF')" onclick="Set('66CCFF')"></td>\r
+<td bgcolor="#66FFFF" onmouseover="View('66FFFF')" onclick="Set('66FFFF')"></td>\r
+</tr>\r
+<tr>\r
+<td bgcolor="#000000" onmouseover="View('000000')" onclick="Set('000000')"></td>\r
+<td bgcolor="#FF0000" onmouseover="View('FF0000')" onclick="Set('FF0000')"></td>\r
+<td bgcolor="#000000" onmouseover="View('000000')" onclick="Set('000000')"></td>\r
+<td bgcolor="#990000" onmouseover="View('990000')" onclick="Set('990000')"></td>\r
+<td bgcolor="#993300" onmouseover="View('993300')" onclick="Set('993300')"></td>\r
+<td bgcolor="#996600" onmouseover="View('996600')" onclick="Set('996600')"></td>\r
+<td bgcolor="#999900" onmouseover="View('999900')" onclick="Set('999900')"></td>\r
+<td bgcolor="#99CC00" onmouseover="View('99CC00')" onclick="Set('99CC00')"></td>\r
+<td bgcolor="#99FF00" onmouseover="View('99FF00')" onclick="Set('99FF00')"></td>\r
+<td bgcolor="#CC0000" onmouseover="View('CC0000')" onclick="Set('CC0000')"></td>\r
+<td bgcolor="#CC3300" onmouseover="View('CC3300')" onclick="Set('CC3300')"></td>\r
+<td bgcolor="#CC6600" onmouseover="View('CC6600')" onclick="Set('CC6600')"></td>\r
+<td bgcolor="#CC9900" onmouseover="View('CC9900')" onclick="Set('CC9900')"></td>\r
+<td bgcolor="#CCCC00" onmouseover="View('CCCC00')" onclick="Set('CCCC00')"></td>\r
+<td bgcolor="#CCFF00" onmouseover="View('CCFF00')" onclick="Set('CCFF00')"></td>\r
+<td bgcolor="#FF0000" onmouseover="View('FF0000')" onclick="Set('FF0000')"></td>\r
+<td bgcolor="#FF3300" onmouseover="View('FF3300')" onclick="Set('FF3300')"></td>\r
+<td bgcolor="#FF6600" onmouseover="View('FF6600')" onclick="Set('FF6600')"></td>\r
+<td bgcolor="#FF9900" onmouseover="View('FF9900')" onclick="Set('FF9900')"></td>\r
+<td bgcolor="#FFCC00" onmouseover="View('FFCC00')" onclick="Set('FFCC00')"></td>\r
+<td bgcolor="#FFFF00" onmouseover="View('FFFF00')" onclick="Set('FFFF00')"></td>\r
+</tr>\r
+<tr>\r
+<td bgcolor="#000000" onmouseover="View('000000')" onclick="Set('000000')"></td>\r
+<td bgcolor="#00FF00" onmouseover="View('00FF00')" onclick="Set('00FF00')"></td>\r
+<td bgcolor="#000000" onmouseover="View('000000')" onclick="Set('000000')"></td>\r
+<td bgcolor="#990033" onmouseover="View('990033')" onclick="Set('990033')"></td>\r
+<td bgcolor="#993333" onmouseover="View('993333')" onclick="Set('993333')"></td>\r
+<td bgcolor="#996633" onmouseover="View('996633')" onclick="Set('996633')"></td>\r
+<td bgcolor="#999933" onmouseover="View('999933')" onclick="Set('999933')"></td>\r
+<td bgcolor="#99CC33" onmouseover="View('99CC33')" onclick="Set('99CC33')"></td>\r
+<td bgcolor="#99FF33" onmouseover="View('99FF33')" onclick="Set('99FF33')"></td>\r
+<td bgcolor="#CC0033" onmouseover="View('CC0033')" onclick="Set('CC0033')"></td>\r
+<td bgcolor="#CC3333" onmouseover="View('CC3333')" onclick="Set('CC3333')"></td>\r
+<td bgcolor="#CC6633" onmouseover="View('CC6633')" onclick="Set('CC6633')"></td>\r
+<td bgcolor="#CC9933" onmouseover="View('CC9933')" onclick="Set('CC9933')"></td>\r
+<td bgcolor="#CCCC33" onmouseover="View('CCCC33')" onclick="Set('CCCC33')"></td>\r
+<td bgcolor="#CCFF33" onmouseover="View('CCFF33')" onclick="Set('CCFF33')"></td>\r
+<td bgcolor="#FF0033" onmouseover="View('FF0033')" onclick="Set('FF0033')"></td>\r
+<td bgcolor="#FF3333" onmouseover="View('FF3333')" onclick="Set('FF3333')"></td>\r
+<td bgcolor="#FF6633" onmouseover="View('FF6633')" onclick="Set('FF6633')"></td>\r
+<td bgcolor="#FF9933" onmouseover="View('FF9933')" onclick="Set('FF9933')"></td>\r
+<td bgcolor="#FFCC33" onmouseover="View('FFCC33')" onclick="Set('FFCC33')"></td>\r
+<td bgcolor="#FFFF33" onmouseover="View('FFFF33')" onclick="Set('FFFF33')"></td>\r
+</tr>\r
+<tr>\r
+<td bgcolor="#000000" onmouseover="View('000000')" onclick="Set('000000')"></td>\r
+<td bgcolor="#0000FF" onmouseover="View('0000FF')" onclick="Set('0000FF')"></td>\r
+<td bgcolor="#000000" onmouseover="View('000000')" onclick="Set('000000')"></td>\r
+<td bgcolor="#990066" onmouseover="View('990066')" onclick="Set('990066')"></td>\r
+<td bgcolor="#993366" onmouseover="View('993366')" onclick="Set('993366')"></td>\r
+<td bgcolor="#996666" onmouseover="View('996666')" onclick="Set('996666')"></td>\r
+<td bgcolor="#999966" onmouseover="View('999966')" onclick="Set('999966')"></td>\r
+<td bgcolor="#99CC66" onmouseover="View('99CC66')" onclick="Set('99CC66')"></td>\r
+<td bgcolor="#99FF66" onmouseover="View('99FF66')" onclick="Set('99FF66')"></td>\r
+<td bgcolor="#CC0066" onmouseover="View('CC0066')" onclick="Set('CC0066')"></td>\r
+<td bgcolor="#CC3366" onmouseover="View('CC3366')" onclick="Set('CC3366')"></td>\r
+<td bgcolor="#CC6666" onmouseover="View('CC6666')" onclick="Set('CC6666')"></td>\r
+<td bgcolor="#CC9966" onmouseover="View('CC9966')" onclick="Set('CC9966')"></td>\r
+<td bgcolor="#CCCC66" onmouseover="View('CCCC66')" onclick="Set('CCCC66')"></td>\r
+<td bgcolor="#CCFF66" onmouseover="View('CCFF66')" onclick="Set('CCFF66')"></td>\r
+<td bgcolor="#FF0066" onmouseover="View('FF0066')" onclick="Set('FF0066')"></td>\r
+<td bgcolor="#FF3366" onmouseover="View('FF3366')" onclick="Set('FF3366')"></td>\r
+<td bgcolor="#FF6666" onmouseover="View('FF6666')" onclick="Set('FF6666')"></td>\r
+<td bgcolor="#FF9966" onmouseover="View('FF9966')" onclick="Set('FF9966')"></td>\r
+<td bgcolor="#FFCC66" onmouseover="View('FFCC66')" onclick="Set('FFCC66')"></td>\r
+<td bgcolor="#FFFF66" onmouseover="View('FFFF66')" onclick="Set('FFFF66')"></td>\r
+</tr>\r
+<tr>\r
+<td bgcolor="#000000" onmouseover="View('000000')" onclick="Set('000000')"></td>\r
+<td bgcolor="#FFFF00" onmouseover="View('FFFF00')" onclick="Set('FFFF00')"></td>\r
+<td bgcolor="#000000" onmouseover="View('000000')" onclick="Set('000000')"></td>\r
+<td bgcolor="#990099" onmouseover="View('990099')" onclick="Set('990099')"></td>\r
+<td bgcolor="#993399" onmouseover="View('993399')" onclick="Set('993399')"></td>\r
+<td bgcolor="#996699" onmouseover="View('996699')" onclick="Set('996699')"></td>\r
+<td bgcolor="#999999" onmouseover="View('999999')" onclick="Set('999999')"></td>\r
+<td bgcolor="#99CC99" onmouseover="View('99CC99')" onclick="Set('99CC99')"></td>\r
+<td bgcolor="#99FF99" onmouseover="View('99FF99')" onclick="Set('99FF99')"></td>\r
+<td bgcolor="#CC0099" onmouseover="View('CC0099')" onclick="Set('CC0099')"></td>\r
+<td bgcolor="#CC3399" onmouseover="View('CC3399')" onclick="Set('CC3399')"></td>\r
+<td bgcolor="#CC6699" onmouseover="View('CC6699')" onclick="Set('CC6699')"></td>\r
+<td bgcolor="#CC9999" onmouseover="View('CC9999')" onclick="Set('CC9999')"></td>\r
+<td bgcolor="#CCCC99" onmouseover="View('CCCC99')" onclick="Set('CCCC99')"></td>\r
+<td bgcolor="#CCFF99" onmouseover="View('CCFF99')" onclick="Set('CCFF99')"></td>\r
+<td bgcolor="#FF0099" onmouseover="View('FF0099')" onclick="Set('FF0099')"></td>\r
+<td bgcolor="#FF3399" onmouseover="View('FF3399')" onclick="Set('FF3399')"></td>\r
+<td bgcolor="#FF6699" onmouseover="View('FF6699')" onclick="Set('FF6699')"></td>\r
+<td bgcolor="#FF9999" onmouseover="View('FF9999')" onclick="Set('FF9999')"></td>\r
+<td bgcolor="#FFCC99" onmouseover="View('FFCC99')" onclick="Set('FFCC99')"></td>\r
+<td bgcolor="#FFFF99" onmouseover="View('FFFF99')" onclick="Set('FFFF99')"></td>\r
+</tr>\r
+<tr>\r
+<td bgcolor="#000000" onmouseover="View('000000')" onclick="Set('000000')"></td>\r
+<td bgcolor="#00FFFF" onmouseover="View('00FFFF')" onclick="Set('00FFFF')"></td>\r
+<td bgcolor="#000000" onmouseover="View('000000')" onclick="Set('000000')"></td>\r
+<td bgcolor="#9900CC" onmouseover="View('9900CC')" onclick="Set('9900CC')"></td>\r
+<td bgcolor="#9933CC" onmouseover="View('9933CC')" onclick="Set('9933CC')"></td>\r
+<td bgcolor="#9966CC" onmouseover="View('9966CC')" onclick="Set('9966CC')"></td>\r
+<td bgcolor="#9999CC" onmouseover="View('9999CC')" onclick="Set('9999CC')"></td>\r
+<td bgcolor="#99CCCC" onmouseover="View('99CCCC')" onclick="Set('99CCCC')"></td>\r
+<td bgcolor="#99FFCC" onmouseover="View('99FFCC')" onclick="Set('99FFCC')"></td>\r
+<td bgcolor="#CC00CC" onmouseover="View('CC00CC')" onclick="Set('CC00CC')"></td>\r
+<td bgcolor="#CC33CC" onmouseover="View('CC33CC')" onclick="Set('CC33CC')"></td>\r
+<td bgcolor="#CC66CC" onmouseover="View('CC66CC')" onclick="Set('CC66CC')"></td>\r
+<td bgcolor="#CC99CC" onmouseover="View('CC99CC')" onclick="Set('CC99CC')"></td>\r
+<td bgcolor="#CCCCCC" onmouseover="View('CCCCCC')" onclick="Set('CCCCCC')"></td>\r
+<td bgcolor="#CCFFCC" onmouseover="View('CCFFCC')" onclick="Set('CCFFCC')"></td>\r
+<td bgcolor="#FF00CC" onmouseover="View('FF00CC')" onclick="Set('FF00CC')"></td>\r
+<td bgcolor="#FF33CC" onmouseover="View('FF33CC')" onclick="Set('FF33CC')"></td>\r
+<td bgcolor="#FF66CC" onmouseover="View('FF66CC')" onclick="Set('FF66CC')"></td>\r
+<td bgcolor="#FF99CC" onmouseover="View('FF99CC')" onclick="Set('FF99CC')"></td>\r
+<td bgcolor="#FFCCCC" onmouseover="View('FFCCCC')" onclick="Set('FFCCCC')"></td>\r
+<td bgcolor="#FFFFCC" onmouseover="View('FFFFCC')" onclick="Set('FFFFCC')"></td>\r
+</tr>\r
+<tr>\r
+<td bgcolor="#000000" onmouseover="View('000000')" onclick="Set('000000')"></td>\r
+<td bgcolor="#FF00FF" onmouseover="View('FF00FF')" onclick="Set('FF00FF')"></td>\r
+<td bgcolor="#000000" onmouseover="View('000000')" onclick="Set('000000')"></td>\r
+<td bgcolor="#9900FF" onmouseover="View('9900FF')" onclick="Set('9900FF')"></td>\r
+<td bgcolor="#9933FF" onmouseover="View('9933FF')" onclick="Set('9933FF')"></td>\r
+<td bgcolor="#9966FF" onmouseover="View('9966FF')" onclick="Set('9966FF')"></td>\r
+<td bgcolor="#9999FF" onmouseover="View('9999FF')" onclick="Set('9999FF')"></td>\r
+<td bgcolor="#99CCFF" onmouseover="View('99CCFF')" onclick="Set('99CCFF')"></td>\r
+<td bgcolor="#99FFFF" onmouseover="View('99FFFF')" onclick="Set('99FFFF')"></td>\r
+<td bgcolor="#CC00FF" onmouseover="View('CC00FF')" onclick="Set('CC00FF')"></td>\r
+<td bgcolor="#CC33FF" onmouseover="View('CC33FF')" onclick="Set('CC33FF')"></td>\r
+<td bgcolor="#CC66FF" onmouseover="View('CC66FF')" onclick="Set('CC66FF')"></td>\r
+<td bgcolor="#CC99FF" onmouseover="View('CC99FF')" onclick="Set('CC99FF')"></td>\r
+<td bgcolor="#CCCCFF" onmouseover="View('CCCCFF')" onclick="Set('CCCCFF')"></td>\r
+<td bgcolor="#CCFFFF" onmouseover="View('CCFFFF')" onclick="Set('CCFFFF')"></td>\r
+<td bgcolor="#FF00FF" onmouseover="View('FF00FF')" onclick="Set('FF00FF')"></td>\r
+<td bgcolor="#FF33FF" onmouseover="View('FF33FF')" onclick="Set('FF33FF')"></td>\r
+<td bgcolor="#FF66FF" onmouseover="View('FF66FF')" onclick="Set('FF66FF')"></td>\r
+<td bgcolor="#FF99FF" onmouseover="View('FF99FF')" onclick="Set('FF99FF')"></td>\r
+<td bgcolor="#FFCCFF" onmouseover="View('FFCCFF')" onclick="Set('FFCCFF')"></td>\r
+<td bgcolor="#FFFFFF" onmouseover="View('FFFFFF')" onclick="Set('FFFFFF')"></td>\r
+</tr>\r
+</table>\r
+\r
+</body></html>
\ No newline at end of file
diff --git a/mailboxes/xinha/release-notes.txt b/mailboxes/xinha/release-notes.txt
new file mode 100644 (file)
index 0000000..30611b3
--- /dev/null
@@ -0,0 +1,63 @@
+Xinha - The Community-Built Online HTML Editor\r
+\r
+For bug reports, forum, and updates go to \r
+http://xinha.org\r
+\r
+Release History\r
+\r
+Xinha 0.92beta\r
+ - fixed Ticket #831 stop empty anchors from being removed\r
+ - Make htmlarea.js just load XinhaCore.js instead of being a copy of it.\r
+ - fixed Ticket #445 Toggle TableBorder doesn't work if this.fullpage=true or using FullPage plugin\r
+ - fixed Ticket #551 shape and coord attributes of area element are ignored by IE\r
+ - fixed Ticket #650 SpellChecker is not compatible with FullPage plugin\r
+ - fixed Ticket #816 CharacterMap and ListType panel modes make editor jump to top in IE\r
+ - fixed Ticket #570 change behaviour of justify when an image is selected to make the image float left/right, \r
+   click the justify button again to undo it\r
+ - fixed Ticket #891 noscript content got escaped\r
+ - fixed Ticket #857 Firefox fails to load in fullpage mode when <link ..> tag in wrong place.\r
+ - fixed Ticket #841 Spell Checker - Won't return to xinha.\r
+ - fixed Ticket #914 All buttons stay grey & this.getParentElement is not a function message when not calling\r
+   Xinha.loadPlugins() in xinha_init function\r
+ - fixed Ticket #911 Background color does not work in Firefox\r
+ - fixed Ticket #912 an error is thrown in Mozilla when clicking merge cells button and fewer than 2 cells are selected\r
+ - fixed Ticket #816 CharacterMap and ListType panel modes make editor jump to top in IE\r
+ - fixed Ticket #917 getHTMLWrapper in IE renders attributes like _stylist_usedtobe="[object Object]"\r
+ - fixed Ticket #556 stripBaseHref didn't work without baseHref defined explicitly.\r
+ - Update InsertPicture plugin Update\r
+ - fixed Ticket #921 EFM extended file manager - iframe height problem (IE)\r
+ - Ticket #923 colorPicker face lift & localisation\r
+ - fixed Ticket #924 EFM + ImageManager re-transforming of url to original image after automatic resize broken\r
+ - Ticket #900- retain editing position position between modes\r
+ - fixed Ticket #928 ImageManager fails if (another) Files.php exists in include path\r
+ - fixed Ticket #935 EFM demo_images php upload allowed: possible security risk\r
+ - Ticket #939 Japanese langage support \r
+ - fixed Ticket #3 Horizontal Rule Selection\r
+ - Plugin ExtendedFileManager: added doc to allowed upload extensions\r
+ - Plugin PasteText update\r
+ - Plugin HtmlEntities: default preset iso-8859-1\r
+ - fixed Ticket #948 IE: inline styles are not rendered by DOMwalk getHTML()\r
+ - Plugin HorizontalRule update\r
+ - Plugin SaveSubmit update\r
+ - Plugin Linker update\r
+ - fixed Ticket #823 editor contents was not submitted when using JavaScript form.submit() without prior form.onsubmit()\r
+ - fixed Ticket #459 all body attributes removed in full page mode in FireFox\r
+ - _editor_url is now converted to absolute if it is relative. \r
+ - fixed Ticket #594 IE: insertHTML() inserts HTML outside editor when editor is not focussed\r
+ - Ticket #954 FullScreen mode can now be called programmatically or on startup with or without button\r
+ - fixed Ticket #321 FullScreen : select elements show through in IE6 \r
+ - fixed Ticket #953 Statusbar has ugly white gap at the bottom when switching back from full screen in IE \r
+ - fixed Ticket #952 FullScreen: scrollbars don't disappear in IE in QuirksMode\r
+ - fixed Ticket #603 Popop Dialog Height problems\r
+ - fixed Ticket #955 DOMwalk getHTML outputs empty attribute with value "true" instead of attribute name and some are skipped at all\r
\r
+Xinha 0.91beta\r
+ - changed namespace from HTMLArea to Xinha\r
+ - the core file is now named XinhaCore.js instead of htmlarea.js,\r
+   please change your pages accordingly (the htmlarea.js will be removed!)\r
+ - the color picker script is now loaded on demand to reduce initial loading time\r
\r
+Xinha 0.9beta\r
+This equals Xinha revision 635 of Jan 11 2007\r
+ - All JavaScript files over 2kb have been compressed using the dojo JavaScript compressor\r
+ - All gifs have been optimized using Trout's GIF Optimizer version 2.3
\ No newline at end of file