From 414389951e28d88e195aaf5e23316a39523b354a Mon Sep 17 00:00:00 2001 From: lars Date: Mon, 6 Nov 2006 16:05:00 +0000 Subject: [PATCH] moved pythonrewrite branch to trunk --- {bin => bin-perl-old}/Makefile | 0 {bin => bin-perl-old}/cbox-manage.sh | 0 {bin => bin-perl-old}/cbox-root-actions.sh | 0 {bin => bin-perl-old}/cryptobox.pl | 2 +- {bin => bin-perl-old}/cryptobox_wrapper.c | 0 {bin => bin-perl-old}/ro-system.sh | 0 bin/CryptoBox.py | 276 + bin/CryptoBoxContainer.py | 607 + bin/CryptoBoxExceptions.py | 107 + bin/CryptoBoxPlugin.py | 165 + bin/CryptoBoxRootActions.py | 386 + bin/CryptoBoxSettings.py | 481 + bin/CryptoBoxTools.py | 186 + bin/CryptoBoxWebserver.py | 38 + bin/Plugins.py | 67 + bin/WebInterfaceDataset.py | 136 + bin/WebInterfaceSites.py | 427 + bin/WebInterfaceTestClass.py | 77 + bin/coding_guidelines.txt | 18 + bin/cryptobox.conf | 83 + bin/cryptoboxd | 39 + bin/cryptoboxwebserver.conf | 17 + bin/do_unittests.sh | 22 + bin/example-super.tab | 2 + bin/test.complete.CryptoBox.py | 116 + bin/uml-setup.sh | 23 + bin/unittests.CryptoBox.py | 138 + bin/unittests.CryptoBoxTools.py | 48 + bin/unittests.Plugins.py | 33 + bin/unittests.WebSites.py | 39 + debian/README.Debian | 3 +- debian/control | 3 +- debian/rules | 7 +- design/background_frame_corner.svg | 265 + design/icon_background_active.svg | 92 + design/icons/applications-system_tango.svg | 245 + design/icons/computer_tango.svg | 738 + design/icons/dialog-error_tango.svg | 316 + design/icons/dialog-information_tango.svg | 1145 + design/icons/dialog-warning_tango.svg | 290 + design/icons/drive-cdrom_tango.svg | 444 + design/icons/drive-harddisk_tango.svg | 469 + design/icons/drive-removable-media_tango.svg | 390 + design/icons/globe-lips.svg | 512 + .../icons/gnome-dev-removable-usb_nuvola.svg | 1004 + design/icons/gnome-globe_nuvola.svg | 1195 + design/icons/gtk-zoom-in_nuvola.svg | 433 + design/icons/help_contents.svg | 701 + .../inaccessible_tango_emblem-unreadable.svg | 357 + design/icons/language.png | Bin 0 -> 3520 bytes design/icons/locked_tango-emblem-readonly.svg | 298 + .../icons/multimedia-dell-dj-pocket_tango.svg | 4405 ++++ ...multimedia-player-ipod-mini-blue_tango.svg | 4126 +++ .../multimedia-player-motorola-rokr_tango.svg | 1025 + .../icons/network-transmit-receive_design.svg | 1041 + design/icons/pile_of_devices.png | Bin 0 -> 17218 bytes design/icons/pile_of_devices.svg | 22002 ++++++++++++++++ .../preferences-desktop-locale_tango.svg | 828 + design/icons/preferences-system_tango.svg | 396 + design/icons/redhat-config-users_wasp.svg | 1904 ++ design/icons/seahorse-preferences_gnome.svg | 1250 + design/icons/spherecrystal_help.svg | 60 + design/icons/system-log-out_tango.svg | 362 + design/icons/unlocked_clavdia.svg | 515 + design/icons/unlocked_lars.svg | 1198 + doc/html/fr | 1 + doc/html/si | 1 + known_problems | 4 + lang/README | 31 +- lang/TODO | 2 + lang/de.hdf | 403 +- lang/en.hdf | 436 +- lang/fr.hdf | 476 +- lang/language_specification.txt | 24 + lang/si.hdf | 402 +- plugins/date/date.py | 69 + plugins/date/form_date.cs | 44 + plugins/date/lang/en.hdf | 35 + plugins/date/plugin_icon.png | Bin 0 -> 3285 bytes plugins/date/root_action.py | 36 + plugins/date/unittests.py | 60 + plugins/disks/disks.cs | 17 + plugins/disks/disks.py | 17 + plugins/disks/lang/de.hdf | 6 + plugins/disks/lang/en.hdf | 6 + plugins/disks/plugin_icon.png | Bin 0 -> 6279 bytes plugins/disks/unittests.py | 9 + plugins/format_fs/format_fs.py | 95 + plugins/format_fs/lang/en.hdf | 49 + plugins/format_fs/plugin_icon.png | Bin 0 -> 6376 bytes plugins/format_fs/unittests.py | 10 + plugins/format_fs/volume_format.cs | 37 + plugins/format_fs/volume_format_luks.cs | 32 + plugins/help/doc.cs | 9 + plugins/help/help.py | 25 + plugins/help/lang/en.hdf | 5 + plugins/help/plugin_icon.png | Bin 0 -> 12693 bytes plugins/help/unittests.py | 29 + plugins/language_selection/lang/en.hdf | 5 + .../language_selection/language_selection.cs | 15 + .../language_selection/language_selection.py | 16 + plugins/language_selection/plugin_icon.png | Bin 0 -> 13094 bytes plugins/language_selection/unittests.py | 10 + plugins/logs/lang/en.hdf | 6 + plugins/logs/logs.css | 6 + plugins/logs/logs.py | 29 + plugins/logs/plugin_icon.png | Bin 0 -> 16601 bytes plugins/logs/show_log.cs | 19 + plugins/logs/unittests.py | 21 + plugins/network/form_network.cs | 30 + plugins/network/lang/en.hdf | 23 + plugins/network/network.py | 126 + plugins/network/plugin_icon.png | Bin 0 -> 13698 bytes plugins/network/root_action.py | 42 + plugins/network/unittests.py | 45 + plugins/partition/current_partition_info.cs | 11 + plugins/partition/lang/en.hdf | 83 + plugins/partition/partition.css | 4 + plugins/partition/partition.py | 416 + plugins/partition/plugin_icon.png | Bin 0 -> 2943 bytes plugins/partition/root_action.py | 96 + plugins/partition/select_device.cs | 45 + plugins/partition/set_partitions.cs | 78 + plugins/partition/show_format_progress.cs | 17 + plugins/partition/unittests.py | 10 + plugins/plugin-interface.txt | 63 + plugins/plugin_icon_unknown.png | Bin 0 -> 14269 bytes plugins/plugin_manager/lang/en.hdf | 15 + plugins/plugin_manager/plugin_icon.png | Bin 0 -> 631 bytes plugins/plugin_manager/plugin_list.cs | 65 + plugins/plugin_manager/plugin_manager.py | 52 + plugins/plugin_manager/unittests.py | 12 + plugins/shutdown/form_shutdown.cs | 15 + plugins/shutdown/gnome-reboot.png | Bin 0 -> 3752 bytes plugins/shutdown/gnome-shutdown.png | Bin 0 -> 4532 bytes plugins/shutdown/lang/en.hdf | 34 + plugins/shutdown/plugin_icon.png | Bin 0 -> 7588 bytes plugins/shutdown/progress_reboot.cs | 6 + plugins/shutdown/progress_shutdown.cs | 6 + plugins/shutdown/root_action.py | 48 + plugins/shutdown/shutdown.py | 51 + plugins/shutdown/unittests.py | 11 + plugins/system_preferences/lang/en.hdf | 5 + plugins/system_preferences/plugin_icon.png | Bin 0 -> 12762 bytes plugins/system_preferences/show_plugins.cs | 15 + .../system_preferences/system_preferences.py | 16 + plugins/system_preferences/unittests.py | 8 + plugins/user_manager/lang/en.hdf | 51 + plugins/user_manager/plugin_icon.png | Bin 0 -> 10613 bytes plugins/user_manager/unittests.py | 27 + plugins/user_manager/user_list.cs | 82 + plugins/user_manager/user_manager.py | 81 + plugins/volume_details/lang/en.hdf | 20 + plugins/volume_details/plugin_icon.png | Bin 0 -> 11235 bytes plugins/volume_details/unittests.py | 10 + plugins/volume_details/volume_details.cs | 21 + plugins/volume_details/volume_details.py | 18 + plugins/volume_mount/lang/en.hdf | 56 + plugins/volume_mount/plugin_icon.png | Bin 0 -> 4535 bytes plugins/volume_mount/unittests.py | 10 + plugins/volume_mount/volume_mount.cs | 18 + plugins/volume_mount/volume_mount.py | 103 + plugins/volume_mount/volume_status.cs | 9 + plugins/volume_mount/volume_umount.cs | 10 + plugins/volume_props/lang/en.hdf | 63 + plugins/volume_props/plugin_icon.png | Bin 0 -> 15249 bytes plugins/volume_props/unittests.py | 10 + plugins/volume_props/volume_properties.cs | 75 + plugins/volume_props/volume_props.py | 81 + scripts/check_languages.py | 106 + scripts/check_languages.sh | 26 - scripts/userdocexport.sh | 2 +- templates/access_denied.cs | 6 + templates/empty.cs | 3 + templates/error.cs | 3 - templates/footer.cs | 35 +- templates/form_config.cs | 34 - templates/form_init.cs | 27 - templates/form_init_partition.cs | 33 - templates/form_mount.cs | 40 - templates/form_system.cs | 29 - templates/form_umount.cs | 37 - templates/header.cs | 67 +- templates/macros.cs | 204 +- templates/main.cs | 10 +- templates/nav.cs | 26 - templates/show_doc.cs | 7 - templates/show_log.cs | 13 - templates/show_status.cs | 28 - templates/show_volume.cs | 73 +- templates/show_volume_footer.cs | 3 + templates/show_volume_header.cs | 21 + templates/show_volumes.cs | 15 - templates/volume_plugins.cs | 25 + www-data/background_frame_corner.png | Bin 0 -> 2355 bytes www-data/background_frame_top.png | Bin 0 -> 2109 bytes www-data/cryptobox.css | 532 +- www-data/dialog-error_tango.png | Bin 0 -> 8750 bytes www-data/dialog-information_tango.png | Bin 0 -> 14120 bytes www-data/dialog-warning_tango.png | Bin 0 -> 7611 bytes www-data/disc_gray.png | Bin 0 -> 4524 bytes www-data/disc_green.png | Bin 4752 -> 4936 bytes www-data/disc_red.png | Bin 4804 -> 4988 bytes www-data/evil_stick.png | Bin 0 -> 32626 bytes www-data/footer_line.png | Bin 0 -> 262 bytes www-data/icon_background_active.png | Bin 0 -> 14372 bytes www-data/icon_background_active_060.png | Bin 0 -> 1845 bytes www-data/icon_background_active_080.png | Bin 0 -> 2891 bytes www-data/icon_background_active_100.png | Bin 0 -> 4101 bytes www-data/icon_background_active_256.png | Bin 0 -> 14372 bytes www-data/icon_background_passive_060.png | Bin 0 -> 1570 bytes www-data/icon_background_passive_080.png | Bin 0 -> 2338 bytes www-data/icon_background_passive_100.png | Bin 0 -> 3292 bytes www-data/pane_bottom_left.png | Bin 0 -> 249 bytes www-data/pane_bottom_right.png | Bin 0 -> 249 bytes www-data/pane_side_bottom.png | Bin 0 -> 131 bytes www-data/pane_side_left.png | Bin 0 -> 131 bytes www-data/pane_side_right.png | Bin 0 -> 131 bytes www-data/pane_side_top.png | Bin 0 -> 133 bytes www-data/pane_top_left.png | Bin 0 -> 249 bytes www-data/pane_top_right.png | Bin 0 -> 273 bytes www-data/register_active.png | Bin 0 -> 560 bytes www-data/register_active2.png | Bin 0 -> 1078 bytes www-data/register_passive.png | Bin 0 -> 523 bytes www-data/register_passive2.png | Bin 0 -> 1087 bytes www-data/volume_active_crypto.png | Bin 0 -> 6590 bytes www-data/volume_active_plain.png | Bin 0 -> 5903 bytes www-data/volume_passive_crypto.png | Bin 0 -> 8140 bytes www-data/volume_passive_plain.png | Bin 0 -> 7661 bytes www-data/volume_property_frame.png | Bin 0 -> 6389 bytes 230 files changed, 56027 insertions(+), 1620 deletions(-) rename {bin => bin-perl-old}/Makefile (100%) rename {bin => bin-perl-old}/cbox-manage.sh (100%) rename {bin => bin-perl-old}/cbox-root-actions.sh (100%) rename {bin => bin-perl-old}/cryptobox.pl (99%) rename {bin => bin-perl-old}/cryptobox_wrapper.c (100%) rename {bin => bin-perl-old}/ro-system.sh (100%) create mode 100755 bin/CryptoBox.py create mode 100755 bin/CryptoBoxContainer.py create mode 100644 bin/CryptoBoxExceptions.py create mode 100644 bin/CryptoBoxPlugin.py create mode 100755 bin/CryptoBoxRootActions.py create mode 100644 bin/CryptoBoxSettings.py create mode 100644 bin/CryptoBoxTools.py create mode 100755 bin/CryptoBoxWebserver.py create mode 100644 bin/Plugins.py create mode 100644 bin/WebInterfaceDataset.py create mode 100755 bin/WebInterfaceSites.py create mode 100644 bin/WebInterfaceTestClass.py create mode 100644 bin/coding_guidelines.txt create mode 100644 bin/cryptobox.conf create mode 100755 bin/cryptoboxd create mode 100644 bin/cryptoboxwebserver.conf create mode 100755 bin/do_unittests.sh create mode 100644 bin/example-super.tab create mode 100755 bin/test.complete.CryptoBox.py create mode 100755 bin/uml-setup.sh create mode 100755 bin/unittests.CryptoBox.py create mode 100755 bin/unittests.CryptoBoxTools.py create mode 100755 bin/unittests.Plugins.py create mode 100755 bin/unittests.WebSites.py create mode 100644 design/background_frame_corner.svg create mode 100644 design/icon_background_active.svg create mode 100644 design/icons/applications-system_tango.svg create mode 100644 design/icons/computer_tango.svg create mode 100644 design/icons/dialog-error_tango.svg create mode 100644 design/icons/dialog-information_tango.svg create mode 100644 design/icons/dialog-warning_tango.svg create mode 100644 design/icons/drive-cdrom_tango.svg create mode 100644 design/icons/drive-harddisk_tango.svg create mode 100644 design/icons/drive-removable-media_tango.svg create mode 100644 design/icons/globe-lips.svg create mode 100644 design/icons/gnome-dev-removable-usb_nuvola.svg create mode 100644 design/icons/gnome-globe_nuvola.svg create mode 100644 design/icons/gtk-zoom-in_nuvola.svg create mode 100644 design/icons/help_contents.svg create mode 100644 design/icons/inaccessible_tango_emblem-unreadable.svg create mode 100644 design/icons/language.png create mode 100644 design/icons/locked_tango-emblem-readonly.svg create mode 100644 design/icons/multimedia-dell-dj-pocket_tango.svg create mode 100644 design/icons/multimedia-player-ipod-mini-blue_tango.svg create mode 100644 design/icons/multimedia-player-motorola-rokr_tango.svg create mode 100644 design/icons/network-transmit-receive_design.svg create mode 100644 design/icons/pile_of_devices.png create mode 100644 design/icons/pile_of_devices.svg create mode 100644 design/icons/preferences-desktop-locale_tango.svg create mode 100644 design/icons/preferences-system_tango.svg create mode 100644 design/icons/redhat-config-users_wasp.svg create mode 100644 design/icons/seahorse-preferences_gnome.svg create mode 100644 design/icons/spherecrystal_help.svg create mode 100644 design/icons/system-log-out_tango.svg create mode 100644 design/icons/unlocked_clavdia.svg create mode 100644 design/icons/unlocked_lars.svg create mode 120000 doc/html/fr create mode 120000 doc/html/si create mode 100644 known_problems create mode 100644 lang/language_specification.txt create mode 100644 plugins/date/date.py create mode 100644 plugins/date/form_date.cs create mode 100644 plugins/date/lang/en.hdf create mode 100644 plugins/date/plugin_icon.png create mode 100755 plugins/date/root_action.py create mode 100644 plugins/date/unittests.py create mode 100644 plugins/disks/disks.cs create mode 100644 plugins/disks/disks.py create mode 100644 plugins/disks/lang/de.hdf create mode 100644 plugins/disks/lang/en.hdf create mode 100644 plugins/disks/plugin_icon.png create mode 100644 plugins/disks/unittests.py create mode 100644 plugins/format_fs/format_fs.py create mode 100644 plugins/format_fs/lang/en.hdf create mode 100644 plugins/format_fs/plugin_icon.png create mode 100644 plugins/format_fs/unittests.py create mode 100644 plugins/format_fs/volume_format.cs create mode 100644 plugins/format_fs/volume_format_luks.cs create mode 100644 plugins/help/doc.cs create mode 100644 plugins/help/help.py create mode 100644 plugins/help/lang/en.hdf create mode 100644 plugins/help/plugin_icon.png create mode 100644 plugins/help/unittests.py create mode 100644 plugins/language_selection/lang/en.hdf create mode 100644 plugins/language_selection/language_selection.cs create mode 100644 plugins/language_selection/language_selection.py create mode 100644 plugins/language_selection/plugin_icon.png create mode 100644 plugins/language_selection/unittests.py create mode 100644 plugins/logs/lang/en.hdf create mode 100644 plugins/logs/logs.css create mode 100644 plugins/logs/logs.py create mode 100644 plugins/logs/plugin_icon.png create mode 100644 plugins/logs/show_log.cs create mode 100644 plugins/logs/unittests.py create mode 100644 plugins/network/form_network.cs create mode 100644 plugins/network/lang/en.hdf create mode 100644 plugins/network/network.py create mode 100644 plugins/network/plugin_icon.png create mode 100755 plugins/network/root_action.py create mode 100644 plugins/network/unittests.py create mode 100644 plugins/partition/current_partition_info.cs create mode 100644 plugins/partition/lang/en.hdf create mode 100644 plugins/partition/partition.css create mode 100644 plugins/partition/partition.py create mode 100644 plugins/partition/plugin_icon.png create mode 100755 plugins/partition/root_action.py create mode 100644 plugins/partition/select_device.cs create mode 100644 plugins/partition/set_partitions.cs create mode 100644 plugins/partition/show_format_progress.cs create mode 100644 plugins/partition/unittests.py create mode 100644 plugins/plugin-interface.txt create mode 100644 plugins/plugin_icon_unknown.png create mode 100644 plugins/plugin_manager/lang/en.hdf create mode 100644 plugins/plugin_manager/plugin_icon.png create mode 100644 plugins/plugin_manager/plugin_list.cs create mode 100644 plugins/plugin_manager/plugin_manager.py create mode 100644 plugins/plugin_manager/unittests.py create mode 100644 plugins/shutdown/form_shutdown.cs create mode 100644 plugins/shutdown/gnome-reboot.png create mode 100644 plugins/shutdown/gnome-shutdown.png create mode 100644 plugins/shutdown/lang/en.hdf create mode 100644 plugins/shutdown/plugin_icon.png create mode 100644 plugins/shutdown/progress_reboot.cs create mode 100644 plugins/shutdown/progress_shutdown.cs create mode 100755 plugins/shutdown/root_action.py create mode 100644 plugins/shutdown/shutdown.py create mode 100644 plugins/shutdown/unittests.py create mode 100644 plugins/system_preferences/lang/en.hdf create mode 100644 plugins/system_preferences/plugin_icon.png create mode 100644 plugins/system_preferences/show_plugins.cs create mode 100644 plugins/system_preferences/system_preferences.py create mode 100644 plugins/system_preferences/unittests.py create mode 100644 plugins/user_manager/lang/en.hdf create mode 100644 plugins/user_manager/plugin_icon.png create mode 100644 plugins/user_manager/unittests.py create mode 100644 plugins/user_manager/user_list.cs create mode 100644 plugins/user_manager/user_manager.py create mode 100644 plugins/volume_details/lang/en.hdf create mode 100644 plugins/volume_details/plugin_icon.png create mode 100644 plugins/volume_details/unittests.py create mode 100644 plugins/volume_details/volume_details.cs create mode 100644 plugins/volume_details/volume_details.py create mode 100644 plugins/volume_mount/lang/en.hdf create mode 100644 plugins/volume_mount/plugin_icon.png create mode 100644 plugins/volume_mount/unittests.py create mode 100644 plugins/volume_mount/volume_mount.cs create mode 100644 plugins/volume_mount/volume_mount.py create mode 100644 plugins/volume_mount/volume_status.cs create mode 100644 plugins/volume_mount/volume_umount.cs create mode 100644 plugins/volume_props/lang/en.hdf create mode 100644 plugins/volume_props/plugin_icon.png create mode 100644 plugins/volume_props/unittests.py create mode 100644 plugins/volume_props/volume_properties.cs create mode 100644 plugins/volume_props/volume_props.py create mode 100755 scripts/check_languages.py delete mode 100755 scripts/check_languages.sh create mode 100644 templates/access_denied.cs delete mode 100644 templates/error.cs delete mode 100644 templates/form_config.cs delete mode 100644 templates/form_init.cs delete mode 100644 templates/form_init_partition.cs delete mode 100644 templates/form_mount.cs delete mode 100644 templates/form_system.cs delete mode 100644 templates/form_umount.cs delete mode 100644 templates/nav.cs delete mode 100644 templates/show_doc.cs delete mode 100644 templates/show_log.cs delete mode 100644 templates/show_status.cs create mode 100644 templates/show_volume_footer.cs create mode 100644 templates/show_volume_header.cs delete mode 100644 templates/show_volumes.cs create mode 100644 templates/volume_plugins.cs create mode 100644 www-data/background_frame_corner.png create mode 100644 www-data/background_frame_top.png create mode 100644 www-data/dialog-error_tango.png create mode 100644 www-data/dialog-information_tango.png create mode 100644 www-data/dialog-warning_tango.png create mode 100644 www-data/disc_gray.png create mode 100644 www-data/evil_stick.png create mode 100644 www-data/footer_line.png create mode 100644 www-data/icon_background_active.png create mode 100644 www-data/icon_background_active_060.png create mode 100644 www-data/icon_background_active_080.png create mode 100644 www-data/icon_background_active_100.png create mode 100644 www-data/icon_background_active_256.png create mode 100644 www-data/icon_background_passive_060.png create mode 100644 www-data/icon_background_passive_080.png create mode 100644 www-data/icon_background_passive_100.png create mode 100644 www-data/pane_bottom_left.png create mode 100644 www-data/pane_bottom_right.png create mode 100644 www-data/pane_side_bottom.png create mode 100644 www-data/pane_side_left.png create mode 100644 www-data/pane_side_right.png create mode 100644 www-data/pane_side_top.png create mode 100644 www-data/pane_top_left.png create mode 100644 www-data/pane_top_right.png create mode 100644 www-data/register_active.png create mode 100644 www-data/register_active2.png create mode 100644 www-data/register_passive.png create mode 100644 www-data/register_passive2.png create mode 100644 www-data/volume_active_crypto.png create mode 100644 www-data/volume_active_plain.png create mode 100644 www-data/volume_passive_crypto.png create mode 100644 www-data/volume_passive_plain.png create mode 100644 www-data/volume_property_frame.png diff --git a/bin/Makefile b/bin-perl-old/Makefile similarity index 100% rename from bin/Makefile rename to bin-perl-old/Makefile diff --git a/bin/cbox-manage.sh b/bin-perl-old/cbox-manage.sh similarity index 100% rename from bin/cbox-manage.sh rename to bin-perl-old/cbox-manage.sh diff --git a/bin/cbox-root-actions.sh b/bin-perl-old/cbox-root-actions.sh similarity index 100% rename from bin/cbox-root-actions.sh rename to bin-perl-old/cbox-root-actions.sh diff --git a/bin/cryptobox.pl b/bin-perl-old/cryptobox.pl similarity index 99% rename from bin/cryptobox.pl rename to bin-perl-old/cryptobox.pl index 9c3a999..7f36806 100755 --- a/bin/cryptobox.pl +++ b/bin-perl-old/cryptobox.pl @@ -532,7 +532,7 @@ if ( ! &check_ssl()) { if ($device eq '') { &debug_msg(DEBUG_INFO, "invalid device: " . $query->param('device')); $pagedata->setValue('Data.Warning', 'InvalidDevice'); - $pagedata->setValue('Data.Action', 'empty'); + $pagedata->setValue('Data.Action', 'emptu'); } elsif ( ! &check_config()) { $pagedata->setValue('Data.Warning', 'NotInitialized'); $pagedata->setValue('Data.Action', 'form_init'); diff --git a/bin/cryptobox_wrapper.c b/bin-perl-old/cryptobox_wrapper.c similarity index 100% rename from bin/cryptobox_wrapper.c rename to bin-perl-old/cryptobox_wrapper.c diff --git a/bin/ro-system.sh b/bin-perl-old/ro-system.sh similarity index 100% rename from bin/ro-system.sh rename to bin-perl-old/ro-system.sh diff --git a/bin/CryptoBox.py b/bin/CryptoBox.py new file mode 100755 index 0000000..1472b53 --- /dev/null +++ b/bin/CryptoBox.py @@ -0,0 +1,276 @@ +#!/usr/bin/env python2.4 +''' +This is the web interface for a fileserver managing encrypted filesystems. + +It was originally written in bash/perl. Now a complete rewrite is in +progress. So things might be confusing here. Hopefully not for long. +:) +''' + +# check python version +import sys +(ver_major, ver_minor, ver_sub, ver_desc, ver_subsub) = sys.version_info +if (ver_major < 2) or ((ver_major == 2) and (ver_minor < 4)): + sys.stderr.write("You need a python version >= 2.4\nCurrent version is:\n %s\n" % sys.version) + sys.exit(1) + +import CryptoBoxContainer +from CryptoBoxExceptions import * +import re +import os +import CryptoBoxTools +import subprocess + + + +class CryptoBox: + '''this class rules them all! + + put things like logging, conf and oter stuff in here, + that might be used by more classes, it will be passed on to them''' + + VERSION = "0.3~1" + + def __init__(self, config_file=None): + import CryptoBoxSettings + self.log = self.__getStartupLogger() + self.prefs = CryptoBoxSettings.CryptoBoxSettings(config_file) + self.__runTests() + + + def __getStartupLogger(self): + import logging + '''initialises the logging system + + use it with: 'self.log.[debug|info|warning|error|critical](logmessage)' + all classes should get the logging instance during __init__: + self.log = logging.getLogger("CryptoBox") + + first we output all warnings/errors to stderr + as soon as we opened the config file successfully, we redirect debug output + to the configured destination''' + ## basicConfig(...) needs python >= 2.4 + try: + log_handler = logging.getLogger("CryptoBox") + logging.basicConfig( + format='%(asctime)s CryptoBox %(levelname)s: %(message)s', + stderr=sys.stderr) + log_handler.setLevel(logging.ERROR) + log_handler.info("loggingsystem is up'n running") + ## from now on everything can be logged via self.log... + except: + raise CBEnvironmentError("couldn't initialise the loggingsystem. I give up.") + return log_handler + + + # do some initial checks + def __runTests(self): + self.__runTestUID() + self.__runTestRootPriv() + + + def __runTestUID(self): + if os.geteuid() == 0: + raise CBEnvironmentError("you may not run the cryptobox as root") + + + def __runTestRootPriv(self): + """try to run 'super' with 'CryptoBoxRootActions'""" + try: + devnull = open(os.devnull, "w") + except IOError: + raise CBEnvironmentError("could not open %s for writing!" % os.devnull) + try: + prog_super = self.prefs["Programs"]["super"] + except KeyError: + raise CBConfigUndefinedError("Programs", "super") + try: + prog_rootactions = self.prefs["Programs"]["CryptoBoxRootActions"] + except KeyError: + raise CBConfigUndefinedError("Programs", "CryptoBoxRootActions") + try: + proc = subprocess.Popen( + shell = False, + stdout = devnull, + stderr = devnull, + args = [prog_super, prog_rootactions, "check"]) + except OSError: + raise CBEnvironmentError("failed to execute 'super' (%s)" % self.prefs["Programs"]["super"]) + proc.wait() + if proc.returncode != 0: + raise CBEnvironmentError("failed to call CryptoBoxRootActions (%s) via 'super' - maybe you did not add the appropriate line to /etc/super.tab?" % prog_rootactions) + + + # this method just demonstrates inheritance effects - may be removed + def cbx_inheritance_test(self, string="you lucky widow"): + self.log.info(string) + + +# RFC: why should CryptoBoxProps inherit CryptoBox? [l] +# RFC: shouldn't we move all useful functions of CryptoBoxProps to CryptoBox? [l] +class CryptoBoxProps(CryptoBox): + '''Get and set the properties of a CryptoBox + + This class contains all available devices that may be accessed. + All properties of the cryptobox can be accessed by this class. + ''' + + def __init__(self, config_file=None): + '''read config and fill class variables''' + CryptoBox.__init__(self, config_file) + self.reReadContainerList() + + + def reReadContainerList(self): + self.log.debug("rereading container list") + self.containers = [] + for device in CryptoBoxTools.getAvailablePartitions(): + if self.isDeviceAllowed(device) and not self.isConfigPartition(device): + self.containers.append(CryptoBoxContainer.CryptoBoxContainer(device, self)) + ## sort by container name + self.containers.sort(cmp = lambda x,y: x.getName() < y.getName() and -1 or 1) + + + def isConfigPartition(self, device): + proc = subprocess.Popen( + shell = False, + stdout = subprocess.PIPE, + args = [ + self.prefs["Programs"]["blkid"], + "-c", os.path.devnull, + "-o", "value", + "-s", "LABEL", + device]) + (output, error) = proc.communicate() + return output.strip() == self.prefs["Main"]["ConfigVolumeLabel"] + + + def isDeviceAllowed(self, devicename): + "check if a device is white-listed for being used as cryptobox containers" + import types + allowed = self.prefs["Main"]["AllowedDevices"] + if type(allowed) == types.StringType: allowed = [allowed] + for a_dev in allowed: + "remove double dots and so on ..." + real_device = os.path.realpath(devicename) + if a_dev and re.search('^' + a_dev, real_device): return True + return False + + + def getLogData(self, lines=None, maxSize=None): + """get the most recent log entries of the cryptobox + + the maximum number and size of these entries can be limited by 'lines' and 'maxSize' + """ + # return nothing if the currently selected log output is not a file + try: + if self.prefs["Log"]["Destination"].upper() != "FILE": return [] + log_file = self.prefs["Log"]["Details"] + except KeyError: + self.log.error("could not evaluate one of the following config settings: [Log]->Destination or [Log]->Details") + return [] + try: + fd = open(log_file, "r") + if maxSize: fd.seek(-maxSize, 2) # seek relative to the end of the file + content = fd.readlines() + fd.close() + except IOError: + self.log.warn("failed to read the log file (%s)" % log_file) + return [] + if lines: content = content[-lines:] + content.reverse() + return content + + + def getContainerList(self, filterType=None, filterName=None): + "retrieve the list of all containers of this cryptobox" + try: + result = self.containers[:] + if filterType != None: + if filterType in range(len(CryptoBoxContainer.Types)): + return [e for e in self.containers if e.getType() == filterType] + else: + self.log.info("invalid filterType (%d)" % filterType) + result.clear() + if filterName != None: + result = [e for e in self.containers if e.getName() == filterName] + return result + except AttributeError: + return [] + + + def getContainer(self, device): + "retrieve the container element for this device" + all = [e for e in self.getContainerList() if e.device == device] + if all: + return all[0] + else: + return None + + + def setNameForUUID(self, uuid, name): + "assign a name to a uuid in the ContainerNameDatabase" + used_uuid = self.getUUIDForName(name) + "first remove potential conflicting uuid/name combination" + if used_uuid: + ## remember the container which name was overriden + for e in self.containers: + if e.getName() == name: + forcedRename = e + break + del self.prefs.nameDB[used_uuid] + self.prefs.nameDB[uuid] = name + self.prefs.nameDB.write() + ## rename the container that lost its name (necessary while we use cherrypy) + if used_uuid: + ## this is surely not the best way to regenerate the name + dev = e.getDevice() + old_index = self.containers.index(e) + self.containers.remove(e) + self.containers.insert(old_index, CryptoBoxContainer.CryptoBoxContainer(dev,self)) + ## there should be no reason for any failure + return True + + + def getNameForUUID(self, uuid): + "get the name belonging to a specified key (usually the UUID of a fs)" + try: + return self.prefs.nameDB[uuid] + except KeyError: + return None + + + def getUUIDForName(self, name): + """ get the key belonging to a value in the ContainerNameDatabase + this is the reverse action of 'getNameForUUID' """ + for key in self.prefs.nameDB.keys(): + if self.prefs.nameDB[key] == name: return key + "the uuid was not found" + return None + + + def removeUUID(self, uuid): + if uuid in self.prefs.nameDB.keys(): + del self.prefs.nameDB[uuid] + return True + else: + return False + + + def getAvailableLanguages(self): + '''reads all files in path LangDir and returns a list of + basenames from existing hdf files, that should are all available + languages''' + languages = [ f.rstrip(".hdf") + for f in os.listdir(self.prefs["Locations"]["LangDir"]) + if f.endswith(".hdf") ] + if len(languages) < 1: + self.log.error("No .hdf files found! The website won't render properly.") + return languages + + + + +if __name__ == "__main__": + cb = CryptoBoxProps() + diff --git a/bin/CryptoBoxContainer.py b/bin/CryptoBoxContainer.py new file mode 100755 index 0000000..e658c53 --- /dev/null +++ b/bin/CryptoBoxContainer.py @@ -0,0 +1,607 @@ +#!/usr/bin/env python2.4 + +## check python version +import sys +(ver_major, ver_minor, ver_sub, ver_desc, ver_subsub) = sys.version_info +if (ver_major < 2) or ((ver_major == 2) and (ver_minor < 4)): + sys.stderr.write("You need a python version >= 2.4\nCurrent version is:\n %s\n" % sys.version) + sys.exit(1) + +import subprocess +import os +import re +import logging +from CryptoBoxExceptions import * + +"""exceptions: + VolumeIsActive + NameActivelyUsed + InvalidName + InvalidPassword + InvalidType + CreateError + MountError + ChangePasswordError + """ + +class CryptoBoxContainer: + + Types = { + "unused":0, + "plain":1, + "luks":2, + "swap":3} + + + __fsTypes = { + "plain":["ext3", "ext2", "vfat", "reiser"], + "swap":["swap"]} + # TODO: more filesystem types? / check 'reiser' + + __dmDir = "/dev/mapper" + + + def __init__(self, device, cbox): + self.device = device + self.cbox = cbox + self.log = logging.getLogger("CryptoBox") + self.resetObject() + + + def getName(self): + return self.name + + + def setName(self, new_name): + if new_name == self.name: return + if self.isMounted(): + raise CBVolumeIsActive("the container must be inactive during renaming") + if not re.search(r'^[a-zA-Z0-9_\.\- ]+$', new_name): + raise CBInvalidName("the supplied new name contains illegal characters") + "check for active partitions with the same name" + prev_name_owner = self.cbox.getContainerList(filterName=new_name) + if prev_name_owner: + for a in prev_name_owner: + if a.isMounted(): + raise CBNameActivelyUsed("the supplied new name is already in use for an active partition") + if not self.cbox.setNameForUUID(self.uuid, new_name): + raise CBContainerError("failed to change the volume name for unknown reasons") + self.name = new_name + + + def getDevice(self): + return self.device + + + def getType(self): + return self.type + + + def isMounted(self): + return os.path.ismount(self.__getMountPoint()) + + + def getCapacity(self): + """return the current capacity state of the volume + + the volume may not be mounted + the result is a tuple of values in megabyte: + (size, available, used) + """ + info = os.statvfs(self.__getMountPoint()) + return ( + int(info.f_bsize*info.f_blocks/1024/1024), + int(info.f_bsize*info.f_bavail/1024/1024), + int(info.f_bsize*(info.f_blocks-info.f_bavail)/1024/1024)) + + + def getSize(self): + """return the size of the block device (_not_ of the filesystem) + + the result is a value in megabyte + an error is indicated by "-1" + """ + import CryptoBoxTools + return CryptoBoxTools.getBlockDeviceSize(self.device) + + + def resetObject(self): + """ recheck the information about this container + this is especially useful after changing the type via 'create' """ + self.uuid = self.__getUUID() + self.type = self.__getTypeOfPartition() + self.name = self.__getNameOfContainer() + if self.type == self.Types["luks"]: + self.mount = self.__mountLuks + self.umount = self.__umountLuks + elif self.type == self.Types["plain"]: + self.mount = self.__mountPlain + self.umount = self.__umountPlain + + + def create(self, type, password=None): + old_name = self.getName() + if type == self.Types["luks"]: + self.__createLuks(password) + elif type == self.Types["plain"]: + self.__createPlain() + else: + raise CBInvalidType("invalid container type (%d) supplied" % (type, )) + ## no exception was raised during creation -> we can continue + ## reset the properties (encryption state, ...) of the device + self.resetObject() + ## restore the old name (must be after resetObject) + self.setName(old_name) + + + def changePassword(self, oldpw, newpw): + if self.type != self.Types["luks"]: + raise CBInvalidType("changing of password is possible only for luks containers") + if not oldpw: + raise CBInvalidPassword("no old password supplied for password change") + if not newpw: + raise CBInvalidPassword("no new password supplied for password change") + "return if new and old passwords are the same" + if oldpw == newpw: return + if self.isMounted(): + raise CBVolumeIsActive("this container is currently active") + devnull = None + try: + devnull = open(os.devnull, "w") + except IOError: + self.log.warn("Could not open %s" % (os.devnull, )) + "remove any potential open luks mapping" + self.__umountLuks() + "create the luks header" + proc = subprocess.Popen( + shell = False, + stdin = subprocess.PIPE, + stdout = subprocess.PIPE, + stderr = subprocess.PIPE, + args = [ + self.cbox.prefs["Programs"]["super"], + self.cbox.prefs["Programs"]["CryptoBoxRootActions"], + "cryptsetup", + "luksAddKey", + self.device, + "--batch-mode"]) + proc.stdin.write("%s\n%s" % (oldpw, newpw)) + (output, errout) = proc.communicate() + if proc.returncode != 0: + errorMsg = "Could not add a new luks key: %s - %s" % (output.strip(), errout.strip(), ) + self.log.error(errorMsg) + raise CBChangePasswordError(errorMsg) + ## retrieve the key slot we used for unlocking + keys_found = re.search(r'key slot (\d{1,3}) unlocked', output).groups() + if keys_found: + keyslot = int(keys_found[0]) + else: + raise CBChangePasswordError("could not get the old key slot") + "remove the old key" + proc = subprocess.Popen( + shell = False, + stdin = None, + stdout = devnull, + stderr = subprocess.PIPE, + args = [ + self.cbox.prefs["Programs"]["cryptsetup"], + "--batch-mode", + "luksDelKey", + self.device, + "%d" % (keyslot, )]) + proc.wait() + if proc.returncode != 0: + errorMsg = "Could not remove the old luks key: %s" % (proc.stderr.read().strip(), ) + self.log.error(errorMsg) + raise CBChangePasswordError(errorMsg) + + + + " ****************** internal stuff ********************* " + + def __getNameOfContainer(self): + "retrieve the name of the container by querying the database" + def_name = self.cbox.getNameForUUID(self.uuid) + if def_name: return def_name + "there is no name defined for this uuid - we will propose a good one" + prefix = self.cbox.prefs["Main"]["DefaultVolumePrefix"] + unused_found = False + counter = 1 + while not unused_found: + guess = prefix + str(counter) + if self.cbox.getUUIDForName(guess): + counter += 1 + else: + unused_found = True + self.cbox.setNameForUUID(self.uuid, guess) + return guess + + + def __getUUID(self): + if self.__getTypeOfPartition() == self.Types["luks"]: + guess = self.__getLuksUUID() + else: + guess = self.__getNonLuksUUID() + ## did we get a valid value? + if guess: + return guess + else: + ## emergency default value + return self.device.replace(os.path.sep, "_") + + + def __getLuksUUID(self): + """get uuid for luks devices""" + proc = subprocess.Popen( + shell = False, + stdout = subprocess.PIPE, + stderr = subprocess.PIPE, + args = [self.cbox.prefs["Programs"]["cryptsetup"], + "luksUUID", + self.device]) + (stdout, stderr) = proc.communicate() + if proc.returncode != 0: + self.cbox.log.info("could not retrieve luks uuid (%s): %s", (self.device, stderr.strip())) + return None + return stdout.strip() + + + def __getNonLuksUUID(self): + """return UUID for ext2/3 and vfat filesystems""" + try: + devnull = open(os.devnull, "w") + except IOError: + self.warn("Could not open %s" % (os.devnull, )) + proc = subprocess.Popen( + shell=False, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + args=[self.cbox.prefs["Programs"]["blkid"], + "-s", "UUID", + "-o", "value", + "-c", os.devnull, + "-w", os.devnull, + self.device]) + (stdout, stderr) = proc.communicate() + devnull.close() + ## execution failed? + if proc.returncode != 0: + self.log.info("retrieving of partition type (%s) via 'blkid' failed: %s - maybe it is encrypted?" % (self.device, stderr.strip())) + return None + ## return output of blkid + return stdout.strip() + + + def __getTypeOfPartition(self): + "retrieve the type of the given partition (see CryptoBoxContainer.Types)" + if self.__isLuksPartition(): return self.Types["luks"] + typeOfPartition = self.__getTypeIdOfPartition() + if typeOfPartition in self.__fsTypes["plain"]: + return self.Types["plain"] + if typeOfPartition in self.__fsTypes["swap"]: + return self.Types["swap"] + return self.Types["unused"] + + + def __getTypeIdOfPartition(self): + "returns the type of the partition (see 'man blkid')" + devnull = None + try: + devnull = open(os.devnull, "w") + except IOError: + self.log.warn("Could not open %s" % (os.devnull, )) + proc = subprocess.Popen( + shell=False, + stdin=None, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + args=[self.cbox.prefs["Programs"]["blkid"], + "-s", "TYPE", + "-o", "value", + "-c", os.devnull, + "-w", os.devnull, + self.device]) + proc.wait() + output = proc.stdout.read().strip() + if proc.returncode != 0: + self.log.warn("retrieving of partition type via 'blkid' failed: %s" % (proc.stderr.read().strip(), )) + return None + devnull.close() + return output + + + def __isLuksPartition(self): + "check if the given device is a luks partition" + devnull = None + try: + devnull = open(os.devnull, "w") + except IOError: + self.log.warn("Could not open %s" % (os.devnull, )) + proc = subprocess.Popen( + shell = False, + stdin = None, + stdout = devnull, + stderr = devnull, + args = [ + self.cbox.prefs["Programs"]["cryptsetup"], + "--batch-mode", + "isLuks", + self.device]) + proc.wait() + devnull.close() + return proc.returncode == 0 + + + def __getMountPoint(self): + "return the name of the mountpoint of this volume" + return os.path.join(self.cbox.prefs["Locations"]["MountParentDir"], self.name) + + + def __mountLuks(self, password): + "mount a luks partition" + if not password: + raise CBInvalidPassword("no password supplied for luksOpen") + if self.isMounted(): raise CBVolumeIsActive("this container is already active") + self.__umountLuks() + try: + devnull = open(os.devnull, "w") + except IOError: + self.log.warn("Could not open %s" % (os.devnull, )) + self.__cleanMountDirs() + if not os.path.exists(self.__getMountPoint()): + os.mkdir(self.__getMountPoint()) + if not os.path.exists(self.__getMountPoint()): + errorMsg = "Could not create mountpoint (%s)" % (self.__getMountPoint(), ) + self.log.error(errorMsg) + raise CBMountError(errorMsg) + proc = subprocess.Popen( + shell = False, + stdin = subprocess.PIPE, + stdout = devnull, + stderr = subprocess.PIPE, + args = [ + self.cbox.prefs["Programs"]["super"], + self.cbox.prefs["Programs"]["CryptoBoxRootActions"], + "cryptsetup", + "luksOpen", + self.device, + self.name, + "--batch-mode"]) + proc.stdin.write(password) + (output, errout) = proc.communicate() + if proc.returncode != 0: + errorMsg = "Could not open the luks mapping: %s" % (errout.strip(), ) + self.log.warn(errorMsg) + raise CBMountError(errorMsg) + proc = subprocess.Popen( + shell = False, + stdin = None, + stdout = devnull, + stderr = subprocess.PIPE, + args = [ + self.cbox.prefs["Programs"]["super"], + self.cbox.prefs["Programs"]["CryptoBoxRootActions"], + "mount", + os.path.join(self.__dmDir, self.name), + self.__getMountPoint()]) + proc.wait() + if proc.returncode != 0: + errorMsg = "Could not mount the filesystem: %s" % (proc.stderr.read().strip(), ) + self.log.warn(errorMsg) + raise CBMountError(errorMsg) + devnull.close() + + + def __umountLuks(self): + "umount a luks partition" + devnull = None + try: + devnull = open(os.devnull, "w") + except IOError: + self.log.warn("Could not open %s" % (os.devnull, )) + if self.isMounted(): + proc = subprocess.Popen( + shell = False, + stdin = None, + stdout = devnull, + stderr = subprocess.PIPE, + args = [ + self.cbox.prefs["Programs"]["super"], + self.cbox.prefs["Programs"]["CryptoBoxRootActions"], + "umount", + self.__getMountPoint()]) + proc.wait() + if proc.returncode != 0: + errorMsg = "Could not umount the filesystem: %s" % (proc.stderr.read().strip(), ) + self.log.warn(errorMsg) + raise CBUmountError(errorMsg) + if os.path.exists(os.path.join(self.__dmDir, self.name)): + proc = subprocess.Popen( + shell = False, + stdin = None, + stdout = devnull, + stderr = subprocess.PIPE, + args = [ + self.cbox.prefs["Programs"]["super"], + self.cbox.prefs["Programs"]["CryptoBoxRootActions"], + "cryptsetup", + "luksClose", + self.name, + "--batch-mode"]) + proc.wait() + if proc.returncode != 0: + errorMsg = "Could not remove the luks mapping: %s" % (proc.stderr.read().strip(), ) + self.log.warn(errorMsg) + raise CBUmountError(errorMsg) + devnull.close() + + + def __mountPlain(self): + "mount a plaintext partition" + if self.isMounted(): raise CBVolumeIsActive("this container is already active") + devnull = None + try: + devnull = open(os.devnull, "w") + except IOError: + self.log.warn("Could not open %s" % (os.devnull, )) + self.__cleanMountDirs() + if not os.path.exists(self.__getMountPoint()): + os.mkdir(self.__getMountPoint()) + if not os.path.exists(self.__getMountPoint()): + errorMsg = "Could not create mountpoint (%s)" % (self.__getMountPoint(), ) + self.log.error(errorMsg) + raise CBMountError(errorMsg) + proc = subprocess.Popen( + shell = False, + stdin = None, + stdout = devnull, + stderr = subprocess.PIPE, + args = [ + self.cbox.prefs["Programs"]["super"], + self.cbox.prefs["Programs"]["CryptoBoxRootActions"], + "mount", + self.device, + self.__getMountPoint()]) + proc.wait() + if proc.returncode != 0: + errorMsg = "Could not mount the filesystem: %s" % (proc.stderr.read().strip(), ) + self.log.warn(errorMsg) + raise CBMountError(errorMsg) + devnull.close() + + + def __umountPlain(self): + "umount a plaintext partition" + devnull = None + try: + devnull = open(os.devnull, "w") + except IOError: + self.log.warn("Could not open %s" % (os.devnull, )) + if self.isMounted(): + proc = subprocess.Popen( + shell = False, + stdin = None, + stdout = devnull, + stderr = subprocess.PIPE, + args = [ + self.cbox.prefs["Programs"]["super"], + self.cbox.prefs["Programs"]["CryptoBoxRootActions"], + "umount", + self.__getMountPoint()]) + proc.wait() + if proc.returncode != 0: + errorMsg = "Could not umount the filesystem: %s" % (proc.stderr.read().strip(), ) + self.log.warn(errorMsg) + raise CBUmountError(errorMsg) + devnull.close() + + + def __createPlain(self): + "make a plaintext partition" + if self.isMounted(): + raise CBVolumeIsActive("deactivate the partition before filesystem initialization") + devnull = None + try: + devnull = open(os.devnull, "w") + except IOError: + self.log.warn("Could not open %s" % (os.devnull, )) + proc = subprocess.Popen( + shell = False, + stdin = None, + stdout = devnull, + stderr = subprocess.PIPE, + args = [ + self.cbox.prefs["Programs"]["mkfs-data"], + self.device]) + proc.wait() + if proc.returncode != 0: + errorMsg = "Could not create the filesystem: %s" % (proc.stderr.read().strip(), ) + self.log.error(errorMsg) + raise CBCreateError(errorMsg) + devnull.close() + + + def __createLuks(self, password): + "make a luks partition" + if not password: + raise CBInvalidPassword("no password supplied for new luks mapping") + if self.isMounted(): + raise CBVolumeIsActive("deactivate the partition before filesystem initialization") + devnull = None + try: + devnull = open(os.devnull, "w") + except IOError: + self.log.warn("Could not open %s" % (os.devnull, )) + "remove any potential open luks mapping" + self.__umountLuks() + "create the luks header" + proc = subprocess.Popen( + shell = False, + stdin = subprocess.PIPE, + stdout = devnull, + stderr = subprocess.PIPE, + args = [ + self.cbox.prefs["Programs"]["super"], + self.cbox.prefs["Programs"]["CryptoBoxRootActions"], + "cryptsetup", + "luksFormat", + self.device, + "--batch-mode", + "--cipher", self.cbox.prefs["Main"]["DefaultCipher"], + "--iter-time", "2000"]) + proc.stdin.write(password) + (output, errout) = proc.communicate() + if proc.returncode != 0: + errorMsg = "Could not create the luks header: %s" % (errout.strip(), ) + self.log.error(errorMsg) + raise CBCreateError(errorMsg) + "open the luks container for mkfs" + proc = subprocess.Popen( + shell = False, + stdin = subprocess.PIPE, + stdout = devnull, + stderr = subprocess.PIPE, + args = [ + self.cbox.prefs["Programs"]["super"], + self.cbox.prefs["Programs"]["CryptoBoxRootActions"], + "cryptsetup", + "luksOpen", + self.device, + self.name, + "--batch-mode"]) + proc.stdin.write(password) + (output, errout) = proc.communicate() + if proc.returncode != 0: + errorMsg = "Could not open the new luks mapping: %s" % (errout.strip(), ) + self.log.error(errorMsg) + raise CBCreateError(errorMsg) + "make the filesystem" + proc = subprocess.Popen( + shell = False, + stdin = None, + stdout = devnull, + stderr = subprocess.PIPE, + args = [ + self.cbox.prefs["Programs"]["mkfs-data"], + os.path.join(self.__dmDir, self.name)]) + proc.wait() + "remove the mapping - for every exit status" + self.__umountLuks() + if proc.returncode != 0: + errorMsg = "Could not create the filesystem: %s" % (proc.stderr.read().strip(), ) + self.log.error(errorMsg) + "remove the luks mapping" + raise CBCreateError(errorMsg) + devnull.close() + + + def __cleanMountDirs(self): + """ remove all unnecessary subdirs of the mount parent directory + this should be called for every (u)mount """ + subdirs = os.listdir(self.cbox.prefs["Locations"]["MountParentDir"]) + for dir in subdirs: + abs_dir = os.path.join(self.cbox.prefs["Locations"]["MountParentDir"], dir) + if (not os.path.islink(abs_dir)) and os.path.isdir(abs_dir) and (not os.path.ismount(abs_dir)): + os.rmdir(abs_dir) + + diff --git a/bin/CryptoBoxExceptions.py b/bin/CryptoBoxExceptions.py new file mode 100644 index 0000000..743bfcd --- /dev/null +++ b/bin/CryptoBoxExceptions.py @@ -0,0 +1,107 @@ +""" +exceptions of the cryptobox package +""" + + +class CryptoBoxError(Exception): + """base class for exceptions of the cryptobox""" + pass + + +class CBConfigError(CryptoBoxError): + """any kind of error related to the configuration of a cryptobox""" + pass + + +class CBConfigUnavailableError(CBConfigError): + """config file/input was not available at all""" + + def __init__(self, source=None): + self.source = source + + def __str__(self): + if self.source: + return "failed to access the configuration of the cryptobox: %s" % self.source + else: + return "failed to access the configuration of the cryptobox" + + +class CBConfigUndefinedError(CBConfigError): + """a specific configuration setting was not defined""" + + def __init__(self, section, name=None): + self.section = section + self.name = name + + def __str__(self): + # is it a settings or a section? + if self.name: + # setting + return "undefined configuration setting: [%s]->%s - please check your configuration file" % (self.section, self.name) + else: + # section + return "undefined configuration section: [%s] - please check your configuration file" % (self.section, ) + + + +class CBConfigInvalidValueError(CBConfigError): + """a configuration setting was invalid somehow""" + + def __init__(self, section, name, value, reason): + self.section = section + self.name = name + self.value = value + self.reason = reason + + def __str__(self): + return "invalid configuration setting [%s]->%s (%s): %s" % (self.section, self.name, self.value, self.reason) + + +class CBEnvironmentError(CryptoBoxError): + """some part of the environment of the cryptobox is broken + e.g. the wrong version of a required program + """ + + def __init__(self, desc): + self.desc = desc + + def __str__(self): + return "misconfiguration detected: %s" % self.desc + + +class CBContainerError(CryptoBoxError): + """any error raised while manipulating a cryptobox container""" + + def __init__(self, desc): + self.desc = desc + + def __str__(self): + return self.desc + +class CBCreateError(CBContainerError): + pass + +class CBVolumeIsActive(CBContainerError): + pass + +class CBInvalidName(CBContainerError): + pass + +class CBNameActivelyUsed(CBContainerError): + pass + +class CBInvalidType(CBContainerError): + pass + +class CBInvalidPassword(CBContainerError): + pass + +class CBChangePasswordError(CBContainerError): + pass + +class CBMountError(CBContainerError): + pass + +class CBUmountError(CBContainerError): + pass + diff --git a/bin/CryptoBoxPlugin.py b/bin/CryptoBoxPlugin.py new file mode 100644 index 0000000..abb3f0c --- /dev/null +++ b/bin/CryptoBoxPlugin.py @@ -0,0 +1,165 @@ +# $Id$ +# +# parent class for all plugins of the CryptoBox +# + +import os +import cherrypy + + +class CryptoBoxPlugin: + + ## default capability is "system" - the other supported capability is: "volume" + pluginCapabilities = [ "system" ] + + ## does this plugin require admin authentification? + requestAuth = False + + ## is this plugin enabled by default? + enabled = True + + ## default rank (0..100) of the plugin in listings (lower value means higher priority) + rank = 80 + + + ## default icon of this plugin (relative path) + defaultIconFileName = "plugin_icon.png" + + + def __init__(self, cbox, pluginDir): + self.cbox = cbox + self.hdf = {} + self.pluginDir = pluginDir + self.hdf_prefix = "Data.Plugins.%s." % self.getName() + + + def doAction(self, **args): + """override doAction with your plugin code""" + raise Exception, "undefined action handler ('doAction') in plugin '%'" % self.getName() + + + def getStatus(self): + """you should override this, to supply useful state information""" + raise Exception, "undefined state handler ('getStatus') in plugin '%'" % self.getName() + + + def getName(self): + """the name of the python file (module) should be the name of the plugin""" + return self.__module__ + + + @cherrypy.expose + def getIcon(self, image=None, **kargs): + """return the image data of the icon of the plugin + + the parameter 'image' may be used for alternative image locations (relative + to the directory of the plugin) + '**kargs' is necessary, as a 'weblang' attribute may be specified (and ignored)""" + import cherrypy, re + if (image is None): # or (re.search(u'[\w-\.]', image)): + plugin_icon_file = os.path.join(self.pluginDir, self.defaultIconFileName) + else: + plugin_icon_file = os.path.join(self.pluginDir, image) + if not os.access(plugin_icon_file, os.R_OK): + plugin_icon_file = os.path.join(self.cbox.prefs["Locations"]["PluginDir"], "plugin_icon_unknown.png") + return cherrypy.lib.cptools.serveFile(plugin_icon_file) + + + def getTemplateFileName(self, template_name): + """return the filename of the template, if it is part of this plugin + + use this function to check, if the plugin provides the specified template + """ + result_file = os.path.join(self.pluginDir, template_name + ".cs") + if os.access(result_file, os.R_OK) and os.path.isfile(result_file): + return result_file + else: + return None + + + def getLanguageData(self, lang="en"): + try: + import neo_cgi, neo_util + except: + raise CryptoBoxExceptions.CBEnvironmentError("couldn't import 'neo_*'! Try 'apt-get install python-clearsilver'.") + langdir = os.path.abspath(os.path.join(self.pluginDir, "lang")) + ## first: the default language file (english) + langFiles = [os.path.join(langdir, "en.hdf")] + ## maybe we have to load a translation afterwards + if lang != "en": + langFiles.append(os.path.join(langdir, lang + ".hdf")) + file_found = False + lang_hdf = neo_util.HDF() + for langFile in langFiles: + if os.access(langFile, os.R_OK): + lang_hdf.readFile(langFile) + file_found = True + if file_found: + return lang_hdf + else: + self.cbox.log.debug("Couldn't find a valid plugin language file (%s)" % str(langFiles)) + return None + + + def loadDataSet(self, hdf): + for (key, value) in self.hdf.items(): + hdf.setValue(key, str(value)) + + + def isAuthRequired(self): + """check if this plugin requires authentication + first step: check plugin configuration + second step: check default value of plugin""" + try: + if self.cbox.prefs.pluginConf[self.getName()]["requestAuth"] is None: + return self.requestAuth + if self.cbox.prefs.pluginConf[self.getName()]["requestAuth"]: + return True + else: + return False + except KeyError: + return self.requestAuth + + + def isEnabled(self): + """check if this plugin is enabled + first step: check plugin configuration + second step: check default value of plugin""" + import types + try: + if self.cbox.prefs.pluginConf[self.getName()]["enabled"] is None: + return self.enabled + if self.cbox.prefs.pluginConf[self.getName()]["enabled"]: + return True + else: + return False + except KeyError: + return self.enabled + + + def getRank(self): + """check the rank of this plugin + first step: check plugin configuration + second step: check default value of plugin""" + try: + if self.cbox.prefs.pluginConf[self.getName()]["rank"] is None: + return self.rank + return int(self.cbox.prefs.pluginConf[self.getName()]["rank"]) + except KeyError, TypeError: + return self.rank + + + def getTestClass(self): + import imp + pl_file = os.path.join(self.pluginDir, "unittests.py") + if os.access(pl_file, os.R_OK) and os.path.isfile(pl_file): + try: + return getattr(imp.load_source("unittests_%s" % self.getName(), pl_file), "unittests") + except AttributeError: + pass + try: + self.cbox.log.info("could not load unittests for plugin: %s" % self.getName()) + except AttributeError: + pass + return None + diff --git a/bin/CryptoBoxRootActions.py b/bin/CryptoBoxRootActions.py new file mode 100755 index 0000000..b92ae3c --- /dev/null +++ b/bin/CryptoBoxRootActions.py @@ -0,0 +1,386 @@ +#!/usr/bin/env python2.4 + +"""module for executing the programs, that need root privileges + +Syntax: + - program + - device + - [action] + - [action args] + +this script will always return with an exitcode 0 (true), if "check" is the only argument +""" + +import os +import sys +import subprocess +import pwd +import grp +import types + +allowedProgs = { + "sfdisk": "/sbin/sfdisk", + "cryptsetup": "/sbin/cryptsetup", + "mount": "/bin/mount", + "umount": "/bin/umount", + "blkid": "/sbin/blkid", + } + + +DEV_TYPES = { "pipe":1, "char":2, "dir":4, "block":6, "file":8, "link":10, "socket":12} + + +def checkIfPluginIsSafe(plugin): + """check if the plugin and its parents are only writeable for root""" + #FIXME: for now we may skip this test - but users will not like it this way :) + return True + props = os.stat(plugin) + ## check if it is owned by non-root + if props.st_uid != 0: return False + ## check group-write permission if gid is not zero + if (props.st_gid != 0) and (props.st_mode % 32 / 16 > 0): return False + ## check if it is world-writeable + if props.st_mode % 4 / 2 > 0: return False + ## are we at root-level (directory-wise)? If yes, then we are ok ... + if plugin == os.path.sep: return True + ## check if the parent directory is ok - recursively :) + return checkIfPluginIsSafe(os.path.dirname(os.path.abspath(plugin))) + + +def checkIfPluginIsValid(plugin): + import imp + try: + x = imp.load_source("cbox_plugin",plugin) + except Exception: + return False + try: + if getattr(x, "PLUGIN_TYPE") == "cryptobox": + return True + else: + return False + except Exception: + return False + + +def call_plugin(args): + """check if the plugin may be called - and do it finally ...""" + plugin = os.path.abspath(args[0]) + del args[0] + ## check existence and excutability + if not os.access(plugin, os.X_OK): + raise Exception, "could not find executable plugin (%s)" % plugin + ## check if the plugin (and its parents) are only writeable for root + if not checkIfPluginIsSafe(plugin): + raise Exception, "the plugin (%s) was not safe - check its (and its parents') permissions" % plugin + ## check if the plugin is a python program, that is marked as a cryptobox plugin + if not checkIfPluginIsValid(plugin): + raise Exception, "the plugin (%s) is not a correctly marked python script" % plugin + args.insert(0,plugin) + proc = subprocess.Popen( + shell = False, + args = args) + proc.wait() + return proc.returncode == 0 + + +def isWriteable(device, force_dev_type=None): + """check if the calling user (not root!) has write access to the device/file + + the real (not the effictive) user id is used for the check + additionally the permissions of the default groups of the real uid are checked + this check works nicely together with "super", as it changes (by default) only + the effective uid (not the real uid) + """ + # first check, if the device/file exists + if not os.path.exists(device): + return False + # check the type of the device - if necessary + if not force_dev_type is None: + dev_type = os.stat(device).st_mode % 65536 / 4096 + if dev_type != force_dev_type: return False + # retrieve the information for the real user id + (trustUserName, trustUID, groupsOfTrustUser) = getUserInfo(os.getuid()) + # set the default groups of the caller for the check (restore them later) + savedGroups = os.getgroups() + os.setgroups(groupsOfTrustUser) + # check permissions + result = os.access(device, os.W_OK) and os.access(device, os.R_OK) + # reset the groups of this process + os.setgroups(savedGroups) + return result + + +def run_cryptsetup(args): + """execute cryptsetup as root + + @args: list of arguments - they will be treated accordingly to the first element + of this list (the action)""" + if not args: raise "WrongArguments", "no action for cryptsetup supplied" + if type(args) != types.ListType: raise "WrongArguments", "invalid arguments supplied: %s" % (args, ) + try: + action = args[0] + del args[0] + device = None + cmd_args = [] + if action == "luksFormat": + device = args[0]; del args[0] + cmd_args.append(action) + cmd_args.append(device) + elif action == "luksUUID": + device = args[0]; del args[0] + cmd_args.append(action) + cmd_args.append(device) + elif action == "luksOpen": + if len(args) < 2: raise "WrongArguments", "missing arguments" + device = args[0]; del args[0] + destination = args[0]; del args[0] + cmd_args.append(action) + cmd_args.append(device) + cmd_args.append(destination) + elif action == "luksClose": + if len(args) < 1: raise "WrongArguments", "missing arguments" + destination = args[0]; del args[0] + # maybe add a check for the mapped device's permissions? + # dmsetup deps self.device + cmd_args.append(action) + cmd_args.append(destination) + elif action == "luksAddKey": + device = args[0]; del args[0] + cmd_args.append(action) + cmd_args.append(device) + elif action == "luksDelKey": + if len(cs_args) < 2: raise "WrongArguments", "missing arguments" + device = args[0]; del args[0] + cmd_args.insert(-1, action) + cmd_args.insert(-1, device) + elif action == "isLuks": + device = args[0]; del args[0] + cmd_args.append(action) + cmd_args.append(device) + else: raise "WrongArguments", "invalid action supplied: %s" % (action, ) + # check if a device was defined - and check it + if (not device is None) and (not isWriteable(device, DEV_TYPES["block"])): + raise "WrongArguments", "%s is not a writeable block device" % (device, ) + cs_args = [allowedProgs["cryptsetup"]] + cs_args.extend(args) + cs_args.extend(cmd_args) + except (TypeError, IndexError): + raise "WrongArguments", "invalid arguments supplied: %s" % (args, ) + # execute cryptsetup with the given parameters + proc = subprocess.Popen( + shell = False, + args = cs_args) + proc.wait() + ## chown the devmapper block device to the cryptobox user + if (proc.returncode == 0) and (action == "luksOpen"): + os.chown(os.path.join(os.path.sep, "dev", "mapper", destination), os.getuid(), os.getgid()) + return proc.returncode == 0 + + +def run_sfdisk(args): + """execute sfdisk for partitioning + + not implemented yet""" + print "ok - you are free to call sfdisk ..." + print " not yet implemented ..." + return True + + +def getFSType(device): + """get the filesystem type of a device""" + proc = subprocess.Popen( + shell = False, + stdout = subprocess.PIPE, + args = [ allowedProgs["blkid"], + "-s", "TYPE", + "-o", "value", + "-c", os.devnull, + "-w", os.devnull, + device]) + (stdout, stderr) = proc.communicate() + if proc.returncode != 0: + return None + return stdout.strip() + + +def run_mount(args): + """execute mount + """ + if not args: raise "WrongArguments", "no destination for mount supplied" + if type(args) != types.ListType: raise "WrongArguments", "invalid arguments supplied: %s" % (args, ) + try: + device = args[0] + del args[0] + destination = args[0] + del args[0] + # check permissions for the device + if not isWriteable(device, DEV_TYPES["block"]): + raise "WrongArguments", "%s is not a writeable block device" % (device, ) + ## check permissions for the mountpoint + if not isWriteable(destination, DEV_TYPES["dir"]): + raise "WrongArguments", "the mountpoint (%s) is not writeable" % (destination, ) + # check for additional (not allowed) arguments + if len(args) != 0: + raise "WrongArguments", "too many arguments for 'mount': %s" % (args, ) + except TypeError: + raise "WrongArguments", "invalid arguments supplied: %s" % (args, ) + # execute mount with the given parameters + # first overwrite the real uid, as 'mount' wants this to be zero (root) + savedUID = os.getuid() + os.setuid(os.geteuid()) + ## we have to change the permissions of the mounted directory - otherwise it will + ## not be writeable for the cryptobox user + ## for 'vfat' we have to do this during mount + ## for ext2/3 we have to do it afterward + ## first: get the user/group of the target + (trustUserName, trustUID, groupsOfTrustUser) = getUserInfo(savedUID) + trustGID = groupsOfTrustUser[0] + fsType = getFSType(device) + ## define arguments + if fsType == "vfat": + ## add the "uid/gid" arguments to the mount call + mount_args = [allowedProgs["mount"], + "-o", "uid=%d,gid=%d" % (trustUID, trustGID), + device, + destination] + else: + ## all other filesystem types will be handled after mount + mount_args = [allowedProgs["mount"], device, destination] + # execute mount + proc = subprocess.Popen( + shell = False, + args = mount_args) + proc.wait() + ## return in case of an error + if proc.returncode != 0: + return False + ## for vfat: we are done + if fsType == "vfat": return True + ## for all other filesystem types: chown the mount directory + try: + os.chown(destination, trustUID, groupsOfTrustUser[0]) + except OSError, errMsg: + sys.stderr.write("could not chown the mount destination (%s) to the specified user (%d/%d): %s\n" % (destination, trustUID, groupsOfTrustUser[0], errMsg)) + sys.stderr.write("UID: %d\n" % (os.geteuid(),)) + return False + ## BEWARE: it would be nice, if we could restore the previous uid (not euid) but + ## this would also override the euid (see 'man 2 setuid') - any ideas? + return True + + +def run_umount(args): + """execute mount + """ + if not args: raise "WrongArguments", "no mountpoint for umount supplied" + if type(args) != types.ListType: raise "WrongArguments", "invalid arguments supplied" + try: + destination = args[0] + del args[0] + # check permissions for the destination + if not isWriteable(os.path.dirname(destination), DEV_TYPES["dir"]): + raise "WrongArguments", "the parent of the mountpoint (%s) is not writeable" % (destination, ) + if len(args) != 0: raise "WrongArguments", "umount does not allow arguments" + except TypeError: + raise "WrongArguments", "invalid arguments supplied" + # execute umount with the given parameters + # first overwrite the real uid, as 'umount' wants this to be zero (root) + savedUID = os.getuid() + os.setuid(os.geteuid()) + # execute umount (with the parameter '-l' - lazy umount) + proc = subprocess.Popen( + shell = False, + args = [allowedProgs["umount"], "-l", destination]) + proc.wait() + # restore previous real uid + os.setuid(savedUID) + return proc.returncode == 0 + + +def getUserInfo(user): + """return information about the specified user + + @user: (uid or name) + @return: tuple of (name, uid, (groups)) + """ + if user is None: raise "KeyError", "no user supplied" + # first check, if 'user' contains an id - then check for a name + try: + userinfo = pwd.getpwuid(user) + except TypeError: + # if a KeyError is raised again, then the supplied user was invalid + userinfo = pwd.getpwnam(user) + u_groups =[one_group.gr_gid + for one_group in grp.getgrall() + if userinfo.pw_name in one_group.gr_mem] + if not userinfo.pw_gid in u_groups: u_groups.append(userinfo.pw_gid) + return (userinfo.pw_name, userinfo.pw_uid, u_groups) + + +# **************** main ********************** + +# prevent import +if __name__ == "__main__": + + # do we have root privileges (effective uid is zero)? + if os.geteuid() != 0: + sys.stderr.write("the effective uid is not zero - you should use 'super' to call this script (%s)" % sys.argv[0]) + sys.exit(100) + + # remove program name + args = sys.argv[1:] + + # do not allow to use root permissions (real uid may not be zero) + if os.getuid() == 0: + sys.stderr.write("the uid of the caller is zero (root) - this is not allowed\n") + sys.exit(100) + + # check if there were arguments + if (len(args) == 0): + sys.stderr.write("No arguments supplied\n") + sys.exit(100) + + # did the user call the "check" action? + if (len(args) == 1) and (args[0].lower() == "check"): + # exit silently + sys.exit(0) + + if args[0].lower() == "plugin": + del args[0] + try: + isOK = call_plugin(args) + except Exception, errMsg: + sys.stderr.write("Execution of plugin failed: %s\n" % errMsg) + sys.exit(100) + if isOK: + sys.exit(0) + else: + sys.exit(1) + + # check parameters count + if len(args) < 2: + sys.stderr.write("Not enough arguments supplied (%s)!\n" % " ".join(args)) + sys.exit(100) + + progRequest = args[0] + del args[0] + + if not progRequest in allowedProgs.keys(): + sys.stderr.write("Invalid program requested: %s\n" % progRequest) + sys.exit(100) + + if progRequest == "cryptsetup": runner = run_cryptsetup + elif progRequest == "sfdisk": runner = run_sfdisk + elif progRequest == "mount": runner = run_mount + elif progRequest == "umount": runner = run_umount + else: + sys.stderr.write("The interface for this program (%s) is not yet implemented!\n" % progRequest) + sys.exit(100) + try: + if runner(args): + sys.exit(0) + else: + sys.exit(1) + except "WrongArguments", errstr: + sys.stderr.write("Execution failed: %s\n" % errstr) + sys.exit(100) + diff --git a/bin/CryptoBoxSettings.py b/bin/CryptoBoxSettings.py new file mode 100644 index 0000000..73ca9a6 --- /dev/null +++ b/bin/CryptoBoxSettings.py @@ -0,0 +1,481 @@ +import logging +try: + import validate +except: + raise CryptoBoxExceptions.CBEnvironmentError("couldn't import 'validate'! Try 'apt-get install python-formencode'.") +import os +import CryptoBoxExceptions +import subprocess +try: + import configobj ## needed for reading and writing of the config file +except: + raise CryptoBoxExceptions.CBEnvironmentError("couldn't import 'configobj'! Try 'apt-get install python-configobj'.") + + + +class CryptoBoxSettings: + + CONF_LOCATIONS = [ + "./cryptobox.conf", + "~/.cryptobox.conf", + "/etc/cryptobox/cryptobox.conf"] + + NAMEDB_FILE = "cryptobox_names.db" + PLUGINCONF_FILE = "cryptobox_plugins.conf" + USERDB_FILE = "cryptobox_users.db" + + + def __init__(self, config_file=None): + self.log = logging.getLogger("CryptoBox") + config_file = self.__getConfigFileName(config_file) + self.log.info("loading config file: %s" % config_file) + self.prefs = self.__getPreferences(config_file) + self.__validateConfig() + self.__configureLogHandler() + self.__checkUnknownPreferences() + self.preparePartition() + self.nameDB = self.__getNameDatabase() + self.pluginConf = self.__getPluginConfig() + self.userDB = self.__getUserDB() + self.misc_files = self.__getMiscFiles() + + + def write(self): + """ + write all local setting files including the content of the "misc" subdirectory + """ + ok = True + try: + self.nameDB.write() + except IOError: + self.log.warn("could not save the name database") + ok = False + try: + self.pluginConf.write() + except IOError: + self.log.warn("could not save the plugin configuration") + ok = False + try: + self.userDB.write() + except IOError: + self.log.warn("could not save the user database") + ok = False + for misc_file in self.misc_files: + if not misc_file.save(): + self.log.warn("could not save a misc setting file (%s)" % misc_file.filename) + ok = False + return ok + + + def requiresPartition(self): + return bool(self.prefs["Main"]["UseConfigPartition"]) + + + def getActivePartition(self): + settings_dir = self.prefs["Locations"]["SettingsDir"] + if not os.path.ismount(settings_dir): return None + for line in file("/proc/mounts"): + fields = line.split(" ") + mount_dir = fields[1] + try: + if os.path.samefile(mount_dir, settings_dir): return fields[0] + except OSError: + pass + ## no matching entry found + return None + + + def mountPartition(self): + self.log.debug("trying to mount configuration partition") + if not self.requiresPartition(): + self.log.warn("mountConfigPartition: configuration partition is not required - mounting anyway") + if self.getActivePartition(): + self.log.warn("mountConfigPartition: configuration partition already mounted - not mounting again") + return False + confPartitions = self.getAvailablePartitions() + if not confPartitions: + self.log.error("no configuration partitions found - you have to create it first") + return False + partition = confPartitions[0] + proc = subprocess.Popen( + shell = False, + stdout = subprocess.PIPE, + stderr = subprocess.PIPE, + args = [ + self.prefs["Programs"]["super"], + self.prefs["Programs"]["CryptoBoxRootActions"], + "mount", + partition, + self.prefs["Locations"]["SettingsDir"]]) + (stdout, stderr) = proc.communicate() + if proc.returncode != 0: + self.log.error("failed to mount the configuration partition: %s" % partition) + self.log.error("output of mount: %s" % (stderr,)) + return False + self.log.info("configuration partition mounted: %s" % partition) + return True + + + def umountPartition(self): + if not self.getActivePartition(): + self.log.warn("umountConfigPartition: no configuration partition mounted") + return False + proc = subprocess.Popen( + shell = False, + stdout = subprocess.PIPE, + stderr = subprocess.PIPE, + args = [ + self.prefs["Programs"]["super"], + self.prefs["Programs"]["CryptoBoxRootActions"], + "umount", + self.prefs["Locations"]["SettingsDir"]]) + (stdout, stderr) = proc.communicate() + if proc.returncode != 0: + self.log.error("failed to unmount the configuration partition") + self.log.error("output of mount: %s" % (stderr,)) + return False + self.log.info("configuration partition unmounted") + return True + + + def getAvailablePartitions(self): + """returns a sequence of found config partitions""" + proc = subprocess.Popen( + shell = False, + stdout = subprocess.PIPE, + args = [ + self.prefs["Programs"]["blkid"], + "-c", os.path.devnull, + "-t", "LABEL=%s" % self.prefs["Main"]["ConfigVolumeLabel"] ]) + (output, error) = proc.communicate() + if output: + return [e.strip().split(":",1)[0] for e in output.splitlines()] + else: + return [] + + + def preparePartition(self): + if self.requiresPartition() and not self.getActivePartition(): + self.mountPartition() + + + def __getitem__(self, key): + """redirect all requests to the 'prefs' attribute""" + return self.prefs[key] + + + def __getPreferences(self, config_file): + import StringIO + config_rules = StringIO.StringIO(self.validation_spec) + try: + prefs = configobj.ConfigObj(config_file, configspec=config_rules) + if prefs: + self.log.info("found config: %s" % prefs.items()) + else: + raise CryptoBoxExceptions.CBConfigUnavailableError("failed to load the config file: %s" % config_file) + except IOError: + raise CryptoBoxExceptions.CBConfigUnavailableError("unable to open the config file: %s" % config_file) + return prefs + + + def __validateConfig(self): + result = self.prefs.validate(CryptoBoxSettingsValidator(), preserve_errors=True) + error_list = configobj.flatten_errors(self.prefs, result) + if not error_list: return + errorMsgs = [] + for sections, key, text in error_list: + section_name = "->".join(sections) + if not text: + errorMsg = "undefined configuration value (%s) in section '%s'" % (key, section_name) + else: + errorMsg = "invalid configuration value (%s) in section '%s': %s" % (key, section_name, text) + errorMsgs.append(errorMsg) + raise CryptoBoxExceptions.CBConfigError, "\n".join(errorMsgs) + + + def __checkUnknownPreferences(self): + import StringIO + config_rules = configobj.ConfigObj(StringIO.StringIO(self.validation_spec), list_values=False) + self.__recursiveConfigSectionCheck("", self.prefs, config_rules) + + + def __recursiveConfigSectionCheck(self, section_path, section_config, section_rules): + """should be called by '__checkUnknownPreferences' for every section + sends a warning message to the logger for every undefined (see validation_spec) + configuration setting + """ + for e in section_config.keys(): + element_path = section_path + e + if e in section_rules.keys(): + if isinstance(section_config[e], configobj.Section): + if isinstance(section_rules[e], configobj.Section): + self.__recursiveConfigSectionCheck(element_path + "->", section_config[e], section_rules[e]) + else: + self.log.warn("configuration setting should be a value instead of a section name: %s" % element_path) + else: + if not isinstance(section_rules[e], configobj.Section): + pass # good - the setting is valid + else: + self.log.warn("configuration setting should be a section name instead of a value: %s" % element_path) + else: + self.log.warn("unknown configuration setting: %s" % element_path) + + + def __getNameDatabase(self): + try: + try: + nameDB_file = os.path.join(self.prefs["Locations"]["SettingsDir"], self.NAMEDB_FILE) + except KeyError: + raise CryptoBoxExceptions.CBConfigUndefinedError("Locations", "SettingsDir") + except SyntaxError: + raise CryptoBoxExceptions.CBConfigInvalidValueError("Locations", "SettingsDir", nameDB_file, "failed to interprete the filename of the name database correctly (%s)" % nameDB_file) + ## create nameDB if necessary + if os.path.exists(nameDB_file): + nameDB = configobj.ConfigObj(nameDB_file) + else: + nameDB = configobj.ConfigObj(nameDB_file, create_empty=True) + ## check if nameDB file was created successfully? + if not os.path.exists(nameDB_file): + raise CryptoBoxExceptions.CBEnvironmentError("failed to create name database (%s)" % nameDB_file) + return nameDB + + + def __getPluginConfig(self): + import StringIO + plugin_rules = StringIO.StringIO(self.pluginValidationSpec) + try: + try: + pluginConf_file = os.path.join(self.prefs["Locations"]["SettingsDir"], self.PLUGINCONF_FILE) + except KeyError: + raise CryptoBoxExceptions.CBConfigUndefinedError("Locations", "SettingsDir") + except SyntaxError: + raise CryptoBoxExceptions.CBConfigInvalidValueError("Locations", "SettingsDir", pluginConf_file, "failed to interprete the filename of the plugin config file correctly (%s)" % pluginConf_file) + ## create pluginConf_file if necessary + if os.path.exists(pluginConf_file): + pluginConf = configobj.ConfigObj(pluginConf_file, configspec=plugin_rules) + else: + pluginConf = configobj.ConfigObj(pluginConf_file, configspec=plugin_rules, create_empty=True) + ## validate and convert values according to the spec + pluginConf.validate(validate.Validator()) + ## check if pluginConf_file file was created successfully? + if not os.path.exists(pluginConf_file): + raise CryptoBoxExceptions.CBEnvironmentError("failed to create plugin configuration file (%s)" % pluginConf_file) + return pluginConf + + + def __getUserDB(self): + import StringIO, sha + userDB_rules = StringIO.StringIO(self.userDatabaseSpec) + try: + try: + userDB_file = os.path.join(self.prefs["Locations"]["SettingsDir"], self.USERDB_FILE) + except KeyError: + raise CryptoBoxExceptions.CBConfigUndefinedError("Locations", "SettingsDir") + except SyntaxError: + raise CryptoBoxExceptions.CBConfigInvalidValueError("Locations", "SettingsDir", userDB_file, "failed to interprete the filename of the users database file correctly (%s)" % userDB_file) + ## create userDB_file if necessary + if os.path.exists(userDB_file): + userDB = configobj.ConfigObj(userDB_file, configspec=userDB_rules) + else: + userDB = configobj.ConfigObj(userDB_file, configspec=userDB_rules, create_empty=True) + ## validate and set default value for "admin" user + userDB.validate(validate.Validator()) + ## check if userDB file was created successfully? + if not os.path.exists(userDB_file): + raise CryptoBoxExceptions.CBEnvironmentError("failed to create user database file (%s)" % userDB_file) + ## define password hash function - never use "sha" directly - SPOT + userDB.getDigest = lambda password: sha.new(password).hexdigest() + return userDB + + + def __getMiscFiles(self): + misc_dir = os.path.join(self.prefs["Locations"]["SettingsDir"], "misc") + if (not os.path.isdir(misc_dir)) or (not os.access(misc_dir, os.X_OK)): + return [] + return [MiscConfigFile(os.path.join(misc_dir, f), self.log) + for f in os.listdir(misc_dir) + if os.path.isfile(os.path.join(misc_dir, f))] + + + def __getConfigFileName(self, config_file): + # search for the configuration file + import types + if config_file is None: + # no config file was specified - we will look for it in the ususal locations + conf_file_list = [os.path.expanduser(f) + for f in self.CONF_LOCATIONS + if os.path.exists(os.path.expanduser(f))] + if not conf_file_list: + # no possible config file found in the usual locations + raise CryptoBoxExceptions.CBConfigUnavailableError() + config_file = conf_file_list[0] + else: + # a config file was specified (e.g. via command line) + if type(config_file) != types.StringType: + raise CryptoBoxExceptions.CBConfigUnavailableError("invalid config file specified: %s" % config_file) + if not os.path.exists(config_file): + raise CryptoBoxExceptions.CBConfigUnavailableError("could not find the specified configuration file (%s)" % config_file) + return config_file + + + def __configureLogHandler(self): + try: + log_level = self.prefs["Log"]["Level"].upper() + log_level_avail = ["DEBUG", "INFO", "WARN", "ERROR"] + if not log_level in log_level_avail: + raise TypeError + except KeyError: + raise CryptoBoxExceptions.CBConfigUndefinedError("Log", "Level") + except TypeError: + raise CryptoBoxExceptions.CBConfigInvalidValueError("Log", "Level", log_level, "invalid log level: only %s are allowed" % log_level_avail) + try: + try: + log_handler = logging.FileHandler(self.prefs["Log"]["Details"]) + except KeyError: + raise CryptoBoxExceptions.CBConfigUndefinedError("Log", "Details") + except IOError: + raise CryptoBoxExceptions.CBEnvironmentError("could not create the log file (%s)" % self.prefs["Log"]["Details"]) + log_handler.setFormatter(logging.Formatter('%(asctime)s CryptoBox %(levelname)s: %(message)s')) + cbox_log = logging.getLogger("CryptoBox") + ## remove previous handlers + cbox_log.handlers = [] + ## add new one + cbox_log.addHandler(log_handler) + ## do not call parent's handlers + cbox_log.propagate = False + ## 'log_level' is a string -> use 'getattr' + cbox_log.setLevel(getattr(logging,log_level)) + ## the logger named "CryptoBox" is configured now + + + validation_spec = """ +[Main] +AllowedDevices = list(min=1) +DefaultVolumePrefix = string(min=1) +DefaultCipher = string(default="aes-cbc-essiv:sha256") +ConfigVolumeLabel = string(min=1, default="cbox_config") +UseConfigPartition = integer(min=0, max=1, default=0) + +[Locations] +MountParentDir = directoryExists(default="/var/cache/cryptobox/mnt") +SettingsDir = directoryExists(default="/var/cache/cryptobox/settings") +TemplateDir = directoryExists(default="/usr/share/cryptobox/template") +LangDir = directoryExists(default="/usr/share/cryptobox/lang") +DocDir = directoryExists(default="/usr/share/doc/cryptobox/html") +PluginDir = directoryExists(default="/usr/share/cryptobox/plugins") + +[Log] +Level = option("debug", "info", "warn", "error", default="warn") +Destination = option("file", default="file") +Details = string(min=1) + +[WebSettings] +Stylesheet = string(min=1) +Language = string(min=1, default="en") + +[Programs] +cryptsetup = fileExecutable(default="/sbin/cryptsetup") +mkfs-data = fileExecutable(default="/sbin/mkfs.ext3") +blkid = fileExecutable(default="/sbin/blkid") +blockdev = fileExecutable(default="/sbin/blockdev") +mount = fileExecutable(default="/bin/mount") +umount = fileExecutable(default="/bin/umount") +super = fileExecutable(default="/usr/bin/super") +# this is the "program" name as defined in /etc/super.tab +CryptoBoxRootActions = string(min=1) + """ + + pluginValidationSpec = """ +[__many__] +enabled = boolean(default=None) +requestAuth = boolean(default=None) +rank = integer(default=None) + """ + + userDatabaseSpec = """ +[admins] +admin = string(default=d033e22ae348aeb5660fc2140aec35850c4da997) + """ + + +class CryptoBoxSettingsValidator(validate.Validator): + + def __init__(self): + validate.Validator.__init__(self) + self.functions["directoryExists"] = self.check_directoryExists + self.functions["fileExecutable"] = self.check_fileExecutable + self.functions["fileWriteable"] = self.check_fileWriteable + + + def check_directoryExists(self, value): + dir_path = os.path.abspath(value) + if not os.path.isdir(dir_path): + raise validate.VdtValueError("%s (not found)" % value) + if not os.access(dir_path, os.X_OK): + raise validate.VdtValueError("%s (access denied)" % value) + return dir_path + + + def check_fileExecutable(self, value): + file_path = os.path.abspath(value) + if not os.path.isfile(file_path): + raise validate.VdtValueError("%s (not found)" % value) + if not os.access(file_path, os.X_OK): + raise validate.VdtValueError("%s (access denied)" % value) + return file_path + + + def check_fileWriteable(self, value): + file_path = os.path.abspath(value) + if os.path.isfile(file_path): + if not os.access(file_path, os.W_OK): + raise validate.VdtValueError("%s (not found)" % value) + else: + parent_dir = os.path.dirname(file_path) + if os.path.isdir(parent_dir) and os.access(parent_dir, os.W_OK): + return file_path + raise validate.VdtValueError("%s (directory does not exist)" % value) + return file_path + + + +class MiscConfigFile: + + maxSize = 20480 + + def __init__(self, filename, logger): + self.filename = filename + self.log = logger + self.load() + + + def load(self): + fd = open(self.filename, "rb") + ## limit the maximum size + self.content = fd.read(self.maxSize) + if fd.tell() == self.maxSize: + self.log.warn("file in misc settings directory (%s) is bigger than allowed (%s)" % (self.filename, self.maxSize)) + fd.close() + + + def save(self): + save_dir = os.path.dirname(self.filename) + ## create the directory, if necessary + if not os.path.isdir(save_dir): + try: + os.mkdir(save_dir) + except IOError: + return False + ## save the content of the file + try: + fd = open(self.filename, "wb") + except IOError: + return False + try: + fd.write(self.content) + fd.close() + return True + except IOError: + fd.close() + return False + diff --git a/bin/CryptoBoxTools.py b/bin/CryptoBoxTools.py new file mode 100644 index 0000000..25ffa03 --- /dev/null +++ b/bin/CryptoBoxTools.py @@ -0,0 +1,186 @@ +import logging +import os +import re + +logger = logging.getLogger("CryptoBox") + + +def getAvailablePartitions(): + "retrieve a list of all available containers" + ret_list = [] + try: + "the following reads all lines of /proc/partitions and adds the mentioned devices" + fpart = open("/proc/partitions", "r") + try: + line = fpart.readline() + while line: + p_details = line.split() + if (len(p_details) == 4): + "the following code prevents double entries like /dev/hda and /dev/hda1" + (p_major, p_minor, p_size, p_device) = p_details + ## ignore lines with: invalid minor/major or extend partitions (size=1) + if re.search('^[0-9]*$', p_major) and re.search('^[0-9]*$', p_minor) and (p_size != "1"): + p_parent = re.sub('[1-9]?[0-9]$', '', p_device) + if p_parent == p_device: + if [e for e in ret_list if re.search('^' + p_parent + '[1-9]?[0-9]$', e)]: + "major partition - its children are already in the list" + pass + else: + "major partition - but there are no children for now" + ret_list.append(p_device) + else: + "minor partition - remove parent if necessary" + if p_parent in ret_list: ret_list.remove(p_parent) + ret_list.append(p_device) + line = fpart.readline() + finally: + fpart.close() + return map(getAbsoluteDeviceName, ret_list) + except IOError: + logger.warning("Could not read /proc/partitions") + return [] + + +def getAbsoluteDeviceName(shortname): + """ returns the absolute file name of a device (e.g.: "hda1" -> "/dev/hda1") + this does also work for device mapper devices + if the result is non-unique, one arbitrary value is returned""" + if re.search('^/', shortname): return shortname + default = os.path.join("/dev", shortname) + if os.path.exists(default): return default + result = findMajorMinorOfDevice(shortname) + "if no valid major/minor was found -> exit" + if not result: return default + (major, minor) = result + "for device-mapper devices (major == 254) ..." + if major == 254: + result = findMajorMinorDeviceName("/dev/mapper", major, minor) + if result: return result[0] + "now check all files in /dev" + result = findMajorMinorDeviceName("/dev", major, minor) + if result: return result[0] + return default + + +def findMajorMinorOfDevice(device): + "return the major/minor numbers of a block device" + if re.match("/", device) or not os.path.exists(os.path.join(os.path.sep,"sys","block",device)): + ## maybe it is an absolute device name + if not os.path.exists(device): return None + ## okay - it seems to to a device node + rdev = os.stat(device).st_rdev + return (os.major(rdev), os.minor(rdev)) + blockdev_info_file = os.path.join(os.path.join(os.path.sep,"sys","block", device), "dev") + try: + f_blockdev_info = open(blockdev_info_file, "r") + blockdev_info = f_blockdev_info.read() + f_blockdev_info.close() + (str_major, str_minor) = blockdev_info.split(":") + "numeric conversion" + try: + major = int(str_major) + minor = int(str_minor) + return (major, minor) + except ValueError: + "unknown device numbers -> stop guessing" + return None + except IOError: + pass + return None + + +def findMajorMinorDeviceName(dir, major, minor): + "returns the names of devices with the specified major and minor number" + collected = [] + try: + subdirs = [os.path.join(dir, e) for e in os.listdir(dir) if (not os.path.islink(os.path.join(dir, e))) and os.path.isdir(os.path.join(dir, e))] + "do a recursive call to parse the directory tree" + for dirs in subdirs: + collected.extend(findMajorMinorDeviceName(dirs, major, minor)) + "filter all device inodes in this directory" + collected.extend([os.path.realpath(os.path.join(dir, e)) for e in os.listdir(dir) if (os.major(os.stat(os.path.join(dir, e)).st_rdev) == major) and (os.minor(os.stat(os.path.join(dir, e)).st_rdev) == minor)]) + ## remove double entries + result = [] + for e in collected: + if e not in result: result.append(e) + return result + except OSError: + return [] + + +def getParentBlockDevices(): + devs = [] + for line in file("/proc/partitions"): + p_details = line.split() + ## we expect four values - otherwise continue with next iteration + if len(p_details) != 4: continue + (p_major, p_minor, p_size, p_device) = p_details + ## we expect numeric values in the first two columns + if re.search(u'\D',p_major) or re.search(u'\D',p_minor): continue + ## now let us check, if it is a (parent) block device or a partition + if not os.path.isdir(os.path.join(os.path.sep, "sys", "block", p_device)): continue + devs.append(p_device) + return map(getAbsoluteDeviceName, devs) + + +def isPartOfBlockDevice(parent, subdevice): + """check if the given block device is a parent of 'subdevice' + e.g. for checking if a partition belongs to a block device""" + try: + (par_major, par_minor) = findMajorMinorOfDevice(parent) + (sub_major, sub_minor) = findMajorMinorOfDevice(subdevice) + except TypeError: + ## at least one of these devices did not return a valid major/minor combination + return False + ## search the entry below '/sys/block' belonging to the parent + root = os.path.join(os.path.sep, 'sys', 'block') + for bldev in os.listdir(root): + blpath = os.path.join(root, bldev, 'dev') + if os.access(blpath, os.R_OK): + try: + if (str(par_major), str(par_minor)) == tuple([e for e in file(blpath)][0].strip().split(":",1)): + parent_path = os.path.join(root, bldev) + break + except IndexError, OSError: + pass + else: + ## no block device with this major/minor combination found below '/sys/block' + return False + for subbldev in os.listdir(parent_path): + subblpath = os.path.join(parent_path, subbldev, "dev") + if os.access(subblpath, os.R_OK): + try: + if (str(sub_major), str(sub_minor)) == tuple([e for e in file(subblpath)][0].strip().split(":",1)): + ## the name of the subdevice node is not important - we found it! + return True + except IndexError, OSError: + pass + return False + + +def getBlockDeviceSize(device): + if not device: return -1 + try: + rdev = os.stat(device).st_rdev + except OSError: + return -1 + minor = os.minor(rdev) + major = os.major(rdev) + for f in file("/proc/partitions"): + try: + elements = f.split() + if len(elements) != 4: continue + if (int(elements[0]) == major) and (int(elements[1]) == minor): + return int(elements[2])/1024 + except ValueError: + pass + return -1 + + +def getBlockDeviceSizeHumanly(device): + size = getBlockDeviceSize(device) + if size > 5120: + return "%sGB" % size/1024 + else: + return "%sMB" % size + diff --git a/bin/CryptoBoxWebserver.py b/bin/CryptoBoxWebserver.py new file mode 100755 index 0000000..b841262 --- /dev/null +++ b/bin/CryptoBoxWebserver.py @@ -0,0 +1,38 @@ +#!/usr/bin/env python2.4 +import os +import WebInterfaceSites +import sys + +try: + import cherrypy +except: + print "Could not import the cherrypy module! Try 'apt-get install python-cherrypy'." + sys.exit(1) + +class CryptoBoxWebserver: + '''this class starts the cherryp webserver and serves the single sites''' + + def __init__(self): + cherrypy.root = WebInterfaceSites.WebInterfaceSites() + #expose static content: + #I currently have no idea how to cleanly extract the stylesheet path from + #the config object without an extra CryptoBox.CryptoBoxProps instance. + #perhaps put config handling into a seperate class in CryptoBox.py? + # + # the following manual mapping is necessary, as we may not use relative + # paths in the config file + cherrypy.config.configMap.update({ + "/cryptobox-misc": { + "staticFilter.on" : True, + "staticFilter.dir": os.path.abspath("../www-data" )} + }) + + def start(self): + # just use this config, when we're started directly + cherrypy.config.update(file = "cryptoboxwebserver.conf") + cherrypy.server.start() + +if __name__ == "__main__": + cbw = CryptoBoxWebserver() + cbw.start() + diff --git a/bin/Plugins.py b/bin/Plugins.py new file mode 100644 index 0000000..97a7e83 --- /dev/null +++ b/bin/Plugins.py @@ -0,0 +1,67 @@ +# $Id$ + +import imp +import os +import logging + + +class PluginManager: + """manage available plugins""" + + def __init__(self, cbox, plugin_dirs="."): + self.cbox = cbox + self.log = logging.getLogger("CryptoBox") + if hasattr(plugin_dirs, "__iter__"): + self.plugin_dirs = [os.path.abspath(dir) for dir in plugin_dirs] + else: + self.plugin_dirs = [os.path.abspath(plugin_dirs)] + self.pluginList = self.__getAllPlugins() + + + def getPlugins(self): + return self.pluginList[:] + + + def getPlugin(self, name): + for p in self.pluginList[:]: + if p.getName() == name: + return p + return None + + + def __getAllPlugins(self): + list = [] + for plfile in self.__getPluginFiles(): + list.append(self.__getPluginClass(os.path.basename(plfile)[:-3])) + return list + + + def __getPluginClass(self, name): + for plfile in self.__getPluginFiles(): + if name == os.path.basename(plfile)[:-3]: + try: + pl_class = getattr(imp.load_source(name, plfile), name) + except AttributeError: + return None + return pl_class(self.cbox, os.path.dirname(plfile)) + else: + return None + + + def __getPluginFiles(self): + result = [] + for dir in [os.path.abspath(e) for e in self.plugin_dirs if os.access(e, os.R_OK) and os.path.isdir(e)]: + for plname in [f for f in os.listdir(dir)]: + pldir = os.path.join(dir, plname) + plfile = os.path.join(pldir, plname + ".py") + if os.path.isfile(plfile) and os.access(plfile, os.R_OK): + result.append(plfile) + return result + + +if __name__ == "__main__": + x = PluginManager(None, "../plugins") + for a in x.getPlugins(): + if not a is None: + print "Plugin: %s" % a.getName() + diff --git a/bin/WebInterfaceDataset.py b/bin/WebInterfaceDataset.py new file mode 100644 index 0000000..7f2de6c --- /dev/null +++ b/bin/WebInterfaceDataset.py @@ -0,0 +1,136 @@ +import os +import CryptoBoxContainer +import CryptoBoxTools + +## useful constant for some functions +CONT_TYPES = CryptoBoxContainer.CryptoBoxContainer.Types + +class WebInterfaceDataset(dict): + """this class contains all data that should be available for the clearsilver + templates + """ + + def __init__(self, cbox, prefs, plugins): + self.prefs = prefs + self.cbox = cbox + self.__setConfigValues() + self.plugins = plugins + self.setCryptoBoxState() + self.setPluginData() + self.setContainersState() + + + def setCryptoBoxState(self): + import cherrypy + self["Data.Version"] = self.cbox.VERSION + langs = self.cbox.getAvailableLanguages() + langs.sort() + for (index, lang) in enumerate(langs): + self.cbox.log.info("language loaded: %s" % lang) + self["Data.Languages.%d.name" % index] = lang + self["Data.Languages.%d.link" % index] = self.__getLanguageName(lang) + try: + self["Data.ScriptURL.Prot"] = cherrypy.request.scheme + host = cherrypy.request.headers["Host"] + self["Data.ScriptURL.Host"] = host.split(":",1)[0] + complete_url = "%s://%s" % (self["Data.ScriptURL.Prot"], self["Data.ScriptURL.Host"]) + try: + port = int(host.split(":",1)[1]) + complete_url += ":%s" % port + except (IndexError, ValueError): + if cherrypy.request.scheme == "http": + port = 80 + elif cherrypy.request.scheme == "https": + port = 443 + else: + ## unknown scheme -> port 0 + self.cbox.log.info("unknown protocol scheme used: %s" % (cherrypy.request.scheme,)) + port = 0 + self["Data.ScriptURL.Port"] = port + ## retrieve the relative address of the CGI (or the cherrypy base address) + ## remove the last part of the url and add a slash + path = "/".join(cherrypy.request.path.split("/")[:-1]) + "/" + self["Data.ScriptURL.Path"] = path + complete_url += path + self["Data.ScriptURL"] = complete_url + except AttributeError: + self["Data.ScriptURL"] = "" + + + def setCurrentDiskState(self, device): + for container in self.cbox.getContainerList(): + if container.getDevice() == device: + isEncrypted = (container.getType() == CONT_TYPES["luks"]) and 1 or 0 + isPlain = (container.getType() == CONT_TYPES["plain"]) and 1 or 0 + isMounted = container.isMounted() and 1 or 0 + self["Data.CurrentDisk.device"] = container.getDevice() + self["Data.CurrentDisk.name"] = container.getName() + self["Data.CurrentDisk.encryption"] = isEncrypted + self["Data.CurrentDisk.plaintext"] = isPlain + self["Data.CurrentDisk.active"] = isMounted + self["Data.CurrentDisk.size"] = CryptoBoxTools.getBlockDeviceSizeHumanly(container.getDevice()) + if isMounted: + (size, avail, used) = container.getCapacity() + percent = used / size + self["Data.CurrentDisk.capacity.used"] = used + self["Data.CurrentDisk.capacity.free"] = avail + self["Data.CurrentDisk.capacity.size"] = size + self["Data.CurrentDisk.capacity.percent"] = percent + self["Settings.LinkAttrs.device"] = device + + + def setContainersState(self): + avail_counter = 0 + active_counter = 0 + for container in self.cbox.getContainerList(): + ## useful if the container was changed during an action + container.resetObject() + isEncrypted = (container.getType() == CONT_TYPES["luks"]) and 1 or 0 + isPlain = (container.getType() == CONT_TYPES["plain"]) and 1 or 0 + isMounted = container.isMounted() and 1 or 0 + self["Data.Disks.%d.device" % avail_counter] = container.getDevice() + self["Data.Disks.%d.name" % avail_counter] = container.getName() + self["Data.Disks.%d.encryption" % avail_counter] = isEncrypted + self["Data.Disks.%d.plaintext" % avail_counter] = isPlain + self["Data.Disks.%d.active" % avail_counter] = isMounted + self["Data.Disks.%d.size" % avail_counter] = CryptoBoxTools.getBlockDeviceSizeHumanly(container.getDevice()) + if isMounted: active_counter += 1 + avail_counter += 1 + self["Data.activeDisksCount"] = active_counter + + + def setPluginData(self): + for p in self.plugins: + lang_data = p.getLanguageData() + entryName = "Settings.PluginList." + p.getName() + self[entryName] = p.getName() + self[entryName + ".Link"] = lang_data.getValue("Link", p.getName()) + self[entryName + ".Rank"] = p.getRank() + self[entryName + ".RequestAuth"] = p.isAuthRequired() and "1" or "0" + self[entryName + ".Enabled"] = p.isEnabled() and "1" or "0" + for a in p.pluginCapabilities: + self[entryName + ".Types." + a] = "1" + + + def __setConfigValues(self): + self["Settings.TemplateDir"] = os.path.abspath(self.prefs["Locations"]["TemplateDir"]) + self["Settings.LanguageDir"] = os.path.abspath(self.prefs["Locations"]["LangDir"]) + self["Settings.DocDir"] = os.path.abspath(self.prefs["Locations"]["DocDir"]) + self["Settings.Stylesheet"] = self.prefs["WebSettings"]["Stylesheet"] + self["Settings.Language"] = self.prefs["WebSettings"]["Language"] + self["Settings.PluginDir"] = self.prefs["Locations"]["PluginDir"] + self["Settings.SettingsDir"] = self.prefs["Locations"]["SettingsDir"] + + + def __getLanguageName(self, lang): + try: + import neo_cgi, neo_util, neo_cs + except: + raise CryptoBoxExceptions.CBEnvironmentError("couldn't import 'neo_*'! Try 'apt-get install python-clearsilver'.") + hdf_path = os.path.join(self.prefs["Locations"]["LangDir"], lang + ".hdf") + hdf = neo_util.HDF() + hdf.readFile(hdf_path) + return hdf.getValue("Name",lang) + + + diff --git a/bin/WebInterfaceSites.py b/bin/WebInterfaceSites.py new file mode 100755 index 0000000..82405e9 --- /dev/null +++ b/bin/WebInterfaceSites.py @@ -0,0 +1,427 @@ +import CryptoBox +import WebInterfaceDataset +import re +import Plugins +from CryptoBoxExceptions import * +import cherrypy +import types +import os + +try: + import neo_cgi, neo_util, neo_cs +except ImportError: + errorMsg = "Could not import clearsilver module. Try 'apt-get install python-clearsilver'." + self.log.error(errorMsg) + sys.stderr.write(errorMsg) + raise ImportError, errorMsg + + + +class PluginIconHandler: + + def __init__(self, plugins): + for plugin in plugins.getPlugins(): + if not plugin: continue + plname = plugin.getName() + ## expose the getIcon function of this plugin + setattr(self, plname, plugin.getIcon) + + + +class WebInterfaceSites: + ''' + ''' + + ## this template is used under strange circumstances + defaultTemplate = "empty" + + + def __init__(self): + import logging + self.cbox = CryptoBox.CryptoBoxProps() + self.log = logging.getLogger("CryptoBox") + self.prefs = self.cbox.prefs + self.__resetDataset() + + + def __resetDataset(self): + """this method has to be called at the beginning of every "site" action + important: only at the beginning of an action (to not loose information) + important: for _every_ "site" action (cherrypy is stateful) + also take care for the plugins, as they also contain datasets + """ + self.__loadPlugins() + self.dataset = WebInterfaceDataset.WebInterfaceDataset(self.cbox, self.prefs, self.pluginList.getPlugins()) + ## publish plugin icons + self.icons = PluginIconHandler(self.pluginList) + self.icons.exposed = True + ## check, if a configuration partition has become available + self.cbox.prefs.preparePartition() + + + def __loadPlugins(self): + self.pluginList = Plugins.PluginManager(self.cbox, self.prefs["Locations"]["PluginDir"]) + for plugin in self.pluginList.getPlugins(): + if not plugin: continue + plname = plugin.getName() + if plugin.isEnabled(): + self.cbox.log.info("Plugin '%s' loaded" % plname) + ## this should be the "easiest" way to expose all plugins as URLs + setattr(self, plname, self.return_plugin_action(plugin)) + setattr(getattr(self, plname), "exposed", True) + # TODO: check, if this really works - for now the "stream_response" feature seems to be broken + #setattr(getattr(self, plname), "stream_respones", True) + else: + self.cbox.log.info("Plugin '%s' is disabled" % plname) + ## remove the plugin, if it was active before + setattr(self, plname, None) + + + ## this is a function decorator to check authentication + ## it has to be defined before any page definition requiring authentification + def __requestAuth(self=None): + def check_credentials(site): + def _inner_wrapper(self, *args, **kargs): + import base64 + ## define a "non-allowed" function + user, password = None, None + try: + resp = cherrypy.request.headers["Authorization"][6:] # ignore "Basic " + (user, password) = base64.b64decode(resp).split(":",1) + except KeyError: + ## no "authorization" header was sent + pass + except TypeError: + ## invalid base64 string + pass + except AttributeError: + ## no cherrypy response header defined + pass + authDict = self.cbox.prefs.userDB["admins"] + if user in authDict.keys(): + if self.cbox.prefs.userDB.getDigest(password) == authDict[user]: + ## ok: return the choosen page + self.cbox.log.info("access granted for: %s" % user) + return site(self, *args, **kargs) + else: + self.cbox.log.info("wrong password supplied for: %s" % user) + else: + self.cbox.log.info("unknown user: %s" % str(user)) + ## wrong credentials: return "access denied" + cherrypy.response.headers["WWW-Authenticate"] = '''Basic realm="CryptoBox"''' + cherrypy.response.status = 401 + return self.__render("access_denied") + return _inner_wrapper + return check_credentials + + + ###################################################################### + ## put real sites down here and don't forget to expose them at the end + + + @cherrypy.expose + def index(self, weblang=""): + self.__resetDataset() + self.__setWebLang(weblang) + self.__checkEnvironment() + ## do not forget the language! + param_dict = {"weblang":weblang} + ## render "disks" plugin by default + return self.return_plugin_action(self.pluginList.getPlugin("disks"))(**param_dict) + + + def return_plugin_action(self, plugin): + def handler(self, **args): + self.__resetDataset() + self.__checkEnvironment() + args_orig = dict(args) + ## set web interface language + try: + self.__setWebLang(args["weblang"]) + del args["weblang"] + except KeyError: + self.__setWebLang("") + ## we always read the "device" setting - otherwise volume-plugin links + ## would not work easily (see "volume_props" linking to "format_fs") + ## it will get ignored for non-volume plugins + try: + plugin.device = None + if self.__setDevice(args["device"]): + plugin.device = args["device"] + del args["device"] + except KeyError: + pass + ## check the device argument of volume plugins + if "volume" in plugin.pluginCapabilities: + ## initialize the dataset of the selected device if necessary + if plugin.device: + self.dataset.setCurrentDiskState(plugin.device) + else: + ## invalid (or missing) device setting + return self.__render(self.defaultTemplate) + ## check if there is a "redirect" setting - this will override the return + ## value of the doAction function (e.g. useful for umount-before-format) + try: + if args["redirect"]: + override_nextTemplate = { "plugin":args["redirect"] } + if "volume" in plugin.pluginCapabilities: + override_nextTemplate["values"] = {"device":plugin.device} + del args["redirect"] + except KeyError: + override_nextTemplate = None + ## call the plugin handler + nextTemplate = plugin.doAction(**args) + ## for 'volume' plugins: reread the dataset of the current disk + ## additionally: set the default template for plugins + if "volume" in plugin.pluginCapabilities: + ## maybe the state of the current volume was changed? + self.dataset.setCurrentDiskState(plugin.device) + if not nextTemplate: nextTemplate = { "plugin":"volume_mount", "values":{"device":plugin.device}} + else: + ## maybe a non-volume plugin changed some plugin settings (e.g. plugin_manager) + self.dataset.setPluginData() + ## update the container hdf-dataset (maybe a plugin changed the state of a container) + self.dataset.setContainersState() + ## default page for non-volume plugins is the disk selection + if not nextTemplate: nextTemplate = { "plugin":"disks", "values":{} } + ## was a redirect requested? + if override_nextTemplate: + nextTemplate = override_nextTemplate + ## if another plugins was choosen for 'nextTemplate', then do it! + if isinstance(nextTemplate, types.DictType) \ + and "plugin" in nextTemplate.keys() \ + and "values" in nextTemplate.keys() \ + and self.pluginList.getPlugin(nextTemplate["plugin"]): + valueDict = dict(nextTemplate["values"]) + ## force the current weblang attribute - otherwise it gets lost + valueDict["weblang"] = self.dataset["Settings.Language"] + new_plugin = self.pluginList.getPlugin(nextTemplate["plugin"]) + return self.return_plugin_action(new_plugin)(**valueDict) + ## save the currently active plugin name + self.dataset["Data.ActivePlugin"] = plugin.getName() + return self.__render(nextTemplate, plugin) + ## apply authentication? + if plugin.isAuthRequired(): + return lambda **args: self.__requestAuth()(handler)(self, **args) + else: + return lambda **args: handler(self, **args) + + + ## test authentication + @cherrypy.expose + @__requestAuth + def test(self, weblang=""): + self.__resetDataset() + self.__setWebLang(weblang) + self.__checkEnvironment() + return "test passed" + + + @cherrypy.expose + def test_stream(self): + """just for testing purposes - to check if the "stream_response" feature + actually works - for now (September 02006) it does not seem to be ok""" + import time + yield "neu

" + + + + ##################### input checker ########################## + + def __checkEnvironment(self): + """here we should place all interesting checks to inform the user of problems + + examples are: non-https, readonly-config, ... + """ + ## TODO: maybe add an option "mount"? + if self.cbox.prefs.requiresPartition() and not self.cbox.prefs.getActivePartition(): + self.dataset["Data.EnvironmentWarning"] = "ReadOnlyConfig" + # TODO: turn this on soon (add "not") - for now it is annoying + if self.__checkHTTPS(): + self.dataset["Data.EnvironmentWarning"] = "NoSSL" + + + def __checkHTTPS(self): + ## check the request scheme + if cherrypy.request.scheme == "https": return True + ## check an environment setting - this is quite common behind proxies + try: + if os.environ["HTTPS"]: return True + except KeyError: + pass + ## check http header TODO (check pound for the name) + try: + if cherrypy.request.headers["TODO"]: return True + except KeyError: + pass + ## the connection seems to be unencrypted + return False + + + def __setWebLang(self, value): + guess = value + availLangs = self.cbox.getAvailableLanguages() + ## no language specified: check browser language + if not guess: + guess = self.__getPreferredBrowserLanguage(availLangs) + ## no preferred language or invalid language? + if not guess \ + or not guess in availLangs \ + or re.search(u'\W', guess): + ## warn only for invalid languages + if not guess is None: + self.cbox.log.info("invalid language choosen: %s" % guess) + guess = self.prefs["WebSettings"]["Language"] + ## maybe the language is still not valid + if not guess in availLangs: + self.log.warn("the configured language is invalid: %s" % guess) + guess = "en" + ## maybe there is no english dataset??? + if not guess in availLangs: + self.log.warn("couldn't find the english dataset") + guess = availLangs[0] + self.dataset["Settings.Language"] = guess + ## we only have to save it, if it was specified correctly and explicitly + if value == guess: + self.dataset["Settings.LinkAttrs.weblang"] = guess + + + def __getPreferredBrowserLanguage(self, availLangs): + """guess the preferred language of the user (as sent by the browser) + take the first language, that is part of 'availLangs' + """ + try: + pref_lang_header = cherrypy.request.headers["Accept-Language"] + except KeyError: + ## no language header was specified + return None + ## this could be a typical 'Accept-Language' header: + ## de-de,de;q=0.8,en-us;q=0.5,en;q=0.3 + regex = re.compile(u"\w+(-\w+)?(;q=[\d\.]+)?$") + pref_langs = [e.split(";",1)[0] + for e in pref_lang_header.split(",") + if regex.match(e)] + ## is one of these preferred languages available? + for lang in pref_langs: + if lang in availLangs: return lang + ## we try to be nice: also look for "de" if "de-de" was specified ... + for lang in pref_langs: + ## use only the first part of the language + short_lang = lang.split("-",1)[0] + if short_lang in availLangs: return short_lang + ## we give up + return None + + + def __setDevice(self, device): + if device and re.match(u'[\w /\-]+$', device) and self.cbox.getContainer(device): + self.log.debug("select device: %s" % device) + return True + else: + self.log.warn("invalid device: %s" % device) + self.dataset["Data.Warning"] = "InvalidDevice" + return False + + + def __checkVolumeName(self, name): + if name and re.match(u'[\w \-]+$', name): + return True + else: + return False + + + def __getLanguageValue(self, value): + hdf = self.__getLanguageData(self.dataset["Settings.Language"]) + return hdf.getValue(value, "") + + + def __getLanguageData(self, web_lang="en"): + default_lang = "en" + conf_lang = self.prefs["WebSettings"]["Language"] + hdf = neo_util.HDF() + langDir = os.path.abspath(self.prefs["Locations"]["LangDir"]) + langFiles = [] + ## first: read default language (en) + if (default_lang != conf_lang) and (default_lang != web_lang): + langFiles.append(os.path.join(langDir, default_lang + ".hdf")) + ## second: read language as defined in the config file + if (conf_lang != web_lang): + langFiles.append(os.path.join(langDir, conf_lang + ".hdf")) + ## third: read language as configured via web interface + langFiles.append(os.path.join(langDir, web_lang + ".hdf")) + for langFile in langFiles: + if os.access(langFile, os.R_OK): + hdf.readFile(langFile) + else: + log.warn("Couldn't read language file: %s" % langFile) + return hdf + + + def __render(self, renderInfo, plugin=None): + '''renders from clearsilver templates and returns the resulting html + ''' + ## is renderInfo a string (filename of the template) or a dictionary? + if type(renderInfo) == types.DictType: + template = renderInfo["template"] + if renderInfo.has_key("generator"): + generator = renderInfo["generator"] + else: + generator = False + else: + (template, generator) = (renderInfo, None) + + ## load the language data + hdf = neo_util.HDF() + hdf.copy("Lang", self.__getLanguageData(self.dataset["Settings.Language"])) + + ## first: assume, that the template file is in the global template directory + self.dataset["Settings.TemplateFile"] = os.path.abspath(os.path.join(self.prefs["Locations"]["TemplateDir"], template + ".cs")) + + if plugin: + ## check, if the plugin provides the template file -> overriding + plugin_cs_file = plugin.getTemplateFileName(template) + if plugin_cs_file: + self.dataset["Settings.TemplateFile"] = plugin_cs_file + + ## add the current state of the plugins to the hdf dataset + self.dataset["Data.Status.Plugins.%s" % plugin.getName()] = plugin.getStatus() + ## load the language data + pl_lang = plugin.getLanguageData(self.dataset["Settings.Language"]) + if pl_lang: + hdf.copy("Lang.Plugins.%s" % plugin.getName(), pl_lang) + ## load the dataset of the plugin + plugin.loadDataSet(hdf) + + self.log.info("rendering site: " + template) + + cs_path = os.path.abspath(os.path.join(self.prefs["Locations"]["TemplateDir"], "main.cs")) + if not os.access(cs_path, os.R_OK): + log.error("Couldn't read clearsilver file: %s" % cs_path) + yield "Couldn't read clearsilver file: %s" % cs_path + return + + self.log.debug(self.dataset) + for key in self.dataset.keys(): + hdf.setValue(key,str(self.dataset[key])) + cs = neo_cs.CS(hdf) + cs.parseFile(cs_path) + + ## is there a generator containing additional information? + if generator is None: + ## all content in one flush + yield cs.render() + else: + content_generate = generator() + dummy_line = """""" + ## now we do it linewise - checking for the content marker + for line in cs.render().splitlines(): + if line.find(dummy_line) != -1: + yield line.replace(dummy_line, content_generate.next()) + else: + yield line + "\n" + + diff --git a/bin/WebInterfaceTestClass.py b/bin/WebInterfaceTestClass.py new file mode 100644 index 0000000..c210a0f --- /dev/null +++ b/bin/WebInterfaceTestClass.py @@ -0,0 +1,77 @@ +""" +super class of all web interface unittests for the cryptobox + +just inherit this class and add some test functions +""" + +import unittest +import twill +import cherrypy +import WebInterfaceSites + +## we do the following, for easy surfing +## e.g. use: cbx.go(your_url) +## commands api: http://twill.idyll.org/commands.html +CBXHOST="localhost" +CBXPORT=8081 +CBX_URL="http://%s:%d/" % (CBXHOST, CBXPORT) +LOG_FILE="/tmp/twill.log" + +class WebInterfaceTestClass(unittest.TestCase): + '''this class checks the webserver, using "twill" + + the tests in this class are from the browsers point of view, so not + really unittests. + fetch twill from: http://twill.idyll.org + one way to manually run twill code is through the python + interpreter commandline e.g.: + + import twill + twill.shell.main() + go http://localhost:8080 + find "my very special html content" + help + ''' + + def setUp(self): + '''configures the cherrypy server that it works nice with twill + ''' + cherrypy.config.update({ + 'server.logToScreen' : False, + 'autoreload.on': False, + 'server.threadPool': 1, + 'server.environment': 'production', + }) + cherrypy.root = WebInterfaceSites.WebInterfaceSites() + cherrypy.server.start(initOnly=True, serverClass=None) + + from cherrypy._cpwsgi import wsgiApp + twill.add_wsgi_intercept(CBXHOST, CBXPORT, lambda: wsgiApp) + + # grab the output of twill commands + self.output = open(LOG_FILE,"a") + twill.set_output(self.output) + self.cmd = twill.commands + self.URL = CBX_URL + self.cbox = cherrypy.root.cbox + self.globals, self.locals = twill.namespaces.get_twill_glocals() + + + def tearDown(self): + '''clean up the room when leaving''' + # remove intercept. + twill.remove_wsgi_intercept(CBXHOST, CBXPORT) + # shut down the cherrypy server. + cherrypy.server.stop() + self.output.close() + + + def __get_soup(): + browser = twill.commands.get_browser() + soup = BeautifulSoup(browser.get_html()) + return soup + + + def register_auth(self, url, user="admin", password="admin"): + self.cmd.add_auth("CryptoBox", url, user, password) + diff --git a/bin/coding_guidelines.txt b/bin/coding_guidelines.txt new file mode 100644 index 0000000..a6fb47c --- /dev/null +++ b/bin/coding_guidelines.txt @@ -0,0 +1,18 @@ +Maybe we can add some notes here to get a consistent coding experience :) + +------------------------------------------------------------------------------- + +comments: + - should be usable for pydoc + - ''' or """ at the beginning of every class/method + - ## for longterm comments, that are useful for understanding + - #blabla for codelines, that are out for experimenting and might be used later again + +error handling: + - unspecific error handling is evil (try: "grep -r except: .") + +unit testing: + - first write a unittest and then write the relating code until the unittest stops failing :) + - 'unittests.ClassName.py' should contain all tests for 'ClassName.py' + - commits with broken unit tests are evil (fix or disable the code (not the test ;) )) + diff --git a/bin/cryptobox.conf b/bin/cryptobox.conf new file mode 100644 index 0000000..02ef334 --- /dev/null +++ b/bin/cryptobox.conf @@ -0,0 +1,83 @@ +[Main] + +# comma separated list of possible prefixes for accesible devices +# beware: .e.g "/dev/hd" grants access to _all_ harddisks +AllowedDevices = /dev/loop, /dev/ubdb + +# use sepepate config partition? (1=yes / 0=no) +UseConfigPartition = 1 + +# the default name prefix of not unnamed containers +DefaultVolumePrefix = "Disk " + +# which cipher should cryptsetup-luks use? +#TODO: uml does not support this module - DefaultCipher = aes-cbc-essiv:sha256 +DefaultCipher = aes-plain + +# label of the configuration partition (you should never change this) +ConfigVolumeLabel = cbox_config + + +[Locations] +# where should we mount volumes? +# this directory must be writeable by the cryptobox user (see above) +MountParentDir = /var/cache/cryptobox/mnt + +# settings directory: contains name database and plugin configuration +SettingsDir = /var/cache/cryptobox/settings + +# where are the clearsilver templates? +#TemplateDir = /usr/share/cryptobox/templates +TemplateDir = ../templates + +# path to language files +#LangDir = /usr/share/cryptobox/lang +LangDir = ../lang + +# path to documentation files +#DocDir = /usr/share/doc/cryptobox/html +DocDir = ../doc/html + +# path to the plugin directory +#PluginDir = /usr/share/cryptobox/plugins +PluginDir = ../plugins + + + +[Log] +# possible values are "debug", "info", "warn" and "error" or numbers from +# 0 (debug) to 7 (error) +Level = debug + +# where to write the log messages to? +# possible values are: file +# syslog support will be added later +Destination = file + +# depending on the choosen destination (see above) you may select +# details. Possible values for the different destinations are: +# file: $FILENAME +# syslog: $LOG_FACILITY +#Details = /var/log/cryptobox.log +Details = ./cryptobox.log + + +[WebSettings] +# URL of default stylesheet +Stylesheet = /cryptobox-misc/cryptobox.css + +# default language +Language = de + + +[Programs] +cryptsetup = /sbin/cryptsetup +mkfs-data = /sbin/mkfs.ext3 +blkid = /sbin/blkid +blockdev = /sbin/blockdev +mount = /bin/mount +umount = /bin/umount +super = /usr/bin/super +# this is the "program" name as defined in /etc/super.tab +CryptoBoxRootActions = CryptoBoxRootActions + diff --git a/bin/cryptoboxd b/bin/cryptoboxd new file mode 100755 index 0000000..85258bd --- /dev/null +++ b/bin/cryptoboxd @@ -0,0 +1,39 @@ +#!/bin/sh + +#TODO: CBXPATH=/usr/lib/cryptobox +CBXPATH=$(pwd) +CBXSERVER=CryptoBoxWebserver.py +PIDFILE=/var/run/cryptobox.pid +DAEMON=/usr/bin/python2.4 +DAEMON_OPTS=${CBXPATH}/CryptoBoxWebserver.py +NAME=cryptoboxd +DESC="CryptoBox Daemon (webinterface)" +#TODO: RUNAS=cryptobox +RUNAS=$USERNAME + +#test -x $DAEMON -a -f /etc/exports || exit 0 + +set -e + +case "$1" in + start) + echo -n "Starting $DESC: " + start-stop-daemon --background --chdir "$CBXPATH" --chuid "$RUNAS" --start --quiet --oknodo --user "$RUNAS" --make-pidfile --pidfile "$PIDFILE" --exec "$DAEMON" \ + -- $DAEMON_OPTS + echo "$NAME." + ;; + + stop) + echo -n "Stopping $DESC: " + #FIXME: this is the same as "killall python2.4" + # using a pid file instead prevents problems, but does not kill children??? + start-stop-daemon --stop --oknodo --exec "$DAEMON" + echo "$NAME." + ;; + *) + echo "Usage: $(basename $0) {start|stop}" >&2 + exit 1 + ;; +esac + +exit 0 diff --git a/bin/cryptoboxwebserver.conf b/bin/cryptoboxwebserver.conf new file mode 100644 index 0000000..b8e1e6a --- /dev/null +++ b/bin/cryptoboxwebserver.conf @@ -0,0 +1,17 @@ +[global] +server.socketPort = 8080 +#server.environment = "production" +server.environment = "development" +server.logToScreen = True +server.log_tracebacks = True +server.threadPool = 1 +server.reverseDNS = False +server.logFile = "cryptoboxwebserver.log" + +[/favicon.ico] +static_filter.on = True +# TODO: use live-cd/live-cd-tree.d/var/www/favicon.ico +static_filter.file = "/usr/share/doc/python-cherrypy/cherrypy/favicon.ico" + +[/test_stream] +stream_response = True diff --git a/bin/do_unittests.sh b/bin/do_unittests.sh new file mode 100755 index 0000000..6af0d02 --- /dev/null +++ b/bin/do_unittests.sh @@ -0,0 +1,22 @@ +#!/bin/sh +# +# run this script _before_ you do a commit and fix errors before uploading +# + +# check if /dev/loop1 is available - otherwise some tests will fail! +if /sbin/losetup /dev/loop1 &>/dev/null + then true + else echo "misconfiguration detected: sorry - you need /dev/loop1 for the tests" >&2 + echo "just do the following:" >&2 + echo " dd if=/dev/zero of=test.img bs=1M count=1 seek=100" >&2 + echo " sudo /sbin/losetup /dev/loop1 test.img" >&2 + echo "then you can run the tests again ..." >&2 + echo >&2 + exit 1 + fi + +# do the tests +for a in unittests.*.py + do testoob -v "$a" + done + diff --git a/bin/example-super.tab b/bin/example-super.tab new file mode 100644 index 0000000..03d21c0 --- /dev/null +++ b/bin/example-super.tab @@ -0,0 +1,2 @@ +# adapt the following line to your local setup and add it to /etc/super.tab +CryptoBoxRootActions /your/local/path/to/CryptoBoxRootActions.py yourUserName diff --git a/bin/test.complete.CryptoBox.py b/bin/test.complete.CryptoBox.py new file mode 100755 index 0000000..db5300d --- /dev/null +++ b/bin/test.complete.CryptoBox.py @@ -0,0 +1,116 @@ +#!/usr/bin/env python2.4 + +""" +BEWARE: this script may overwrite the data of one of your loop devices. You +should restrict the AllowedDevices directive in cryptobox.conf to exclude +your precious black devices from being used by this script. + +the following script runs a number of tests for different parts +""" + +from CryptoBox import CryptoBoxProps +from CryptoBoxContainer import CryptoBoxContainer +import sys + + +def main(): + cb = CryptoBoxProps() + + print "Confguration:" + print "\tConfig file:\t\t%s" % (cb.prefs.prefs.filename, ) + print "\tAllowed devices:\t%s" % (cb.prefs["Main"]["AllowedDevices"], ) + + """for e in cb.getContainerList(filterType=CryptoBoxContainer.Types["luks"]):""" + for e in cb.getContainerList(): + print "\t\t%d\t\t%s - %s - %d" % (cb.getContainerList().index(e), e.getDevice(), e.getName(), e.getType()) + + if not cb.getContainerList() or len(cb.getContainerList()) < 1: + print "no loop devices found for testing" + sys.exit(1) + + if len(cb.getContainerList()) > 1: + print "I found more than one available loop device - I will stop now to avoid risking data loss." + print "Please change the 'AllowedDevices' setting in 'cryptobox.conf' to reduce the number of allowed devices to only one." + sys.exit(1) + + testElement = cb.getContainerList()[0] + print "\nRunning some tests now ..." + if not plain_tests(testElement): + print "some previous tests failed - we should stop now" + sys.exit(1) + luks_tests(testElement) + + +" ***************** some functions ******************** " + +def luks_tests(e): + # umount if necessary + try: + e.umount() + except "MountError": + pass + + e.create(e.Types["luks"], "alt") + print "\tluks create:\tok" + + e.changePassword("alt","neu") + print "\tluks changepw:\tok" + + e.setName("lalla") + print "\tluks setName:\tok" + + try: + e.mount("neu") + except "MountError": + pass + if e.isMounted(): print "\tluks mount:\tok" + else: print "\tluks mount:\tfailed" + + print "\tCapacity (size, free, used) [MB]:\t%s" % (e.getCapacity(), ) + + try: + e.umount() + except "MountError": + pass + if e.isMounted(): print "\tluks umount:\tfailed" + else: print "\tluks umount:\tok" + + if e.isMounted(): return False + else: return True + + +def plain_tests(e): + # umount if necessary + try: + e.umount() + except "MountError": + pass + + e.create(e.Types["plain"]) + print "\tplain create:\tok" + + e.setName("plain-lili") + print "\tplain setName:\tok" + + try: + e.mount() + except "MountError": + pass + if e.isMounted(): print "\tplain mount:\tok" + else: print "\tplain mount:\tfailed" + + print "\tCapacity (size, free, used) [MB]:\t%s" % (e.getCapacity(), ) + + try: + e.umount() + except "MountError": + pass + if e.isMounted(): print "\tplain umount:\tfailed" + else: print "\tplain umount:\tok" + + if e.isMounted(): return False + else: return True + +# ************ main **************** + +main() diff --git a/bin/uml-setup.sh b/bin/uml-setup.sh new file mode 100755 index 0000000..8826f3d --- /dev/null +++ b/bin/uml-setup.sh @@ -0,0 +1,23 @@ +#!/bin/sh + +ROOT_IMG=/home/lars/devel-stuff/devel-chroots/cryptobox.img +TEST_IMG=test.img +TEST_SIZE=256 +MEM_SIZE=128M + +# Preparations: +# echo "tun" >>/etc/modules +# follow the instructions in /usr/share/doc/uml-utilities/README.Debian +# add your user to the group 'uml-net' +# + +/sbin/ifconfig tap0 &>/dev/null || { echo "tap0 is not configured - read /usr/share/doc/uml-utilities/README.Debian for hints"; exit 1; } + + +if [ ! -e "$TEST_IMG" ] + then echo "Creating testing image file ..." + dd if=/dev/zero of="$TEST_IMG" bs=1M count=$TEST_SIZE + fi + +linux ubd0="$ROOT_IMG" ubd1="$TEST_IMG" con=xterm hostfs=../ fakehd eth0=daemon mem=$MEM_SIZE + diff --git a/bin/unittests.CryptoBox.py b/bin/unittests.CryptoBox.py new file mode 100755 index 0000000..baaad3c --- /dev/null +++ b/bin/unittests.CryptoBox.py @@ -0,0 +1,138 @@ +#!/usr/bin/env python2.4 + +import unittest +import sys +from CryptoBox import * +from CryptoBoxExceptions import * +import CryptoBoxSettings + +class CryptoBoxPropsDeviceTests(unittest.TestCase): + import CryptoBox + cb = CryptoBox.CryptoBoxProps() + + def testAllowedDevices(self): + '''isDeviceAllowed should accept permitted devices''' + self.assertTrue(self.cb.isDeviceAllowed("/dev/loop")) + self.assertTrue(self.cb.isDeviceAllowed("/dev/loop1")) + self.assertTrue(self.cb.isDeviceAllowed("/dev/loop/urgd")) + self.assertTrue(self.cb.isDeviceAllowed("/dev/usb/../loop1")) + + def testDeniedDevices(self): + '''isDeviceAllowed should fail with not explicitly allowed devices''' + self.assertFalse(self.cb.isDeviceAllowed("/dev/hda")) + self.assertFalse(self.cb.isDeviceAllowed("/dev/loopa/../hda")) + self.assertFalse(self.cb.isDeviceAllowed("/")) + + +class CryptoBoxPropsConfigTests(unittest.TestCase): + '''test here if everything with the config turns right''' + import os + import CryptoBox + + files = { + "configFileOK" : "cbox-test_ok.conf", + "configFileBroken" : "cbox-test_broken.conf", + "nameDBFile" : "cryptobox_names.db", + "pluginConf" : "cryptobox_plugins.conf", + "userDB" : "cryptobox_users.db", + "logFile" : "cryptobox.log", + "tmpdir" : "cryptobox-mnt" } + tmpdirname = "" + filenames = {} + configContentOK = """ +[Main] +AllowedDevices = /dev/loop +DefaultVolumePrefix = "Data " +DefaultCipher = aes-cbc-essiv:sha256 +[Locations] +SettingsDir = %s +MountParentDir = %s +TemplateDir = ../templates +LangDir = ../lang +DocDir = ../doc/html +PluginDir = ../plugins +[Log] +Level = debug +Destination = file +Details = %s/cryptobox.log +[WebSettings] +Stylesheet = /cryptobox-misc/cryptobox.css +[Programs] +blkid = /sbin/blkid +cryptsetup = /sbin/cryptsetup +super = /usr/bin/super +CryptoBoxRootActions = CryptoBoxRootActions +""" + + + def setUp(self): + '''generate all files in tmp and remember the names''' + import tempfile + os = self.os + self.tmpdirname = tempfile.mkdtemp(prefix="cbox-") + for file in self.files.keys(): + self.filenames[file] = os.path.join(self.tmpdirname, self.files[file]) + self.writeConfig() + + + def tearDown(self): + '''remove the created tmpfiles''' + os = self.os + # remove temp files + for file in self.filenames.values(): + compl_name = os.path.join(self.tmpdirname, file) + if os.path.exists(compl_name): + os.remove(compl_name) + # remove temp dir + os.rmdir(self.tmpdirname) + + + def testConfigInit(self): + '''Check various branches of config file loading''' + import os + self.assertRaises(CBConfigUnavailableError, self.CryptoBox.CryptoBoxProps,"/invalid/path/to/config/file") + self.assertRaises(CBConfigUnavailableError, self.CryptoBox.CryptoBoxProps,"/etc/shadow") + """ check one of the following things: + 1) are we successfully using an existing config file? + 2) do we break, if no config file is there? + depending on the existence of a config file, only one of these conditions + can be checked - hints for more comprehensive tests are appreciated :) """ + for a in CryptoBoxSettings.CryptoBoxSettings.CONF_LOCATIONS: + if os.path.exists(a): + self.CryptoBox.CryptoBoxProps() + break # this skips the 'else' clause + else: self.assertRaises(CBConfigUnavailableError, self.CryptoBox.CryptoBoxProps) + self.assertRaises(CBConfigUnavailableError, self.CryptoBox.CryptoBoxProps,[]) + + + def testBrokenConfigs(self): + """Check various broken configurations""" + self.writeConfig("SettingsDir", "SettingsDir=/foo/bar", filename=self.filenames["configFileBroken"]) + self.assertRaises(CBConfigError, self.CryptoBox.CryptoBoxProps,self.filenames["configFileBroken"]) + self.writeConfig("Level", "Level = ho", filename=self.filenames["configFileBroken"]) + self.assertRaises(CBConfigError, self.CryptoBox.CryptoBoxProps,self.filenames["configFileBroken"]) + self.writeConfig("Details", "#out", filename=self.filenames["configFileBroken"]) + self.assertRaises(CBConfigError, self.CryptoBox.CryptoBoxProps,self.filenames["configFileBroken"]) + self.writeConfig("super", "super=/bin/invalid/no", filename=self.filenames["configFileBroken"]) + self.assertRaises(CBConfigError, self.CryptoBox.CryptoBoxProps,self.filenames["configFileBroken"]) + self.writeConfig("CryptoBoxRootActions", "#not here", filename=self.filenames["configFileBroken"]) + self.assertRaises(CBConfigError, self.CryptoBox.CryptoBoxProps,self.filenames["configFileBroken"]) + self.writeConfig("CryptoBoxRootActions", "CryptoBoxRootActions = /bin/false", filename=self.filenames["configFileBroken"]) + self.assertRaises(CBEnvironmentError, self.CryptoBox.CryptoBoxProps,self.filenames["configFileBroken"]) + + + def writeConfig(self, replace=None, newline=None, filename=None): + """write a config file and (optional) replace a line in it""" + import re + if not filename: filename = self.filenames["configFileOK"] + content = self.configContentOK % (self.tmpdirname, self.tmpdirname, self.tmpdirname) + if replace: + pattern = re.compile('^' + replace + '\\s*=.*$', flags=re.M) + content = re.sub(pattern, newline, content) + cf = open(filename, "w") + cf.write(content) + cf.close() + + +if __name__ == "__main__": + unittest.main() diff --git a/bin/unittests.CryptoBoxTools.py b/bin/unittests.CryptoBoxTools.py new file mode 100755 index 0000000..10daf4e --- /dev/null +++ b/bin/unittests.CryptoBoxTools.py @@ -0,0 +1,48 @@ +#!/usr/bin/env python2.4 + +import unittest +import CryptoBoxTools +import os + + +class CryptoBoxToolsTests(unittest.TestCase): + + def testGetAbsoluteDeviceName(self): + func = CryptoBoxTools.getAbsoluteDeviceName + self.assertTrue(func("hda") == "/dev/hda") + self.assertTrue(func("loop0") == "/dev/loop0") + self.assertTrue(func(os.path.devnull) == os.path.devnull) + + + def testFindMajorMinorOfDevice(self): + func = CryptoBoxTools.findMajorMinorOfDevice + self.assertTrue(func("/dev/hda") == (3,0)) + self.assertTrue(func("/dev/hda1") == (3,1)) + self.assertTrue(func(os.path.devnull) == (1,3)) + self.assertTrue(func("/dev/nothere") is None) + + + def testFindMajorMinorDeviceName(self): + func = CryptoBoxTools.findMajorMinorDeviceName + dir = os.path.join(os.path.sep, "dev") + self.assertTrue(os.path.join(dir,"hda") in func(dir,3,0)) + self.assertTrue(os.path.devnull in func(dir,1,3)) + self.assertFalse(os.path.devnull in func(dir,2,3)) + + + def testIsPartOfBlockDevice(self): + func = CryptoBoxTools.isPartOfBlockDevice + self.assertTrue(func("/dev/hda", "/dev/hda1")) + self.assertFalse(func("/dev/hda", "/dev/hda")) + self.assertFalse(func("/dev/hda1", "/dev/hda")) + self.assertFalse(func("/dev/hda1", "/dev/hda1")) + self.assertFalse(func("/dev/hda", "/dev/hdb1")) + self.assertFalse(func(None, "/dev/hdb1")) + self.assertFalse(func("/dev/hda", None)) + self.assertFalse(func(None, "")) + self.assertFalse(func("loop0", "loop1")) + + +if __name__ == "__main__": + unittest.main() + diff --git a/bin/unittests.Plugins.py b/bin/unittests.Plugins.py new file mode 100755 index 0000000..929c9de --- /dev/null +++ b/bin/unittests.Plugins.py @@ -0,0 +1,33 @@ +#!/usr/bin/python2.4 + +import unittest +import Plugins + +class CheckForUndefinedTestCases(unittest.TestCase): + """here we will add failing test functions for every non-existing testcase""" + + +def create_testcases(): + + plugins = Plugins.PluginManager(None, "../plugins").getPlugins() + glob_dict = globals() + loc_dict = locals() + for pl in plugins: + test_class = pl.getTestClass() + if test_class: + ## add the testclass to the global dictionary + glob_dict["unittest" + pl.getName()] = test_class + else: + subname = "test_existence_%s" % pl.getName() + def test_existence(self): + """check if the plugin (%s) contains tests""" % pl.getName() + self.fail("no tests defined for plugin: %s" % pl.getName()) + ## add this function to the class above + setattr(CheckForUndefinedTestCases, subname, test_existence) + #FIXME: the failure output always contains the same name for all plugins + + +create_testcases() + +if __name__ == "__main__": + unittest.main() diff --git a/bin/unittests.WebSites.py b/bin/unittests.WebSites.py new file mode 100755 index 0000000..89e514d --- /dev/null +++ b/bin/unittests.WebSites.py @@ -0,0 +1,39 @@ +#!/usr/bin/env python2.4 + +import unittest + +## this makes assertRaises shorter +from twill.errors import * +from mechanize import BrowserStateError, LinkNotFoundError + +## import the module of the common super class of all web interface test classes +import WebInterfaceTestClass + + + +class WebServer(WebInterfaceTestClass.WebInterfaceTestClass): + + def test_is_server_running(self): + '''the server should run under given name and port''' + self.cmd.go(self.URL) + ## other URLs must not be checked, as we do not know, if they are valid + + +class BuiltinPages(WebInterfaceTestClass.WebInterfaceTestClass): + + + def test_goto_index(self): + '''display all devices''' + self.cmd.go(self.URL + "?weblang=en") + self.cmd.find("The CryptoBox") + self.cmd.go(self.URL + "?weblang=de") + self.cmd.find("Die CryptoBox") + self.cmd.go(self.URL + "?weblang=si") + self.cmd.find("Privatnost v vsako vas") + self.cmd.go(self.URL + "?weblang=fr") + self.cmd.find("La CryptoBox") + + +if __name__ == "__main__": + unittest.main() + diff --git a/debian/README.Debian b/debian/README.Debian index 8a503fc..f06e815 100644 --- a/debian/README.Debian +++ b/debian/README.Debian @@ -1,6 +1,5 @@ CryptoBox for Debian - installation notes -be aware of two things: +be aware of one thing: 1) you need cryptsetup with luks support (for now only in unstable) -2) the debian perl-clearsilver package is broken (at least until April 02006) diff --git a/debian/control b/debian/control index b2dba57..26058dc 100644 --- a/debian/control +++ b/debian/control @@ -7,8 +7,7 @@ Standards-Version: 3.6.2 Package: cryptobox Architecture: any -Depends: bash (>=2.0), sed (>=4.0), coreutils, grep (>=2.0), perl, httpd-cgi, hashalot, libconfigfile-perl, cryptsetup (>=20050111), dmsetup, pmount, initscripts, e2fsprogs (>= 1.27), adduser -Recommends: perl-clearsilver +Depends: bash (>=2.0), sed (>=4.0), coreutils, grep (>=2.0), httpd-cgi, hashalot, cryptsetup (>=20050111), dmsetup, initscripts, e2fsprogs (>= 1.27), adduser, python (>=2.4), python-clearsilver Suggests: cron, samba Description: Web interface for an encrypting fileserver This bundle of scripts and cgis allow you to manage an encrypted harddisk diff --git a/debian/rules b/debian/rules index 33284e8..5d6d90b 100755 --- a/debian/rules +++ b/debian/rules @@ -74,19 +74,14 @@ binary-arch: build install # dh_installmenu # dh_installdebconf # dh_installlogrotate -# dh_installemacsen -# dh_installpam -# dh_installmime dh_installinit # dh_installcron -# dh_installinfo dh_installman dh_link dh_strip dh_compress dh_fixperms - dh_perl -# dh_python + dh_python # dh_makeshlibs dh_installdeb dh_shlibdeps diff --git a/design/background_frame_corner.svg b/design/background_frame_corner.svg new file mode 100644 index 0000000..deb4ae3 --- /dev/null +++ b/design/background_frame_corner.svg @@ -0,0 +1,265 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + diff --git a/design/icon_background_active.svg b/design/icon_background_active.svg new file mode 100644 index 0000000..c6ee31a --- /dev/null +++ b/design/icon_background_active.svg @@ -0,0 +1,92 @@ + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + diff --git a/design/icons/applications-system_tango.svg b/design/icons/applications-system_tango.svg new file mode 100644 index 0000000..35e2ffa --- /dev/null +++ b/design/icons/applications-system_tango.svg @@ -0,0 +1,245 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + System Applications + + + Jakub Steiner + + + http://jimmac.musichall.cz/ + + + system + applications + group + category + admin + root + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/design/icons/computer_tango.svg b/design/icons/computer_tango.svg new file mode 100644 index 0000000..d6e0f6b --- /dev/null +++ b/design/icons/computer_tango.svg @@ -0,0 +1,738 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + Computer + 2005-03-08 + + + Jakub Steiner + + + + + workstation + computer + node + client + + + + http://jimmac.musichall.cz/ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/design/icons/dialog-error_tango.svg b/design/icons/dialog-error_tango.svg new file mode 100644 index 0000000..602fa79 --- /dev/null +++ b/design/icons/dialog-error_tango.svg @@ -0,0 +1,316 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + Rodney Dawes + + + + + Jakub Steiner, Garrett LeSage + + + + Dialog Error + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/design/icons/dialog-information_tango.svg b/design/icons/dialog-information_tango.svg new file mode 100644 index 0000000..1e957cc --- /dev/null +++ b/design/icons/dialog-information_tango.svg @@ -0,0 +1,1145 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + Info + + + Jakub Steiner + + + + + dialog + info + + + http://jimmac.musichall.cz + + + + Garrett LeSage + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/design/icons/dialog-warning_tango.svg b/design/icons/dialog-warning_tango.svg new file mode 100644 index 0000000..3870db2 --- /dev/null +++ b/design/icons/dialog-warning_tango.svg @@ -0,0 +1,290 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + Dialog Warning + 2005-10-14 + + + Andreas Nilsson + + + + + Jakub Steiner, Garrett LeSage + + + + + dialog + warning + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/design/icons/drive-cdrom_tango.svg b/design/icons/drive-cdrom_tango.svg new file mode 100644 index 0000000..6588a65 --- /dev/null +++ b/design/icons/drive-cdrom_tango.svg @@ -0,0 +1,444 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + Drive - CD-ROM + + + Jakub Steiner + + + + + cdrom + cd-rom + optical + drive + + + + + http://jimmac.musichall.cz + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/design/icons/drive-harddisk_tango.svg b/design/icons/drive-harddisk_tango.svg new file mode 100644 index 0000000..406c4ac --- /dev/null +++ b/design/icons/drive-harddisk_tango.svg @@ -0,0 +1,469 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + Drive - Hard Disk + + + Jakub Steiner + + + + + hdd + hard drive + fixed + media + solid + + + + + http://jimmac.musichall.cz + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/design/icons/drive-removable-media_tango.svg b/design/icons/drive-removable-media_tango.svg new file mode 100644 index 0000000..e448605 --- /dev/null +++ b/design/icons/drive-removable-media_tango.svg @@ -0,0 +1,390 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + Drive - Removable + + + Jakub Steiner + + + + + media + removable + + + + + http://jimmac.musichall.cz + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/design/icons/globe-lips.svg b/design/icons/globe-lips.svg new file mode 100644 index 0000000..8b700f9 --- /dev/null +++ b/design/icons/globe-lips.svg @@ -0,0 +1,512 @@ + + + + + + + mouth - body part + + + + bodypart + mouth + + + + + Open Clip Art Library + + + + + Nicu Buculei + + + + + Nicu Buculei + + + + image/svg+xml + + + en + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/design/icons/gnome-dev-removable-usb_nuvola.svg b/design/icons/gnome-dev-removable-usb_nuvola.svg new file mode 100644 index 0000000..a2c624a --- /dev/null +++ b/design/icons/gnome-dev-removable-usb_nuvola.svg @@ -0,0 +1,1004 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + Adobe PDF library 6.66 + + + + + + + + + + 2004-10-01T14:58:15+02:00 + + 2005-02-15T09:49:25Z + + Illustrator + + 2004-10-01T14:58:15+02:00 + + + + + JPEG + + 256 + + 256 + + /9j/4AAQSkZJRgABAgEASABIAAD/7QAsUGhvdG9zaG9wIDMuMAA4QklNA+0AAAAAABAASAAAAAEA +AQBIAAAAAQAB/+4ADkFkb2JlAGTAAAAAAf/bAIQABgQEBAUEBgUFBgkGBQYJCwgGBggLDAoKCwoK +DBAMDAwMDAwQDA4PEA8ODBMTFBQTExwbGxscHx8fHx8fHx8fHwEHBwcNDA0YEBAYGhURFRofHx8f +Hx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8f/8AAEQgBAAEAAwER +AAIRAQMRAf/EAaIAAAAHAQEBAQEAAAAAAAAAAAQFAwIGAQAHCAkKCwEAAgIDAQEBAQEAAAAAAAAA +AQACAwQFBgcICQoLEAACAQMDAgQCBgcDBAIGAnMBAgMRBAAFIRIxQVEGE2EicYEUMpGhBxWxQiPB +UtHhMxZi8CRygvElQzRTkqKyY3PCNUQnk6OzNhdUZHTD0uIIJoMJChgZhJRFRqS0VtNVKBry4/PE +1OT0ZXWFlaW1xdXl9WZ2hpamtsbW5vY3R1dnd4eXp7fH1+f3OEhYaHiImKi4yNjo+Ck5SVlpeYmZ +qbnJ2en5KjpKWmp6ipqqusra6voRAAICAQIDBQUEBQYECAMDbQEAAhEDBCESMUEFURNhIgZxgZEy +obHwFMHR4SNCFVJicvEzJDRDghaSUyWiY7LCB3PSNeJEgxdUkwgJChgZJjZFGidkdFU38qOzwygp +0+PzhJSktMTU5PRldYWVpbXF1eX1RlZmdoaWprbG1ub2R1dnd4eXp7fH1+f3OEhYaHiImKi4yNjo ++DlJWWl5iZmpucnZ6fkqOkpaanqKmqq6ytrq+v/aAAwDAQACEQMRAD8A9U4q7FXYq7FXYq7FXYq7 +FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7F +XYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FX +Yq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXY +q7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq +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 +rF+j9Kff61cAxoy+KJ9uT/iPuM1mo1/Qn4BmIvavJn5QeUfLJS4WH9Iamm/125AYq3jHH9lPnu3v +mqyaiUvIMwGcZQl2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KvnX82fJH+Hda+u +2kdNI1Fi0QA2il6vF7D9pfbbtmywz8SNHmGB2Zn+UfnE32m/oS8k5XdktbV26yQDYD5x9PlmHkhR +ZBl19rSopFsnrsNjJWkYPf4v2iPBfppkEvMPO3mlUJS9ufVmG6WybAf7Cvw/Nt/nlmPFKfLkgli/ +l/yV5r86XHOyt/q+nBqPeTVWIeIBpWRvZR86Zk8OPFz3kx3KX+d/J2oeUNabTrl/XgkUSWt0F4rK +h6mlWoVOxFf4Zm6XUA7sSEsLX99YQ2d7f3EumQ1EGncykC/EWPJVpzPIk1b5ZsYaWBPEWFrTd2dp +GIoEWg6IgAUfdkp6iEBQWmS+WPy087+a+EqQ/o/S33+t3AKIy+KL9uT/AIj75rNRr+hPwDMRey+U +Pyb8oeXeE8kP6U1FaH61dAFVbxji3RfatSPHNVk1MpeQbAGd5jpdirsVdirsVdirsVdirsVdirsV +dirsVdirsVdirsVdirsVdirsVdiqV+ZvL1j5g0W50q8H7udfgkH2o5Bujr7qcnjmYmwpD568uxQ+ +VvPcdp5jpDDYSSC5JUupUxtSigEsrggjbeubDLHjjcWA2ZDq/nrzH5vv20ryfYSpFsDPQepx6cmb +7EK/M/TlcdPGAuZTbJvJ/wCR+m2brfeZZBqd8TyNsCfQU/5RPxSn50HscryaonaOwUReoRRRxRrF +EixxoAqIoAVQNgAB0GYjJiX5peUo/MnlO5iSPnqFmpubBgPi5oKsg/4yL8NPGnhl2DJwy8kEPFvK +/wCT3nTXkje4T9FacaH1roEOVPdIRRj/ALLiPfNll1oAq7YCL2Lyn+UPk/y9xm+r/pG/Xf61dgPx +P+RHTgvsaV9812TUSl5BmAzbKEuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2K +uxV2KuxV2KsT84flroPmm/s72+aSKW2BSUw0Bmj6hGJBpQ9/n9F2LPKAICCE/wBI0XStHs1s9MtY +7S2X9iMUqfFid2PuTXK5TMjZSjcirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsV +dirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVd +irsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdi +rsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdir +sVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirs +VdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsV +dirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVd +irsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdi +rsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdir//Z + + + + + + + + + + + + uuid:74d5a603-9ab8-427f-9735-c474bf2487a1 + + + + + + image/svg+xml + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/design/icons/gnome-globe_nuvola.svg b/design/icons/gnome-globe_nuvola.svg new file mode 100644 index 0000000..3a5a620 --- /dev/null +++ b/design/icons/gnome-globe_nuvola.svg @@ -0,0 +1,1195 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +2004-03-28T20:03:13Z + +2004-03-28T20:03:13Z + +Illustrator + + + + +JPEG + +256 + +256 + +/9j/4AAQSkZJRgABAgEASABIAAD/7QAsUGhvdG9zaG9wIDMuMAA4QklNA+0AAAAAABAASAAAAAEA +AQBIAAAAAQAB/+4ADkFkb2JlAGTAAAAAAf/bAIQABgQEBAUEBgUFBgkGBQYJCwgGBggLDAoKCwoK +DBAMDAwMDAwQDA4PEA8ODBMTFBQTExwbGxscHx8fHx8fHx8fHwEHBwcNDA0YEBAYGhURFRofHx8f +Hx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8f/8AAEQgBAAEAAwER +AAIRAQMRAf/EAaIAAAAHAQEBAQEAAAAAAAAAAAQFAwIGAQAHCAkKCwEAAgIDAQEBAQEAAAAAAAAA +AQACAwQFBgcICQoLEAACAQMDAgQCBgcDBAIGAnMBAgMRBAAFIRIxQVEGE2EicYEUMpGhBxWxQiPB +UtHhMxZi8CRygvElQzRTkqKyY3PCNUQnk6OzNhdUZHTD0uIIJoMJChgZhJRFRqS0VtNVKBry4/PE +1OT0ZXWFlaW1xdXl9WZ2hpamtsbW5vY3R1dnd4eXp7fH1+f3OEhYaHiImKi4yNjo+Ck5SVlpeYmZ +qbnJ2en5KjpKWmp6ipqqusra6voRAAICAQIDBQUEBQYECAMDbQEAAhEDBCESMUEFURNhIgZxgZEy +obHwFMHR4SNCFVJicvEzJDRDghaSUyWiY7LCB3PSNeJEgxdUkwgJChgZJjZFGidkdFU38qOzwygp +0+PzhJSktMTU5PRldYWVpbXF1eX1RlZmdoaWprbG1ub2R1dnd4eXp7fH1+f3OEhYaHiImKi4yNjo ++DlJWWl5iZmpucnZ6fkqOkpaanqKmqq6ytrq+v/aAAwDAQACEQMRAD8A9U4q7FXYq7FXYq7FXYq7 +FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYqlvmDzFo +3l7TJdT1e5W1tItuTbszHoiKN2Y+AxV4j5g/5ydvTcMnl/SYlgU0Se/LOzDxMcTIF/4M4qk//QzP +nv8A5YNL/wCRVx/2UYq7/oZnz3/ywaX/AMirj/soxV3/AEMz57/5YNL/AORVx/2UYq7/AKGZ89/8 +sGl/8irj/soxV3/QzPnv/lg0v/kVcf8AZRirv+hmfPf/ACwaX/yKuP8AsoxV3/QzPnv/AJYNL/5F +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 +4lCRoEQdFUAD8MVXYq7FXYq//9k= + + + + + + +image/svg+xml + + + + + + + + + +2004-03-28T20:07:21Z + +2004-03-28T20:07:21Z + +Illustrator + + + + +JPEG + +256 + +256 + +/9j/4AAQSkZJRgABAgEASABIAAD/7QAsUGhvdG9zaG9wIDMuMAA4QklNA+0AAAAAABAASAAAAAEA +AQBIAAAAAQAB/+4ADkFkb2JlAGTAAAAAAf/bAIQABgQEBAUEBgUFBgkGBQYJCwgGBggLDAoKCwoK +DBAMDAwMDAwQDA4PEA8ODBMTFBQTExwbGxscHx8fHx8fHx8fHwEHBwcNDA0YEBAYGhURFRofHx8f +Hx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8f/8AAEQgBAAEAAwER +AAIRAQMRAf/EAaIAAAAHAQEBAQEAAAAAAAAAAAQFAwIGAQAHCAkKCwEAAgIDAQEBAQEAAAAAAAAA +AQACAwQFBgcICQoLEAACAQMDAgQCBgcDBAIGAnMBAgMRBAAFIRIxQVEGE2EicYEUMpGhBxWxQiPB +UtHhMxZi8CRygvElQzRTkqKyY3PCNUQnk6OzNhdUZHTD0uIIJoMJChgZhJRFRqS0VtNVKBry4/PE +1OT0ZXWFlaW1xdXl9WZ2hpamtsbW5vY3R1dnd4eXp7fH1+f3OEhYaHiImKi4yNjo+Ck5SVlpeYmZ +qbnJ2en5KjpKWmp6ipqqusra6voRAAICAQIDBQUEBQYECAMDbQEAAhEDBCESMUEFURNhIgZxgZEy +obHwFMHR4SNCFVJicvEzJDRDghaSUyWiY7LCB3PSNeJEgxdUkwgJChgZJjZFGidkdFU38qOzwygp +0+PzhJSktMTU5PRldYWVpbXF1eX1RlZmdoaWprbG1ub2R1dnd4eXp7fH1+f3OEhYaHiImKi4yNjo ++DlJWWl5iZmpucnZ6fkqOkpaanqKmqq6ytrq+v/aAAwDAQACEQMRAD8A9U4q7FXYq7FXYq7FXYq7 +FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYqlvmDzFo +3l7TJdT1e5W1tItuTbszHoiKN2Y+AxV4j5g/5ydvTcMnl/SYlgU0Se/LOzDxMcTIF/4M4qk//QzP +nv8A5YNL/wCRVx/2UYq7/oZnz3/ywaX/AMirj/soxV3/AEMz57/5YNL/AORVx/2UYq7/AKGZ89/8 +sGl/8irj/soxV3/QzPnv/lg0v/kVcf8AZRirv+hmfPf/ACwaX/yKuP8AsoxV3/QzPnv/AJYNL/5F +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 + + + + + + +image/svg+xml + + + + +begin='' id='W5M0MpCehiHzreSzNTczkc9d' + + + + + + +2004-03-28T20:10:24Z + +2004-03-28T20:13:14Z + +Illustrator + + + + +JPEG + +256 + +256 + +/9j/4AAQSkZJRgABAgEASABIAAD/7QAsUGhvdG9zaG9wIDMuMAA4QklNA+0AAAAAABAASAAAAAEA +AQBIAAAAAQAB/+4ADkFkb2JlAGTAAAAAAf/bAIQABgQEBAUEBgUFBgkGBQYJCwgGBggLDAoKCwoK +DBAMDAwMDAwQDA4PEA8ODBMTFBQTExwbGxscHx8fHx8fHx8fHwEHBwcNDA0YEBAYGhURFRofHx8f +Hx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8f/8AAEQgBAAEAAwER +AAIRAQMRAf/EAaIAAAAHAQEBAQEAAAAAAAAAAAQFAwIGAQAHCAkKCwEAAgIDAQEBAQEAAAAAAAAA +AQACAwQFBgcICQoLEAACAQMDAgQCBgcDBAIGAnMBAgMRBAAFIRIxQVEGE2EicYEUMpGhBxWxQiPB +UtHhMxZi8CRygvElQzRTkqKyY3PCNUQnk6OzNhdUZHTD0uIIJoMJChgZhJRFRqS0VtNVKBry4/PE +1OT0ZXWFlaW1xdXl9WZ2hpamtsbW5vY3R1dnd4eXp7fH1+f3OEhYaHiImKi4yNjo+Ck5SVlpeYmZ +qbnJ2en5KjpKWmp6ipqqusra6voRAAICAQIDBQUEBQYECAMDbQEAAhEDBCESMUEFURNhIgZxgZEy +obHwFMHR4SNCFVJicvEzJDRDghaSUyWiY7LCB3PSNeJEgxdUkwgJChgZJjZFGidkdFU38qOzwygp +0+PzhJSktMTU5PRldYWVpbXF1eX1RlZmdoaWprbG1ub2R1dnd4eXp7fH1+f3OEhYaHiImKi4yNjo ++DlJWWl5iZmpucnZ6fkqOkpaanqKmqq6ytrq+v/aAAwDAQACEQMRAD8A9U4q7FXYq7FXYq7FXYq7 +FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYqlvmDzFo +3l7TJdT1e5W1tItuTbszHoiKN2Y+AxV4j5g/5ydvTcMnl/SYlgU0Se/LOzDxMcTIF/4M4qk//QzP +nv8A5YNL/wCRVx/2UYq7/oZnz3/ywaX/AMirj/soxV3/AEMz57/5YNL/AORVx/2UYq7/AKGZ89/8 +sGl/8irj/soxV3/QzPnv/lg0v/kVcf8AZRirv+hmfPf/ACwaX/yKuP8AsoxV3/QzPnv/AJYNL/5F +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 +/9k= + + + + + + +image/svg+xml + + + + +end='w' + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/design/icons/gtk-zoom-in_nuvola.svg b/design/icons/gtk-zoom-in_nuvola.svg new file mode 100644 index 0000000..1737355 --- /dev/null +++ b/design/icons/gtk-zoom-in_nuvola.svg @@ -0,0 +1,433 @@ + + +image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/design/icons/help_contents.svg b/design/icons/help_contents.svg new file mode 100644 index 0000000..c3beee8 --- /dev/null +++ b/design/icons/help_contents.svg @@ -0,0 +1,701 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + Help Contents + + + help + index + contents + + + + + + Jakub Steiner + + + http://jimmac.musichall.cz + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/design/icons/inaccessible_tango_emblem-unreadable.svg b/design/icons/inaccessible_tango_emblem-unreadable.svg new file mode 100644 index 0000000..82a4a4f --- /dev/null +++ b/design/icons/inaccessible_tango_emblem-unreadable.svg @@ -0,0 +1,357 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + Jakub Steiner + + + http://jimmac.musichall.cz + + Unreadable + + + emblem + access + denied + unreadable + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/design/icons/language.png b/design/icons/language.png new file mode 100644 index 0000000000000000000000000000000000000000..3334b5d6db6c37e0f2bb49331307d4e076172507 GIT binary patch literal 3520 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 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + Jakub Steiner + + + http://jimmac.musichall.cz + + Read Only Emblem + + + emblem + read-only + nowrite + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/design/icons/multimedia-dell-dj-pocket_tango.svg b/design/icons/multimedia-dell-dj-pocket_tango.svg new file mode 100644 index 0000000..0e25751 --- /dev/null +++ b/design/icons/multimedia-dell-dj-pocket_tango.svg @@ -0,0 +1,4405 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + Portable Media - iPod Mini Silver + October 2005 + + + Ryan Collier (pseudo) + + + + + http://www.tango-project.org + + + http://www.pseudocode.org + + + media + device + ipod + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + R + + + + + + + + + + diff --git a/design/icons/multimedia-player-ipod-mini-blue_tango.svg b/design/icons/multimedia-player-ipod-mini-blue_tango.svg new file mode 100644 index 0000000..0c35cf8 --- /dev/null +++ b/design/icons/multimedia-player-ipod-mini-blue_tango.svg @@ -0,0 +1,4126 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + Portable Media - iPod Mini Blue + October 2005 + + + Ryan Collier (pseudo) + + + http://www.pseudocode.org + + + media + device + ipod + + + + + + http://www.tango-project.org + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + MENU + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + R + + + + + + + + + + + diff --git a/design/icons/multimedia-player-motorola-rokr_tango.svg b/design/icons/multimedia-player-motorola-rokr_tango.svg new file mode 100644 index 0000000..207e27f --- /dev/null +++ b/design/icons/multimedia-player-motorola-rokr_tango.svg @@ -0,0 +1,1025 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + Portable Media - Motorola ROKR + October 2005 + + + Ryan Collier (pseudo) + + + + + http://www.tango-project.org + + + http://www.pseudocode.org + + + media + device + ipod + phone + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + R + + + + + + + + + + diff --git a/design/icons/network-transmit-receive_design.svg b/design/icons/network-transmit-receive_design.svg new file mode 100644 index 0000000..dd48cc4 --- /dev/null +++ b/design/icons/network-transmit-receive_design.svg @@ -0,0 +1,1041 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + Computer + 2005-03-08 + + + Jakub Steiner + + + + + workstation + computer + node + client + + + + http://jimmac.musichall.cz/ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/design/icons/pile_of_devices.png b/design/icons/pile_of_devices.png new file mode 100644 index 0000000000000000000000000000000000000000..3591de98d51b9fbeb4f39a0fcb4f63aaa9acf692 GIT binary patch literal 17218 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 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + MENU + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + R + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + R + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/design/icons/preferences-desktop-locale_tango.svg b/design/icons/preferences-desktop-locale_tango.svg new file mode 100644 index 0000000..e5e87cd --- /dev/null +++ b/design/icons/preferences-desktop-locale_tango.svg @@ -0,0 +1,828 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + Jakub Steiner + + + http://jimmac.musichall.cz + + Locale Preferences + + + locale preferences + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/design/icons/preferences-system_tango.svg b/design/icons/preferences-system_tango.svg new file mode 100644 index 0000000..a6183e8 --- /dev/null +++ b/design/icons/preferences-system_tango.svg @@ -0,0 +1,396 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + Jakub Steiner + + + http://jimmac.musichall.cz + + Preferences System + + + preferences + settings + control panel + tweaks + system + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/design/icons/redhat-config-users_wasp.svg b/design/icons/redhat-config-users_wasp.svg new file mode 100644 index 0000000..4668db8 --- /dev/null +++ b/design/icons/redhat-config-users_wasp.svg @@ -0,0 +1,1904 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/design/icons/seahorse-preferences_gnome.svg b/design/icons/seahorse-preferences_gnome.svg new file mode 100644 index 0000000..583aa18 --- /dev/null +++ b/design/icons/seahorse-preferences_gnome.svg @@ -0,0 +1,1250 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/design/icons/spherecrystal_help.svg b/design/icons/spherecrystal_help.svg new file mode 100644 index 0000000..dfa203c --- /dev/null +++ b/design/icons/spherecrystal_help.svg @@ -0,0 +1,60 @@ + + + + + + + + + + + + + + + + diff --git a/design/icons/system-log-out_tango.svg b/design/icons/system-log-out_tango.svg new file mode 100644 index 0000000..c04625a --- /dev/null +++ b/design/icons/system-log-out_tango.svg @@ -0,0 +1,362 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + Jakub Steiner + + + http://jimmac.musichall.cz + + System Log Out + + + log out + logout + exit + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/design/icons/unlocked_clavdia.svg b/design/icons/unlocked_clavdia.svg new file mode 100644 index 0000000..ce5175e --- /dev/null +++ b/design/icons/unlocked_clavdia.svg @@ -0,0 +1,515 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + Jakub Steiner + + + http://jimmac.musichall.cz + + Read Only Emblem + + + emblem + read-only + nowrite + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/design/icons/unlocked_lars.svg b/design/icons/unlocked_lars.svg new file mode 100644 index 0000000..963467d --- /dev/null +++ b/design/icons/unlocked_lars.svg @@ -0,0 +1,1198 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + Drive - Hard Disk + + + Jakub Steiner + + + + + hdd + hard drive + fixed + media + solid + + + + + http://jimmac.musichall.cz + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/doc/html/fr b/doc/html/fr new file mode 120000 index 0000000..2c4c454 --- /dev/null +++ b/doc/html/fr @@ -0,0 +1 @@ +en \ No newline at end of file diff --git a/doc/html/si b/doc/html/si new file mode 120000 index 0000000..2c4c454 --- /dev/null +++ b/doc/html/si @@ -0,0 +1 @@ +en \ No newline at end of file diff --git a/known_problems b/known_problems new file mode 100644 index 0000000..0d87995 --- /dev/null +++ b/known_problems @@ -0,0 +1,4 @@ +Bug-Report: twill/other-modules/ClientForm.py: uncomment line 846 ("XHTMLCompatibleFormParser" instead of default parser) - this solves an eternal loop-problem caused by line 958 ("fp.feed(ch)") + +Bug-Report: testoob - loading of a TestSuite (as advertised by the commandline help) does not work (due to unittest.py/loadTestsFromModule) + diff --git a/lang/README b/lang/README index 8edf0b0..e117d71 100644 --- a/lang/README +++ b/lang/README @@ -3,26 +3,35 @@ $Id$ You may want to translate the CryptoBox into another language? Most welcome! -Existing translation can be found in the .hdf files. There are three -easy steps, that won't take longer than one hour to go through: +Existing translations can be found in the .hdf files in the +language directory (online available as +https://systemausfall.org/svn/cryptobox/trunk/lang/). -1.) Download one .hdf in your preferred language (e.g. en.hdf if you're a native english speaker). +There are three easy steps, that won't take longer than one hour +to go through, if you want to translate the cryptobox into a +language of your choice: -2.) Start to change the text behind the equal sign line for line. +1.) Download one .hdf in your preferred language (e.g. "en.hdf" +if you're a native english speaker). + +2.) Start to change the text following the equal signs line by line. 3.) Send us your changed .hdf file. That's it - thank you! Hints: -We are available via mail (cryptobox@systemausfall.org) for any questions. Don't worry to ask us if -something is vague. We prefer "utf" encoded hdf-files. If you don't -know what udf means - just don't care, send us your file and -everything else will be fine. You don't have to translate the whole -file, some minor parts are enough to send back. The community will do -the rest. ;) +We are available via mail (cryptobox@systemausfall.org) for any +questions. Don't worry to ask us if something is vague. + +We prefer "utf" encoded hdf-files. If you don't know what utf +means - just don't care, send us your file and everything else +will be fine. + +You don't have to translate the whole file - some minor parts are +enough to send back. The community will do the rest. ;) Again, thanks for your help! We do the Cryptobox things in our -freetime for fun and to provide an open source solution for this +free time for fun and to provide an open source solution for this special purpose. By helping us, you're taking part in this development. diff --git a/lang/TODO b/lang/TODO index 8afaa30..0207e79 100644 --- a/lang/TODO +++ b/lang/TODO @@ -9,6 +9,8 @@ help! - SuccessMessage.InitRunning contains only the first sentence - SuccessMessage.ReBoot: add a hint for automatic redirection (3rd sentence) - EmptyCryptoPassword: change 'Text' accordingly to English string. +- Button.System: is in eglish +- ErrorMessage.NoDocumentation: is in english Below are some smaller changes in English sentences. Someone may change it accordingly. - MountFailed: add 'Pleasy try again' to the end of the string. diff --git a/lang/de.hdf b/lang/de.hdf index 4bfd1ee..a1c1960 100644 --- a/lang/de.hdf +++ b/lang/de.hdf @@ -1,215 +1,230 @@ -Lang { +Name = Deutsch - Name = deutsch +Status = $Id$ - Status = $Id$ +Title { + Top = Die CryptoBox + Slogan = ... und 1984 war gestern! + Init = Initialisierung der CryptoBox + Mount = Aktivierung des Containers + Umount = Deaktivierung des Containers + Config = Konfiguration der CryptoBox + System = System + Status = Status der CryptoBox + Volume = Eigenschaften von +} - Title { - Top = Die CryptoBox - Slogan = ... und 1984 war gestern! - Init = Initialisierung der CryptoBox - Mount = Aktivierung der Crypto-Daten - Umount = Deaktivierung der Crypto-Daten - Config = Konfiguration der CryptoBox - Log = Protokoll der CryptoBox - System = System - Status = Status der CryptoBox + +Text { + EnterCurrentCryptoPassword = Das Crypto-Passwort eingeben + EnterNewCryptoPassword = Das neue Crypto-Passwort eingeben + EnterSameCryptoPassword = Das Crypto-Passwort wiederholen + EnterCurrentAdminPassword = Das aktuelle Admin-Passwort eingeben: + EnterNewAdminPassword = Das neue Admin-Passwort eingeben: + EnterSameAdminPassword = Das neue Admin-Passwort wiederholen: + InitWarning = Bei der Initialisierung werden ALLE DATEN auf der Festplatte GELÖSCHT! + ConfirmInitHint = Um zu bestätigen, dass du weisst, was du tust, tippe hier bitte exakt Folgendes ein: + ConfirmInit = Ja, loesche alle Daten! + PartitionInfo = Derzeitige Partitionierung der Festplatte: + IPAddress = Netzwerk-Adresse (IP) der CryptoBox: + TimeOut = Zeitabschaltung des Crypto-Dateisystems (in Minuten): + SelectLanguage = Spracheinstellung: + RedirectNote = Klicke hier, falls dein Browser die automatische Weiterleitung nicht unterstützt. + ProjectHomePage = Projekt-Seite + ProjectNote = Die CryptoBox ist ein Projekt von + DoUmount = Deaktivierung des verschlüsselten Dateisystems + DoMount = Aktivierung des verschlüsselten Dateisystems + Configuration = Einstellungen + CryptoIsActive = Die Crypto-Daten sind verfügbar. + CryptoIsDown = Die Crypto-Daten sind vor jedem Zugriff geschützt. + ChoosePartition = Welchen Daten-Container möchtest du auswählen? + ChosenPartition = Der aktuelle Daten-Container ist + ActivePartitions = Die folgenden Daten-Container sind derzeit aktiv + PassivePartitions = Die folgenden Daten-Container sind derzeit inaktiv + ContainerName = Der Name des Daten-Containers + ContainerEncryption = Aktiviere Verschlüsselung +} + + +Button { + DoInit = Initialisierung + SaveConfig = Speichere Konfiguration + Update = Aktualisieren + Mount = Container aktivieren + Umount = Container deaktivieren + Config = Einstellungen + PowerOff = ausschalten + ReBoot = neu starten + Documentation = Hilfe + Status = Status + System = System + ContainerNameSet = Setze den neuen Namen + InitContainer = Reinitialisiere den Container +} + + +WarningMessage { + InitNotConfirmed { + Title = Bestätigung schlug fehl + Text = Der Bestätigungssatz muss exakt eingegeben werden! } - - Text { - EnterCurrentCryptoPassword = Das Crypto-Passwort eingeben: - EnterNewCryptoPassword = Das neue Crypto-Passwort eingeben: - EnterSameCryptoPassword = Das Crypto-Passwort wiederholen: - EnterCurrentAdminPassword = Das aktuelle Admin-Passwort eingeben: - EnterNewAdminPassword = Das neue Admin-Passwort eingeben: - EnterSameAdminPassword = Das neue Admin-Passwort wiederholen: - InitWarning = Bei der Initialisierung werden ALLE DATEN auf der Festplatte GELÖSCHT! - ConfirmInitHint = Um zu bestätigen, dass du weisst, was du tust, tippe hier bitte exakt Folgendes ein: - ConfirmInit = Ja, loesche alle Daten! - PartitionInfo = Derzeitige Partitionierung der Festplatte: - IPAddress = Netzwerk-Adresse (IP) der CryptoBox: - TimeOut = Zeitabschaltung des Crypto-Dateisystems (in Minuten): - EmptyLog = Das Logbuch der CryptoBox ist leer. - SelectLanguage = Spracheinstellung: - RedirectNote = Klicke hier, falls dein Browser die automatische Weiterleitung nicht unterstützt. - ProjectHomePage = Projekt-Seite - ProjectNote = Die CryptoBox ist ein Projekt von - DoUmount = Deaktivierung des verschlüsselten Dateisystems - DoMount = Aktivierung des verschlüsselten Dateisystems - Configuration = Einstellungen - CryptoIsActive = Die Crypto-Daten sind verfügbar. - CryptoIsDown = Die Crypto-Daten sind vor jedem Zugriff geschützt. + EmptyCryptoPassword { + Title = Ungültiges Crypto-Passwort + Text = Du musst ein Crypto-Passwort eingeben! } - - Button { - DoInit = Initialisierung - SaveConfig = Speichere Konfiguration - Update = Aktualisieren - Mount = Crypto-Daten aktivieren - Umount = Crypto-Daten deaktivieren - Config = Einstellungen - PowerOff = ausschalten - ReBoot = neu starten - Protocol = Protokoll anzeigen - Documentation = Hilfe - Status = Status + DifferentCryptoPasswords { + Title = Ungleiche Crypto-Passworte + Text = Die beiden Passworte müssen identisch sein, um Eingabefehler zu verhindern. } - - WarningMessage { - InitNotConfirmed { - Title = Bestätigung schlug fehl - Text = Der Bestätigungssatz muss exakt eingegeben werden! - } - - EmptyCryptoPassword { - Title = Ungültiges Crypto-Passwort - Text = Du musst ein Crypto-Passwort eingeben! - } - - DifferentCryptoPasswords { - Title = Ungleiche Crypto-Passworte - Text = Die beiden Passworte müssen identisch sein, um Eingabefehler zu verhindern. - } - - DifferentAdminPasswords { - Title = Ungleiche Administrations-Passworte - Text = Die beiden Passworte müssen identisch sein, um Eingabefehler zu verhindern. - } - - WrongAdminPassword { - Title = Falsches Administrations-Passwort - Text = Das eingegebene Administrations-Passwort ist falsch. Versuche es noch einmal. - } - - MountFailed { - Title = Aktivierung schlug fehl - Text = Das verschlüsselte Dateisystem konnte nicht aktiviert werden. Wahrscheinlich war das Passwort falsch. Versuche es noch einmal. - } - - UmountFailed { - Title = Deaktivierung schlug fehl - Text = Das verschlüsselte Dateisystem konnte nicht abgeschaltet werden. Wahrscheinlich sind noch Dateien geöffnet. Schließe alle potentiell unsauberen Programme (beispielsweise die weitverbreitete Textverarbeitung). Notfalls einfach die CryptoBox ausschalten! - } - - NotInitialized { - Title = Keine Konfiguration gefunden - Text = Die CryptoBox wurde noch nicht eingerichtet. - } - - InitNotFinished { - Title = Initalisierung noch nicht abgeschlossen - Text = Die Initialisierung wird in wenigen Minuten beendet sein. Erst danach ist diese Aktion möglich. - } - - IsMounted { - Title = Bereits aktiv - Text = Das verschlüsselte Dateisystem ist bereits aktiv. - } - - NotMounted { - Title = Nicht aktiv - Text = Das verschlüsselte Dateisystem ist derzeit nicht aktiv. - } - - AlreadyConfigured { - Title = Konfiguration gefunden - Text = Die CryptoBox wurde bereits eingerichtet. Bei einer erneuten Initialisierung werden alle Daten gelöscht! - } - - InvalidLanguage { - Title = Ungültige Sprache - Text = Die ausgewählte Sprache ist nicht verfügbar! - } - - InvalidIP { - Title = Ungültige IP - Text = Die ausgewählte Netzwerkadresse ist nicht gültig! - } - - InvalidTimeOut { - Title = Ungültige Zeitabschaltung - Text = Der ausgewählte Wert der Zeitabschaltung ist nicht gültig! - } - - ConfigTimeOutFailed { - Title = Fehler beim Ändern der Zeitabschaltung - Text = Der Wert der Zeitabschaltung konnte nicht geändert werden! - } - - ConfigLanguageFailed { - Title = Fehler beim Ändern der Spracheinstellung - Text = Die Spracheinstellung konnte nicht geändert werden! - } - - ConfigIPFailed { - Title = Fehler beim Ändern der Netzwerkadresse - Text = Die Netzwerkadresse konnte nicht geändert werden! - } - - IPAddressChanged { - Title = Änderung der Netzwerk-Adresse - Text = Die Netzwerk-Adresse der CryptoBox wurde verändert. In wenigen Sekunden werden sie zu der neuen Adresse umgeleitet. - } + DifferentAdminPasswords { + Title = Ungleiche Administrations-Passworte + Text = Die beiden Passworte müssen identisch sein, um Eingabefehler zu verhindern. } - - SuccessMessage { - InitRunning { - Title = Initialisierung läuft - Text = Die Initialisierung der CryptoBox wird im Hintergrund abgeschlossen. Du kannst die CryptoBox nun konfigurieren und das verschlüsselte Dateisystem in ein paar Minuten aktivieren. - } - - ConfigSaved { - Title = Konfiguration gespeichert - Text = Die neuen Einstellungen der CryptoBox wurden übernommem. - } - - MountDone { - Title = Crypto-Daten aktiviert - Text = Das verschlüsselte Dateisystem ist nun verfügbar. - } - - UmountDone { - Title = Crypto-Daten deaktiviert - Text = Das verschlüsselte Dateisystem ist nun vor jedem Zugriff geschützt. - } - - PowerOff { - Title = Abschaltung - Text = Die CryptoBox wird gerade heruntergefahren. In wenigen Sekunden kannst du sie ausschalten (falls dies nicht automatisch geschieht). - } - - ReBoot { - Title = Neustart - Text = Die CryptoBox wird gerade neu gestartet. In wenigen Sekunden ist sie wieder verfügbar. Bitte warte solange - du wirst automatisch weitergeleitet. - } + WrongAdminPassword { + Title = Falsches Administrations-Passwort + Text = Das eingegebene Administrations-Passwort ist falsch. Versuche es noch einmal. } + MountFailed { + Title = Aktivierung schlug fehl + Text = Das verschlüsselte Dateisystem konnte nicht aktiviert werden. Wahrscheinlich war das Passwort falsch. Versuche es noch einmal. + } - ErrorMessage { + UmountFailed { + Title = Deaktivierung schlug fehl + Text = Das verschlüsselte Dateisystem konnte nicht abgeschaltet werden. Wahrscheinlich sind noch Dateien geöffnet. Schließe alle potentiell unsauberen Programme (beispielsweise die weitverbreitete Textverarbeitung). Notfalls einfach die CryptoBox ausschalten! + } - UnknownAction { - Title = Unbekannte Aktion - Text = Du hast eine undefinierte Aktion angefordert. - } + NotInitialized { + Title = Keine Konfiguration gefunden + Text = Die CryptoBox wurde noch nicht eingerichtet. + } - NoSSL { - Title = Unverschlüsselte Verbindung - Text = Die CryptoBox akzeptiert nur verschlüsselte Verbindungen (https). So bleibt das Passwort vor neugierigen Augen geschützt. In wenigen Sekunden wird eine verschlüsselte Verbindung hergestellt. - } + InitNotFinished { + Title = Initalisierung noch nicht abgeschlossen + Text = Die Initialisierung wird in wenigen Minuten beendet sein. Erst danach ist diese Aktion möglich. + } - InitFailed { - Title = Initialisierung fehlgeschlagen - Text = Sende bitte den Inhalt des Protokolls (siehe oben) an die Entwickler der CryptoBox (cryptobox@systemausfall.org). - } + IsMounted { + Title = Bereits aktiv + Text = Das verschlüsselte Dateisystem ist bereits aktiv. + } - NoHardDisk { - Title = Keine Festplatte - Text = Es wurde kein Datenträger gefunden, der zur Speicherung der verschlüsselten Daten geeignet ist. Prüfe bitte, ob beim Anschalten des Computers eine Festplatte vom BIOS erkannt wurde. - } + NotMounted { + Title = Nicht aktiv + Text = Das verschlüsselte Dateisystem ist derzeit nicht aktiv. + } + + AlreadyConfigured { + Title = Konfiguration gefunden + Text = Die CryptoBox wurde bereits eingerichtet. Bei einer erneuten Initialisierung werden alle Daten gelöscht! + } + + InvalidLanguage { + Title = Ungültige Sprache + Text = Die ausgewählte Sprache ist nicht verfügbar! + } + + ConfigLanguageFailed { + Title = Fehler beim Ändern der Spracheinstellung + Text = Die Spracheinstellung konnte nicht geändert werden! + } + + NoDiskAvailableForMount { + Title = Kein Daten-Container verfügbar + Text = Es ist kein inaktiver Daten-Container verfügbar. Vielleicht sind bereits alle Container aktiv? + } + + NoDiskAvailableForUmount { + Title = Kein Daten-Container verfügbar + Text = Kein Daten-Container ist aktiv. Vielleicht wurden alle Container deaktiviert. + } + + InvalidDevice { + Title = Ungültiger Container + Text = Der angegebene Daten-Container ist nicht zulässig. + } + + InvalidVolumeName { + Title = Umbenennung fehlgeschlagen + Text = Der gewählte neue Name des Containers ist ungültig. Versuche es erneut. + } + + SetVolumeNameFailed { + Title = Umbenennung fehlgeschlagen + Text = Die Umbenennung des Containers schlug fehl. Details findest du in der Log-Datei. + } + + VolumeMayNotBeMounted { + Title = Der Container ist derzeit aktiv + Text = Die gewünschte Aktion kann nicht durchgeführt werden, solange der Container aktiv ist. + } +} + + +SuccessMessage { + InitRunning { + Title = Initialisierung läuft + Text = Die Initialisierung der CryptoBox wird im Hintergrund abgeschlossen. Du kannst die CryptoBox nun konfigurieren und das verschlüsselte Dateisystem in ein paar Minuten aktivieren. + } + + ConfigSaved { + Title = Konfiguration gespeichert + Text = Die neuen Einstellungen der CryptoBox wurden übernommem. + } + + MountDone { + Title = Crypto-Daten aktiviert + Text = Das verschlüsselte Dateisystem ist nun verfügbar. + } + + UmountDone { + Title = Crypto-Daten deaktiviert + Text = Das verschlüsselte Dateisystem ist nun vor jedem Zugriff geschützt. + } + + PowerOff { + Title = Abschaltung + Text = Die CryptoBox wird gerade heruntergefahren. In wenigen Sekunden kannst du sie ausschalten (falls dies nicht automatisch geschieht). + } + + ReBoot { + Title = Neustart + Text = Die CryptoBox wird gerade neu gestartet. In wenigen Sekunden ist sie wieder verfügbar. Bitte warte solange - du wirst automatisch weitergeleitet. + } +} + + +ErrorMessage { + + UnknownAction { + Title = Unbekannte Aktion + Text = Du hast eine undefinierte Aktion angefordert. + } + + NoSSL { + Title = Unverschlüsselte Verbindung + Text = Die CryptoBox akzeptiert nur verschlüsselte Verbindungen (https). So bleibt das Passwort vor neugierigen Augen geschützt. In wenigen Sekunden wird eine verschlüsselte Verbindung hergestellt. + } + + InitFailed { + Title = Initialisierung fehlgeschlagen + Text = Sende bitte den Inhalt des Protokolls (siehe oben) an die Entwickler der CryptoBox (cryptobox@systemausfall.org). + } + + NoHardDisk { + Title = Keine Festplatte + Text = Es wurde kein Datenträger gefunden, der zur Speicherung der verschlüsselten Daten geeignet ist. Prüfe bitte, ob beim Anschalten des Computers eine Festplatte vom BIOS erkannt wurde. + } + + NoDocumentation { + Title = Keine Hilfeseiten + Text = Es sind keine Hilfeseiten verfügbar! } } + diff --git a/lang/en.hdf b/lang/en.hdf index 8cd6ccf..b06631b 100644 --- a/lang/en.hdf +++ b/lang/en.hdf @@ -1,255 +1,187 @@ -Lang { +Name = English - Name = english - - Status = $Id$ - - Title { - Top = The CryptoBox - Slogan = Privacy for the rest of us. - Init = CryptoBox initialization - Mount = Activation of encrypted data - Umount = Deactivation of encrypted data - Config = CryptoBox configuration - Log = CryptoBox logfiles - System = System - Status = Status - Volume = Properties of - } - - - Text { - EnterCurrentCryptoPassword = Enter the crypto password: - EnterNewCryptoPassword = Enter the new crypto password: - EnterSameCryptoPassword = Repeat the new crypto password: - EnterCurrentAdminPassword = Enter the current administration password: - EnterNewAdminPassword = Enter the new administration password: - EnterSameAdminPassword = Repeat the new administration password: - InitWarning = During the process of initialization ALL DATA on the disk WILL BE DELETED! - ConfirmInitHint = To confirm that you know what you are doing, please enter exactly the following sequence: - ConfirmInit = Yes, delete all data! - PartitionInfo = Current partioning of the disk: - IPAddress = Network address (IP) of the CryptoBox: - TimeOut = Timeout for deactivation of the encrypted filesystem (in minutes): - EmptyLog = The logfiles of the CryptoBox are empty. - SelectLanguage = Language preferences: - RedirectNote = Click here if your browser does not support automatic redirection. - ProjectHomePage = Website of project - ProjectNote = The CryptoBox is a project of - DoUmount = Deactivation of the encrypted filesystem - DoMount = Activation of the encrypted filesystem - Configuration = Configuration - CryptoIsActive = The encrypted data is accessible. - CryptoIsDown = The encrypted data is protected from any access. - ChoosePartition = Which container do you want to enable? - ChosenPartition = The chosen container is - ActivePartitions = The following containers are enabled - PassivePartitions = The following containers are disabled - ContainerName = Container's name: - ContainerEncryption = Enable encryption - } - - - Button { - DoInit = Initialization - SaveConfig = Save configuration - Update = Refresh - Mount = Activate filesystem - Umount = Deactivate filesystem - Config = Configuration - PowerOff = Shutdown - ReBoot = Reboot - Protocol = Show logfiles - Documentation = Help - Status = Status - System = System - ContainerNameSet = Change name - InitContainer = Initialize container - } - - - WarningMessage { - InitNotConfirmed { - Title = Confirmation failed - Text = The sentence has to be entered exactly as shown! - } - - EmptyCryptoPassword { - Title = Invalid crypto password - Text = You have to enter a crypto password! - } - - DifferentCryptoPasswords { - Title = Crypto passwords do not match - Text = Both entered passwords have to be identical to ensure this is the desired password. - } - - DifferentAdminPasswords { - Title = Administration passwords do not match - Text = Both entered passwords have to be identical to ensure this is the desired password. - } - - WrongAdminPassword { - Title = Wrong Administration password - Text = The entered administration password is wrong. Please try again. - } - - MountFailed { - Title = Activation failed - Text = The encrypted filesystem could not be activated. Probably the given password was wrong. Please try again. - } - - UmountFailed { - Title = Deactivation failed - Text = The encrypted filesystem could not be deactivated. Probably some files are still in use. Close all unclean programs (for example that widely used word processor). In case of emergency just shut down the CryptoBox! - } - - NotInitialized { - Title = No configuration found - Text = The CryptoBox has not yet been configured. - } - - InitNotFinished { - Title = Initialization not yet completed - Text = Initialization will be completed in a few minutes. After completed initialization this action will become available. - } - - IsMounted { - Title = Already active - Text = The encrypted filesystem has already been activated. - } - - NotMounted { - Title = Inactive - Text = The encrypted filesystem is currently not active. - } - - AlreadyConfigured { - Title = Configuration found - Text = The CryptoBox has already been configured. If you initialize again, all data will be deleted! - } - - InvalidLanguage { - Title = Invalid language - Text = The selected language is not available! - } - - InvalidIP { - Title = Invalid IP address - Text = The selected network address is not valid! - } - - InvalidTimeOut { - Title = Invalid timeout - Text = The selected timeout is not valid! - } - - ConfigTimeOutFailed { - Title = Error during change of timeout - Text = The timeout value could not be changed! - } - - ConfigLanguageFailed { - Title = Error during change of language preferences - Text = The language preferences could not be changed! - } - - ConfigIPFailed { - Title = Error during change of network address - Text = The network address could not be changed! - } - - IPAddressChanged { - Title = Change of network address - Text = The network address has been changed. In a few seconds you will get redirected to the new address. - } - - NoDiskAvailableForMount { - Title = No partition available - Text = There is no unused container available. Maybe all containers are already mounted? - } - - NoDiskAvailableForUmount { - Title = No partition available - Text = There is no active container available for turning off. Maybe there is no active container? - } - - InvalidDevice { - Title = Invalid device - Text = The device you have chosen is invalid! - } - - InvalidVolumeName { - Title = Changing of container's name failed - Text = The supplied new name of the container was invalid. Please try again! - } - - SetVolumeNameFailed { - Title = Changing of container's name failed - Text = Could not change the name of the container. Take a look at the log files for details. - } - - VolumeMayNotBeMounted { - Title = The container is mounted - Text = This action is not available while the container is active. Please turn it off first. - } - } - - - SuccessMessage { - InitRunning { - Title = Initialization running - Text = The initialization will be completed in background. You may configure it now and activate the encrypted filesystem in a few minutes. - } - - ConfigSaved { - Title = Configuration saved - Text = The new settings have been accepted. - } - - MountDone { - Title = Encrypted filesystem activated - Text = The encrypted filesystem is now available. - } - - UmountDone { - Title = Encrypted filesystem deactivated - Text = The encrypted filesystem is now secured from all forms of access. - } - - PowerOff { - Title = Shutdown - Text = The CryptoBox is currently going to halt. In a few seconds you can power it off (in case this does not happen automatically). - } - - ReBoot { - Title = Reboot - Text = The CryptoBox is currently rebooting. In a few seconds it will be available again. Please wait - you will get redirected, when the reboot has finished. - } - } - - - ErrorMessage { - - UnknownAction { - Title = Unknown action - Text = You have requested an undefined action. - } - - NoSSL { - Title = Unencrypted connection - Text = The CryptoBox only accepts encrypted connections (https), so the password is safe from curious eyes. The encrypted connection will be established in a few seconds. - } - - InitFailed { - Title = Initialization failed - Text = Please send the logfiles (see above) to the developers of the CryptoBox (cryptobox@systemausfall.org). - } - - NoHardDisk { - Title = No hard disk - Text = No disk suitable for an encrypted filesystem found. Please ensure the BIOS detected the disk during power-on of the computer. - } - } +Status = $Id$ +Title { + Top = The CryptoBox + Slogan = Privacy for the rest of us. + Init = CryptoBox initialization + Config = CryptoBox configuration + System = System + Status = Status + Volume = Volume + AccessDenied = Access denied } + + +Text { + AccessDenied = Sorry - you are not allowed to do this! + EnterCurrentPassword = Enter the password + EnterNewPassword = Enter new password + EnterSamePassword = Repeat new password + TimeOut = Timeout for deactivation of the encrypted filesystem (in minutes): + SelectLanguage = Language preferences: + RedirectNote = Click here if your browser does not support automatic redirection. + ProjectHomePage = Website of project + ProjectNote = The CryptoBox is a project of + Configuration = Configuration + CryptoIsActive = The encrypted data is accessible. + CryptoIsDown = The encrypted data is protected from any access. + ChoosePartition = Which container do you want to enable? + ChosenPartition = The chosen container is + ActivePartitions = The following containers are enabled + PassivePartitions = The following containers are disabled + ContainerName = Container's name + ContainerEncryption = Enable encryption +} + + +Button { + DoInit = Initialization + SaveConfig = Save configuration + Update = Refresh + Config = Configuration + Documentation = Help + Status = Status + System = System + InitContainer = Initialize container + SelectLanguage = Select language + HelpForForm = Get help +} + + +WarningMessage { + + EmptyPassword { + Title = Missing password + Text = You have to enter a password! + } + + EmptyNewPassword { + Title = Missing new password + Text = You have to enter a new password! + } + + DifferentPasswords { + Title = Different passwords + Text = The passwords you entered did not match. + } + + NotInitialized { + Title = No configuration found + Text = The CryptoBox has not yet been configured. + } + + InitNotFinished { + Title = Initialization not yet completed + Text = Initialization will be completed in a few minutes. After completed initialization this action will become available. + } + + AlreadyConfigured { + Title = Configuration found + Text = The CryptoBox has already been configured. If you initialize again, all data will be deleted! + } + + InvalidLanguage { + Title = Invalid language + Text = The selected language is not available! + } + + + ConfigLanguageFailed { + Title = Error during change of language preferences + Text = The language preferences could not be changed! + } + + + NoDiskAvailableForMount { + Title = No partition available + Text = There is no unused container available. Maybe all containers are already mounted? + } + + NoDiskAvailableForUmount { + Title = No partition available + Text = There is no active container available for turning off. Maybe there is no active container? + } + + InvalidDevice { + Title = Invalid device + Text = The device you have chosen is invalid! + } + + + InvalidType { + Title = Unknown type + Text = The type of this volume is unknown. + } + + VolumeMayNotBeMounted { + Title = The container is mounted + Text = This action is not available while the container is active. Please turn it off first. + } +} + + +SuccessMessage { + InitRunning { + Title = Initialization running + Text = The initialization will be completed in background. You may configure it now and activate the encrypted filesystem in a few minutes. + } + + ConfigSaved { + Title = Configuration saved + Text = The new settings have been accepted. + } + + PowerOff { + Title = Shutdown + Text = The CryptoBox is currently going to halt. In a few seconds you can power it off (in case this does not happen automatically). + } + + ReBoot { + Title = Reboot + Text = The CryptoBox is currently rebooting. In a few seconds it will be available again. Please wait - you will get redirected, when the reboot has finished. + } +} + + +EnvironmentWarning { + + ReadOnlyConfig { + Text = Read-only setup detected - probably you should create a configuration partition. + Link.Text = Initialize partition + Link.Rel = partition + } + + NoSSL { + Text = The connection is not encrypted - passwords can be easily intercepted. + Link.Text = Use encrypted connection + Link.Prot = https + } + } +} + + +ErrorMessage { + + UnknownAction { + Title = Unknown action + Text = You have requested an undefined action. + } + + InitFailed { + Title = Initialization failed + Text = Please send the logfiles (see above) to the developers of the CryptoBox (cryptobox@systemausfall.org). + } + + NoHardDisk { + Title = No hard disk + Text = No disk suitable for an encrypted filesystem found. Please ensure the BIOS detected the disk during power-on of the computer. + } + + NoDocumentation { + Title = No documentation + Text = There is no documentation available! + } +} + diff --git a/lang/fr.hdf b/lang/fr.hdf index 462f796..e6241ee 100644 --- a/lang/fr.hdf +++ b/lang/fr.hdf @@ -1,256 +1,254 @@ -Lang { +Name = Francais - Name = francais +Status = $Id$ - Status = $Id$ +Title { + Top = La CryptoBox + Slogan = La vie privée pour nous autres. + Init = Initialisation CryptoBox + Mount = Activation des données cryptées + Umount = Déactivation des données cryptées + Config = CryptoBox configuration + Log = fichiers log CryptoBox + System = Système + Status = Statut + Volume = Propriétés de +} - Title { - Top = La CryptoBox - Slogan = La vie privée pour nous autres. - Init = Initialisation CryptoBox - Mount = Activation des données cryptées - Umount = Déactivation des données cryptées - Config = CryptoBox configuration - Log = fichiers log CryptoBox - System = Système - Status = Statut - Volume = Propriétés de + +Text { + EnterCurrentCryptoPassword = Entrez le mot de passe crypto : + EnterNewCryptoPassword = Entrez le nouveau mot de passe crypto : + EnterSameCryptoPassword = Répétez le nouveau mot de passe crypto : + EnterCurrentAdminPassword = Entrez le mot de passe administrateur actuel: + EnterNewAdminPassword = Entrez le nouveau mot de passe administrateur : + EnterSameAdminPassword = Répétez le nouveau mot de passe administrateur : + InitWarning = Pendant le procès d'initialisation TOUTES LES DONNÉES sur le disque vont être SUPPRIMÉES ! + ConfirmInitHint = Pour confirmer que vous sachiez exactement ce que vous faites, veuillez taper exactement la phrase suivante : + ConfirmInit = Oui, supprimer toutes les données ! + PartitionInfo = Partitionnement actuel du disque : + IPAddress = Adresse réseau (IP) de la CryptoBox : + TimeOut = Timeout pour la désactivation du système de fichiers crypté (en minutes): + EmptyLog = Les fichiers log de la CryptoBox sont vides. + SelectLanguage = Préférences de langue : + RedirectNote = Cliquez ici si votre navigateur ne supporte pas la redirection automatique. + ProjectHomePage = Site web du projet + ProjectNote = La CryptoBox est un projet de + DoUmount = Déactivation du système de fichiers crypté + DoMount = Activation du système de fichiers crypté + Configuration = Configuration + CryptoIsActive = Les données cryptées sont accessibles. + CryptoIsDown = Les données cryptées sont protégés de tout accès. + ChoosePartition = Quel conteneur voulez-vous activer ? + ChosenPartition = Le conteneur que vous avez choisi est + ActivePartitions = Les conteneurs suivants sont activés + PassivePartitions = Les conteneurs suivants sont désactivés + ContainerName = Nom du conteneur : + ContainerEncryption = Aciver le cryptage +} + + +Button { + DoInit = Initialisation + SaveConfig = Sauvegarder la configuration + Update = Recharger + Mount = Activer système de fichiers + Umount = Déactiver système de fichiers + Config = Configuration + PowerOff = Arrêt + ReBoot = Redémarrage + Protocol = Montrer fichiers log + Documentation = Aide + Status = Status + System = Système + ContainerNameSet = Changer nom + InitContainer = Initialiser conteneur +} + + +WarningMessage { + InitNotConfirmed { + Title = Echec de la confirmation + Text = La phrase n'a pas été tapé exactement ! } - - Text { - EnterCurrentCryptoPassword = Entrez le mot de passe crypto : - EnterNewCryptoPassword = Entrez le nouveau mot de passe crypto : - EnterSameCryptoPassword = Répétez le nouveau mot de passe crypto : - EnterCurrentAdminPassword = Entrez le mot de passe administrateur actuel: - EnterNewAdminPassword = Entrez le nouveau mot de passe administrateur : - EnterSameAdminPassword = Répétez le nouveau mot de passe administrateur : - InitWarning = Pendant le procès d'initialisation TOUTES LES DONNÉES sur le disque vont être SUPPRIMÉES ! - ConfirmInitHint = Pour confirmer que vous sachiez exactement ce que vous faites, veuillez taper exactement la phrase suivante : - ConfirmInit = Oui, supprimer toutes les données ! - PartitionInfo = Partitionnement actuel du disque : - IPAddress = Adresse réseau (IP) de la CryptoBox : - TimeOut = Timeout pour la désactivation du système de fichiers crypté (en minutes): - EmptyLog = Les fichiers log de la CryptoBox sont vides. - SelectLanguage = Préférences de langue : - RedirectNote = Cliquez ici si votre navigateur ne supporte pas la redirection automatique. - ProjectHomePage = Site web du projet - ProjectNote = La CryptoBox est un projet de - DoUmount = Déactivation du système de fichiers crypté - DoMount = Activation du système de fichiers crypté - Configuration = Configuration - CryptoIsActive = Les données cryptées sont accessibles. - CryptoIsDown = Les données cryptées sont protégés de tout accès. - ChoosePartition = Quel conteneur voulez-vous activer ? - ChosenPartition = Le conteneur que vous avez choisi est - ActivePartitions = Les conteneurs suivants sont activés - PassivePartitions = Les conteneurs suivants sont désactivés - ContainerName = Nom du conteneur : - ContainerEncryption = Aciver le cryptage + EmptyCryptoPassword { + Title = Mot de passe crypto invalide + Text = Vous devez entrer un mot de passe crypto ! } - - Button { - DoInit = Initialisation - SaveConfig = Sauvegarder la configuration - Update = Recharger - Mount = Activer système de fichiers - Umount = Déactiver système de fichiers - Config = Configuration - PowerOff = Arrêt - ReBoot = Redémarrage - Protocol = Montrer fichiers log - Documentation = Aide - Status = Status - System = Système - ContainerNameSet = Changer nom - InitContainer = Initialiser conteneur + DifferentCryptoPasswords { + Title = Les mots de passe crypto ne correspondent pas + Text = Les deux mots de passe rentrés doivent être identiques afin de vérifier que ce mot de passe soit celui qui est désiré. } - - WarningMessage { - InitNotConfirmed { - Title = Echec de la confirmation - Text = La phrase n'a pas été tapé exactement ! - } - - EmptyCryptoPassword { - Title = Mot de passe crypto invalide - Text = Vous devez entrer un mot de passe crypto ! - } - - DifferentCryptoPasswords { - Title = Les mots de passe crypto ne correspondent pas - Text = Les deux mots de passe rentrés doivent être identiques afin de vérifier que ce mot de passe soit celui qui est désiré. - } - - DifferentAdminPasswords { - Title = Les mots de passe administrateur ne correspondent pas - Text = Les deux mots de passe rentrés doivent être identiques afin de vérifier que ce mot de passe soit celui qui est désiré. - } - - WrongAdminPassword { - Title = Mot de passe administrateur erroné - Text = Le mot de passe administrateur que vous avez rentré est erroné. Veuillez réessayer. - } - - MountFailed { - Title = Echec de l'activation - Text = Le sytème de fichiers crypté n'a pu être activé. Probablement le mot de passe que vous avez fourni était erroné. Veuillez réessayer. - } - - UmountFailed { - Title = Echec de la désactivation - Text = Le système de fichiers crypté n'a pu être désactivé. Probablement certains fichiers sont en cours d'utilisation. Fermez tous les programmes. En cas d'urgence, éteignez la CryptoBox ! - } - - NotInitialized { - Title = Aucune configuration trouvée - Text = La CryptoBox n'a pas encore été configurée. - } - - InitNotFinished { - Title = L'initialisation n'est pas encore complétée - Text = L'initialisation va être complétée en quelques minutes. Après comlpétion de l'initialisation cette action va être disponible. - } - - IsMounted { - Title = Déjà actif - Text = Le système de fichiers crypté a déjà été activé. - } - - NotMounted { - Title = Inactif - Text = Le système de fichiers crypté n'est pas actif actuellement. - } - - AlreadyConfigured { - Title = Configuration trouvé - Text = La CryptoBox a déjà été configurée. Si vous réinitialisez une nouvelle fois, toutes les données seront perdues ! - } - - InvalidLanguage { - Title = Langue invalide - Text = La langue choisie n'est pas disponible ! - } - - InvalidIP { - Title = Addresse IP invalide - Text = L'adresse réseau sélectionnée n'est pas valide ! - } - - InvalidTimeOut { - Title = Timeout invalide - Text = Le timeout sélectionné n'est pas valide ! - } - - ConfigTimeOutFailed { - Title = Erreur pendant le changement du timeout - Text = La valeur du timeout n'a pas pu être changé ! - } - - ConfigLanguageFailed { - Title = Erreur pendant le changement des préférences de langue - Text = Les préférences de langue n'ont pu être changées ! - } - - ConfigIPFailed { - Title = Erreur pendant le changement de l'adresse réseau - Text = L'adresse réseau n'a pas pu être changée ! - } - - IPAddressChanged { - Title = Changement de l'adresse réseau - Text = L'adresse réseau a été changée. dans quelques secondes vous allez être redirigés vers la nouvelle adresse. - } - - NoDiskAvailableForMount { - Title = Aucune partition disponible - Text = Il n'y a pas de conteneur inutilisé disponible. Peut-être tous les conteneur sont déjà montés ? - } - - NoDiskAvailableForUmount { - Title = Aucune partition disponible - Text = Il n'y a pas de conteneur actif qui peut être éteint. Peut-être n y a-t-il pas de conteneur actif ? - } - - InvalidDevice { - Title = Device invalide - Text = Le device que vous avez choisi est invalide ! - } - - InvalidVolumeName { - Title = Echec du changement du nom du conteneur - Text = Le nouveau nom du conteneur était invalide. Veuillez réessayer ! - } - - SetVolumeNameFailed { - Title = Echec lors du changmenent du nom du conteneur - Text = Le nom du conteneur n'a pas pu être changé. Veuillez regarder les fichiers log pour plus de détails. - } - - VolumeMayNotBeMounted { - Title = Le conteneur est monté - Text = Cette action n'est pas disponible pendant que le conteneur est actif. Désactivez-le d'abord. - } + DifferentAdminPasswords { + Title = Les mots de passe administrateur ne correspondent pas + Text = Les deux mots de passe rentrés doivent être identiques afin de vérifier que ce mot de passe soit celui qui est désiré. } - - SuccessMessage { - InitRunning { - Title = Initialisation en cours - Text = L'initialisation va être complétée en tant que tâche de fond. Vous pouvez configurer maintenant et le système de fichiers crypté dans quelques minutes. - } - - ConfigSaved { - Title = Configuration sauvegardée - Text = Les nouveaux paramètres ont été acceptés. - } - - MountDone { - Title = Système de fichiers crypté activé - Text = Le système de fichiers crypté est maintenant disponible. - } - - UmountDone { - Title = Système de fichiers crypté déactivé - Text = Le système de fichiers crypté est maintenant sécurisé de toute forme d'accès. - } - - PowerOff { - Title = Arrêt - Text = La CryptoBox va maintenant s'éteindre. Dans quelques secondes vous pouvez l'éteindre (au cas où ceci ne se passe pas automatiquement). - } - - ReBoot { - Title = Redémarrage - Text = La CryptoBox est en train de redémarrer. Dans quelques secondes elle va à nouveau être disponible. Veuillez attendre - vous allez être redirigés quand le redémarrage est accomplie. - } + WrongAdminPassword { + Title = Mot de passe administrateur erroné + Text = Le mot de passe administrateur que vous avez rentré est erroné. Veuillez réessayer. } - - ErrorMessage { - - UnknownAction { - Title = Action inconnue - Text = Vous avez demandé une action indéfinie. - } - - NoSSL { - Title = Connexion non cryptée - Text = La CryptoBox accepte seulement des connexions cryptées (https), afin que le mot de passe ne soit pas visible pour des yeux curieux. La connexion cryptée va être établie dans quelques secondes. - } - - InitFailed { - Title = Initialisation échouée - Text = Veuillez envoyer les fichiers log (voir ci-dessus) aux développeurs de la CryptoBox (cryptobox@systemausfall.org). - } - - NoHardDisk { - Title = Aucun disque dur - Text = Aucun disque utilisable pour un système de fichier crypté a été trouvé. Veuillez vérifier que le BIOS a détecté le disque pendant le démarrage de l'ordinateur. - } + MountFailed { + Title = Echec de l'activation + Text = Le sytème de fichiers crypté n'a pu être activé. Probablement le mot de passe que vous avez fourni était erroné. Veuillez réessayer. } + UmountFailed { + Title = Echec de la désactivation + Text = Le système de fichiers crypté n'a pu être désactivé. Probablement certains fichiers sont en cours d'utilisation. Fermez tous les programmes. En cas d'urgence, éteignez la CryptoBox ! + } + + NotInitialized { + Title = Aucune configuration trouvée + Text = La CryptoBox n'a pas encore été configurée. + } + + InitNotFinished { + Title = L'initialisation n'est pas encore complétée + Text = L'initialisation va être complétée en quelques minutes. Après comlpétion de l'initialisation cette action va être disponible. + } + + IsMounted { + Title = Déjà actif + Text = Le système de fichiers crypté a déjà été activé. + } + + NotMounted { + Title = Inactif + Text = Le système de fichiers crypté n'est pas actif actuellement. + } + + AlreadyConfigured { + Title = Configuration trouvé + Text = La CryptoBox a déjà été configurée. Si vous réinitialisez une nouvelle fois, toutes les données seront perdues ! + } + + InvalidLanguage { + Title = Langue invalide + Text = La langue choisie n'est pas disponible ! + } + + InvalidIP { + Title = Addresse IP invalide + Text = L'adresse réseau sélectionnée n'est pas valide ! + } + + InvalidTimeOut { + Title = Timeout invalide + Text = Le timeout sélectionné n'est pas valide ! + } + + ConfigTimeOutFailed { + Title = Erreur pendant le changement du timeout + Text = La valeur du timeout n'a pas pu être changé ! + } + + ConfigLanguageFailed { + Title = Erreur pendant le changement des préférences de langue + Text = Les préférences de langue n'ont pu être changées ! + } + + ConfigIPFailed { + Title = Erreur pendant le changement de l'adresse réseau + Text = L'adresse réseau n'a pas pu être changée ! + } + + IPAddressChanged { + Title = Changement de l'adresse réseau + Text = L'adresse réseau a été changée. dans quelques secondes vous allez être redirigés vers la nouvelle adresse. + } + + NoDiskAvailableForMount { + Title = Aucune partition disponible + Text = Il n'y a pas de conteneur inutilisé disponible. Peut-être tous les conteneur sont déjà montés ? + } + + NoDiskAvailableForUmount { + Title = Aucune partition disponible + Text = Il n'y a pas de conteneur actif qui peut être éteint. Peut-être n y a-t-il pas de conteneur actif ? + } + + InvalidDevice { + Title = Device invalide + Text = Le device que vous avez choisi est invalide ! + } + + InvalidVolumeName { + Title = Echec du changement du nom du conteneur + Text = Le nouveau nom du conteneur était invalide. Veuillez réessayer ! + } + + SetVolumeNameFailed { + Title = Echec lors du changmenent du nom du conteneur + Text = Le nom du conteneur n'a pas pu être changé. Veuillez regarder les fichiers log pour plus de détails. + } + + VolumeMayNotBeMounted { + Title = Le conteneur est monté + Text = Cette action n'est pas disponible pendant que le conteneur est actif. Désactivez-le d'abord. + } +} + + +SuccessMessage { + InitRunning { + Title = Initialisation en cours + Text = L'initialisation va être complétée en tant que tâche de fond. Vous pouvez configurer maintenant et le système de fichiers crypté dans quelques minutes. + } + + ConfigSaved { + Title = Configuration sauvegardée + Text = Les nouveaux paramètres ont été acceptés. + } + + MountDone { + Title = Système de fichiers crypté activé + Text = Le système de fichiers crypté est maintenant disponible. + } + + UmountDone { + Title = Système de fichiers crypté déactivé + Text = Le système de fichiers crypté est maintenant sécurisé de toute forme d'accès. + } + + PowerOff { + Title = Arrêt + Text = La CryptoBox va maintenant s'éteindre. Dans quelques secondes vous pouvez l'éteindre (au cas où ceci ne se passe pas automatiquement). + } + + ReBoot { + Title = Redémarrage + Text = La CryptoBox est en train de redémarrer. Dans quelques secondes elle va à nouveau être disponible. Veuillez attendre - vous allez être redirigés quand le redémarrage est accomplie. + } +} + + +ErrorMessage { + + UnknownAction { + Title = Action inconnue + Text = Vous avez demandé une action indéfinie. + } + + NoSSL { + Title = Connexion non cryptée + Text = La CryptoBox accepte seulement des connexions cryptées (https), afin que le mot de passe ne soit pas visible pour des yeux curieux. La connexion cryptée va être établie dans quelques secondes. + } + + InitFailed { + Title = Initialisation échouée + Text = Veuillez envoyer les fichiers log (voir ci-dessus) aux développeurs de la CryptoBox (cryptobox@systemausfall.org). + } + + NoHardDisk { + Title = Aucun disque dur + Text = Aucun disque utilisable pour un système de fichier crypté a été trouvé. Veuillez vérifier que le BIOS a détecté le disque pendant le démarrage de l'ordinateur. + } +} + } diff --git a/lang/language_specification.txt b/lang/language_specification.txt new file mode 100644 index 0000000..7cfaf44 --- /dev/null +++ b/lang/language_specification.txt @@ -0,0 +1,24 @@ +Some important notes regarding the language files: + +1) language file informations + +The toplevel attribute "Name" is mandatory and should represent the local name of +the language (e.g.: English, German, French, ...). + + +2) warning/success messages + +Attributes: + Title - title of the message + Text - text of the message + Link.Text - textual representation of a link + Link.Abs - absolute URL e.g. http://cryptobox.org/trac + Link.Prot - http|https + Link.Rel - relative url (based on the cgi root) + + All values are optional, but you should follow these rules: + - at least one of "Title" and "Text" should be defined + - if there are "Link" attributes, then you should also define "Link.Text" + - "Link.Abs" can not be combined with "Link.Prot" or "Link.Rel" + - "Link.Rel" and "Link.Prot" may be used together + diff --git a/lang/si.hdf b/lang/si.hdf index 2ec7a35..802cd8e 100644 --- a/lang/si.hdf +++ b/lang/si.hdf @@ -1,215 +1,193 @@ -Lang { +Name = Slovenščina - Name = slovenščina - - Status = $Id$ - - Title { - Top = The CryptoBox - Slogan = Privatnost v vsako vas! - Init = CryptoBox zagon - Mount = Aktivacija kriptiranih podatkov - Umount = Deaktivacija kriptiranih podatkov - Config = CryptoBox konfiguracija - Log = CryptoBox dnevnik - ShutDown = Ugasni - Status = Stanje - } - - - Text { - EnterCurrentCryptoPassword = Vpišite geslo: - EnterNewCryptoPassword = Vpišite novo geslo: - EnterSameCryptoPassword = Ponovite novo geslo: - EnterCurrentAdminPassword = Vnesite trenutno obstoječe geslo administratorja/ke: - EnterNewAdminPassword = Vnesite novo geslo administratorja/ke: - EnterSameAdminPassword = Ponovite novo geslo administratorja/ke: - InitWarning = Med incializacijo bodo VSI PODATKI z vašega trdega diska IZBRISANI! - ConfirmInitHint = Vsled potrditve vaših dejanj vpišite naslednje besedilo: - ConfirmInit = Da, zbirši vse podatke! - PartitionInfo = trenutna porazdelitev trdega diska: - IPAddress = IP CryptoBoxa: - TimeOut = Čas preklica deaktivacije kriptiranega datotečnega sistema:(v minutah) - EmptyLog = Dnevnik CryptoBoxa je prazen. - SelectLanguage = Jezikovne nastavitve: - RedirectNote = Kliknite če vaš iskalnik ne podpira avtomatične preusmeritve. - ProjectHomePage = Spletna stran projekta - ProjectNote = CryptoBox je projekt - DoUmount = Deaktivacija kriptiranega datotečnega sistema - DoMount = Aktivacija kriptiranega datotečnega sistema - Configuration = Konfiguracija - CryptoIsActive = Kriptirani podatki so dostopni. - CryptoIsDown = Kriptirani podatki soso zaèiteni pred kakr nimkoli dostopom. - } - - - Button { - DoInit = Zagon CryptoBoxa - SaveConfig = Shrani konfiguracijo - Update = Osveži - Mount = Aktivacija kriptiranega datotečnega sistema - Umount = Deaktivacija kriptiranega datotečnega sistema - Config = Konfiguracija - PowerOff = Ugasni - ReBoot = Ponovni zagon - Protocol = Dnevnik - Documentation = Priročnik - Status = Stanje - } - - - WarningMessage { - InitNotConfirmed { - Title = Potrditev ni uspela - Text = Besedilo mora biti vpisano natanko kot je prikazano! - } - - EmptyCryptoPassword { - Title = Nepravilno geslo - Text = Geslo ne sme biti prazno! - } - - DifferentCryptoPasswords { - Title = Gesli se ne ujemata! - Text = Obe vnešeni gesli morata biti identični, v zagotovilo, da je vpisano željeno geslo. - } - - DifferentAdminPasswords { - Title = Administracijski gesli se ne ujemata - Text = Obe vnešeni gesli morata biti identični, v zagotovilo da je vpisano željeno geslo. - } - - WrongAdminPassword { - Title = Napačno administracijsko geslo! - Text = Vnešeno administracijsko geslo je nepravilno. Prosimo poskusite znova! - } - - MountFailed { - Title = Aktivacija ni uspela - Text = Kriptiran datotečni sistem se ni aktiviral.Po vsej verjetnosti je bilo geslo napačno. - } - - UmountFailed { - Title = Deaktivacija ni uspela - Text = Kriptiran datotečni sistem se ni aktiviral.Datoteke so morda v uporabi. Zaprite vse programe. (naprimer ta nadvse razširjen urejevalnik besedil). V primeru nuje ugasnite CryptoBox! - } - - NotInitialized { - Title = Konfiguracija ni najdena - Text = CryptoBox še ni bil konfiguriran. - } - - InitNotFinished { - Title = Zagon še ni dovršen - Text = Zagon bo dovršen v nekaj minutah. Po končanem zagonu bo ta možnost omogočena. - } - - IsMounted { - Title = Že aktivno - Text = Kriptiran datotečni sistem je že aktiviran. - } - - NotMounted { - Title = Onemogočeno - Text = Kriptiran datotečni sistem trenutno ni aktiven. - } - - AlreadyConfigured { - Title = Konfiguracija uspela - Text = CryptoBox je đe bil konfiguriran. Če ponovno zaženete bodo vsi podatki izbrisani! - } - - InvalidLanguage { - Title = Nepravilna izbira jezika - Text = Izbrani jezik ni na voljo! - } - - InvalidIP { - Title = Napačen IP naslov - Text = Izbran omrežni naslov ni veljaven! - } - - InvalidTimeOut { - Title = Nepravilen čas preklica - Text = Izbran čas preklica ni veljaven! - } - - ConfigTimeOutFailed { - Title = Napaka med spremembo časa preklica - Text = Časa preklica ne morete spremeniti! - } - - ConfigLanguageFailed { - Title = Napaka med spremembo jezikovnih nastavitev - Text = Spreminjanje jezikovnih nastavitev ni mogoče. - } - - ConfigIPFailed { - Title = Napaka med spreminjanjem omrežnega naslova. - Text = Spreminjanje omrežnega naslova ni mogoče. - } - - IPAddressChanged { - Title = Sprememba omrežnega naslova - Text = Omrežni naslov je spremenjen. V nekaj sekundah boste preusmerjeni na nov naslov. - } - } - - - SuccessMessage { - InitRunning { - Title = Zagon poteka - Text = Zagon bo dokončan v ozadju. - } - - ConfigSaved { - Title = Konfiguracija spravljena - Text = Nove nastavitve so sprejete. - } - - MountDone { - Title = Kriptiran datotečni sistem aktiviran - Text = Kriptiran datotečni sistem je na voljo. - } - - UmountDone { - Title = Kriptiran datotečni sistem deaktiviran. - Text = Kriptiran datotečni sistem je varovan pred vstopom. - } - - PowerOff { - Title = Ugasni - Text = CryptoBox se zaustavlja. V nekaj sekundah ga lahko izklopite.(v kolikor se to ne zgodi avtomatično). - } - - ReBoot { - Title = Ponovni zagon - Text = CryptoBox se zaganja. V nekaj sekundah bo zopet na voljo. - } - } - - - ErrorMessage { - - UnknownAction { - Title = Neznan zahtevek - Text = Podali ste nedefiniran zahtevek. - } - - NoSSL { - Title = Nekriptirana povezava - Text = CryptoBox sprejme le kriptirane povezave (https), da je geslo zaščiteno pred radovednimi očmi. V nekaj sekundah bo kriptirana povezava vzpostavljena. - } - - InitFailed { - Title = Zagon ni uspel - Text = Prosim pošljite dnevnik (poglejte zgoraj) razvijalcem CryptoBoxa (cryptobox@systemausfall.org). - } - - NoHardDisk { - Title = Ni trdega diska - Text = Primeren trdi disk za shranjenje kriptiranega datotečnega sistema ni zaznan. Poskrbite da bo med zagonom BIOS zaznal trdi disk. - } - } +Status = $Id$ +Title { + Top = The CryptoBox + Slogan = Privatnost v vsako vas! + Init = CryptoBox zagon + Mount = Aktivacija kriptiranih podatkov + Umount = Deaktivacija kriptiranih podatkov + Config = CryptoBox konfiguracija + Log = CryptoBox dnevnik + ShutDown = Ugasni + Status = Stanje } + + +Text { + EnterCurrentCryptoPassword = Vpišite geslo: + EnterNewCryptoPassword = Vpišite novo geslo: + EnterSameCryptoPassword = Ponovite novo geslo: + EnterCurrentAdminPassword = Vnesite trenutno obstoječe geslo administratorja/ke: + EnterNewAdminPassword = Vnesite novo geslo administratorja/ke: + EnterSameAdminPassword = Ponovite novo geslo administratorja/ke: + InitWarning = Med incializacijo bodo VSI PODATKI z vašega trdega diska IZBRISANI! + ConfirmInitHint = Vsled potrditve vaših dejanj vpišite naslednje besedilo: + ConfirmInit = Da, zbirši vse podatke! + PartitionInfo = trenutna porazdelitev trdega diska: + IPAddress = IP CryptoBoxa: + TimeOut = Čas preklica deaktivacije kriptiranega datotečnega sistema:(v minutah) + EmptyLog = Dnevnik CryptoBoxa je prazen. + SelectLanguage = Jezikovne nastavitve: + RedirectNote = Kliknite če vaš iskalnik ne podpira avtomatične preusmeritve. + ProjectHomePage = Spletna stran projekta + ProjectNote = CryptoBox je projekt + DoUmount = Deaktivacija kriptiranega datotečnega sistema + DoMount = Aktivacija kriptiranega datotečnega sistema + Configuration = Konfiguracija + CryptoIsActive = Kriptirani podatki so dostopni. + CryptoIsDown = Kriptirani podatki soso zaèiteni pred kakr nimkoli dostopom. +} + + +Button { + DoInit = Zagon CryptoBoxa + SaveConfig = Shrani konfiguracijo + Update = Osveži + Mount = Aktivacija kriptiranega datotečnega sistema + Umount = Deaktivacija kriptiranega datotečnega sistema + Config = Konfiguracija + PowerOff = Ugasni + ReBoot = Ponovni zagon + Protocol = Dnevnik + Documentation = Priročnik + Status = Stanje + System = System +} + + +WarningMessage { + InitNotConfirmed { + Title = Potrditev ni uspela + Text = Besedilo mora biti vpisano natanko kot je prikazano! + } + + EmptyCryptoPassword { + Title = Nepravilno geslo + Text = Geslo ne sme biti prazno! + } + + DifferentCryptoPasswords { + Title = Gesli se ne ujemata! + Text = Obe vnešeni gesli morata biti identični, v zagotovilo, da je vpisano željeno geslo. + } + + DifferentAdminPasswords { + Title = Administracijski gesli se ne ujemata + Text = Obe vnešeni gesli morata biti identični, v zagotovilo da je vpisano željeno geslo. + } + + WrongAdminPassword { + Title = Napačno administracijsko geslo! + Text = Vnešeno administracijsko geslo je nepravilno. Prosimo poskusite znova! + } + + MountFailed { + Title = Aktivacija ni uspela + Text = Kriptiran datotečni sistem se ni aktiviral.Po vsej verjetnosti je bilo geslo napačno. + } + + UmountFailed { + Title = Deaktivacija ni uspela + Text = Kriptiran datotečni sistem se ni aktiviral.Datoteke so morda v uporabi. Zaprite vse programe. (naprimer ta nadvse razširjen urejevalnik besedil). V primeru nuje ugasnite CryptoBox! + } + + NotInitialized { + Title = Konfiguracija ni najdena + Text = CryptoBox še ni bil konfiguriran. + } + + InitNotFinished { + Title = Zagon še ni dovršen + Text = Zagon bo dovršen v nekaj minutah. Po končanem zagonu bo ta možnost omogočena. + } + + IsMounted { + Title = Že aktivno + Text = Kriptiran datotečni sistem je že aktiviran. + } + + NotMounted { + Title = Onemogočeno + Text = Kriptiran datotečni sistem trenutno ni aktiven. + } + + AlreadyConfigured { + Title = Konfiguracija uspela + Text = CryptoBox je đe bil konfiguriran. Če ponovno zaženete bodo vsi podatki izbrisani! + } + + InvalidLanguage { + Title = Nepravilna izbira jezika + Text = Izbrani jezik ni na voljo! + } + + ConfigLanguageFailed { + Title = Napaka med spremembo jezikovnih nastavitev + Text = Spreminjanje jezikovnih nastavitev ni mogoče. + } +} + + +SuccessMessage { + InitRunning { + Title = Zagon poteka + Text = Zagon bo dokončan v ozadju. + } + + ConfigSaved { + Title = Konfiguracija spravljena + Text = Nove nastavitve so sprejete. + } + + MountDone { + Title = Kriptiran datotečni sistem aktiviran + Text = Kriptiran datotečni sistem je na voljo. + } + + UmountDone { + Title = Kriptiran datotečni sistem deaktiviran. + Text = Kriptiran datotečni sistem je varovan pred vstopom. + } + + PowerOff { + Title = Ugasni + Text = CryptoBox se zaustavlja. V nekaj sekundah ga lahko izklopite.(v kolikor se to ne zgodi avtomatično). + } + + ReBoot { + Title = Ponovni zagon + Text = CryptoBox se zaganja. V nekaj sekundah bo zopet na voljo. + } +} + + +ErrorMessage { + + UnknownAction { + Title = Neznan zahtevek + Text = Podali ste nedefiniran zahtevek. + } + + NoSSL { + Title = Nekriptirana povezava + Text = CryptoBox sprejme le kriptirane povezave (https), da je geslo zaščiteno pred radovednimi očmi. V nekaj sekundah bo kriptirana povezava vzpostavljena. + } + + InitFailed { + Title = Zagon ni uspel + Text = Prosim pošljite dnevnik (poglejte zgoraj) razvijalcem CryptoBoxa (cryptobox@systemausfall.org). + } + + NoHardDisk { + Title = Ni trdega diska + Text = Primeren trdi disk za shranjenje kriptiranega datotečnega sistema ni zaznan. Poskrbite da bo med zagonom BIOS zaznal trdi disk. + } + + NoDocumentation { + Title = No documentation + Text = There is no documentation available! + } +} + diff --git a/plugins/date/date.py b/plugins/date/date.py new file mode 100644 index 0000000..466a81a --- /dev/null +++ b/plugins/date/date.py @@ -0,0 +1,69 @@ +import CryptoBoxPlugin + + +class date(CryptoBoxPlugin.CryptoBoxPlugin): + + pluginCapabilities = [ "system" ] + requestAuth = False + rank = 10 + + def doAction(self, store=None, year=None, month=None, day=None, hour=None, minute=None): + import datetime + if store: + try: + year, month, day = int(year), int(month), int(day) + hour, minute = int(hour), int(minute) + new_date = datetime.datetime(year, month, day, hour, minute) + except ValueError: + self.hdf["Data.Warning"] = "Plugins.date.InvalidDate" + self.__prepareFormData() + return "form_date" + date = "%02d%02d%02d%02d%d" % (month, day, hour, minute, year) + if self.__setDate(date): + self.cbox.log.info("changed date to: %s" % date) + self.hdf["Data.Success"] = "Plugins.date.DateChanged" + return None + else: + ## a failure should usually be an invalid date (we do not check it really) + self.cbox.log.info("failed to set date: %s" % date) + self.hdf["Data.Warning"] = "Plugins.date.InvalidDate" + self.__prepareFormData() + return "form_date" + else: + self.__prepareFormData() + return "form_date" + + + def getStatus(self): + now = self.__getCurrentDate() + return "%d/%d/%d/%d/%d/%d" % (now.year, now.month, now.day, now.hour, now.minute, now.second) + + + def __prepareFormData(self): + date = self.__getCurrentDate() + self.hdf[self.hdf_prefix + "year"] = date.year + self.hdf[self.hdf_prefix + "month"] = date.month + self.hdf[self.hdf_prefix + "day"] = date.day + self.hdf[self.hdf_prefix + "hour"] = date.hour + self.hdf[self.hdf_prefix + "minute"] = date.minute + + + def __getCurrentDate(self): + import datetime + return datetime.datetime(2000,1,1).now() + + + def __setDate(self, date): + import subprocess + import os + proc = subprocess.Popen( + shell = False, + args = [ + self.cbox.prefs["Programs"]["super"], + self.cbox.prefs["Programs"]["CryptoBoxRootActions"], + "plugin", + os.path.join(self.pluginDir, "root_action.py"), + date]) + proc.wait() + return proc.returncode == 0 + diff --git a/plugins/date/form_date.cs b/plugins/date/form_date.cs new file mode 100644 index 0000000..3dd732a --- /dev/null +++ b/plugins/date/form_date.cs @@ -0,0 +1,44 @@ + + +

+ + + + + +


+ + +

+ +


+  :  +

+ + + + + + + diff --git a/plugins/date/lang/en.hdf b/plugins/date/lang/en.hdf new file mode 100644 index 0000000..df00df5 --- /dev/null +++ b/plugins/date/lang/en.hdf @@ -0,0 +1,35 @@ +Name = Change date and time +Link = Set date/time + +Title.ConfigDate = Date and time setting + +Button.ConfigDate = Set date and time + +Text.Date = Date +Text.Time = Time +Text.Months { + 1 = January + 2 = February + 3 = March + 4 = April + 5 = May + 6 = June + 7 = July + 8 = August + 9 = September + 10 = October + 11 = November + 12 = December +} + +SuccessMessage.DateChanged { + Title = Date changed + Text = The date was changed successfully. +} + +WarningMessage { + InvalidDate { + Title = Invalid value + Text = An invalid value for date or time was supplied. Please try again. + } +} diff --git a/plugins/date/plugin_icon.png b/plugins/date/plugin_icon.png new file mode 100644 index 0000000000000000000000000000000000000000..f26390681e007bb92eff31d2e074a56f061d666b GIT binary patch literal 3285 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 1: + sys.stderr.write("%s: too many arguments (%s)\n" % (self_bin, args)) + sys.exit(1) + + if len(args) == 0: + sys.stderr.write("%s: no argument supplied\n" % self_bin) + sys.exit(1) + + if re.search(u'\D', args[0]): + sys.stderr.write("%s: illegal argument (%s)\n" % (self_bin, args[0])) + sys.exit(1) + + proc = subprocess.Popen( + shell = False, + stdout = subprocess.PIPE, + args = [DATE_BIN, args[0]]) + proc.wait() + sys.exit(proc.returncode) + diff --git a/plugins/date/unittests.py b/plugins/date/unittests.py new file mode 100644 index 0000000..82f1faa --- /dev/null +++ b/plugins/date/unittests.py @@ -0,0 +1,60 @@ +import WebInterfaceTestClass + +class unittests(WebInterfaceTestClass.WebInterfaceTestClass): + + def test_get_date(self): + date = self._getCurrentDate() + + + def test_change_date(self): + now = self._getCurrentDate() + ## copy current time + new_date = dict(now) + ## move three minutes forward (more is not nice because of screensavers) + new_date["minute"] = (now["minute"] + 3) % 60 + ## in case of minute-overflow we also have to move the hour a little bit forward + new_date["hour"] = now["hour"] + ((now["minute"] + 3) / 60) + ## move forward ... + self._setDate(new_date) + self.assertEquals(new_date, self._getCurrentDate()) + ## ... and backward + self._setDate(now) + self.assertEquals(now, self._getCurrentDate()) + + + def _getCurrentDate(self): + date_url = self.URL + "date" + self.register_auth(date_url) + self.cmd.go(date_url) + self.cmd.find("Data.Status.Plugins.date=([0-9]+/[0-9]+/[0-9]+/[0-9]+/[0-9]+/[0-9]+)$", "m") + dateNumbers = self.locals["__match__"].split("/") + self.assertEquals(len(dateNumbers), 6) + ## we ignore seconds + dateField = { + "year" : int(dateNumbers[0]), + "month" : int(dateNumbers[1]), + "day" : int(dateNumbers[2]), + "hour" : int(dateNumbers[3]), + "minute" : int(dateNumbers[4])} + return dateField + + + def _setDate(self, date): + """for now we have to use this function instead of the one below""" + date_url = self.URL + "date?store=1&year=%d&month=%d&day=%d&hour=%d&minute=%d" % (date["year"], date["month"], date["day"], date["hour"], date["minute"]) + self.register_auth(date_url) + self.cmd.go(date_url) + + + def _setDateBroken(self, date): + """this should work, but the parsing of twill seems to be broken""" + date_url = self.URL + "date" + self.register_auth(date_url) + self.cmd.go(date_url) + self.cmd.formvalue("set_date", "year", str(date["year"])) + self.cmd.formvalue("set_date", "month", str(date["month"])) + self.cmd.formvalue("set_date", "day", str(date["day"])) + self.cmd.formvalue("set_date", "hour", str(date["hour"])) + self.cmd.formvalue("set_date", "minute", str(date["minute"])) + self.cmd.submit() + diff --git a/plugins/disks/disks.cs b/plugins/disks/disks.cs new file mode 100644 index 0000000..8857774 --- /dev/null +++ b/plugins/disks/disks.cs @@ -0,0 +1,17 @@ + + +

+ + + + + + + + + + + + + + diff --git a/plugins/disks/disks.py b/plugins/disks/disks.py new file mode 100644 index 0000000..5824a72 --- /dev/null +++ b/plugins/disks/disks.py @@ -0,0 +1,17 @@ +import CryptoBoxPlugin + +class disks(CryptoBoxPlugin.CryptoBoxPlugin): + + pluginCapabilities = [ "menu" ] + requestAuth = False + rank = 10 + + def doAction(self): + self.cbox.reReadContainerList() + return "disks" + + + def getStatus(self): + return "TODO" + + diff --git a/plugins/disks/lang/de.hdf b/plugins/disks/lang/de.hdf new file mode 100644 index 0000000..c2ef355 --- /dev/null +++ b/plugins/disks/lang/de.hdf @@ -0,0 +1,6 @@ +Name = Datenträger-Übersicht +Link = Datenträger + +Title.Disks = Verfügbare Datenträger + +Text.NoDisksAvailable = Es sind keine Datenträger verfügbar! diff --git a/plugins/disks/lang/en.hdf b/plugins/disks/lang/en.hdf new file mode 100644 index 0000000..f30795a --- /dev/null +++ b/plugins/disks/lang/en.hdf @@ -0,0 +1,6 @@ +Name = Disk overview +Link = Disks + +Title.Disks = Available disks + +Text.NoDisksAvailable = No available disks were found! diff --git a/plugins/disks/plugin_icon.png b/plugins/disks/plugin_icon.png new file mode 100644 index 0000000000000000000000000000000000000000..ae6bd7479dc4decc3b4ae3ab20d3988f799c5dad GIT binary patch literal 6279 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 literal 0 HcmV?d00001 diff --git a/plugins/format_fs/unittests.py b/plugins/format_fs/unittests.py new file mode 100644 index 0000000..4310865 --- /dev/null +++ b/plugins/format_fs/unittests.py @@ -0,0 +1,10 @@ +import WebInterfaceTestClass + +class unittests(WebInterfaceTestClass.WebInterfaceTestClass): + + def test_read_form(self): + url = self.URL + "format_fs?weblang=en&device=%2Fdev%2Floop1" + self.register_auth(url) + self.cmd.go(url) + self.cmd.find('Initializing filesystem') + diff --git a/plugins/format_fs/volume_format.cs b/plugins/format_fs/volume_format.cs new file mode 100644 index 0000000..ba9ed14 --- /dev/null +++ b/plugins/format_fs/volume_format.cs @@ -0,0 +1,37 @@ + + + + +

+ + + + + + + + + + +

+ +

+ +

+ + + + + + + + + diff --git a/plugins/format_fs/volume_format_luks.cs b/plugins/format_fs/volume_format_luks.cs new file mode 100644 index 0000000..e2c52bb --- /dev/null +++ b/plugins/format_fs/volume_format_luks.cs @@ -0,0 +1,32 @@ + + + + +

+ + + + + + + + + + +

:

+

:

+ +

+

+ + + + + + + + + + diff --git a/plugins/help/doc.cs b/plugins/help/doc.cs new file mode 100644 index 0000000..57bfdad --- /dev/null +++ b/plugins/help/doc.cs @@ -0,0 +1,9 @@ + + +
+ + + + + +
diff --git a/plugins/help/help.py b/plugins/help/help.py new file mode 100644 index 0000000..b9c9be5 --- /dev/null +++ b/plugins/help/help.py @@ -0,0 +1,25 @@ +import CryptoBoxPlugin + +class help(CryptoBoxPlugin.CryptoBoxPlugin): + + pluginCapabilities = [ "menu" ] + requestAuth = False + rank = 80 + + def doAction(self, page=""): + '''prints the offline wikipage + ''' + import re + ## check for invalid characters + if page and not re.search(u'\W', page): + self.hdf[self.hdf_prefix + "Page"] = page + else: + ## display this page as default help page + self.hdf[self.hdf_prefix + "Page"] ="CryptoBoxUser" + return "doc" + + + def getStatus(self): + return "TODO" + + diff --git a/plugins/help/lang/en.hdf b/plugins/help/lang/en.hdf new file mode 100644 index 0000000..f2eb3fc --- /dev/null +++ b/plugins/help/lang/en.hdf @@ -0,0 +1,5 @@ +Name = User manual +Link = Help + +Title.Help = User manual + diff --git a/plugins/help/plugin_icon.png b/plugins/help/plugin_icon.png new file mode 100644 index 0000000000000000000000000000000000000000..c3792009e3ad4f0e92654a5e2535aada72a1e879 GIT binary patch literal 12693 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 OfWWdUGSyP1A^!t|@C#A^ literal 0 HcmV?d00001 diff --git a/plugins/help/unittests.py b/plugins/help/unittests.py new file mode 100644 index 0000000..f1e1f97 --- /dev/null +++ b/plugins/help/unittests.py @@ -0,0 +1,29 @@ +import WebInterfaceTestClass +from twill.errors import * + +class unittests(WebInterfaceTestClass.WebInterfaceTestClass): + + def test_help_pages(self): + '''help pages should be available in different languages''' + + ## check english help pages + self.cmd.go(self.URL + "help?weblang=en") + self.cmd.find("Table of Contents") + self.cmd.find("Getting started") + + self.cmd.go(self.URL + "help?weblang=de") + self.cmd.find("Table of Contents") + self.cmd.find("Wie geht es los") + + self.cmd.go(self.URL + "help?weblang=si") + self.assertRaises(TwillAssertionError, self.cmd.notfind, "Table of Contents") + #TODO: add a slovene text here, as soon as the help is translated + + self.cmd.go(self.URL + "help?weblang=fr") + self.assertRaises(TwillAssertionError, self.cmd.notfind, "Table of Contents") + #TODO: add a french text here, as soon as the help is translated + + ## test a random language - it should fall back to english + self.cmd.go(self.URL + "help?weblang=foobar") + self.assertRaises(TwillAssertionError, self.cmd.notfind, "Table of Contents") + diff --git a/plugins/language_selection/lang/en.hdf b/plugins/language_selection/lang/en.hdf new file mode 100644 index 0000000..5789cbc --- /dev/null +++ b/plugins/language_selection/lang/en.hdf @@ -0,0 +1,5 @@ +Name = Choose interface language +Link = Languages + +Title.Language = Choose an interface language + diff --git a/plugins/language_selection/language_selection.cs b/plugins/language_selection/language_selection.cs new file mode 100644 index 0000000..e3f0ac1 --- /dev/null +++ b/plugins/language_selection/language_selection.cs @@ -0,0 +1,15 @@ + + +

+ + + +
diff --git a/plugins/language_selection/language_selection.py b/plugins/language_selection/language_selection.py new file mode 100644 index 0000000..9b28764 --- /dev/null +++ b/plugins/language_selection/language_selection.py @@ -0,0 +1,16 @@ +import CryptoBoxPlugin + + +class language_selection(CryptoBoxPlugin.CryptoBoxPlugin): + + pluginCapabilities = [ "system", "menu" ] + requestAuth = False + rank = 60 + + def doAction(self): + return "language_selection" + + + def getStatus(self): + return "TODO" + diff --git a/plugins/language_selection/plugin_icon.png b/plugins/language_selection/plugin_icon.png new file mode 100644 index 0000000000000000000000000000000000000000..fdaa85296c6831e525961a24f533bfc274e94ac3 GIT binary patch literal 13094 zcmV+>Guh0EP)oGn|Ha}YL8ILHBP z{MHy2Si-XGBRzP_F1sYNV8HNLvN2$T4VG;zN6E4!OR}PsH3~Bt&1iBCJ)NuSzCWtF zr+b3JOpgNa`+V+H=&7n(b$|DqbI(2ZULmFA@A%t=a;`J*cbr@NodAF1_&Wjq#_@Lo z{Eg%91o#`r-wE(H4$HaDK)NArT&(~FKrxbHkVUb5bBm1c=K;M)*$T7)tw>ryw#N3& zbI5kNex-h{-e_|Iz+m8et^s5#lA_ zCAnc`efr`Vt)-uO8OnQj~Y zK!l$D5WW3jdiz84_J`={51Pk)=5beV07FmuMuu?^Ayxw~A=xT7-hDw!hYN}T;^tRw z1bHuT3z8D^ti)-zuy|G;7tb$b>D&UcJoeP<0EYz}D%dSxQ9u=-8xWNc(IKosNC#us z=kN!j>^|7e&V8-Cd!QYkFFf?4A$vf+f)J~b{6%iMC(H$g3xWXRmRD8)-$1fFE}Kj8 z-CR7cfQ#o9Fn4+m7FEHnLY@=sDmbm+vYPwx0=Foj#`c9U3tKlJYDjcL5H(C91T^qR zAru7z@P#3$gJDRH)b+4)Un@KJw$jivKtfj-Mia=dA(SWNrh6|Ksc>EsK-~K363UmI_1JWgFhpLt`c9YczLLnzP4&G(+a zA2){ck^th?SC;}`2X01!H^a(3H%#ZIE6TB3RkH2iu!2p29H*IV>d5`) z&O7WDvNDI%gf9@~m5ue}W;rR!bup_l3#(;h3wbaMO+IKFG$jd`l0q{i@9b>m56>T@ zxoyA{3Q|9SP`@lU-}_SfzD)XYUJ*dt_S$^lzmW1SfJ~Q_JFlC{ZC6*~u&Ly`%vxFC zhN28p#K$2eVCV=TKv9T9b?Te?Qwm>E1SJI-crxq^hi*vJB(Wio2qXpyBKD0W0)+Xj zo**3Uf%ZYjb(v}-s!P^yuIG<0RMXWHh}~G*fuepx2sLP;HNkb16ZKYJfy23c3ONj&u@=Xo+WJdmPND z$i_aJs2BxO0@45}6VCy&atjos5C~xsVP6My2O-}DK@B2No!8gb@#oct=pP7~7aGwQ z5!MIgmiv4Y{9I|pIVXU4-|JZ*e~;ugMG@S2Z6zzOpGJnuN|qfo1G4N;nI*9+5>3}p zgo4#-7NnFA4o8vFz@n;HZ5H+%?xen@9{{Jr%7Phrl;oQ>L;pY!X-G0M99V{lc`0SW z7a#!)gH)%%h}k>U-PIdJ zQ3N?&$B5_l4upwD4f1jv2nm{|p=)}In4iE~p;}Z1HI2tjt1dxS_I3(}B8H4QDInO>rFLzqO^-)&`YyJ@pjctMl!!mEe*-81_z3<-L0 z1IU$f$HP67kc-pH>e(cKxMNKrl0SjG+M){n@s2s%bomrq7U&FsT``#N38F_LXIvvK zRu!v7MO73OMW8AQLRC;zkvJzML?RKQ;ppVN;4lcG(5I=aIbkDez|0&#!2arXzWb9c z4EVxeM4HfocgS5I+jUklH9U`JivZ$|HF>}mBvsj&R=)VqVrExl4w zIXg=P5O=Jx1M7e*$_q32+Q*iX>#?Kj&=mx0sE_$s5hh0-5236yhfpAduAhBRttR0I zTcZ@^=aA^8pDqEJ}q#qSFe3Z0ui8w0_o zU+8Ax%qiq%SXodAk%-P$zx^ghYP&%jtf6<;GFkUOeP=6AXM+G=N6LS*TUEaOsY{q% z>Y*n90})9_YX>)$8>T^YhA7DQ;C9+LexeHiyWK+7)Pj><6AXkI^qYQwa|J^v-2Fr| zm(MR}T44r@3ZdT@;)`EfOM9mu!rhx`dG0#dvF@xGDw9e8ap&5HfF~3rUwL>57thYe z7lAgP!OrR?KDx+)uAjNi7eX*+b`e&KN+hZ?Fc8FMRq=S7C%w);7{b@@ALrYo4v#k?t8GbfKZ`Owlfz!$%^7Qa6Nq1LC_|G5X`S5T%X7%j%S`F?nJ83M!Q*w))zv@on@tdl3l(miBMGkb= z=ITNQeW4K-WM{dUQkqS1fd@r3W&LEe(sW%C4xgUwaVj`n4jgtXnO+wbi$cTk_Hngn z0A}S_SYF{`>#8X}tbstxCZSUlc;TE~V^FOXic1HUfy1Z_EKc zrU>}Ztuv7l_IJQ58{WmE1SxeuR2%w0CBI_>!VVfy+82}dU8UHr<;Q@Q=IHg@dXO;J%HQ#@Agzh@zDZ>c9p z{vygB{y6Zh2|Z*Kq*DTX7qG0jq=bs14BC89*Vs<=p&AqcgZdQIsQHnyyiOJ^C_@o~ zvt^49_<}Sxb)}rMs48U@`DA1absJ}TTr@Rx5e%3r*J8D#^cs3RF5DRof}tqAeSSir z=!9DoQc89;^??>9I2dNno;^&@yM#;29Nc@ye17-z9>~7zlfs_&ve(8@J0vTSq4VkNnfk+_}8+toF$Dbo*#(>Ppek zilUfF6(~X=6oH~BsIh(jV2BwrOK~`@DfbG6q73?ifEhT})R-z&&IF1gcFHG2fd*4r+Z zop4k`wW?TD1ud%6(>F+9Fidy%z=ZQAEDDc2w~al=+DHApv8I~Fj%9Qgdb#}4a^}p+ z=g^VPObTxLj1XdXx|)UQAixI!R$fzy&7!jT1nk?p3tw+1ci(qEPM3@7g9q@q?c8zO zjUWUa0eGVxmJ~tB_&mC&L|4}UjmJCD#@Y2dFc4(mU?Tu_hYeL#2>3^~V$K|rw!t7@ z{q0&J+Ss;(1P<@rL1Er|xM`ZH3Jx9V1Xty9U;(f*4Vjr30mR+wyukGU%jXvm(V=}H z!miEhDJw0dsI-io+qPm6l3Q-QiOft7hBQa*M0D8M3MD?6lb0IfKM_PC8prF}>Fr5h z^tG0zg4c!Mck6btY2WGDakcw)u=LU^2xv~0E-t}lQ;Fc2ig(Gfm=u_BV8cWuz-@qS zR=JlPkDZ1-IDTY5kw8CFtFB@DrnlI=Z6jB&xQQv{6&MnPG~4+CbH8oS3<@dt!c_0* zBYaL5ogD)luWLio&VIm$K%iI@^byaNL@;=A%{I1GpP2!#>0xRQ?xJPN70k%BaM7Y- zw(e+z+-t9(fAG9CK<{-P@G|gRi7>*5sliK*0kv6i6FyiMc0iO0VN?_nG}i3SD|1+7S!HfIq+VN?PnZxc>B!&D2sA+x2B?J#5O-PWN& znQ=s;8lm9XiSj)eq+)QqwUzJx;aLhQs;I81P0M`^9h#3HqN8*%Q@mCd%`f8J-7SLr z6<07={SweRk^4`C0H*k#pAW#{PJ{aWTMVi_^|_iR82=nD+8_h>C|Zr?*FJk%=Ftxo1HT*{&SyVLTl z#-j&mSg@Ef4_tm}Iq&Xn0e96Uz!c5}0mR+wte6HM%je}2)#1pIBZT~e2%$3LqAO8U zv+xaRek>u)0@f9h=HH2VSPh1Jl1Wkk0Wx-88X$!^Uf&;uPGJ(lh(z&4LS(t!=$f1q z15mXn2nD+P2dJ*AXX~D7dVHy^3?YV$mX^jwmRx!@tp0utURxs>7^dp1|h$sf~>+)`a4^hzT~Pz&DZ5n%}$mC z@fxqk3RsLrhWK%e2uZv_66-ZoT|B#CD>b#Ig`Jn>WlC`YrFpsJdOf(DP8@a{cB>Ve z)nX13iHR8rNW<)wjp|WC;V8jKgn_|9I(z$Q?dhSZt&@J=NJo-TPVSN2@y-Trd*ET7 z{`n6l+*c479H6_sh2H!U@-pnqnU&AMLmdh^S1iR}`zp{e-g}Ri02KfgMHv9}2EpNS zaozhr#jB71H+wg{#N6d8C@P*gNO%7rdq`iONpkHzU39g#GuZFrvK6=S&KoaJI1ddqhnQbgLY@nX3Ntu( zr~~Xpr9c@IAi#J#7K#CKVv{lXBW7U)2D*ucgLE_;W&JZhK7O=nvRx@7cZ z#OCT5z8^c*409VlPHbbpX$DyAc0Tjzk5N@M4WTIKP1^F-Yb?C%8nW^VM*L=VI1!3M z#r$O~UUAnc?^So?ApV${k(2E*cWgN&#HYkN%LFI^$c+)e7XhTfyKA3E2sM#H{y`4! z*vKnS{E!!Z`6zofze#^@kEzebD)^+rjT1pjDtyf_kF{Y0vDh4Z>XRR3+O(-Dc{rD; zr?Zoz`}cC`irZ7p=MC8psmrWBsnDxd@)$7=^^j)AY<_7?r!9b@Xofu2rwZ(PIfjcX_D+M9A3rvP*~~Ozvwm4@4nFaTf8?h>{WbLs6K{j313TVc!`&bI0u{3s&{$i| zw1vx?byK)->tN=3cs(9zV4ykMnw@Q0&;R<5*tdE8Y3s_)mL@t|n%KJT6=u#~!s5%XBrh+2M4=~>AvwkS6OSqwD1#fNOaM~_cu0KKdb^dh!{1 zdU`Po11TjAyPd4;EV5%MCnuYuM{C)(EiD~uW1{I;YE-vS6jZB)-RoZ9x_duN(CUMM4-P9MZmPVOTdu4_rCvy&0;}S%=Zch{1gqG%*fuH9 zX?Rycatu`Y)TwF9`{`iLWh?|i;-2t$AT)?JW# z%G9asc{e>BH=|+Z#aA-(;-SJ<1T4Agy2QlQ^)*M(bseE7Xpu0Ttu5w+l!}7W>4-J` zyy%e-cGawbJ-udND%xO(ky`GwF&+XWqAq#{!W3mYQ4}?85+E6!Ev>xx``@wRu6rmb zE=rIfUh@q`*Z(kN_=!1t5Sl1DpMc-b;w1}kyX+~=-`LPe zy5kd31BBbpE&*aV1}yCv2%}5L%*>dSrj$?=DrU`P_QlK4WUAXYt_qUX07-s;`2ML7 zR|6T|thByE!IVneZuh7KuPV?`Q$u52J-6L^7fz?$40b6mK@lY>Dk32{V#m6K?%w!J z4Men;L_Vx;`VsW7dKMfzA4V1q%Dk2faHBSd|!gw zla0MrLtFvBXxJ?G7M32+EZ z8vvjvXGj7_J&I5#9^^m!vTLwqc+usM11Mqqjph7dZ1Eo_z;LY)CxF%IPMZXnGIKUX zWo0Rb-f#&}TvklY!9xu6^e|`M%#^}M*XV3MN_Igh8JXS$5!^QOMS9{YAllaggkx!3 zn1}?RCq{s2W!c#6b|R4|k?vZ84V!RHy&7BooN?XU=5SI}QE9q^r77l(HDY|g}A1_2iNrL&gkQoOs~RfQ4(=*QVzun#GO1TY5<1xzaYgC z6#FR!qe=nAGHwZAwcDv$xQMB9W}}6IW7hn5Dk>?UtD}>~hGwEsovj<+q5a)e9D4C5 zNZpj?H$3!Z?)daKF?5}V6KzyYEy803zXmnOVsVE4+6K(pKW+kyi}G^SiaMk`5Q^yR zsp&zNP+3u&aCv$c-H#=+7=5rE-`=O_dFvZ!11+cRGA*yFO04n~U+F8pA79la5ganS zX?TXN;6zlB(={<}7#x6?fmm)R{9lymKdmeSJgCy=0x9l-w={`T02rp%CqQ^@N=#QSEt-ad}fCXyny6NolgAwVcb4@J}oX9}Y@yk3R zRsn1|(19Ug$*kf;^on9}l6~nXAy%WJz4dgh`ws>Wtu|}tNg`h|z1igC=O&hVOCdti ze*9R()Wn$m1aj>5e{!<9Jrq~XI%(cUOG~c0l8(kk-hBQqu?Z*2Qu3Jm9?47B2?2vg zUZs2ex6lJ-Pp+`K$i4bYM6#Fi&em%7AE+l9l`NiDNrugwTKAppu~9~$=68|0223dI zcp^$~6~OlDPITR1Ze=!hhav7(E?8Er>KVwF_0+!4Y@^AP$j-vS~&zMr0Pqx=hcC5p9>-JcXOaGo7z#uRo=f$`O zhR9Vb_KTHo)D8xqV-!mWD=cT^|NMY?O~So39aF8d4_ zWlLDNbUsC8#dNlJ;dHwwC@C5;_+oAUcvo@bqh(AY;yn-7erpZ3*V02e?)=*ks+H^pWV%ucJQ2UBYaHJA99y4#j1vdm zNx6q&_mFexCvcWsJmzy2%r+O)7>vM{cR>dR;dGMIVWn(Z`+4=ORBQD;?h~v)TK^);woN9 z&cz?5XUDHb+@s_8etz?*%UHJZpILOvN64Q#KOy>)Re?;B0>j0AqD?PPzxKmO6%aq3 zK6^S%$4-oS-Mqzf$j{H>#@ntzRYaoZM}t0U-+hCl@4U!?bFtK~(eBlzdJ!r5dN+Ik%(OGU?g=bBt&pHYHWS3Sk>&iQ+SaLm1 zPaZb67n?f^i`_*yI7rCXN7&y_Xt0lvzn}1+c^n?>LytzVxja~19&Bz8R;LG>Yh{RfS~>r{J}jYZY(Y5>u_A_C4tS2+%F19LcaVL^@^(y9jCQ zv{)4Jlh0jFdA^g)2Rr%0^E*cU{~2XA>eqgcBimk|&~qmQpKSC&ps_p5C;s(C{DCO6{^fhL{Am-gQ%c!9;ZKo{ z>EgwmuReofz3;O51$^njg}ANoz2Cn}ZGCE_vZ4q+^5E6D+zvXM>)EyDX?CrBn%3G= zTiq8Ta!aRk$vt19J}{O0?^#YkUdE^{Vnsk*eJ8(r&Qu8%rCEI9{!1vxQ0NW8FP_-R zD{GEIq`R8^pZPY>h)IA6uYQpp0<3&v3euF|X(-ZMDicN-7# z%V%-bJ)dOB%@6b2r?;}=s@W`AFlEfbSA;oAdH<0%1aQ%uLL4>~n+i22`uWV4Uct}| zNy9JyjqVMHfFn}M>Qm+8bda_MBg7pqKZ0WaQdOCU?|%9cybi(YEzP{NZm2--xpg7a zD)UkZklgf>z=^}VIq>#hIJ*4}8mqTqoMd5%Z{N0+>ucJ0d|MqSPLo8Xvo2!cwRf@jx|I}9n`;_{ zQH}q3{B7>MX#q2*=a1e&G)9L}juSfl@Qv@SXXow~@Yk>B(Et54a2(h#rA*6&Na-WM z$~Q95wc{w3ywARW9@j1@MpfYd{c#V+o6JS9%8I=_xN^x*!81Au;>SV1kJ@+Fb9BcW z)NEf%N5i4S^X^_)%Ht1Sb=vvcT-U)D|FVk%t*Hxg*)0k$fBbqbsmL2~O(?4K!}W*w z_9!!X#_`^N{2I${eTe+h%AqJ_hPtntT6!tSb(7(;om2uO6T+^V<1OFa*T#Q*`%REq zh(rJNc>>2y0EeZNHK+aJr$E{wAmnrJ83g%dfZxAXLs-*s+u+U{XJNNn5sFH4`ye&P zyGCuNtD~HAXJoSQn)h+{=N{t=e>^~LSykeBD=NI433;!WR=^7%y_OL8k*y?^=B=7kLn41{53k(2vwo`Vu|Zmi$h0MbbLV8cJ! zXwIJ69c$@s8k%_Nn#wF>=*(=O{0s*_fB*8t@$K`=Sh=YDl-GUk`ni1IqRLT!(?Y&6 z3xBi@t>pI`;q(%aX*K6VdSk;e8XEgR4|Q?;r>|mm`m{+Y(=lsh0%;|HeEvNKiux&l z=iWL_bEls`6qe1-;g)NrArzH9UzFVk+edtallJ;4(0*)x;yINbE5$Y)!v520h9%?5 zX$7dUKIK`3p3}bW$M;;q@`{mjF=!$Gm|n->5>6eZ;FHfeEif20L%yE+^FH$d-J704 z^96uT(l9UJaHLlP0P=I~c)`u;P+pE?ZQLA`~{g+d?F)jqbi3 z!$sTEGStbG=TwnFbCM=G*vsk6ktC%=Mnb4sh$&ukSk<)L)1Ro=s=#+|s2XwJ?y!$q ztCOX{Xf=I!vMOeGcHf}JHy+(U|3KJm_MQ06R?I12nx&NKS?(kWX(s>x7UzRR2amM% z1o_qzd+>#HY%1J&T{*L-C&4s$i@@7rP35 zLCK>(*-Tx1FKB@_YQFPh%yIvnOkf_MlVMT_AfLY>Kws@mXra!-4gEa!^Z|TfiPtW8 z;O42UxU_^T78Q&s`c-A5NYx@ks?TLHvn>PtNG$@oh8*ZZh66}LN9r0f?BjsM1-mEd-;Z&+1ew<;2 z&H()S$zAN&-2z5*kmEmplwea2&*9a)K?1vl?&=l8GH&>MoH zOqCm#7n7T18&&j=)q|Hp0P9e;48rQykMQ!^8jyxY^Qy<_ z+tG|^@i#~*CvB0#Nh1J&-1PZP^w)kADUIjes%P!?COQL<;~2fr$5aJkxCBV*#W8dY zUl;NuoiJu*bYvue3=S|5j8N{eG3v14u`Uk|KqScYEE|O$bBY$#V#S@2F{bf4ny61E zg~w*ji@beTGmk$NYw~q%_!Vt09s-(x7AfWED@vpZlS%*px#6Fm!dL$dfS;~9#Ga$w zwEH33e)2+3e5}}hG(1uTB%no29Ut<=h#--A6d4X+`1>&W+A(_DOj{x7qbm@_E~I%b zG?)m>8a4lV6l0(xVRQm0G2(c#^G;g($C3sX1u|{W?1S3lJv{n9@0e<7;J^#i|8g_t z^sjBei8K|%n3#-}Lq|Zq_3{4@?|GuiaF;yr$Zz-XxeqSjlDP%syC9?wO%snEkk86&JV@(5m_JM_XT^0)5=7f`DMg1g??ihdZgFiOMie&=J zmKm|7O}#mJDH32TNuUUDSj^6yUcb)Ie!G)5H`bds^VPh@;cq@23z}@kEc~;!zWrGv z0Dv5*j|d??!J>aXMA`LU-F~ps{rMkm;VTa>AzGNhluQ`ZMkj#HHq`#sCa|`wCFTWG zC%~eq)FX7R|24;d`3|O=w;ebpr3{Su z;7Pz)B>;ewGAM-jKE8%SRNepMmQKn)|JW8jd;daMP)MQM{AdxKMD#BLthS+$>FB8) ze`6wUKE#P|rsxmrLlZUS#v}kE3msx(6oBJ>u>Vj8-~GuJ27DpVgB{fW_Axrw*JB!d zU7Ul$hjTyx04Zfu2(g;p!*wkB+|NRkT=B?nc609y)A_)S)3I651!h`s&#cdaLY`VFDS&WfWtmC=RkA< z1PD%)07(*{ptvlt@3cVspt+jK-s*OqTy=n&x}I1YHqb%Gx+iIPayO>9ZwKn7lxduk zW_&p31OSjy`h^fbrTQ0dv*0riQF!?WPP9?<$m6@HD)aK-Z8M09p+JtvC`-VfE;_K6>}O$gy9nLmLQS7J6T7+jO?t_@{&Oh5(ST3tEH_9qjo| zE%uz}nD?ps$y>5=ARxW*|C@*kn6M2HHk%RNyi%TCeV844TBxn>jRiAm0s0TTO6|{I zBhux^Ebv~;W?oCW7JN=8&MN}M5wnOJg%IuR`g#q{qGxgDKMKr-2@)Wt0>f!5+IJ4N3325(MquGkHCLI zdW^t4=b68{aCvyQHKvNJo)Es8x9Q!ri{{mb(L)hTG2b2AHm76Y#$@BXCO{lg%CHb( zTl{z;j-a>-C~%(Pd_XoQwS7@~gE1qpbK{G2Zd`>qNOKSfV9ul5BBe~<(&dwk3xWVK zB!U7bn)=YgXWS+(!=F5CY-M1dEkzCwk&%8>N(o zFZ9BP3y1(Q)B$t^#@~#NtAHHG=@CF=WFr&{{nEyfj)X5_>$o#;p5cNdzzJWJ@Hjns zaTRb{GQ{CVSe;<8va>m5Eau+{aE?$1X5rQKkI&wUtAJCg5sMAQW%{0kBZIA}a~*6M z*Tc_4TxbMHEbMc%cYHxJ!I;o zorSQN;b+3>L8umfy`?t!U;Z?%2cM_7pa>AdD=>Zn#8rScN(_d`%t6>=i&r@%46ycuA7A9m)Idm+TwUfuWMrdc<|V9sh!wtVXleFezLPQ+)1S_lFs^js zLM6av0lttf`Q4$uQ~!4o99ETY-8>&*a}Avo!gTp275-oLH?p~|Gx^ddCY0mz6c;1` zq?Az!j|2Q6oRw}5}Yu8P9Uq?y7L6_J^nVgvL9!hG)eoyk|fD5XrD?)$F< z7bF2*KK6P2(>kJ7MW3V;~geUsk1v{zl;2 zlaTN81{X8|q?Ct%Ujsb4tB0PD87Cmzo)qgIy9?pTLvd$~x-L%~|NinGn)_46XMXNn z4A?xIxS$CT!xt4nS7%V?{#Tm`=t`}(J553e=S9WeU|#Ay^fz^=v~KKjR$7!N=2$w|oWd55&k7JjZFgjfyS z29W2n^1b)YuM;Lj@&<%s%Y&*u6U))V3wJG_al=7*`&GGqxza|1?0)GLni=V5= zcJsi+Q@MU-5fwRZ3NjpY`NK5!23UKfh2L(g8?N&Kejug%-)A%L=MsN41OO01*fE=i zcaQ%2NmmjJ1OF_g{N?22?)=0BZv!MlN*R_?-UHkW>>T;~sF!KLv%vDdwZezLG6^so zLWnDX6~OgC72t@aCSW&ajNu=olxd%a{X)V22b8nY#Vr5F*Z=?k07*qoM6N<$g80xt AiU0rr literal 0 HcmV?d00001 diff --git a/plugins/language_selection/unittests.py b/plugins/language_selection/unittests.py new file mode 100644 index 0000000..6e9b349 --- /dev/null +++ b/plugins/language_selection/unittests.py @@ -0,0 +1,10 @@ +import WebInterfaceTestClass + +class unittests(WebInterfaceTestClass.WebInterfaceTestClass): + + def test_read_form(self): + url = self.URL + "language_selection?weblang=en" + self.register_auth(url) + self.cmd.go(url) + self.cmd.find('hoose an interface language') + diff --git a/plugins/logs/lang/en.hdf b/plugins/logs/lang/en.hdf new file mode 100644 index 0000000..c81fbc4 --- /dev/null +++ b/plugins/logs/lang/en.hdf @@ -0,0 +1,6 @@ +Name = Show the content of the log file +Link = Show log file + +Title.Log = CryptoBox logfiles + +Text.EmptyLog = The logfile of the CryptoBox is empty. diff --git a/plugins/logs/logs.css b/plugins/logs/logs.css new file mode 100644 index 0000000..41335ed --- /dev/null +++ b/plugins/logs/logs.css @@ -0,0 +1,6 @@ +#log p.console { + margin-left: 10%; + margin-right: 10%; + font-family: monospace; + text-align: left; +} diff --git a/plugins/logs/logs.py b/plugins/logs/logs.py new file mode 100644 index 0000000..683056a --- /dev/null +++ b/plugins/logs/logs.py @@ -0,0 +1,29 @@ +import CryptoBoxPlugin +import os + +class logs(CryptoBoxPlugin.CryptoBoxPlugin): + + pluginCapabilities = [ "system" ] + requestAuth = False + rank = 90 + + def doAction(self): + self.__prepareFormData() + return "show_log" + + + def getStatus(self): + return "%s:%s:%s" % ( + self.cbox.prefs["Log"]["Level"], + self.cbox.prefs["Log"]["Destination"], + self.cbox.prefs["Log"]["Details"]) + + + def __prepareFormData(self): + self.hdf[self.hdf_prefix + "Content"] = self.__getLogContent() + self.hdf[self.hdf_prefix + "StyleSheetFile"] = os.path.abspath(os.path.join(self.pluginDir, "logs.css")) + + + def __getLogContent(self, lines=30, maxSize=2000): + return "
".join(self.cbox.getLogData(lines, maxSize)) + diff --git a/plugins/logs/plugin_icon.png b/plugins/logs/plugin_icon.png new file mode 100644 index 0000000000000000000000000000000000000000..1fbb4f0220b857306284222d7d80b060c170b3ce GIT binary patch literal 16601 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 literal 0 HcmV?d00001 diff --git a/plugins/logs/show_log.cs b/plugins/logs/show_log.cs new file mode 100644 index 0000000..623b4b7 --- /dev/null +++ b/plugins/logs/show_log.cs @@ -0,0 +1,19 @@ + + + + +
+ +

+ + + + +

+ +

+ + +
diff --git a/plugins/logs/unittests.py b/plugins/logs/unittests.py new file mode 100644 index 0000000..25b7727 --- /dev/null +++ b/plugins/logs/unittests.py @@ -0,0 +1,21 @@ +import WebInterfaceTestClass + +class unittests(WebInterfaceTestClass.WebInterfaceTestClass): + + def test_read_logs(self): + log_url = self.URL + "logs" + self.register_auth(log_url) + self.cmd.go(log_url) + self.cmd.find('class="console"') + + def test_write_logs(self): + ## we have to assume two things: + ## 1) log level is at least "error" + ## 2) the log message does not get lost in a possible stream of log messages + log_text = "unittest - just a marker - please ignore" + self.cbox.log.error(log_text) + log_url = self.URL + "logs" + self.register_auth(log_url) + self.cmd.go(log_url) + self.cmd.find(log_text) + diff --git a/plugins/network/form_network.cs b/plugins/network/form_network.cs new file mode 100644 index 0000000..509e2d5 --- /dev/null +++ b/plugins/network/form_network.cs @@ -0,0 +1,30 @@ + + + + +

+ + + + + +

+ ...

+ + + + + + + + + diff --git a/plugins/network/lang/en.hdf b/plugins/network/lang/en.hdf new file mode 100644 index 0000000..0828755 --- /dev/null +++ b/plugins/network/lang/en.hdf @@ -0,0 +1,23 @@ +Name = Configure network +Link = Configure network + +Title.Network = Network settings + +Button.Network = Update network settings + +Text.IP = Network address + + +WarningMessage { + InvalidIP { + Title = Invalid value + Text = An invalid network address (IP) was supplied. Please try again. + } +} + +SuccessMessage { + IPChanged { + Title = Network address changed + Text = The network address has been changed. In a few seconds you will get redirected to the new address. + } +} diff --git a/plugins/network/network.py b/plugins/network/network.py new file mode 100644 index 0000000..5d6d0d2 --- /dev/null +++ b/plugins/network/network.py @@ -0,0 +1,126 @@ +import subprocess +import os +import CryptoBoxPlugin + + +## specify (in seconds), how long we should wait before redirecting and ip change +REDIRECT_DELAY=20 +CHANGE_IP_DELAY=1 + +class network(CryptoBoxPlugin.CryptoBoxPlugin): + + pluginCapabilities = [ "system" ] + requestAuth = True + rank = 30 + + def doAction(self, store=None, redirected="", ip1="", ip2="", ip3="", ip4=""): + ## if we were redirected, then we should display the default page + self.cbox.log.debug("executing network plugin") + if redirected == "1": + self.cbox.log.debug("network plugin: redirected") + return None + ## request for IP change? + if store: + self.cbox.log.debug("network plugin: changing IP") + try: + for ip_in in (ip1, ip2, ip3, ip4): + if (int(ip_in) < 0) or (int(ip_in) > 255): + self.cbox.log.info("invalid IP supplied: %s" % str((ip1,ip2,ip3,ip4))) + raise ValueError + ip = "%d.%d.%d.%d" % (int(ip1), int(ip2), int(ip3), int(ip4)) + except ValueError: + self.hdf["Data.Warning"] = "Plugins.network.InvalidIP" + self.__prepareFormData() + return "form_network" + if self.__setIP(ip): + self.cbox.log.info("the IP was successfully changed: %s" % ip) + self.hdf["Data.Success"] = "Plugins.network.IPChanged" + self.hdf["Data.Redirect.URL"] = self.__getRedirectDestination(ip) + self.hdf["Data.Redirect.Delay"] = REDIRECT_DELAY + return None + else: + self.cbox.log.warn("failed to change IP address to: %s" % ip) + self.hdf["Data.Warning"] = "Plugins.network.InvalidIP" + self.__prepareFormData() + return "form_network" + else: + self.cbox.log.debug("network plugin: show form") + ## just show the form + self.__prepareFormData() + return "form_network" + + + def getStatus(self): + return "%d.%d.%d.%d" % self.__getCurrentIP() + + + def __getRedirectDestination(self, ip): + import cherrypy + req = cherrypy.request + base_parts = req.base.split(":") + dest = "%s:%s" % (base_parts[0], ip) + if len(base_parts) == 3: + dest += ":%s" % base_parts[2] + return dest + + + def __prepareFormData(self): + (oc1, oc2, oc3, oc4) = self.__getCurrentIP() + self.hdf[self.hdf_prefix + "ip.oc1"] = oc1 + self.hdf[self.hdf_prefix + "ip.oc2"] = oc2 + self.hdf[self.hdf_prefix + "ip.oc3"] = oc3 + self.hdf[self.hdf_prefix + "ip.oc4"] = oc4 + + + def __getCurrentIP(self): + import re + import imp + ## load some values from the root_action.py script + root_action_plug = imp.load_source("root_action", os.path.join(self.pluginDir, "root_action.py")) + ## get the current IP of the network interface + proc = subprocess.Popen( + shell = False, + stdout = subprocess.PIPE, + args = [ + root_action_plug.IFCONFIG_BIN, + root_action_plug.IFACE]) + (stdout, stderr) = proc.communicate() + if proc.returncode != 0: return (0,0,0,0) + ## this regex matches the four numbers of the IP + match = re.search(u'inet [\w]+:(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})\s', stdout) + if match: + ## use the previously matched numbers + return tuple([int(e) for e in match.groups()]) + else: + return (0,0,0,0) + + + def __setIP(self, ip): + import threading + ## call the root_action script after some seconds - so we can deliver the page before + def delayedIPchange(): + import time + time.sleep(CHANGE_IP_DELAY) + proc = subprocess.Popen( + shell = False, + stderr = subprocess.PIPE, + args = [ + self.cbox.prefs["Programs"]["super"], + self.cbox.prefs["Programs"]["CryptoBoxRootActions"], + "plugin", + os.path.join(self.pluginDir, "root_action.py"), + ip]) + proc.wait() + if proc.returncode != 0: + self.cbox.log.warn("failed to change IP address: %s" % ip) + self.cbox.log.warn("error output: %s" % str(proc.stderr.read())) + return + thread = threading.Thread() + thread.run = delayedIPchange + thread.setDaemon(True) + thread.start() + # TODO: how could we guess, if it failed? + return True + + + diff --git a/plugins/network/plugin_icon.png b/plugins/network/plugin_icon.png new file mode 100644 index 0000000000000000000000000000000000000000..0a1ee2d888f74a869a51f40e666f8f9aa827cb58 GIT binary patch literal 13698 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 literal 0 HcmV?d00001 diff --git a/plugins/network/root_action.py b/plugins/network/root_action.py new file mode 100755 index 0000000..d0ec3b1 --- /dev/null +++ b/plugins/network/root_action.py @@ -0,0 +1,42 @@ +#!/usr/bin/env python2.4 + +#TODO: add netmask and gateway + +## necessary: otherwise CryptoBoxRootActions.py will refuse to execute this script +PLUGIN_TYPE = "cryptobox" + +IFCONFIG_BIN = "/sbin/ifconfig" +#TODO: put IFACE in config +IFACE = "eth0" + +import subprocess +import re +import sys +import os + + +if __name__ == "__main__": + args = sys.argv[1:] + + self_bin =sys.argv[0] + + if len(args) > 1: + sys.stderr.write("%s: too many arguments (%s)\n" % (self_bin, args)) + sys.exit(1) + + if len(args) == 0: + sys.stderr.write("%s: no argument supplied\n" % self_bin) + sys.exit(1) + + match = re.search(u'^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})$', args[0]) + ## did we match? If yes, then: are there wrong values inside? + if not match or [e for e in match.groups() if int(e) > 255]: + sys.stderr.write("%s: illegal argument (%s)\n" % (self_bin, args[0])) + sys.exit(1) + + proc = subprocess.Popen( + shell = False, + args = [IFCONFIG_BIN, IFACE, args[0]]) + proc.wait() + sys.exit(proc.returncode) + diff --git a/plugins/network/unittests.py b/plugins/network/unittests.py new file mode 100644 index 0000000..0a04f59 --- /dev/null +++ b/plugins/network/unittests.py @@ -0,0 +1,45 @@ +import WebInterfaceTestClass +from network import CHANGE_IP_DELAY + + +class unittests(WebInterfaceTestClass.WebInterfaceTestClass): + + def test_ip_change(self): + '''change of network address''' + ## the time module is necessary for the CHANGE_IP_DELAY + import time + self.register_auth(self.URL + "network") + self.cmd.go(self.URL + "network") + ## extract the current IP from the network plugin output + def getCurrentIP(): + self.cmd.go(self.URL + "network") + self.cmd.show() + self.cmd.find(u'Data.Status.Plugins.network=([0-9\.]*)$', "m") + return self.locals["__match__"] + origIPtext = getCurrentIP() + origIPocts = origIPtext.split(".") + ## check, if the original IP is valid (contains four octets) + self.assertEquals(4, len(origIPocts)) + self.cmd.echo(origIPocts) + wrongIP = "192.168.123.321" + def setIP((ip1, ip2, ip3, ip4)): + self.cmd.go(self.URL + "network") + self.cmd.formvalue("set_ip", "ip1", str(ip1)) + self.cmd.formvalue("set_ip", "ip2", str(ip2)) + self.cmd.formvalue("set_ip", "ip3", str(ip3)) + self.cmd.formvalue("set_ip", "ip4", str(ip4)) + self.cmd.submit() + ## sleep a little bit longer than the delay necessary for ip-change + time.sleep(CHANGE_IP_DELAY + 0.2) + setIP([1,-2,0,1]) + self.assertEquals(origIPtext, getCurrentIP()) + setIP([1,0,0,256]) + self.assertEquals(origIPtext, getCurrentIP()) + setIP([1,"foo",0,1]) + self.assertEquals(origIPtext, getCurrentIP()) + setIP([10,12,0,2]) + self.assertEquals("10.12.0.2", getCurrentIP()) + setIP(origIPocts) + self.assertEquals(origIPtext, getCurrentIP()) + + diff --git a/plugins/partition/current_partition_info.cs b/plugins/partition/current_partition_info.cs new file mode 100644 index 0000000..eaeb349 --- /dev/null +++ b/plugins/partition/current_partition_info.cs @@ -0,0 +1,11 @@ + + + + + + + + + + diff --git a/plugins/partition/lang/en.hdf b/plugins/partition/lang/en.hdf new file mode 100644 index 0000000..64bfb72 --- /dev/null +++ b/plugins/partition/lang/en.hdf @@ -0,0 +1,83 @@ +Name = Disk partitioning +Link = Partition a disk + +Title.Partition = Disk partitions + +Button { + SelectDevice = Manual partitioning + EasySetup = Automatic setup + AddPartition = Add + DelPartition = Remove + SavePartitions = Write new partition table + AbortPartitions = Cancel +} + +Text { + FS { + Type = Filesystem type + Fat = FAT (Windows) + Ext2 = Ext2 + Ext3 = Ext3 + Reiser = Reiser + } + PartNum = Id + PartType = Type + Size = Size (MB) + SelectDevice = Choose a disk for partitioning + ProgressInfo = Progress of formatting: + CreateConfigPartition = Automatically creating a configuration partition. + RemovalContainers = These disks will be destroyed, if you continue +} + +AdviceMessage { + DeviceDataIsLost { + Text = If you continue, you will destroy all data on the choosen disk. Please be VERY careful! + } +} + +SuccessMessage { + Partitioned { + Title = Partitioning complete + Text = The disk was partitioned successfully. + } + + EasySetup { + Title = Initialization completed + Text = Automatic initialization was finished successfully. + + } +} + +WarningMessage { + NoDisksAvailable { + Title = No disks found + Text = No suitable disks found - please check your configuration and hardware setup. + } + + PartitioningFailed { + Title = Partitioning failed + Text = The partitioning of the device failed for some reason - sorry! + } + + FormattingFailed { + Title = Formatting failed + Text = The formatting of the filesystems of the device failed - sorry! + } + + DiskIsBusy { + Title = This disk is busy + Text = Please deactivate all containers of this disk before partitioning. + Link.Text = Show all disks + Link.Rel = disks + } + + PartitionTooBig { + Title = Invalid size + Text = The container size you entered exceeded the available size of the disk. + } + + PartitionTooSmall { + Title = Invalid size + Text = The minimum size of a container is 10 megabytes. + } +} diff --git a/plugins/partition/partition.css b/plugins/partition/partition.css new file mode 100644 index 0000000..4c47c37 --- /dev/null +++ b/plugins/partition/partition.css @@ -0,0 +1,4 @@ +table.partition tr td{ + text-align: center + } + diff --git a/plugins/partition/partition.py b/plugins/partition/partition.py new file mode 100644 index 0000000..20b2222 --- /dev/null +++ b/plugins/partition/partition.py @@ -0,0 +1,416 @@ +import subprocess +import os +import logging +import CryptoBoxTools +import CryptoBoxPlugin + +class partition(CryptoBoxPlugin.CryptoBoxPlugin): + + pluginCapabilities = [ "system" ] + requestAuth = True + rank = 80 + + PartTypes = { + "windows" : ["0xC", "vfat"], + "linux" : ["L", "ext3"]} + + ConfigPartition = { + "size" : 5, # size of configuration partition (if necessary) in MB + "type" : "L", + "fs" : "ext2"} + + + def doAction(self, **args): + ## load default hdf values + self.__prepareDataset() + ## retrieve some values from 'args' - defaults are empty + self.device = self.__getSelectedDevice(args) + self.withConfigPartition = self.__isWithConfigPartition() + self.cbox.log.debug("partition plugin: selected device=%s" % str(self.device)) + self.deviceSize = self.__getAvailableDeviceSize(self.device) + try: + step = args["step"] + del args["step"] + except KeyError: + step = "select_device" + try: + ## this way of selecting the easy setup is necessary: see select_device.py for details + if args["easy"]: step = "easy" + except KeyError: + pass + ## no (or invalid) device was supplied + if not self.device: + step == "select_device" + if step == "add_partition": + return self.__actionAddPartition(args) + elif step == "del_partition": + return self.__actionDelPartition(args) + elif step == "finish": + return self.__actionFinish(args) + elif step == "easy": + return self.__actionEasySetup(args) + else: # for "select_device" and for invalid targets + return self.__actionSelectDevice(args) + + + def getStatus(self): + return "%s / %s / %s" % (self.device, self.deviceSize, self.withConfigPartition) + + + def __prepareDataset(self): + self.hdf[self.hdf_prefix + "StyleSheetFile"] = os.path.join(self.pluginDir, "partition.css") + + + def __getSelectedDevice(self, args): + try: + device = args["block_device"] + except KeyError: + return None + if not self.__isDeviceValid(device): + return None + if self.__isDeviceBusy(device): + self.hdf["Data.Warning"] = "Plugins.partition.DiskIsBusy" + return None + return device + + + def __isDeviceValid(self, device): + if not device: + return False + if not self.cbox.isDeviceAllowed(device): + return False + if not device in CryptoBoxTools.getParentBlockDevices(): + return False + return True + + + def __isDeviceBusy(self, device): + """check if the device (or one of its partitions) is mounted""" + # the config partition is ignored, as it will get unmounted if necessary + import re + for c in self.cbox.getContainerList(): + if re.match(device + "\d*$", c.getDevice()): + if c.isMounted(): return True + return False + + + def __actionSelectDevice(self, args): + import CryptoBoxTools + block_devices = [e + for e in CryptoBoxTools.getParentBlockDevices() + if self.cbox.isDeviceAllowed(e)] + counter = 0 + for a in block_devices: + self.hdf[self.hdf_prefix + "BlockDevices.%d.name" % counter] = a + self.hdf[self.hdf_prefix + "BlockDevices.%d.size" % counter] = CryptoBoxTools.getBlockDeviceSizeHumanly(a) + self.cbox.log.debug("found a suitable block device: %s" % a) + counter += 1 + if self.withConfigPartition: + self.hdf[self.hdf_prefix + "CreateConfigPartition"] = "1" + ## there is no disk available + if not block_devices: + self.hdf["Data.Warning"] = "Plugins.partition.NoDisksAvailable" + return "select_device" + + + def __actionAddPartition(self, args): + self.hdf[self.hdf_prefix + "Device"] = self.device + self.hdf[self.hdf_prefix + "Device.Size"] = self.deviceSize + parts = self.__getPartitionsFromArgs(args) + self.__setPartitionData(parts) + return "set_partitions" + + + def __actionDelPartition(self, args): + try: + part_num = int(args["del_num"]) + except (TypeError,KeyError): + return self.__actionAddPartition(args) + self.hdf[self.hdf_prefix + "Device"] = self.device + self.hdf[self.hdf_prefix + "Device.Size"] = self.deviceSize + parts = self.__getPartitionsFromArgs(args) + ## valid partition number to be deleted? + if part_num < len(parts): + del parts[part_num] + else: + return self.__actionAddPartition(args) + self.__setPartitionData(parts) + return "set_partitions" + + + def __actionFinish(self, args): + parts = self.__getPartitionsFromArgs(args) + if parts: + self.__setPartitionData(parts) + if CryptoBoxTools.isPartOfBlockDevice(self.device, self.cbox.prefs.getActivePartition()): + self.cbox.prefs.umountPartition() + if not self.__runFDisk(parts): + self.hdf["Data.Warning"] = "Plugins.partition.PartitioningFailed" + return self.__actionAddPartition(args) + else: + """ + tricky problem: if the device was partitioned, then a created config partition is still part of the containerlist, as the label is not checked again - very ugly!!! So we will call reReadContainerList after formatting the last partition - see below + """ + self.cbox.reReadContainerList() + def result_generator(): + counter = 0 + ## initialize the generator + formatPart_gen = self.__formatPartitions(parts) + while counter < len(parts): + ## first part: get the device name + yield formatPart_gen.next() + counter += 1 + ## second part: do the real formatting of a partition + result = formatPart_gen.next() + ## after the first partiton, we can reRead the containerList (as the possible config partition was already created) + if self.withConfigPartition and (counter == 1): + ## important: reRead the containerList - but somehow it breaks the flow (hanging process) + #self.cbox.reReadContainerList() + ## write config data + self.cbox.prefs.mountPartition() + self.cbox.prefs.write() + self.cbox.log.info("settings stored on config partition") + ## return the result + if result: + yield "OK" + else: + yield "Error" + return { + "template": "show_format_progress", + "generator": result_generator} + else: + return self.__actionAddPartition(args) + + + def __actionEasySetup(self, args): + import types + ## we do not have to take special care for a possible config partition + parts = [ { "size": self.deviceSize, "type": "windows" } ] + ## umount partition if necessary + if CryptoBoxTools.isPartOfBlockDevice(self.device, self.cbox.prefs.getActivePartition()): + self.cbox.prefs.umountPartition() + ## partition it + if not self.__runFDisk(parts): + self.hdf["Data.Warning"] = "Plugins.partition.PartitioningFailed" + return None + ## "formatPartitions" is a generator, returning device names and bolean values + result = [e for e in self.__formatPartitions(parts) if type(e) == types.BooleanType] + if self.withConfigPartition: + self.cbox.prefs.mountPartition() + self.cbox.prefs.write() + ## check if there is a "False" return value + if False in result: + ## operation failed + self.hdf["Data.Warning"] = "Plugins.partition.FormattingFailed" + self.cbox.log.info("easy partitioning failed") + return "select_partitions" + else: + ## operation was successful + self.hdf["Data.Success"] = "Plugins.partition.EasySetup" + self.cbox.log.info("easy partitioning succeeded") + ## do not show the disk overview immediately - it does not get updated that fast + return { "plugin":"system_preferences", "values":[] } + + + def __setPartitionData(self, parts): + availSize = self.deviceSize + i = 0 + for part in parts: + self.cbox.log.debug(part) + self.hdf[self.hdf_prefix + "Parts.%d.Size" % i] = part["size"] + self.hdf[self.hdf_prefix + "Parts.%d.Type" % i] = part["type"] + availSize -= part["size"] + i += 1 + self.hdf[self.hdf_prefix + "availSize"] = availSize + if self.withConfigPartition: + self.hdf[self.hdf_prefix + "CreateConfigPartition"] = "1" + for t in self.PartTypes.keys(): + self.hdf[self.hdf_prefix + "Types.%s" % t] = t + ## store the currently existing partitions of the choosen block device + current_containers = [ e for e in self.cbox.getContainerList() if CryptoBoxTools.isPartOfBlockDevice(self.device, e.getDevice()) ] + ## additionally store the uuids (to be removed after partitioning) + for (index, t) in enumerate(current_containers): + self.hdf[self.hdf_prefix + "ExistingContainers.%d.name" % index] = t.getName() + self.hdf[self.hdf_prefix + "ExistingContainers.%d.size" % index] = CryptoBoxTools.getBlockDeviceSizeHumanly(t.getDevice()) + + + def __getPartitionsFromArgs(self, args): + parts = [] + done = False + availSize = self.deviceSize + i = -1 + while not done: + i += 1 + try: + size = int(args["part%d_size" % i]) + partType = args["part%d_type" % i] + if int(size) > availSize: + self.hdf["Data.Warning"] = "Plugins.partition.PartitionTooBig" + continue + if int(size) < 10: + self.hdf["Data.Warning"] = "Plugins.partition.PartitionTooSmall" + continue + if not partType in self.PartTypes.keys(): continue + parts.append({"size":size, "type":partType}) + availSize -= size + except TypeError: + pass + except KeyError: + done = True + return parts + + + def __getAvailableDeviceSize(self, device): + """calculate the available size (MB) of the device + also consider a (possible) configuration partition""" + import CryptoBoxTools + deviceSize = CryptoBoxTools.getBlockDeviceSize(device) + if deviceSize < 0: return 0 + if self.withConfigPartition: + deviceSize -= self.ConfigPartition["size"] + return deviceSize + + + def __isWithConfigPartition(self): + """check if we have to create a configuration partition""" + if self.cbox.prefs.requiresPartition(): + active = self.cbox.prefs.getActivePartition() + ## we need a partition, if there is no active one + if not active: return True + ## check if the active one is part of the current device + return CryptoBoxTools.isPartOfBlockDevice(self.device, active) + return False + + + def __runFDisk(self, parts): + ## check if the device is completely filled (to avoid some empty last blocks) + avail_size = self.deviceSize + for d in parts: avail_size -= d["size"] + self.cbox.log.debug("remaining size: %d" % avail_size) + isFilled = avail_size == 0 + proc = subprocess.Popen( + shell = False, + stdin = subprocess.PIPE, + stdout = subprocess.PIPE, + stderr = subprocess.PIPE, + args = [ + self.cbox.prefs["Programs"]["super"], + self.cbox.prefs["Programs"]["CryptoBoxRootActions"], + "plugin", + os.path.join(self.pluginDir, "root_action.py"), + "partition", + self.device]) + for line in self.__getSFDiskLayout(parts, isFilled): + proc.stdin.write(line + "\n") + (output, error) = proc.communicate() + if proc.returncode != 0: self.cbox.log.debug("partitioning failed: %s" % error) + return proc.returncode == 0 + + + def __getSFDiskLayout(self, paramParts, isFilled): + """this generator returns the input lines for sfdisk""" + parts = paramParts[:] + ## first a (possible) configuration partition - so it will be reusable + if self.withConfigPartition: + ## fill the main table (including a config partition) + yield ",%d,%s" % (self.ConfigPartition["size"], self.ConfigPartition["type"]) + ## one primary partition + if isFilled and (len(parts) == 1): + ## fill the rest of the device + yield ",,%s,*" % self.PartTypes[parts[0]["type"]][0] + else: + ## only use the specified size + yield ",%d,%s,*" % (parts[0]["size"], self.PartTypes[parts[0]["type"]][0]) + del parts[0] + ## no extended partition, if there is only one disk + if not parts: return + ## an extended container for the rest + yield ",,E" + ## an empty partition in main table + yield ";" + ## maybe another empty partition if there is no config partition + if not self.withConfigPartition: yield ";" + while parts: + if isFilled and (len(parts) == 1): + yield ",,%s" % (self.PartTypes[parts[0]["type"]][0],) + else: + yield ",%d,%s" % (parts[0]["size"], self.PartTypes[parts[0]["type"]][0]) + del parts[0] + + + def __formatPartitions(self, paramParts): + import threading + parts = paramParts[:] + part_num = 1 + ## maybe a config partition? + if self.withConfigPartition: + dev_name = self.device + str(part_num) + self.cbox.log.info("formatting config partition (%s)" % dev_name) + if self.__formatOnePartition(dev_name, self.ConfigPartition["fs"]): + self.__setLabelOfPartition(dev_name, self.cbox.prefs["Main"]["ConfigVolumeLabel"]) + part_num += 1 + ## the first data partition + dev_name = self.device + str(part_num) + partType = self.PartTypes[parts[0]["type"]][1] + self.cbox.log.info("formatting partition (%s) as '%s'" % (dev_name, partType)) + yield dev_name + yield self.__formatOnePartition(dev_name, partType) + del parts[0] + ## other data partitions + part_num = 5 + while parts: + dev_name = self.device + str(part_num) + partType = self.PartTypes[parts[0]["type"]][1] + self.cbox.log.info("formatting partition (%s) as '%s'" % (dev_name, partType)) + yield dev_name + yield self.__formatOnePartition(dev_name, partType) + part_num += 1 + del parts[0] + return + + + def __formatOnePartition(self, dev_name, type): + ## first: retrieve UUID - it can be removed from the database afterwards + prev_uuid = [self.cbox.getUUIDForName(e.getName()) for e in self.cbox.getContainerList() if e.getDevice() == dev_name ] + ## call "mkfs" + proc = subprocess.Popen( + shell = False, + args = [ + self.cbox.prefs["Programs"]["super"], + self.cbox.prefs["Programs"]["CryptoBoxRootActions"], + "plugin", + os.path.join(self.pluginDir, "root_action.py"), + "format", + dev_name, + type]) + (output, error) = proc.communicate() + if proc.returncode != 0: + self.cbox.log.warn("failed to create filesystem on %s: %s" % (dev_name, error)) + return False + else: + ## remove unused uuid + if prev_uuid: + self.cbox.removeUUID(prev_uuid[0]) + return True + + + def __setLabelOfPartition(self, dev_name, label): + proc = subprocess.Popen( + shell = False, + stdout = subprocess.PIPE, + stderr = subprocess.PIPE, + args = [ + self.cbox.prefs["Programs"]["super"], + self.cbox.prefs["Programs"]["CryptoBoxRootActions"], + "plugin", + os.path.join(self.pluginDir, "root_action.py"), + "label", + dev_name, + label]) + (output, error) = proc.communicate() + if proc.returncode == 0: + return True + else: + self.cbox.log.warn("failed to create filesystem on %s: %s" % (dev_name, error)) + return False + diff --git a/plugins/partition/plugin_icon.png b/plugins/partition/plugin_icon.png new file mode 100644 index 0000000000000000000000000000000000000000..235cf749af525c5bb20666ea96e2c01763bfb927 GIT binary patch literal 2943 zcmV-_3xM>AP)N-}n4}zwf!{z;%4S5Q`1C8r1Ii+r9rY;YBHx)vhTM zr%F6E@yZkby}+xV7>>mTTn$=oUcTd}U%s6`{-?JwTr*RN_!!#Q#eG+J^%FxbHsETI zS$EUQ(R*#CzBd1|AcW0g&MZ<`IA#2r%>9{$P$t3Dj0aK^ll*w|J|g8K50^jkpeNgnyr z`(f&>CQ?V6rdt8%8ycrCI`XM<ip=AhRPb~yUDG@?&>F8l7uYmLISOP2o4IM`q9O~!J!lihtZ$juA zlOrjFQlRGBB&DRbHWJP+aE6n~1f8AdzBDkvbc9$SP7w91e#a3D(TI{-x@J;Xc}zG_myddy!HS8=pYY3C#C^Whq=PNo(sf zEMHzrW8< z=U4m6aCgsN!a7i%$%a(0n%a$e{fBa{EHZZW)KRG${ zP2du<+;^oUu1|*#fMMua-!yY`aL1|$=RJO?UWX+lLX$X@LP$YgVM!*`q;#-#>-}85 z+{ga?tsFV>5o^|b30-eukAchOKq-jFQ{?68Y}l|BpRe$b+S>N~OETH}B#_xB z7W|mox%00++`Rd=^1M7dX#)Z8bkZ^_d1oiH-L7eJx?N^+C!Od*2u4Svw6*;wue|aq zPG=eS+;bnHP>9QyKgQ{F(9%*%MMVJs?d_L1bf}$z0zZL39v^(r&2!KFlK0;~_-$Yh zFrwu${{H(P4H$;;_qDaF5JI5qnyoT(Aac-8lSzkJdQ4h+Uw-*Nc=p*{R8*{D=gyt1 zT^nZU(tJuw^RX<&AO6tIk|hByTo~l&(JsQ_QmU$o2nIcbLM22Z%|xTt`ktQdpk+1Z*w_egQ%(X(S(yZOIJB(fwUOx->?t}^#MfYH%7LcoR%HC((n!hr*)7#o|Sxp@Vt zltq7klxQ?YUYS zxuGB7aC@#7abeYzXSu@49#!d|b}&@?S$NE&GpGsP7YN=Y;t zqqFlP>gy|!Qd@|Cj6@=YO-<|Ay?ZbB-n)Z}ihS<4Bf`^9xADa<)>2&T#p5w>I&HHiCzH5bIt2v* zVzD?aEvreTOirEZ=DFv70njsVBU`s_(|5LQyLGwW?_qZMOq08+IzVl0Ex-HS3+&$g z?-Um=$L;oD820DG;9!i($^r@s+&G;MdV4?St+zT@yY_~REFV1h29G`Vt^TgAH-8K4 zo=2d$`L>_kdFK|7t~(Yr=+o8#G)*HE3NbNpnSJ|SV02W+?=N8e`jxodPL3b%qO>$X zN5=p?JyGiGmm#I(^y!QI`qz7DZGGxeEY`al_$_cUi@-wr4 zfzRiouC9*K(g1C3dpLi7n3|d}m6e6m)Rgd>-<-kWkgQ%^PCRb%=9}-cYu8Wd>N>*s z_~4#YDzz7Q54fC_14799nwqNVXTe-0TFAiJ?TbC9e@jYAC@CqSy1JUDpZ+;dKKUaa ze)xOT)GX!Zn?nG6^wAiH55LE=&pytI6(!WwZRX_36Uj4Y-aQM91Lhn8Vr@8FMJknI zcz8Hx2wY1@DXFQc;rrkJ4u=mP=DXkhCYv^Woo(A%h{dLO{`r67$u%+wr}4~b8|B< zzW5^J<384}-%3?g70Z?_qp+}$p`j?6=5PQ8Fa>i62m!;xBNP|=|6d8Dk)NNB+wJDQ z`|e|GYz&{zM=%(~<#J8$#ACBQ-)&?OkWvzlCs9ge4lsRveIybIlv30EaSX#C9*?8z zI*CLAUDrt_lNpt%t*zz%3d1n)dcE`7l~NdnbKa%FTopno6Qxwf13sURcs!12nz&pp z`uqFwcs#`8ahy&krfCB9p}()Mk7zW?nl)><7BEdSqlC$1a{5?$MFRGzXEuSnyga0o zq*5uQv=2A4ix2{b!;$ft)9JkG@Utw7e?0sQH`IjaKG(yew|#>|B7vFXntrYa;_-L_ z%d#>dB<2R)BG9zl=jTrY(=-{B104VG1D1&Znxad-ps2`$ z!{IyOOeI7`r z`{u<*I2`deHg5UnV6eDBh#dEXp97S#tbu_`r}poEX3x~r002ovPDHLkV1jI`ov8o- literal 0 HcmV?d00001 diff --git a/plugins/partition/root_action.py b/plugins/partition/root_action.py new file mode 100755 index 0000000..4cb4758 --- /dev/null +++ b/plugins/partition/root_action.py @@ -0,0 +1,96 @@ +#!/usr/bin/env python2.4 + +## necessary: otherwise CryptoBoxRootActions.py will refuse to execute this script +PLUGIN_TYPE = "cryptobox" + +SFDISK_BIN = "/sbin/sfdisk" +MKFS_BIN = "/sbin/mkfs" +LABEL_BIN = "/sbin/e2label" + +import subprocess +import re +import sys +import os + + +def __partitionDevice(device): + ## do not use the "-q" flag, as this spoils the exit code of sfdisk (seems to be a bug) + proc = subprocess.Popen( + shell = False, + args = [ + SFDISK_BIN, + "-uM", + device]) + proc.wait() + return proc.returncode == 0 + + +def __formatPartition(device, type): + import time, threading + result = True + def formatting(): + proc = subprocess.Popen( + shell = False, + stdin = subprocess.PIPE, + stdout = subprocess.PIPE, + stderr = subprocess.PIPE, + args = [ + MKFS_BIN, + "-t", type, + device]) + proc.wait() + ## TODO: very ugly way of communication: it assumes, that failures are fast - success is slow + if proc.returncode == 0: + time.sleep(1) + return True + else: + return False + thread = threading.Thread() + thread.setDaemon(True) + thread.run = formatting + thread.start() + time.sleep(0.5) + return thread.isAlive() + + +def __labelPartition(device, label): + proc = subprocess.Popen( + shell = False, + args = [ + LABEL_BIN, + device, + label]) + proc.wait() + return proc.returncode == 0 + + +if __name__ == "__main__": + args = sys.argv[1:] + + self_bin =sys.argv[0] + + if len(args) == 0: + sys.stderr.write("%s: no argument supplied\n" % self_bin) + sys.exit(1) + + try: + if args[0] == "partition": + if len(args) != 2: raise "InvalidArgNum" + result = __partitionDevice(args[1]) + elif args[0] == "format": + if len(args) != 3: raise "InvalidArgNum" + result = __formatPartition(args[1], args[2]) + elif args[0] == "label": + if len(args) != 3: raise "InvalidArgNum" + result = __labelPartition(args[1], args[2]) + else: + sys.stderr.write("%s: invalid action (%s)\n" % (self_bin, args[0])) + sys.exit(1) + if result: + sys.exit(0) + else: + sys.exit(1) + except "InvalidArgNum": + sys.stderr.write("%s: invalid number of arguments (%s)\n" % (self_bin, args)) + sys.exit(1) + diff --git a/plugins/partition/select_device.cs b/plugins/partition/select_device.cs new file mode 100644 index 0000000..bfb7b59 --- /dev/null +++ b/plugins/partition/select_device.cs @@ -0,0 +1,45 @@ + + + + +

+ + + + 0 ?> + + + + + +


+

+ + + + + +
+ + + +
+ + + + + + + diff --git a/plugins/partition/set_partitions.cs b/plugins/partition/set_partitions.cs new file mode 100644 index 0000000..f2a9db5 --- /dev/null +++ b/plugins/partition/set_partitions.cs @@ -0,0 +1,78 @@ + + + + +

+ + + +
+ + 0) || (subcount(Data.Plugins.partition.Parts) > 0) ?> + + + + + + + + + + + + + + + + +

+ + + 0 ?> + + + + + + + + + + + + +
+ + + + + + +
+ +
+ +
+ +

+ + +

: +

    + +
  • ()
  • + +

+ + + 0 ?> + + + + + + + + + diff --git a/plugins/partition/show_format_progress.cs b/plugins/partition/show_format_progress.cs new file mode 100644 index 0000000..126d37b --- /dev/null +++ b/plugins/partition/show_format_progress.cs @@ -0,0 +1,17 @@ + + + + +

+ + + +

+

    + +
  • :  +
  • + +
+

+ diff --git a/plugins/partition/unittests.py b/plugins/partition/unittests.py new file mode 100644 index 0000000..c2f5512 --- /dev/null +++ b/plugins/partition/unittests.py @@ -0,0 +1,10 @@ +import WebInterfaceTestClass + +class unittests(WebInterfaceTestClass.WebInterfaceTestClass): + + def test_read_form(self): + url = self.URL + "partition?weblang=en" + self.register_auth(url) + self.cmd.go(url) + self.cmd.find('VERY careful') + diff --git a/plugins/plugin-interface.txt b/plugins/plugin-interface.txt new file mode 100644 index 0000000..3fef72f --- /dev/null +++ b/plugins/plugin-interface.txt @@ -0,0 +1,63 @@ +The following directory structure is required: + - python code: plugins/PLUGINNAME/PLUGINNAME.py (all lower case is recommended) + - language files: plugins/PLUGINNAME/lang/(en|de|??).hdf + - clearsilver templates: plugins/PLUGINNAME/*.cs + - icon (128x128px recommended): plugins/PLUGINNAME/plugin_icon.png + + +Python code interface: + - create a class with the same name as the plugin - it must inherit CryptoBoxPlugin + - function "doAction": + - this function will get called whenever this plugins is involved in a request + - all arguments should be optional (e.g. for displaying a form without previous input values) + - the argument "store" should be used to process a form submission (just a recommendation) + - if the processing failed for some reason (invalid input, ...), it should manually set + "Data.Warning" or "Data.Success") to a value of your choice (preferably + you may want to use messages of the namespace of your plugin + (e.g. "Data.Plugins.PLUGINNAME.WarningMessage.InvalidInput")) + - the return value should be the name of the template that should be displayed after processing + (a template file in the plugin directory takes precedence over global template files) + - the return value may also be a dictionary with the following elements: + * template: the name of the template file (mandatory) + * generator: a generator object ("yield") - its content will replace every + occurrence of "" in the template (useful for pages that + are displayed step by step (as for formatting of filesystems)) + - the return value may also be a dictionary with the following elements: + * plugin: the name of a plugin + * values: a dictionary of variables that should be defined for this plugin + - an empty (e.g. None) return value can be used to go to the default page ("disks" + or "volume_mount" (for volume plugins)) + - function "getStatus": + - returns a string, that describes a state connected to this plugin (e.g. the current date and + time (for the "date" plugin)) + - the class variable "pluginCapabilities" must be an array of strings (supported: "system" and + "volume") + - the class variable "requestAuth" is boolean and defines, if admin authentication is necessary + for this plugin + - the class variable "rank" is an integer in the range of 0..100 - it determines the order + of plugins in listings (lower value -> higher priority) + - the class variable "enabled" is boolean and detemines the default availability of the plugin + - volume plugins contain the attribute "device" (you may trust this value - a volume plugin will + never get called with an invalid device) + - the python module which contains the plugin's class should also contain a class called + 'unittests' - it should inherit WebInterfaceTestClass.WebInterfaceTestClass + + +Language file structure: + - the content of the language file will be added to the hdf dataset below "Lang.Plugins.PLUGINNAME" + (this avoids namespace conflicts) + - the following values _must_ be defined: + Name (a short description) + Link (the visible text for links to this plugin) + Rank (defines the order of the plugins displayed (0..100)) + - all warnings, error and success messages should be stored below WarningMessage.??? + (resp. ErrorMessage or SuccessMessage) + + +Clearsilver template: + - should start with a "

" tag (volume plugins: "h2") + - links to the plugin (e.g. in form headers) could look like the following: + + - volume plugins must include "show_volume_[header|footer]" (see examples) + + diff --git a/plugins/plugin_icon_unknown.png b/plugins/plugin_icon_unknown.png new file mode 100644 index 0000000000000000000000000000000000000000..d6e9b3a184b012df341fe6e378e0b4603f9e3fc9 GIT binary patch literal 14269 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 literal 0 HcmV?d00001 diff --git a/plugins/plugin_manager/lang/en.hdf b/plugins/plugin_manager/lang/en.hdf new file mode 100644 index 0000000..c1c7856 --- /dev/null +++ b/plugins/plugin_manager/lang/en.hdf @@ -0,0 +1,15 @@ +Name = Plugin Manager +Link = Manage plugins + +Title.PluginManager = Plugin Manager + +Button.SaveSettings = Save settings + +Text { + PluginName = Plugin + PluginRank = Priority + PluginEnabled = Enabled? + RequestsAuth = Requires admin? + VolumePlugins = Volume plugins + SystemPlugins = System plugins +} diff --git a/plugins/plugin_manager/plugin_icon.png b/plugins/plugin_manager/plugin_icon.png new file mode 100644 index 0000000000000000000000000000000000000000..236169c602af7a41ecfa7f18eea0a4bff0c7408d GIT binary patch literal 631 zcmV--0*L*IP)wF@bTE5(h_ zjTUqv=)#52;zs-r3SGEX+;wHeNNhwUVnMOiVhc8D8;yC*BzLY0@5HHEN$p0|0~hXN z?sw+J!AORRE$>x;kju@jC_9o=Y_$67IxFwxe`5<_390%-QG4b`(fUs>Dy8u~#`bfG< z3v&7L*>&KVd{$=iS-HGc2r`NL40Hv05I77RQuBVGx9l{TP4{fbDm^e1TwVuKKmn)% zQU_RgKBGB~W8!(yh^|MYmTB0Q)xM?0lX$fXjobqmxe^2%1wH{a;4@GH9KV8efT?R2 zHC$IWM9YW?*GLIh-`%Y9vE>2KcV+}pT4lBFuyd`@-UrIS98gpV%0PV`__@0eH#8tK zxQEKU+nBurthgTT+tHTeOg{V7 zuzmd`ZA~Ofb$|p(G{H=sx)dU@P_Xr z5N>*Ma=O&04o=UF0X*5Cr~FOx^8uZIFtn2wWdSKGtQ`%7LVZb%n*b#t#~tD2fOo25 z8j5vQDbW#lNImZukOaEbkiZhKs47K#-^e!83pAA^0(7apN&xr|ZMpwed;#r);jz2X Rq*MR^002ovPDHLkV1kwF9P|JH literal 0 HcmV?d00001 diff --git a/plugins/plugin_manager/plugin_list.cs b/plugins/plugin_manager/plugin_list.cs new file mode 100644 index 0000000..7449f3e --- /dev/null +++ b/plugins/plugin_manager/plugin_list.cs @@ -0,0 +1,65 @@ + + + + + +

+ + + + + + +

+

+ + + + + + + + + + + + + +
checked="checked" />checked="checked" />

+ + +

+

+ + + + + + + + + + + + + +
checked="checked" />checked="checked" />

+ +

+ + + +

+ + diff --git a/plugins/plugin_manager/plugin_manager.py b/plugins/plugin_manager/plugin_manager.py new file mode 100644 index 0000000..3b6a839 --- /dev/null +++ b/plugins/plugin_manager/plugin_manager.py @@ -0,0 +1,52 @@ +import CryptoBoxPlugin + + +class plugin_manager(CryptoBoxPlugin.CryptoBoxPlugin): + + pluginCapabilities = [ "system" ] + requestAuth = True + rank = 90 + + def doAction(self, store=None, **args): + import re + if store: + for key in args.keys(): + if key.endswith("_listed"): + if not re.search(u'\W',key): + self.__setConfig(key[:-7], args) + else: + self.cbox.log.info("plugin_manager: invalid plugin name (%s)" % str(key[:-7])) + try: + self.cbox.prefs.pluginConf.write() + except IOError: + self.cbox.log.warn("failed to write plugin configuration") + return "plugin_list" + + + def getStatus(self): + return "no status" + + + def __setConfig(self, name, args): + setting = {} + setting["enabled"] = False + try: + if args[name + "_enabled"]: + setting["enabled"] = True + except KeyError: + pass + setting["rank"] = "80" + try: + r = int(args[name + "_rank"]) + if r>=0 and r<=100: + setting["rank"] = r + except KeyError, ValueError: + pass + setting["requestAuth"] = False + try: + if args[name + "_auth"]: + setting["requestAuth"] = True + except KeyError, ValueError: + pass + self.cbox.prefs.pluginConf[name] = setting + diff --git a/plugins/plugin_manager/unittests.py b/plugins/plugin_manager/unittests.py new file mode 100644 index 0000000..93d61bc --- /dev/null +++ b/plugins/plugin_manager/unittests.py @@ -0,0 +1,12 @@ +import WebInterfaceTestClass + +class unittests(WebInterfaceTestClass.WebInterfaceTestClass): + + def test_read_form(self): + url = self.URL + "plugin_manager?weblang=en" + self.register_auth(url) + self.cmd.go(url) + self.cmd.find('Plugin Manager') + self.cmd.find('System plugins') + self.cmd.find('Volume plugins') + diff --git a/plugins/shutdown/form_shutdown.cs b/plugins/shutdown/form_shutdown.cs new file mode 100644 index 0000000..306ffd1 --- /dev/null +++ b/plugins/shutdown/form_shutdown.cs @@ -0,0 +1,15 @@ + + +

+ + + +
+ + icon: shutdown
+ +
+ + icon: reboot
diff --git a/plugins/shutdown/gnome-reboot.png b/plugins/shutdown/gnome-reboot.png new file mode 100644 index 0000000000000000000000000000000000000000..1556089ccfb1ae03d2b839fb003e3f52c23306a5 GIT binary patch literal 3752 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 SnSu2H0000Ul5 literal 0 HcmV?d00001 diff --git a/plugins/shutdown/lang/en.hdf b/plugins/shutdown/lang/en.hdf new file mode 100644 index 0000000..2356e4a --- /dev/null +++ b/plugins/shutdown/lang/en.hdf @@ -0,0 +1,34 @@ +Name = Shutdown or reboot the computer +Link = Shutdown + +Title.Shutdown = Shutdown computer +Title.ProgressShutdown = The CryptoBox is shutting down +Title.ProgressReboot = The CryptoBox is rebooting + +Button.Shutdown = Poweroff +Button.Reboot = Reboot + + +SuccessMessage { + Shutdown { + Title = Shutting down the system + Text = If the computer does not turn off itself within a minute, then you should plug it off manually. + } + + Reboot { + Title = Rebooting the system + Text = This may take a while (depending on your hardware) ... + } +} + +WarningMessage { + ShutdownFailed { + Title = Shutdown failed + Text = Shutting down of the system failed for some reason - sorry! + } + + RebootFailed { + Title = Reboot failed + Text = Reboot of the system failed for some reason - sorry! + } +} diff --git a/plugins/shutdown/plugin_icon.png b/plugins/shutdown/plugin_icon.png new file mode 100644 index 0000000000000000000000000000000000000000..9d537e73815b3c3935d21cd28cdceac75db6f27b GIT binary patch literal 7588 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 + +

+ + + diff --git a/plugins/shutdown/progress_shutdown.cs b/plugins/shutdown/progress_shutdown.cs new file mode 100644 index 0000000..a4afd10 --- /dev/null +++ b/plugins/shutdown/progress_shutdown.cs @@ -0,0 +1,6 @@ + + +

+ + + diff --git a/plugins/shutdown/root_action.py b/plugins/shutdown/root_action.py new file mode 100755 index 0000000..9aa553b --- /dev/null +++ b/plugins/shutdown/root_action.py @@ -0,0 +1,48 @@ +#!/usr/bin/env python2.4 + +## necessary: otherwise CryptoBoxRootActions.py will refuse to execute this script +PLUGIN_TYPE = "cryptobox" + +SHUTDOWN_BIN = "/sbin/shutdown" + +## delay (in seconds) before shutdown +SHUTDOWN_DELAY = 3 + +import subprocess +import sys + + +def call_prog(progy): + proc = subprocess.Popen( + shell = False, + args = progy) + proc.communicate() + return proc.returncode == 0 + + +if __name__ == "__main__": + args = sys.argv[1:] + + self_bin = sys.argv[0] + + if len(args) > 1: + sys.stderr.write("%s: too many arguments (%s)\n" % (self_bin, args)) + sys.exit(1) + + if len(args) == 0: + sys.stderr.write("%s: no argument supplied\n" % self_bin) + sys.exit(1) + + if args[0] == "reboot": + result = call_prog([SHUTDOWN_BIN, "-t", str(SHUTDOWN_DELAY), "-r", "now"]) + elif args[0] == "shutdown": + result = call_prog([SHUTDOWN_BIN, "-t", str(SHUTDOWN_DELAY), "-h", "now"]) + else: + sys.stderr.write("%s: illegal argument (%s)\n" % (self_bin, args[0])) + sys.exit(1) + + if result: + sys.exit(0) + else: + sys.exit(1) + diff --git a/plugins/shutdown/shutdown.py b/plugins/shutdown/shutdown.py new file mode 100644 index 0000000..0638058 --- /dev/null +++ b/plugins/shutdown/shutdown.py @@ -0,0 +1,51 @@ +import CryptoBoxPlugin + +REDIRECT_DELAY = 180 + +class shutdown(CryptoBoxPlugin.CryptoBoxPlugin): + + pluginCapabilities = [ "system", "menu" ] + requestAuth = False + rank = 90 + + def doAction(self, type=None): + if not type: + return "form_shutdown" + elif type == "shutdown": + if self.__doShutdown("shutdown"): + self.hdf["Data.Success"] = "Plugins.shutdown.Shutdown" + return "progress_shutdown" + else: + self.hdf["Data.Warning"] = "Plugins.shutdown.ShutdownFailed" + return "form_shutdown" + elif type == "reboot": + if self.__doShutdown("reboot"): + self.hdf["Data.Success"] = "Plugins.shutdown.Reboot" + self.hdf["Data.Redirect.URL"] = "" + self.hdf["Data.Redirect.Delay"] = REDIRECT_DELAY + return "progress_reboot" + else: + self.hdf["Data.Warning"] = "Plugins.shutdown.RebootFailed" + return "form_shutdown" + else: + return "form_shutdown" + + + def getStatus(self): + return "the box is up'n'running" + + + def __doShutdown(self, action): + import subprocess + import os + proc = subprocess.Popen( + shell = False, + args = [ + self.cbox.prefs["Programs"]["super"], + self.cbox.prefs["Programs"]["CryptoBoxRootActions"], + "plugin", + os.path.join(self.pluginDir, "root_action.py"), + action]) + proc.wait() + return proc.returncode == 0 + diff --git a/plugins/shutdown/unittests.py b/plugins/shutdown/unittests.py new file mode 100644 index 0000000..baa0308 --- /dev/null +++ b/plugins/shutdown/unittests.py @@ -0,0 +1,11 @@ +import WebInterfaceTestClass + +class unittests(WebInterfaceTestClass.WebInterfaceTestClass): + + def test_read_form(self): + url = self.URL + "shutdown" + self.register_auth(url) + self.cmd.go(url) + self.cmd.find('shutdown\?type=reboot') + self.cmd.find('shutdown\?type=shutdown') + diff --git a/plugins/system_preferences/lang/en.hdf b/plugins/system_preferences/lang/en.hdf new file mode 100644 index 0000000..2beeec5 --- /dev/null +++ b/plugins/system_preferences/lang/en.hdf @@ -0,0 +1,5 @@ +Name = System preferences +Link = Preferences + +Title.Preferences = Preferences + diff --git a/plugins/system_preferences/plugin_icon.png b/plugins/system_preferences/plugin_icon.png new file mode 100644 index 0000000000000000000000000000000000000000..1355333906b55d31fca9210e939d055af84303b0 GIT binary patch literal 12762 zcmZ{LbyQT}_x8-t-6>rHQqtYs2uPQ-bV}kIh-~Y88 ze&as)#_83d_L@)DFp^Cb%G9;)!y$`pt38k;lu`c?-Dg5uS9;>rASJuO$M4Fd-z~`d z!@(NRxn@4d`d><-S)YCStADGpO66w&TuFFD^xF&(SZ>_C9!a zd$iR1=7E}jEOK=7@-^dlCyshR7T5lNw|2x@!G@UwF7F)_x*oh==J65}iq1Ei0 zb@Qte@=YfjJQw$>S!hL+mEl#0SUUbBTBM<&RraUhCMzP>Iru^KQpYn1!>&j}vZDIR zWvn~U)$br!byb4L0@;1A6p|O=vQ60=N;R7cHej%a-dRP|BOW?DzSE#BbQY$I3ii~52#d~8L2jc6K%0Ie^C#W9(hQTIBc07Cj7 zbKrEczVA$VvaU~1(3Q%{+B|*7MV{%e71yXJ4nC1-7-mG<8x)^g>qJ1~1SAf+K9Og{ z9qWbTRR>*_wgv!0%M$uD*9e zBypIm6UBzzrQh2Ezgv#Bc!fYf0Njr_NCFUSa&SFGRv{unmMQdlXb5|LcJ(9(8y&)# z0Ove@({iK(K<%fQSJD*~6+z3YUzY6C&i|Tg8sA-ix^w7L zSBw?Z{svWxs=K^n#QWbkk@T+A#rq5lp`R1h)?Udkf0~+_(khS1EhoW^wIszSAUJ_{ z$Z=$9g8f0GfXl_ztve>A0fQJ1xq1?I=O;+;n75M~5l3sO6P(UnXElCnZjr4sKr{lY zk%<)X_+j>;TkA70yDnA&i3MSTOIZ||q~u@lF455Nu)^%%-@th#WpO&E!`zFBK|iQm zi}m4T7V!A4xA|LJARg=181~VuVwdz9vH)!r1JkcA%$bQ)67h=NdVtL$H(&ofsh)9nmn7c5P6|mwXD2z}YmpN#6 zlS(4^^hYRJbPs|(w_8{zn_L!_A|>YKBRf6a6=20C6Mkzljkvv&6|0BHWv9!QIWDlPOjx&%^1zy! ziSz#39Cs0N36LyD8<8uI?MW#wJ@DhTX5_lP76_WV*E*XfM#-A#8xJ1bo6OQ)usvG5 zo~TV=n@&;(c3^yz7&I7!$GzR?c`qs5ilq|u(awtGDuJbLQW#&C!S8z^IIJGMS;+mLIXLj)=5BY zI2*Z9u=aN8o7-EaB`Ih5El6v5WsOBwR{At4y6pmGDGuwG`j|7X#qgG=C=neDJ2-qv zDY9-KW0~jf8<0$>^Myd#XpM3p2c(j636L=0JInhK!TfeUH~mZEyL z&qyc673M_a97z(NmX$t5+c=lXe-uQ4g^g9_DBlM~pYd+z%k5!wS_0wJcyiNu3=;_b zlS&O|@a|_HbS?tNepO=T(~masO+j+<&|0giI1|z}(uZer>I~d1m}2Cy^Gv1RyPBJ3 zz4tIP&01{c!5eM=;pI%1{kMBSSjT+gn{96S8Qi84h)`$de??i*fycuz+OZdNWR0-z zYU(Q=I%&iUv_K0D{9wt@(ch#DOa0-hB2ctWj|FTgH1E;gLfaKVc7+&^Qf?;qyxL^l z5+#Fe=f?)XWF51An_QpD+Z%?=vj(J9EHS;xa2uywa`F9`pD_MDVGjHP0Y1qKf9FA_~4!O`qZLuK-ReMYiJFUd!EOXn% zLN9B!XeDARLlQ+>TCNG0TL5L-+7Q>uH}r%FbTBR`AyF&ns#xup@P~#5iLlFT*?5~; zuTW?t0L$av=Pq9wjrb4uN#}H(4Zh5cr>FdQt?=7e;&Y-OS?9+56}l>d9w6p}lhDvC z-lFQFO5EP2UH4-qO22iw$Swd8SBth!IFO%Ur_FIixG*Y?iYgn~G8xfh_R%1!Vp^-+ zga@$c8FV&^7xc`eX6-~xE^2l^pr^nE+xxFD?Gtu}K(D?1*|3@Q(Qu}U!WZ{i_IwtH z&Zw}jCm}M}3mk&f#TBz6ei251v!Kcwql8qULDIV1nY{w|>BDSscBy0|t8my|(vvAP zVG+@p$bHR-_WH^Q3U81p;a8tNr#I3_;wtW$GCOL|8r!X{cS=xo2Z6JzC$mB0+5h&c zoC=?f&@=v|s!CS^CbnN&fR!4En^d}wmF7e%9n$~&c~en9r5-=1Q!y5E@dQl+G}rDviS8_f3XBwPs>l(m4vu z>n$;8QJ|`-Y53A1-QGW15XQWUTL$n8M(`t}ym)@!-i($$h3nXdfwi67gxX5ZgoKjl z1c)nDU37E_)Ya@;Q-Ccs2bAFM_2H2fT1EltwF3t>F_t>pIv;hDM}|6^0RCHfCg|+` z`cypQc0a5`-phu+`xqkiByVpLKR z=S3;a$1z!fs2}K8wvzmvz_dJz7?{MK{`_mKH6=`7w{U(*ROHHiy=qyB)9~#hFZ#6g zH<}$rTbCh%mp6@nm)fjzCf-O2lA-T_-}SFer;j6BbILUYbJ&QniLudxuT}?xcFU-C z5B5N&S<4)xV9tb+Q$%vX*q2Ywh;Vy`hSWn2^2O6JuLo->3I$(GtQG)n1FHtoL4{if z637rX7@cUTWVm17j0^f(^)+EEb=?1#uoex|Ec>dR_|f$hwV<^^=LoMa$z3zl%i3*$ zBi%Ix`?Y)33Qju+`SYo)hU>7qnD8h`%HaAKwt&uCRy88B_xR5RNgX+9*%?wk+%B}% zo(=V-7@EJpwM@du9}&T3);G>vT(p$Od@qZ-vyM|M0dXOQ0RxAdgf0QuB9h`NR2~QO zuEwSdQdi3wQ&io|$c)|}d;P+qe=T1!pwRTLA#K;dHXJaQb6t~Fc18{p6l=G1!lEmJ zHPAhMqWhpo$7&Y7znYy4&vHyt=TQH-{&U?&RZWfN!v}%L2zHEXjHAG{ldG7h46^sN z`<~yt9XNX%C+~j`^S=X20qld&a#uv`!1aL+nF6Im-p+lEz>rPBdZJ(w$k^mzXm0tA zINDUY=Y7U@Ll2ca`tHnuo)=m-(d)qDkS)yxKXyUZ=TQL6kJw?%#Mns2$jwpTd&w^k zLj7i$Z7*{0?<(IZ$wD8ZA}(cb$tpZZ5D&oOi7IX^fW^Z=4;aX+Fl*5-Hc^w?nKE4! zDIe_wE51?3Fcqr%-fc*zZsvCqU9lsm1;h(>txZ^jRG^crGSSio7aAj@&pwKW(ztOy?{k7+Mx+ zMukcFKHCR$oJ$ksft<#g~3~$z;OsC&bTPFmfTC zTJ6f6CsxD8pZ~Um8y}zWIXS)ERCJ3=Rf6kZI+>|8C?f_#OHOw@4V%0q(W4lcHB+rqx4?jH|%nh{tfZ$o%+qAnsWeV_{3+}t!UHQkA*YinCzk6M>}NgkW|>}9>je#eBP zJ7&c6oY66s%N)F&bpFh_vTCIK;@R*dUTG0AO@bgX%`2Zx>6U4ic{+(V138mKtUZy= z{>%_%^JMHRaLgRl<~>=PPX3I4s$p50NwA9 zuot_b-3W<^eKbM9z~&Z{XJgBetZDDs6rNbu&0bJiSBiS`1aF7th(L^t`;Sp=Y<~FN zcXweUBO{ii7w6|c;>zsx{;L|H&ujkb+Q$Jx-^(a!#~K45yKu`gc)@m00#swIAt7e1 zF5uj%R~FGJ$@%4xOb|hzvlAd7mxtH8wr;d;V3sG!NE6&i0x4#YKYXu|oSZ!AdO4^% za9(ivw?1~1qlHV=R%c*)%;BM6)bIU^7X}8av|&Kiw!CZ&^&eBraif-OB!vslcRgYx ze?Nz^JbE5hv470Y1PPEP`V^NYvg5H|l{@lS5X-RK{X)dejiXBPrWU2XgpKau+A#VTDoL|;+d?;G2FZ}z+9O#gI?@4{#cjoQ z@_5Z+>Nyf9mSekN5F2#MpE3+YnRt{htgRL7>_{SDDGmUE&SYaq6e9od>u*S5?NRLk zj1F=N^}m|uUq1<2++m7G2kL5jbp-W?H1!x@E=_hXBfxUYw+*EuX6@d500b%u%sHAq zwO`)$<85goJrYxr3yT}8shDuW_FgB7EMm75Uw~=z_GX!9C+awqW|dp*V5+LjVrX5s z`UcOl^&Z*EuIBA0%Op2g8($41A_+&cc;pqB9jWu( z+?-eMT6SEKM2v35rHxn{32PN@KytY>KN`Ns$}5pqwl}`6#vh*Wutk zzv0!NTJz}WXjij;3ykQelWs+SoO@6C_ir1#KP)}c^!B!EN!VV@0dupQt^2#WB*Q8! z4TH_J-Q{_T+~v~G!p)uK;Rj4Uc(d_j zhk<8d>uR|1oAnL^{Syg!8Xf59OJ%K;FB;!LnSHi^-4K8esW# z?cY?yn(ul^Kt<)Pe6jdVb41>28$Q-SC&Ah1$)cRx+-rzcA2(yO}W&5il+gB z`g6_cLFzzsQg1%OI@(v*qiXltFSoGrJu==nSX=Wwnf)SfD1si{l*~7xnTQOTM!-Bb zEiJG>QpYR3(<%+UL($in2VpxTgHhjz=dveK?o5aH^9c~@j>dhsOdt2DS{Ab2$pnM{ zOyKsm2JE*yF4MPP{=sUG)Xy@-*^c|+2Y)Tynk@fod0xlI4lG+oWigj830EC$!2>OJ z1XJYwscZdAIy_~s_D{Ab_rZ%LI9GyS9S$m@``Nvn z%WQW`*!?dxQJ%V--C5(+CnFoilC7bfW3bKW$nvreaA)?bKu^<&-fKT(NJmWHs+Kx( zHJ%Z<=T;|f?j(nNf9~xMAK^y_b63;)Lc5fbZ!-wpsvu9emE5h#Sr90nS$a2Qh1H;y zO!(NyX#eq<9bR6R-ytw*+-|f5Q3HdQ8&1p;Y@o#%wS}hO$L(WZ#JE^K#+OKph!#LZ zibD_6Rgt~?H*roS%2$6uMOC0?TNr22rtu)PZU~PZ8?M1b*NX?k$>wySCGl) zGZ)vm6IL3Hm^IFUHar(v?_-|?R&Zpc@csjk5>f~D^Viud93M4n4&h>`o1h;}_+&!L zvVqdKrvA)N>n!ZLcD6T`W2I~oC^!6ZxZdwxS#8bT`guImCOWe7dj?sB<4He3HC3a7 zU2OGbLD{R6!MCotwT8nQYR!jhsj)i)&mN@I#WY*0|crF)rSsw!l<}k&WN6 z%;?;1lvMD)f5uM?1@ZQe#@$T2oO1HsBFLS57e@$j@?$xe(OYZVinj=HN~ZoZ{1h_y z1j~AVsVU&LZT3D+hlNoJV)RlZD9SlXC%5Wr_$W8!*fRh`rBbd|OnV-#$&4A1=~zk& z$vE*PhJHa0h&6$+A=fjmqBb$bk<{FxNTvje>>g%|W6QyTfKd`f#6^fkS#_#pZnqCx zurvdHpLaY&jZFMsMms&l#l*yXhpz%+h+OY>t7!IEdYJm~BXE6qaD>FjY^I3m%Y7&X zhG?DD&t43Qxn3u4HjPN(D7(Mykqy}&?UPdahSPtJmq_N4oeeMpnu8VII<$Wqf z4np+90+C0%Bw0oiPV23ko?|5?oSv1z%;@!|Ny;p%abFx|3kNhD{t+~iV`^ps?MI+oEWbLQ76-!6ZY48tR|0E;Yvgl zbQUYOiW}YtF%RMFkjI5%KUsT^?$sHYA0!=H)m`U7A$@&i&tv|Lx_kUZ1JCF{cNuJQDUM{W<4%p zzx9ZZ{w=<}78~i&FVGM9X9?*O7h;RO|El-AjM2AJZ(iu@V>g)p)dMoLnAP#^Ctg65 z2CRReEUTED9xY(eT%!UvN{>@P+4A6pEi5q-g>5(XtfJR{j{1tBa@|rZcI?ocPO6B! z!<+Q!ylB^g0RCN7g%0r@B3Ast=KY7N-Ma2cQm$s10}1x1Z+wzj<7u$>plt`qpw~_RRNgvCD&A7DmS&TRc{U9~oLr=L4pVQ&*gZWZEU zgv<(9WeV7_wI z;6g1mzqJ)W*Nd2?&Y696E9GntF9@l5l|`X zylqd7FZl}V{R%76+x{I34q2EaPPr_7ZEds^UlMHK&-h=%tL!RyJMnk&?38-q^U|Ql z%!s5ER*#KgR2#7QLFD}p3gT8*Frm=6$+K~#I(XsCH zzRl}S%b0gslC>wTLptkNfb;!Id;VYpc0H8)8ZJ|;Pdu9SdS&+-8~esMTJ}Tgq|y+7 z)IGw8D4d=&Oa)b3F4l6aG8M*zPQh;1ZcWq#Rf^Y>sI;N{x_7Yrmu%j{&AloT)Urm6 zjUFxi;6ib|`^q?Hz00sdVBZWKbo9!g9AIf-vVdOVcRok59DeLExOw*owP|5UY3Q7AlOfsswrV91#ldu(=FVX~T{P z)~K!N!^@Wm>>0mBdw4C41OptyS~V`4eeHyLvI|*b@%j%}D4M+=RpaPPuL`L+Z4Xyy zDJSk|_7~gxn-3FS;S6TB_5+)7WK8t^XWYK+j#GCp-YmA^z$O!2=lw5INVRf<0%Gsz z>d@uFNWvB<+x^-zI=}rj5hkkIQu66RsY950P{AEV@@i^CU#J%21-c-2Jr0L8m9+~g zV`Azbl9L;}9{uoD;)xXzs9XL@zmF1h4z;wBdFz2d{R`+KK!q$2dJu# z^XDrk^}b5+(2=URNv6TuFsQNCw$I=I#eY@NcJuz1DJ<>^-4!IrJ@k88W|->GG4l@m zT@qXHMIRo%|F=}?Bdl@Ona(Yhi>sx28)Nd3#InC?QJ0qtx`izW;dNo}+4+v|*}oyQ z{{kOO`G#FOIJohjW<5MS40hoCnKamvR8Q29BH`d8tRg_|Pn9iug$z%olQ?im&IL3n zFB|xWLS;|vebaJb*RLE)|Yea^Npl*D9 zm;M*&D1*;lf@1t2i(LWq>?#;@y&P!`)U-NlC>Qi_S};uYS4GBpwv61yti&wEWW7JuOON%S)AFCP$GZV-qVvWw>(XqEeLhrEaA5$ zJoI|i2JnvC)tRM>_nWPxMGaM}a%ehGq9R{wu7#!=A69R*HOz8X667vAyj$yg9ogxA z%pF5=Ghq_ghVymncBnxBPG^ZmUrmWHKm8to2UV-MUsIp!hz8pg{d%+i>whz2-#Gnd zZfV(zhLTZdBL0+$-c9YCwyy2_vofqOFE6on|L3dH_WL7?e7Q(M2gqIq03bX!B*F^& zU!I_=JY~eVzJh-VtbKKze$CWPDq8v+&B_EjNQ5d3r&VZczdih0;w(`h@vfwn^xt@S zg^R1Qscj9tYhkmv+IF@ulDj6piG06$*(mhKT!!W?b)Yh>V?Ct)r|}#uMaV@&f`C&u zQU|I&e<-HqB#U}VF_||lVZ4EH=F}9)a>TYyg{?7u(2w#~&%dt;$2tGGnr3R*L}Z4) z>&;?5P{1rG+bK}ny|puAGt8n}?`WkvvqA>=-ZA)Ft>&O@i-&Z=Rv!peZx%?rea0p~ z9q+fsC!~MH|BY_G(CX5H{ouoPm2LFt2c;`;D#JSsZ#tFObnkGMl7%q>yh63qvr=;K z%C96*aA_b%ft~65Tcv0h;gLMAE`?@%!H%_%fsElKV!&?9(EOM8%%?s+pc*T);qr^o zUm^r{c%*Sr(Tbk(Pj}!rSJQj4MQyKF1Uw#A`^o&zQ-~7@1%3x$S}=VHPtmKo|2C*M zBR$5OY5yROU_^VPEq3+4(_{uD70B<|mhY^U#Yu2khO>(cHh|YLv*p3z#9CS@1|1Qz zI_bvdCg7%kn&QiQ}k=33bR5ph(TT<;uF^K(op$# zE#z(coTBX~O2xU2?W-ZYU~H_8n70h4in30z?PIvX!+60G5fQ0fkq^`W1xwS^MCvG+ zqgusewzS^rO-X+wj~T+bQHYvI_SCfzzDj25InF7XhjT16dCDPy@&X^xGo&A*EUl}d zQE34e$k9&rq3Sa|w%huONbO^eo!n+6#wNv&-8tC%*)VnuZSdH?%&sdNhnIt=qn;dt zB6Q$CP=()lDtXj&Qi8L1)yuVg-%73)ZNI9Uq&r5F1j8o$ykO2NQeWnJ2LyF)_~{1K z2R0V1Q7yU*gX?0?#N?-Hh&g|)Fs_!C@=oj>*|A-9zguXp@2bG{^)-+^Pft&Y6}=CxNMxn`iJ4;vq)iR2 zP$M%<0g^D9^8kmMCn?fjbAmSoI~A~jY9Yu!Rw5Q0m_Rk6e=7l&=!c8MZaJr%^i*2o zbUKplzV1;Uu*|N_-CdsPh=EvB-Oaf~mkt7|I&2FQN~8}wc^v3#Vbl-3IoA+2c09}t z*lCUT7T0DY^?9q@P*zu$(%C87Tyg`ng8&*YITcmuz9b;>#AYV$937Q3RvThOr1<0B z%?y33!2tLVIWv`jY3qNz7;>2rrvV0YTCSb2J4;547GGwyoAZ0}&1){)+vnPe<&0s7 zXs?Cn)lN~;QWEl+j5o(l(7NE0-~dfLKZPixk+nY7is!sf9Kx6NmaviZ!1>-SD`HZC z@;~vg4OVTqH=c=eEs1!Z9rZaN>>{L^q6}wUfK4>ofV{pNgEeL@zWOEe zxj^b!_DC;Ix#K$ASkvh`Cv(4yX;!Tmualpk)AD!lu>=2#OGMtEBRi_QDRr^ZG*5El z!0?-is=|_yn_fp1J0s(s2E@>b5F4o{$7Nl?l0|6W{>5*j+3 z4yUub_e|2@McB>h zDa=i(@!tQIy{E*|LplAtz~O=0{&&k$WNA66M+TZftI}1lChpQxEAlNa6%+crA3Uvh z&~C<7K>1)H-$)z{91HsGo#XI~`n%za({>L_sk4##zZv0jH!;bU67iSKHdjm)Ty%bF z6~LapD+tj;qmEPI$znpF?9D_^J`XRh+5Vl*#*;>JoC6wHyaT8g=VhK0bEImt1 z0CU_d*gTBX-9*`3?;x4UjU88`Rj%$MX3aikWUT*@UG9pMTlJpf^(RQp%gZ}Sa)7gA zde2N!v~v&?sT+6*1>ko#U?@!)xZ9Pwa!k%*P-T8KppK%Fw0=C|Z20k+3dxJ-fNEEaU zwDFS?WZz8`UluP!p8hPkK4?tmrHIb{!Ho3+g)8)2BVQ-LT1|?uX=AKI2{5>UE<-@>cPU`IJ6axCHMn3HqCSc~LW2y^T zJ44D*j1LA7Whr^*K)K$mdao96xF%wu$@X&>z5W37$n+7CnO~Icoy@3Dlcel={831H zLHl7L)}-^E&X;nH-ucX--RU@`nm(U_Q2U#sbys1%Bg5!JG8XdNcPssey-3*$kv@S- zpxBIOq~A~s%^P!5mz&}d6U#0sVd3WH1{7Qte*Tp7bW$^eTxI;hs!BO_dtfnwH~#4&XiyNHL~9Nw!}fDXVR zx|moJBJ{g6>44FX^j{+MQN8nLhinDHwW~*RDTSf-E(t|?=n)*Y)}J{}9G2uW zM>Ae=D|Kn36+I``AA(-Xi{06scZ~bKzJ8sl15kYdWuOVp z-zpH6OH0q-t6^vMXLurcqV@4Q`|PwhQvn{)o8)$JgAw>ZLWcfhGrR}(T$b!oI{ zbDvbQt3|G|y4?2Xg(bP_T=Z&C!obJg33xraSiq%%YxLvYl&WOg{@Q5liw1MOJc-;`y&mmKhoJU>yR>^o)CY-j{XVvx!|M(FVuQ>U%3%FT3Iu^)$ZSQ8(7E2`sI3bQkU#65t`lo&xCHwpPOfO0T8Lp8 zMIey#T>R)|#LDbrHyQUGDYUAJ$I3_{*bIzaQBxYNPp++{Ff$!lK6Fd1$0mL{^<357 zCx%${+vdy)jdcAnXZB^1+dKy?c#ke29*D~*L-gYAj%FpsSu zsWwI!DwKlg_k)Q{D#~p8b1VkX9Z{g8%L%O-Wg%dy4k#Lm9u4of|Mo4P)o}>%?q_$4 zS767*?=HdT)lXS}8od)-9uM>sx1^cN-oGFlb};9>y?5yARsQ_7ajDN_AB*}pbbfGv zTE5I8V%;wCA=-Sd~rqOX#|>8USHEaWqOMi9@0{38>WA%s6hy)5qUW64?f zCrVCUbN5qB#0xn!x`+*O9~GCFzR^AGu6NeeU0vfB!%W6{P6{m)SK>;2%72cHlGqwns#|Rcvc60_aSi1cR3Iy#X00`16|bY5^+t<&4^gZ& zx|b3pnP0Mp)9m>62ZPOIY*`Qm_6}i~>lvW(H-6=Uvbz1$zM+p73Dm7Qo4I6412j+R z5&X^+dXAQ3ABTUhDLYUL&9o6*l?mF<_t5-=y1^JE(h0mTpZ_2@rx?~T0wAeOh#&7M7l52+HgQ^dJ43|BqXbSQ>pSqe04(M!^3t OKpHB#%C(BN(fj8oQ literal 0 HcmV?d00001 diff --git a/plugins/system_preferences/show_plugins.cs b/plugins/system_preferences/show_plugins.cs new file mode 100644 index 0000000..b39f6a1 --- /dev/null +++ b/plugins/system_preferences/show_plugins.cs @@ -0,0 +1,15 @@ + + +

+ + + + + + + + diff --git a/plugins/system_preferences/system_preferences.py b/plugins/system_preferences/system_preferences.py new file mode 100644 index 0000000..df86e14 --- /dev/null +++ b/plugins/system_preferences/system_preferences.py @@ -0,0 +1,16 @@ +import CryptoBoxPlugin + +class system_preferences(CryptoBoxPlugin.CryptoBoxPlugin): + + pluginCapabilities = [ "menu" ] + requestAuth = False + rank = 20 + + def doAction(self): + return "show_plugins" + + + def getStatus(self): + return "TODO" + + diff --git a/plugins/system_preferences/unittests.py b/plugins/system_preferences/unittests.py new file mode 100644 index 0000000..5e0e862 --- /dev/null +++ b/plugins/system_preferences/unittests.py @@ -0,0 +1,8 @@ +import WebInterfaceTestClass + +class unittests(WebInterfaceTestClass.WebInterfaceTestClass): + + def test_preferences_overview(self): + self.cmd.go(self.URL + "system_preferences?weblang=en") + self.cmd.find("Preferences") + diff --git a/plugins/user_manager/lang/en.hdf b/plugins/user_manager/lang/en.hdf new file mode 100644 index 0000000..3d99c44 --- /dev/null +++ b/plugins/user_manager/lang/en.hdf @@ -0,0 +1,51 @@ +Name = User Manager +Link = Manage users + +Title { + UserManager = Manage users + AddUser = Add new user + DelUser = Remove user + ChangePassword = Change password +} + +Button { + AddUser = Add new user + DelUser = Remove + ChangePassword = Change password +} + +Text { + NewUser = Name of the new user + DelUser = User to remove + ChangePasswordUser = Change user's password +} + +SuccessMessage { + UserAdded { + Title = User added + Text = The new user was added successfully. + } + + UserRemoved { + Title = User removed + Text = The user was removed successfully. + } + + PasswordChanged { + Title = Password changed + Text = The password was changed successfully. + } +} + + +WarningMessage { + InvalidUserName { + Title = Invalid username + Text = The choosen username is invalid: only letters and digits are allowed. + } + + UserAlreadyExists { + Title = User exists + Text = The choosen username does already exist. Please choose another one. + } +} diff --git a/plugins/user_manager/plugin_icon.png b/plugins/user_manager/plugin_icon.png new file mode 100644 index 0000000000000000000000000000000000000000..e035d2b1c3eb33e618d627fc86bd09afbb26562d GIT binary patch literal 10613 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 literal 0 HcmV?d00001 diff --git a/plugins/user_manager/unittests.py b/plugins/user_manager/unittests.py new file mode 100644 index 0000000..d2622f2 --- /dev/null +++ b/plugins/user_manager/unittests.py @@ -0,0 +1,27 @@ +import WebInterfaceTestClass + +## this user may not be removed +from user_manager import RESERVED_USERS + +class unittests(WebInterfaceTestClass.WebInterfaceTestClass): + + def test_read_users(self): + cur_users = self._getUsers() + # self.cmd.showforms() + + + def _addUser(self, user, password): + self._gotPage() + ## TODO: finish + + + def _gotoPage(self): + url = self.URL + "user_manager" + self.register_auth(url) + self.cmd.go(url) + + + def _getUsers(self): + self._gotoPage() + self.cmd.find("Data.Status.Plugins.user_manager=([\w/]+)") + return self.locals["__match__"].split("/") diff --git a/plugins/user_manager/user_list.cs b/plugins/user_manager/user_list.cs new file mode 100644 index 0000000..9938ecc --- /dev/null +++ b/plugins/user_manager/user_list.cs @@ -0,0 +1,82 @@ + + + + + +

+ + + + + +

+

+ + + + + + + + + + + +
+
+
+ + +
+ +

+ + +

+

+ + + + + + + + + + + +
+
+
+ + +
+ +

+ + + 1 ?> +

+

+
+ + + + +
+

+ + diff --git a/plugins/user_manager/user_manager.py b/plugins/user_manager/user_manager.py new file mode 100644 index 0000000..088060f --- /dev/null +++ b/plugins/user_manager/user_manager.py @@ -0,0 +1,81 @@ +import CryptoBoxPlugin + +RESERVED_USERS = [ "admin" ] + +class user_manager(CryptoBoxPlugin.CryptoBoxPlugin): + + pluginCapabilities = [ "system" ] + requestAuth = True + rank = 45 + + def doAction(self, store=None, user=None, new_pw=None, new_pw2=None): + import re + adminDict = self.cbox.prefs.userDB["admins"] + self.__cleanHDF() + if store is None: + pass + elif store == "add_user": + if (user is None) or (re.search(u'\W', user)): + self.hdf["Data.Warning"] = "Plugins.user_manager.InvalidUserName" + elif not new_pw: + self.hdf["Data.Warning"] = "EmptyNewPassword" + elif new_pw != new_pw2: + self.hdf["Data.Warning"] = "DifferentPasswords" + elif user in adminDict.keys(): + self.hdf["Data.Warning"] = "Plugins.user_manager.UserAlreadyExists" + else: + adminDict[user] = self.cbox.prefs.userDB.getDigest(new_pw) + self.hdf["Data.Success"] = "Plugins.user_manager.UserAdded" + try: + self.cbox.prefs.userDB.write() + except IOError: + self.cbox.log.warn("failed to write user database") + elif store == "change_password": + if not new_pw: + self.hdf["Data.Warning"] = "EmptyNewPassword" + elif new_pw != new_pw2: + self.hdf["Data.Warning"] = "DifferentPasswords" + elif user in adminDict.keys(): + adminDict[user] = self.cbox.prefs.userDB.getDigest(new_pw) + self.hdf["Data.Success"] = "Plugins.user_manager.PasswordChanged" + try: + self.cbox.prefs.userDB.write() + except IOError: + self.cbox.log.warn("failed to write user database") + else: + self.cbox.log.info("user_manager: invalid user choosen (%s)" % str(user)) + elif store == "del_user": + if user in RESERVED_USERS: + self.cbox.log.info("user_manager: tried to remove reserved user (%s)" % user) + elif user in adminDict.keys(): + del adminDict[user] + self.hdf["Data.Success"] = "Plugins.user_manager.UserAdded" + try: + self.cbox.prefs.userDB.write() + except IOError: + self.cbox.log.warn("failed to write user database") + else: + self.cbox.log.info("user_manager: tried to remove non-existing user (%s)" % str(user)) + else: + self.cbox.log.info("user_manager: invalid value of 'store' (%s)" % store) + self.__prepareHDF(adminDict) + return "user_list" + + + def getStatus(self): + return "/".join(self.cbox.prefs.userDB["admins"].keys()) + + + def __cleanHDF(self): + for key in self.hdf.keys(): + del self.hdf[key] + + + def __prepareHDF(self, dict): + ## sort by name + users = dict.keys() + users.sort() + ## export all users + for name in users: + self.hdf[self.hdf_prefix + "Users." + name] = name + diff --git a/plugins/volume_details/lang/en.hdf b/plugins/volume_details/lang/en.hdf new file mode 100644 index 0000000..36a95ca --- /dev/null +++ b/plugins/volume_details/lang/en.hdf @@ -0,0 +1,20 @@ +Name = Technical details of a volume +Link = Details + +Title.Details = Technical details + +Text { + DeviceName = Name of device + Status = Status + StatusActive = active + StatusPassive = passive + EncryptionStatus = Encryption + Yes = Yes + No = No + Size { + All = Space of volume + Avail = Available space of volume + Used = Used space of volume + } +} + diff --git a/plugins/volume_details/plugin_icon.png b/plugins/volume_details/plugin_icon.png new file mode 100644 index 0000000000000000000000000000000000000000..d15b3b3b0c8cedbf0dcdf195f4409420739b4acd GIT binary patch literal 11235 zcmZviWmFqo*M^5+!QH(@3&q``xVyBtQ{0L}aCdhrQd~-Lch>^N-6>YwzC6FaKW|oM zW@T0;Idf*qy{~;DRFtIAkcp8206>$Kkx+vkA^&Y)1n9m`*mw+jfO8R-)c`|ZK47zO z=w~EH8C@3ugktjF219;2AOt;0Tc|84!FC!Gh5r+xR@F{nln2%TW0+g zBnALVKvqIb!z24F$1@x6cg9^;Y5CvTyW90?w24_;@%LHpq97)EvX~$%P7Aid@h1zD zXwRe!v&4*%C;p@vYKuf7X;Uc<-3c>=`S7wbi9msh!lj8q(K!;dwQ29WwTj6n>9z9e zwi|InD^$TeWjD>7a$!xk+>HmJDbGpgkmST`=f9Ecvn-AVQd*O+o1|&i0PoPAATa5~ zhXHZX`yZ!-{{hV}w<|)1_q_zj(vL#ImG35RRW2+=p8lYOV9E!uiHEaO^%LeRfK~A~ z2*6a+ZbBx6Qc=|>Luh?gzqs`mv(WmM90&((ri7ntt%5}kDw7(}fxOp$rO?Z@VJYy$ zD*;+C6@V$eDTtt=i6^NVE>(2c8e4rr7;5YgdmUeWQbQbiS!hg{1-(Y(jG{nUuwu6o z=>FKW%B~HrL(riLZq@)71pY9K8w#2__%z3fE7@~!osg7ddM-qc5m|_dihu+M`geOf zJ2&@9Ss5ciCfJJet6|iDiIUMgNoXAddia5X|I5uVn4m9wI2^Q@l=r;*j;epHTdqRN zCR57Zp4qP}?{a5|!+w=_FqTZ-z<}JoX{Gu>P0oAFl^i|n=I#1sg`ej?gqFx-Wuhv>$6&F`h zwmJ9tZR8+86apR8<-!*Sr+{iGlBy8!F7u~Ldf<;wvH8dqP zHGPCB;Njt4>SgJ1P0rUlPL~^_1_!11`T3dV92FLCYBaUAMf{dgtb=!d!fp zu8xh3ML+q20ky0cSVZ*a0lvsmG=OK-G3Z_JJv!|kV{vgYlTIz9#pOUvMJ3+3%b4(+ zVpFC|K)jh@QJzra!|4yk{FaF*doU_2>Zf-!IyEf8jYzogxbV)aShOje9L3E6k-Z4d z)k?1V(sHtXe!_Toc-&9CJ(v*^W##4DzqJkbh7;aP6se;2Eft(RP9S2CSBiXu=N5PV z4Ak1MwW^ATzu}8lVv4icEpw(a>57}17u%J7tZTn9VhuMYyl+OZaO5bFl`_FCEXZne$+(BjNksnm>=r7$_ z>+*A*JR6feZN!du_x9M_jy2@uP&|`E(Zvpb5+vRM4-R;1+DM?bgM7SaNMgP zCfE^G^f1j(oD3|XtJHk zjw1>&G%lQ}lQV>x-ziybyi5!zcf9+S<-yW4=baR$(kYYGzN%ldP&G93%T#vl4tlrA zqVKfc#}Gp8M|7uIx)lR4!vky< zt6!2aw|O8o^JS_`2JPwaDEOOqPiNkhfiVLnhpm$d7BM#hD1Y4O2|k4hc5ObI+v86y zq^lO>f9Ta}#Y`WPol5x!(+vuuQg2#N3TnC=@-WvL-Q18(8lrcCsWe{5}&l(c$JjT`&O6R~M>lPYe=!}Wps=g*l&_{Yb`OFyk~G&MD~ zD393sScT{2=Q*#o=GBuc0i%gbt`za`D$$wwAL=c)%QhMOZeN$Zi<8@17snh77^rbo zhwu7E$JX>(x^nB-8(&Ck#On=mHW29MOj)LKkrsu{ImnmBuU z@-b>vZuX+EC^So};s#>|ze9-YOr5gvejyAs8&Kn+Xd4fa#wn@iVAv3Qfn|ft;iBTh zg$92@3BY)@PjH*K9r3Zo$-u5~JEzh|3(>NJU#= zdy7D1xoE&EcTp>OUold3>+uI4;XFHZbW+~#P&5)=j|M*pv=GMl>n+f#~(^!dH z(co4@os&xsolYOVG(Bh8z^gTT9_F+tSViJE+sXI1>N>1nc5X@9cMm?uAbIh_QWY#Wf3CA?2H#CUkal;60i9^7XP&6IAvTeWt`7UStXn%epU z!ALlOV0TSqWTapp{Tv5#DIDZ?g|;$mCrqvDks3aWLG;kv-5PTmO59Li-M5f1o&#f< zp85yo>M#3Z^o9O=0=U+MT2%@lvDOKvHJK!3T1+!`44fhB)#ll#6Z%?D1%i?C$)gP4NA^DUT*8avXT)-sPIDBsMbd!1ycgW5~yBCU!6;Z;z3kd68!7N9Xr&aAU$j zf3qeN-b*jFx!0ftW@nS8GV6~n)tSOLLogJhjAQ_v#;+-WA8N@Gi$29hsCex$JwFe{fc_p`{*I2V_BBYoa(TnNc1qhK+&D-eT+mg%6KR^?^ zEhbb}1VIzowsQ-hb+EthCzut&I!A!vXpiCE@kQ{)+D|ld|^}%MKId8v?MFTW7 zTmH?;XRw<`YEr*#T|Z2Eg*i?G&99FqLfv=3{&+^;PwP4C>Vty=Q^JdXEB3*l21_fC zj?3yVYAEws;af89({AiBrJBtN1}!6=yaCDR9!G>D50eXi`=|uuJSw~xSC-CDt&*9Q z1;fG3`t4MQxn&po>go!hg*mA&_&&$*$r!Q{APddh<2eG0k1_yMfI#iNDoo>ZtLD`; z9M0g6Z(=14=SYo$I4PyquTMp9uC$bj74L{_VFLk<)chZT3`tzUfZOaxh|%5OHHNA{ zr=3B*vD3=b0$`ICBAZ03s;X*ChfFm*HAROTLU5ZN!f_2Ez)sM5P=ZI5csa`EU)kay zrKOq>vc8t9ehGpL$U%=LtJuC{DD%X#GEJRXUsqC8j6s96w+mWKW?RR2HxKSyMo$%X z)-wlzx^ET@4N;l2aYL-w}bm)ova%#z?mGpo`wX{aTy=B-Uhyi9)1-bDGX zY7rS1PsN&S!j8a>C`%%)!X_&Tc2w>_!X@y&J*^v}mSccE3}onZXg&BbpL`xGL`{X! zg88hgOJr_tjzj>Rodm-6+kn7#Mpx7yO<`zPUT~js1AcKVzoPc!zg{UAQ@qz48Bs{d z)>5lfQ=y-Bmb-}lqQpx0y|{S5A^-`8mN#oCZz{L7o+Svr`!nR~${DI3P9+rQ#vRX{ z0_?$8*LbVs3BNXHKZyLeCAP**1%-1eB*dqRJ&f#AlYwV5KVVPc!IVR;*puJ|P4Qu- zr>BizyH9S|O$SkJsITCe(@yG7%RhRvQvzDmdKp1{=1~mlB}=Lz3^c-d7?2J{P=Tlz z!m;wVN3}y<*`81ZO`YXh2meKOfHjBV2m@}?hiR_@Ir@W|3Z7$JW`yH|m94FUBLbaL zwiSFRU@xfnNb(Cc6s8*1r${>Pm&ov=OxH%7eRqHSf!ObXk$leY4pdtm((??Pc;nbW(>L$5a|4qtL)oM+GKUK^`#K$ozPVi*4s^_)oaAV#)7;2_j!?TKdL+;k>mmq}OtA86@FWGpK`S(FU>jDl+YHj6guo7Cf#|M;Zr>ze zJQ!VK{V!N?&nAwR%^vWrD;LJ|^YMIOWrl6QwM?_|>{v zyNhjMCS0JIdOQF$ApuQO|L%i&@+VSTT3T)^&GJn|?V=&5a1du!7^4Dy7(fWS0J2EN zB4K$}s(d6)TMtQeMVB>#bS88q&iCXe;yTgN)QUP!xbK=WaIAG}(BYK5K)MgO=JcEp zzz9J|eu<{-!HV#fNvN4H*+4D6=^^?F_lu33+L`vO#P38g(XVNY-#-}e&T(Xb!klv& z*FBkKM9wLTuAX25+)l64nGPqpEQt_>#i+5y_B=1`=Ply;TbvqPxF=u)ocX8Z$jQeCqZ z7Du@Fa;WbydK30le6DQY(*X!3Ag(_Y;iZyp5rq(P+yBCb1*khh>~JS~OcdZn@C6(n zfjXUN!8js}IHJqXK}U$ZLa%1t=cGht1Mt~HBTPWVuXYjH3o;qCPSZTWBUe^nxF5tO zWPlWS2HfJZkHJClL&tg;Hoy%lQ8W`wugytJvv|ffQxo)oIua9!yld}9Aizjn_{RZ5 zr1Gy5qI6MS^x*ds7J;aM%51hJNWyunn;j>Cuq=|2nDq&U2>}qsMV)q_JbXCFI|l;m zbjEM(t3DEQl`1_5QfjDQY5~J7G^IbRXZ61xzgatN6mDD>tX2dv9_g0%`5&FBQHK`nkWLvR!A?c%>RmI@X|h0&p+${I4-7RILX8<9DtI*S1F!?# z1^or-0cIQGJk)E(qJ!X{+$9q7D=RBG059VrExCfBD4D3cJ5PXU_bcy7UkQHJ$={#1 za&44PMYFD1V|9P#a{_vw#@-hZsgONjIJ~|hmad}b4*A|8+C+-Zz zeW@x1YES-^IeM@YB6wikk3;L#!^6YcAvlB}G4TRS9OTtzO+G)J<-BoFEbPhZ5x_r%qtGK-1(VPzYqhtdm(fpDFZ}1ds)pFf78wvt#4)JlM~_>7 zfC_wZ(|=PLgaNogbdGq6FsG+!}Nu-2LM7;SU?a>Z*6{F!qT!-h;#gNm%B-4 zV2QXToJpvJdhi5DOTo|tzcASs9_a9>?1Q*OF6Z{=S?HfQ>N)sfm zs8-9^S3(2tk(iS6{2Waw<9E(E#AOFk#F}^TkF=`If=af6@(&Agdtn$9`%pxub(!JI66!;8FTajZWA=Zt)Aa#9Ieh#9%Tb`hh8-J!~VD%A}ILBaxJ zp|}g3LfDoib=US>i3OZ=!hRT$uB7%^QXu6MaR_g+J>xo3C=9n{=LjwRgV5s{F)f3L z@gs#B8SU77`zxVZ+<#D}?G}Zqtg347aHdErV=WJ!EJuro6X3RAT|#(9>GnSFNMmJL zXGP7-%$&we*CiBHFDT%c**a-Tjp%<$#7ov1IZA&wA#`*luQUZl#TJ{MH^23{<%Ukj ztKIIwfx4EK1)~TqveRZajF-1JOe`>ECa+p5kFbJVyGz%dwz9lj@^_;vZ+qLM?XIG# zDkd=zoAzFi^H$0_s+o5lfkW!Xb4ts0gZSCH2^Fv=oX_J`khgTn9E#-3fmxvGDSK9< zO;r@biga_o@~6uV-*w%(xSC+XsYZdR>Tvu?f=jgXBJzd+`A_nW5{NrqY>Jg$-}Q-7 z8r{L?Rk81eywk4we1k;QkHg2uSNXXy68cF0tUD+vp{tiDP?>;#8t^{Wt&yECWUIow&ckNw4rWa+LSNW=sG9my|z_ajT!Kwzl@~c$9B&W@WV9iCr+iDEM7v6mkVKpaU){FDHYU z_z z{ZYOji_@;%7MCaZCVxx%XJ~(ogzwKxZ_$Q9+}BqGb8)our>R#l%->ro6-S7qD_tpl zE8Ou=#{PB{adSckMNb;q+Fx|vPQoM~93xSITCbbq4KS6kwvNs)p0=JClyFBDE-9!O z7$~W#Vhw$QV)IBg>gcf`H4(v!?YsLClnum92VpOQyR|EQ&Kr%oB|0{t!6x#KF#nYc zuuM;y$ZOMTdz3$?NH%LyC z2i|?5qAfV^Ksy({&I41=l`7NG(G6+&QDPcFNjfyle5rAK3zL0dMFj+(P8(p1xKzBM zaqrvNa!Rd2Wb%IEF%-tLYx58^$L^?_XmE8(Yw#c@`CLEN{K@tHC4q&!p?7Bv6xM+e ziEk~U;i-cFq!@jb4QnZ^xW9jTC+Ykpn6S-`=YK%Gna<-yI4>?T61j~6Xs#(MJ3OxR zo3>6h*6gs%^(^s(qCm!gDl{~-5QCaDC>>o|D-1RMuV(?R2}43c6x2yPE|;{P-qIAl zBqv&Gx@eX^p*uw?ISgCI$z{8@e0LTz^y4~b^*44!D=JQodXl$f#%f#K`$Ey4 z7NL&~eaRK-_U=%hb~%`AT*L+%aDJ4Q4v{KgS2nyg@ix)ZbdCADwfw32AhXK&U6DY1 z+Q-u5G@rNW9Fal5dj-b}J}Cfmu7C!xgW+IXTIly?c}~o|#lO0~CKB-z%q*dyH=N_# zpU9Gi8X+`~H>C}3w0m+$$C6%x8S>gEc3SSLWz52`Ep=JioePqM`aYi9%4ckhHrSQ4UUx^5Mn*;oN=p&DHl|Xv4I;+&de6SO{A)RAKI*uepHX`iiQrR3 z$S2@m$KJ-mBX8CjE#deKev$ds&a!z_n)iCP+-Sr2J^8KPa*7;EbJC#=7-&ha154Ap z#k#OL%RyU+0jOo-ZV<7P2?10^q$^E9t?q_YOLGy0^w5w*6>?RTE6msdolgtiys4q9 zy)ma2z4krlIz_Nn=aYSXkS5qiKCEkkoh^?qUjK2gLf>ETDa^{=-eRja}T`dwrQ-SXk~d>9qaB4sD~^Ir653xPXkI5K%)@v(DEYCCV~PRgxf(nbyaY z&r6yoChjldHBV@+&!_MemZYmD%bzNwiH(Nnc!u-Bp#_4$xJgfOYeRQxl>RF@iOO3T z$YE>90H)0-;-4l;`VofB)Rsjy%TrFM!FasA-0`^{VxFCy-NGp|&?M0*?_Z6vC=&wP zmmbjW7&Q+MLEco@#`)0s-Oi1dC-mzhztPzmwh0OKDOa|Zkd7(-$e$^1G#F@t+&`vW z6;#rjgtOZsVDg}(fEFFZ9+Fe`)$|oegz)0Vl869nlBYjOQM6O_>#QGv-UQaL*xy(& z$3GGHJ*}RSH1HLSoRyhBPJCPl&&BB$feld`^+qQbl;I>cf4JJybL@c!Q-u$h@LDSB zdNa^K@#{~{5HS%Wkmylph9B*^6IHq4&UD6v7#)G+jmi{;jhlKy(jTQSUeM1NtsR%1|&(6j#S0@<#pG_1ZDh3R)5 z@UwuUp_`i89QpCSeXOAi;7d zTsCgLdSIFVsDJwQS80snWgVXixjOM`rL;JN2F4MNA)nNc5FxOfTk_x^a5cQxP< zv4x?_c-$*CU)wWG4eJDM!Ec8=bLKw^`H{a=7@~c7cu&KuxHIGMO4zlC2%6c8(NFr@ z8H9-O`c3i6N*_XPetG1HMXYg8`z#y)7bFH1{YO?+rc!?xEzTANsFq+0=;8YI@gsWS zh2<7CSkH&jKE+zRu#cpr_7v5C6K?Q7^AnK+-`jDGj^=5i8JQ>!|2BLcOo=u=3c22~ zbapCvV*YzJ!NGdKi+>>ZVU#9nzubzLJ~B|ABc1z}b&O>-P}`0zWn&E?M-?oE-QhOw7~! z;pgtPdOYGck@!rCcV{kDO2r|?D7reWtWKLPllLB)=yP zCD;BTyt@~wYL@)6`q%x_kSs4gC@!p1OV!WJGWZJqPHD+*1?kH~(f{ePy0*+ve_q6)p=(G9EYA=*j-e^mIG96Jc;w zLvKFwUc2s^u?F)i5}-#0N#jPYyYgaKnd_Rlpl-{#Jb;v4q{9dys60gx9Qs}og=cgA zFgOp+-MKK}YHubHw&Fm>xa1eFgvNc{0P?0#VWMTcNf6p zdmlS!m{=KqwS++R7w^Kike|hW9|?c$O0c-$+30Y3&Sp}6%qeSKm^#)*LHCwA0&8^j z9-pA$pQU!gS+(||@G253fyWCURkbdSxsY!1HL4?WJ;L9li}-#$66+_^4$jUQwIaP9RX&#))&HgFcz5e4~|;F^STHw=)oyr_fV6GT*}o%b|JfAyK88?T}2u z8l^i2C5y~&#ojBE)*ZM79WTD41y}lo4s-Lw*!sHbm|d58{9(aO%Z=-Q1zQ~jsnLs>b!{Q?u@3YWw)0Lc($er$`% zcv*CT_Ti}oAJZ`ng;U&xF(6E1rQcTz`?wH(l8C-gjW8-pM!=g~QfR7CampNHNT_9q z9<$C(oeEfTyg3}0f>}imFQy$r?w<-e&mCdKurZ}FbH^eJk(u*+JjFd0kw?V89xn3x zH*8H%b@k6)73<(UR|x|G{=psd;n&L^{>L?~0;%697Fd6LDPFy_p{mBbNQBfE-6W>v zMmyf1VK;5SM~=aXwZ=QPc3j6$$HR{QQ}=vAGyE~N_9Ksd+AklLW%35NS4qzfe zGM?hdWLlh0Xqx`p(D{aXDR9w!)DtTW+x&*#l9X-Jm!CSwUSh{KCi`18uM_XTCy>U=1Xw_|w(Cz@xDR&PJ75@7rIU!Y4 zF2>8vdS>a2{T{2iTY8`sx%zo#4_x#00p9(Y*pENmowORG7HL!GfUWao$e+|g>pkPf z>z*&EO%%%!OSIyj(YtR9B)zq(5O=ivBC^~k-{@)_RgT~?tNmukF0_T1(+>@Z4}|_N z9@XBP@5@?l(*s#v^ups%|M~TSAkVs|ci>$3X8&v0)yoaT0>&k->8>2jS-iuoSj4x4 zr^UBfIWH}H{;MZj;T?IkUuBNPFkjH}D@bx5e4~HuU^Q@vIf>~|c=>dL#OaZfe4<;t zm=<5}oulC6FQ%u058cxjYQsXJSi)z@s`W+Tv5{RObvQk#8b(?Aiw4?$SE;t7_D^;B zUyGFPrd6>8_`e&EdEK;ra0=T%-Z~E{ zh1Y}KyzL$((HNy|9#3I*ThrTII+k_)J6B9&c-?H|fWhKx%E*;9@j!qu-u7wqj=e)& zYac$m3)_>v+B@C{o*@}yr5&I~P}=^0umi_Y2x zaiN2n*lX)K^iQULJYE{CKi|1mNgLeKFmP~J)w7d}zKVZ?eUtX-xdulzBQqSlPtnye zUCt)Ni57##Dju_Vi%k!?a>{62?}SsSgfF|-jZbQCTu;tv$y;>6_x-Z>DZyXft9saX zOuAJVUAZb?O1Q>jT~i>~Vo}F2Kx7=?r27Qp{HQBn#dlfyo*Yp#CEw5W% zA~%g66m~>%>JHGv?Gx3c!&k_InPJ?C`32u9C%G7n25q2?|> zx^rXI*ERnj53yU9(lF&J?2TE1-~uzh{WVmKo^-38ku^hf))F0C0mc^+E_*BFl2|T6 z1|69?n4IMB&w)VDB=&>XY2wk^IEqg8cV&c+IyWfREONSHPw6XRu3H@VnUBpSzzL>^FG6Kj%>(&%EDs z>Td9kqsEQBs!Pdl-g!a9f(^f;It0C^U;6bU#g66Y_F+=nb(Wev#W=^5AOP3)aoOu> zxQ!{x&$@T7#_;jRSY-RG56LNRt#9%Ib#z1aVH_h^hrtLo{)?|DcC z4^lc?7oC&>20|kS6q$zM8I`nf;`KqUHanXi82=8v3YY0^~XD90zRpbn(9iy^qx*N+Tg^Kl7{c;cb| zrl%g>!ZqwM#V<}b1bc1>s)j$WlWZ)HAA5^f&KB%4JSfb?=#D+3j@<>PqX~kT&JMVd z1o1*x);{iy6bPG|0ghk%kU}d#gCELPJbjW_@*-V`TzRgS_=NWTqK+Y zevp2`bJI^ge}CB)J{KaD6P&x%(1Km?4kvaggXS8>1?k5CHlu<*%r|Q4F)Z z3rP&2h-Qu7wv>3ckvP@l*Zf#4EhB{s+{&-&Wk%s=25IaUgtPey5g}9G^T{4a2}V=# zp`VH+X + + + +

+ + + +

    +
  • :
  • +
  • :
  • +
  • :
  • +
  • :
  • + +
  • :
  • +
  • :
  • +
  • : %
  • + +

+ + diff --git a/plugins/volume_details/volume_details.py b/plugins/volume_details/volume_details.py new file mode 100644 index 0000000..40d82db --- /dev/null +++ b/plugins/volume_details/volume_details.py @@ -0,0 +1,18 @@ +import CryptoBoxPlugin + + +class volume_details(CryptoBoxPlugin.CryptoBoxPlugin): + + pluginCapabilities = [ "volume" ] + requestAuth = False + rank = 100 + + + def doAction(self): + ## all variables are already set somewhere else + return "volume_details" + + + def getStatus(self): + return "no status" + diff --git a/plugins/volume_mount/lang/en.hdf b/plugins/volume_mount/lang/en.hdf new file mode 100644 index 0000000..c2805b0 --- /dev/null +++ b/plugins/volume_mount/lang/en.hdf @@ -0,0 +1,56 @@ +Name = Mount and umount volumes +Link = Activation + + +Title { + Mount = Activate volume + Umount = Deactivate volume +} + + +Button { + Mount = Activate volume + Umount = Deactivate volume +} + + +SuccessMessage { + MountDone { + Title = Encrypted filesystem activated + Text = The encrypted filesystem is now available. + } + + UmountDone { + Title = Encrypted filesystem deactivated + Text = The encrypted filesystem is now secured from all forms of access. + } +} + + +WarningMessage { + MountFailed { + Title = Activation failed + Text = The encrypted filesystem could not be activated. Probably the given password was wrong. Please try again. + } + + MountCryptoFailed { + Title = Activation failed + Text = Maybe you entered the wrong password? + } + + UmountFailed { + Title = Deactivation failed + Text = The encrypted filesystem could not be deactivated. Probably some files are still in use. Close all unclean programs (for example that widely used word processor). In case of emergency just shut down the CryptoBox! + } + + IsAlreadyMounted { + Title = Already active + Text = The volume is already active. + } + + IsNotMounted { + Title = Inactive + Text = The volume is currently not active. + } + +} diff --git a/plugins/volume_mount/plugin_icon.png b/plugins/volume_mount/plugin_icon.png new file mode 100644 index 0000000000000000000000000000000000000000..952890b56cc3b8f2f2fc8c88d7b9894200f67183 GIT binary patch literal 4535 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 Yj~|IGcjuIYKUn}>O}#r+NSm<#0TiB&^Z)<= literal 0 HcmV?d00001 diff --git a/plugins/volume_mount/unittests.py b/plugins/volume_mount/unittests.py new file mode 100644 index 0000000..9a3e1ee --- /dev/null +++ b/plugins/volume_mount/unittests.py @@ -0,0 +1,10 @@ +import WebInterfaceTestClass + +class unittests(WebInterfaceTestClass.WebInterfaceTestClass): + + def test_read_form(self): + url = self.URL + "volume_mount?weblang=en&device=%2Fdev%2Floop1" + self.register_auth(url) + self.cmd.go(url) + self.cmd.find('ctivate volume') + diff --git a/plugins/volume_mount/volume_mount.cs b/plugins/volume_mount/volume_mount.cs new file mode 100644 index 0000000..407eb21 --- /dev/null +++ b/plugins/volume_mount/volume_mount.cs @@ -0,0 +1,18 @@ +

+ + + +

+ + + + + + + + + + + +

+ diff --git a/plugins/volume_mount/volume_mount.py b/plugins/volume_mount/volume_mount.py new file mode 100644 index 0000000..4fc3922 --- /dev/null +++ b/plugins/volume_mount/volume_mount.py @@ -0,0 +1,103 @@ +import CryptoBoxPlugin +from CryptoBoxExceptions import * + + +class volume_mount(CryptoBoxPlugin.CryptoBoxPlugin): + + pluginCapabilities = [ "volume" ] + requestAuth = False + rank = 0 + + + def doAction(self, action=None, pw=None): + self.container = self.cbox.getContainer(self.device) + if action == "mount_plain": + return self.__doMountPlain() + elif action == "mount_luks": + return self.__doMountLuks(pw) + elif action == "umount": + return self.__doUmount() + elif not action: + return "volume_status" + else: + self.cbox.log.info("plugin 'volume_mount' - unknown action: %s" % action) + return None + + + def getStatus(self): + container = self.cbox.getContainer(self.device) + if not self.container: + return "invalid device" + if container.isMounted(): + return "active" + else: + return "passive" + + + def __doMountPlain(self): + if self.container.isMounted(): + self.hdf["Data.Warning"] = "Plugins.volume_mount.IsAlreadyMounted" + self.cbox.log.info("the device (%s) is already mounted" % self.device) + return "volume_status" + if self.container.getType() != self.container.Types["plain"]: + ## not a plain container - fail silently + self.cbox.log.info("plugin 'volume_mount' - invalid container type") + return "volume_status" + try: + self.container.mount() + except CBMountError, errMsg: + self.hdf["Data.Warning"] = "Plugins.volume_mount.MountFailed" + self.cbox.log.warn("failed to mount the device (%s): %s" % (self.device, errMsg)) + return "volume_status" + except CBContainerError, errMsg: + self.hdf["Data.Warning"] = "Plugins.volume_mount.MountFailed" + self.cbox.log.warn("failed to mount the device (%s): %s" % (self.device, errMsg)) + return "volume_status" + self.cbox.log.info("successfully mounted the volume: %s" % self.device) + self.hdf["Data.Success"] = "Plugins.volume_mount.MountDone" + return "volume_status" + + + def __doMountLuks(self, pw): + if self.container.isMounted(): + self.hdf["Data.Warning"] = "Plugins.volume_mount.IsAlreadyMounted" + self.cbox.log.info("the device (%s) is already mounted" % self.device) + return "volume_status" + if not pw: + self.dataset["Data.Warning"] = "EmptyPassword" + self.log.info("no password was supplied for mounting of device: '%s'" % self.device) + return "volume_status" + if self.container.getType() != self.container.Types["luks"]: + ## not a luks container - fail silently + self.cbox.log.info("plugin 'volume_mount' - invalid container type") + return "volume_status" + try: + self.container.mount(pw) + except CBMountError, errMsg: + self.hdf["Data.Warning"] = "Plugins.volume_mount.MountCryptoFailed" + self.cbox.log.warn("failed to mount the device (%s): %s" % (self.device, errMsg)) + return "volume_status" + except CBContainerError, errMsg: + self.hdf["Data.Warning"] = "Plugins.volume_mount.MountCryptoFailed" + self.cbox.log.warn("failed to mount the device (%s): %s" % (self.device, errMsg)) + return "volume_status" + self.cbox.log.info("successfully mounted the volume: %s" % self.device) + self.hdf["Data.Success"] = "Plugins.volume_mount.MountDone" + return "volume_status" + + + def __doUmount(self): + if not self.container.isMounted(): + self.hdf["Data.Warning"] = "Plugins.volume_mount.IsNotMounted" + self.cbox.log.info("the device (%s) is currently not mounted" % self.device) + return "volume_status" + try: + self.container.umount() + except CBUmountError, errMsg: + self.hdf["Data.Warning"] = "InvalidType" + self.cbox.log.warn("could not umount the volume (%s): %s" % (self.device, errMsg)) + return "volume_status" + self.cbox.log.info("successfully unmounted the container: %s" % self.device) + self.hdf["Data.Success"] = "Plugins.volume_mount.UmountDone" + return "volume_status" + diff --git a/plugins/volume_mount/volume_status.cs b/plugins/volume_mount/volume_status.cs new file mode 100644 index 0000000..724af1a --- /dev/null +++ b/plugins/volume_mount/volume_status.cs @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/plugins/volume_mount/volume_umount.cs b/plugins/volume_mount/volume_umount.cs new file mode 100644 index 0000000..6002510 --- /dev/null +++ b/plugins/volume_mount/volume_umount.cs @@ -0,0 +1,10 @@ +

+ + + + +

+ +

+ + diff --git a/plugins/volume_props/lang/en.hdf b/plugins/volume_props/lang/en.hdf new file mode 100644 index 0000000..55bbfcc --- /dev/null +++ b/plugins/volume_props/lang/en.hdf @@ -0,0 +1,63 @@ +Name = Volume properties +Link = Properties + +Title { + Properties = Properties + ChangeVolumeName = Change the name of this volume + ChangePassword = Change the password of this volume + Encryption = Encryption +} + + +Button { + ContainerNameSet = Change name + ChangePassword = Change password + FormatContainer = Format volume +} + + +Text.FormatForEncryptionSupport = This volume is not encrypted. If you want to turn on encryption, then you have to format the volume. Beware: this will destroy all data of this container. + + +AdviceMessage { + UmountBeforeProps { + Text = You must deactivate a volume, if you want to change its properties. + Link.Text = Deactive volume now + Link.Rel = volume_mount + Link.Attr1.name = action + Link.Attr1.value = umount + Link.Attr2.name = redirect + Link.Attr2.value = volume_props + } +} + + +SuccessMessage { + PasswordChange { + Title = Password changed + Text = The password of this volume was changed successfully. + } +} + + +WarningMessage { + InvalidVolumeName { + Title = Changing of container's name failed + Text = The supplied new name of the container was invalid. Please try again! + } + + SetVolumeNameFailed { + Title = Changing of container's name failed + Text = Could not change the name of the container. Take a look at the log files for details. + } + + VolumeNameIsInUse { + Title = Volume name is in use + Text = Another volume with the same name is active. + } + + PasswordChange { + Title = Could not change password + Text = The password of this volume could not be changed - sorry! + } +} diff --git a/plugins/volume_props/plugin_icon.png b/plugins/volume_props/plugin_icon.png new file mode 100644 index 0000000000000000000000000000000000000000..20d6d5965679424327cfd1d114a510efc8034c09 GIT binary patch literal 15249 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 + + + +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+

+
+ + + +
+

+
+ + + +
+

+
+ + +
+ + + + diff --git a/plugins/volume_props/volume_props.py b/plugins/volume_props/volume_props.py new file mode 100644 index 0000000..605dc75 --- /dev/null +++ b/plugins/volume_props/volume_props.py @@ -0,0 +1,81 @@ +import CryptoBoxPlugin +from CryptoBoxExceptions import * + + +class volume_props(CryptoBoxPlugin.CryptoBoxPlugin): + + pluginCapabilities = [ "volume" ] + requestAuth = False + rank = 40 + + + def doAction(self, store=None, vol_name=None, old_pw=None, new_pw=None, new_pw2=None): + self.container = self.cbox.getContainer(self.device) + if not self.container: + return None + self.__prepareHDF() + if store == "set_name": + return self.__setVolumeName(vol_name) + elif store == "change_pw": + return self.__changePassword(old_pw, new_pw, new_pw2) + elif not store: + return "volume_properties" + else: + self.cbox.log.info("plugin 'volume_props' - unknown action: %s" % store) + return "volume_properties" + + + def getStatus(self): + self.container = self.cbox.getContainer(self.device) + if not self.container: + return "invalid device" + return "name=%s" % self.container.getName() + + + def __prepareHDF(self): + self.hdf[self.hdf_prefix + "vol_name"] = self.container.getName() + + + def __setVolumeName(self, vol_name): + if not vol_name: + self.hdf["Data.Warning"] = "Plugins.volume_props.InvalidVolumeName" + return "volume_properties" + if vol_name == self.container.getName(): + ## nothing has to be done + return "volume_properties" + try: + self.container.setName(vol_name) + except CBInvalidName: + self.hdf["Data.Warning"] = "Plugins.volume_props.InvalidVolumeName" + except CBNameActivelyUsed: + self.hdf["Data.Warning"] = "Plugins.volume_props.VolumeNameIsInUse" + except CBContainerError: + self.hdf["Data.Warning"] = "Plugins.volume_props.SetVolumeNameFailed" + ## reread the volume name + self.__prepareHDF() + return "volume_properties" + + + def __changePassword(self, old_pw, new_pw, new_pw2): + if not old_pw: + self.hdf["Data.Warning"] = "EmptyPassword" + elif not new_pw: + self.hdf["Data.Warning"] = "EmptyNewPassword" + elif new_pw != new_pw2: + self.hdf["Data.Warning"] = "DifferentPasswords" + elif old_pw == new_pw: + ## do nothing + pass + else: + try: + self.container.changePassword(old_pw, new_pw) + except CBInvalidType, errMsg: + self.cbox.log.info("plugin 'volume_props' - cannot change passphrase for non-encrypted container (%s): %s" % (self.device, errMsg)) + except CBVolumeIsActive: + self.hdf["Data.Warning"] = "VolumeMayNotBeMounted" + except CBChangePasswordError, errMsg: + self.cbox.log.warn("plugin 'volume_props' - cannot change password for device (%s): %s" % (self.device, errMsg)) + self.hdf["Data.Warning"] = "Plugins.volume_props.PasswordChange" + else: + self.hdf["Data.Success"] = "Plugins.volume_props.PasswordChange" + return "volume_properties" diff --git a/scripts/check_languages.py b/scripts/check_languages.py new file mode 100755 index 0000000..3004c13 --- /dev/null +++ b/scripts/check_languages.py @@ -0,0 +1,106 @@ +#!/usr/bin/env python2.4 +# +# $Id$ +# +# compare translated language files with the master (english) language files +# it is useful for finding incomplete translations +# +# Copyright (c) 02006 sense.lab +# +# License: This script is distributed under the terms of version 2 +# of the GNU GPL. See the LICENSE file included with the package. +# +# Parameter: LANGUAGE (e.g. "de") +# + +import os, sys +try: + import neo_cgi, neo_util +except ImportError, errMsg: + sys.stderr.write("Could not import the python clearsilver module: %s\n" % (errMsg,)) + sys.stderr.write("Maybe you should try 'apt-get install python-clearsilver'?\n") + sys.exit(1) + +SUFFIX = ".hdf" +DEFAULT_LANGUAGE = "en" + + +def getHDFkeys(hdf): + import types + if type(hdf.dump()) == types.NoneType: return [] + return [e.split(" = ",1)[0].strip() for e in hdf.dump().splitlines()] + + +def compareFiles(langFile1, langFile2): + ## check for existence of files + for filename in (langFile2, langFile1): + if not os.path.exists(filename): + sys.stderr.write("could not find language file: %s\n" % filename) + return + + ## check if the files are the same + if langFile1 == langFile2: + sys.stderr.write("cannot compare the language file with itself: %s\n" % langFile1) + return + + print "Comparing '%s' and '%s':" % (langFile1, langFile2) + + hdf1 = neo_util.HDF() + hdf1.readFile(langFile1) + hdf2 = neo_util.HDF() + hdf2.readFile(langFile2) + + hdfKeys1 = getHDFkeys(hdf1) + hdfKeys2 = getHDFkeys(hdf2) + + ## check for empty language data sets + for (keys, filename) in ((hdfKeys1, langFile1), (hdfKeys2, langFile2)): + if len(keys) == 0: + sys.stderr.write("\tthe language file is empty: %s\n" % filename) + return + + ## check for missing keys (only part of the first (master) file) + for key in hdfKeys1: + if not key in hdfKeys2: + print "\tmissing key: %s" % key + + ## check for superfluous keys (only part of the second file) + for key in hdfKeys2: + if not key in hdfKeys1: + print "\tsuperfluous key: %s" % key + + +def find_lang_file(arg, dirname, fnames): + lfile = os.path.join(dirname, arg["fname"]) + if os.path.isfile(lfile): + arg["list"].append(lfile) + + + +############ main ############# +if "__main__" != __name__: + sys.exit(0) + +args = sys.argv[1:] + +## check parameters +if len(args) != 1: + sys.stderr.write("invalid number of parameters: a language name is required\n") + sys.exit(1) + + +language = args[0] + +if language == DEFAULT_LANGUAGE: + sys.stderr.write("you must specify a language different from the default (%s)!\n" % DEFAULT_LANGUAGE) + sys.exit(1) + +obj = { "fname":"%s%s" % (DEFAULT_LANGUAGE, SUFFIX), + "list": [] } + +os.path.walk(os.getcwd(), find_lang_file, obj) + +for langFile in obj["list"]: + refLang = langFile.replace("/%s%s" % (DEFAULT_LANGUAGE, SUFFIX), "/%s%s" % (language, SUFFIX)) + compareFiles(refLang, langFile) + diff --git a/scripts/check_languages.sh b/scripts/check_languages.sh deleted file mode 100755 index 502dbe9..0000000 --- a/scripts/check_languages.sh +++ /dev/null @@ -1,26 +0,0 @@ -#!/bin/sh -# -# compare the defined fields of a language file with the english translation -# -# nice for finding unavailable definitions -# -# Parameter: LANGUAGE -# (e.g. "de") -# - -set -u - -LANG_DIR=$(dirname $0)/../lang -DEFAULT_LANG=en -TMP_FILE1=/tmp/$(basename $0)-$$-1 -TMP_FILE2=/tmp/$(basename $0)-$$-2 - -[ $# -ne 1 ] && echo -e "Syntax: $(basename $0) LANGUAGE\n" >&2 && exit 1 - -grep "=" "$LANG_DIR/${DEFAULT_LANG}.hdf" | grep -v "^[[:space:]]*#" | cut -f 1 -d "=" >"$TMP_FILE1" -grep "=" "$LANG_DIR/${1}.hdf" | grep -v "^[[:space:]]*#" | cut -f 1 -d "=" >"$TMP_FILE2" - -diff -wu "$TMP_FILE1" "$TMP_FILE2" - -rm "$TMP_FILE1" "$TMP_FILE2" - diff --git a/scripts/userdocexport.sh b/scripts/userdocexport.sh index 192c296..299df52 100755 --- a/scripts/userdocexport.sh +++ b/scripts/userdocexport.sh @@ -29,7 +29,7 @@ WIKI_HOST="https://systemausfall.org" # the trailing slash is important WIKI_URL=/trac/cryptobox/wiki/ -CBOX_CGI="?action=doc\&page=" +CBOX_CGI="/doc?page=" LANGUAGES="de en" diff --git a/templates/access_denied.cs b/templates/access_denied.cs new file mode 100644 index 0000000..cfe44fe --- /dev/null +++ b/templates/access_denied.cs @@ -0,0 +1,6 @@ +

+ + + + + diff --git a/templates/empty.cs b/templates/empty.cs index a8716a3..0447a4d 100644 --- a/templates/empty.cs +++ b/templates/empty.cs @@ -1,3 +1,6 @@ + + + diff --git a/templates/error.cs b/templates/error.cs deleted file mode 100644 index 18ac8f1..0000000 --- a/templates/error.cs +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/templates/footer.cs b/templates/footer.cs index a784024..6ae1777 100644 --- a/templates/footer.cs +++ b/templates/footer.cs @@ -1,35 +1,32 @@ -

+

- - + + +Settings.Language= +Data.Version= +Data.ScriptURL= +Data.Status.Plugins.= +CBOX-STATUS-end --> - + + + +MESSAGES WERE NOT HANDLED PROPERLY - PLEASE FIX THIS! diff --git a/templates/form_config.cs b/templates/form_config.cs deleted file mode 100644 index 4b94e31..0000000 --- a/templates/form_config.cs +++ /dev/null @@ -1,34 +0,0 @@ - - -
-

- - - -


-

- - - - -


-

- -


-

- - - - - -
- diff --git a/templates/form_init.cs b/templates/form_init.cs deleted file mode 100644 index 3118718..0000000 --- a/templates/form_init.cs +++ /dev/null @@ -1,27 +0,0 @@ - - -

- -
- - -

-


-

- - - -
- -
- - diff --git a/templates/form_init_partition.cs b/templates/form_init_partition.cs deleted file mode 100644 index 91bee8a..0000000 --- a/templates/form_init_partition.cs +++ /dev/null @@ -1,33 +0,0 @@ - - -

- - - -

- -


-

- -


-

-


- -

- - -


-

- -

- -

- - diff --git a/templates/form_mount.cs b/templates/form_mount.cs deleted file mode 100644 index bf5c8fd..0000000 --- a/templates/form_mount.cs +++ /dev/null @@ -1,40 +0,0 @@ - - - - - - - - -

- -
- - - - 1 ?> -

-

- -

:

- - -

-

- - - - - -
- - - diff --git a/templates/form_system.cs b/templates/form_system.cs deleted file mode 100644 index 4ae64d2..0000000 --- a/templates/form_system.cs +++ /dev/null @@ -1,29 +0,0 @@ - - -

- -

    - - -
  • - - -
  • - - -
  • - - -
  • - - -
  • - -

- - diff --git a/templates/form_umount.cs b/templates/form_umount.cs deleted file mode 100644 index 3a5b2ac..0000000 --- a/templates/form_umount.cs +++ /dev/null @@ -1,37 +0,0 @@ - - - - - - - - -

- -
- - - 1 ?> -

-

- - -

:

- - - - - - -
- - - diff --git a/templates/header.cs b/templates/header.cs index 8bbf8a7..425fe4b 100644 --- a/templates/header.cs +++ b/templates/header.cs @@ -1,8 +1,9 @@ - - + + + CryptoBox @@ -11,44 +12,44 @@ - + - 0 ?> -
- -
- -
- -
- + +
- 0 ?> -
- -
- -

-

-
-
- - +
+ + +
diff --git a/templates/macros.cs b/templates/macros.cs index e15f8c4..dacd199 100644 --- a/templates/macros.cs +++ b/templates/macros.cs @@ -1,66 +1,16 @@
-

-

- -

unknown warning message

-

could not find warning message: ''

-
-

-

- -

unknown error message

-

could not find error message: ''

-
-

-

- -

unknown success message

-

could not find success message: ''

-
0 ?>

0 + ?>" title="">
0 ?> 0 ?> +
icon: info

unknown message

+ could not find message: ''
" method="post" enctype="application/x-www-from-urlencoded" accept-charset="utf-8">" method="post" enctype="application/x-www-form-urlencoded" accept-charset="utf-8"> +
+ + <?cs var:html_escape('icon: ' + plugin) ?>
+ + icon: volume + + HANDLE_MESSAGE CALLED TWICE - PLEASE FIX THIS + + diff --git a/templates/main.cs b/templates/main.cs index 33a2335..0290e75 100644 --- a/templates/main.cs +++ b/templates/main.cs @@ -3,14 +3,8 @@ - + - - - - - - - + diff --git a/templates/nav.cs b/templates/nav.cs deleted file mode 100644 index 5c4efac..0000000 --- a/templates/nav.cs +++ /dev/null @@ -1,26 +0,0 @@ - - - - - - - - - - - - - - - - diff --git a/templates/show_doc.cs b/templates/show_doc.cs deleted file mode 100644 index 4f8af80..0000000 --- a/templates/show_doc.cs +++ /dev/null @@ -1,7 +0,0 @@ - - -
- - - -
diff --git a/templates/show_log.cs b/templates/show_log.cs deleted file mode 100644 index e8b134c..0000000 --- a/templates/show_log.cs +++ /dev/null @@ -1,13 +0,0 @@ - - -
- -

- - -

- -

- - -
diff --git a/templates/show_status.cs b/templates/show_status.cs deleted file mode 100644 index 803b281..0000000 --- a/templates/show_status.cs +++ /dev/null @@ -1,28 +0,0 @@ - - -

- - - - -

Sorry - you should have never seen this ...

- - - - - - - - - 0 ?> -

: -

    -
- 0 ?> -

: -

    -
- - diff --git a/templates/show_volume.cs b/templates/show_volume.cs index 61d7174..2e1c94e 100644 --- a/templates/show_volume.cs +++ b/templates/show_volume.cs @@ -1,71 +1,10 @@ -

+ - - - - -

Mount container

- -

- - - - - - -

-
- -

Unmount container

- -

- -

- - - - - - -

Change the name of the container

- -

- - - -

- - - - - - -

Reinitialize container

- -

- - - -

- - - -

Some more stuff

-

For example: changing password? / backup? / access control?

- -

Details

-

    -
  • Name of container:
  • -
  • Name of device:
  • -
  • Current status: activepassive
  • -
  • Encryption: onoff
  • - -
  • Size of container:
  • -
  • Available space of container:
  • -
  • Used space of container: /
  • - -

+
+ +

just a placeholder - any suggestions are welcome!

+
+ diff --git a/templates/show_volume_footer.cs b/templates/show_volume_footer.cs new file mode 100644 index 0000000..301725b --- /dev/null +++ b/templates/show_volume_footer.cs @@ -0,0 +1,3 @@ + +
+ diff --git a/templates/show_volume_header.cs b/templates/show_volume_header.cs new file mode 100644 index 0000000..0a4f47a --- /dev/null +++ b/templates/show_volume_header.cs @@ -0,0 +1,21 @@ + + + + +
+
+

+
+ +
+
+ + + + + + +
 
+ +
+ diff --git a/templates/show_volumes.cs b/templates/show_volumes.cs deleted file mode 100644 index 7a5d8a8..0000000 --- a/templates/show_volumes.cs +++ /dev/null @@ -1,15 +0,0 @@ - - - - - - -
-

-
-
- - - diff --git a/templates/volume_plugins.cs b/templates/volume_plugins.cs new file mode 100644 index 0000000..b23b2e2 --- /dev/null +++ b/templates/volume_plugins.cs @@ -0,0 +1,25 @@ + + + + + + + + +0 ?> + + + class="volume_plugin_active"class="volume_plugin_passive">" alt="icon: " />  + + diff --git a/www-data/background_frame_corner.png b/www-data/background_frame_corner.png new file mode 100644 index 0000000000000000000000000000000000000000..2d0da076b9aaf7764c0f7f71d0d3177dc1edce64 GIT binary patch literal 2355 zcmeAS@N?(olHy`uVBq!ia0y~yU2n7*a9k?OnsXDd8dw7k}6FD&1fR&Em4^3R$_SFNL`zQT0M9bE0U(DW;$eoMwp= zW{6yiV6kK|n*Xt)$j|iHqpArGHC6dv?r**SK0WQ(qeqXH&#bgF_D_HId*{KZ*URGn zX+1YtJgeBxc$FoXp#@|tHv)=)7-=0f15-9UrT%T z_ROA{GsUyl+NY&mtJ^=nySrQ5`15P~zw3hkcGpfX_Dgj$eIAk)QhY8G$e06TfJ9zN zE|(CxpY<*F&!5|K%DLm#ZJoFN`uh0)Hpctw|IGcxJLi#o-|l%}C#BsvH>bE-|DVlv z^W#0|&;MBeKXZOkNX@%1&+ObTUEjCQb^n{RXEz_NcJG%v`}lbO^MlRo_aC)0Gu(-8 z?+>nhax*Wj&Z6e)larHgzAd}|#g~!cj(hf6|HrS|bJO-&*93KC$r zA@Vq|L&YUlW+X_aa6Wl%@2d=)2G+(mfG{uI3oVH6vKo12ixC1Jlwwi zT9khJx}+D6C;Ym!;@9%~>4{S;$DvPL<_eA8#oM}u6{1-oD!M$FjA1pb zfNYG#AwE~YY%W`(6R^|C?5ML#knw7r2~jK+y0SJiVw=2ZJ88!DQdqDXAkGZH5hg4~Ct~mJA@mck&6kuUpie`>z83U9 zQC;|+7U2)!FQ<0Zkvw$Bt=(B#`%aC#mZ;EF0YMO(l&WfNWrezGQ;kOP<$cjgfF(ak z3U-v#8E2fE_jf8^(Yt7w>gd+Av~j&|T>o*4bRca-cFTEbYq{&SA9}9kZ`|lB`TWBw z!;ThhhA$x6liy@%$mLl~=_$=5Ss%FXbd&Yl`Nay)2qk(I^uluo9E0LRLs@*fLVm|i z*-Q%i_G{Gp0)fCmnS01CMJNZR+7qNfmyI&two|8Hx&;2wrnMp8&qh)l;M0FR6m)fU z(SDb8&}=rgwYAY7A=aUbXb1hlAfbRe{OIeiH{!DZl!sS{y6BkFc%PWKtKCu(En>VxCG$4fgA8|z`DAp>X@zQdOLv*OAVc9X8&23 zxu>m*13j-f0~QwTNk=VdFqO;MXSM)zM3IbY)|v`1og?BbFxrRpb7LyUD!xG)MkyP` ziPrxNqx~__{|kpQ-O?#Yjojf+tFOdTg6VEndk|&X z#CbPSmyV5M+qLM~`^w6giwvFDj=jCX8WSC%QS3W`4a-`GtMieTcG+dfwoB&VD>AHb-ejqgT2|mlnHBTHQH-(zLLXk3}(r?65 zQIAtM zyy2GtE>_maJr8ukk!`$`&lH}&DN#3>!KbGhCedrea&MGsI8FxmTGIi?FiBeAiy|p^ TZz!lnbwIMSsNigV`Qd*66>Yk? literal 0 HcmV?d00001 diff --git a/www-data/cryptobox.css b/www-data/cryptobox.css index 3fc24c1..1a9807f 100644 --- a/www-data/cryptobox.css +++ b/www-data/cryptobox.css @@ -1,20 +1,27 @@ body { - background-image: url(backg.gif); - background-position: top center; - background-attachment: fixed; - background-repeat: no-repeat; text-align: center; margin: 0; padding: 0; font-family: verdana, lucida, arial, helvetica, sans-serif; } -#main { +a { + text-decoration: none; + } + + +div#main_menu { + width: 130px; + } + +div#main { background: none; - width: 600px; - padding: 0px; - margin-left: auto; + padding: 20px; + margin-left: 140px; + /* RFC: 'auto' is not a valid value, or? [l] */ margin-right: auto; + /* RFC: should we restrict the width? [l] */ + max-width: 700px; } #main h1, h2, h3 { @@ -39,71 +46,16 @@ body { font-size: 1.2em; } -#head { - width: 600px; - height: 120px; - margin: 0; - padding: 0; - background-image: url(antlogo100px.png); - background-position: top right; - background-attachment: scroll; - background-repeat: no-repeat; +div#head { + height: 130px; } -#head_green { - width: 600px; - height: 120px; - margin: 0; - padding: 0; - background-image: url(antlogo100px_green.png); - background-position: top right; - background-attachment: scroll; - background-repeat: no-repeat; +div#head table { + width:100%; } -#head_red { - width: 600px; - height: 120px; - margin: 0; - padding: 0; - background-image: url(antlogo100px_red.png); - background-position: top right; - background-attachment: scroll; - background-repeat: no-repeat; - } -#development { - font-weight: bold; -} - - -#content { - margin: 0; - padding: 0; - width: 600px; - font-size: 0.9em; - min-height: 300px; - } - -#content a { - line-height: 1.2em; - color: black; - text-decoration: none; - font-weight: bold; - font-size: 0.9em; - } - -#content a:hover { - text-decoration: underline; - } - -#content a:visited { - color: #acacac; - } - -#content p { - font-size: 0.9em; - padding: 0 1em; - text-align: justify; +div#head img { + vertical-align: middle; } #menu { @@ -127,7 +79,10 @@ body { } #words { - width: 565px; + background-image: url(background_frame_top.png); + background-position: top left; + background-repeat: no-repeat; + min-height: 300px; padding: 1.1em 0em 1.1em 1.1em; margin-top: 0; } @@ -155,17 +110,20 @@ body { list-style-image: url(list.gif); } -#footer { +div#footer { clear: both; text-align: center; - border-top: 1px solid #5e5e5e; + /* border-top: 1px solid #5e5e5e; border-bottom: 1px solid #5e5e5e; - background-color: #ACE149; + background-color: #ACE149; */ + background-image: url(footer_line.png); + background-repeat: no-repeat; + background-position: top center; font-size: 0.8em; color: #5e5e5e; } -#footer a:link, #footer a:visited { +div#footer a:link, #footer a:visited { color: #5e5e5e; margin: 5px; text-decoration: none; @@ -173,67 +131,161 @@ body { padding: 4px; } -#footer a:hover { +div#footer a:hover { text-decoration: underline; } -#confirmtext span { - color: red; - font-weight: bold; + +#volume_pane table, #volume_pane td { + padding: 0px; + margin: 0px; } - -/* -------=-=-=- warnings, errors and success messages-=-=-=-------- */ - -#words div.warning,div.error,div.success { - margin-top: 20px; - margin-bottom: 20px; - padding-top: 10px; - padding-bottom: 15px; - color: #707070; +#volume_pane td.pane_top_left, #volume_pane td.pane_top_right { + height: 39px; + width: 10px; } -#words .warning,.error,.success { +#volume_pane td.pane_bottom_left, #volume_pane td.pane_bottom_right { + height: 10px; + width: 10px; + } + +#volume_pane td.pane_top_left { + background-image: url(pane_top_left.png); + } + +#volume_pane td.pane_top_right { + background-image: url(pane_top_right.png); + } + +#volume_pane td.pane_bottom_left { + background-image: url(pane_bottom_left.png); + } + +#volume_pane td.pane_bottom_right { + background-image: url(pane_bottom_right.png); + } + +#volume_pane td.pane_left { + background-image: url(pane_side_left.png); + } + +#volume_pane td.pane_bottom { + background-image: url(pane_side_bottom.png); + } + +#volume_pane td.pane_right { + background-image: url(pane_side_right.png); + } + +#volume_pane td.pane_top { + background-image: url(pane_side_top.png); + } + +#volume_pane .volume_plugin_active { + background-image: url(register_active.png); + text-align: center; + vertical-align: middle; + width: 120px; + } + +#volume_pane .volume_plugin_passive { + background-image: url(register_passive.png); + text-align: center; + vertical-align: middle; + width: 120px; + } + +#words div.unavailable_action { border: 1px dashed #808080; text-align: center; - color: #5e5e5e; - text-decoration: none; - font-weight: bold; - font-size: 0.9em; - padding-left: 40px; - padding-right: 40px; + background-color: #f0f0f0; + padding: 10px; + margin: 20px; + font-size: 1.1em; } -#words .warning { - background-color: #f5f5f5; - } - -#words .error { - background-color: #f5f5f5; - } - -#words .success { -/* nice green color - but no one likes it right? - background-color: #90EE90; */ - } - -#words div.warning,div.error,div.success h1,h2 { - color: #808080; +/* -------=-=-=- operational messages -=-=-=-------- */ + +#words div.message { + text-align: left; + margin: 20px; + padding: 0 10px 0 0; + border: 1px solid #bbb; + color: #5e5e5e; + font-size: 0.9em; } -#words .warning,.error,.success a { - color: #5e5e5e; - text-decoration: none; - font-weight: bold; - font-size: 0.9em; +#words div.message table { + width: 100%; } -#words .note { - text-align: center; - color: #F48659; - font-style: italic; +#words div.message td.text h1 { + font-size: 1.5em; + padding: 0px; } - + +#words div.message td.message_symbol img { + width: 32px; + height: 32px; + padding: 5px 20px 5px 10px; + border: none; + vertical-align: middle; + } + +#words div.message td.link { + text-align: right; +} + +#words div.message td.link a { + color: blue; + font-style: italic; + text-decoration: none; +} + +#words div.message td.link a:hover { + text-decoration: underline; +} + +/* -------=-=-=- environmental messages -=-=-=-------- */ + +#EnvironmentWarning div.message { + text-align: left; + margin: 2px; + padding: 0 5px 0 0; + border: 1px solid #bbb; + color: #5e5e5e; + font-size: 0.9em; + background-color: #eea; + } + +#EnvironmentWarning div.message table { + width: 100%; +} + +#EnvironmentWarning div.message td.message_symbol img { + width: 16px; + height: 16px; + padding: 2px; + border: none; + vertical-align: middle; + } + +#EnvironmentWarning div.message td.link { + text-align: right; +} + +#EnvironmentWarning div.message td.link a { + color: #55b; + font-style: italic; + text-decoration: none; +} + +#EnvironmentWarning div.message td.link a:hover { + text-decoration: underline; +} + /* ----------------------=-=-=- Forms -=-=-=--------------------- */ /* pretty forms and buttons */ input { @@ -293,9 +345,9 @@ button:hover { text-align: center; } -/* -------------=-=-=- volume selection -=-=-=-------------- */ +/* -------------=-=-=- main menu -=-=-=-------------- */ -#volumes { +#main_menu { position: absolute; float: left; right: 5px; @@ -303,47 +355,22 @@ button:hover { width: 140px; } -#volumes div { - height: 80px; - padding: 5px; - margin: 5px; - background-repeat: no-repeat; - background-position: center; - } - -#volumes div.active { - background-image: url(disc_red.png); - } - -#volumes div.passive { - background-image: url(disc_green.png); - } - -#volumes div.current { - border-style: dashed; - border-width: 2px; - border-color: gray; - } /* ------------=-=-=- language selection -=-=-=------------- */ -#lang { - position: absolute; - float: right; - right: 5px; - top: 5px; - text-align: right; - } +div#lang { + /* position: fixed; + float: left; + left: 30px; + bottom: 10px; */ + text-align: center; + padding: 5px; + margin: 10px; +} -#lang a { - color: #acacac; - font-family: verdana, lucida, arial, helvetica, sans-serif; - font-size: smaller; - } - -#lang a:hover { - color: #707070; - } +div#lang button { + margin-top: 5px; +} /* ------------=-=-=- documentation -=-=-=------------- */ @@ -371,54 +398,161 @@ button:hover { padding-top: 10px; } -/* ------------=-=-=- special things -=-=-=------------- */ -#partition_info p, #log p.console { - margin-left: 10%; - margin-right: 10%; - font-family: monospace - text-align: left; +/* -------------=-=-=- plugin icons -=-=-=--------------- */ + + +div.plugin_menu, div.plugin_system, div.plugin_volume, div.volume { + text-align: center; + margin: 10px 15px 10px 15px; + vertical-align: center; + padding: none; +} + +div.plugin_menu a, div.plugin_system a, div.plugin_volume a, div.volume a { + text-decoration: none; +} + +/* remove underline decoration for '#content a:hover' */ +div.plugin_menu a:hover, div.plugin_system a:hover, div.plugin_volume a:hover, div.volume a:hover { + text-decoration: none; +} + +div.plugin_menu img, div.plugin_system img, div.plugin_volume img, div.volume img { + border: none; + padding: none; + margin-top: 10px; +} + +div.plugin_menu { + width: 100px; + height: 110px; + background-position: center; +} + +div.plugin_menu_passive { + background-image: url(icon_background_passive_100.png); +} + +div.plugin_menu_active { + background-image: url(icon_background_active_100.png); +} + +div.plugin_menu img { + width: 64px; + height: 64px; +} + +div.plugin_system { + float: left; + width: 64px; +} + +div.plugin_system img { + width: 48px; + height: 48px; +} + +div.plugin_volume { + width: 48px; +} + +div.plugin_volume img { + width: 32px; + height: 32px; +} + +div.volume { + /* FIXME: float-left breaks the background image in firefox */ + float: left; + width: 64px; +} + +div.volume img { + width: 48px; + height: 48px; +} + +/* -------------=-=-=- help -=-=-=------------------ */ + +div.help_link { + text-align: right; + margin: 20px 10px 10px 10px; } -/* ---------=-=-=-=- onscreen help -=-=-=-=--------- */ -/* not active anymore */ +div.help_link a { + text-decoration: none; + color: #555; + font-size: 0.9em; +} -#words a.popup { - line-height: inherit; - color: inherit; - background-color: inherit; - text-decoration: inherit; - font-weight: inherit; - font-size: inherit; - } +div.help_link img { + width: 24px; + height: 24px; + vertical-align: middle; + border: none; +} -#words a.popup:hover { - text-decoration: inherit; - } -#words a.popup span { - display: none; - position: fixed; - bottom: 10px; - left: 9%; - width: 80%; - background: #f0f0f0; - padding: 10px; - border-color: #e0e0e0; - border-width: 2px; - border-style: solid; +/* --------------=-=-=- new volume pane -=-=-=----------- */ + +div#volume_area_inner { + width: 620px; + /* margin: 13px; */ + padding: 20px; + border: 1px solid #ccc; + border-right: 1px solid #ccc; + border-bottom: 1px solid #ccc; + background-image: url(volume_property_frame.png); + background-repeat: no-repeat; + background-position: top left; +} + +table#volume_area { + clear: both; margin: 0; - } + padding: 0; + border: 0; +} -#words a.popup:hover span { - display: inline; - } - -#words a.popup span p { +table#volume_area td.volume_plugin_active, table#volume_area td.volume_plugin_passive { + width: 120px; + height: 40px; text-align: left; - } + padding: 4px 0 0 10px; + background-repeat: no-repeat; + background-position: top left; +} + +table#volume_area td.volume_plugin_active { + background-image: url(register_active2.png); +} -#words a.popup span h3 { - color: #909090; - margin-top: 0px; - } +table#volume_area td.volume_plugin_passive { + background-image: url(register_passive2.png); +} + +table#volume_area td.volume_plugin_active img, table#volume_area td.volume_plugin_passive img { + width: 32px; + height: 32px; + vertical-align: middle; + border: 0; +} + +div.volume_spacer { + width: 30px; + float: left; +} + +div#volume_name table { + width: 100%; + border: 0px; + padding: 0px; + margin: 0px; +} + +div#volume_name td img { + width: 64px; + height: 64px; + padding-right: 30px; +} diff --git a/www-data/dialog-error_tango.png b/www-data/dialog-error_tango.png new file mode 100644 index 0000000000000000000000000000000000000000..31c4475377bacf3e61d048e6972afcb1bf9dc2a6 GIT binary patch literal 8750 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 Yf1x5B3Pmw9(EtDd07*qoM6N<$f>BVL^Z)<= literal 0 HcmV?d00001 diff --git a/www-data/dialog-information_tango.png b/www-data/dialog-information_tango.png new file mode 100644 index 0000000000000000000000000000000000000000..3a4f370ca1cdf70c0eb1c31d03d1f07263882fbe GIT binary patch literal 14120 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 literal 0 HcmV?d00001 diff --git a/www-data/dialog-warning_tango.png b/www-data/dialog-warning_tango.png new file mode 100644 index 0000000000000000000000000000000000000000..c1afbe3890c64fcc6e5317fe57706bab33b20857 GIT binary patch literal 7611 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 KMNUMnLSTZ@^O|q~ literal 0 HcmV?d00001 diff --git a/www-data/disc_green.png b/www-data/disc_green.png index 887bd547dba8360d00600f1f5239bfcf32bc332d..0d0c48315d5c6b6782ac84ab76e2c9d33bc82b19 100644 GIT binary patch delta 4899 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 VY74l|MKJ&X002ovPDHLkV1nD>l|KLg delta 4713 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 diff --git a/www-data/disc_red.png b/www-data/disc_red.png index 7ee121a8fd21ab113147e80229f25c9f51e6f9b2..699336ad2409275e4af8aaf58a9fb88f70356898 100644 GIT binary patch delta 4951 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 s9Cw0S_Q6MPyZxT8H4c{FY8P7m2fnJ4N94FCWD diff --git a/www-data/evil_stick.png b/www-data/evil_stick.png new file mode 100644 index 0000000000000000000000000000000000000000..dc4ffeb3cbefef7a7572b030006bc5f7573ab9e1 GIT binary patch literal 32626 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 E0N6)d+W-In literal 0 HcmV?d00001 diff --git a/www-data/icon_background_active.png b/www-data/icon_background_active.png new file mode 100644 index 0000000000000000000000000000000000000000..a930c0db1e4b2749ae97c0b3ec471aca60fc9f01 GIT binary patch literal 14372 zcmYLQcT^K!u-+6xmjsj|J<^K^ND(Ok=|~X-L=aG_Qk7nl00JsX2LXX7AR@hqQiFxw zdl87D(jh?TA-w$F`{V67d$ydjXYbC=eD|9>^F1>&(PL(~$N&HUvw^<0IRJpDr=W{; zFzSJ#@hpdWfcj|}+@_;$;dIXN)N6WQeH%Xjp!@mX4$5D4P^I4F^4GcJZ{g$WA9VkT z3lJ0(borsTho94ZUzf{1Puy}gRWAa70AQf4c{}*cMub|$WrHX8#v)i=ehQPi|6v*{ z5SOHh`3bT{)vA5*uY=h5y988fT^EoB`48~(&eb{by8cSOv-USJ3?R8X^SFJ z=KP<*aSjY;9(xO=@nvg`kq5mpZDhBQZ_^8r?8#_&a^}M6X3*KGk_P$TL1|gOb9$!b zMCR%O^~5E8Pe)tbhJ*3T>vWUdo_7T3-u|^_+#vGBSJNK0y+1Y23$eW$#WnIiS5*?< zHGox!;fdRmIvFxGU5#Bk5--9-$Y+!-jnf5<)BT0WGoLeKZOSj|7o8TH=2H%H_#=1Y zEGGun8e8MLjJCH-ku|E6)0&FXi%+9Dbs-wiV==-O)c>Dd4L;sPbR=O=hpC=_thLVe zm2N*{!-_5CLd)NKf3GO574w`4y=d-|aBxN>TiQD(TiduK>#*Cn9Q~qx(>4p5{Egqi z!QqTTeRJnl>&Yg%eC>FpdIo*hC88vrFq(@&TMs3|PWsG+RjgUDb@5mc8mL|DOGDTP zbPOGnTJNADb%3IX5Jd81+?fp0#|AI4widJyaj=Cx>UE&>E=gko?rA8EoO+EbnLg*_ zrwLbD-U1&cj=iWF5Jtfj#LZ#|-RD%DeL0YKGXCy1RyeTR*z^BA(WQG4-=z!4P>l{N zJ`r^=co-=Yk#Jb&sf9VyM;#b$T~v-2dh_CK)Lp4#3A-<~!p)?xXYrb8;CrgKo zO^r*$+$`+c3Lg#rx+|0WO)hPO(9@z#L1n8@{e!oeDHoW8qB316k7<1`MSY)E-Hu6XTcIUsCS&C#ec3=GQrh9})q(UFz*~_RS9DaX$ugs^ zgKlR9e6zJ0yhX_TygRn&%W}SOto~AXfi|HVmJNm5?74v!G|vrL5NIqFr?#Np+DX9| zs_I}P`t8ZQp3aV&V#+z;{bd6B&n+UE}3w|R4XGC|Y3y?;*vSJ&g zrnJ4l=kD(1!w!RBWL|pVlR3cg=@x60=ylXusJLN_jrNs~QmAaq$_mAkAc~EYAZ*e3 z8kM~+fF|qEJ)5H>YbV*Wqm1!gA2lF{L7)g2Dj)F&F+317G~rOYtBzs9?O=qW)@yKC z%%p4#Ki{F$a)Bc+bpI_hAp0h*k^5Ku%(kV1+KbPj-2&Mc5=6jt24W-LU{}o9G$#Mt zF%%dnnsmP(ip-76Q33g6wz&)@wbvrBqTExIB|n9sH+GC=g78Cm3|e%E79SG7_$bNk zSztdmi?II)Ld&rW=x6BK5x5D>qhL~)rI0@ov7xbd@#(4%;wWkhDp3#Fxm6V8@G)+z z8D+0UOw!b{f{R3frZLk{?GA66aR2&zXz1tjJtL zEodtTlM~a94kDx;e*@6P380t4(PFFf=MCu)azpW`;Tz$+WTZA+oKXsl0L?>rnpYD6 z%FUr-FvUb*5WW%K{D&Y2&0tbFuGj&Dqg-sVw-b9{iudQCKOt5fSDQjr>z<}Flc#ZK z?v8Fn0hIP2P)4#dCXE(>43>(0TZ2z!zS0Kc$=e)+sej;rk@c%+Ba|Axxr-X8Va3F& zra?Dgsqq50*@J69b@Z>RSK~$^7!M&ryb0Q+>HgO~ zrZ+yY#&}0(8;mPNpKS>GwaZe2@$iynJ}6kIHUwP#$%aMr+VY-5e?=dYt~1SfgmW5= zqoOYoYpm`=8_I$U8b>1uP3Gi3DUQlhYTM_gHa+IP!Nsc-BJMlwGfkB_!Vw=_K(Cqo zjiT(?d^r!dpkyu_KWN{E`MW`3vM$3sET2>O>&tk2E;Gq6iXHR;&so3f2B5z`$_GL@ z2yR$a+q%#6Pd;o#JiD!OE{bWa4A{%kgicSRDut9!*$J>x3Y?G#jersd8T=DT0~dhh z>k_3*f>o71E3viU7`n?nu)udGb|$|;XX&MpB18zTO`0ITba9Af>c{Yu2kTb>Ieo#X zj{RRmPau-5Y80v}9@MdHd1iy%0!1hiDtkCzhZH#f5{Alf^$A4j+YtI;@ccX_P(yWs zr3TxfZ-fRb$p$cng+i141+cgIciax&c4Nvm-l}WH&^1qE_UC~;5l1ffrNdmSQ-8uB zv}H8#@Y8+AicNYK1SGkmsT(NUFrD=%8i=pKbK1s8(W`3EzKYTa7;gg~vVqXEl&F1n z+a=|jOal$4xX%*@E<8I%@b=sq&}SjX$a8NRR~Kjw`yOq9INjx)HYB=;LfyNv*U=89Rw~ zd-52n<+AY29-nYOE(p>l!GAiVoM{A+sEbeMJ@K_hW~r!BdS&6iXm-LN?DQUX6H!Hr zI=QF=V~fYxuq)Sqf()wmXg-})=3o%S;zF_kfuEjyb;uOB9&Tz9yA&rXeW!pHH7)O) zqyzm!iS+oH&g`54okf2@b2|HhdD?nEYA_s%<4mEdW8BZ;Gp6gc!>$j=^BdAJaXBNf z<5E27%)v(B$;+s~mMt3e)j2&>RK-3AX8zQsX8PS+oV^kcDN_d~imVEUXu~fvrceju zfH@L#5A4a`E~&QQi7&s9*_o-=kau8%A25`>?xf$I%Ij~{8*%49m(kK2Id+A@U<&9&vBEL;gf;6jZg8`j_xa84DPOAJgABeLuke7dWez+z>}3ZNCH zT1KAd2tUnUM`+PqJ^HClSYZKPwkD*Ygt5l2w?MB6ZL=TqX{Z5;wyG*%6@I#Nt*T34 z=ug2jfxGNXQuG3>s31@*W}#_yf%z~mH1a&b4Q}v+AG2?ptf?%?X$(#F#9Odl5yGs4 zgWJ)WmS{Sn&5#?2jU->ze#johcLPWLfhETlJr0w5nVtFOg}7j-5At_-nPNJLZ*d=p0_n;l;6V$5)KN<$(GbP%RQSK)*=MA zu~Ricl$~TFJxCZ*XiX3Rf0U}K!IrfGD4vsMbGwt%TEY+9?aoG!qDlO4P%(Mf|BQj4 z2SN*XDGdLX19G+K=rH`D;{RcNjbW4!D?E815qpQ|fytt$aN${^BJb!v)_a&62fmUH zA8Ls(QZj^6u|tgBr2fm_N-;kzA>tQu{@2GoUoD7PdlWsOm6lWs4ECkgkGuThr`KfGhst z=*}d3xx@+_Ug_ZTYgXMrXp!YDJ9g58T{xs_By$k|Xqhiu_ZdEi**OKZrK%bMLM-IN zaz@s<>sI45R7Zy1p70lS4h`VZg^0O_-S#HGF+ehH{v%h#$E&Vu z70B6o+QQwvTFe%x^4q_ZR;|((KeO-dYLbA)z?PN(17uXt`cVz&laSdlxD>QMzs*KZ ziaA0iage`RP@dsUZ8zvoufBWsT){0J%zqP+FU2LMT_vxXMAztot^dg3l$?Se=kZKN znR1*WhvcI)NW;m7qBMuN%K`nHLWVsK`KdF$lIysmx*dTRbagdU$4l@&_f7~Cj&N5# zthJylfAj%qaFKi;k`hJ)X;R^wa;P3i8l9>OHWM1U-6sU{<0eWF6ZMbWKxYy|gF=aA z-@0xKLlW17xu zhJx(v|I7Z@3DnJS12xzZA+tg2=bspdZl2O&(RX^bR;~$r!kGjYwLzx1ZXE+@E$Li1 z8%^4--jgb0Xr3n^1VR+ZBx!=n%^g$uK*5m*>P%!qsS1vikK1qYZXmKJMz65C$3jsW zU*}IHk^Qi{%v$d{lyah_=t%=_b->bK94g?#VddRD1v#yVE4$aohcEmds36e%7~z=b zOnqhe;pOOFa8XBb4K`WG@lZJlWehkl9?H-}LP;$i>Na)u%*_%TFp7u+mDWxi_$nq@ zvfAAQBh62Fra+Nf+y2Bb>(eI4nFz+MF!Gg{7RqWn)?N|uVHz-F?Gpi&?{9U;{3gqq z`3SdMJFK;eU^pD$LrD-Wap^fX`S|@3gkF_IJ;O*bYE9#5gsM{64Z%6VkxaKu9<7RC zHTFh-2SHk`Mw|!ado&DKjcC06z;&Nw{7tms{jfJezVYotrh*_5T|}n?t#`Ru5Tq?= zJQ0k(x~vyHCt3bEev@=!!4kRXu$!+5E%$URrRRBgMF0Ad0dAaSD7^X2E3uvWDD*w+ zQTBd@iGX`%M4!l8XF;r{>$CNO6~Zcc$5L>`xfE{!?+?=9hlA1b^$%f(VFKMIN0&;~ zcO7p$j~Se9eb)jw?_DmIw0a~a>FCg0?WrcLIk*tEgNbNM?1x?iZ!w|Jd~cwa%SsPF z^H6a@1NAzsuZO8F1m<(Hfe%>U;{PWZ4|uZ%8b)2Fcc)C~`X%XaHFaKU98v=DYblHv z<`@tX--lRG`Nn0()W$H!u7|>EP_5>-uo)CO)OOb*L8%sG>fp#q@6R2sn=tpDAo7m` zdza;^MOy;2t+->i;+PBXra1`Tp&7|;Gh%pVix$@&0%usVlI*_8A1P5wRG^7t~HXySdU|qWXt+cZPNdQbo$>ys^;2=@^Rcfz=#?4W-p%p!nB1-!Fr1 zV(RggG{f0WLWgSNXHW54$1ULX1WW{G2ajcXr(HFd%7?(LL=*~fnt=E0Vi(>=O3x_b zidQDbg~LD@o5F;Kk%&JR@6m^LIQY+2<_#htS0#aG_|Z27LG-K!0)?}zb?2A8Anzwn zdBaS12N)@4J(cChS@=YZdg>J^hi+SRoC;4j0~2q(6!0kgqf8PCT>ReAwB`h~zCfd_ zEW@kxYR|vHDewIRoJb`i2tpVpEWiMGPk!&^5oWB9BsxRpQv{-+q1CUZxT^jTC9X6c z>#b4v4qtw$0M5(@ZD(x0-}(``<8tJ_BB%IVsz<|n`}@?e+V>mwYpF7(`-(eXEW(4? zb%F|#ikNX1;+FgU_{{p4l5;XV_cErLUwxv_c zxXpmzv+B|M@lMIbe9$Q`;PXt2?mhFhb9=N2)mcJ>?leJG$FBFnw)9t2_E?loQ78Vy zXuna(RzVVE@eM7zX^YK#@7GpaHjiEZj<=UTTd~$1gKhqzgnM%&()WYH=&bCPRUaDT zpRjIKYu(YB@e+xqGVIOJNL}5boanh@+gC7S;=@Sp#om*l7Jb~~ghvc1&c9MD=;1b} z1$%vqpNm5jX~-|r_@9sJ`!$^BH`yw^Jv+uP?kSaCG2@e^Qo^Uj`>X=k zxv>qpm`iD|d%g^Bfv%+Ly$az}`0MIcOokEfV}gsEdjGs~^Z(mQ2^;ZfiL!m^@#*CD z)(ui|Q}bU;i7ov&OMDtQW8SD_O0FV`FXcFs@+{ayvCGann%{f(Ei-mV3f8n|J@AzVQ z2ETN9Wk-_>=oV|46&FIB_PJA7CchN-lsRzR4renxR>U2e8(Y%-UYQQdN#1;)f{|3` zWC<`XD1GkAl5)c!{NH4_LfZv|Sqt`}`^&|T$_6O4iKNjG;kD8cDNt1D*|L+kBY=`) zB5SL8zTdOFc15+wzmN8`nx!{WH^x(hgNThU=D)J@`k2S<0ML4=F{TlMP%UZWAU`Un z5>XpRJ(>j3o6sMpOzD2>YA-S8&VS0vomj)R(sLTOw|t`CHKEa{in3X(u?fB;67WUeN7bi&#cPk26iX8PxVS<^Bpa`By>?wp?fhkJ%CmuI zZ(baGL+`NGy*eNrgwWsHk_}Ae&eBoZ59SRn>|a?g8=w_JY_Dev8yR>5{E- zzx4QCFAxzMap~N1x&%+0M*)u;2Y9f0Y-!`8l)wz+^XO{+Y8wqcSu#aY=236ox7D{N zqv@Ob{2nDYCJx7}170k~&DiEN#0NMt=$!cc_}Qdewq>m415Uy7*?3fI|1mhtNui1K zxS(To@9V0iz-26bCGB-;O5Zo%6laFR$JVdY8FL3-uF)G} z+l^DgoEMxh(?PWov~Td$7ZUj;2HQD7=P$pOZB!0C|6P?%63UbWhZY z8x~nG&j&ls6L7};c%uWxZ0&GfAC%w!woEf@33j%}7*6GcGSYu{YsEYO__Ly4uJm6! zpw-B*e})@B{Gsal@p@y$D+aFo(AN3govEx@45Xc}-f!+`eVqT^y^EceL`Z3*&-$Nl z#PkQVl2}8B=X34s&5=mRriJ5ugSp4AT{3q(@Y8`GDkr|BujFr+7q#@+S$*81X%>!O zD8CBt^cZBp25H_^{9{+OHO%EM7umN|U02`$eWajz{=Tv%5#8vt zpuc67me+2~SKhZlt$iU~=IM(94NRsX9B2Z2asPVi#?j{w6~4YmN*A_Lvl-tmm))`s z$SSXiZdFLUG136t8f)U*iPLl~*S^(le+t<>2SQ)+a)?J0o_y_sy}ue*TZ_5g6R&Iu z7Rc796A(9f68f*kLdeUuMD${Covw{+Bv?U^*7gNcf~+aSbyATu@>LaYisq~E3I*Q% zFrZ)+t@~Pp_JU7fzs>OkEK8l%EX-%uC8&rc3$&J4me_mkzTg)BsT2!7USeJyC@|Us zbl)4+TJICSe73OW_%K*vKd*m*4VDWkjt#4=O zBXgBWHkCb*Fz8&!(8X5K*p?%{8%wW?&tw1ob8)&;}k zFh)uOAIhhUb-#W(teM`at5&@YY`^ z5XQ7~xL!Po#Br~K&qM}DC!gewTF*7PW-UGLWg6%~uA`A+5QQ9T{GdUygmB)2B?APP%DNcuW+?^kkuLn99(?984Qb`A3`Gn>LyLqDS zH63A)(Om-CH7neq^E6m^Th%)Y6#SP9lauuzy3cY ze4w=Qw!z_>g+4yJbVRm&>scacsfPQ{$wRrfz3L1qhA`G4!$Vg-!1eFbW1!D)RuBt8 zh(%!TP0F&ciQaG})ze0>fc9#oUN;C}+`inYPcT`!@HpCHzP7c$Nm##lKiyzd)&6HoY zD>T;6buR0)^IscG-fOcK8OVH9N=*zr>k2oyE-5$(1(XM*`m~BZRsZn~!xO@RCvHr; z=!YeV&i9h?Ov=M_EJ*BXz-?VQulHM6$@^Q%yZA5Km;NA_zxMHI8s+`9xhTB?_3cb; ztFjUOS{c}Xk6wn}nFOS?n>w)Q9u+8Y)H=PRC@rrMNzlEV`p~sm_&VxX6J@Y%pLzB5hont_{4PtTYA1`Ht zbCkQJGkZ2f^IEeW@ZF66AWi&+6<3}|cU^%pze^o14#`D1E*I(D zOM+x8Hn+H+RMq#;J4ucG$CF>+ON;PIFkHF#Xzg1MK!OzSD|h(c5Fj-oUu zXp1q&Gg8vVHCEnCbG*6vh3(f67mzw&pv8|VNtD*)f3MGeh43r(x0cnaRQ11XCJJd> z1Mr-rpgTFrJbvK1u|VDzwnsm^d?S`yl6k`##XnC=_kt0cid+Jb{EKsukXkzUw#NAC zLW<;LH&;MOI<~)8P_{>dYs0^@scNB((Z$nb%k?&zp{T?@HNA)OvZ;PBaI866rpPr z@5BXd^$jbBEa(;`%U2-1ehS(Fq@sZSu{AY@yfexlr{mIfhK+h#06@G+Zb5 zyP_)V(grWLE_h<N?JQLf^i%`|JR5(EHo1 zYSnd9#g;GH(0SzT^-%NZKWZ1uJ*UU)s-H%%DYVg58;xd{E(MWR7oO9+gdlwEk`DkJ z?VP0hy%s5Isk<~}Uz!zIx|i8t(0AwgBC-Bq(!yfZInqvS^yJUc`$|8mSwk+qZ;~!Z zd{5cq!Oj00%-Q?$RQ#D#i4n%FEKHRmt|oW_x_NErS|lJ@Q#0`KEbAJ#N@kS93waCE zLb67!PLxTJpCVF-pVA>iIC3 zubfZe2-%S}kg5ke!26=((m{5+BWv3KTe}o=;XqD zh>YXQ#8u0$n?LeD(b(?pKM?d^$wSFbsEV!~dn(oJ(pN`6@=6uE>PW}C8)wq9Z6>1P z`@`7g^ipVcA^D_p)_RW3c0jCvHr3ppDXbDT)8;;*eED7C;$j z{xyc~Sgo{Ex@+h#qbSfMtYvDl{L~4AdDJSlC z`#9>rlm#A-M-&S~-eZY1s4TzQ40KXkltv&|Q(7U&Os-`)-#k}iS1@Qi2={9F4 z%)&aRZW4=&Db|$~WFU)UL@j;9??x`|e{OYQyO$j`a4M~nOBiqQwHmc_X9q`aw0uOE z;sxHuL{?$!pDzR-F3!$kY@33$X^?hCHlgwa?~Fb_mP>Pm()~h7fer3vV77u6Kjei< zYnEMTBaL&j6$QPUT9<`4HW`Z$RODRtd<;^7p%;O}vY*c5UN!uCd`O3Gt3-l7^YI%q z{0Zk4jK#FQWIVb|ll_2A9e&L#4yjJdXOX{1Wn}L^ZNN_vMSwEL?Q`*U4gcy=@Ks(b zo`(NI+*!OPB)`Yv=K^9l4f8*Ev5@<|NVl72NBc1^SdA4L0n!3+l=H#w>BgMranBxJ zzl`jkdcN4z%ncujN1d}HDvl|PoH`qZHq06pKf5q1RB$d>jo?61kt6MzS!r-wiqr? z%M;Px*QN#hUW$O6ZqDsq-srx)hoWOPCnkOCj@<1BT3;c?-dddwO!*Qg{kS46xlQec z#~q-{Akv7l_TXcwa(+i1p9~1nH5C2fn{>_q=WX^hbt$#pPkCV(%01Yt1dqVJ-}qy$ z3!L7YX~*$NC8|85xSZS+X1do1&&Vmu1BS;LkU6Hm^!Tmp)aFF^tDz@_ zZ)SHb3HOUolBX}^K0bcax-y>v2!k`2Dny`)8HUz0txA8}z~q~6gl-j5p`)7Qh1VB1 z>w0@0*l4;AnF%j{f`2|}!3r$?eJS4?ow1Pm?YqVsR^f|-tI$_B>Nnkq(TqsjgKv*R zTrOcoyE!oy?Gs6^UOs(`E3|f+;Nm;c+EquhBr!Sne!q06wV^Zl6Tycr{aVAnm1jYF z9D)s?OH$)aQyr>##}X|(0)|ne(Fif-bCDcw2NCaY{{`w!2+g`p4!ec-_L> z^2QbtN`z&ef_fkMOT^`+JxE-o!u?FbBUY>kv?|DK4huhzzkv&SfAZGSfp79vHGR}V z0pnsJoISBre;5*6Sm(LXp8<{^JpRl@UE*rsfg>KLLs`B(iJX2Q`VK$@87|TZ1(<;8RnUIRoJL)EjJj?)>AC7Hmhb zyn^ejmgp=L@oF$~(w6y}NVRJ6V938mF(S5RrBN@)x-hcTe)&-2* zu(Xn-uj4$w5I2GwTls-Ik{0%x9h65$NzcX^I_#BJ3%)Svj4RfFx#_80L*Rc`HmRKg zs~n;9LPT~PsogsbYCdppW-nYqMHDHvCxxEjQM*reDSC}-yG?ju9^~;!jS#I!Yb&xi z7SHf3`B}O8L?6J(YPR0*1iB|fzy7JPUtL63k!a>Vld1C5zT}NAY~rW95H~44kLEph zlt$T$j)|JMR{A?CI)OBGxVsoT_TxdH8`WN&IwruAOlacvp6sSb)GI!1Skun;v;|h; zFVfiOq&$wpan75D&j|VE2M0WF`JuOjXIPTjg@(A~jo5LmTNyMtE%8mW4%`7oiFb8b zAz3HcX!!y}nd{{;t$WF}52Yy>I=jUi5!m@VvUyt2BJUdu zLNOVM-M=3-UfYWU9BS8iire{LD-~_Z(46w&&$Hg?3ff_G%<^V($GWW+O0i#FOdyYX$Xj4)=ue zt#6bXoJyv#4=0y0BIJojoxuJCevRzfI=h{FpP9>gXY5I&=QZ zuMFuYete;;r6tmBAI`2n6W!mKflspXo>dWiT*UfnwR|9-2(zVPCW*0+ozqjDa=N@zv6Vf6s)ycLSr17E})8AUJa`+TgfU z4BgmrZx>8agXO|!F1i&3PjmYFRiA;j?jQJth!D&fXAU!E^RafrWss_n?+n*#O)zqa z7u^5;qK7L4>Gup6-k8dZ4nB~N=DC@4u z`&$1xca9_(X*v^M3olx)>&^mD^aI7eSnh2}c_>93wuOT(h z4=K2d#kC;RQrd+(lt*m|{d2-4w7%B~8Xe10&O(PTubEaqY69{cmUZ+Ak6)h!*jLKn zrmuF~&>kALP5ijYhaX;fX&`u)-MM1_IbYhQ?V={JGIudvW-v}Sr}}OaAc~S~yu~`T zw3~v_4>Qdz+sfJY*vHw|YNN>e|E?KdN#=x2oUIx_v27>(pm6*qJlZ7{M41RLdaVI_ z;IVs?6^?B@-g}+ROj`fAZJ~&nnhbsJEiaD>FW8;8(Z$XWF+oq>!bFVGNE@a#iasP-|&dyl3r1S4B3wftjrm_CzMntRB^Vybc! z^|Fj%tOLtPazZ0J?9<3e8JgsUo z_JRuwo+mBa;a)Kwjt4uBbI9%=+7LZj?KEJl(NOKNbhQx`CqRoJ~N@}#x zENSYd7K`24<+M~Io;Cq(vXt`#nM5C`ucPGy7p3bPo_sKe8;_@MkpFd7Gy;z(K`LR9biE=)NaF0AcHv=G{ z&K1`g43zzkSBH&ZtzA0*^)N_}9NRH#H*Q#DN2kyIa!#7MzQ6a`02-g4vB`HulTP|1 zw$ex_$ub5QoYBTE484^*PldT~SFA(f95lx2HVZvxYqro(?aFr`lLmJ-$3M+6b$z zHw2?+DylZFeU|^-B+4r&p?58bnVf36^|5k#0NNH$u?57+2Sh;0kp@K0wd1l5)BoaN z;O_Ek)1*wMN3%Bo71{NFW6p10i>9O}S4Eax<=?wTcZeKFYvbIFLT~WIMKU_>)0}68 zuasq@60rj?mBmZ(1Y%XSv*VE~c?`^|t*@KTGr$M$)M6!*G`N>OxOiZBq;gc zw{;2n=hfbdLs_QWL2TF}(eZv=x#$TVf}N@Gtp{4xp{^Cf_=a_P3rfdp`bY%D(lHg9 zMqRmu|EdK^vj0#rh9=D4Xt*6!8y#9%*uNPMZf;O2hB74d*h1_oB6W&{cY#I1QG0 z<;XkF^wOZbO;6OVZE(w1pW%0YtgwtrfxN`|{|4;$0A{)Gs|>i_o?J%qAL zD3Yx;{*aIrwuVx?l0p}l?>vJ(M1#l5$1lA+Uy(*P#dz|8IoArJB0?R{tO3H!(98hG@wj3iV=p!*(-9ZV z{|IcDK9Sjx6@eY#Ok*aI*%$jMzyqJby6;?ev5Pw#&Rz0*!~NGzr01q8Y>)Rrcc^U-NAr@*z*sj?lm5=&gND zON`Cc%HT2NncaWucZ4{1wxp zhyE|*@+=oPEpjr3O7U9zG#8z?rZHX4|4Rj>BYwO$ppy=B-E5+#8}rB4`YnJCUq1ef zeKBz&jkTwle^YM=E|oERd21RYmuCWfkVgAK<42q8te-n8gb)_5jH0LI5Pv9V9d=uW z>yXyrlGW*xvLlJA_?hJb!>Eoaaqi`Q+Ju~!TF$Rx`QBU*h=hC+k#%pCVaQq$R=OZ?}l1Fn_`Tw2~{?Ktw`yC=)wpEEC%tdZo6RxX*E? zi3JEp$o;MCRDaQ|A@;rJ-t{MLkiX>LVvG_ecVi>^8*admLV~#6kUt`7Aj>zhS}RZd zTbT$!{)9ulvf~yJg1yo8{gt_C)Hj)QW{k>p#GO_{bi8-DjF8~f^epP~ZccNOaqq^pmVwg>(sr3K(o-On$1 z)Nd`{T9OYQ*Z4l-0j}^-tZ*Pbt;IfiBLX%{>|vlL%aLBY-k~)`c~&d}s)e#8F?7*$ z9ywoTf=D$O899KA3}u5$&});YQG=i4)f4C#)F$0p*gjFd77||XG8jO@zSGvs$hU4W zkROoAzUjvJ9mhq@n8N;iyWg22Xi_U&3^E?HqY2l zkUXtpZgAyEy~a_HA{`5b)#}v3PI`7zPgBQ)WzwGYYR9=xC`AFP`BhYCk*L$O(Adqp zE3x$0dbbF+C#BvG()2m;wYziF>be?)Y^>@7c6Ej%-gmGW8h*^CS;Dx?A4-)hc&Yo& zvk2SReA6G2Jf6n06rGGKGe!Q_xw`3z=Tzzi_qP^UNR0jkmGBRo3*F5V38lvCt&3Hd zgYQ@j-yY-gO``+buVkL7bY_7_9dSkm-oakf+cMl3t(Q49PU9}(q3<|tb|w98iT-=G zZGzVe4{rBS1nG>Gx7?b>J1NJ}NwoM{5$YbYg5^ae<=eBQE!RmjjTcF{d1*{rw=Q6; z_sWx?sEzwSXzPuwnoYl3i0?b=8wrNr6WtKL{$ukY)@S;XF-qc|RR^I4x75TL7mwO4 zSkA2hcN8nqJ}gy8%gf-YL3tncQJWn@lc5A_m0y-;znm^SR03I-@HO9>MsYdD(7`og z?IpOzKJlIC+Lh-_qd(FnJ>AB=H|3Oe;~bLToufI-U@2@~Q2=zaMeHOe`(2`-HDx>E9w%{ zg)w7u!^$;QVPn?N!=B)|gn61H5rLNUSQ72kFy~iVqSBIA{5`(;YYmadpXfz#<^}|V zSY|@~^#F(O!LuZDr-xNV;p_a7&PMe2Y4W%o&Z`EhP z!g~|Pm#Q9IS%8*xm`no?Bu8WzodLx8P%1LI7qDu14Gx?pKLy#R@K65CI*k$|ra$%@ zmnqtt#3t)H1lkbIx&@=Oe56SNa;WfVMQKsEI8IhICkarH!8_u&WW$9BlTLFEkSJ7O zI!*3l4g@^Z>>2)Wb6V^ZT%1G(QDjmb4dkHfpxLqU*0-7TF)Luk`RMqrzI5)EGy@>CbG~vB*itiOcN=`^_ zc=x5KIZ-2MOhpb;(lt1Ltc-Ki^(u_yg>IoM#g&4%N`D!Mx*WBf^q(+A=MUS_J5!H0 z-si!7Jn^U(G%vrrwN1fx327}KA<6T_=iIwKNc~_8PV{okPCS00c!)`GydWOQR43KD z)>yGtBPBys9J!mcXeUv7bC01#8rlsb#Wm5Pmt9Q~7c%HDNK-km-j?*HlrqBxOX z#D^!tKx7TX5s8y5gp~0Zh*NFS#vCp$4@H3e=U*}B^49%xUUL209SSx!P^O)f*sAW< z-(}9dNvPb(Td6dsj#?+f+QvDZs!gH_lv=xL*ffL8Jrra*?x}%MahF;C12dOJk(kg; z^Ubkwe(7hN(gA&1ss|$?eM-+7dyZ?mYAV)_ zYovOM3q5hFUj(T-9KTf1Ba7*$bZy!s6~%Ze0KKqarf8L$Kr^$o^0*4 zG*2F%oK>zJ&rzjnJNbW@eb7y1YZ4k~+~P)WXJt)}*zpnwRVuQ}WJdKBDolr<)M_bo^FB`tQpahZvK9lRDU_@Z25u~-vJ}xyR;AM#0I@{k@it%!X z`-XJ@mD(=tZ2Up6r*awVDtn>MQTS4z#MVXR5z?U0TcAZQZ%u%$BVvV1)oJdE47KjP zx8D2oaARIQ%;jvU3SqHb0(b-|@;QNHTs)-3!)@m>LVZE4BRC3lV0(o07*OifD!0w` zB37rcR-E9zT6-~s9q3q>+ZDl*>vH)bfL~GTMW7P+EUa5Mg9ufcdG%DSpM@P@C+ge= zoMmfJ??O6+JqdAy+*NC`EJc7=*CY<%DOgUW8Eoa%9kpg)+1iZi47QzJRCce+Tf4MD z1q&cFw=L9ZE(iG;L5h5)rKtBd=0t7|N_JxNB2QI{;k$nSvKFfIF ze)(E~*Gi+;k$sGH_J6?(9DJ4AXlHFQwcb?c1zPPyR#A1cPbBucr~Vj76{v&Nzrl)tYz2Rqb$pw@LZ(I8omDnRLj%A zxYH?0mq)Hz8xDHA!)IVco&eSt^~VODZTcIwpOw8Y8~Qf>re6P${-`ZzpOy%C0U3g@ zC0y2vDqQ+J4qDq=!D+4a6BvA+y+g~zjAY;#zTt$7wl;+4MBzqW`_lK2fp_Ei5-5q1 zSr!iyNEWu3i_Y`dN9JAIUZJ9aD|i8w_Hp6hv+X5#aT$ylc;Zbw%Q{E|N3S!$!uxLA z+BOrdF7u={4Bm^@Yu)ULE%dy{0kCC7+eff=;0Q@PN+;nmmAGtJNDFU6*x3=s|B=G zHtJpTa=LLL;kgAK!A7|)@>rG@#kF>w43?;-LncyN9^O%FGoC2K9jxigO;EY` zyPLqfxqJjWMN1!Kc^iN&Aq=}J2Ds&23&TJeoQ}Ejy56TDZ9|mpqOubZK%5*aBM&?H zAjC`Xc|0tj5flZ>4wQwj{ji?FQdzrEo|BNAfHHwvc0M~LQ)`6N`(iRW<}iX(xmpU;Zte!K3uFX}_r%jKq}{CG z){#Ih-eutn&mR?xf=I2$?@@@`Wk~=uTW+EdMsaGDSS7F>?nAW>z|q!QYs+~!6RZ;( z)@_%-yLz3fwfS9yOP>o54>;4Z4=z}@>*X)1wQC=!^y7FapwbCYsm=qCs`KWuMD4q3 zZQGuxbOo6SRJiu+E=#8EiCUMQpC9oylzFjPb%wAlcUDzNa(P#+U0b^>RZ4v7T~>cw zHr09;)Oq+FX7ePh(Y_3lc)3(_7WNxzEwb^I>5_wBg7-EOzH<2b&U&4BLL zO|nF6vvbHQ<9Bst+9thfp)$7_2I6&PT$|r+wY%WK0ZEvYpoquGrM+pRdBe#$P(3t zq5TulG$%n`AMr0#3O;)AbvD2BME&qX>Yt+AzI_Z0t;5Rm7%k~q&>1snlQv%-i>Ll z@pH0F3kCFKjb@NU(CQ)=R&dxDrsp@eX^kvljHyJsA15=g0Z36m4{J1=>%h?Jn5(L| zr(7KSyqs}q1u3fkvf59?!l{7v(4#s~#>K47&%xjX5X(tLxa3l{a32}h@~#B%3RuPqYm7FN#_Hdvl*C4c+T}Ys`kTi zqV8KDnyqC_|J;I?fvZ3}1CrH$KFgYb$v~j;pGTVentdXrjKNF%6fX+xo~2Y^z6D6;FA0(BTjRzN?dU$d`*czp%D2wVg$fyfdI zO&9>okE5B^R;x|xJ|hs#bRm5j9&=$~h0{Dre7wQA9E8d9vP@fycPYdH@CER8&&ykH zW}<$q|5V9h#?QG~ri~yDfWd}CconNt-~xB9)Pzf%Lzo0)SoeXRgxL4d$jbz_Vf>Wv zlr;uz6R^w*LF9=8bx2}W)|f#Y>t5=>J~6tk-&g_Ot4o_u(29In!?HAS)yWz&i1+~3I}{od-Q51qJ=)^J9)a$@mNmh2 zx-ZGJKHec@vrh|*j$af^22X)Y=1L{dlkfHVP_J7}63%^P9L)YQL7g&a+m3aLxpv;l z?7o_htc=28S=Y~JW{;By0TRZ&SGaZrG!qx=RLQdKs{Wy1JhU~5m>FbYzre(uX@(Bq zm0*(Z^jHS5aOhPtZQ!cdPhHsG^HP8{0U$ar$DXC{r>+=j!~V{DN^hJ|6ftue3?=PFh>#X_^Y1x8S!%_-Ln+>}`-##XO8e*79i zzp;7!ef>J{A&Py-x$yd8tDK&U#U;Oq$hEwi5?$J#%(pzeY|TZFm;zxK10%`cGE4Nj zQT*~hL0jc@8nxOE;@N$h%~_Z!m7fI2(wDfgd>}Xto<>-vwYmJjbtzJ#kHkhf=WyXC zEYEB&PEOZROe2;jW9G^Gr&%`Rz+#|MPEQGy*qCgNf)gO4dCJ#;&@(!U1b8+km;haP z;wnS?`fqzKB3p06Gd%>ja~3^)Q@j^*Es|uWb~3+Qdlk}#jC++_808l%D7sI{m0ozi zEja~{WVc@;_xl|G=lg950(|MCP8M`9J%k3I^q>E5zRh)2y4%vn=Jdy~uJ4XZX-w;IGS`N6DcJ(u zWD8;$S3bQBf#hpIMu2Cd4oX?o66kS-MHErpZmc`?~%rypnZ_y{B2) zXW_{*m^HcU$qI?Z(oB0ie%0s~{vf};uq>S}7=#Qa(;@xz+OO@3N`BKGl0o@l!}BH} z2(5t|k1-C|;jU|`FyCxyi9{zm=`5aPw#GoL`<7Uk#JLb+U&tL8f*gH2l2}v~-ZEce z;bXpQ|DxaksLWLdM7PJ`szlE~_c4gPT4Dx@;FI`w*0m*MmW8Pmo^@5!BcP_gx9jkf zH=`v78{LMdTpQ+|`mY|6135O!fEdKXD#SZ4gSjV|2t*0$FmSu@5RMKglgP6W=l#=Dg$K33 zbc3hOS71SV>CIBXy-Vc{je!Y7Gg}Mg&60&Cj-5=*<`Pyo!C(*&9JWPqlEy#=(JxF{ zqt{a)1*l^O9s#7<6gV^P-WAuTF;JE(S-gV;u~;a&B!j77R3(d(5jfwz3(xG4h0kW% z<(zBAk7xIG72nR&6N!bzIMTk1-+*amvjvuqV|~L}rj2SpKi*Efg+UPz%P&h26l-Ia zeDxhh>NYcevS67uDp?F5MfG2IYbKGa`^_`m2%K@No7#Juzt7dvg}O|hbLYMCmn?mb zyVA6op#EdE9~Cy9jb(sPkHt1+b;^a)Bq7Qg>Y^kAn5)=29g*3)CmVL)!mF4v{xk>0|1-yvnVxs8H>XsC;@TuRS_;%&?SJr4E(^hIf_uYM+i)o?vlrWEA z?HJ!POJv~Cg%7kMpEWX&xL}ciCmquM3`JQyt9E5=zE8!>B?}+N(Jq(E`|Wo7O0utt zY3eoBv^Y|f`}V1c?)-!Zi1HnwyWaj)z;PUx>-+ore}4S<@lX4-f{P{p`Vf^E73nVw pex)>CbMB1KRBmQ=yuZKy`9DIjKPQdmw}t=!002ovPDHLkV1i*AqG$jB literal 0 HcmV?d00001 diff --git a/www-data/icon_background_active_100.png b/www-data/icon_background_active_100.png new file mode 100644 index 0000000000000000000000000000000000000000..1c1eced923cc2b56f5a689c8eb79c72cdec6c232 GIT binary patch literal 4101 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 DN-%dK literal 0 HcmV?d00001 diff --git a/www-data/icon_background_active_256.png b/www-data/icon_background_active_256.png new file mode 100644 index 0000000000000000000000000000000000000000..a930c0db1e4b2749ae97c0b3ec471aca60fc9f01 GIT binary patch literal 14372 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 literal 0 HcmV?d00001 diff --git a/www-data/pane_side_bottom.png b/www-data/pane_side_bottom.png new file mode 100644 index 0000000000000000000000000000000000000000..90699c803c9feb59dfa5d9472e74705c55149242 GIT binary patch literal 131 zcmeAS@N?(olHy`uVBq!ia0vp^j6lrA!3HEtFPV4&DYhhUcNd2LAh=-f^2tCE&H|6f zVg?3oVGw3ym^DWND9B#o>FdgVjf;iXP&VO_QVdW?z|+MsgyVX0f&yzxTiZW&1{OWW U|5d!pK!!7Ty85}Sb4q9e0OIEz=Kufz literal 0 HcmV?d00001 diff --git a/www-data/pane_side_left.png b/www-data/pane_side_left.png new file mode 100644 index 0000000000000000000000000000000000000000..798db00a89a30af9bf21398c958efcc037ecf1ff GIT binary patch literal 131 zcmeAS@N?(olHy`uVBq!ia0vp^AT}ch8<2ed)xHi$u_bxCyDx`7I;J! zGca%qgD@k*tT_@uLG}_)Usv{PTr9i>0_-073xGlbo-U3d9M_XMczOSxcVJ;)@V?9V Uzy6n?C{T{U)78&qol`;+049R literal 0 HcmV?d00001 diff --git a/www-data/pane_side_right.png b/www-data/pane_side_right.png new file mode 100644 index 0000000000000000000000000000000000000000..8dbd61709aa614b299b5e3806f7b70443b4a4acf GIT binary patch literal 131 zcmeAS@N?(olHy`uVBq!ia0vp^AT}ch8<2ed)xHi$u_bxCyDx`7I;J! zGca%qgD@k*tT_@uLG}_)Usv{PTr9i>IvVQ2>Odg@PZ!4!j_b(@3QR3+ZU5LASX3GR U$0x_60Oc4wUHx3vIVCg!00>SU)c^nh literal 0 HcmV?d00001 diff --git a/www-data/pane_side_top.png b/www-data/pane_side_top.png new file mode 100644 index 0000000000000000000000000000000000000000..eb33bafaa1ed23866ffbb4e43405dbfec9a8ffef GIT binary patch literal 133 zcmeAS@N?(olHy`uVBq!ia0vp^j6kf;!3HGHJy2Q%q}Y|gW!U_%O?XxI14-? ziy0WWg+Z8+Vb&Z8pdfpRr>`sfH7*uj3ubTqC1pS%Ax{^_5RU7~2@*L9CMI9(J*61p XQW^iBZCG;wD9hmK>gTe~DWM4fceNmc literal 0 HcmV?d00001 diff --git a/www-data/pane_top_left.png b/www-data/pane_top_left.png new file mode 100644 index 0000000000000000000000000000000000000000..69f67bf563e412a288d7de48dd3a29fd46b1fd8a GIT binary patch literal 249 zcmeAS@N?(olHy`uVBq!ia0vp^AhtRO8;~?(xETth*pj^6T^Rm@;DWu&Cj&(|3p^r= z85p>QL70(Y)*K0-AbW|YuPggCE*3sPb}lE!yFj5fPZ!4!i_>o}X!9Kk5O90AerdEu zSE@)q`)$voe548JF2gw6dC4QN54hwVc~!jiypM zf|~_1yAQ~^m)r>LG;r^iTHUc@8S|6BmpR_mdKI;Vst0EwJe*#H0l literal 0 HcmV?d00001 diff --git a/www-data/pane_top_right.png b/www-data/pane_top_right.png new file mode 100644 index 0000000000000000000000000000000000000000..f53f33daa0872bba2ecd8ebe2ed3caaf1349194c GIT binary patch literal 273 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 FV1h07?(hHr literal 0 HcmV?d00001 diff --git a/www-data/volume_active_crypto.png b/www-data/volume_active_crypto.png new file mode 100644 index 0000000000000000000000000000000000000000..faa165607fa363022d28953624f715af0e839f37 GIT binary patch literal 6590 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 CL1i5P literal 0 HcmV?d00001 diff --git a/www-data/volume_active_plain.png b/www-data/volume_active_plain.png new file mode 100644 index 0000000000000000000000000000000000000000..61b0d92bac32fc89539541b2d957afe838cc08a4 GIT binary patch literal 5903 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 YqSwrL9j}=iEM5bes(LC_O13Zm51+r}2mk;8 literal 0 HcmV?d00001 diff --git a/www-data/volume_passive_crypto.png b/www-data/volume_passive_crypto.png new file mode 100644 index 0000000000000000000000000000000000000000..de0da42c2aff6376c54a752b0013f709253b60e9 GIT binary patch literal 8140 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 literal 0 HcmV?d00001 diff --git a/www-data/volume_passive_plain.png b/www-data/volume_passive_plain.png new file mode 100644 index 0000000000000000000000000000000000000000..02f2ce60711fd7023e5410e5551aab0510004049 GIT binary patch literal 7661 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 literal 0 HcmV?d00001 diff --git a/www-data/volume_property_frame.png b/www-data/volume_property_frame.png new file mode 100644 index 0000000000000000000000000000000000000000..11155688ff900db11e7345ad54daf28be582b5ff GIT binary patch literal 6389 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