diff options
author | Ruben Rodriguez <ruben@activitycentral.com> | 2012-09-12 19:28:25 (GMT) |
---|---|---|
committer | Ruben Rodriguez <ruben@activitycentral.com> | 2012-09-12 19:28:25 (GMT) |
commit | 3981a3e0228ae0a844a4cc7b4ac6c5d1309b1fcd (patch) | |
tree | 0b3f734c15bc892d03510ba6c33915891f1ebb4d | |
parent | 306b9d67461493f2a0d903eba0b9318e33a2b075 (diff) |
Removed 0130 patches in AU branch
-rw-r--r-- | rpms/sugar/0130-1-to-N-feature-via-School-Server.patch | 1954 | ||||
-rw-r--r-- | rpms/sugar/0130-Remove-1-to-N-feature-for-AU.patch | 6700 |
2 files changed, 0 insertions, 8654 deletions
diff --git a/rpms/sugar/0130-1-to-N-feature-via-School-Server.patch b/rpms/sugar/0130-1-to-N-feature-via-School-Server.patch deleted file mode 100644 index 81cd2f8..0000000 --- a/rpms/sugar/0130-1-to-N-feature-via-School-Server.patch +++ /dev/null @@ -1,1954 +0,0 @@ -From d56362d85ecd6ce9921a67c2f4252d01c26a71af Mon Sep 17 00:00:00 2001 -From: Ajay Garg <ajay@activitycentral.com> -Date: Tue, 4 Sep 2012 18:16:19 +0530 -Subject: [sugar PATCH] 1-to-N-feature via Peer-to-Peer mechanism; and Via-School-Server mechanism. -Organization: Sugar Labs Foundation -Signed-off-by: Ajay Garg <ajay@activitycentral.com> ---- - src/jarabe/frame/activitiestray.py | 5 + - src/jarabe/frame/frame.py | 3 + - src/jarabe/intro/window.py | 12 + - src/jarabe/journal/expandedentry.py | 24 ++- - src/jarabe/journal/journalactivity.py | 17 ++- - src/jarabe/journal/journaltoolbox.py | 44 ++++- - src/jarabe/journal/model.py | 375 ++++++++++++++++++++++----------- - src/jarabe/journal/palettes.py | 283 ++++++++++++++++++++++--- - src/jarabe/journal/volumestoolbar.py | 92 ++++++--- - src/jarabe/journal/webdavmanager.py | 172 ++++++++++----- - src/jarabe/view/buddymenu.py | 45 ++++- - src/jarabe/view/palettes.py | 38 ++-- - src/webdav/Connection.py | 11 +- - src/webdav/WebdavClient.py | 16 +- - src/webdav/davlib.py | 9 +- - 15 files changed, 867 insertions(+), 279 deletions(-) - -diff --git a/src/jarabe/frame/activitiestray.py b/src/jarabe/frame/activitiestray.py -index 55e0c31..7cbeefb 100644 ---- a/src/jarabe/frame/activitiestray.py -+++ b/src/jarabe/frame/activitiestray.py -@@ -249,6 +249,8 @@ class ActivitiesTray(HTray): - # JournalActivity is always the first activity to be added. - # Broadcast the signal-of-its-creation. - if group is None: -+ self._journal_button = button -+ self._journal_activity = home_activity - self._signal_addition_of_journal_activity() - - def _signal_addition_of_journal_activity(self): -@@ -306,6 +308,9 @@ class ActivitiesTray(HTray): - if window: - window.activate(gtk.get_current_event_time()) - -+ def _show_journal_activity(self): -+ self.__activity_clicked_cb(self._journal_button, self._journal_activity) -+ - def __remove_invite_cb(self, icon, invite): - self._invites.remove_invite(invite) - -diff --git a/src/jarabe/frame/frame.py b/src/jarabe/frame/frame.py -index cd1dc20..821e31f 100644 ---- a/src/jarabe/frame/frame.py -+++ b/src/jarabe/frame/frame.py -@@ -449,3 +449,6 @@ class Frame(object): - # Do nothing for now. Our notification UI is so simple, there's no - # point yet. - pass -+ -+ def switch_to_journal_activity(self): -+ self._activities_tray._show_journal_activity() -diff --git a/src/jarabe/intro/window.py b/src/jarabe/intro/window.py -index df19fbf..c4ae56e 100644 ---- a/src/jarabe/intro/window.py -+++ b/src/jarabe/intro/window.py -@@ -56,6 +56,18 @@ def create_profile(name, color=None): - else: - logging.error('Keypair exists, skip generation.') - -+ # Also, generate SSL key and cert pair; to be used in secure -+ # sharing of webdav entries via httpd. -+ keypath = os.path.join(env.get_profile_path(), 'ssl.key') -+ certpath = os.path.join(env.get_profile_path(), 'ssl.crt') -+ -+ cmd = 'openssl req -new -newkey rsa:1024 -days 365 -nodes ' + \ -+ '-x509 -subj "/C=US/ST=Denial/L=Springfield/O=Dis/CN=www.example.com" ' + \ -+ '-keyout %s -out %s' % (keypath, certpath) -+ (s, o) = commands.getstatusoutput(cmd) -+ if s != 0: -+ logging.error('Could not generate ssl key and cert: %d %s', s, o) -+ - - class _Page(hippo.CanvasBox): - __gproperties__ = { -diff --git a/src/jarabe/journal/expandedentry.py b/src/jarabe/journal/expandedentry.py -index 03f8cd1..1e857ba 100644 ---- a/src/jarabe/journal/expandedentry.py -+++ b/src/jarabe/journal/expandedentry.py -@@ -166,13 +166,35 @@ class ExpandedEntry(hippo.CanvasBox): - self._buddy_list.append(self._create_buddy_list()) - - description = self._description.text_view_widget -- description.props.buffer.props.text = metadata.get('description', '') -+ -+ # TRANS: Do not translate the """%s""". -+ uploader_nick_text = self.__create_text_description( -+ _('Source XO Nick :: \n%s'), metadata.get('uploader-nick', '')) -+ -+ # TRANS: Do not translate the """%s""". -+ uploader_serial_text = self.__create_text_description( -+ _('Source XO Serial Number :: \n%s'), metadata.get('uploader-serial', '')) -+ -+ # TRANS: Do not translate the """%s""". -+ misc_info_text = self.__create_text_description( -+ _('Misellaneous Information :: \n%s'), metadata.get('description', '')) -+ -+ description.props.buffer.props.text = uploader_nick_text + \ -+ uploader_serial_text + \ -+ misc_info_text -+ - description.props.editable = model.is_editable(metadata) - - tags = self._tags.text_view_widget - tags.props.buffer.props.text = metadata.get('tags', '') - tags.props.editable = model.is_editable(metadata) - -+ def __create_text_description(self, heading, value): -+ if (value == '') or (value is None): -+ return '' -+ -+ return ((heading % value) + '\n\n') -+ - def _create_keep_icon(self): - keep_icon = KeepIcon(False) - keep_icon.connect('activated', self._keep_icon_activated_cb) -diff --git a/src/jarabe/journal/journalactivity.py b/src/jarabe/journal/journalactivity.py -index fc4773d..5f2a734 100644 ---- a/src/jarabe/journal/journalactivity.py -+++ b/src/jarabe/journal/journalactivity.py -@@ -180,10 +180,14 @@ class JournalActivity(JournalWindow): - self._check_available_space() - - def __volume_error_cb(self, gobject, message, severity): -- alert = ErrorAlert(title=severity, msg=message) -- alert.connect('response', self.__alert_response_cb) -- self.add_alert(alert) -- alert.show() -+ self.update_title_and_message(self._error_alert, severity, -+ message) -+ self._callback = None -+ self._data = None -+ self.update_alert(self._error_alert) -+ -+ def _show_alert(self, message, severity): -+ self.__volume_error_cb(None, message, severity) - - def _volume_error_cb(self, gobject, message, severity): - self.update_error_alert(severity, message, None, None) -@@ -449,6 +453,8 @@ class JournalActivity(JournalWindow): - self.remove_alert(self._current_alert) - self.add_alert(alert) - -+ self.remove_alert(self._current_alert) -+ self.add_alert(alert) - self._current_alert = alert - self._current_alert.show() - show_normal_cursor() -@@ -475,6 +481,9 @@ class JournalActivity(JournalWindow): - self._data = data - self.update_alert(self._confirmation_alert) - -+ def update_progress(self, fraction): -+ self.get_toolbar_box().update_progress(fraction) -+ - def get_metadata_list(self, selected_state): - metadata_list = [] - -diff --git a/src/jarabe/journal/journaltoolbox.py b/src/jarabe/journal/journaltoolbox.py -index 6b2494e..b1c0cac 100644 ---- a/src/jarabe/journal/journaltoolbox.py -+++ b/src/jarabe/journal/journaltoolbox.py -@@ -78,6 +78,16 @@ class MainToolbox(Toolbox): - self.add_toolbar(_('Search'), self.search_toolbar) - self.search_toolbar.show() - -+ self._info_widget = MultiSelectEntriesInfoWidget() -+ self.add(self._info_widget) -+ self._info_widget.hide() -+ -+ def update_progress(self, fraction): -+ self._info_widget.update_progress(fraction) -+ -+ def hide_info_widget(self): -+ self._info_widget.hide() -+ - - class SearchToolbar(gtk.Toolbar): - __gtype_name__ = 'SearchToolbar' -@@ -532,6 +542,9 @@ class EditToolbox(Toolbox): - def get_current_entry_number(self): - return self.edit_toolbar._get_current_entry_number() - -+ def update_progress(self, fraction): -+ self.edit_toolbar.get_multi_select_info_widget().update_progress(fraction) -+ - - class EditToolbar(gtk.Toolbar): - def __init__(self): -@@ -552,6 +565,9 @@ class EditToolbar(gtk.Toolbar): - - self.show_all() - -+ def get_multi_select_info_widget(self): -+ return self._multi_select_info_widget -+ - def _set_total_number_of_entries(self, total): - self._multi_select_info_widget.set_total_number_of_entries(total) - -@@ -703,16 +719,40 @@ class MultiSelectEntriesInfoWidget(gtk.ToolItem): - def __init__(self): - gtk.ToolItem.__init__(self) - -+ self._box = gtk.VBox() - self._selected_entries = 0 - - self._label = gtk.Label() -- self.add(self._label) -+ self._box.pack_start(self._label, expand=False) -+ -+ self._progress_label = gtk.Label() -+ self._box.pack_start(self._progress_label, expand=False) -+ -+ self.add(self._box) - - self.show_all() -+ self._box.show_all() -+ self._progress_label.hide() - - def set_total_number_of_entries(self, total): - self._total = total - -+ def update_progress(self, fraction): -+ percent = '%.02f' % (fraction * 100) -+ -+ # TRANS: Do not translate %.02f -+ text = '%.02f%% complete' % (fraction * 100) -+ if (str(percent) != '100.00') and (str(percent).endswith('00')): -+ self._progress_label.set_text(text) -+ self._progress_label.show() -+ self.show_all() -+ gtk.gdk.window_process_all_updates() -+ else: -+ self._progress_label.hide() -+ from jarabe.journal.journalactivity import get_journal -+ if not get_journal().is_editing_mode_present(): -+ self.hide() -+ - def update_text(self, primary_text, secondary_text, special_action, - update_selected_entries): - # If "special_action" is None, -@@ -749,6 +789,8 @@ class MultiSelectEntriesInfoWidget(gtk.ToolItem): - self._label.set_text(message) - self._label.show() - -+ gtk.gdk.window_process_all_updates() -+ - def get_current_entry_number(self): - return self._selected_entries - -diff --git a/src/jarabe/journal/model.py b/src/jarabe/journal/model.py -index 422e947..c06b2ee 100644 ---- a/src/jarabe/journal/model.py -+++ b/src/jarabe/journal/model.py -@@ -43,7 +43,7 @@ from sugar import dispatch - from sugar import mime - from sugar import util - --from jarabe.journal.webdavmanager import get_remote_webdav_share_metadata -+from jarabe.journal import webdavmanager - - DS_DBUS_SERVICE = 'org.laptop.sugar.DataStore' - DS_DBUS_INTERFACE = 'org.laptop.sugar.DataStore' -@@ -58,6 +58,9 @@ PROPERTIES = ['activity', 'activity_id', 'buddies', 'bundle_id', - MIN_PAGES_TO_CACHE = 3 - MAX_PAGES_TO_CACHE = 5 - -+WEBDAV_MOUNT_POINT = '/tmp/' -+LOCAL_SHARES_MOUNT_POINT = '/var/www/web1/web/' -+ - JOURNAL_METADATA_DIR = '.Sugar-Metadata' - - _datastore = None -@@ -66,6 +69,52 @@ updated = dispatch.Signal() - deleted = dispatch.Signal() - - -+SCHOOL_SERVER_IP_ADDRESS_OR_DNS_NAME_PATH = \ -+ '/desktop/sugar/network/school_server_ip_address_or_dns_name' -+ -+client = gconf.client_get_default() -+SCHOOL_SERVER_IP_ADDRESS_OR_DNS_NAME = client.get_string(SCHOOL_SERVER_IP_ADDRESS_OR_DNS_NAME_PATH) or '127.0.0.1' -+ -+ -+def _get_mount_point(path): -+ dir_path = os.path.dirname(path) -+ while dir_path: -+ if os.path.ismount(dir_path): -+ return dir_path -+ else: -+ dir_path = dir_path.rsplit(os.sep, 1)[0] -+ return None -+ -+ -+def is_mount_point_for_school_server(mount_point): -+ from jarabe.journal.journalactivity import get_journal -+ from jarabe.journal.volumestoolbar import SHARE_TYPE_SCHOOL_SERVER -+ -+ mount_point_button = get_journal().get_volumes_toolbar()._get_button_for_mount_point(mount_point) -+ if mount_point_button._share_type == SHARE_TYPE_SCHOOL_SERVER: -+ return True -+ return False -+ -+ -+def extract_ip_address_or_dns_name_from_locally_mounted_remote_share_path(path): -+ """ -+ Path is of type :: -+ -+ /tmp/1.2.3.4/webdav/a.txt; OR -+ /tmp/this.is.dns.name/a.txt -+ """ -+ return path.split('/')[2] -+ -+ -+def is_mount_point_for_locally_mounted_remote_share(mount_point): -+ """ -+ The mount-point can be either of the ip-Address, or the DNS name. -+ More importantly, whatever the "name" be, it does NOT have a -+ forward-slash. -+ """ -+ return mount_point.find(WEBDAV_MOUNT_POINT) == 0 -+ -+ - class _Cache(object): - - __gtype_name__ = 'model_Cache' -@@ -430,8 +479,8 @@ class InplaceResultSet(BaseResultSet): - - - class RemoteShareResultSet(object): -- def __init__(self, ip_address, query): -- self._ip_address = ip_address -+ def __init__(self, ip_address_or_dns_name, query): -+ self._ip_address_or_dns_name = ip_address_or_dns_name - self._file_list = [] - - self.ready = dispatch.Signal() -@@ -464,7 +513,11 @@ class RemoteShareResultSet(object): - self._sort = query.get('order_by', ['+timestamp'])[0] - - def setup(self): -- metadata_list_complete = get_remote_webdav_share_metadata(self._ip_address) -+ try: -+ metadata_list_complete = webdavmanager.get_remote_webdav_share_metadata(self._ip_address_or_dns_name) -+ except Exception, e: -+ metadata_list_complete = [] -+ - for metadata in metadata_list_complete: - - add_to_list = False -@@ -553,15 +606,8 @@ def _get_file_metadata(path, stat, fetch_preview=True): - metadata based on the file properties. - - """ -- filename = os.path.basename(path) -- dir_path = os.path.dirname(path) -- metadata = _get_file_metadata_from_json(dir_path, filename, fetch_preview) -+ metadata = _get_file_metadata_from_json(path, fetch_preview) - if metadata: -- # For Documents/Shares/Mounted-Drives. -- # Special case: for locally-mounted-remote-files, ensure that -- # "metadata['filesize' is already present before-hand. This -- # will have to be done at the time of fetching -- # webdav-properties per resource. - if 'filesize' not in metadata: - if stat is not None: - metadata['filesize'] = stat.st_size -@@ -582,17 +628,26 @@ def _get_file_metadata(path, stat, fetch_preview=True): - 'description': path} - - --def _get_file_metadata_from_json(dir_path, filename, fetch_preview): -+def _get_file_metadata_from_json(path, fetch_preview): - """Read the metadata from the json file and the preview - stored on the external device. - - If the metadata is corrupted we do remove it and the preview as well. - - """ -+ filename = os.path.basename(path) -+ dir_path = os.path.dirname(path) -+ -+ # In case of nested mount-points, (eg. ~/Documents/in1/in2/in3.txt), -+ # "dir_path = ~/Documents/in1/in2"; while -+ # "metadata_dir_path = ~/Documents". -+ from jarabe.journal.journalactivity import get_mount_point -+ metadata_dir_path = get_mount_point() -+ - metadata = None -- metadata_path = os.path.join(dir_path, JOURNAL_METADATA_DIR, -+ metadata_path = os.path.join(metadata_dir_path, JOURNAL_METADATA_DIR, - filename + '.metadata') -- preview_path = os.path.join(dir_path, JOURNAL_METADATA_DIR, -+ preview_path = os.path.join(metadata_dir_path, JOURNAL_METADATA_DIR, - filename + '.preview') - - if not os.path.exists(metadata_path): -@@ -670,7 +725,8 @@ def find(query_, page_size): - Regex Matching is used, to ensure that the mount-point is an - IP-Address. - """ -- return RemoteShareResultSet(mount_points[0], query) -+ ip_address = extract_ip_address_or_dns_name_from_locally_mounted_remote_share_path(mount_points[0]) -+ return RemoteShareResultSet(ip_address, query) - else: - """ - For Documents/Shares/Mounted-Drives. -@@ -678,56 +734,13 @@ def find(query_, page_size): - return InplaceResultSet(query, page_size, mount_points[0]) - - --def is_mount_point_for_locally_mounted_remote_share(mount_point): -- import re -- -- pattern = '[1-9][0-9]{0,2}\.[0-9]{0,3}\.[0-9]{0,3}\.[0-9]{0,3}' -- if re.match(pattern, mount_point) is None: -- return False -- return True -- -- --def _get_mount_point(path): -- dir_path = os.path.dirname(path) -- while dir_path: -- if os.path.ismount(dir_path): -- return dir_path -- else: -- dir_path = dir_path.rsplit(os.sep, 1)[0] -- return None -- -- --def is_locally_mounted_remote_share(path): -- return string.find(path, '/tmp/') == 0 -- -- --def extract_ip_address_from_locally_mounted_remote_share_path(path): -- """ -- Path is of type :: -- -- /tmp/127.0.0.1/webdav/a.txt -- """ -- return path.split('/')[2] -- -- - def get(object_id): - """Returns the metadata for an object - """ - if (object_id[0] == '/'): -- """ -- For Documents/Shares/Mounted-Drives/Locally-Mounted-Remote-Shares, -- where ".Sugar-Metadata" folder exists. -- -- The only thing is that, for locally-mounted-remote-shares, the -- "file" is not physically present. -- """ - if os.path.exists(object_id): -- # if the file is physically present, derive filesize-metadata -- # by physical examination of the file. - stat = os.stat(object_id) - else: -- # if the file is remote, derive file-metadata by fetching -- # properties remotely (webdav properties). - stat = None - - metadata = _get_file_metadata(object_id, stat) -@@ -812,7 +825,21 @@ def delete(object_id): - def copy(metadata, mount_point): - """Copies an object to another mount point - """ -+ # In all cases (except one), "copy" means the actual duplication of -+ # the content. -+ # Only in case of remote downloading, the content is first copied -+ # to "/tmp" folder. In those cases, copying would refer to a mere -+ # renaming. -+ transfer_ownership = False -+ -+ from jarabe.journal.journalactivity import get_mount_point -+ current_mount_point = get_mount_point() -+ -+ if is_mount_point_for_locally_mounted_remote_share(current_mount_point): -+ transfer_ownership = True -+ - metadata = get(metadata['uid']) -+ - if mount_point == '/' and metadata['icon-color'] == '#000000,#ffffff': - client = gconf.client_get_default() - metadata['icon-color'] = client.get_string('/desktop/sugar/user/color') -@@ -823,7 +850,7 @@ def copy(metadata, mount_point): - metadata['mountpoint'] = mount_point - del metadata['uid'] - -- return write(metadata, file_path, transfer_ownership=False) -+ return write(metadata, file_path, transfer_ownership=transfer_ownership) - - - def write(metadata, file_path='', update_mtime=True, transfer_ownership=True): -@@ -845,27 +872,105 @@ def write(metadata, file_path='', update_mtime=True, transfer_ownership=True): - object_id = _get_datastore().create(dbus.Dictionary(metadata), - file_path, - transfer_ownership) -- else: -- # HACK: For documents: modify the mount-point -- from jarabe.journal.journalactivity import get_mount_point -- if get_mount_point() == get_documents_path(): -- metadata['mountpoint'] = get_documents_path() -+ elif metadata.get('mountpoint', '/') == WEBDAV_MOUNT_POINT: -+ filename = metadata['title'] -+ -+ ip_address_or_dns_name = SCHOOL_SERVER_IP_ADDRESS_OR_DNS_NAME -+ webdavmanager.get_remote_webdav_share_metadata(ip_address_or_dns_name) -+ -+ data_webdav_manager = \ -+ webdavmanager.get_data_webdav_manager(ip_address_or_dns_name) -+ metadata_webdav_manager = \ -+ webdavmanager.get_metadata_webdav_manager(ip_address_or_dns_name) -+ -+ -+ # If we get a resource by this name, there is already an entry -+ # on the server with this name; we do not want to do any -+ # overwrites. -+ data_resource = webdavmanager.get_resource_by_resource_key(data_webdav_manager, -+ '/webdav/' + filename) -+ metadata_resource = webdavmanager.get_resource_by_resource_key(metadata_webdav_manager, -+ '/webdav/.Sugar-Metadata/' + filename + '.metadata') -+ if (data_resource is not None) or (metadata_resource is not None): -+ raise Exception(_('Entry already present on the server with ' -+ 'this name. Try again after renaming.')) -+ -+ # No entry for this name present. -+ # So, first write the metadata- and preview-file to temporary -+ # locations. -+ metadata_file_path, preview_file_path = \ -+ _write_metadata_and_preview_files_and_return_file_paths(metadata, -+ filename) -+ -+ # Finally, -+ # Upload the data file. -+ webdavmanager.add_resource_by_resource_key(data_webdav_manager, -+ filename, -+ file_path) -+ -+ # Upload the preview file. -+ if preview_file_path is not None: -+ webdavmanager.add_resource_by_resource_key(metadata_webdav_manager, -+ filename + '.preview', -+ preview_file_path) -+ -+ # Upload the metadata file. -+ # -+ # Note that this needs to be the last step. If there was any -+ # error uploading the data- or the preview-file, control would -+ # not reach here. -+ # -+ # In other words, the control reaches here only if the data- -+ # and the preview- files have been uploaded. Finally, IF this -+ # file is successfully uploaded, we have the guarantee that all -+ # files for a particular journal entry are in place. -+ webdavmanager.add_resource_by_resource_key(metadata_webdav_manager, -+ filename + '.metadata', -+ metadata_file_path) -+ -+ -+ object_id = 'doesn\'t matter' - -- object_id = _write_entry_on_external_device(metadata, file_path) -+ else: -+ object_id = _write_entry_on_external_device(metadata, -+ file_path, -+ transfer_ownership) - - return object_id - - --def _rename_entry_on_external_device(file_path, destination_path, -- metadata_dir_path): -+def make_file_fully_permissible(file_path): -+ fd = os.open(file_path, os.O_RDONLY) -+ os.fchmod(fd, stat.S_IRWXU | stat.S_IRWXG |stat.S_IRWXO) -+ os.close(fd) -+ -+ -+def _rename_entry_on_external_device(file_path, destination_path): - """Rename an entry with the associated metadata on an external device.""" - old_file_path = file_path - if old_file_path != destination_path: -- os.rename(file_path, destination_path) -+ # Strangely, "os.rename" works fine on sugar-jhbuild, but fails -+ # on XOs, wih the OSError 13 ("invalid cross-device link"). So, -+ # using the system call "mv". -+ os.system('mv "%s" "%s"' % (file_path, destination_path)) -+ make_file_fully_permissible(destination_path) -+ -+ -+ # In renaming, we want to delete the metadata-, and preview- -+ # files of the current mount-point, and not the destination -+ # mount-point. -+ # But we also need to ensure that the directory of -+ # 'old_file_path' and 'destination_path' are not same. -+ if os.path.dirname(old_file_path) == os.path.dirname(destination_path): -+ return -+ -+ from jarabe.journal.journalactivity import get_mount_point -+ source_metadata_dir_path = get_mount_point() + '/.Sugar-Metadata' -+ - old_fname = os.path.basename(file_path) -- old_files = [os.path.join(metadata_dir_path, -+ old_files = [os.path.join(source_metadata_dir_path, - old_fname + '.metadata'), -- os.path.join(metadata_dir_path, -+ os.path.join(source_metadata_dir_path, - old_fname + '.preview')] - for ofile in old_files: - if os.path.exists(ofile): -@@ -876,41 +981,32 @@ def _rename_entry_on_external_device(file_path, destination_path, - 'for file=%s', ofile, old_fname) - - --def _write_entry_on_external_device(metadata, file_path): -- """Create and update an entry copied from the -- DS to an external storage device. -- -- Besides copying the associated file a file for the preview -- and one for the metadata are stored in the hidden directory -- .Sugar-Metadata. -- -- This function handles renames of an entry on the -- external device and avoids name collisions. Renames are -- handled failsafe. -+def _write_metadata_and_preview_files_and_return_file_paths(metadata, -+ file_name): -+ metadata_copy = metadata.copy() -+ metadata_copy.pop('mountpoint', None) -+ metadata_copy.pop('uid', None) - -- """ -- if 'uid' in metadata and os.path.exists(metadata['uid']): -- file_path = metadata['uid'] - -- if not file_path or not os.path.exists(file_path): -- raise ValueError('Entries without a file cannot be copied to ' -- 'removable devices') -+ # For copying to School-Server, we need to retain this property. -+ # Else wise, I have no idea why this property is being removed !! -+ if (metadata.get('mountpoint', '/') != WEBDAV_MOUNT_POINT) and \ -+ (metadata.get('mountpoint', '/') != LOCAL_SHARES_MOUNT_POINT): -+ metadata_copy.pop('filesize', None) - -- if not metadata.get('title'): -- metadata['title'] = _('Untitled') -- file_name = get_file_name(metadata['title'], metadata['mime_type']) -- -- destination_path = os.path.join(metadata['mountpoint'], file_name) -- if destination_path != file_path: -- file_name = get_unique_file_name(metadata['mountpoint'], file_name) -- destination_path = os.path.join(metadata['mountpoint'], file_name) -- clean_name, extension_ = os.path.splitext(file_name) -- metadata['title'] = clean_name -+ # For journal case, there is the special treatment. -+ if metadata.get('mountpoint', '/') == '/': -+ if metadata.get('uid', ''): -+ object_id = _get_datastore().update(metadata['uid'], -+ dbus.Dictionary(metadata), -+ '', -+ False) -+ else: -+ object_id = _get_datastore().create(dbus.Dictionary(metadata), -+ '', -+ False) -+ return - -- metadata_copy = metadata.copy() -- metadata_copy.pop('mountpoint', None) -- metadata_copy.pop('uid', None) -- metadata_copy.pop('filesize', None) - - metadata_dir_path = os.path.join(metadata['mountpoint'], - JOURNAL_METADATA_DIR) -@@ -939,25 +1035,64 @@ def _write_entry_on_external_device(metadata, file_path): - os.close(fh) - os.rename(fn, os.path.join(metadata_dir_path, preview_fname)) - -- if not os.path.dirname(destination_path) == os.path.dirname(file_path): -- shutil.copy(file_path, destination_path) -+ metadata_destination_path = os.path.join(metadata_dir_path, file_name + '.metadata') -+ make_file_fully_permissible(metadata_destination_path) -+ if preview: -+ preview_destination_path = os.path.join(metadata_dir_path, preview_fname) -+ make_file_fully_permissible(preview_destination_path) - else: -- _rename_entry_on_external_device(file_path, destination_path, -- metadata_dir_path) -+ preview_destination_path = None - -- # For "Shares" folder, we need to set the permissions of the newly -- # copied file to 0777, else it will not be accessible by "httpd" -- # service. -- if metadata['mountpoint'] == '/var/www/web1/web': -- fd = os.open(destination_path, os.O_RDONLY) -- os.fchmod(fd, stat.S_IRWXU | stat.S_IRWXG | stat.S_IRWXO) -- os.close(fd) -+ return (metadata_destination_path, preview_destination_path) - -- metadata_file_path = os.path.join(metadata_dir_path, file_name + '.metadata') -- fd = os.open(metadata_file_path, os.O_RDONLY) -- os.fchmod(fd, stat.S_IRWXU | stat.S_IRWXG | stat.S_IRWXO) -- os.close(fd) - -+def update_only_metadata_and_preview_files_and_return_file_paths(metadata): -+ file_name = get_file_name(metadata['title'], metadata['mime_type']) -+ _write_metadata_and_preview_files_and_return_file_paths(metadata, -+ file_name) -+ -+ -+def _write_entry_on_external_device(metadata, file_path, -+ transfer_ownership): -+ """Create and update an entry copied from the -+ DS to an external storage device. -+ -+ Besides copying the associated file a file for the preview -+ and one for the metadata are stored in the hidden directory -+ .Sugar-Metadata. -+ -+ This function handles renames of an entry on the -+ external device and avoids name collisions. Renames are -+ handled failsafe. -+ -+ """ -+ if 'uid' in metadata and os.path.exists(metadata['uid']): -+ file_path = metadata['uid'] -+ -+ if not file_path or not os.path.exists(file_path): -+ raise ValueError('Entries without a file cannot be copied to ' -+ 'removable devices') -+ -+ if not metadata.get('title'): -+ metadata['title'] = _('Untitled') -+ file_name = get_file_name(metadata['title'], metadata['mime_type']) -+ -+ destination_path = os.path.join(metadata['mountpoint'], file_name) -+ if destination_path != file_path: -+ file_name = get_unique_file_name(metadata['mountpoint'], file_name) -+ destination_path = os.path.join(metadata['mountpoint'], file_name) -+ clean_name, extension_ = os.path.splitext(file_name) -+ metadata['title'] = clean_name -+ -+ _write_metadata_and_preview_files_and_return_file_paths(metadata, -+ file_name) -+ -+ if (os.path.dirname(destination_path) == os.path.dirname(file_path)) or \ -+ (transfer_ownership == True): -+ _rename_entry_on_external_device(file_path, destination_path) -+ else: -+ shutil.copy(file_path, destination_path) -+ make_file_fully_permissible(destination_path) - - object_id = destination_path - created.send(None, object_id=object_id) -@@ -1013,7 +1148,11 @@ def is_editable(metadata): - # called, upon an entry in the context of a singular - # mount-point. - from jarabe.journal.journalactivity import get_mount_point -- return os.access(get_mount_point(), os.W_OK) -+ mount_point = get_mount_point() -+ -+ if is_mount_point_for_locally_mounted_remote_share(mount_point): -+ return False -+ return os.access(mount_point, os.W_OK) - - - def get_documents_path(): -diff --git a/src/jarabe/journal/palettes.py b/src/jarabe/journal/palettes.py -index 66dcadc..91e5460 100644 ---- a/src/jarabe/journal/palettes.py -+++ b/src/jarabe/journal/palettes.py -@@ -30,6 +30,7 @@ import gio - import glib - import time - import socket -+import dbus - - from sugar import _sugarext - -@@ -44,14 +45,13 @@ from jarabe.model import friends - from jarabe.model import filetransfer - from jarabe.model import mimeregistry - from jarabe.journal import misc --from jarabe.journal import model -+from jarabe.journal import model, webdavmanager - from jarabe.journal.journalwindow import freeze_ui, \ - unfreeze_ui, \ - show_normal_cursor, \ - show_waiting_cursor - - from webdav.Connection import WebdavError --from jarabe.journal.webdavmanager import get_resource_by_ip_address_and_resource_key - - - friends_model = friends.get_model() -@@ -59,6 +59,49 @@ friends_model = friends.get_model() - _copy_menu_helper = None - _current_action_item = None - -+ -+class PassphraseDialog(gtk.Dialog): -+ def __init__(self, callback, metadata): -+ gtk.Dialog.__init__(self, flags=gtk.DIALOG_MODAL) -+ self._callback = callback -+ self._metadata = metadata -+ self.set_title(_('Passphrase required')) -+ self.set_has_separator(False) -+ -+ # TRANS: Please do not translate the '%s'. -+ label_text = _('Please enter the passphrase for %s' % metadata['title']) -+ label = gtk.Label(label_text) -+ self.vbox.pack_start(label) -+ -+ self.add_buttons(gtk.STOCK_OK, gtk.RESPONSE_OK) -+ self.set_default_response(gtk.RESPONSE_OK) -+ self.set_has_separator(True) -+ self.add_key_entry() -+ -+ self.connect('response', self._key_dialog_response_cb) -+ self.show_all() -+ -+ def add_key_entry(self): -+ self._entry = gtk.Entry() -+ self._entry.connect('activate', self._entry_activate_cb) -+ self.vbox.pack_start(self._entry) -+ self.vbox.set_spacing(6) -+ self.vbox.show_all() -+ -+ self._entry.grab_focus() -+ -+ def _entry_activate_cb(self, entry): -+ self.response(gtk.RESPONSE_OK) -+ -+ def get_response_object(self): -+ return self._response -+ -+ def _key_dialog_response_cb(self, widget, response_id): -+ self.hide() -+ gobject.idle_add(self._callback, self._metadata, -+ self._entry.get_text()) -+ -+ - class ObjectPalette(Palette): - - __gtype_name__ = 'ObjectPalette' -@@ -528,6 +571,7 @@ class ActionItem(gobject.GObject): - This is the stage, just after EVERY metadata has been - processed. - """ -+ self._hide_info_widget_for_single_mode() - - # Toggle the corresponding checkbox - but only for batch-mode. - if self._batch_mode and self._auto_deselect_source_entries: -@@ -573,6 +617,25 @@ class ActionItem(gobject.GObject): - from jarabe.journal.journalactivity import get_journal - get_journal().get_list_view().refresh() - -+ def _handle_single_mode_notification(self, message, severity): -+ from jarabe.journal.journalactivity import get_journal -+ journal = get_journal() -+ -+ journal._show_alert(message, severity) -+ self._hide_info_widget_for_single_mode() -+ -+ def _hide_info_widget_for_single_mode(self): -+ if (not self._batch_mode): -+ from jarabe.journal.journalactivity import get_journal -+ journal = get_journal() -+ -+ journal.get_toolbar_box().hide_info_widget() -+ -+ def _unhide_info_widget_for_single_mode(self): -+ if not self._batch_mode: -+ from jarabe.journal.journalactivity import get_journal -+ get_journal().update_progress(0) -+ - def _handle_error_alert(self, error_message, metadata): - """ - This handles any error scenarios. Examples are of entries that -@@ -593,19 +656,11 @@ class ActionItem(gobject.GObject): - - current_len = len(self._metadata_list) - -- # TRANS: Do not translate the two %d, and the three %s. -- info_alert_message = _('( %d / %d ) Error while %s %s : %s') % ( -- self._metadata_list_initial_len - current_len, -- self._metadata_list_initial_len, -- self._get_info_alert_title(), -- metadata['title'], -- error_message) -- - # Only show the alert, if allowed to. - if self._show_not_completed_ops_info: - from jarabe.journal.journalactivity import get_journal -- get_journal().update_confirmation_alert(self._get_info_alert_title() + ' ...', -- info_alert_message, -+ get_journal().update_confirmation_alert(_('Error'), -+ error_message, - self._process_error_skipping, - metadata) - else: -@@ -639,19 +694,55 @@ class ActionItem(gobject.GObject): - if model.is_mount_point_for_locally_mounted_remote_share(current_mount_point): - file_path = metadata['uid'] - filename = os.path.basename(file_path) -- ip_address = model.extract_ip_address_from_locally_mounted_remote_share_path(file_path) -- resource = get_resource_by_ip_address_and_resource_key(ip_address, '/webdav/' + filename) -- download_file_path = '/tmp/' + ip_address + '/' + filename -+ ip_address_or_dns_name = \ -+ model.extract_ip_address_or_dns_name_from_locally_mounted_remote_share_path(file_path) -+ -+ data_webdav_manager = \ -+ webdavmanager.get_data_webdav_manager(ip_address_or_dns_name) -+ metadata_webdav_manager = \ -+ webdavmanager.get_metadata_webdav_manager(ip_address_or_dns_name) -+ -+ # Download the preview file, if it exists. -+ preview_resource = \ -+ webdavmanager.get_resource_by_resource_key(metadata_webdav_manager, -+ '/webdav/.Sugar-Metadata/' + filename + '.preview') -+ preview_path = os.path.dirname(file_path) + '/.Sugar-Metadata/'+ filename + '.preview' -+ -+ if preview_resource is not None: -+ try: -+ preview_resource.downloadFile(preview_path, -+ show_progress=False, -+ filesize=0) -+ except (WebdavError, socket.error), e: -+ error_message = e -+ logging.warn(error_message) -+ if self._batch_mode: -+ self._handle_error_alert(error_message, metadata) -+ else: -+ self._handle_single_mode_notification(error_message, -+ _('Error')) -+ return False -+ -+ # If we manage to reach here, download the data file. -+ data_resource = \ -+ webdavmanager.get_resource_by_resource_key(data_webdav_manager, -+ '/webdav/'+ filename) - try: -- resource.downloadFile(download_file_path) -+ data_resource.downloadFile(metadata['uid'], -+ show_progress=True, -+ filesize=int(metadata['filesize'])) - return True - except (WebdavError, socket.error), e: -+ # Delete the downloaded preview file, if it exists. -+ if os.path.exists(preview_path): -+ os.unlink(preview_path) -+ - error_message = e - logging.warn(error_message) - if self._batch_mode: - self._handle_error_alert(error_message, metadata) - else: -- self.emit('volume-error', error_message, -+ self._handle_single_mode_notification(error_message, - _('Error')) - return False - -@@ -662,7 +753,7 @@ class ActionItem(gobject.GObject): - if self._batch_mode: - self._handle_error_alert(error_message, metadata) - else: -- self.emit('volume-error', error_message, _('Warning')) -+ self._handle_single_mode_notification(error_message, _('Warning')) - return False - else: - return True -@@ -674,12 +765,12 @@ class ActionItem(gobject.GObject): - model.copy(metadata, mount_point) - return True - except Exception, e: -- logging.exception('Error while copying the entry. %s', e) -+ logging.exception(e) - error_message = _('Error while copying the entry. %s') % e - if self._batch_mode: - self._handle_error_alert(error_message, metadata) - else: -- self.emit('volume-error', error_message, _('Error')) -+ self._handle_single_mode_notification(error_message, _('Error')) - return False - finally: - self._set_bundle_installation_allowed(True) -@@ -689,7 +780,7 @@ class ActionItem(gobject.GObject): - self._set_bundle_installation_allowed(False) - - try: -- model.write(metadata, update_mtime=False) -+ model.update_only_metadata_and_preview_files_and_return_file_paths(metadata) - return True - except Exception, e: - logging.exception('Error while writing the metadata. %s', e) -@@ -698,7 +789,7 @@ class ActionItem(gobject.GObject): - if self._batch_mode: - self._handle_error_alert(error_message, metadata) - else: -- self.emit('volume-error', error_message, _('Error')) -+ self._handle_single_mode_notification(error_message, _('Error')) - return False - finally: - self._set_bundle_installation_allowed(True) -@@ -751,6 +842,30 @@ class BaseCopyMenuItem(MenuItem, ActionItem): - def _get_info_alert_title(self): - return _('Copying') - -+ def _operate(self, metadata): -+ from jarabe.journal.journalactivity import get_mount_point -+ if(model.is_mount_point_for_locally_mounted_remote_share(get_mount_point())) \ -+ and (model.is_mount_point_for_school_server(get_mount_point()) == True): -+ PassphraseDialog(self._proceed_after_receiving_passphrase, metadata) -+ else: -+ self._proceed_with_copy(metadata) -+ -+ def _proceed_after_receiving_passphrase(self, metadata, passphrase): -+ if metadata['passphrase'] != passphrase: -+ error_message = _('Passphrase does not match.') -+ if self._batch_mode: -+ self._handle_error_alert(error_message, metadata) -+ else: -+ self._handle_single_mode_notification(error_message, _('Error')) -+ return False -+ else: -+ self._unhide_info_widget_for_single_mode() -+ gobject.idle_add(self._proceed_with_copy, metadata) -+ -+ def _proceed_with_copy(self, metadata): -+ return NotImplementedError -+ -+ - - class VolumeMenu(BaseCopyMenuItem): - def __init__(self, metadata_list, label, mount_point, -@@ -761,9 +876,10 @@ class VolumeMenu(BaseCopyMenuItem): - show_progress_info_alert, batch_mode) - self._mount_point = mount_point - -- def _operate(self, metadata): -+ def _proceed_with_copy(self, metadata): - if not self._file_path_valid(metadata): - return False -+ - if not self._metadata_copy_valid(metadata, self._mount_point): - return False - -@@ -780,7 +896,7 @@ class ClipboardMenu(BaseCopyMenuItem): - batch_mode) - self._temp_file_path_list = [] - -- def _operate(self, metadata): -+ def _proceed_with_copy(self, metadata): - if not self._file_path_valid(metadata): - return False - -@@ -813,9 +929,10 @@ class DocumentsMenu(BaseCopyMenuItem): - show_progress_info_alert, - batch_mode) - -- def _operate(self, metadata): -+ def _proceed_with_copy(self, metadata): - if not self._file_path_valid(metadata): - return False -+ - if not self._metadata_copy_valid(metadata, - model.get_documents_path()): - return False -@@ -824,10 +941,41 @@ class DocumentsMenu(BaseCopyMenuItem): - self._post_operate_per_metadata_per_action(metadata) - - --class SharesMenu(BaseCopyMenuItem): -+class LocalSharesMenu(BaseCopyMenuItem): -+ def __init__(self, metadata_list, show_editing_alert, -+ show_progress_info_alert, batch_mode): -+ BaseCopyMenuItem.__init__(self, metadata_list, _('Local Shares'), -+ show_editing_alert, -+ show_progress_info_alert, -+ batch_mode) -+ -+ def _proceed_with_copy(self, metadata): -+ if not self._file_path_valid(metadata): -+ return False -+ -+ # Attach the filesize. -+ file_path = model.get_file(metadata['uid']) -+ metadata['filesize'] = os.stat(file_path).st_size -+ -+ # Attach the current mount-point. -+ from jarabe.journal.journalactivity import get_mount_point -+ metadata['mountpoint'] = get_mount_point() -+ -+ if not self._metadata_write_valid(metadata): -+ return False -+ -+ if not self._metadata_copy_valid(metadata, -+ model.LOCAL_SHARES_MOUNT_POINT): -+ return False -+ -+ # This is sync-operation. Call the post-operation now. -+ self._post_operate_per_metadata_per_action(metadata) -+ -+ -+class SchoolServerMenu(BaseCopyMenuItem): - def __init__(self, metadata_list, show_editing_alert, - show_progress_info_alert, batch_mode): -- BaseCopyMenuItem.__init__(self, metadata_list, _('Shares'), -+ BaseCopyMenuItem.__init__(self, metadata_list, _('School Server'), - show_editing_alert, - show_progress_info_alert, - batch_mode) -@@ -835,13 +983,75 @@ class SharesMenu(BaseCopyMenuItem): - def _operate(self, metadata): - if not self._file_path_valid(metadata): - return False -+ -+ # If the entry is copyable, proceed with asking the -+ # upload-passphrase. -+ PassphraseDialog(self._proceed_after_receiving_passphrase, metadata) -+ -+ def _proceed_after_receiving_passphrase(self, metadata, passphrase): -+ self._unhide_info_widget_for_single_mode() -+ gobject.idle_add(self._proceed_with_uploading, metadata, -+ passphrase) -+ -+ def _proceed_with_uploading(self, metadata, passphrase): -+ # -+ # Attach the passphrase. -+ metadata['passphrase'] = passphrase -+ -+ # Attach the filesize. -+ file_path = model.get_file(metadata['uid']) -+ metadata['filesize'] = os.stat(file_path).st_size -+ -+ # Attach the current mount-point. -+ from jarabe.journal.journalactivity import get_mount_point, \ -+ get_journal -+ metadata['mountpoint'] = get_mount_point() -+ -+ # Attach the info of the uploader. -+ from jarabe.model.buddy import get_owner_instance -+ metadata['uploader-nick'] = get_owner_instance().props.nick -+ metadata['uploader-serial'] = self.__get_serial_number() -+ -+ if not self._metadata_write_valid(metadata): -+ return False -+ - if not self._metadata_copy_valid(metadata, -- '/var/www/web1/web'): -+ model.WEBDAV_MOUNT_POINT): - return False - - # This is sync-operation. Call the post-operation now. - self._post_operate_per_metadata_per_action(metadata) - -+ def __get_serial_number(self): -+ _OFW_TREE = '/ofw' -+ _PROC_TREE = '/proc/device-tree' -+ _SN = 'serial-number' -+ _not_available = _('Not available') -+ -+ serial_no = None -+ if os.path.exists(os.path.join(_OFW_TREE, _SN)): -+ serial_no = self.__read_file(os.path.join(_OFW_TREE, _SN)) -+ elif os.path.exists(os.path.join(_PROC_TREE, _SN)): -+ serial_no = self.__read_file(os.path.join(_PROC_TREE, _SN)) -+ -+ if serial_no is None: -+ serial_no = _not_available -+ return serial_no -+ -+ def __read_file(self, path): -+ if os.access(path, os.R_OK) == 0: -+ return None -+ -+ fd = open(path, 'r') -+ value = fd.read() -+ fd.close() -+ if value: -+ value = value.strip('\n') -+ return value -+ else: -+ logging.debug('No information in file or directory: %s', path) -+ return None -+ - - class FriendsMenu(gtk.Menu): - __gtype_name__ = 'JournalFriendsMenu' -@@ -970,12 +1180,23 @@ class CopyMenuHelper(gtk.Menu): - menu.append(documents_menu) - documents_menu.show() - -- if get_mount_point() != '/var/www/web1/web': -- documents_menu = SharesMenu(metadata_list, -+ if get_mount_point() != model.LOCAL_SHARES_MOUNT_POINT: -+ local_shares_menu = LocalSharesMenu(metadata_list, -+ show_editing_alert, -+ show_progress_info_alert, -+ batch_mode) -+ local_shares_menu.set_image(Icon(icon_name='emblem-neighborhood-shared', -+ icon_size=gtk.ICON_SIZE_MENU)) -+ local_shares_menu.connect('volume-error', self.__volume_error_cb) -+ menu.append(local_shares_menu) -+ local_shares_menu.show() -+ -+ if not model.is_mount_point_for_locally_mounted_remote_share(get_mount_point()): -+ documents_menu = SchoolServerMenu(metadata_list, - show_editing_alert, - show_progress_info_alert, - batch_mode) -- documents_menu.set_image(Icon(icon_name='emblem-neighborhood-shared', -+ documents_menu.set_image(Icon(icon_name='school-server', - icon_size=gtk.ICON_SIZE_MENU)) - documents_menu.connect('volume-error', self.__volume_error_cb) - menu.append(documents_menu) -diff --git a/src/jarabe/journal/volumestoolbar.py b/src/jarabe/journal/volumestoolbar.py -index c24475d..b004657 100644 ---- a/src/jarabe/journal/volumestoolbar.py -+++ b/src/jarabe/journal/volumestoolbar.py -@@ -37,7 +37,6 @@ from sugar.graphics.palette import Palette - from sugar.graphics.xocolor import XoColor - from sugar import env - --from jarabe.frame.notification import NotificationIcon - from jarabe.journal import model - from jarabe.view.palettes import JournalVolumePalette, JournalXSPalette, RemoteSharePalette - import jarabe.frame -@@ -45,6 +44,9 @@ import jarabe.frame - - _JOURNAL_0_METADATA_DIR = '.olpc.store' - -+SHARE_TYPE_PEER = 1 -+SHARE_TYPE_SCHOOL_SERVER = 2 -+ - - def _get_id(document): - """Get the ID for the document in the xapian database.""" -@@ -211,7 +213,13 @@ class VolumesToolbar(gtk.Toolbar): - - def _set_up_volumes(self): - self._set_up_documents_button() -- self._set_up_shares_button() -+ self._set_up_local_shares_button() -+ -+ client = gconf.client_get_default() -+ color = XoColor(client.get_string('/desktop/sugar/user/color')) -+ self._add_remote_share_button(_('School-Server Shares'), -+ model.SCHOOL_SERVER_IP_ADDRESS_OR_DNS_NAME, -+ color, SHARE_TYPE_SCHOOL_SERVER) - - volume_monitor = gio.volume_monitor_get() - self._mount_added_hid = volume_monitor.connect('mount-added', -@@ -242,18 +250,28 @@ class VolumesToolbar(gtk.Toolbar): - 'user-documents', - _('Documents')) - -- def _set_up_shares_button(self): -- shares_dir_path = '/var/www/web1/web' -- self._set_up_directory_button(shares_dir_path, -+ def _set_up_local_shares_button(self): -+ local_shares_path = model.LOCAL_SHARES_MOUNT_POINT -+ self._set_up_directory_button(local_shares_path, - 'emblem-neighborhood-shared', -- _('Shares')) -- -- def _add_remote_share_button(self, buddy): -- button = RemoteSharesButton(buddy) -+ _('Local Shares')) -+ -+ def _add_remote_share_button(self, primary_text, -+ ip_address_or_dns_name, color, -+ share_type): -+ button = RemoteSharesButton(primary_text, ip_address_or_dns_name, -+ color, share_type) -+ button._share_type = share_type - button.props.group = self._volume_buttons[0] -- label = glib.markup_escape_text(_('%s\'s share') % \ -- buddy.props.nick) -- button.set_palette(RemoteSharePalette(buddy, button)) -+ -+ show_unmount_option = None -+ if share_type == SHARE_TYPE_PEER: -+ show_unmount_option = True -+ else: -+ show_unmount_option = False -+ button.set_palette(RemoteSharePalette(primary_text, -+ ip_address_or_dns_name, button, -+ show_unmount_option)) - button.connect('toggled', self._button_toggled_cb) - button.show() - -@@ -262,12 +280,7 @@ class VolumesToolbar(gtk.Toolbar): - self._volume_buttons.append(button) - self.show() - -- frame = jarabe.frame.get_view() -- notif_icon = NotificationIcon() -- notif_icon.props.icon_name = 'emblem-neighborhood-shared' -- notif_icon.props.xo_color = buddy.props.color -- frame.add_notification(notif_icon, -- gtk.CORNER_BOTTOM_RIGHT) -+ return button - - def __mount_added_cb(self, volume_monitor, mount): - self._add_button(mount) -@@ -302,13 +315,13 @@ class VolumesToolbar(gtk.Toolbar): - - def _button_toggled_cb(self, button, force_toggle=False): - if button.props.active or force_toggle: -+ button.set_active(True) - from jarabe.journal.journalactivity import get_journal - journal = get_journal() - - journal.get_list_view()._selected_entries = 0 - journal.switch_to_editing_mode(False) - journal.get_list_view().inhibit_refresh(False) -- journal.get_list_view().refresh() - - self.emit('volume-changed', button.mount_point) - -@@ -328,6 +341,14 @@ class VolumesToolbar(gtk.Toolbar): - logging.error('Couldnt find button with mount_point %r', mount_point) - return None - -+ def _get_button_for_mount_point(self, mount_point): -+ for button in self.get_children(): -+ if button.mount_point == mount_point: -+ return button -+ logging.error('Couldnt find button with mount_point %r', mount_point) -+ return None -+ -+ - def _remove_button(self, mount): - button = self._get_button_for_mount(mount) - self._volume_buttons.remove(button) -@@ -337,16 +358,20 @@ class VolumesToolbar(gtk.Toolbar): - if len(self.get_children()) < 2: - self.hide() - -- def _remove_remote_share_button(self, mount_point): -- # Here, IP_Address is the mount_point. -+ def _remove_remote_share_button(self, ip_address_or_dns_name): - for button in self.get_children(): - if type(button) == RemoteSharesButton and \ -- button.mount_point == mount_point: -+ button.mount_point == (model.WEBDAV_MOUNT_POINT + ip_address_or_dns_name): - self._volume_buttons.remove(button) - self.remove(button) -+ -+ from jarabe.journal.webdavmanager import \ -+ unmount_share_from_backend -+ unmount_share_from_backend(ip_address_or_dns_name) -+ - self.get_children()[0].props.active = True - -- if len(sel.get_children()) < 2: -+ if len(self.get_children()) < 2: - self.hide() - break; - -@@ -478,6 +503,7 @@ class DirectoryButton(BaseButton): - - def __init__(self, dir_path, icon_name): - BaseButton.__init__(self, mount_point=dir_path) -+ self._mount = dir_path - - self.props.named_icon = icon_name - -@@ -488,16 +514,22 @@ class DirectoryButton(BaseButton): - - class RemoteSharesButton(BaseButton): - -- def __init__(self, buddy): -- BaseButton.__init__(self, mount_point=buddy.props.ip_address) -+ def __init__(self, primary_text, ip_address_or_dns_name, color, -+ share_type): -+ BaseButton.__init__(self, mount_point=(model.WEBDAV_MOUNT_POINT + ip_address_or_dns_name)) -+ -+ self._primary_text = primary_text -+ self._ip_address_or_dns_name = ip_address_or_dns_name - -- self._buddy = buddy -- self.props.named_icon = 'emblem-neighborhood-shared' -- self.props.xo_color = buddy.props.color -- self._buddy_ip_address = buddy.props.ip_address -+ if share_type == SHARE_TYPE_PEER: -+ self.props.named_icon = 'emblem-neighborhood-shared' -+ elif share_type == SHARE_TYPE_SCHOOL_SERVER: -+ self.props.named_icon = 'school-server' -+ self.props.xo_color = color - - def create_palette(self): -- palette = RemoteSharePalette(self._buddy) -+ palette = RemoteSharePalette(self._primary_text, self._ip_address_or_dns_name, -+ self, True) - return palette - - -diff --git a/src/jarabe/journal/webdavmanager.py b/src/jarabe/journal/webdavmanager.py -index 6cd0713..3ff9990 100644 ---- a/src/jarabe/journal/webdavmanager.py -+++ b/src/jarabe/journal/webdavmanager.py -@@ -1,5 +1,6 @@ - from gettext import gettext as _ - -+import logging - import os - import sys - -@@ -13,11 +14,6 @@ from webdav.WebdavClient import CollectionStorer - def get_key_from_resource(resource): - return resource.path - --def ensure_correct_remote_webdav_hierarchy(remote_webdav_share_resources, -- remote_webdav_share_collections): -- pass -- #assert len(remote_webdav_share_collections.keys()) == 1 -- - class WebDavUrlManager(gobject.GObject): - """ - This class holds all data, relevant to a WebDavUrl. -@@ -50,6 +46,9 @@ class WebDavUrlManager(gobject.GObject): - def _get_number_of_collections(self): - return len(self._remote_webdav_share_collections) - -+ def _get_root(self): -+ return self._root -+ - def _get_resources_dict(self): - return self._remote_webdav_share_resources - -@@ -57,7 +56,13 @@ class WebDavUrlManager(gobject.GObject): - return self._remote_webdav_share_collections - - def _get_resource_by_key(self, key): -- return self._remote_webdav_share_resources[key]['resource'] -+ if key in self._remote_webdav_share_resources.keys(): -+ return self._remote_webdav_share_resources[key]['resource'] -+ return None -+ -+ def _add_or_replace_resource_by_key(self, key, resource): -+ self._remote_webdav_share_resources[key] = {} -+ self._remote_webdav_share_resources[key]['resource'] = resource - - def _get_metadata_list(self): - metadata_list = [] -@@ -69,11 +74,9 @@ class WebDavUrlManager(gobject.GObject): - resource_container = self._remote_webdav_share_resources[resource_key] - return resource_container['webdav-properties'] - -- def _set_metadata_for_resource(self, key, metadata): -- self._remote_webdav_share_resources[key]['metadata'] = metadata -- - def _fetch_resources_and_collections(self): - webdavConnection = CollectionStorer(self._WebDavUrl, validateResourceNames=False) -+ self._root = webdavConnection - - authFailures = 0 - while authFailures < 2: -@@ -111,8 +114,13 @@ class WebDavUrlManager(gobject.GObject): - error_message = e - get_journal()._volume_error_cb(None, error_message,_('Error')) - -- # Simply return, in case of connection-not-available. -- return False -+ # Re-raise this error. -+ # Note that since this is not an -+ # "AuthorizationError", this will not be caught -+ # by the outer except-block. Instead, it will -+ # navigate all the way back up, and will report -+ # the error in the enclosing except block. -+ raise e - - else: - # If this indeed is an Authorization Error, -@@ -138,25 +146,54 @@ class WebDavUrlManager(gobject.GObject): - - webdav_manager = {} - -+def get_data_webdav_manager(ip_address_or_dns_name): -+ return webdav_manager[ip_address_or_dns_name]['data'] -+ -+ -+def get_metadata_webdav_manager(ip_address_or_dns_name): -+ return webdav_manager[ip_address_or_dns_name]['metadata'] - --def get_resource_by_ip_address_and_resource_key(ip_address, key): -- global webdav_manager - -- if ip_address in webdav_manager.keys(): -- root_webdav = webdav_manager[ip_address] -- resources_dict = root_webdav._get_resources_dict() -+def get_resource_by_resource_key(root_webdav, key): -+ resources_dict = root_webdav._get_resources_dict() -+ if key in resources_dict.keys(): - resource_dict = resources_dict[key] - resource = resource_dict['resource'] -- - return resource -+ return None - - --def get_remote_webdav_share_metadata(ip_address): -- protocol = 'dav://' -+def add_resource_by_resource_key(root_webdav, key, -+ content_file_path): -+ root = root_webdav._get_root() - -- root_webdav_url = '/webdav' -+ resource = root.addResource(key) -+ -+ # Procure the resource-lock. -+ lockToken = resource.lock('olpc') -+ -+ input_stream = open(content_file_path) - -- complete_root_url = protocol + ip_address + root_webdav_url -+ # Now, upload the data; but it's necessary to enclose this in a -+ # try-except-finally block here, since we need to close the -+ # input-stream, whatever may happen. -+ try: -+ resource.uploadFile(input_stream, lockToken) -+ root_webdav._add_or_replace_resource_by_key(key, resource) -+ except Exception, e: -+ logging.exception(e) -+ resource.delete(lockToken) -+ raise e -+ else: -+ resource.unlock(lockToken) -+ finally: -+ input_stream.close() -+ -+ -+def get_remote_webdav_share_metadata(ip_address_or_dns_name): -+ protocol = 'davs://' -+ root_webdav_url = '/webdav' -+ complete_root_url = protocol + ip_address_or_dns_name + root_webdav_url - - root_webdav = WebDavUrlManager(complete_root_url, 'test', 'olpc') - if root_webdav._fetch_resources_and_collections() is False: -@@ -165,7 +202,8 @@ def get_remote_webdav_share_metadata(ip_address): - - # Keep reference to the "WebDavUrlManager", keyed by IP-Address. - global webdav_manager -- webdav_manager[ip_address] = root_webdav -+ webdav_manager[ip_address_or_dns_name] = {} -+ webdav_manager[ip_address_or_dns_name]['data'] = root_webdav - - - # Assert that the number of collections is only one at this url -@@ -174,12 +212,14 @@ def get_remote_webdav_share_metadata(ip_address): - - root_sugar_metadata_url = root_webdav_url + '/.Sugar-Metadata' - -- complete_root_sugar_metadata_url = protocol + ip_address + root_sugar_metadata_url -+ complete_root_sugar_metadata_url = protocol + ip_address_or_dns_name + root_sugar_metadata_url - root_webdav_sugar_metadata = WebDavUrlManager(complete_root_sugar_metadata_url, 'test', 'olpc') - if root_webdav_sugar_metadata._fetch_resources_and_collections() is False: - # Return empty metadata list. - return [] - -+ webdav_manager[ip_address_or_dns_name]['metadata'] = root_webdav_sugar_metadata -+ - # assert that the number of collections is zero at this url. - assert root_webdav_sugar_metadata._get_number_of_collections() == 0 - -@@ -189,68 +229,82 @@ def get_remote_webdav_share_metadata(ip_address): - root_webdav_sugar_metadata_resources = root_webdav_sugar_metadata._get_resources_dict() - - # Prepare the metadata-download folder. -- downloaded_data_root_dir = '/tmp/' + ip_address -+ downloaded_data_root_dir = '/tmp/' + ip_address_or_dns_name - downloaded_metadata_file_dir = downloaded_data_root_dir + '/.Sugar-Metadata' - if os.path.isdir(downloaded_data_root_dir): - shutil.rmtree(downloaded_data_root_dir) - os.makedirs(downloaded_metadata_file_dir) - -- for root_webdav_resource_name in root_webdav_resources.keys(): -- """ -- root_webdav_resource_name is of the type :: -+ metadata_list = [] - -- /webdav/a.txt -+ # Note that the presence of a resource in the metadata directory, -+ # is the only assurance of the entry (and its constituents) being -+ # present in entirety. Thus, always proceed taking the metadata as -+ # the "key". -+ for root_webdav_sugar_metadata_resource_name in root_webdav_sugar_metadata_resources.keys(): - """ -- split_tokens_array = root_webdav_resource_name.split('/') -+ root_webdav_sugar_metadata_resource_name is of the type :: - -- # This will provide us with "a.txt" -- basename = split_tokens_array[len(split_tokens_array) - 1] -+ /webdav/.Sugar-Metadata/a.txt.metadata, OR -+ /webdav/.Sugar-Metadata/a.txt.preview -+ """ - -- # This will provide us with "a.txt.metadata" -- sugar_metadata_basename = basename + '.metadata' -+ # If this is a "preview" resource, continue forward, as we only -+ # want the metadata list. The "preview" resources are anyways -+ # already present in the manager DS. -+ if root_webdav_sugar_metadata_resource_name.endswith('.preview'): -+ continue - -- # Thus will provide us with "/webdav/.Sugar-Metadata/a.txt.metadata" -- sugar_metadata_url = root_sugar_metadata_url + '/' + sugar_metadata_basename -+ split_tokens_array = root_webdav_sugar_metadata_resource_name.split('/') - -- # Ensure that "sugar_metadata_url" is present as one of the -- # keys in "root_webdav_sugar_metadata_resources" -- assert sugar_metadata_url in root_webdav_sugar_metadata_resources.keys() -+ # This will provide us with "a.txt.metadata" -+ sugar_metadata_basename = split_tokens_array[len(split_tokens_array) - 1] - -- # Now download the metadata file, read its contents, and store -- # the metadata in memory. -- # It is assumed that the metadata-file is small enough to be -- # read in one call to "read". -+ # This will provide us with "a.txt" -+ basename = sugar_metadata_basename[0:sugar_metadata_basename.index('.metadata')] - - downloaded_metadata_file_path = downloaded_metadata_file_dir + '/' + sugar_metadata_basename -- metadata_resource = root_webdav_sugar_metadata._get_resource_by_key(sugar_metadata_url) -+ metadata_resource = \ -+ root_webdav_sugar_metadata._get_resource_by_key(root_webdav_sugar_metadata_resource_name) - metadata_resource.downloadFile(downloaded_metadata_file_path) - -+ -+ # We need to download the preview-file as well at this stage, -+ # so that it can be shown in the expanded entry. -+ downloaded_preview_file_path = downloaded_metadata_file_dir + \ -+ '/' + basename + '.preview' -+ root_webdav_sugar_preview_resource_name = \ -+ root_webdav_sugar_metadata_resource_name[0:root_webdav_sugar_metadata_resource_name.index('.metadata')] + \ -+ '.preview' -+ preview_resource = \ -+ root_webdav_sugar_metadata._get_resource_by_key(root_webdav_sugar_preview_resource_name) -+ if preview_resource is not None: -+ preview_resource.downloadFile(downloaded_preview_file_path) -+ -+ - file_pointer = open(downloaded_metadata_file_path) - metadata = eval(file_pointer.read()) - file_pointer.close() - -- # Very critical. -- # 1. CRITICAL ONE: -- # Fill in the uid. -- # Note that the file is not physically present. -+ # Fill in the missing metadata properties. -+ # Note that the file is not physically present. - metadata['uid'] = downloaded_data_root_dir + '/' + basename -- -- # 2. CRITICAL TWO: -- # Fill in the properties, that can only be done by reading -- # in the webdav-properties. -- live_properties = root_webdav._get_live_properties(root_webdav_resource_name) -- metadata['filesize'] = live_properties.getContentLength() -- metadata['timestamp'] = live_properties.getLastModified() -- metadata['creation_time'] = live_properties.getCreationDate() -+ metadata['creation_time'] = metadata['timestamp'] - - # Now, write this to the metadata-file, so that - # webdav-properties get gelled into sugar-metadata. -- - file_pointer = open(downloaded_metadata_file_path, 'w') - file_pointer.write(simplejson.dumps(metadata)) - file_pointer.close() - -- root_webdav._set_metadata_for_resource(root_webdav_resource_name, -- metadata) -+ metadata_list.append(metadata) -+ -+ return metadata_list -+ -+ -+def is_remote_webdav_loaded(ip_address_or_dns_name): -+ return ip_address_or_dns_name in webdav_manager.keys() -+ - -- return root_webdav._get_metadata_list() -+def unmount_share_from_backend(ip_address_or_dns_name): -+ del webdav_manager[ip_address_or_dns_name] -diff --git a/src/jarabe/view/buddymenu.py b/src/jarabe/view/buddymenu.py -index dfbcfa3..f6af1b5 100644 ---- a/src/jarabe/view/buddymenu.py -+++ b/src/jarabe/view/buddymenu.py -@@ -27,6 +27,7 @@ from sugar.graphics.palette import Palette - from sugar.graphics.menuitem import MenuItem - from sugar.graphics.icon import Icon - -+from jarabe.frame.notification import NotificationIcon - from jarabe.model import shell - from jarabe.model import friends - from jarabe.model.session import get_session_manager -@@ -73,11 +74,17 @@ class BuddyMenu(Palette): - self.menu.append(menu_item) - menu_item.show() - -- access_buddy_remote_share_menu_item = MenuItem(_('Access Share'), 'list-add') -- access_buddy_remote_share_menu_item.connect('activate', -- self._access_share_cb) -- self.menu.append(access_buddy_remote_share_menu_item) -- access_buddy_remote_share_menu_item.show() -+ remote_share_menu_item = None -+ from jarabe.journal import webdavmanager -+ if not webdavmanager.is_remote_webdav_loaded(self._buddy.props.ip_address): -+ remote_share_menu_item = MenuItem(_('Access Share'), 'list-add') -+ remote_share_menu_item.connect('activate', self._access_share_cb) -+ else: -+ remote_share_menu_item = MenuItem(_('Unmount Share'), 'list-remove') -+ remote_share_menu_item.connect('activate', self.__unmount_cb) -+ -+ self.menu.append(remote_share_menu_item) -+ remote_share_menu_item.show() - - self._invite_menu = MenuItem('') - self._invite_menu.connect('activate', self._invite_friend_cb) -@@ -89,10 +96,36 @@ class BuddyMenu(Palette): - activity = home_model.get_active_activity() - self._update_invite_menu(activity) - -+ def __unmount_cb(self, menuitem): -+ from jarabe.journal.journalactivity import get_journal -+ singleton_volumes_toolbar = get_journal().get_volumes_toolbar() -+ singleton_volumes_toolbar._remove_remote_share_button(self._buddy.props.ip_address) -+ - def _access_share_cb(self, menuitem): - from jarabe.journal.journalactivity import get_journal - volumes_toolbar = get_journal().get_volumes_toolbar() -- volumes_toolbar._add_remote_share_button(self._buddy) -+ -+ # TRANS: Do not translate the """%s""". -+ primary_text = _('%s\'s Shares') % self._buddy.props.nick -+ button = volumes_toolbar._add_remote_share_button(primary_text, -+ self._buddy.props.ip_address, -+ self._buddy.props.color, -+ jarabe.journal.volumestoolbar.SHARE_TYPE_PEER) -+ -+ # Now, make three levels of transitions, from the -+ # Neighborhood-view. -+ -+ # 1. Switch to the home-view. -+ from jarabe.model import shell -+ from jarabe.model.shell import ShellModel -+ shell.get_model().set_zoom_level(ShellModel.ZOOM_HOME) -+ -+ # 2. Switch to the journal-activity-view. -+ top_frame = jarabe.frame.get_view() -+ top_frame.switch_to_journal_activity() -+ -+ # 3. Switch to the newly mounted-view. -+ volumes_toolbar._button_toggled_cb(button, force_toggle=True) - - def _add_my_items(self): - item = MenuItem(_('Shutdown'), 'system-shutdown') -diff --git a/src/jarabe/view/palettes.py b/src/jarabe/view/palettes.py -index 3b26faf..f6bca48 100644 ---- a/src/jarabe/view/palettes.py -+++ b/src/jarabe/view/palettes.py -@@ -339,33 +339,35 @@ class JournalXSPalette(Palette): - - - class RemoteSharePalette(Palette): -- def __init__(self, buddy, button): -- Palette.__init__(self, label=('%s\'s share' % buddy.props.nick)) -- self._buddy = buddy -+ def __init__(self, primary_text, ip_address_or_dns_name, button, -+ show_unmount_option): -+ Palette.__init__(self, label=primary_text) - self._button = button -+ self._ip_address_or_dns_name = ip_address_or_dns_name - -- self.props.secondary_text = glib.markup_escape_text(buddy.props.ip_address) -+ self.props.secondary_text = \ -+ glib.markup_escape_text(self._ip_address_or_dns_name) - -- vbox = gtk.VBox() -- self.set_content(vbox) -- vbox.show() -+ if show_unmount_option == True: -+ vbox = gtk.VBox() -+ self.set_content(vbox) -+ vbox.show() - -- self.connect('popup', self.__popup_cb) -+ self.connect('popup', self.__popup_cb) -+ menu_item = MenuItem(pgettext('Share', 'Unmount')) -+ icon = Icon(icon_name='media-eject', icon_size=gtk.ICON_SIZE_MENU) -+ menu_item.set_image(icon) -+ icon.show() - -- menu_item = MenuItem(pgettext('Share', 'Unmount')) -- -- icon = Icon(icon_name='media-eject', icon_size=gtk.ICON_SIZE_MENU) -- menu_item.set_image(icon) -- icon.show() -- -- menu_item.connect('activate', self.__unmount_activate_cb) -- self.menu.append(menu_item) -- menu_item.show() -+ menu_item.connect('activate', self.__unmount_activate_cb) -+ self.menu.append(menu_item) -+ menu_item.show() - - def __unmount_activate_cb(self, menu_item): - from jarabe.journal.journalactivity import get_journal -+ - singleton_volumes_toolbar = get_journal().get_volumes_toolbar() -- singleton_volumes_toolbar._remove_remote_share_button(self._buddy.props.ip_address) -+ singleton_volumes_toolbar._remove_remote_share_button(self._ip_address_or_dns_name) - - def __popup_cb(self, palette): - pass -diff --git a/src/webdav/Connection.py b/src/webdav/Connection.py -index 66f7833..33719f9 100644 ---- a/src/webdav/Connection.py -+++ b/src/webdav/Connection.py -@@ -197,7 +197,9 @@ class Connection(DAV): - path = _urlEncode(path) - try: - HTTPConnection.request(self, 'PUT', path, "", header) -- self._blockCopySocket(srcfile, self, Connection.blockSize) -+ filesize = os.path.getsize(srcfile.name) -+ self._blockCopySocket(srcfile, self, -+ Connection.blockSize,filesize) - srcfile.close() - response = self.getresponse() - except (CannotSendRequest, socket.error, BadStatusLine, ResponseNotReady), exc: -@@ -215,14 +217,15 @@ class Connection(DAV): - finally: - self._lock.release() - -- def _blockCopySocket(self, source, toSocket, blockSize): -+ def _blockCopySocket(self, source, toSocket, blockSize, filesize): - transferedBytes = 0 - block = source.read(blockSize) -- #while source.readinto(block, blockSize): - while len(block): -- toSocket.send(block) - self.logger.debug("Wrote %d bytes." % len(block)) - transferedBytes += len(block) -+ toSocket.send(block) -+ from jarabe.journal.journalactivity import get_journal -+ get_journal().update_progress(transferedBytes/(filesize*1.0)) - block = source.read(blockSize) - self.logger.info("Transfered %d bytes." % transferedBytes) - -diff --git a/src/webdav/WebdavClient.py b/src/webdav/WebdavClient.py -index ab5cec3..8ce5c77 100644 ---- a/src/webdav/WebdavClient.py -+++ b/src/webdav/WebdavClient.py -@@ -336,6 +336,9 @@ class ResourceStorer(object): - else: - header["Content-length"] = 0 - -+ # We need to change the header["Content-length"] to a string. -+ header["Content-length"] = str(header["Content-length"]) -+ - try: - response = self.connection.put(self.path, content, extra_hdrs=header) - finally: -@@ -367,7 +370,8 @@ class ResourceStorer(object): - # TODO: Other interface ? return self.connection.getfile() - return response - -- def downloadFile(self, localFileName): -+ def downloadFile(self, localFileName, show_progress=False, -+ filesize=0): - """ - Copy binary data from permanent storage to a local file. - -@@ -377,7 +381,8 @@ class ResourceStorer(object): - remoteFile = self.downloadContent() - try: - socket.setdefaulttimeout(SOCKET_DEFAULT_TIMEOUT) -- _blockCopyFile(remoteFile, localFile, Connection.blockSize) -+ _blockCopyFile(remoteFile, localFile, Connection.blockSize, -+ show_progress, filesize) - except socket.error, e: - raise e - remoteFile.close() -@@ -807,7 +812,7 @@ class LockToken(object): - return self.value() - - --def _blockCopyFile(source, dest, blockSize): -+def _blockCopyFile(source, dest, blockSize, show_progress, filesize): - """ - Copies a file in chunks of C{blockSize}. - -@@ -821,8 +826,11 @@ def _blockCopyFile(source, dest, blockSize): - transferedBytes = 0 - block = source.read(blockSize) - while len(block): -- dest.write(block) - transferedBytes += len(block); -+ dest.write(block) -+ if show_progress: -+ from jarabe.journal.journalactivity import get_journal -+ get_journal().update_progress(transferedBytes/(filesize * 1.0)) - block = source.read(blockSize) - - def _checkUrl(url): -diff --git a/src/webdav/davlib.py b/src/webdav/davlib.py -index f4dac91..a611e51 100644 ---- a/src/webdav/davlib.py -+++ b/src/webdav/davlib.py -@@ -31,7 +31,7 @@ BLOCKSIZE = 16384 - class HTTPProtocolChooser(httplib.HTTPSConnection): - def __init__(self, *args, **kw): - self.protocol = kw.pop('protocol') -- if self.protocol == "https": -+ if self.__is_secure_protocol(): - self.default_port = 443 - else: - self.default_port = 80 -@@ -39,11 +39,14 @@ class HTTPProtocolChooser(httplib.HTTPSConnection): - apply(httplib.HTTPSConnection.__init__, (self,) + args, kw) - - def connect(self): -- if self.protocol == "https": -+ if self.__is_secure_protocol(): - httplib.HTTPSConnection.connect(self) - else: - httplib.HTTPConnection.connect(self) - -+ def __is_secure_protocol(self): -+ return (self.protocol == 'https') or (self.protocol == 'davs') -+ - - class HTTPConnectionAuth(HTTPProtocolChooser): - def __init__(self, *args, **kw): -@@ -333,4 +336,4 @@ class DAV(HTTPConnectionAuth): - response = self.lock(url, owner, timeout, depth) - response.parse_lock_response() - return response.locktoken -- -\ No newline at end of file -+ --- -1.7.4.4 - diff --git a/rpms/sugar/0130-Remove-1-to-N-feature-for-AU.patch b/rpms/sugar/0130-Remove-1-to-N-feature-for-AU.patch deleted file mode 100644 index 39fb40f..0000000 --- a/rpms/sugar/0130-Remove-1-to-N-feature-for-AU.patch +++ /dev/null @@ -1,6700 +0,0 @@ -From 242544979105932b4e47e81d54b7032dc4ad650c Mon Sep 17 00:00:00 2001 -From: Ajay Garg <ajay@activitycentral.com> -Date: Sat, 18 Aug 2012 23:13:22 +0530 -Subject: [PATCH] Remove 1-to-N feature for AU. -Organization: Sugar Labs Foundation - - -Signed-off-by: Ajay Garg <ajay@activitycentral.com> ---- - configure.ac | 2 - - src/Makefile.am | 3 +- - src/jarabe/journal/Makefile.am | 3 +- - src/jarabe/journal/journaltoolbox.py | 7 - - src/jarabe/journal/listview.py | 9 - - src/jarabe/journal/model.py | 162 ------- - src/jarabe/journal/palettes.py | 71 --- - src/jarabe/journal/volumestoolbar.py | 59 +--- - src/jarabe/journal/webdavmanager.py | 256 ----------- - src/jarabe/view/buddymenu.py | 11 - - src/jarabe/view/palettes.py | 33 -- - src/webdav/Condition.py | 475 ------------------- - src/webdav/Connection.py | 321 ------------- - src/webdav/Constants.py | 199 -------- - src/webdav/Makefile.am | 20 - - src/webdav/NameCheck.py | 193 -------- - src/webdav/Utils.py | 154 ------- - src/webdav/VersionHandler.py | 198 -------- - src/webdav/WebdavClient.py | 840 ---------------------------------- - src/webdav/WebdavRequests.py | 205 --------- - src/webdav/WebdavResponse.py | 525 --------------------- - src/webdav/__init__.py | 16 - - src/webdav/acp/Ace.py | 293 ------------ - src/webdav/acp/AceHandler.py | 182 -------- - src/webdav/acp/Acl.py | 311 ------------- - src/webdav/acp/GrantDeny.py | 241 ---------- - src/webdav/acp/Makefile.am | 12 - - src/webdav/acp/Principal.py | 189 -------- - src/webdav/acp/Privilege.py | 125 ----- - src/webdav/acp/__init__.py | 33 -- - src/webdav/davlib.py | 336 -------------- - src/webdav/logger.py | 51 -- - src/webdav/qp_xml.py | 240 ---------- - src/webdav/uuid_.py | 476 ------------------- - 34 files changed, 3 insertions(+), 6248 deletions(-) - delete mode 100644 src/jarabe/journal/webdavmanager.py - delete mode 100644 src/webdav/Condition.py - delete mode 100644 src/webdav/Connection.py - delete mode 100644 src/webdav/Constants.py - delete mode 100644 src/webdav/Makefile.am - delete mode 100644 src/webdav/NameCheck.py - delete mode 100644 src/webdav/Utils.py - delete mode 100644 src/webdav/VersionHandler.py - delete mode 100644 src/webdav/WebdavClient.py - delete mode 100644 src/webdav/WebdavRequests.py - delete mode 100644 src/webdav/WebdavResponse.py - delete mode 100644 src/webdav/__init__.py - delete mode 100644 src/webdav/acp/Ace.py - delete mode 100644 src/webdav/acp/AceHandler.py - delete mode 100644 src/webdav/acp/Acl.py - delete mode 100644 src/webdav/acp/GrantDeny.py - delete mode 100644 src/webdav/acp/Makefile.am - delete mode 100644 src/webdav/acp/Principal.py - delete mode 100644 src/webdav/acp/Privilege.py - delete mode 100644 src/webdav/acp/__init__.py - delete mode 100644 src/webdav/davlib.py - delete mode 100644 src/webdav/logger.py - delete mode 100644 src/webdav/qp_xml.py - delete mode 100644 src/webdav/uuid_.py - -diff --git a/configure.ac b/configure.ac -index e04bdec..b7ef245 100644 ---- a/configure.ac -+++ b/configure.ac -@@ -77,8 +77,6 @@ src/jarabe/model/Makefile - src/jarabe/util/Makefile - src/jarabe/util/telepathy/Makefile - src/jarabe/view/Makefile --src/webdav/acp/Makefile --src/webdav/Makefile - src/Makefile - ]) - -diff --git a/src/Makefile.am b/src/Makefile.am -index 765da8b..dcaaec6 100644 ---- a/src/Makefile.am -+++ b/src/Makefile.am -@@ -1,3 +1,2 @@ - SUBDIRS = \ -- jarabe \ -- webdav -+ jarabe -diff --git a/src/jarabe/journal/Makefile.am b/src/jarabe/journal/Makefile.am -index 8efca6d..96d6ee8 100644 ---- a/src/jarabe/journal/Makefile.am -+++ b/src/jarabe/journal/Makefile.am -@@ -16,5 +16,4 @@ sugar_PYTHON = \ - objectchooser.py \ - palettes.py \ - volumestoolbar.py \ -- processdialog.py \ -- webdavmanager.py -+ processdialog.py -diff --git a/src/jarabe/journal/journaltoolbox.py b/src/jarabe/journal/journaltoolbox.py -index 6b2494e..18b523f 100644 ---- a/src/jarabe/journal/journaltoolbox.py -+++ b/src/jarabe/journal/journaltoolbox.py -@@ -637,13 +637,6 @@ class BatchEraseButton(ToolButton, palettes.ActionItem): - show_not_completed_ops_info=True) - self.props.tooltip = _('Erase') - -- # De-sensitize Batch-Erase button, for locally-mounted-remote-shares. -- from jarabe.journal.journalactivity import get_mount_point -- current_mount_point = get_mount_point() -- -- if model.is_mount_point_for_locally_mounted_remote_share(current_mount_point): -- self.set_sensitive(False) -- - def _get_actionable_signal(self): - return 'clicked' - -diff --git a/src/jarabe/journal/listview.py b/src/jarabe/journal/listview.py -index 333d88a..7ff0709 100644 ---- a/src/jarabe/journal/listview.py -+++ b/src/jarabe/journal/listview.py -@@ -728,15 +728,6 @@ class ListView(BaseListView): - self.emit('volume-error', message, severity) - - def __icon_clicked_cb(self, cell, path): -- # For locally-mounted remote shares, we do not want to launch -- # by clicking on the icons. -- # So, check if this is a part of locally-mounted-remote share, -- # and if yes, return, without doing anything. -- from jarabe.journal.journalactivity import get_mount_point -- current_mount_point = get_mount_point() -- if model.is_mount_point_for_locally_mounted_remote_share(current_mount_point): -- return -- - row = self.tree_view.get_model()[path] - metadata = model.get(row[ListModel.COLUMN_UID]) - misc.resume(metadata) -diff --git a/src/jarabe/journal/model.py b/src/jarabe/journal/model.py -index 422e947..7cd0a79 100644 ---- a/src/jarabe/journal/model.py -+++ b/src/jarabe/journal/model.py -@@ -43,8 +43,6 @@ from sugar import dispatch - from sugar import mime - from sugar import util - --from jarabe.journal.webdavmanager import get_remote_webdav_share_metadata -- - DS_DBUS_SERVICE = 'org.laptop.sugar.DataStore' - DS_DBUS_INTERFACE = 'org.laptop.sugar.DataStore' - DS_DBUS_PATH = '/org/laptop/sugar/DataStore' -@@ -429,123 +427,6 @@ class InplaceResultSet(BaseResultSet): - return - - --class RemoteShareResultSet(object): -- def __init__(self, ip_address, query): -- self._ip_address = ip_address -- self._file_list = [] -- -- self.ready = dispatch.Signal() -- self.progress = dispatch.Signal() -- -- # First time, query is none. -- if query is None: -- return -- -- query_text = query.get('query', '') -- if query_text.startswith('"') and query_text.endswith('"'): -- self._regex = re.compile('*%s*' % query_text.strip(['"'])) -- elif query_text: -- expression = '' -- for word in query_text.split(' '): -- expression += '(?=.*%s.*)' % word -- self._regex = re.compile(expression, re.IGNORECASE) -- else: -- self._regex = None -- -- if query.get('timestamp', ''): -- self._date_start = int(query['timestamp']['start']) -- self._date_end = int(query['timestamp']['end']) -- else: -- self._date_start = None -- self._date_end = None -- -- self._mime_types = query.get('mime_type', []) -- -- self._sort = query.get('order_by', ['+timestamp'])[0] -- -- def setup(self): -- metadata_list_complete = get_remote_webdav_share_metadata(self._ip_address) -- for metadata in metadata_list_complete: -- -- add_to_list = False -- if self._regex is not None: -- for f in ['fulltext', 'title', -- 'description', 'tags']: -- if f in metadata and \ -- self._regex.match(metadata[f]): -- add_to_list = True -- break -- else: -- add_to_list = True -- if not add_to_list: -- continue -- -- add_to_list = False -- if self._date_start is not None: -- if metadata['timestamp'] > self._date_start: -- add_to_list = True -- else: -- add_to_list = True -- if not add_to_list: -- continue -- -- add_to_list = False -- if self._date_end is not None: -- if metadata['timestamp'] < self._date_end: -- add_to_list = True -- else: -- add_to_list = True -- if not add_to_list: -- continue -- -- add_to_list = False -- if self._mime_types: -- mime_type = metadata['mime_type'] -- if mime_type in self._mime_types: -- add_to_list = True -- else: -- add_to_list = True -- if not add_to_list: -- continue -- -- # If control reaches here, the current metadata has passed -- # out all filter-tests. -- file_info = (metadata['timestamp'], -- metadata['creation_time'], -- metadata['filesize'], -- metadata) -- self._file_list.append(file_info) -- -- if self._sort[1:] == 'filesize': -- keygetter = itemgetter(2) -- elif self._sort[1:] == 'creation_time': -- keygetter = itemgetter(1) -- else: -- # timestamp -- keygetter = itemgetter(0) -- -- self._file_list.sort(lambda a, b: cmp(b, a), -- key=keygetter, -- reverse=(self._sort[0] == '-')) -- -- self.ready.send(self) -- -- def get_length(self): -- return len(self._file_list) -- -- length = property(get_length) -- -- def seek(self, position): -- self._position = position -- -- def read(self): -- modified_timestamp, creation_timestamp, filesize, metadata = self._file_list[self._position] -- return metadata -- -- def stop(self): -- self._stopped = True -- -- - def _get_file_metadata(path, stat, fetch_preview=True): - """Return the metadata from the corresponding file. - -@@ -664,13 +545,6 @@ def find(query_, page_size): - For Journal. - """ - return DatastoreResultSet(query, page_size) -- elif is_mount_point_for_locally_mounted_remote_share(mount_points[0]): -- """ -- For Locally-Mounted-Remote-Shares. -- Regex Matching is used, to ensure that the mount-point is an -- IP-Address. -- """ -- return RemoteShareResultSet(mount_points[0], query) - else: - """ - For Documents/Shares/Mounted-Drives. -@@ -678,15 +552,6 @@ def find(query_, page_size): - return InplaceResultSet(query, page_size, mount_points[0]) - - --def is_mount_point_for_locally_mounted_remote_share(mount_point): -- import re -- -- pattern = '[1-9][0-9]{0,2}\.[0-9]{0,3}\.[0-9]{0,3}\.[0-9]{0,3}' -- if re.match(pattern, mount_point) is None: -- return False -- return True -- -- - def _get_mount_point(path): - dir_path = os.path.dirname(path) - while dir_path: -@@ -697,19 +562,6 @@ def _get_mount_point(path): - return None - - --def is_locally_mounted_remote_share(path): -- return string.find(path, '/tmp/') == 0 -- -- --def extract_ip_address_from_locally_mounted_remote_share_path(path): -- """ -- Path is of type :: -- -- /tmp/127.0.0.1/webdav/a.txt -- """ -- return path.split('/')[2] -- -- - def get(object_id): - """Returns the metadata for an object - """ -@@ -945,20 +797,6 @@ def _write_entry_on_external_device(metadata, file_path): - _rename_entry_on_external_device(file_path, destination_path, - metadata_dir_path) - -- # For "Shares" folder, we need to set the permissions of the newly -- # copied file to 0777, else it will not be accessible by "httpd" -- # service. -- if metadata['mountpoint'] == '/var/www/web1/web': -- fd = os.open(destination_path, os.O_RDONLY) -- os.fchmod(fd, stat.S_IRWXU | stat.S_IRWXG | stat.S_IRWXO) -- os.close(fd) -- -- metadata_file_path = os.path.join(metadata_dir_path, file_name + '.metadata') -- fd = os.open(metadata_file_path, os.O_RDONLY) -- os.fchmod(fd, stat.S_IRWXU | stat.S_IRWXG | stat.S_IRWXO) -- os.close(fd) -- -- - object_id = destination_path - created.send(None, object_id=object_id) - -diff --git a/src/jarabe/journal/palettes.py b/src/jarabe/journal/palettes.py -index 66dcadc..633d2ba 100644 ---- a/src/jarabe/journal/palettes.py -+++ b/src/jarabe/journal/palettes.py -@@ -28,8 +28,6 @@ import gtk - import gconf - import gio - import glib --import time --import socket - - from sugar import _sugarext - -@@ -50,10 +48,6 @@ from jarabe.journal.journalwindow import freeze_ui, \ - show_normal_cursor, \ - show_waiting_cursor - --from webdav.Connection import WebdavError --from jarabe.journal.webdavmanager import get_resource_by_ip_address_and_resource_key -- -- - friends_model = friends.get_model() - - _copy_menu_helper = None -@@ -99,15 +93,10 @@ class ObjectPalette(Palette): - resume_with_label = _('Start with') - menu_item = MenuItem(resume_label, 'activity-start') - menu_item.connect('activate', self.__start_activate_cb) -- if model.is_mount_point_for_locally_mounted_remote_share(current_mount_point): -- menu_item.set_sensitive(False) - self.menu.append(menu_item) - menu_item.show() - - menu_item = MenuItem(resume_with_label, 'activity-start') -- if model.is_mount_point_for_locally_mounted_remote_share(current_mount_point): -- menu_item.set_sensitive(False) -- - self.menu.append(menu_item) - menu_item.show() - start_with_menu = StartWithMenu(self._metadata) -@@ -146,14 +135,10 @@ class ObjectPalette(Palette): - icon_size=gtk.ICON_SIZE_MENU) - menu_item.set_image(icon) - menu_item.connect('activate', self.__duplicate_activate_cb) -- if model.is_mount_point_for_locally_mounted_remote_share(current_mount_point): -- menu_item.set_sensitive(False) - self.menu.append(menu_item) - menu_item.show() - - menu_item = MenuItem(_('Send to'), 'document-send') -- if model.is_mount_point_for_locally_mounted_remote_share(current_mount_point): -- menu_item.set_sensitive(False) - self.menu.append(menu_item) - menu_item.show() - -@@ -164,15 +149,11 @@ class ObjectPalette(Palette): - if detail == True: - menu_item = MenuItem(_('View Details'), 'go-right') - menu_item.connect('activate', self.__detail_activate_cb) -- if model.is_mount_point_for_locally_mounted_remote_share(current_mount_point): -- menu_item.set_sensitive(False) - self.menu.append(menu_item) - menu_item.show() - - menu_item = MenuItem(_('Erase'), 'list-remove') - menu_item.connect('activate', self.__erase_activate_cb) -- if model.is_mount_point_for_locally_mounted_remote_share(current_mount_point): -- menu_item.set_sensitive(False) - self.menu.append(menu_item) - menu_item.show() - -@@ -633,28 +614,6 @@ class ActionItem(gobject.GObject): - from jarabe.journal.journalactivity import get_mount_point - current_mount_point = get_mount_point() - -- # Now, for locally mounted remote-shares, download the file. -- # Note that, always download the file, to avoid the problems -- # of stale-cache. -- if model.is_mount_point_for_locally_mounted_remote_share(current_mount_point): -- file_path = metadata['uid'] -- filename = os.path.basename(file_path) -- ip_address = model.extract_ip_address_from_locally_mounted_remote_share_path(file_path) -- resource = get_resource_by_ip_address_and_resource_key(ip_address, '/webdav/' + filename) -- download_file_path = '/tmp/' + ip_address + '/' + filename -- try: -- resource.downloadFile(download_file_path) -- return True -- except (WebdavError, socket.error), e: -- error_message = e -- logging.warn(error_message) -- if self._batch_mode: -- self._handle_error_alert(error_message, metadata) -- else: -- self.emit('volume-error', error_message, -- _('Error')) -- return False -- - file_path = model.get_file(metadata['uid']) - if not file_path or not os.path.exists(file_path): - logging.warn('Entries without a file cannot be copied.') -@@ -824,25 +783,6 @@ class DocumentsMenu(BaseCopyMenuItem): - self._post_operate_per_metadata_per_action(metadata) - - --class SharesMenu(BaseCopyMenuItem): -- def __init__(self, metadata_list, show_editing_alert, -- show_progress_info_alert, batch_mode): -- BaseCopyMenuItem.__init__(self, metadata_list, _('Shares'), -- show_editing_alert, -- show_progress_info_alert, -- batch_mode) -- -- def _operate(self, metadata): -- if not self._file_path_valid(metadata): -- return False -- if not self._metadata_copy_valid(metadata, -- '/var/www/web1/web'): -- return False -- -- # This is sync-operation. Call the post-operation now. -- self._post_operate_per_metadata_per_action(metadata) -- -- - class FriendsMenu(gtk.Menu): - __gtype_name__ = 'JournalFriendsMenu' - -@@ -970,17 +910,6 @@ class CopyMenuHelper(gtk.Menu): - menu.append(documents_menu) - documents_menu.show() - -- if get_mount_point() != '/var/www/web1/web': -- documents_menu = SharesMenu(metadata_list, -- show_editing_alert, -- show_progress_info_alert, -- batch_mode) -- documents_menu.set_image(Icon(icon_name='emblem-neighborhood-shared', -- icon_size=gtk.ICON_SIZE_MENU)) -- documents_menu.connect('volume-error', self.__volume_error_cb) -- menu.append(documents_menu) -- documents_menu.show() -- - if get_mount_point() != '/': - client = gconf.client_get_default() - color = XoColor(client.get_string('/desktop/sugar/user/color')) -diff --git a/src/jarabe/journal/volumestoolbar.py b/src/jarabe/journal/volumestoolbar.py -index c24475d..209fbfd 100644 ---- a/src/jarabe/journal/volumestoolbar.py -+++ b/src/jarabe/journal/volumestoolbar.py -@@ -39,7 +39,7 @@ from sugar import env - - from jarabe.frame.notification import NotificationIcon - from jarabe.journal import model --from jarabe.view.palettes import JournalVolumePalette, JournalXSPalette, RemoteSharePalette -+from jarabe.view.palettes import JournalVolumePalette, JournalXSPalette - import jarabe.frame - - -@@ -211,7 +211,6 @@ class VolumesToolbar(gtk.Toolbar): - - def _set_up_volumes(self): - self._set_up_documents_button() -- self._set_up_shares_button() - - volume_monitor = gio.volume_monitor_get() - self._mount_added_hid = volume_monitor.connect('mount-added', -@@ -242,33 +241,6 @@ class VolumesToolbar(gtk.Toolbar): - 'user-documents', - _('Documents')) - -- def _set_up_shares_button(self): -- shares_dir_path = '/var/www/web1/web' -- self._set_up_directory_button(shares_dir_path, -- 'emblem-neighborhood-shared', -- _('Shares')) -- -- def _add_remote_share_button(self, buddy): -- button = RemoteSharesButton(buddy) -- button.props.group = self._volume_buttons[0] -- label = glib.markup_escape_text(_('%s\'s share') % \ -- buddy.props.nick) -- button.set_palette(RemoteSharePalette(buddy, button)) -- button.connect('toggled', self._button_toggled_cb) -- button.show() -- -- position = self.get_item_index(self._volume_buttons[-1]) + 1 -- self.insert(button, position) -- self._volume_buttons.append(button) -- self.show() -- -- frame = jarabe.frame.get_view() -- notif_icon = NotificationIcon() -- notif_icon.props.icon_name = 'emblem-neighborhood-shared' -- notif_icon.props.xo_color = buddy.props.color -- frame.add_notification(notif_icon, -- gtk.CORNER_BOTTOM_RIGHT) -- - def __mount_added_cb(self, volume_monitor, mount): - self._add_button(mount) - -@@ -337,19 +309,6 @@ class VolumesToolbar(gtk.Toolbar): - if len(self.get_children()) < 2: - self.hide() - -- def _remove_remote_share_button(self, mount_point): -- # Here, IP_Address is the mount_point. -- for button in self.get_children(): -- if type(button) == RemoteSharesButton and \ -- button.mount_point == mount_point: -- self._volume_buttons.remove(button) -- self.remove(button) -- self.get_children()[0].props.active = True -- -- if len(sel.get_children()) < 2: -- self.hide() -- break; -- - def set_active_volume(self, mount): - button = self._get_button_for_mount(mount) - button.props.active = True -@@ -486,22 +445,6 @@ class DirectoryButton(BaseButton): - self.props.xo_color = color - - --class RemoteSharesButton(BaseButton): -- -- def __init__(self, buddy): -- BaseButton.__init__(self, mount_point=buddy.props.ip_address) -- -- self._buddy = buddy -- self.props.named_icon = 'emblem-neighborhood-shared' -- self.props.xo_color = buddy.props.color -- self._buddy_ip_address = buddy.props.ip_address -- -- def create_palette(self): -- palette = RemoteSharePalette(self._buddy) -- return palette -- -- -- - class XSButton(ToolButton): - def __init__(self): - ToolButton.__init__(self) -diff --git a/src/jarabe/journal/webdavmanager.py b/src/jarabe/journal/webdavmanager.py -deleted file mode 100644 -index 6cd0713..0000000 ---- a/src/jarabe/journal/webdavmanager.py -+++ /dev/null -@@ -1,256 +0,0 @@ --from gettext import gettext as _ -- --import os --import sys -- --import gobject --import simplejson --import shutil -- --from webdav.Connection import AuthorizationError, WebdavError --from webdav.WebdavClient import CollectionStorer -- --def get_key_from_resource(resource): -- return resource.path -- --def ensure_correct_remote_webdav_hierarchy(remote_webdav_share_resources, -- remote_webdav_share_collections): -- pass -- #assert len(remote_webdav_share_collections.keys()) == 1 -- --class WebDavUrlManager(gobject.GObject): -- """ -- This class holds all data, relevant to a WebDavUrl. -- -- One thing must be noted, that a valid WebDavUrl is the one which -- may contain zero or more resources (files), or zero or more -- collections (directories). -- -- Thus, following are valid WebDavUrls :: -- -- dav://1.2.3.4/webdav -- dav://1.2.3.4/webdav/dir_1 -- dav://1.2.3.4/webdav/dir_1/dir_2 -- -- but following are not :: -- -- dav://1.2.3.4/webdav/a.txt -- dav://1.2.3.4/webdav/dir_1/b.jpg -- dav://1.2.3.4/webdab/dir_1/dir_2/c.avi -- """ -- -- def __init__(self, WebDavUrl, username, password): -- self._WebDavUrl = WebDavUrl -- self._username = username -- self._password = password -- -- def _get_key_from_resource(self, resource): -- return resource.path.encode(sys.getfilesystemencoding()) -- -- def _get_number_of_collections(self): -- return len(self._remote_webdav_share_collections) -- -- def _get_resources_dict(self): -- return self._remote_webdav_share_resources -- -- def _get_collections_dict(self): -- return self._remote_webdav_share_collections -- -- def _get_resource_by_key(self, key): -- return self._remote_webdav_share_resources[key]['resource'] -- -- def _get_metadata_list(self): -- metadata_list = [] -- for key in self._remote_webdav_share_resources.keys(): -- metadata_list.append(self._remote_webdav_share_resources[key]['metadata']) -- return metadata_list -- -- def _get_live_properties(self, resource_key): -- resource_container = self._remote_webdav_share_resources[resource_key] -- return resource_container['webdav-properties'] -- -- def _set_metadata_for_resource(self, key, metadata): -- self._remote_webdav_share_resources[key]['metadata'] = metadata -- -- def _fetch_resources_and_collections(self): -- webdavConnection = CollectionStorer(self._WebDavUrl, validateResourceNames=False) -- -- authFailures = 0 -- while authFailures < 2: -- try: -- self._remote_webdav_share_resources = {} -- self._remote_webdav_share_collections = {} -- -- try: -- self._collection_contents = webdavConnection.getCollectionContents() -- for resource, properties in self._collection_contents: -- try: -- key = self._get_key_from_resource(resource) -- selected_dict = None -- -- if properties.getResourceType() == 'resource': -- selected_dict = self._remote_webdav_share_resources -- else: -- selected_dict = self._remote_webdav_share_collections -- -- selected_dict[key] = {} -- selected_dict[key]['resource'] = resource -- selected_dict[key]['webdav-properties'] = properties -- except UnicodeEncodeError: -- print("Cannot encode resource path or properties.") -- -- return True -- -- except WebdavError, e: -- # Note that, we need to deal with all errors, -- # except "AuthorizationError", as that is not -- # really an error from our perspective. -- if not type(e) == AuthorizationError: -- from jarabe.journal.journalactivity import get_journal -- -- error_message = e -- get_journal()._volume_error_cb(None, error_message,_('Error')) -- -- # Simply return, in case of connection-not-available. -- return False -- -- else: -- # If this indeed is an Authorization Error, -- # re-raise it, so that it is caught by the outer -- # "except" block. -- raise e -- -- -- except AuthorizationError, e: -- if self._username is None or self._password is None: -- raise Exception("WebDav username or password is None. Please specify appropriate values.") -- -- if e.authType == "Basic": -- webdavConnection.connection.addBasicAuthorization(self._username, self._password) -- elif e.authType == "Digest": -- info = parseDigestAuthInfo(e.authInfo) -- webdavConnection.connection.addDigestAuthorization(self._username, self._password, realm=info["realm"], qop=info["qop"], nonce=info["nonce"]) -- else: -- raise -- authFailures += 1 -- -- return False -- --webdav_manager = {} -- -- --def get_resource_by_ip_address_and_resource_key(ip_address, key): -- global webdav_manager -- -- if ip_address in webdav_manager.keys(): -- root_webdav = webdav_manager[ip_address] -- resources_dict = root_webdav._get_resources_dict() -- resource_dict = resources_dict[key] -- resource = resource_dict['resource'] -- -- return resource -- -- --def get_remote_webdav_share_metadata(ip_address): -- protocol = 'dav://' -- -- root_webdav_url = '/webdav' -- -- complete_root_url = protocol + ip_address + root_webdav_url -- -- root_webdav = WebDavUrlManager(complete_root_url, 'test', 'olpc') -- if root_webdav._fetch_resources_and_collections() is False: -- # Return empty metadata list. -- return [] -- -- # Keep reference to the "WebDavUrlManager", keyed by IP-Address. -- global webdav_manager -- webdav_manager[ip_address] = root_webdav -- -- -- # Assert that the number of collections is only one at this url -- # (i.e. only ".Sugar-Metadata" is present). -- assert root_webdav._get_number_of_collections() == 1 -- -- root_sugar_metadata_url = root_webdav_url + '/.Sugar-Metadata' -- -- complete_root_sugar_metadata_url = protocol + ip_address + root_sugar_metadata_url -- root_webdav_sugar_metadata = WebDavUrlManager(complete_root_sugar_metadata_url, 'test', 'olpc') -- if root_webdav_sugar_metadata._fetch_resources_and_collections() is False: -- # Return empty metadata list. -- return [] -- -- # assert that the number of collections is zero at this url. -- assert root_webdav_sugar_metadata._get_number_of_collections() == 0 -- -- # Now. associate sugar-metadata with each of the "root-webdav" -- # resource. -- root_webdav_resources = root_webdav._get_resources_dict() -- root_webdav_sugar_metadata_resources = root_webdav_sugar_metadata._get_resources_dict() -- -- # Prepare the metadata-download folder. -- downloaded_data_root_dir = '/tmp/' + ip_address -- downloaded_metadata_file_dir = downloaded_data_root_dir + '/.Sugar-Metadata' -- if os.path.isdir(downloaded_data_root_dir): -- shutil.rmtree(downloaded_data_root_dir) -- os.makedirs(downloaded_metadata_file_dir) -- -- for root_webdav_resource_name in root_webdav_resources.keys(): -- """ -- root_webdav_resource_name is of the type :: -- -- /webdav/a.txt -- """ -- split_tokens_array = root_webdav_resource_name.split('/') -- -- # This will provide us with "a.txt" -- basename = split_tokens_array[len(split_tokens_array) - 1] -- -- # This will provide us with "a.txt.metadata" -- sugar_metadata_basename = basename + '.metadata' -- -- # Thus will provide us with "/webdav/.Sugar-Metadata/a.txt.metadata" -- sugar_metadata_url = root_sugar_metadata_url + '/' + sugar_metadata_basename -- -- # Ensure that "sugar_metadata_url" is present as one of the -- # keys in "root_webdav_sugar_metadata_resources" -- assert sugar_metadata_url in root_webdav_sugar_metadata_resources.keys() -- -- # Now download the metadata file, read its contents, and store -- # the metadata in memory. -- # It is assumed that the metadata-file is small enough to be -- # read in one call to "read". -- -- downloaded_metadata_file_path = downloaded_metadata_file_dir + '/' + sugar_metadata_basename -- metadata_resource = root_webdav_sugar_metadata._get_resource_by_key(sugar_metadata_url) -- metadata_resource.downloadFile(downloaded_metadata_file_path) -- -- file_pointer = open(downloaded_metadata_file_path) -- metadata = eval(file_pointer.read()) -- file_pointer.close() -- -- # Very critical. -- # 1. CRITICAL ONE: -- # Fill in the uid. -- # Note that the file is not physically present. -- metadata['uid'] = downloaded_data_root_dir + '/' + basename -- -- # 2. CRITICAL TWO: -- # Fill in the properties, that can only be done by reading -- # in the webdav-properties. -- live_properties = root_webdav._get_live_properties(root_webdav_resource_name) -- metadata['filesize'] = live_properties.getContentLength() -- metadata['timestamp'] = live_properties.getLastModified() -- metadata['creation_time'] = live_properties.getCreationDate() -- -- # Now, write this to the metadata-file, so that -- # webdav-properties get gelled into sugar-metadata. -- -- file_pointer = open(downloaded_metadata_file_path, 'w') -- file_pointer.write(simplejson.dumps(metadata)) -- file_pointer.close() -- -- root_webdav._set_metadata_for_resource(root_webdav_resource_name, -- metadata) -- -- return root_webdav._get_metadata_list() -diff --git a/src/jarabe/view/buddymenu.py b/src/jarabe/view/buddymenu.py -index dfbcfa3..de5a772 100644 ---- a/src/jarabe/view/buddymenu.py -+++ b/src/jarabe/view/buddymenu.py -@@ -73,12 +73,6 @@ class BuddyMenu(Palette): - self.menu.append(menu_item) - menu_item.show() - -- access_buddy_remote_share_menu_item = MenuItem(_('Access Share'), 'list-add') -- access_buddy_remote_share_menu_item.connect('activate', -- self._access_share_cb) -- self.menu.append(access_buddy_remote_share_menu_item) -- access_buddy_remote_share_menu_item.show() -- - self._invite_menu = MenuItem('') - self._invite_menu.connect('activate', self._invite_friend_cb) - self.menu.append(self._invite_menu) -@@ -89,11 +83,6 @@ class BuddyMenu(Palette): - activity = home_model.get_active_activity() - self._update_invite_menu(activity) - -- def _access_share_cb(self, menuitem): -- from jarabe.journal.journalactivity import get_journal -- volumes_toolbar = get_journal().get_volumes_toolbar() -- volumes_toolbar._add_remote_share_button(self._buddy) -- - def _add_my_items(self): - item = MenuItem(_('Shutdown'), 'system-shutdown') - item.connect('activate', self.__shutdown_activate_cb) -diff --git a/src/jarabe/view/palettes.py b/src/jarabe/view/palettes.py -index 3b26faf..7a17f32 100644 ---- a/src/jarabe/view/palettes.py -+++ b/src/jarabe/view/palettes.py -@@ -336,36 +336,3 @@ class JournalXSPalette(Palette): - def __journal_restore_activate_cb(self, menu_item, xs_hostname): - dialog = XSRestoreDialog(xs_hostname) - dialog.show() -- -- --class RemoteSharePalette(Palette): -- def __init__(self, buddy, button): -- Palette.__init__(self, label=('%s\'s share' % buddy.props.nick)) -- self._buddy = buddy -- self._button = button -- -- self.props.secondary_text = glib.markup_escape_text(buddy.props.ip_address) -- -- vbox = gtk.VBox() -- self.set_content(vbox) -- vbox.show() -- -- self.connect('popup', self.__popup_cb) -- -- menu_item = MenuItem(pgettext('Share', 'Unmount')) -- -- icon = Icon(icon_name='media-eject', icon_size=gtk.ICON_SIZE_MENU) -- menu_item.set_image(icon) -- icon.show() -- -- menu_item.connect('activate', self.__unmount_activate_cb) -- self.menu.append(menu_item) -- menu_item.show() -- -- def __unmount_activate_cb(self, menu_item): -- from jarabe.journal.journalactivity import get_journal -- singleton_volumes_toolbar = get_journal().get_volumes_toolbar() -- singleton_volumes_toolbar._remove_remote_share_button(self._buddy.props.ip_address) -- -- def __popup_cb(self, palette): -- pass -diff --git a/src/webdav/Condition.py b/src/webdav/Condition.py -deleted file mode 100644 -index 76acf94..0000000 ---- a/src/webdav/Condition.py -+++ /dev/null -@@ -1,475 +0,0 @@ --# pylint: disable-msg=R0921,W0704,R0901,W0511,R0201 --# Copyright 2008 German Aerospace Center (DLR) --# --# Licensed under the Apache License, Version 2.0 (the "License"); --# you may not use this file except in compliance with the License. --# You may obtain a copy of the License at --# --# http://www.apache.org/licenses/LICENSE-2.0 --# --# Unless required by applicable law or agreed to in writing, software --# distributed under the License is distributed on an "AS IS" BASIS, --# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. --# See the License for the specific language governing permissions and --# limitations under the License. -- -- --""" --This module contains classes for creating a search condition according to the DASL draft. --The classes will output the WHERE part of a search request to a WebDAV server. -- --Instances of the classes defined in this module form a tree data structure which represents --a search condition. This tree is made up of AND-nodes, OR-nodes, Operator- and comparison- --nodes and from property (i.e. variable) and constant leaf nodes. --""" -- -- --import types --from time import strftime --from calendar import timegm --from rfc822 import formatdate -- --from webdav.Constants import NS_DAV, PROP_LAST_MODIFIED, DATE_FORMAT_ISO8601 -- -- --__version__ = "$Revision$"[11:-2] -- -- --class ConditionTerm(object): -- """ -- This is the abstact base class for all condition terms. -- """ -- def __init__(self): -- pass -- -- def toXML(self): -- """ -- Abstact method which return a XML string which can be passed to a WebDAV server -- for a search condition. -- """ -- raise NotImplementedError -- -- # start Tamino workaround for missing like-op: -- def postFilter(self, resultSet): -- """ -- Abstact method for temporary workaround for Tamino's absense of the like-operator. -- This method shall filter the given result set for those resources which match -- all Contains-trems. -- """ -- return resultSet -- # end of workaround -- -- --class IsCollectionTerm(ConditionTerm): -- """ Leaf condition. Checks if the matching resources are collections. """ -- -- def __init__(self): -- """ Constructor. """ -- -- ConditionTerm.__init__(self) -- -- def toXML(self): -- """ -- Returns XML encoding. -- """ -- -- return "<D:is-collection/>" -- -- --class Literal(ConditionTerm): -- """ -- A leaf class for condition expressions representing a constant value. -- """ -- def __init__(self, literal): -- ConditionTerm.__init__(self) -- self.literal = literal -- -- def toXML(self): -- ''' -- Returns XML encoding. -- ''' -- return "<D:literal>" + self.literal + "</D:literal>" -- -- --class UnaryTerm(ConditionTerm): -- """ -- Base class of all nodes with a single child node. -- """ -- def __init__(self, child): -- ConditionTerm.__init__(self) -- self.child = child -- -- def toXML(self): -- ''' -- Returns XML encoding. -- ''' -- return self.child.toXML() -- -- --class BinaryTerm(ConditionTerm): -- """ -- Base class of all nodes with two child nodes -- """ -- def __init__(self, left, right): -- ConditionTerm.__init__(self) -- self.left = left -- self.right = right -- -- def toXML(self): -- ''' -- Returns XML encoding. -- ''' -- return self.left.toXML() + self.right.toXML() -- --class TupleTerm(ConditionTerm): -- """ -- Base class of all nodes with multiple single child nodes. -- """ -- def __init__(self, terms): -- ConditionTerm.__init__(self) -- self.terms = terms -- -- def addTerm(self, term): -- ''' -- Removes a term. -- -- @param term: term to add -- ''' -- self.terms.append(term) -- -- def removeTerm(self, term): -- ''' -- Adds a term. -- -- @param term: term to remove -- ''' -- try: -- self.terms.remove(term) -- except ValueError: -- pass -- -- def toXML(self): -- ''' -- Returns XML encoding. -- ''' -- result = "" -- for term in self.terms: -- result += term.toXML() -- return result -- -- --class AndTerm(TupleTerm): -- """ -- This class represents and logical AND-condition with multiple sub terms. -- """ -- def toXML(self): -- ''' -- Returns XML encoding. -- ''' -- return "<D:and>" + TupleTerm.toXML(self) + "</D:and>" -- -- # start Tamino workaround for missing like-op: -- def postFilter(self, resultSet): -- ''' -- Filters the given result set. This is a TAMINO WebDav server workaround -- for the missing 'like' tag. -- -- @param resultSet: the result set that needs to be filtered. -- ''' -- for term in self.terms: -- filtered = term.postFilter(resultSet) -- resultSet = filtered -- return resultSet -- # end of workaround -- --class OrTerm(TupleTerm): -- """ -- This class represents and logical OR-condition with multiple sub terms. -- """ -- def toXML(self): -- ''' -- Returns XML encoding. -- ''' -- return "<D:or>" + TupleTerm.toXML(self) + "</D:or>" -- -- # start Tamino workaround for missing like-op: -- def postFilter(self, resultSet): -- ''' -- Filters the given result set. This is a TAMINO WebDav server workaround -- for the missing 'like' tag. -- -- @param resultSet: the result set that needs to be filtered. -- ''' -- raise NotImplementedError -- -- --class NotTerm(UnaryTerm): -- """ -- This class represents a negation term for the contained sub term. -- """ -- def toXML(self): -- ''' -- Returns XML encoding. -- ''' -- # start Tamino workaround for missing like-op: -- if isinstance(self.child, ContainsTerm): -- return "" -- # end of workaround -- return "<D:not>" + UnaryTerm.toXML(self) + "</D:not>" -- -- # start Tamino workaround for missing like-op: -- def postFilter(self, resultSet): -- ''' -- Filters the given result set. This is a TAMINO WebDav server workaround -- for the missing 'like' tag. -- -- @param resultSet: the result set that needs to be filtered. -- ''' -- if isinstance(self.child, ContainsTerm): -- self.child.negate = 1 -- # TODO: pass on filter -- return self.child.postFilter(resultSet) -- -- --class ExistsTerm(UnaryTerm): -- """ -- Nodes of this class must have a single child with tuple type (of len 2) representing a -- WebDAV property. -- This leaf term evaluates to true if the (child) property exists. -- """ -- def toXML(self): -- ''' -- Returns XML encoding. -- ''' -- return '<D:is-defined><D:prop xmlns="%s"><%s' % self.child + ' /></D:prop></D:is-defined>' -- --class ContentContainsTerm(UnaryTerm): -- """ -- This class can be used to search for a given phrase in resources' contents. -- """ -- def toXML(self): -- ''' -- Returns XML encoding. -- ''' -- return "<D:contains>" + self.child + "</D:contains>" -- -- -- --class BinaryRelationTerm(BinaryTerm): -- """ -- This is the abstact base class for the following relation operands. -- """ -- def __init__(self, left, right): -- BinaryTerm.__init__(self, left, right) -- if isinstance(self.left, types.StringType): # Must be namespace + name pair -- self.left = ('DAV:', self.left) -- if not isinstance(self.right, Literal): -- self.right = Literal(self.right) # Must be Literal instance -- -- def toXML(self): -- ''' -- Returns XML encoding. -- ''' -- ## TODO: extract name space and create shortcut for left element -- return '<D:prop xmlns="%s"><%s /></D:prop>' % self.left + self.right.toXML() -- -- --class StringRelationTerm(BinaryRelationTerm): -- """ -- This is the abstact base class for the following string relation classes. -- """ -- def __init__(self, left, right, caseless=None): -- """ -- @param left: webdav property (namespace, name) -- @param right: string/unicode literal -- qparam caseless: 1 for case sensitive comparison -- """ -- BinaryRelationTerm.__init__(self, left, Literal(right)) -- self.caseless = caseless -- if self.caseless: -- self.attrCaseless = "yes" -- else: -- self.attrCaseless = "no" -- --class NumberRelationTerm(BinaryRelationTerm): -- """ -- This is the abstact base class for the following number comparison classes. -- """ -- def __init__(self, left, right): -- """ -- @param left: webdav property (namespace, name) -- @param right: constant number -- """ -- ## TODO: implemet typed literal -- BinaryRelationTerm.__init__(self, left, Literal(str(right))) -- --class DateRelationTerm(BinaryRelationTerm): -- """ -- This is the abstact base class for the following date comparison classes. -- """ -- def __init__(self, left, right): -- """ -- @param left: webdav property (namespace, name) -- @param right: string literal containing a date in ISO8601 format -- """ -- ## TODO: implemet typed literal -- assert len(right) == 9, "No time is specified for literal: " + str(right) -- BinaryRelationTerm.__init__(self, left, right) -- if self.left == (NS_DAV, PROP_LAST_MODIFIED): -- rfc822Time = formatdate(timegm(right)) # must not use locale setting -- self.right = Literal(rfc822Time) -- else: -- self.right = Literal(strftime(DATE_FORMAT_ISO8601, right)) -- -- --class MatchesTerm(StringRelationTerm): -- """ -- Nodes of this class evaluate to true if the (child) property's value matches the (child) string. -- """ -- def toXML(self): -- ''' -- Returns XML encoding. -- ''' -- return '<D:eq caseless="%s">' % self.attrCaseless + StringRelationTerm.toXML(self) + "</D:eq>" -- --class ContainsTerm(StringRelationTerm): -- """ -- Nodes of this class evaluate to true if the (left child) property's value contains the -- (right child) string. -- """ -- def __init__(self, left, right, isTaminoWorkaround=False): -- right = unicode(right) -- StringRelationTerm.__init__(self, left, "%" + right + "%") -- # Tamino workaround: operator like is not yet implemented: -- self.negate = 0 -- self.isTaminoWorkaround = isTaminoWorkaround -- -- def toXML(self): -- ''' -- Returns XML encoding. -- ''' -- # Tamino workaround: operator like is not yet implemented: -- # Produce a is-defined-condition instead -- if self.isTaminoWorkaround: -- return "<D:isdefined><D:prop xmlns='%s'><%s" % self.left + " /></D:prop></D:isdefined>" -- else: -- return '<D:like caseless="%s">' % self.attrCaseless + StringRelationTerm.toXML(self) + "</D:like>" -- -- # start Tamino workaround for missing like-op: -- def postFilter(self, resultSet): -- ''' -- Filters the given result set. This is a TAMINO WebDav server workaround -- for the missing 'like' tag. -- -- @param resultSet: the result set that needs to be filtered. -- ''' -- newResult = {} -- word = self.right.literal[1:-1] # remove leading and trailing '%' characters (see __init__()) -- for url, properties in resultSet.items(): -- value = properties.get(self.left) -- if self.negate: -- if not value or value.textof().find(word) < 0: -- newResult[url] = properties -- else: -- if value and value.textof().find(word) >= 0: -- newResult[url] = properties -- return newResult -- # end of workaround -- --class IsEqualTerm(NumberRelationTerm): -- """ -- Nodes of this class evaluate to true if the (left child) numerical property's value is equal -- to the (right child) number. -- """ -- def toXML(self): -- ''' -- Returns XML encoding. -- ''' -- return "<D:eq>" + NumberRelationTerm.toXML(self) + "</D:eq>" -- --class IsGreaterTerm(NumberRelationTerm): -- """ -- Nodes of this class evaluate to true if the (left child) numerical property's value is greater -- than the (right child) number. -- """ -- def toXML(self): -- ''' -- Returns XML encoding. -- ''' -- return "<D:gt>" + NumberRelationTerm.toXML(self) + "</D:gt>" -- --class IsGreaterOrEqualTerm(NumberRelationTerm): -- """ -- Nodes of this class evaluate to true if the (left child) numerical property's value is greater -- than or equal to the (right child) number. -- """ -- def toXML(self): -- ''' -- Returns XML encoding. -- ''' -- return "<D:gte>" + NumberRelationTerm.toXML(self) + "</D:gte>" -- --class IsSmallerTerm(NumberRelationTerm): -- """ -- Nodes of this class evaluate to true if the (left child) numerical property's value is less -- than the (right child) number. -- """ -- def toXML(self): -- ''' -- Returns XML encoding. -- ''' -- return "<D:lt>" + NumberRelationTerm.toXML(self) + "</D:lt>" -- --class IsSmallerOrEqualTerm(NumberRelationTerm): -- """ -- Nodes of this class evaluate to true if the (left child) numerical property's value is less -- than or equal to the (right child) number. -- """ -- def toXML(self): -- ''' -- Returns XML encoding. -- ''' -- return "<D:lte>" + NumberRelationTerm.toXML(self) + "</D:lte>" -- -- --class OnTerm(DateRelationTerm): -- """ -- Nodes of this class evaluate to true if the (left child) property's value is a date -- equal to the (right child) date. -- """ -- def toXML(self): -- ''' -- Returns XML encoding. -- ''' -- return "<D:eq>" + DateRelationTerm.toXML(self) + "</D:eq>" -- --class AfterTerm(DateRelationTerm): -- """ -- Nodes of this class evaluate to true if the (left child) property's value is a date -- succeeding the (right child) date. -- """ -- def toXML(self): -- ''' -- Returns XML encoding. -- ''' -- return "<D:gt>" + DateRelationTerm.toXML(self) + "</D:gt>" -- --class BeforeTerm(DateRelationTerm): -- """ -- Nodes of this class evaluate to true if the (left child) property's value is a date -- preceeding the (right child) date. -- """ -- def toXML(self): -- ''' -- Returns XML encoding. -- ''' -- return "<D:lt>" + DateRelationTerm.toXML(self) + "</D:lt>" -- -- -- --# Simple module test --if __name__ == '__main__': -- # use the example from the webdav specification -- condition = AndTerm( (MatchesTerm('getcontenttype', 'image/gif'), \ -- IsGreaterTerm('getcontentlength', 4096)) ) -- print "Where: " + condition.toXML() -diff --git a/src/webdav/Connection.py b/src/webdav/Connection.py -deleted file mode 100644 -index 66f7833..0000000 ---- a/src/webdav/Connection.py -+++ /dev/null -@@ -1,321 +0,0 @@ --# pylint: disable-msg=W0142,W0102,R0901,R0904,E0203,E1101,C0103 --# --# Copyright 2008 German Aerospace Center (DLR) --# --# Licensed under the Apache License, Version 2.0 (the "License"); --# you may not use this file except in compliance with the License. --# You may obtain a copy of the License at --# --# http://www.apache.org/licenses/LICENSE-2.0 --# --# Unless required by applicable law or agreed to in writing, software --# distributed under the License is distributed on an "AS IS" BASIS, --# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. --# See the License for the specific language governing permissions and --# limitations under the License. -- -- --""" --The contained class extends the HTTPConnection class for WebDAV support. --""" -- -- --from httplib import HTTPConnection, CannotSendRequest, BadStatusLine, ResponseNotReady --from copy import copy --import base64 # for basic authentication --try: -- import hashlib --except ImportError: # for Python 2.4 compatibility -- import md5 -- hashlib = md5 --import mimetypes --import os # file handling --import urllib --import types --import socket # to "catch" socket.error --from threading import RLock --try: -- from uuid import uuid4 --except ImportError: # for Python 2.4 compatibility -- from uuid_ import uuid4 --from xml.parsers.expat import ExpatError -- --from davlib import DAV --from qp_xml import Parser -- --from webdav.WebdavResponse import MultiStatusResponse, ResponseFormatError --from webdav import Constants --from webdav.logger import getDefaultLogger -- -- --__version__ = "$LastChangedRevision$" -- -- --class Connection(DAV): -- """ -- This class handles a connection to a WebDAV server. -- This class is used internally. Client code should prefer classes -- L{WebdavClient.ResourceStorer} and L{WebdavClient.CollectionStorer}. -- -- @author: Roland Betz -- """ -- -- # Constants -- # The following switch activates a workaround for the Tamino webdav server: -- # Tamino expects URLs which are passed in a HTTP header to be Latin-1 encoded -- # instead of Utf-8 encoded. -- # Set this switch to zero in order to communicate with conformant servers. -- blockSize = 30000 -- MaxRetries = 10 -- -- def __init__(self, *args, **kwArgs): -- DAV.__init__(self, *args, **kwArgs) -- self.__authorizationInfo = None -- self.logger = getDefaultLogger() -- self.isConnectedToCatacomb = True -- self.serverTypeChecked = False -- self._lock = RLock() -- -- def _request(self, method, url, body=None, extra_hdrs={}): -- -- self._lock.acquire() -- try: -- # add the authorization header -- extraHeaders = copy(extra_hdrs) -- if self.__authorizationInfo: -- -- # update (digest) authorization data -- if hasattr(self.__authorizationInfo, "update"): -- self.__authorizationInfo.update(method=method, uri=url) -- -- extraHeaders["AUTHORIZATION"] = self.__authorizationInfo.authorization -- -- # encode message parts -- body = _toUtf8(body) -- url = _urlEncode(url) -- for key, value in extraHeaders.items(): -- extraHeaders[key] = _toUtf8(value) -- if key == "Destination": # copy/move header -- if self.isConnectedToCatacomb: -- extraHeaders[key] = _toUtf8(value.replace(Constants.SHARP, Constants.QUOTED_SHARP)) -- -- else: # in case of TAMINO 4.4 -- extraHeaders[key] = _urlEncode(value) -- # pass message to httplib class -- for retry in range(0, Connection.MaxRetries): # retry loop -- try: -- self.logger.debug("REQUEST Send %s for %s" % (method, url)) -- self.logger.debug("REQUEST Body: " + repr(body)) -- for hdr in extraHeaders.items(): -- self.logger.debug("REQUEST Header: " + repr(hdr)) -- self.request(method, url, body, extraHeaders) -- response = self.getresponse() -- break # no retry needed -- except (CannotSendRequest, socket.error, BadStatusLine, ResponseNotReady), exc: -- # Workaround, start: reconnect and retry... -- self.logger.debug("Exception: " + str(exc) + " Retry ... ") -- self.close() -- try: -- self.connect() -- except (CannotSendRequest, socket.error, BadStatusLine, ResponseNotReady), exc: -- raise WebdavError("Cannot perform request. Connection failed.") -- if retry == Connection.MaxRetries - 1: -- raise WebdavError("Cannot perform request.") -- return self.__evaluateResponse(method, response) -- finally: -- self._lock.release() -- -- def __evaluateResponse(self, method, response): -- """ Evaluates the response of the WebDAV server. """ -- -- status, reason = response.status, response.reason -- self.logger.debug("Method: " + method + " Status %d: " % status + reason) -- -- if status >= Constants.CODE_LOWEST_ERROR: # error has occured ? -- self.logger.debug("ERROR Response: " + response.read()) -- -- # identify authentication CODE_UNAUTHORIZED, throw appropriate exception -- if status == Constants.CODE_UNAUTHORIZED: -- raise AuthorizationError(reason, status, response.msg["www-authenticate"]) -- -- response.close() -- raise WebdavError(reason, status) -- -- if status == Constants.CODE_MULTISTATUS: -- content = response.read() -- ## check for UTF-8 encoding -- try: -- response.root = Parser().parse(content) -- except ExpatError, error: -- errorMessage = "Invalid XML document has been returned.\nReason: '%s'" % str(error.args) -- raise WebdavError(errorMessage) -- try: -- response.msr = MultiStatusResponse(response.root) -- except ResponseFormatError: -- raise WebdavError("Invalid WebDAV response.") -- response.close() -- self.logger.debug("RESPONSE (Multi-Status): " + unicode(response.msr)) -- elif method == 'LOCK' and status == Constants.CODE_SUCCEEDED: -- response.parse_lock_response() -- response.close() -- elif method != 'GET' and method != 'PUT': -- self.logger.debug("RESPONSE Body: " + response.read()) -- response.close() -- return response -- -- def addBasicAuthorization(self, user, password, realm=None): -- if user and len(user) > 0: -- self.__authorizationInfo = _BasicAuthenticationInfo(realm=realm, user=user, password=password) -- -- def addDigestAuthorization(self, user, password, realm, qop, nonce, uri = None, method = None): -- if user and len(user) > 0: -- # username, realm, password, uri, method, qop are required -- self.__authorizationInfo = _DigestAuthenticationInfo(realm=realm, user=user, password=password, uri=uri, method=method, qop=qop, nonce=nonce) -- -- def putFile(self, path, srcfile, header={}): -- self._lock.acquire() -- try: -- # Assemble header -- try: -- size = os.path.getsize(srcfile.name) -- except os.error, error: -- raise WebdavError("Cannot determine file size.\nReason: ''" % str(error.args)) -- header["Content-length"] = str(size) -- -- contentType, contentEnc = mimetypes.guess_type(path) -- if contentType: -- header['Content-Type'] = contentType -- if contentEnc: -- header['Content-Encoding'] = contentEnc -- if self.__authorizationInfo: -- # update (digest) authorization data -- if hasattr(self.__authorizationInfo, "update"): -- self.__authorizationInfo.update(method="PUT", uri=path) -- header["AUTHORIZATION"] = self.__authorizationInfo.authorization -- -- # send first request -- path = _urlEncode(path) -- try: -- HTTPConnection.request(self, 'PUT', path, "", header) -- self._blockCopySocket(srcfile, self, Connection.blockSize) -- srcfile.close() -- response = self.getresponse() -- except (CannotSendRequest, socket.error, BadStatusLine, ResponseNotReady), exc: -- self.logger.debug("Exception: " + str(exc) + " Retry ... ") -- raise WebdavError("Cannot perform request.") -- status, reason = (response.status, response.reason) -- self.logger.debug("Status %d: %s" % (status, reason)) -- try: -- if status >= Constants.CODE_LOWEST_ERROR: # error has occured ? -- raise WebdavError(reason, status) -- finally: -- self.logger.debug("RESPONSE Body: " + response.read()) -- response.close() -- return response -- finally: -- self._lock.release() -- -- def _blockCopySocket(self, source, toSocket, blockSize): -- transferedBytes = 0 -- block = source.read(blockSize) -- #while source.readinto(block, blockSize): -- while len(block): -- toSocket.send(block) -- self.logger.debug("Wrote %d bytes." % len(block)) -- transferedBytes += len(block) -- block = source.read(blockSize) -- self.logger.info("Transfered %d bytes." % transferedBytes) -- -- def __str__(self): -- return self.protocol + "://" + self.host + ':' + str(self.port) -- -- --class _BasicAuthenticationInfo(object): -- def __init__(self, **kwArgs): -- self.__dict__.update(kwArgs) -- self.cookie = base64.encodestring("%s:%s" % (self.user, self.password) ).strip() -- self.authorization = "Basic " + self.cookie -- self.password = None # protect password security -- --class _DigestAuthenticationInfo(object): -- -- __nc = "0000000" # in hexadecimal without leading 0x -- -- def __init__(self, **kwArgs): -- -- self.__dict__.update(kwArgs) -- -- if self.qop is None: -- raise WebdavError("Digest without qop is not implemented.") -- if self.qop == "auth-int": -- raise WebdavError("Digest with qop-int is not implemented.") -- -- def update(self, **kwArgs): -- """ Update input data between requests""" -- -- self.__dict__.update(kwArgs) -- -- def _makeDigest(self): -- """ Creates the digest information. """ -- -- # increment nonce count -- self._incrementNc() -- -- # username, realm, password, uri, method, qop are required -- -- a1 = "%s:%s:%s" % (self.user, self.realm, self.password) -- ha1 = hashlib.md5(a1).hexdigest() -- -- #qop == auth -- a2 = "%s:%s" % (self.method, self.uri) -- ha2 = hashlib.md5(a2).hexdigest() -- -- cnonce = str(uuid4()) -- -- responseData = "%s:%s:%s:%s:%s:%s" % (ha1, self.nonce, _DigestAuthenticationInfo.__nc, cnonce, self.qop, ha2) -- digestResponse = hashlib.md5(responseData).hexdigest() -- -- authorization = "Digest username=\"%s\", realm=\"%s\", nonce=\"%s\", uri=\"%s\", algorithm=MD5, response=\"%s\", qop=auth, nc=%s, cnonce=\"%s\"" \ -- % (self.user, self.realm, self.nonce, self.uri, digestResponse, _DigestAuthenticationInfo.__nc, cnonce) -- return authorization -- -- authorization = property(_makeDigest) -- -- def _incrementNc(self): -- _DigestAuthenticationInfo.__nc = self._dec2nc(self._nc2dec() + 1) -- -- def _nc2dec(self): -- return int(_DigestAuthenticationInfo.__nc, 16) -- -- def _dec2nc(self, decimal): -- return hex(decimal)[2:].zfill(8) -- -- --class WebdavError(IOError): -- def __init__(self, reason, code=0): -- IOError.__init__(self, code) -- self.code = code -- self.reason = reason -- def __str__(self): -- return self.reason -- -- --class AuthorizationError(WebdavError): -- def __init__(self, reason, code, authHeader): -- WebdavError.__init__(self, reason, code) -- -- self.authType = authHeader.split(" ")[0] -- self.authInfo = authHeader -- -- --def _toUtf8(body): -- if not body is None: -- if type(body) == types.UnicodeType: -- body = body.encode('utf-8') -- return body -- -- --def _urlEncode(url): -- if type(url) == types.UnicodeType: -- url = url.encode('utf-8') -- return urllib.quote(url) -diff --git a/src/webdav/Constants.py b/src/webdav/Constants.py -deleted file mode 100644 -index 56dfd77..0000000 ---- a/src/webdav/Constants.py -+++ /dev/null -@@ -1,199 +0,0 @@ --# pylint: disable-msg=C0103 --# --# Copyright 2008 German Aerospace Center (DLR) --# --# Licensed under the Apache License, Version 2.0 (the "License"); --# you may not use this file except in compliance with the License. --# You may obtain a copy of the License at --# --# http://www.apache.org/licenses/LICENSE-2.0 --# --# Unless required by applicable law or agreed to in writing, software --# distributed under the License is distributed on an "AS IS" BASIS, --# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. --# See the License for the specific language governing permissions and --# limitations under the License. -- -- --""" --Contains XML tag names for the WebDAV protocol (RFC 2815) --and further WebDAV related constants. --""" -- -- --__version__ = "$Revision$"[11:-2] -- -- --QUOTED_SHARP = "%23" --SHARP = "#" -- --# Date formats --DATE_FORMAT_ISO8601 = r"%Y-%m-%dT%H:%M:%SZ" --DATE_FORMAT_HTTP = r"%a, %d %b %Y %H:%M:%S GMT" # not used, substituted by rfc822 function -- --NS_DAV = 'DAV:' --NS_TAMINO = 'http://namespaces.softwareag.com/tamino/response2' -- --TAG_PROPERTY_FIND = 'propfind' --TAG_PROPERTY_NAME = 'propname' --TAG_PROPERTY_UPDATE = 'propertyupdate' --TAG_PROPERTY_SET = 'set' --TAG_PROPERTY_REMOVE = 'remove' --TAG_ALL_PROPERTY = 'allprop' --TAG_PROP = 'prop' -- --TAG_MULTISTATUS = 'multistatus' --TAG_RESPONSE = 'response' --TAG_HREF = 'href' --TAG_PROPERTY_STATUS = 'propstat' --TAG_STATUS = 'status' --TAG_RESPONSEDESCRIPTION = 'responsdescription' -- --PROP_CREATION_DATE = 'creationdate' --PROP_DISPLAY_NAME = 'displayname' --PROP_CONTENT_LANGUAGE = 'getcontentlanguage' --PROP_CONTENT_LENGTH = 'getcontentlength' --PROP_CONTENT_TYPE = 'getcontenttype' --PROP_ETAG = 'getetag' --PROP_MODIFICATION_DATE = 'modificationdate' # this property is supported by --# Tamino 4.4 but not by Catacomb; the date format is ISO8601 --PROP_LAST_MODIFIED = 'getlastmodified' --PROP_LOCK_DISCOVERY = 'lockdiscovery' --PROP_RESOURCE_TYPE = 'resourcetype' --PROP_SOURCE = 'source' --PROP_SUPPORTED_LOCK = 'supportedlock' --PROP_OWNER = 'owner' -- --PROP_RESOURCE_TYPE_RESOURCE = 'resource' --PROP_RESOURCE_TYPE_COLLECTION = 'collection' -- --TAG_LINK = 'link' --TAG_LINK_SOURCE = 'src' --TAG_LINK_DESTINATION = 'dst' -- --TAG_LOCK_ENTRY = 'lockentry' --TAG_LOCK_SCOPE = 'lockscope' --TAG_LOCK_TYPE = 'locktype' --TAG_LOCK_INFO = 'lockinfo' --TAG_ACTIVE_LOCK = 'activelock' --TAG_LOCK_DEPTH = 'depth' --TAG_LOCK_TOKEN = 'locktoken' --TAG_LOCK_TIMEOUT = 'timeout' --TAG_LOCK_EXCLUSIVE = 'exclusive' --TAG_LOCK_SHARED = 'shared' --TAG_LOCK_OWNER = 'owner' -- --# HTTP error code constants --CODE_MULTISTATUS = 207 --CODE_SUCCEEDED = 200 --CODE_CREATED = 201 --CODE_NOCONTENT = 204 -- --CODE_LOWEST_ERROR = 300 -- --CODE_UNAUTHORIZED = 401 --CODE_FORBIDDEN = 403 --CODE_NOT_FOUND = 404 --CODE_CONFLICT = 409 --CODE_PRECONDITION_FAILED = 412 --CODE_LOCKED = 423 # no permission --CODE_FAILED_DEPENDENCY = 424 -- --CODE_OUTOFMEM = 507 -- --# ? --CONFIG_UNICODE_URL = 1 -- --# constants for WebDAV DASL according to draft -- --TAG_SEARCH_REQUEST = 'searchrequest' --TAG_SEARCH_BASIC = 'basicsearch' --TAG_SEARCH_SELECT = 'select' --TAG_SEARCH_FROM = 'from' --TAG_SEARCH_SCOPE = 'scope' --TAG_SEARCH_WHERE = 'where' -- --# constants for WebDAV ACP (according to draft-ietf-webdav-acl-09) below ... -- --TAG_ACL = 'acl' --TAG_ACE = 'ace' --TAG_GRANT = 'grant' --TAG_DENY = 'deny' --TAG_PRIVILEGE = 'privilege' --TAG_PRINCIPAL = 'principal' --TAG_ALL = 'all' --TAG_AUTHENTICATED = 'authenticated' --TAG_UNAUTHENTICATED = 'unauthenticated' --TAG_OWNER = 'owner' --TAG_PROPERTY = 'property' --TAG_SELF = 'self' --TAG_INHERITED = 'inherited' --TAG_PROTECTED = 'protected' --TAG_SUPPORTED_PRIVILEGE = 'supported-privilege' --TAG_DESCRIPTION = 'description' -- --# privileges for WebDAV ACP: --TAG_READ = 'read' --TAG_WRITE = 'write' --TAG_WRITE_PROPERTIES = 'write-properties' --TAG_WRITE_CONTENT = 'write-content' --TAG_UNLOCK = 'unlock' --TAG_READ_ACL = 'read-acl' --TAG_READ_CURRENT_USER_PRIVILEGE_SET = 'read-current-user-privilege-set' --TAG_WRITE_ACL = 'write-acl' --TAG_ALL = 'all' --TAG_BIND = 'bind' --TAG_UNBIND = 'unbind' --# Tamino-specific privileges --TAG_TAMINO_SECURITY = 'security' --# Limestone-specific privileges --TAG_BIND_COLLECTION = 'bind-collection' --TAG_UNBIND_COLLECTION = 'unbind-collection' --TAG_READ_PRIVATE_PROPERTIES = 'read-private-properties' --TAG_WRITE_PRIVATE_PROPERTIES = 'write-private-properties' -- --# properties for WebDAV ACP: --PROP_CURRENT_USER_PRIVILEGE_SET = 'current-user-privilege-set' --PROP_SUPPORTED_PRIVILEGE_SET = 'supported-privilege-set' --PROP_PRINCIPAL_COLLECTION_SET = 'principal-collection-set' -- --# reports for WebDAV ACP --REPORT_ACL_PRINCIPAL_PROP_SET = 'acl-principal-prop-set' -- -- -- --# constants for WebDAV Delta-V -- --# WebDAV Delta-V method names --METHOD_REPORT = 'REPORT' --METHOD_VERSION_CONTROL = 'VERSION-CONTROL' --METHOD_UNCHECKOUT = 'UNCHECKOUT' --METHOD_CHECKOUT = 'CHECKOUT' --METHOD_CHECKIN = 'CHECKIN' --METHOD_UPDATE = 'UPDATE' -- --# Special properties --PROP_SUCCESSOR_SET = (NS_DAV, 'successor-set') --PROP_PREDECESSOR_SET = (NS_DAV, 'predecessor-set') --PROP_VERSION_HISTORY = (NS_DAV, 'version-history') --PROP_CREATOR = (NS_DAV, 'creator-displayname') --PROP_VERSION_NAME = (NS_DAV, 'version-name') --PROP_CHECKEDIN = (NS_DAV, 'checked-in') --PROP_CHECKEDOUT = (NS_DAV, 'checked-out') --PROP_COMMENT = (NS_DAV, 'comment') -- --# XML tags for request body --TAG_VERSION_TREE = 'version-tree' --TAG_LOCATE_BY_HISTORY = 'locate-by-history' --TAG_UPDATE = 'update' --TAG_VERSION = 'version' -- --# HTTP header constants --HTTP_HEADER_DEPTH_INFINITY = 'infinity' --HTTP_HEADER_IF = 'if' --HTTP_HEADER_DAV = 'dav' --HTTP_HEADER_DASL = 'dasl' --HTTP_HEADER_OPTION_ACL = 'access-control' --HTTP_HEADER_OPTION_DAV_BASIC_SEARCH = 'DAV:basicsearch' --HTTP_HEADER_SERVER = 'server' --HTTP_HEADER_SERVER_TAMINO = 'Apache/2.0.54 (Win32)' -diff --git a/src/webdav/Makefile.am b/src/webdav/Makefile.am -deleted file mode 100644 -index 3356daf..0000000 ---- a/src/webdav/Makefile.am -+++ /dev/null -@@ -1,20 +0,0 @@ --SUBDIRS = acp -- --sugardir = $(pythondir)/webdav --sugar_PYTHON = \ -- Connection.py \ -- davlib.py \ -- logger.py \ -- NameCheck.py \ -- Utils.py \ -- VersionHandler.py \ -- WebdavRequests.py \ -- Condition.py \ -- Constants.py \ -- __init__.py \ -- qp_xml.py \ -- uuid_.py \ -- WebdavClient.py \ -- WebdavResponse.py -- -- -diff --git a/src/webdav/NameCheck.py b/src/webdav/NameCheck.py -deleted file mode 100644 -index 7976973..0000000 ---- a/src/webdav/NameCheck.py -+++ /dev/null -@@ -1,193 +0,0 @@ --# pylint: disable-msg=R0904,W0142,W0511,W0104,C0321,E1103,W0212 --# --# Copyright 2008 German Aerospace Center (DLR) --# --# Licensed under the Apache License, Version 2.0 (the "License"); --# you may not use this file except in compliance with the License. --# You may obtain a copy of the License at --# --# http://www.apache.org/licenses/LICENSE-2.0 --# --# Unless required by applicable law or agreed to in writing, software --# distributed under the License is distributed on an "AS IS" BASIS, --# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. --# See the License for the specific language governing permissions and --# limitations under the License. -- -- --""" --Check name of new collections/resources for "illegal" characters. --""" -- -- --import re --import unicodedata -- -- --__version__ = "$LastChangedRevision$" -- -- --_unicodeUmlaut = [unicodedata.lookup("LATIN CAPITAL LETTER A WITH DIAERESIS"), -- unicodedata.lookup("LATIN SMALL LETTER A WITH DIAERESIS"), -- unicodedata.lookup("LATIN CAPITAL LETTER O WITH DIAERESIS"), -- unicodedata.lookup("LATIN SMALL LETTER O WITH DIAERESIS"), -- unicodedata.lookup("LATIN CAPITAL LETTER U WITH DIAERESIS"), -- unicodedata.lookup("LATIN SMALL LETTER U WITH DIAERESIS"), -- unicodedata.lookup("LATIN SMALL LETTER SHARP S")] -- --# Define characters and character base sets --_german = u"".join(_unicodeUmlaut) --_alpha = "A-Za-z" --_num = "0-9" --_alphaNum = _alpha + _num --_space = " " --_under = "_" --_dash = "\-" --_dot = "\." --_exclam = "\!" --_tilde = "\~" --_dollar = "\$" --_plus = "+" --_equal = "=" --_sharp = "#" -- --# Define character groups --_letterNum = _alphaNum + _german --_letter = _alpha + _german -- --# Define character sets for names --firstPropertyChar = _letter + _under --propertyChar = firstPropertyChar + _num + _dash + _dot --firstResourceChar = firstPropertyChar + _num + _tilde + _exclam + _dollar + \ -- _dot + _dash + _plus + _equal + _sharp --resourceChar = firstResourceChar + _space -- --# Define regular expressions for name validation --_propertyFirstRe = re.compile(u"^["+ firstPropertyChar +"]") -- --_propertyRe = re.compile(u"[^"+ propertyChar +"]") --_resourceFirstRe = re.compile(u"^["+ firstResourceChar +"]") --_resourceRe = re.compile(u"[^"+ resourceChar +"]") -- -- --def isValidPropertyName(name): -- """ -- Check if the given property name is valid. -- -- @param name: Property name. -- @type name: C{unicode} -- -- @return: Boolean indicating whether the given property name is valid or not. -- @rtype: C{bool} -- """ -- -- illegalChar = _propertyRe.search(name) -- return illegalChar == None and _propertyFirstRe.match(name) != None -- -- --def isValidResourceName(name): -- """ -- Check if the given resource name is valid. -- -- @param name: Resource name. -- @type name: C{unicode} -- -- @return: Boolean indicating whether the given resource name is valid or not. -- @rtype: C{bool} -- """ -- -- illegalChar = _resourceRe.search(name) -- return illegalChar == None and _resourceFirstRe.match(name) != None -- -- --def validatePropertyName(name): -- """ -- Check if the given property name is valid. -- -- @param name: Property name. -- @type name: C{unicode} -- @raise WrongNameError: if validation fails (see L{datafinder.common.NameCheck.WrongNameError}) -- """ -- -- illegalChar = _propertyRe.search(name) -- if illegalChar: -- raise WrongNameError(illegalChar.start(), name[illegalChar.start()]) -- if not _propertyFirstRe.match(name): -- if len(name) > 0: -- raise WrongNameError(0, name[0]) -- else: -- raise WrongNameError(0, 0) -- -- --def validateResourceName(name): -- """ -- Check if the given resource name is valid. -- -- @param name: name of resource/collection -- @type name: C{unicode} -- @raise WrongNameError: if validation fails (@see L{datafinder.common.NameCheck.WrongNameError}) -- """ -- -- illegalChar = _resourceRe.search(name) -- if illegalChar: -- raise WrongNameError(illegalChar.start(), name[illegalChar.start()]) -- if not _resourceFirstRe.match(name): -- if len(name) > 0: -- raise WrongNameError(0, name[0]) -- else: -- raise WrongNameError(0, 0) -- -- --def getResourceNameErrorPosition(name): -- """ -- Get position of illegal character (and the error-message). -- This method can be used to get this information if L{isValidPropertyName} -- or L{isValidResourceName} failed. -- -- @param name: Resource name. -- @type name: C{unicode} -- -- @return: Tuple of error position and message. -- @rtype: C{tuple} of C{int} and C{unicode} -- """ -- -- result = (-1, None) -- illegalChar = _resourceRe.search(name) -- if illegalChar: -- result = (illegalChar.start(), \ -- u"Illegal character '%s' at index %d." % \ -- (name[illegalChar.start()], illegalChar.start())) -- elif not _resourceFirstRe.match(name): -- result = (0, u"Illegal character '%s' at index %d." % (name[0], 0)) -- return result -- -- --class WrongNameError(ValueError): -- """ -- Exception raised if an "illegal" character was found. -- -- @ivar character: character that caused the exception -- @type character: C{unicode} -- @ivar position: position of C{character} -- @type position: C{int} -- """ -- -- def __init__(self, position, character): -- """ -- Constructor. -- -- @param character: Character that caused the exception. -- @type character: C{unicode} -- @param position: Position of C{character} -- @type position: C{int} -- """ -- -- ValueError.__init__(self) -- self.character = character -- self.position = position -- -- def __str__(self): -- """ Returns string representation. """ -- -- return ValueError.__str__(self) + \ -- "Character '%s' at index %d." % (self.character, self.position) -diff --git a/src/webdav/Utils.py b/src/webdav/Utils.py -deleted file mode 100644 -index ec05755..0000000 ---- a/src/webdav/Utils.py -+++ /dev/null -@@ -1,154 +0,0 @@ --# pylint: disable-msg=W0141,R0912 --# --# Copyright 2008 German Aerospace Center (DLR) --# --# Licensed under the Apache License, Version 2.0 (the "License"); --# you may not use this file except in compliance with the License. --# You may obtain a copy of the License at --# --# http://www.apache.org/licenses/LICENSE-2.0 --# --# Unless required by applicable law or agreed to in writing, software --# distributed under the License is distributed on an "AS IS" BASIS, --# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. --# See the License for the specific language governing permissions and --# limitations under the License. -- -- --""" --The module contains functions to support use of the WebDav functionalities. --""" -- -- --import os --import sys -- --from webdav.WebdavClient import CollectionStorer, ResourceStorer --from webdav.Constants import NS_DAV, PROP_RESOURCE_TYPE, CODE_NOT_FOUND, PROP_RESOURCE_TYPE_RESOURCE --from webdav.Connection import WebdavError -- -- --__version__ = "$Revision$"[11:-2] -- -- --def resourceExists(node, name = None, resourceType = PROP_RESOURCE_TYPE_RESOURCE): -- """ -- Check if resource exists. -- -- Usage: -- - resourceExists(ResourceStorer-object): -- check if resource exists -- - resourceExists(CollectionStorer-object, name): -- check if resource name exists in collection -- -- @param node: node that has to be checked or node of collection -- @type node: L{ResourceStorer<webdav.WebdavClient.ResourceStorer>} -- @param name: name of resource (in collection node) that has to be checked -- @type name: string -- -- @return: boolean -- -- @raise WebdavError: all WebDAV errors except WebDAV error 404 (not found) -- """ -- -- exists = False -- if not node: -- return exists -- try: -- myResourceType = "" -- if name: -- # make sure it's unicode: -- if not isinstance(name, unicode): -- name = name.decode(sys.getfilesystemencoding()) -- url = node.url -- if url.endswith("/"): -- url = url + name -- else: -- url = url + "/" + name -- newNode = ResourceStorer(url, node.connection) -- element = newNode.readProperty(NS_DAV, PROP_RESOURCE_TYPE) -- else: # name is "None": -- element = node.readProperty(NS_DAV, PROP_RESOURCE_TYPE) -- -- if len(element.children) > 0: -- myResourceType = element.children[0].name -- if resourceType == myResourceType or resourceType == PROP_RESOURCE_TYPE_RESOURCE: -- exists = True -- else: -- exists = False -- except WebdavError, wderr: -- if wderr.code == CODE_NOT_FOUND: -- # node doesn't exist -> exists = False: -- exists = False -- else: -- # another exception occured -> "re-raise" it: -- raise -- return exists -- -- --def downloadCollectionContent(destinationPath, collectionToDownload): -- """ -- Downloads the resources contained to the given directory. -- -- @param destinationPath: Path to download the files to, will be created if it not exists. -- @type destinationPath: C{String} -- @param collectionToDownload: Collection to download the content from. -- @type collectionToDownload: instance of L{CollectionStorer<webdav.WebdavClient.CollectionStorer>} -- -- @raise WebdavError: If something goes wrong. -- """ -- -- from time import mktime, gmtime -- -- downloadCount = 0 -- -- listOfItems = collectionToDownload.getCollectionContents() -- -- if not os.path.exists(destinationPath): -- try: -- os.makedirs(destinationPath) -- except OSError: -- errorMessage = "Cannot create download destination directory '%s'." % destinationPath -- raise WebdavError(errorMessage) -- -- try: -- itemsInPath = os.listdir(destinationPath) -- except OSError: -- errorMessage = "Cannot read the content of download destination directory '%s'." % destinationPath -- raise WebdavError(errorMessage) -- -- for item in listOfItems: -- # skip collections -- if not isinstance(item[0], CollectionStorer): -- itemSavePath = os.path.join(destinationPath, item[0].name) -- existsItemSavePath = os.path.exists(itemSavePath) -- -- # update? -- if existsItemSavePath: -- try: -- isUpdateNecessary = mktime(item[1].getLastModified()) > mktime(gmtime(os.path.getmtime(itemSavePath))) -- except (ValueError, OverflowError): -- isUpdateNecessary = True -- # windows is not case sensitive -- for realItem in itemsInPath: -- if realItem.lower() == item[0].name.lower(): -- itemsInPath.remove(realItem) -- else: -- isUpdateNecessary = True -- -- # download -- if not existsItemSavePath or (existsItemSavePath and isUpdateNecessary): -- item[0].downloadFile(itemSavePath) -- downloadCount = downloadCount + 1 -- -- # delete old items -- try: -- for item in itemsInPath: -- os.remove(os.path.join(destinationPath, item)) -- except OSError, e: -- if e.errno == 13: # permission error -- sys.stderr.write("permission problem on '%s' in %s\n" % (e.filename, e.strerror)) -- else: -- raise -- -- return downloadCount -diff --git a/src/webdav/VersionHandler.py b/src/webdav/VersionHandler.py -deleted file mode 100644 -index a1962c6..0000000 ---- a/src/webdav/VersionHandler.py -+++ /dev/null -@@ -1,198 +0,0 @@ --# pylint: disable-msg=W0612,W0142 --# --# Copyright 2008 German Aerospace Center (DLR) --# --# Licensed under the Apache License, Version 2.0 (the "License"); --# you may not use this file except in compliance with the License. --# You may obtain a copy of the License at --# --# http://www.apache.org/licenses/LICENSE-2.0 --# --# Unless required by applicable law or agreed to in writing, software --# distributed under the License is distributed on an "AS IS" BASIS, --# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. --# See the License for the specific language governing permissions and --# limitations under the License. -- -- --""" --The WebDAV client module forwards Delta-V related method invocations to --the following VersionHandler class. --""" -- --__version__ = '$Revision$'[11:-2] -- -- --import types -- --from webdav import Constants --from davlib import XML_CONTENT_TYPE, XML_DOC_HEADER -- -- --class VersionHandler(object): -- """ -- Implements a client interface for WebDAV Delta-V methods -- For the Delta-V see RFC 3253 at http://www.ietf.org/rfc/rfc3253.txt -- """ -- -- # restrict instance variables -- __slots__ = ('path', 'connection') -- -- -- def __init__(self, connection, path): -- """ -- Construct a VersionHandler with a URL path and a WebDAV connection. -- This constructor must not be called outside class ResourceStorer. -- -- @param connection: L{webdav.Connection} instance -- @param path: resource's path part of URL -- """ -- #assert isinstance(connection, Connection), \ -- # "Class of connection is %s." % connection.__class__.__name__ -- self.connection = connection -- self.path = path -- -- -- def activateVersionControl(self): -- """ -- Turns version control on for this resource. -- The resource becomes a version controlled resource (VCR) -- """ -- response = self.connection._request(Constants.METHOD_VERSION_CONTROL, self.path, None, {}) -- # set auto-versioning to DAV:locked-checkout -- ## parse response body in case of an error -- -- def uncheckout(self, lockToken=None): -- """ -- Undos a previous check-out operation on this VCR. -- The VCR is reverted to the state before the checkout/lock operation. -- Beware: Property or content changes will be lost ! -- A (optional) lock has to be removed seperatedly. -- -- @param lockToken: returned by a preceeding lock() method invocation or None -- """ -- headers = {} -- if lockToken: -- headers = lockToken.toHeader() -- response = self.connection._request(Constants.METHOD_UNCHECKOUT, self.path, None, headers) -- ## parse response body in case of an error -- -- def listAllVersions(self): -- """ -- List version history. -- -- @return: List of versions for this VCR. Each version entry is a tuple adhering -- to the format (URL-path, name, creator, tuple of successor URL-paths). -- If there are no branches then there is at most one successor within the tuple. -- """ -- # implementation is similar to the propfind method -- headers = {} -- headers['Content-Type'] = XML_CONTENT_TYPE -- body = _createReportVersionTreeBody() -- response = self.connection._request(Constants.METHOD_REPORT, self.path, body, headers) -- # response is multi-status -- result = [] -- for path, properties in response.msr.items(): -- # parse the successor-set value from XML into alist -- result.append( (path, str(properties[Constants.PROP_VERSION_NAME]), \ -- str(properties[Constants.PROP_CREATOR]), \ -- _extractSuccessorList(properties[Constants.PROP_SUCCESSOR_SET])) ) -- ## TODO: sort for path and produce list -- result.sort() -- return result -- -- # warning: not tested yet -- def readVersionProperties(self): -- """ -- Provide version related information on this VCR. -- This include a reference to the latest version resource, -- check-out state information and a comment. -- -- @return: map of version properties with values. -- """ -- versionProperties = (Constants.PROP_CHECKEDIN, Constants.PROP_CHECKEDOUT, Constants.PROP_COMMENT) -- return self.connection.readProperties(*versionProperties) -- -- -- def revertToVersion(self, oldVersion): -- """ -- Revert this VCR to the given version. -- Beware: All versions succeeding the given version are made unavailable. -- -- @param oldVersion: URL-path of a previous version of this VCR. -- """ -- ## send an update request -- assert isinstance(oldVersion, types.StringType) or isinstance(oldVersion, types.UnicodeType) -- response = self.connection._request(Constants.METHOD_UPDATE, self.path, -- _createUpdateBody(oldVersion), {}) -- return response -- -- -- # the following is not needed when using auto-versioning -- -- # warning: not tested yet -- def checkout(self): -- """ -- Marks resource as checked-out -- This is usually followed by a GET (download) operation. -- """ -- response = self.connection._request(Constants.METHOD_CHECKOUT, self.path, None, {}) -- ## parse response body in case of an error -- -- # warning: not tested yet -- def checkin(self): -- """ -- Creates a new version from the VCR's content. -- This opeartion is usually preceeded by one or more write operations. -- """ -- response = self.connection._request(Constants.METHOD_CHECKIN, self.path, None, {}) -- versionUrl = response.getheader('Location') -- return versionUrl -- ## parse response body in case of an error -- -- -- -- --# Helper functions --def _createReportVersionTreeBody(): -- """ -- TBD -- -- @return: ... -- @rtype: string -- """ -- versions = 'D:' + Constants.TAG_VERSION_TREE -- prop = 'D:' + Constants.TAG_PROP -- nameList = [Constants.PROP_SUCCESSOR_SET, Constants.PROP_VERSION_NAME, Constants.PROP_CREATOR] -- return XML_DOC_HEADER + \ -- '<%s xmlns:D="DAV:"><%s>' % (versions, prop) + \ -- reduce(lambda xml, name: xml + "<D:%s/>" % name[1], [''] + nameList) + \ -- '</%s></%s>' % (prop, versions) -- --def _createUpdateBody(path): -- """ -- TBD -- -- @return: ... -- @rtype: string -- """ -- update = 'D:' + Constants.TAG_UPDATE -- version = 'D:' + Constants.TAG_VERSION -- href = 'D:' + Constants.TAG_HREF -- #PROP = 'D:' + TAG_PROP -- return XML_DOC_HEADER + \ -- '<%s xmlns:D="DAV:"><%s><%s>' % (update, version, href) + \ -- path + \ -- '</%s></%s></%s>' % (href, version, update) -- --def _extractSuccessorList(element): -- """ -- TBD -- -- @return: ... -- @rtype: tuple of strings -- """ -- result = [] -- for href in element.children: -- result.append(href.textof()) -- return tuple(result) -diff --git a/src/webdav/WebdavClient.py b/src/webdav/WebdavClient.py -deleted file mode 100644 -index ab5cec3..0000000 ---- a/src/webdav/WebdavClient.py -+++ /dev/null -@@ -1,840 +0,0 @@ --# pylint: disable-msg=R0904,W0142,W0511,W0104,C0321,E1103,W0212 --# --# Copyright 2008 German Aerospace Center (DLR) --# --# Licensed under the Apache License, Version 2.0 (the "License"); --# you may not use this file except in compliance with the License. --# You may obtain a copy of the License at --# --# http://www.apache.org/licenses/LICENSE-2.0 --# --# Unless required by applicable law or agreed to in writing, software --# distributed under the License is distributed on an "AS IS" BASIS, --# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. --# See the License for the specific language governing permissions and --# limitations under the License. -- -- --""" --This module contains the classes ResourceStorer and CollectionStorer for accessing WebDAV resources. --""" -- -- --from davlib import XML_CONTENT_TYPE -- --from urlparse import urlsplit --import re --import types --import sys --import os --import shutil --import socket -- --from webdav import Constants --from webdav.WebdavResponse import LiveProperties --from webdav.WebdavRequests import createFindBody, createUpdateBody, createDeleteBody, createSearchBody --from webdav.Condition import ConditionTerm --from webdav.Connection import Connection, WebdavError, AuthorizationError --from webdav.VersionHandler import VersionHandler -- --from webdav.acp.Privilege import Privilege --from webdav.acp.Acl import ACL --from webdav.NameCheck import validateResourceName, WrongNameError -- -- --__version__ = '$Revision$'[11:-2] -- --SOCKET_DEFAULT_TIMEOUT = 10 -- -- --def switchUnicodeUrlOn(switch): -- """ -- Configure whether to use unicode (UTF-8) encoded URLs (default) or -- Latin-1 encoded URLs. -- -- @param switch: 1 if unicode URLs shall be used -- """ -- -- assert switch == 0 or switch == 1, "Pass boolean argument, please." -- Constants.CONFIG_UNICODE_URL = switch -- -- --def parseDigestAuthInfo(authInfo): -- """ -- Parses the authentication information returned from a server and returns -- a dictionary containing realm, qop, and nonce. -- -- @see: L{AuthorizationError<webdav.Connection.AuthorizationError>} -- or the main function of this module. -- """ -- -- info = dict() -- info["realm"] = re.search('realm="([^"]+)"', authInfo).group(1) -- info["qop"] = re.search('qop="([^"]+)"', authInfo).group(1) -- info["nonce"] = re.search('nonce="([^"]+)"', authInfo).group(1) -- return info -- -- --class ResourceStorer(object): -- """ -- This class provides client access to a WebDAV resource -- identified by an URI. It provides all WebDAV class 2 features which include -- uploading data, getting and setting properties qualified by a XML name space, -- locking and unlocking the resource. -- This class does not cache resource data. This has to be performed by its clients. -- -- @author: Roland Betz -- """ -- -- # Instance properties -- url = property(lambda self: str(self.connection) + self.path, None, None, "Resource's URL") -- -- def __init__(self, url, connection=None, validateResourceNames=True): -- """ -- Creates an instance for the given URL -- User must invoke validate() after construction to check the resource on the server. -- -- @param url: Unique resource location for this storer. -- @type url: C{string} -- @param connection: this optional parameter contains a Connection object -- for the host part of the given URL. Passing a connection saves -- memory by sharing this connection. (defaults to None) -- @type connection: L{webdav.Connection} -- @raise WebdavError: If validation of resource name path parts fails. -- """ -- -- assert connection == None or isinstance(connection, Connection) -- parts = urlsplit(url, allow_fragments=False) -- self.path = parts[2] -- self.validateResourceNames = validateResourceNames -- -- # validate URL path -- for part in self.path.split('/'): -- if part != '' and not "ino:" in part: # explicitly allowing this character sequence as a part of a path (Tamino 4.4) -- if self.validateResourceNames: -- try: -- validateResourceName(part) -- except WrongNameError: -- raise WebdavError("Found invalid resource name part.") -- self.name = part -- # was: filter(lambda part: part and validateResourceName(part), self.path.split('/')) -- # but filter is deprecated -- -- self.defaultNamespace = None # default XML name space of properties -- if connection: -- self.connection = connection -- else: -- conn = parts[1].split(":") -- if len(conn) == 1: -- self.connection = Connection(conn[0], protocol = parts[0]) # host and protocol -- else: -- self.connection = Connection(conn[0], int(conn[1]), protocol = parts[0]) # host and port and protocol -- self.versionHandler = VersionHandler(self.connection, self.path) -- -- -- def validate(self): -- """ -- Check whether URL contains a WebDAV resource -- Uses the WebDAV OPTIONS method. -- -- @raise WebdavError: L{WebdavError} if URL does not contain a WebDAV resource -- """ -- #davHeader = response.getheader(HTTP_HEADER_DAV) -- davHeader = self.getSpecificOption(Constants.HTTP_HEADER_DAV) -- self.connection.logger.debug("HEADER DAV: %s" % davHeader) -- if not(davHeader) or davHeader.find("2") < 0: # DAV class 2 supported ? -- raise WebdavError("URL does not support WebDAV", 0) -- -- def options(self): -- """ -- Send an OPTIONS request to server and return all HTTP headers. -- -- @return: map of all HTTP headers returned by the OPTIONS method. -- """ -- response = self.connection.options(self.path) -- result = {} -- result.update(response.msg) -- self.connection.logger.debug("OPTION returns: " + str(result.keys())) -- return result -- -- def _getAclSupportAvailable(self): -- """ -- Returns True if the current connection has got ACL support. -- -- @return: ACL support (True / False) -- @rtype: C{bool} -- """ -- options = self.getSpecificOption(Constants.HTTP_HEADER_DAV) -- if options.find(Constants.HTTP_HEADER_OPTION_ACL) >= 0: -- return True -- else: -- return False -- -- aclSupportAvailable = property(_getAclSupportAvailable) -- -- def _getDaslBasicsearchSupportAvailable(self): -- """ -- Returns True if the current connection supports DASL basic search. -- -- @return: DASL basic search support (True / False) -- @rtype: C{bool} -- """ -- options = self.getSpecificOption(Constants.HTTP_HEADER_DASL) -- if not options or \ -- not options.find(Constants.HTTP_HEADER_OPTION_DAV_BASIC_SEARCH) >= 0: -- return False -- else: -- return True -- -- daslBasicsearchSupportAvailable = property(_getDaslBasicsearchSupportAvailable) -- -- def isConnectedToCatacombServer(self): -- """ -- Returns True if connected to a Catacomb WebDav server. -- -- @return: if connected to Catacomb Webdav server (True / False) -- @rtype: C{bool} -- """ -- if not self.connection.serverTypeChecked: -- options = self.getSpecificOption(Constants.HTTP_HEADER_SERVER) -- if options.find(Constants.HTTP_HEADER_SERVER_TAMINO) >= 0: -- self.connection.isConnectedToCatacomb = False -- else: -- self.connection.isConnectedToCatacomb = True -- self.connection.serverTypeChecked = True -- return self.connection.isConnectedToCatacomb -- -- def getSpecificOption(self, option): -- """ -- Returns specified WebDav options. -- @param option: name of the option -- -- @return: String containing the value of the option. -- @rtype: C{string} -- """ -- options = '' -- try: -- options = self.options().get(option) -- except KeyError: -- return options -- return options -- -- ### delegate some method invocations -- def __getattr__(self, name): -- """ -- Build-in method: -- Forwards unknow lookups (methods) to delegate object 'versionHandler'. -- -- @param name: name of unknown attribute -- """ -- # delegate Delta-V methods -- return getattr(self.versionHandler, name) -- -- def copy(self, toUrl, infinity=True): -- """ -- Copies this resource. -- -- @param toUrl: target URI path -- @param infinity: Flag that indicates that the complete content of collection is copied. (default) -- @type depth: C{boolean} -- """ -- self.connection.logger.debug("Copy to " + repr(toUrl)); -- _checkUrl(toUrl) -- if infinity: -- response = self.connection.copy(self.path, toUrl) -- else: -- response = self.connection.copy(self.path, toUrl, 0) -- if response.status == Constants.CODE_MULTISTATUS and response.msr.errorCount > 0: -- raise WebdavError("Request failed: " + response.msr.reason, response.msr.code) -- -- def delete(self, lockToken=None): -- """ -- Deletes this resource. -- -- @param lockToken: String returned by last lock operation or null. -- @type lockToken: L{LockToken} -- """ -- assert lockToken == None or isinstance(lockToken, LockToken), \ -- "Invalid lockToken argument %s" % type(lockToken) -- header = {} -- if lockToken: -- header = lockToken.toHeader() -- response = self.connection.delete(self.path, header) -- if response.status == Constants.CODE_MULTISTATUS and response.msr.errorCount > 0: -- raise WebdavError("Request failed: " + response.msr.reason, response.msr.code) -- -- def move(self, toUrl): -- """ -- Moves this resource to the given path or renames it. -- -- @param toUrl: new (URI) path -- """ -- self.connection.logger.debug("Move to " + repr(toUrl)); -- _checkUrl(toUrl) -- response = self.connection.move(self.path, toUrl) -- if response.status == Constants.CODE_MULTISTATUS and response.msr.errorCount > 0: -- raise WebdavError("Request failed: " + response.msr.reason, response.msr.code) -- -- -- def lock(self, owner): -- """ -- Locks this resource for exclusive write access. This means that for succeeding -- write operations the returned lock token has to be passed. -- If the methode does not throw an exception the lock has been granted. -- -- @param owner: describes the lock holder -- @return: lock token string (automatically generated) -- @rtype: L{LockToken} -- """ -- response = self.connection.lock(self.path, owner) -- if response.status == Constants.CODE_MULTISTATUS and response.msr.errorCount > 0: -- raise WebdavError("Request failed: " + response.msr.reason, response.msr.code) -- return LockToken(self.url, response.locktoken) -- -- def unlock(self, lockToken): -- """ -- Removes the lock from this resource. -- -- @param lockToken: which has been return by the lock() methode -- @type lockToken: L{LockToken} -- """ -- self.connection.unlock(self.path, lockToken.token) -- -- -- def deleteContent(self, lockToken=None): -- """ -- Delete binary data at permanent storage. -- -- @param lockToken: None or lock token from last lock request -- @type lockToken: L{LockToken} -- """ -- assert lockToken == None or isinstance(lockToken, LockToken), \ -- "Invalid lockToken argument %s" % type(lockToken) -- header = {} -- if lockToken: -- header = lockToken.toHeader() -- self.connection.put(self.path, "", extra_hdrs=header) -- -- def uploadContent(self, content, lockToken=None): -- """ -- Write binary data to permanent storage. -- -- @param content: containing binary data -- @param lockToken: None or lock token from last lock request -- @type lockToken: L{LockToken} -- """ -- assert not content or isinstance(content, types.UnicodeType) or\ -- isinstance(content, types.StringType), "Content is not a string: " + content.__class__.__name__ -- assert lockToken == None or isinstance(lockToken, LockToken), \ -- "Invalid lockToken argument %s" % type(lockToken) -- header = {} -- if lockToken: -- header = lockToken.toHeader() -- response = None -- if not content is None: -- header["Content-length"] = len(content) -- else: -- header["Content-length"] = 0 -- -- try: -- response = self.connection.put(self.path, content, extra_hdrs=header) -- finally: -- if response: -- self.connection.logger.debug(response.read()) -- response.close() -- -- def uploadFile(self, newFile, lockToken=None): -- """ -- Write binary data to permanent storage. -- -- @param newFile: File containing binary data. -- @param lockToken: None or lock token from last lock request -- @type lockToken: L{LockToken} -- """ -- assert isinstance(newFile, types.FileType), "Argument is no file: " + file.__class__.__name__ -- assert lockToken == None or isinstance(lockToken, LockToken), \ -- "Invalid lockToken argument %s" % type(lockToken) -- header = {} -- if lockToken: -- header = lockToken.toHeader() -- self.connection.putFile(self.path, newFile, header=header) -- -- def downloadContent(self): -- """ -- Read binary data from permanent storage. -- """ -- response = self.connection.get(self.path) -- # TODO: Other interface ? return self.connection.getfile() -- return response -- -- def downloadFile(self, localFileName): -- """ -- Copy binary data from permanent storage to a local file. -- -- @param localFileName: file to write binary data to -- """ -- localFile = open(localFileName, 'wb') -- remoteFile = self.downloadContent() -- try: -- socket.setdefaulttimeout(SOCKET_DEFAULT_TIMEOUT) -- _blockCopyFile(remoteFile, localFile, Connection.blockSize) -- except socket.error, e: -- raise e -- remoteFile.close() -- localFile.close() -- -- def readProperties(self, *names): -- """ -- Reads the given properties. -- -- @param names: a list of property names. -- A property name is a (XmlNameSpace, propertyName) tuple. -- @return: a map from property names to DOM Element or String values. -- """ -- assert names, "Property names are missing." -- body = createFindBody(names, self.defaultNamespace) -- response = self.connection.propfind(self.path, body, depth=0) -- properties = response.msr.values()[0] -- if properties.errorCount > 0: -- raise WebdavError("Property is missing on '%s': %s" % (self.path, properties.reason), properties.code) -- return properties -- -- def readProperty(self, nameSpace, name): -- """ -- Reads the given property. -- -- @param nameSpace: XML-namespace -- @type nameSpace: string -- @param name: A property name. -- @type name: string -- -- @return: a map from property names to DOM Element or String values. -- """ -- results = self.readProperties((nameSpace, name)) -- if len(results) == 0: -- raise WebdavError("Property is missing: " + results.reason) -- return results.values()[0] -- -- def readAllProperties(self): -- """ -- Reads all properties of this resource. -- -- @return: a map from property names to DOM Element or String values. -- """ -- response = self.connection.allprops(self.path, depth=0) -- return response.msr.values()[0] -- -- def readAllPropertyNames(self): -- """ -- Returns the names of all properties attached to this resource. -- -- @return: List of property names -- """ -- response = self.connection.propnames(self.path, depth=0) -- return response.msr.values()[0] -- -- def readStandardProperties(self): -- """ -- Read all WebDAV live properties. -- -- @return: A L{LiveProperties} instance which contains a getter method for each live property. -- """ -- body = createFindBody(LiveProperties.NAMES, Constants.NS_DAV) -- response = self.connection.propfind(self.path, body, depth=0) -- properties = response.msr.values()[0] -- return LiveProperties(properties) -- -- def writeProperties(self, properties, lockToken=None): -- """ -- Sets or updates the given properties. -- -- @param lockToken: if the resource has been locked this is the lock token. -- @type lockToken: L{LockToken} -- @param properties: a map from property names to a String or -- DOM element value for each property to add or update. -- """ -- assert isinstance(properties, types.DictType) -- assert lockToken == None or isinstance(lockToken, LockToken), \ -- "Invalid lockToken argument %s" % type(lockToken) -- header = {} -- if lockToken: -- header = lockToken.toHeader() -- body = createUpdateBody(properties, self.defaultNamespace) -- response = self.connection.proppatch(self.path, body, header) -- if response.msr.errorCount > 0: -- raise WebdavError("Request failed: " + response.msr.reason, response.msr.code) -- -- def deleteProperties(self, lockToken=None, *names): -- """ -- Removes the given properties from this resource. -- -- @param lockToken: if the resource has been locked this is the lock token. -- @type lockToken: L{LockToken} -- @param names: a collection of property names. -- A property name is a (XmlNameSpace, propertyName) tuple. -- """ -- assert lockToken == None or isinstance(lockToken, LockToken), \ -- "Invalid lockToken argument %s" % type(lockToken) -- header = {} -- if lockToken: -- header = lockToken.toHeader() -- body = createDeleteBody(names, self.defaultNamespace) -- response = self.connection.proppatch(self.path, body, header) -- if response.msr.errorCount > 0: -- raise WebdavError("Request failed: " + response.msr.reason, response.msr.code) -- -- # ACP extension -- def setAcl(self, acl, lockToken=None): -- """ -- Sets ACEs in the non-inherited and non-protected ACL or the resource. -- This is the implementation of the ACL method of the WebDAV ACP. -- -- @param acl: ACL to be set on resource as ACL object. -- @param lockToken: If the resource has been locked this is the lock token (defaults to None). -- @type lockToken: L{LockToken} -- """ -- assert lockToken == None or isinstance(lockToken, LockToken), \ -- "Invalid lockToken argument %s" % type(lockToken) -- headers = {} -- if lockToken: -- headers = lockToken.toHeader() -- headers['Content-Type'] = XML_CONTENT_TYPE -- body = acl.toXML() -- response = self.connection._request('ACL', self.path, body, headers) -- return response -- ## TODO: parse DAV:error response -- -- def getAcl(self): -- """ -- Returns this resource's ACL in an ACL instance. -- -- @return: Access Control List. -- @rtype: L{ACL<webdav.acp.Acl.ACL>} -- """ -- xmlAcl = self.readProperty(Constants.NS_DAV, Constants.TAG_ACL) -- return ACL(xmlAcl) -- -- def getCurrentUserPrivileges(self): -- """ -- Returns a tuple of the current user privileges. -- -- @return: list of Privilege instances -- @rtype: list of L{Privilege<webdav.acp.Privilege.Privilege>} -- """ -- privileges = self.readProperty(Constants.NS_DAV, Constants.PROP_CURRENT_USER_PRIVILEGE_SET) -- result = [] -- for child in privileges.children: -- result.append(Privilege(domroot=child)) -- return result -- -- def getPrincipalCollections(self): -- """ -- Returns a list principal collection URLs. -- -- @return: list of principal collection URLs -- @rtype: C{list} of C{unicode} elements -- """ -- webdavQueryResult = self.readProperty(Constants.NS_DAV, Constants.PROP_PRINCIPAL_COLLECTION_SET) -- principalCollectionList = [] -- for child in webdavQueryResult.children: -- principalCollectionList.append(child.first_cdata) -- return principalCollectionList -- -- def getOwnerUrl(self): -- """ Explicitly retireve the Url of the owner. """ -- -- result = self.readProperty(Constants.NS_DAV, Constants.PROP_OWNER) -- if result and len(result.children): -- return result.children[0].textof() -- return None -- --class CollectionStorer(ResourceStorer): -- """ -- This class provides client access to a WebDAV collection resource identified by an URI. -- This class does not cache resource data. This has to be performed by its clients. -- -- @author: Roland Betz -- """ -- -- def __init__(self, url, connection=None, validateResourceNames=True): -- """ -- Creates a CollectionStorer instance for a URL and an optional Connection object. -- User must invoke validate() after constuction to check the resource on the server. -- -- @see: L{webdav.WebdavClient.ResourceStorer.__init__} -- @param url: unique resource location for this storer -- @param connection: this optional parameter contains a Connection object for the host part -- of the given URL. Passing a connection saves memory by sharing this connection. -- """ -- if url[-1] != '/': # Collection URL must end with slash -- url += '/' -- ResourceStorer.__init__(self, url, connection, validateResourceNames) -- -- def getResourceStorer(self, name): -- """ -- Return a ResourceStorer instance for a child resource (member) of this Collection. -- -- @param name: leaf name of child resource -- @return: L{ResourceStorer} instance -- """ -- assert isinstance(name, types.StringType) or isinstance(name, types.UnicodeType) -- return ResourceStorer(self.url + name, self.connection, self.validateResourceNames) -- -- def validate(self): -- """ -- Check whether this URL contains a WebDAV collection. -- Uses the WebDAV OPTION method. -- -- @raise WebdavError: L{WebdavError} if URL does not contain a WebDAV collection resource. -- """ -- super(CollectionStorer, self).validate() -- isCollection = self.readProperty(Constants.NS_DAV, Constants.PROP_RESOURCE_TYPE) -- if not (isCollection and isCollection.children): -- raise WebdavError("Not a collection URL.", 0) -- -- def addCollection(self, name, lockToken=None): -- """ -- Make a new WebDAV collection resource within this collection. -- -- @param name: of the new collection -- @param lockToken: None or token returned by last lock operation -- @type lockToken: L{LockToken} -- """ -- assert isinstance(name, types.StringType) or isinstance(name, types.UnicodeType) -- assert lockToken == None or isinstance(lockToken, LockToken), \ -- "Invalid lockToken argument %s" % type(lockToken) -- header = {} -- if lockToken: -- header = lockToken.toHeader() -- if self.validateResourceNames: -- validateResourceName(name) -- if name[-1] != '/': # Collection URL must end with slash -- name += '/' -- self.connection.mkcol(self.path + name, header) -- return CollectionStorer(self.url + name, self.connection, self.validateResourceNames) -- -- def addResource(self, name, content=None, properties=None, lockToken=None): -- """ -- Create a new empty WebDAV resource contained in this collection with the given -- properties. -- -- @param name: leaf name of the new resource -- @param content: None or initial binary content of resource -- @param properties: name/value-map containing properties -- @param lockToken: None or token returned by last lock operation -- @type lockToken: L{LockToken} -- """ -- assert isinstance(name, types.StringType) or isinstance(name, types.UnicodeType) -- assert lockToken == None or isinstance(lockToken, LockToken), \ -- "Invalid lockToken argument %s" % type(lockToken) -- if self.validateResourceNames: -- validateResourceName(name) # check for invalid characters -- resource_ = ResourceStorer(self.url + name, self.connection, self.validateResourceNames) -- resource_.uploadContent(content, lockToken) -- if properties: -- resource_.writeProperties(properties, lockToken) -- return resource_ -- -- def deleteResource(self, name, lockToken=None): -- """ -- Delete a collection which is contained within this collection -- -- @param name: leaf name of a contained collection resource -- @param lockToken: None or token returned by last lock operation -- @type lockToken: L{LockToken} -- """ -- assert isinstance(name, types.StringType) or isinstance(name, types.UnicodeType) -- assert lockToken == None or isinstance(lockToken, LockToken), \ -- "Invalid lockToken argument %s" % type(lockToken) -- header = {} -- if lockToken: -- header = lockToken.toHeader() -- if self.validateResourceNames: -- validateResourceName(name) -- response = self.connection.delete(self.path + name, header) -- if response.status == Constants.CODE_MULTISTATUS and response.msr.errorCount > 0: -- raise WebdavError("Request failed: %s" % response.msr.reason, response.msr.code) -- -- def lockAll(self, owner): -- """ -- Locks this collection resource for exclusive write access. This means that for -- succeeding write operations the returned lock token has to be passed. -- The operation is applied recursively to all contained resources. -- If the methode does not throw an exception then the lock has been granted. -- -- @param owner: describes the lock holder -- @return: Lock token string (automatically generated). -- @rtype: L{LockToken} -- """ -- assert isinstance(owner, types.StringType) or isinstance(owner, types.UnicodeType) -- response = self.connection.lock(self.path, owner, depth=Constants.HTTP_HEADER_DEPTH_INFINITY) -- return LockToken(self.url, response.locktoken) -- -- def listResources(self): -- """ -- Describe all members within this collection. -- -- @return: map from URI to a L{LiveProperties} instance containing the WebDAV -- live attributes of the contained resource -- """ -- # *LiveProperties.NAMES denotes the list of all live properties as an -- # argument to the method call. -- response = self.connection.getprops(self.path, -- depth=1, -- ns=Constants.NS_DAV, -- *LiveProperties.NAMES) -- result = {} -- for path, properties in response.msr.items(): -- if path == self.path: # omit this collection resource -- continue -- ## some servers do not append a trailing slash to collection paths -- if self.path.endswith('/') and self.path[0:-1] == path: -- continue -- result[path] = LiveProperties(properties=properties) -- return result -- -- def getCollectionContents(self): -- """ -- Return a list of the tuple (resources or collection) / properties) -- -- @return: a list of the tuple (resources or collection) / properties) -- @rtype: C{list} -- """ -- self.validate() -- collectionContents = [] -- result = self.listResources() -- for url, properties_ in result.items(): -- if not self.path == url: -- if properties_.getResourceType() == 'resource': -- myWebDavStorer = ResourceStorer(url, self.connection, self.validateResourceNames) -- else: -- myWebDavStorer = CollectionStorer(url, self.connection, self.validateResourceNames) -- collectionContents.append((myWebDavStorer, properties_)) -- return collectionContents -- -- def findProperties(self, *names): -- """ -- Retrieve given properties for this collection and all directly contained resources. -- -- @param names: a list of property names -- @return: a map from resource URI to a map from property name to value. -- """ -- assert isinstance(names, types.ListType) or isinstance(names, types.TupleType), \ -- "Argument name has type %s" % str(type(names)) -- body = createFindBody(names, self.defaultNamespace) -- response = self.connection.propfind(self.path, body, depth=1) -- return response.msr -- -- def deepFindProperties(self, *names): -- """ -- Retrieve given properties for this collection and all contained (nested) resources. -- -- Note: -- ===== -- This operation can take a long time if used with recursive=true and is therefore -- disabled on some WebDAV servers. -- -- @param names: a list of property names -- @return: a map from resource URI to a map from property name to value. -- """ -- assert isinstance(names, types.ListType.__class__) or isinstance(names, types.TupleType), \ -- "Argument name has type %s" % str(type(names)) -- body = createFindBody(names, self.defaultNamespace) -- response = self.connection.propfind(self.path, body, depth=Constants.HTTP_HEADER_DEPTH_INFINITY) -- return response.msr -- -- def findAllProperties(self): -- """ -- Retrieve all properties for this collection and all directly contained resources. -- -- @return: a map from resource URI to a map from property name to value. -- """ -- response = self.connection.allprops(self.path, depth=1) -- return response.msr -- -- -- # DASL extension -- def search(self, conditions, selects): -- """ -- Search for contained resources which match the given search condition. -- -- @param conditions: tree of ConditionTerm instances representing a logical search term -- @param selects: list of property names to retrieve for the found resources -- """ -- assert isinstance(conditions, ConditionTerm) -- headers = { 'Content-Type' : XML_CONTENT_TYPE, "depth": Constants.HTTP_HEADER_DEPTH_INFINITY} -- body = createSearchBody(selects, self.path, conditions) -- response = self.connection._request('SEARCH', self.path, body, headers) -- return response.msr -- -- --class LockToken(object): -- """ -- This class provides help on handling WebDAV lock tokens. -- -- @author: Roland Betz -- """ -- # restrict instance variables -- __slots__ = ('url', 'token') -- -- def __init__(self, url, token): -- assert isinstance(url, types.StringType) or isinstance(url, types.UnicodeType), \ -- "Invalid url argument %s" % type(url) -- assert isinstance(token, types.StringType) or isinstance(token, types.UnicodeType), \ -- "Invalid lockToken argument %s" % type(token) -- self.url = url -- self.token = token -- -- def value(self): -- """ -- Descriptive string containing the lock token's URL and the token itself. -- -- @return: Descriptive lock token with URL. -- @rtype: C{string} -- """ -- return "<" + self.url + "> (<" + self.token + ">)" -- -- def toHeader(self): -- """ -- Header fragment for WebDAV request. -- -- @return: Dictionary containing an entry for the lock token query. -- @rtype: C{dictionary} -- """ -- return {Constants.HTTP_HEADER_IF: self.value()} -- -- def __str__(self): -- return self.value() -- -- --def _blockCopyFile(source, dest, blockSize): -- """ -- Copies a file in chunks of C{blockSize}. -- -- @param source: Source file. -- @type source: FileIO buffer. -- @param dest: Destination file. -- @type dest: FileIO buffer. -- @param blockSize: Size of block in bytes. -- @type blockSize: C{int} -- """ -- transferedBytes = 0 -- block = source.read(blockSize) -- while len(block): -- dest.write(block) -- transferedBytes += len(block); -- block = source.read(blockSize) -- --def _checkUrl(url): -- """ -- Checks the given URL for validity. -- -- @param url: URL to check. -- @type url: C{string} -- -- @raise ValueError: If the URL does not contain valid/usable content. -- """ -- -- parts = urlsplit(url, allow_fragments=False) -- if len(parts[0]) == 0 or len(parts[1]) == 0 or len(parts[2]) == 0: -- raise ValueError("Invalid URL: " + repr(url)) -diff --git a/src/webdav/WebdavRequests.py b/src/webdav/WebdavRequests.py -deleted file mode 100644 -index 79e586a..0000000 ---- a/src/webdav/WebdavRequests.py -+++ /dev/null -@@ -1,205 +0,0 @@ --# pylint: disable-msg=W0511,W0212,E1111 --# --# Copyright 2008 German Aerospace Center (DLR) --# --# Licensed under the Apache License, Version 2.0 (the "License"); --# you may not use this file except in compliance with the License. --# You may obtain a copy of the License at --# --# http://www.apache.org/licenses/LICENSE-2.0 --# --# Unless required by applicable law or agreed to in writing, software --# distributed under the License is distributed on an "AS IS" BASIS, --# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. --# See the License for the specific language governing permissions and --# limitations under the License. -- -- --""" --This module handles WebDav server requests. --""" -- -- --import types --from webdav import Constants --import qp_xml --from tempfile import TemporaryFile -- --from davlib import XML_DOC_HEADER -- --from webdav.NameCheck import validatePropertyName -- -- --__version__ = "$LastChangedRevision$" -- -- --## TODO: create a property list class -- --class XmlNameSpaceMangler(object): -- ''' -- Handles WebDav requests. -- ''' -- -- # restrict instance variables -- __slots__ = ('shortcuts', 'defaultNameSpace') -- -- def __init__(self, nameList, defaultNameSpace = None): -- ''' -- -- @param nameList: -- @param defaultNameSpace: -- ''' -- -- assert isinstance(nameList, types.ListType) or isinstance(nameList, types.TupleType), \ -- "1. argument has wrong type %s" % type(nameList) -- self.shortcuts = {} -- self.defaultNameSpace = defaultNameSpace -- for name in nameList: -- if not isinstance(name, types.TupleType): -- name = (defaultNameSpace, name) -- assert isinstance(name, types.TupleType) and len(name) == 2, \ -- "Name is not a namespace, name tuple: %s" % type(name) -- validatePropertyName(name[1]) -- if name[0] and not self.shortcuts.has_key(name[0]): -- self.shortcuts[name[0]] = 'ns%d' % len(self.shortcuts) -- -- def getNameSpaces(self): -- ''' -- Returns the namespace. -- ''' -- -- result = "" -- for namespace, short in self.shortcuts.items(): -- result += ' xmlns:%s="%s"' % (short, namespace) -- return result -- -- def getUpdateElements(self, valueMap): -- ''' -- -- @param valueMap: -- ''' -- -- elements = "" -- for name in valueMap.keys(): -- fullname = name -- if isinstance(name, types.StringType): -- fullname = (self.defaultNameSpace, name) -- if not fullname[0]: -- tag = fullname[1] -- else: -- tag = self.shortcuts[fullname[0]] + ':' + fullname[1] -- value = valueMap[name] -- if value: -- if isinstance(value, qp_xml._element): -- tmpFile = TemporaryFile('w+') -- value = qp_xml.dump(tmpFile, value) -- tmpFile.flush() -- tmpFile.seek(0) -- tmpFile.readline() -- value = tmpFile.read() -- else: -- value = "<![CDATA[%s]]>" % value -- else: -- value = "" -- elements += "<%s>%s</%s>" % (tag, value, tag) -- return elements -- -- def getNameElements(self, nameList): -- ''' -- -- @param nameList: -- ''' -- -- elements = "" -- for name in nameList: -- if isinstance(name, types.StringType): -- name = (self.defaultNameSpace, name) -- if not name[0]: -- tag = name[1] -- else: -- tag = self.shortcuts[name[0]] + ':' + name[1] -- elements += "<%s />" % tag -- return elements -- -- -- --def createUpdateBody(propertyDict, defaultNameSpace = None): -- ''' -- -- @param propertyDict: -- @param defaultNameSpace: -- ''' -- -- updateTag = 'D:' + Constants.TAG_PROPERTY_UPDATE -- setTag = 'D:' + Constants.TAG_PROPERTY_SET -- propTag = 'D:' + Constants.TAG_PROP -- mangler = XmlNameSpaceMangler(propertyDict.keys(), defaultNameSpace) -- return XML_DOC_HEADER + \ -- '<%s xmlns:D="DAV:"><%s><%s %s>' % (updateTag, setTag, propTag, mangler.getNameSpaces()) + \ -- mangler.getUpdateElements(propertyDict) + \ -- '</%s></%s></%s>' % (propTag, setTag, updateTag) -- -- --def createDeleteBody(nameList, defaultNameSpace = None): -- ''' -- -- @param nameList: -- @param defaultNameSpace: -- ''' -- -- updateTag = 'D:' + Constants.TAG_PROPERTY_UPDATE -- removeTag = 'D:' + Constants.TAG_PROPERTY_REMOVE -- propTag = 'D:' + Constants.TAG_PROP -- mangler = XmlNameSpaceMangler(nameList, defaultNameSpace) -- return XML_DOC_HEADER + \ -- '<%s xmlns:D="DAV:"><%s><%s %s>' % (updateTag, removeTag, propTag, mangler.getNameSpaces()) + \ -- mangler.getNameElements(nameList) + \ -- '</%s></%s></%s>' % (propTag, removeTag, updateTag) -- -- --def createFindBody(nameList, defaultNameSpace = None): -- ''' -- -- @param nameList: -- @param defaultNameSpace: -- ''' -- -- findTag = 'D:' + Constants.TAG_PROPERTY_FIND -- propTag = 'D:' + Constants.TAG_PROP -- mangler = XmlNameSpaceMangler(nameList, defaultNameSpace) -- return XML_DOC_HEADER + \ -- '<%s xmlns:D="DAV:"><%s %s>' % (findTag, propTag, mangler.getNameSpaces()) + \ -- mangler.getNameElements(nameList) + \ -- '</%s></%s>' % (propTag, findTag) -- -- --def createSearchBody(selects, path, conditions, defaultNameSpace = None): -- ''' -- Creates DASL XML body. -- -- @param selects: list of property names to retrieve for the found resources -- @param path: list of conditions -- @param conditions: tree of ConditionTerm instances representing a logical search term -- @param defaultNameSpace: default namespace -- ''' -- -- searchTag = 'D:' + Constants.TAG_SEARCH_REQUEST -- basicTag = 'D:' + Constants.TAG_SEARCH_BASIC -- selectTag = 'D:' + Constants.TAG_SEARCH_SELECT -- fromTag = 'D:' + Constants.TAG_SEARCH_FROM -- scopeTag = 'D:' + Constants.TAG_SEARCH_SCOPE -- whereTag = 'D:' + Constants.TAG_SEARCH_WHERE -- propTag = 'D:' + Constants.TAG_PROP -- hrefTag = 'D:' + Constants.TAG_HREF -- depthTag = 'D:' + Constants.TAG_LOCK_DEPTH -- depthValue = Constants.HTTP_HEADER_DEPTH_INFINITY -- mangler = XmlNameSpaceMangler(selects, defaultNameSpace) -- return XML_DOC_HEADER + \ -- '<%s xmlns:D="DAV:"><%s>' % (searchTag, basicTag) + \ -- '<%s><%s %s>%s</%s></%s>' % (selectTag, propTag, mangler.getNameSpaces(), -- mangler.getNameElements(selects), propTag, selectTag) + \ -- '<%s><%s><%s>%s</%s><%s>%s</%s></%s></%s>' % (fromTag, scopeTag, hrefTag, path, hrefTag, -- depthTag, depthValue, depthTag, scopeTag, fromTag) + \ -- '<%s>%s</%s>' % (whereTag, conditions.toXML(),whereTag) + \ -- '</%s></%s>' % (basicTag, searchTag) -- -\ No newline at end of file -diff --git a/src/webdav/WebdavResponse.py b/src/webdav/WebdavResponse.py -deleted file mode 100644 -index c84943d..0000000 ---- a/src/webdav/WebdavResponse.py -+++ /dev/null -@@ -1,525 +0,0 @@ --# pylint: disable-msg=R0903,W0142,W0221,W0212,W0104,W0511,C0103,R0901 --# --# Copyright 2008 German Aerospace Center (DLR) --# --# Licensed under the Apache License, Version 2.0 (the "License"); --# you may not use this file except in compliance with the License. --# You may obtain a copy of the License at --# --# http://www.apache.org/licenses/LICENSE-2.0 --# --# Unless required by applicable law or agreed to in writing, software --# distributed under the License is distributed on an "AS IS" BASIS, --# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. --# See the License for the specific language governing permissions and --# limitations under the License. -- -- --""" --Handles WebDAV responses. --""" -- -- --from davlib import _parse_status --import qp_xml --from webdav import Constants --import time --import rfc822 --import urllib --# Handling Jython 2.5 bug concerning the date pattern --# conversion in time.strptime --try: -- from java.lang import IllegalArgumentException --except ImportError: -- class IllegalArgumentException(object): -- pass -- -- --__version__ = "$LastChangedRevision$" -- -- --class HttpStatus(object): -- """ -- TBD -- -- @ivar code: -- @type code: -- @ivar reason: -- @type reason: -- @ivar errorCount: -- @type errorCount: int -- """ -- -- def __init__(self, elem): -- """ -- TBD -- -- @param elem: ... -- @type elem: instance of L{Element} -- """ -- self.code, self.reason = _parse_status(elem) -- self.errorCount = (self.code >= Constants.CODE_LOWEST_ERROR) -- def __str__(self): -- return "HTTP status %d: %s" % (self.code, self.reason) -- -- --class MultiStatusResponse(dict): -- """ -- TBD -- -- @ivar status: -- @type status: -- @ivar reason: -- @type reason: -- @ivar errorCount: -- @type errorCount: -- """ -- -- # restrict instance variables -- __slots__ = ('errorCount', 'reason', 'status') -- -- def __init__(self, domroot): -- dict.__init__(self) -- self.errorCount = 0 -- self.reason = None -- self.status = Constants.CODE_MULTISTATUS -- if (domroot.ns != Constants.NS_DAV) or (domroot.name != Constants.TAG_MULTISTATUS): -- raise ResponseFormatError(domroot, 'Invalid response: <DAV:multistatus> expected.') -- self._scan(domroot) -- -- def getCode(self): -- if self.errorCount == 0: -- return Constants.CODE_SUCCEEDED -- if len(self) > self.errorCount: -- return Constants.CODE_MULTISTATUS -- return self.values()[0].code -- -- def getReason(self): -- result = "" -- for response in self.values(): -- if response.code > Constants.CODE_LOWEST_ERROR: -- result += response.reason -- return result -- -- def __str__(self): -- result = "" -- for key, value in self.items(): -- if isinstance(value, PropertyResponse): -- result += "Resource at %s has %d properties and %d errors.\n" % (key, len(value), value.errorCount) -- else: -- result += "Resource at %s returned " % key + str(value) -- return result -- -- def _scan(self, root): -- for child in root.children: -- if child.ns != Constants.NS_DAV: -- continue -- if child.name == Constants.TAG_RESPONSEDESCRIPTION: -- self.reason = child.textof() -- elif child.name == Constants.TAG_RESPONSE: -- self._scanResponse(child) -- ### unknown child element -- -- def _scanResponse(self, elem): -- hrefs = [] -- response = None -- for child in elem.children: -- if child.ns != Constants.NS_DAV: -- continue -- if child.name == Constants.TAG_HREF: -- try: -- href = _unquoteHref(child.textof()) -- except UnicodeDecodeError: -- raise ResponseFormatError(child, "Invalid 'href' data encoding.") -- hrefs.append(href) -- elif child.name == Constants.TAG_STATUS: -- self._scanStatus(child, *hrefs) -- elif child.name == Constants.TAG_PROPERTY_STATUS: -- if not response: -- if len(hrefs) != 1: -- raise ResponseFormatError(child, 'Invalid response: One <DAV:href> expected.') -- response = PropertyResponse() -- self[hrefs[0]] = response -- response._scan(child) -- elif child.name == Constants.TAG_RESPONSEDESCRIPTION: -- for href in hrefs: -- self[href].reasons.append(child.textOf()) -- ### unknown child element -- if response and response.errorCount > 0: -- self.errorCount += 1 -- -- def _scanStatus(self, elem, *hrefs): -- if len(hrefs) == 0: -- raise ResponseFormatError(elem, 'Invalid response: <DAV:href> expected.') -- status = HttpStatus(elem) -- for href in hrefs: -- self[href] = status -- if status.errorCount: -- self.errorCount += 1 -- -- # Instance properties -- code = property(getCode, None, None, "HTTP response code") -- -- -- --class PropertyResponse(dict): -- """ -- TBD -- -- @ivar errors: -- @type errors: list of ... -- @ivar reasons: -- @type reasons: list of ... -- @ivar failedProperties: -- @type failedProperties: dict of ... -- """ -- -- # restrict instance variables -- __slots__ = ('errors', 'reasons', 'failedProperties') -- -- def __init__(self): -- dict.__init__(self) -- self.errors = [] -- self.reasons = [] -- self.failedProperties = {} -- -- def __str__(self): -- result = "" -- for value in self.values(): -- result += value.name + '= ' + value.textof() + '\n' -- result += self.getReason() -- return result -- -- def getCode(self): -- if len(self.errors) == 0: -- return Constants.CODE_SUCCEEDED -- if len(self) > 0: -- return Constants.CODE_MULTISTATUS -- return self.errors[-1].code -- -- def getReason(self): -- result = "" -- if len(self.errors) > 0: -- result = "Failed for: " + repr(self.failedProperties.keys()) + "\n" -- for error in self.errors: -- result += "%s (%d). " % (error.reason, error.code) -- for reason in self.reasons: -- result += "%s. " % reason -- return result -- -- def _scan(self, element): -- status = None -- statusElement = element.find(Constants.TAG_STATUS, Constants.NS_DAV) -- if statusElement: -- status = HttpStatus(statusElement) -- if status.errorCount: -- self.errors.append(status) -- -- propElement = element.find(Constants.TAG_PROP, Constants.NS_DAV) -- if propElement: -- for prop in propElement.children: -- if status.errorCount: -- self.failedProperties[(prop.ns, prop.name)]= status -- else: -- prop.__class__ = Element # bad, bad trick -- self[prop.fullname] = prop -- reasonElement = element.find(Constants.TAG_RESPONSEDESCRIPTION, Constants.NS_DAV) -- if reasonElement: -- self.reasons.append(reasonElement.textOf()) -- -- # Instance properties -- code = property(getCode, None, None, "HTTP response code") -- errorCount = property(lambda self: len(self.errors), None, None, "HTTP response code") -- reason = property(getReason, None, None, "HTTP response code") -- -- -- -- --class LiveProperties(object): -- """ -- This class provides convenient access to the WebDAV 'live' properties of a resource. -- WebDav 'live' properties are defined in RFC 2518, Section 13. -- Each property is converted from string to its natural data type. -- -- @version: $Revision$ -- @author: Roland Betz -- """ -- -- # restrict instance variables -- __slots__ = ('properties') -- -- NAMES = (Constants.PROP_CREATION_DATE, Constants.PROP_DISPLAY_NAME, -- Constants.PROP_CONTENT_LENGTH, Constants.PROP_CONTENT_TYPE, Constants.PROP_ETAG, -- Constants.PROP_LAST_MODIFIED, Constants.PROP_OWNER, -- Constants.PROP_LOCK_DISCOVERY, Constants.PROP_RESOURCE_TYPE, Constants.PROP_SUPPORTED_LOCK ) -- -- def __init__(self, properties=None, propElement=None): -- """ -- Construct <code>StandardProperties</code> from a map of properties containing -- live properties or from a XML 'prop' element containing live properties -- -- @param properties: map as implemented by class L{PropertyResponse} -- @param propElement: an C{Element} value -- """ -- assert isinstance(properties, PropertyResponse) or \ -- isinstance(propElement, qp_xml._element), \ -- "Argument properties has type %s" % str(type(properties)) -- self.properties = {} -- for name, value in properties.items(): -- if name[0] == Constants.NS_DAV and name[1] in self.NAMES: -- self.properties[name[1]] = value -- -- def getContentLanguage(self): -- """ -- Return the language of a resource's textual content or null -- -- @return: string -- """ -- -- result = "" -- if not self.properties.get(Constants.PROP_CONTENT_LANGUAGE, None) is None: -- result = self.properties.get(Constants.PROP_CONTENT_LANGUAGE).textof() -- return result -- -- def getContentLength(self): -- """ -- Returns the length of the resource's content in bytes. -- -- @return: number of bytes -- """ -- -- result = 0 -- if not self.properties.get(Constants.PROP_CONTENT_LENGTH, None) is None: -- result = int(self.properties.get(Constants.PROP_CONTENT_LENGTH).textof()) -- return result -- -- def getContentType(self): -- """ -- Return the resource's content MIME type. -- -- @return: MIME type string -- """ -- -- result = "" -- if not self.properties.get(Constants.PROP_CONTENT_TYPE, None) is None: -- result = self.properties.get(Constants.PROP_CONTENT_TYPE).textof() -- return result -- -- def getCreationDate(self): -- """ -- Return date of creation as time tuple. -- -- @return: time tuple -- @rtype: C{time.struct_time} -- -- @raise ValueError: If string is not in the expected format (ISO 8601). -- """ -- -- datetimeString = "" -- if not self.properties.get(Constants.PROP_CREATION_DATE, None) is None: -- datetimeString = self.properties.get(Constants.PROP_CREATION_DATE).textof() -- -- result = rfc822.parsedate(datetimeString) -- if result is None: -- result = _parseIso8601String(datetimeString) -- -- return time.mktime(result) -- -- def getEntityTag(self): -- """ -- Return a entity tag which is unique for a particular version of a resource. -- Different resources or one resource before and after modification have different etags. -- -- @return: entity tag string -- """ -- -- result = "" -- if not self.properties.get(Constants.PROP_ETAG, None) is None: -- result = self.properties.get(Constants.PROP_ETAG).textof() -- return result -- -- def getDisplayName(self): -- """ -- Returns a resource's display name. -- -- @return: string -- """ -- -- result = "" -- if not self.properties.get(Constants.PROP_DISPLAY_NAME, None) is None: -- result = self.properties.get(Constants.PROP_DISPLAY_NAME).textof() -- return result -- -- def getLastModified(self): -- """ -- Return last modification of resource as time tuple. -- -- @return: Modification date time. -- @rtype: C{time.struct_time} -- -- @raise ValueError: If the date time string is not in the expected format (RFC 822 / ISO 8601). -- """ -- -- datetimeString = None -- if not self.properties.get(Constants.PROP_LAST_MODIFIED, None) is None: -- datetimeString = self.properties.get(Constants.PROP_LAST_MODIFIED).textof() -- result = rfc822.parsedate(datetimeString) -- if result is None: -- result = _parseIso8601String(datetimeString) -- return time.mktime(result) -- -- def getLockDiscovery(self): -- """ -- Return all current lock's applied to a resource or null if it is not locked. -- -- @return: a lockdiscovery DOM element according to RFC 2815 -- """ -- -- xml = self.properties.get(Constants.PROP_LOCK_DISCOVERY) -- return _scanLockDiscovery(xml) -- -- def getResourceType(self): -- """ -- Return a resource's WebDAV type. -- -- @return: 'collection' or 'resource' -- """ -- -- xml = self.properties.get(Constants.PROP_RESOURCE_TYPE) -- if xml and xml.children: -- return xml.children[0].name -- return "resource" -- -- def getSupportedLock(self): -- """ -- Return a DOM element describing all supported lock options for a resource. -- Usually this is shared and exclusive write lock. -- -- @return: supportedlock DOM element according to RFC 2815 -- """ -- -- xml = self.properties.get(Constants.PROP_SUPPORTED_LOCK) -- return xml -- -- def getOwnerAsUrl(self): -- """ -- Return a resource's owner in form of a URL. -- -- @return: string -- """ -- -- xml = self.properties.get(Constants.PROP_OWNER) -- if xml and len(xml.children): -- return xml.children[0].textof() -- return None -- -- def __str__(self): -- result = "" -- result += " Name=" + self.getDisplayName() -- result += "\n Type=" + self.getResourceType() -- result += "\n Length=" + str(self.getContentLength()) -- result += "\n Content Type="+ self.getContentType() -- result += "\n ETag=" + self.getEntityTag() -- result += "\n Created=" + time.strftime("%c GMT", self.getCreationDate()) -- result += "\n Modified=" + time.strftime("%c GMT", self.getLastModified()) -- return result -- -- --def _parseIso8601String(date): -- """ -- Parses the given ISO 8601 string and returns a time tuple. -- The strings should be formatted according to RFC 3339 (see section 5.6). -- But currently there are two exceptions: -- 1. Time offset is limited to "Z". -- 2. Fragments of seconds are ignored. -- """ -- -- if "." in date and "Z" in date: # Contains fragments of second? -- secondFragmentPos = date.rfind(".") -- timeOffsetPos = date.rfind("Z") -- date = date[:secondFragmentPos] + date[timeOffsetPos:] -- try: -- timeTuple = time.strptime(date, Constants.DATE_FORMAT_ISO8601) -- except IllegalArgumentException: # Handling Jython 2.5 bug concerning the date pattern accordingly -- import _strptime # Using the Jython fall back solution directly -- timeTuple = _strptime.strptime(date, Constants.DATE_FORMAT_ISO8601) -- return timeTuple -- -- --class ResponseFormatError(IOError): -- """ -- An instance of this class is raised when the web server returned a webdav -- reply which does not adhere to the standard and cannot be recognized. -- """ -- def __init__(self, element, message= None): -- IOError.__init__(self, "ResponseFormatError at element %s: %s" % (element.name, message)) -- self.element = element -- self.message = message -- -- --class Element(qp_xml._element): -- """ -- This class improves the DOM interface (i.e. element interface) provided by the qp_xml module -- TODO: substitute qp_xml by 'real' implementation. e.g. domlette -- """ -- def __init__(self, namespace, name, cdata=''): -- qp_xml._element.__init__(self, ns=namespace, name=name, lang=None, parent=None, -- children=[], ns_scope={}, attrs={}, -- first_cdata=cdata, following_cdata='') -- -- def __str__(self): -- return self.textof() -- -- def __getattr__(self, name): -- if (name == 'fullname'): -- return (self.__dict__['ns'], self.__dict__['name']) -- raise AttributeError, name -- -- def add(self, child): -- self.children.append(child) -- return child -- --def _scanLockDiscovery(root): -- assert root.name == Constants.PROP_LOCK_DISCOVERY, "Invalid lock discovery XML element" -- active = root.find(Constants.TAG_ACTIVE_LOCK, Constants.NS_DAV) -- if active: -- return _scanActivelock(active) -- return None -- --def _scanActivelock(root): -- assert root.name == Constants.TAG_ACTIVE_LOCK, "Invalid active lock XML element" -- token = _scanOrError(root, Constants.TAG_LOCK_TOKEN) -- value = _scanOrError(token, Constants.TAG_HREF) -- owner = _scanOwner(root) -- depth = _scanOrError(root, Constants.TAG_LOCK_DEPTH) -- return (value.textof(), owner, depth.textof()) -- --def _scanOwner(root): -- owner = root.find(Constants.TAG_LOCK_OWNER, Constants.NS_DAV) -- if owner: -- href = owner.find(Constants.TAG_HREF, Constants.NS_DAV) -- if href: -- return href.textof() -- return owner.textof() -- return None -- --def _scanOrError(elem, childName): -- child = elem.find(childName, Constants.NS_DAV) -- if not child: -- raise ResponseFormatError(elem, "Invalid response: <"+childName+"> expected") -- return child -- -- --def _unquoteHref(href): -- #print "*** Response HREF=", repr(href) -- if type(href) == type(u""): -- try: -- href = href.encode('ascii') -- except UnicodeError: # URL contains unescaped non-ascii character -- # handle bug in Tamino webdav server -- return urllib.unquote(href) -- href = urllib.unquote(href) -- if Constants.CONFIG_UNICODE_URL: -- return unicode(href, 'utf-8') -- else: -- return unicode(href, 'latin-1') -diff --git a/src/webdav/__init__.py b/src/webdav/__init__.py -deleted file mode 100644 -index 3e46609..0000000 ---- a/src/webdav/__init__.py -+++ /dev/null -@@ -1,16 +0,0 @@ --# Copyright 2008 German Aerospace Center (DLR) --# --# Licensed under the Apache License, Version 2.0 (the "License"); --# you may not use this file except in compliance with the License. --# You may obtain a copy of the License at --# --# http://www.apache.org/licenses/LICENSE-2.0 --# --# Unless required by applicable law or agreed to in writing, software --# distributed under the License is distributed on an "AS IS" BASIS, --# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. --# See the License for the specific language governing permissions and --# limitations under the License. -- -- --__version__ = "$LastChangedRevision$" -diff --git a/src/webdav/acp/Ace.py b/src/webdav/acp/Ace.py -deleted file mode 100644 -index 8321d41..0000000 ---- a/src/webdav/acp/Ace.py -+++ /dev/null -@@ -1,293 +0,0 @@ --# Copyright 2008 German Aerospace Center (DLR) --# --# Licensed under the Apache License, Version 2.0 (the "License"); --# you may not use this file except in compliance with the License. --# You may obtain a copy of the License at --# --# http://www.apache.org/licenses/LICENSE-2.0 --# --# Unless required by applicable law or agreed to in writing, software --# distributed under the License is distributed on an "AS IS" BASIS, --# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. --# See the License for the specific language governing permissions and --# limitations under the License. -- -- --""" --ACE object handling according to WebDAV ACP specification. --""" -- -- --from webdav.acp.Principal import Principal --from webdav.acp.GrantDeny import GrantDeny --from webdav import Constants --from webdav.Connection import WebdavError -- -- --__version__ = "$LastChangedRevision$" -- -- --class ACE(object): -- """ -- This class provides functionality for handling ACEs -- -- @ivar principal: A principal (user or group) -- @type principal: L{Principal} object -- @ivar invert: Flag indicating whether ACE should invert the principal. -- @type invert: C{bool} -- @ivar grantDenies: Grant or deny clauses for privileges -- @type grantDenies: C{list} of L{GrantDeny} objects -- @ivar protected: Flag indicating whether ACE is protected. -- @type protected: C{bool} -- @ivar inherited: URL indicating the source from where the ACE is inherited. -- @type inherited: C{string} -- """ -- -- # restrict instance variables -- __slots__ = ('principal', 'invert', 'grantDenies', 'protected', 'inherited') -- -- def __init__(self, domroot=None, principal=None, grantDenies=None): -- """ -- Constructor should be called with either no parameters (create blank ACE), -- one parameter (a DOM tree or principal), or two parameters (principal and -- sequence of GrantDenies). -- -- @param domroot: A DOM tree (default: None). -- @type domroot: L{webdav.WebdavResponse.Element} object -- @param principal: A principal (user or group), (default: None). -- @type principal: L{Principal} object -- @param grantDenies: Grant and deny clauses for privileges (default: None). -- @type grantDenies: sequence of L{GrantDeny} objects -- -- @raise WebdavError: When non-valid parameters are passed a L{WebdavError} is raised. -- """ -- self.principal = Principal() -- self.protected = None -- self.inherited = None -- self.invert = None -- self.grantDenies = [] -- -- if domroot: -- self.principal = Principal(domroot=domroot.find(Constants.TAG_PRINCIPAL, Constants.NS_DAV)) -- self.inherited = domroot.find(Constants.TAG_INHERITED, Constants.NS_DAV) -- if self.inherited: -- self.inherited = self.inherited.children[0].textof() -- if domroot.find(Constants.TAG_PROTECTED, Constants.NS_DAV): -- self.protected = 1 -- for child in domroot.children: -- if child.ns == Constants.NS_DAV \ -- and (child.name == Constants.TAG_GRANT or child.name == Constants.TAG_DENY): -- self.grantDenies.append(GrantDeny(domroot=child)) -- elif isinstance(principal, Principal): -- newPrincipal = Principal() -- newPrincipal.copy(principal) -- self.principal = newPrincipal -- if (isinstance(grantDenies, list) or isinstance(grantDenies, tuple)): -- self.addGrantDenies(grantDenies) -- elif domroot == None and grantDenies == None: -- # no param ==> blank ACE -- pass -- else: -- # This shouldn't happen, someone screwed up with the params ... -- raise WebdavError('non-valid parameters handed to ACE constructor') -- -- def __cmp__(self, other): -- if not isinstance(other, ACE): -- return 1 -- if self.principal == other.principal \ -- and self.invert == other.invert \ -- and self.protected == other.protected \ -- and self.inherited == other.inherited: -- equal = 1 -- for grantDeny in self.grantDenies: -- inList = 0 -- for otherGrantDeny in other.grantDenies: -- if grantDeny == otherGrantDeny: -- inList = 1 -- if inList == 0: -- equal = 0 -- return not equal -- else: -- return 1 -- -- def __repr__(self): -- repr = '<class ACE: ' -- if self.invert: -- repr += 'inverted principal, ' % (self.invert) -- if self.principal: -- repr += 'principal: %s, ' % (self.principal) -- if self.protected: -- repr += 'protected, ' -- if self.inherited: -- repr += 'inherited href: %s, ' % (self.inherited) -- first = 1 -- repr += 'grantDenies: [' -- for grantDeny in self.grantDenies: -- if first: -- repr += '%s' % grantDeny -- first = 0 -- else: -- repr += ', %s' % grantDeny -- return '%s]>' % (repr) -- -- def copy(self, other): -- '''Copy an ACE object. -- -- @param other: Another ACE to copy. -- @type other: L{ACE} object -- -- @raise WebdavError: When an object that is not an L{ACE} is passed -- a L{WebdavError} is raised. -- ''' -- if not isinstance(other, ACE): -- raise WebdavError('Non-ACE object passed to copy method: %s.' % other.__class__) -- self.invert = other.invert -- self.protected = other.protected -- self.inherited = other.inherited -- self.principal = Principal() -- if other.principal: -- self.principal.copy(other.principal) -- if other.grantDenies: -- self.addGrantDenies(other.grantDenies) -- -- def isValid(self): -- """ -- Returns true/false (1/0) whether necessarry props -- principal and grantDenies are set and whether the ACE contains one -- grant or deny clauses. -- -- @return: Validity of ACE. -- @rtype: C{bool} -- """ -- return self.principal and len(self.grantDenies) == 1 -- -- def isGrant(self): -- ''' -- Returns true/false (1/0) if ACE contains only grant clauses. -- -- @return: Value whether the ACE is of grant type. -- @rtype: C{bool} -- ''' -- if self.isMixed() or len(self.grantDenies) < 1: -- return 0 -- else: -- return self.grantDenies[0].isGrant() -- -- def isDeny(self): -- ''' -- Returns true/false (1/0) if ACE contains only deny clauses. -- -- @return: Value whether the ACE is of deny type. -- @rtype: C{bool} -- ''' -- if self.isMixed() or len(self.grantDenies) < 1: -- return 0 -- else: -- return self.grantDenies[0].isDeny() -- -- def isMixed(self): -- ''' -- Returns true/false (1/0) if ACE contains both types (grant and deny) of clauses. -- -- @return: Value whether the ACE is of mixed (grant and deny) type. -- @rtype: C{bool} -- ''' -- mixed = 0 -- if len(self.grantDenies): -- first = self.grantDenies[0].grantDeny -- for grantDeny in self.grantDenies: -- if grantDeny.grantDeny != first: -- mixed = 1 -- return mixed -- -- def toXML(self, defaultNameSpace=None): -- """ -- Returns ACE content as a string of valid XML as described in WebDAV ACP. -- -- @param defaultNameSpace: Name space (default: None). -- @type defaultNameSpace: C(string) -- """ -- assert self.isValid(), "ACE is not initialized or does not contain valid content!" -- -- ACE = 'D:' + Constants.TAG_ACE -- res = self.principal.toXML(self.invert) -- for grantDeny in self.grantDenies: -- res += grantDeny.toXML() -- if self.protected: -- res += '<D:protected/>' -- if self.inherited: -- res += '<D:inherited><D:href>%s</D:href></D:inherited>' % (self.inherited) -- return '<%s>%s</%s>' % (ACE, res, ACE) -- -- def setPrincipal(self, principal): -- ''' -- Sets the passed principal on the ACE. -- -- @param principal: A principal. -- @type principal: L{Principal} object -- ''' -- self.principal = Principal() -- self.principal.copy(principal) -- -- def setInherited(self, href): -- ''' -- Sets the passed URL on the ACE to denote from where it is inherited. -- -- @param href: A URL. -- @type href: C{string} -- ''' -- self.inherited = href -- -- def addGrantDeny(self, grantDeny): -- ''' -- Adds the passed GrantDeny object to list if it's not in it, yet. -- -- @param grantDeny: A grant or deny clause. -- @type grantDeny: L{GrantDeny} object -- ''' -- # only add it if it's not in the list, yet ... -- inList = 0 -- for element in self.grantDenies: -- if element == grantDeny: -- inList = 1 -- if not inList: -- newGrantDeny = GrantDeny() -- newGrantDeny.copy(grantDeny) -- self.grantDenies.append(newGrantDeny) -- -- def addGrantDenies(self, grantDenies): -- '''Adds the list of passed grant/deny objects to list. -- -- @param grantDenies: Grant or deny clauses. -- @type grantDenies: sequence of L{GrantDeny} objects -- ''' -- map(lambda grantDeny: self.addGrantDeny(grantDeny), grantDenies) -- -- def delGrantDeny(self, grantDeny): -- '''Deletes the passed GrantDeny object from list. -- -- @param grantDeny: A grant or deny clause. -- @type grantDeny: L{GrantDeny} object -- -- @raise WebdavError: A L{WebdavError} is raised if the clause to be -- deleted is not present. -- ''' -- # only add it if it's not in the list, yet ... -- count = 0 -- index = 0 -- for element in self.grantDenies: -- count += 1 -- if element == grantDeny: -- index = count -- if index: -- self.grantDenies.pop(index - 1) -- else: -- raise WebdavError('GrantDeny to be deleted not in list: %s.' % grantDeny) -- -- def delGrantDenies(self, grantDenies): -- '''Deletes the list of passed grant/deny objects from list. -- -- @param grantDenies: Grant or deny clauses. -- @type grantDenies: sequence of L{GrantDeny} objects -- ''' -- map(lambda grantDeny: self.delGrantDeny(grantDeny), grantDenies) -diff --git a/src/webdav/acp/AceHandler.py b/src/webdav/acp/AceHandler.py -deleted file mode 100644 -index e07b74d..0000000 ---- a/src/webdav/acp/AceHandler.py -+++ /dev/null -@@ -1,182 +0,0 @@ --# Copyright 2008 German Aerospace Center (DLR) --# --# Licensed under the Apache License, Version 2.0 (the "License"); --# you may not use this file except in compliance with the License. --# You may obtain a copy of the License at --# --# http://www.apache.org/licenses/LICENSE-2.0 --# --# Unless required by applicable law or agreed to in writing, software --# distributed under the License is distributed on an "AS IS" BASIS, --# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. --# See the License for the specific language governing permissions and --# limitations under the License. -- -- --""" --Handling of WebDAV Access Protocol Extensions and ACL preparation for UI. --""" -- -- --from webdav import Constants --from webdav.WebdavClient import ResourceStorer --from webdav.Connection import WebdavError -- -- --__version__ = "$LastChangedRevision$" -- -- --def extractSupportedPrivilegeSet(userPrivileges): -- """ -- Returns a dictionary of supported privileges. -- -- @param userPrivileges: A DOM tree. -- @type userPrivileges: L{webdav.WebdavResponse.Element} object -- -- @raise WebdavError: When unknown elements appear in the -- C{DAV:supported-privilege} appear a L{WebdavError} is raised. -- -- @return: A dictionary with privilege names as keys and privilege descriptions as values. -- @rtype: C{dictionary} -- """ -- result = {} -- for element in userPrivileges.children: -- if element.name == Constants.TAG_SUPPORTED_PRIVILEGE: -- privName = '' -- privDescription = '' -- for privilege in element.children: -- if privilege.name == Constants.TAG_PRIVILEGE: -- privName = privilege.children[0].name -- elif privilege.name == Constants.TAG_DESCRIPTION: -- privDescription = privilege.textof() -- else: -- raise WebdavError('Unknown element in DAV:supported-privilege: ' + privilege.name) -- -- if privName and privDescription: -- result[privName] = privDescription -- privName = '' -- privDescription = '' -- else: -- raise WebdavError('Invalid element tag in DAV:supported-privilege-set: ' + element.name) -- return result -- -- --def _insertAclDisplaynames(acl): -- """ -- Modifies the ACL by adding the human readable names -- (DAV:displayname property) of each principal found in an ACL. -- -- This should be done with the REPORT method, but it is not supported by -- Jacarta Slide, yet. (As of Aug. 1, 2003 in CVS repository) -- -- So we are going to do it differently by foot the harder way ... -- -- @param acl: An ACL object for which the displaynames should be retrieved. -- @type acl: L{ACL} object -- """ -- ## This is redundant code to be still kept for the REPORT method way of doing it ... -- ## property = '''<D:prop><D:displayname/></D:prop>''' -- ## return self.getReport(REPORT_ACL_PRINCIPAL_PROP_SET, property) -- for ace in acl.aces: -- if not ace.principal.property: -- principalConnection = \ -- ResourceStorer(ace.principal.principalURL) -- ace.principal.displayname = \ -- principalConnection.readProperty(Constants.NS_DAV, Constants.PROP_DISPLAY_NAME) -- -- --def prepareAcls(acls): -- """ -- Returns all ACLs describing the behaviour of the resource. The information -- in the ACL is modified to contain all information needed to display in the UI. -- -- @param acls: ACL objects. -- @type acls: C{list} of L{ACL} objects -- -- @return: (non-valid) ACLs that contain both grant and deny clauses in an ACE. -- Displaynames are added to the Principals where needed. -- @rtype: C{list} of L{ACL} objects -- """ -- for acl in acls.keys(): -- acls[acl] = acls[acl].joinGrantDeny() -- _insertAclDisplaynames(acls[acl]) -- return acls -- -- --def prepareAcl(acl): -- """ -- Returns an ACL describing the behaviour of the resource. The information -- in the ACL is modified to contain all information needed to display in the UI. -- -- @param acl: An ACL object. -- @type acl: L{ACL} object -- -- @return: A (non-valid) ACL that contains both grant and deny clauses in an ACE. -- Displaynames are added to the Principals where needed. -- @rtype: L{ACL} object -- """ -- acl = acl.joinGrantDeny() -- _insertAclDisplaynames(acl) -- return acl -- -- --def refineAclForSet(acl): -- """ -- Sets the ACL composed from the UI on the WebDAV server. For that purpose the -- ACL object gets refined first to form a well accepted ACL to be set by the -- ACL WebDAV method. -- -- @param acl: An ACL object to be refined. -- @type acl: L{ACL} object -- -- @return: A valid ACL that contains only grant or deny clauses in an ACE. -- Inherited and protected ACEs are stripped out. -- @rtype: L{ACL} object -- """ -- acl = acl.splitGrantDeny() -- acl = acl.stripAces() -- return acl -- -- --##~ unsupported or unfinished methods: --##~ --##~ def report(self, report, request=None, lockToken=None): --##~ """ --##~ This method implements the WebDAV ACP method: REPORT for given report --##~ types. --##~ --##~ Parameters: --##~ --##~ 'report' -- Report type as a string. --##~ --##~ 'request' -- XML content of the request for the report (defaults to None). --##~ --##~ 'lockToken' -- Lock token to be set (defaults to None). --##~ """ --##~ raise WebdavError('Reports are not supported by our Jacarta Slide, yet (as of Aug. 1, 2003 in CVS).') --##~ --##~ headers = createCondition(lockToken) --##~ headers['Content-Type'] = XML_CONTENT_TYPE --##~ body = '<D:%s xmlns:D="DAV:">%s</D:%s>' % (report, request, report) --##~ #print "Body: ", body --##~ response = self.connection._request('REPORT', self.path, body, headers) --##~ return response --##~ ## TODO: parse DAV:error response --##~ --##~ --##~ def getAllAcls(self): --##~ """ --##~ Returns a dictionary of ACL resources with respective ACL objects --##~ that apply to the given resource. --##~ --##~ ### This method needs to be extended for inherited ACLs when Tamino --##~ support tells me (Guy) how to get to them. --##~ """ --##~ acls = {self.path: self.getAcl()} --##~ for ace in acls[self.path].aces: --##~ if ace.inherited: --##~ if not ace.inherited in acls: --##~ acls[ace.inherited] = self.getAcl() --##~ --##~ # append some more stuff here to acls for possible inherited ACLs --##~ return acls -diff --git a/src/webdav/acp/Acl.py b/src/webdav/acp/Acl.py -deleted file mode 100644 -index 8f2b36f..0000000 ---- a/src/webdav/acp/Acl.py -+++ /dev/null -@@ -1,311 +0,0 @@ --# pylint: disable-msg=W0622 --# --# Copyright 2008 German Aerospace Center (DLR) --# --# Licensed under the Apache License, Version 2.0 (the "License"); --# you may not use this file except in compliance with the License. --# You may obtain a copy of the License at --# --# http://www.apache.org/licenses/LICENSE-2.0 --# --# Unless required by applicable law or agreed to in writing, software --# distributed under the License is distributed on an "AS IS" BASIS, --# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. --# See the License for the specific language governing permissions and --# limitations under the License. -- -- -- --""" --ACL object handling according to WebDAV ACP specification. --""" -- -- --from webdav.acp.Ace import ACE --from webdav import Constants --from webdav.Connection import WebdavError --from webdav.davlib import XML_DOC_HEADER -- -- --__version__ = "$LastChangedRevision$" -- -- --class ACL(object): -- """ -- This class provides access to Access Control List funcionality -- as specified in the WebDAV ACP. -- -- @ivar aces: ACEs in ACL -- @type aces: C{list} of L{ACE} objects -- @ivar withInherited: Flag indicating whether ACL contains inherited ACEs. -- @type withInherited: C{bool} -- """ -- -- # restrict instance variables -- __slots__ = ('aces', 'withInherited') -- -- def __init__(self, domroot=None, aces=None): -- """ -- Constructor should be called with either no parameters (create blank ACE), -- or one parameter (a DOM tree or ACE list). -- -- @param domroot: A DOM tree (default: None). -- @type domroot: L{webdav.WebdavResponse.Element} object -- @param aces: ACE objects (default: None) -- @type aces: C{list} of L{ACE} objects -- -- @raise WebdavError: When non-valid parameters are passed a L{WebdavError} is raised. -- """ -- self.withInherited = None -- self.aces = [] -- -- if domroot: -- for child in domroot.children: -- if child.name == Constants.TAG_ACE and child.ns == Constants.NS_DAV: -- self.addAce(ACE(child)) -- else: -- # This shouldn't happen, someone screwed up with the params ... -- raise WebdavError('Non-ACE tag handed to ACL constructor: ' + child.ns + child.name) -- elif isinstance(aces, list) or isinstance(aces, tuple): -- self.addAces(aces) -- elif domroot == None and aces == None: -- # no param ==> blank object -- pass -- else: -- # This shouldn't happen, someone screwed up with the params ... -- raise WebdavError('non-valid parameters handed to ACL constructor') -- -- def __cmp__(self, other): -- if not isinstance(other, ACL): -- return 1 -- if self.withInherited == other.withInherited: -- equal = 1 -- for ace in self.aces: -- inList = 0 -- for otherAce in other.aces: -- if ace == otherAce: -- inList = 1 -- if inList == 0: -- equal = 0 -- return not equal -- else: -- return 1 -- -- def __repr__(self): -- repr = '<class ACL: ' -- if self.withInherited: -- repr += 'with inherited, ' -- first = 1 -- repr += 'aces: [' -- for ace in self.aces: -- if first: -- repr += '%s' % ace -- first = 0 -- else: -- repr += ', %s' % ace -- return '%s]>' % (repr) -- -- def copy(self, other): -- '''Copy an ACL object. -- -- @param other: Another ACL to copy. -- @type other: L{ACL} object -- -- @raise WebdavError: When an object that is not an L{ACL} is passed -- a L{WebdavError} is raised. -- ''' -- if not isinstance(other, ACL): -- raise WebdavError('Non-ACL object passed to copy method: %s' % other.__class__) -- self.withInherited = other.withInherited -- if other.aces: -- self.addAces(other.aces) -- -- def toXML(self): -- """ -- Returns ACL content as a string of valid XML as described in WebDAV ACP. -- """ -- aclTag = 'D:' + Constants.TAG_ACL -- return XML_DOC_HEADER +\ -- '<' + aclTag + ' xmlns:D="DAV:">' + reduce(lambda xml, ace: xml + ace.toXML() + '\n', [''] + self.aces) +\ -- '</' + aclTag + '>' -- -- def addAce(self, ace): -- ''' -- Adds the passed ACE object to list if it's not in it, yet. -- -- @param ace: An ACE. -- @type ace: L{ACE} object -- ''' -- newAce = ACE() -- newAce.copy(ace) -- # only add it if it's not in the list, yet ... -- inList = 0 -- for element in self.aces: -- if element == ace: -- inList = 1 -- if not inList: -- self.aces.append(newAce) -- -- def addAces(self, aces): -- '''Adds the list of passed ACE objects to list. -- -- @param aces: ACEs -- @type aces: sequence of L{ACE} objects -- ''' -- for ace in aces: -- self.addAce(ace) -- -- def delAce(self, ace): -- '''Deletes the passed ACE object from list. -- -- @param ace: An ACE. -- @type ace: L{ACE} object -- -- @raise WebdavError: When the ACE to be deleted is not within the ACL -- a L{WebdavError} is raised. -- ''' -- # find where it is and delete it ... -- count = 0 -- index = 0 -- for element in self.aces: -- count += 1 -- if element == ace: -- index = count -- if index: -- self.aces.pop(index - 1) -- else: -- raise WebdavError('ACE to be deleted not in list: %s.' % ace) -- -- def delAces(self, aces): -- '''Deletes the list of passed ACE objects from list. -- -- @param aces: ACEs -- @type aces: sequence of L{ACE} objects -- ''' -- for ace in aces: -- self.delAce(ace) -- -- def delPrincipalsAces(self, principal): -- """ -- Deletes all ACEs in ACL by given principal. -- -- @param principal: A principal. -- @type principal: L{Principal} object -- """ -- # find where it is and delete it ... -- index = 0 -- while index < len(self.aces): -- if self.aces[index].principal.principalURL == principal.principalURL: -- self.aces.pop(index) -- else: -- index += 1 -- -- def joinGrantDeny(self): -- """ -- Returns a "refined" ACL of the ACL for ease of use in the UI. -- The purpose is to post the user an ACE that can contain both, granted -- and denied, privileges. So possible pairs of grant and deny ACEs are joined -- to return them in one ACE. This resulting ACE then of course IS NOT valid -- for setting ACLs anymore. They will have to be reconverted to yield valid -- ACLs for the ACL method. -- -- @return: A (non-valid) ACL that contains both grant and deny clauses in an ACE. -- @rtype: L{ACL} object -- """ -- joinedAces = {} -- for ace in self.aces: -- if not ace.principal.principalURL is None: -- principalKey = ace.principal.principalURL -- elif not ace.principal.property is None: -- principalKey = ace.principal.property -- else: -- principalKey = None -- if ace.inherited: -- principalKey = ace.inherited + ":" + principalKey -- if principalKey in joinedAces: -- joinedAces[principalKey].addGrantDenies(ace.grantDenies) -- else: -- joinedAces[principalKey] = ACE() -- joinedAces[principalKey].copy(ace) -- newAcl = ACL() -- newAcl.addAces(joinedAces.values()) -- return newAcl -- -- def splitGrantDeny(self): -- """ -- Returns a "refined" ACL of the ACL for ease of use in the UI. -- The purpose is to post the user an ACE that can contain both, granted -- and denied, privileges. So possible joined grant and deny clauses in ACEs -- splitted to return them in separate ACEs. This resulting ACE then is valid -- for setting ACLs again. This method is to be seen in conjunction with the -- method joinGrantDeny as it reverts its effect. -- -- @return: A valid ACL that contains only ACEs with either grant or deny clauses. -- @rtype: L{ACL} object -- """ -- acesGrant = {} -- acesDeny = {} -- for ace in self.aces: -- for grantDeny in ace.grantDenies: -- if grantDeny.isGrant(): -- if ace.principal.principalURL in acesGrant: -- ace.addGrantDeny(grantDeny) -- else: -- acesGrant[ace.principal.principalURL] = ACE() -- acesGrant[ace.principal.principalURL].copy(ace) -- acesGrant[ace.principal.principalURL].grantDenies = [] -- acesGrant[ace.principal.principalURL].addGrantDeny(grantDeny) -- else: -- if ace.principal.principalURL in acesDeny: -- ace.addGrantDeny(grantDeny) -- else: -- acesDeny[ace.principal.principalURL] = ACE() -- acesDeny[ace.principal.principalURL].copy(ace) -- acesDeny[ace.principal.principalURL].grantDenies = [] -- acesDeny[ace.principal.principalURL].addGrantDeny(grantDeny) -- newAcl = ACL() -- newAcl.addAces(acesGrant.values()) -- newAcl.addAces(acesDeny.values()) -- return newAcl -- -- def isValid(self): -- """ -- Returns true (1) if all contained ACE objects are valid, -- otherwise false (0) is returned. -- -- @return: Validity of ACL. -- @rtype: C{bool} -- """ -- valid = 1 -- if len(self.aces): -- for ace in self.aces: -- if not ace.isValid(): -- valid = 0 -- return valid -- -- def stripAces(self, inherited=True, protected=True): -- """ -- Returns an ACL object with all ACEs stripped that are inherited -- and/or protected. -- -- @param inherited: Flag to indicate whether inherited ACEs should -- be stripped (default: True). -- @type inherited: C{bool} -- @param protected: Flag to indicate whether protected ACEs should -- be stripped (default: True). -- @type protected: C{bool} -- -- @return: An ACL without the stripped ACEs. -- @rtype: L{ACL} object -- """ -- newAcl = ACL() -- if len(self.aces): -- for ace in self.aces: -- keep = 1 -- if inherited and ace.inherited: -- keep = 0 -- elif protected and ace.protected: -- keep = 0 -- if keep: -- newAcl.addAce(ace) -- return newAcl -diff --git a/src/webdav/acp/GrantDeny.py b/src/webdav/acp/GrantDeny.py -deleted file mode 100644 -index 52c9b93..0000000 ---- a/src/webdav/acp/GrantDeny.py -+++ /dev/null -@@ -1,241 +0,0 @@ --# Copyright 2008 German Aerospace Center (DLR) --# --# Licensed under the Apache License, Version 2.0 (the "License"); --# you may not use this file except in compliance with the License. --# You may obtain a copy of the License at --# --# http://www.apache.org/licenses/LICENSE-2.0 --# --# Unless required by applicable law or agreed to in writing, software --# distributed under the License is distributed on an "AS IS" BASIS, --# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. --# See the License for the specific language governing permissions and --# limitations under the License. -- -- --""" --Handling of grant and deny clauses in ACEs according to WebDAV ACP specification. --""" -- -- --from webdav.acp.Privilege import Privilege --from webdav import Constants --from webdav.Connection import WebdavError -- -- --__version__ = "$LastChangedRevision$" -- -- --class GrantDeny(object): -- """ -- This class provides functionality for handling -- grant and deny clauses in ACEs. -- -- @ivar grantDeny: Flag indicating whether clause grants or denies. -- @type grantDeny: C{bool} -- @ivar privileges: Privileges to be granted or denied. -- @type privileges: C{list} of L{Privilege} objects -- """ -- -- def __init__(self, domroot=None): -- """ -- Constructor should be called with either no parameters -- (create blank GrantDeny), or one parameter (a DOM tree). -- -- @param domroot: A DOM tree (default: None). -- @type domroot: L{webdav.WebdavResponse.Element} object -- -- @raise WebdavError: When non-valid parameters are passed a L{WebdavError} is raised. -- """ -- self.grantDeny = 0 # 0: deny, 1: grant -- self.privileges = [] -- -- if domroot: -- self.grantDeny = (domroot.name == Constants.TAG_GRANT) -- for child in domroot.children: -- if child.name == Constants.TAG_PRIVILEGE and child.ns == Constants.NS_DAV: -- self.privileges.append(Privilege(domroot=child)) -- else: -- # This shouldn't happen, someone screwed up with the params ... -- raise WebdavError('Non-privilege tag handed to GrantDeny constructor: %s' \ -- % child.name) -- elif domroot == None: -- # no param ==> blank object -- pass -- else: -- # This shouldn't happen, someone screwed up with the params ... -- raise WebdavError('Non-valid parameters handed to GrantDeny constructor.') -- -- def __cmp__(self, other): -- """ Compares two GrantDeny instances. """ -- if not isinstance(other, GrantDeny): -- return 1 -- if self.grantDeny == other.grantDeny: -- equal = 1 -- for priv in self.privileges: -- inList = 0 -- for otherPriv in other.privileges: -- if priv == otherPriv: -- inList = 1 -- if inList == 0: -- equal = 0 -- return not equal -- else: -- return 1 -- -- def __repr__(self): -- """ Returns the representation of an instance. """ -- representation = '<class GrantDeny: ' -- if self.grantDeny: -- representation += 'grant privileges: [' -- else: -- representation += 'deny privileges: [' -- first = 1 -- for priv in self.privileges: -- if first: -- representation += '%s' % priv -- first = 0 -- else: -- representation += ', %s' % priv -- return '%s]>' % (representation) -- -- def copy(self, other): -- """ -- Copy a GrantDeny object. -- -- @param other: Another grant or deny clause to copy. -- @type other: L{GrantDeny} object -- -- @raise WebdavError: When an object that is not an L{GrantDeny} is passed -- a L{WebdavError} is raised. -- """ -- if not isinstance(other, GrantDeny): -- raise WebdavError('Non-GrantDeny object passed to copy method: %s' \ -- % other) -- self.grantDeny = other.grantDeny -- if other.privileges: -- self.addPrivileges(other.privileges) -- -- def isGrant(self): -- """ -- Returns whether the set of privileges is of type "grant" -- indicating true or false. -- -- @return: Value whether the clause is of grant type. -- @rtype: C{bool} -- """ -- return self.grantDeny -- -- def isDeny(self): -- """ -- Returns whether the set of privileges is of type "deny" -- indicating true or false. -- -- @return: Value whether the clause is of deny type. -- @rtype: C{bool} -- """ -- return not self.grantDeny -- -- def setGrantDeny(self, grantDeny): -- """ -- Sets the set of privileges to given value for grantDeny. -- -- @param grantDeny: Grant/deny value for clause (grant: True/1, deny: False/0). -- @type grantDeny: C{bool} -- """ -- if grantDeny == 0 or grantDeny == 1: -- self.grantDeny = grantDeny -- -- def setGrant(self): -- """ Sets the set of privileges to type "grant". """ -- self.grantDeny = 1 -- -- def setDeny(self): -- """ Sets the set of privileges to type "deny". """ -- self.grantDeny = 0 -- -- def isAll(self): -- """ -- Checks whether the privileges contained are equal -- to aggregate DAV:all privilege. -- -- @return: Value whether all un-aggregated privileges are present. -- @rtype: C{bool} -- """ -- -- if len(self.privileges) == 1 and self.privileges[0].name == Constants.TAG_ALL: -- return 1 -- return 0 -- -- def addPrivilege(self, privilege): -- """ -- Adds the passed privilege to list if it's not in it, yet. -- -- @param privilege: A privilege. -- @type privilege: L{Privilege} object -- """ -- inList = False -- for priv in self.privileges: -- if priv == privilege: -- inList = True -- if not inList: -- newPrivilege = Privilege() -- newPrivilege.copy(privilege) -- self.privileges.append(newPrivilege) -- -- def addPrivileges(self, privileges): -- """ -- Adds the list of passed privileges to list. -- -- @param privileges: Several privileges. -- @type privileges: sequence of L{Privilege} objects -- """ -- for priv in privileges: -- self.addPrivilege(priv) -- -- def delPrivilege(self, privilege): -- """ -- Deletes the passed privilege from list if it's in it. -- -- @param privilege: A privilege. -- @type privilege: L{Privilege} object -- -- @raise WebdavError: A L{WebdavError} is raised if the privilege to be -- deleted is not present. -- """ -- count = 0 -- index = 0 -- for priv in self.privileges: -- count += 1 -- if priv == privilege: -- index = count -- if index: -- self.privileges.pop(index - 1) -- else: -- raise WebdavError('Privilege to be deleted not in list: %s' % privilege) -- -- def delPrivileges(self, privileges): -- """ -- Deletes the list of passed privileges from list. -- -- @param privileges: Several privileges. -- @type privileges: sequence of L{Privilege} objects -- """ -- for priv in privileges: -- self.delPrivilege(priv) -- -- def toXML(self): -- """ -- Returns string of GrantDeny content to valid XML as described in WebDAV ACP. -- """ -- assert self.privileges, "GrantDeny object is not initialized or does not contain content!" -- -- if self.isGrant(): -- tag = 'D:' + Constants.TAG_GRANT -- else: -- tag = 'D:' + Constants.TAG_DENY -- -- res = '' -- for privilege in self.privileges: -- res += privilege.toXML() -- return '<%s>%s</%s>' % (tag, res, tag) -diff --git a/src/webdav/acp/Makefile.am b/src/webdav/acp/Makefile.am -deleted file mode 100644 -index 506eb92..0000000 ---- a/src/webdav/acp/Makefile.am -+++ /dev/null -@@ -1,12 +0,0 @@ --sugardir = $(pythondir)/webdav/acp --sugar_PYTHON = \ -- AceHandler.py \ -- Ace.py \ -- Acl.py \ -- GrantDeny.py \ -- __init__.py \ -- Principal.py \ -- Privilege.py -- -- -- -diff --git a/src/webdav/acp/Principal.py b/src/webdav/acp/Principal.py -deleted file mode 100644 -index a0d5ec9..0000000 ---- a/src/webdav/acp/Principal.py -+++ /dev/null -@@ -1,189 +0,0 @@ --# Copyright 2008 German Aerospace Center (DLR) --# --# Licensed under the Apache License, Version 2.0 (the "License"); --# you may not use this file except in compliance with the License. --# You may obtain a copy of the License at --# --# http://www.apache.org/licenses/LICENSE-2.0 --# --# Unless required by applicable law or agreed to in writing, software --# distributed under the License is distributed on an "AS IS" BASIS, --# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. --# See the License for the specific language governing permissions and --# limitations under the License. -- -- --""" --Handling of principals for ACEs according to WebDAV ACP specification. --""" -- -- --from webdav import Constants --from webdav.Connection import WebdavError -- -- --__version__ = "$LastChangedRevision$" -- -- --class Principal(object): -- """ -- This class provides functionality for handling -- principals according to the WebDAV ACP. -- -- @ivar displayname: Name of the principal for output -- @type displayname: C{string} -- @ivar principalURL: URL under which the principal can be referenced on the server. -- @type principalURL: C{string} -- @ivar property: Information on type of a pseudo/jproperty principal, e. g. -- DAV:owner, DAV:authenticated, etc. -- @type property: C{string} -- -- @cvar _TAG_LIST_PRINCIPALS: List of allowed XML tags within a principal declaration. -- @type _TAG_LIST_PRINCIPALS: C{tuple} of C{string}s -- @cvar _TAG_LIST_STATUS: List of XML tags for the status of a pseudo principal. -- @type _TAG_LIST_STATUS: C{tuple} of C{string}s -- """ -- -- # some local constants for this class to make things easier/more readable: -- _TAG_LIST_PRINCIPALS = (Constants.TAG_HREF, # directly by URL -- Constants.TAG_ALL, Constants.TAG_AUTHENTICATED, Constants.TAG_UNAUTHENTICATED, -- # by log-in status -- Constants.TAG_PROPERTY, # for property info, e. g. 'owner' -- Constants.TAG_SELF, # only if the resource is the principal itself -- Constants.TAG_PROP) # contains property info like 'displayname' -- _TAG_LIST_STATUS = (Constants.TAG_ALL, Constants.TAG_AUTHENTICATED, Constants.TAG_UNAUTHENTICATED) -- -- # restrict instance variables -- __slots__ = ('displayname', 'principalURL', 'property') -- -- def __init__(self, domroot=None, displayname=None, principalURL=None): -- """ -- Constructor should be called with either no parameters (create blank Principal), -- one parameter (a DOM tree), or two parameters (displayname and URL or property tag). -- -- @param domroot: A DOM tree (default: None). -- @type domroot: L{webdav.WebdavResponse.Element} object -- @param displayname: The display name of a principal (default: None). -- @type displayname: C{string} -- @param principalURL: The URL representing a principal (default: None). -- @type principalURL: C{string} -- -- @raise WebdavError: When non-valid parameters or sets of parameters are -- passed a L{WebdavError} is raised. -- """ -- self.displayname = None -- self.principalURL = None -- self.property = None -- -- if domroot: -- for child in domroot.children: -- if child.ns == Constants.NS_DAV and (child.name in self._TAG_LIST_PRINCIPALS): -- if child.name == Constants.TAG_PROP: -- self.displayname = \ -- child.find(Constants.PROP_DISPLAY_NAME, Constants.NS_DAV) -- elif child.name == Constants.TAG_HREF: -- self.principalURL = child.textof() -- if self.principalURL and self.property in self._TAG_LIST_STATUS: -- raise WebdavError('Principal cannot contain a URL and "%s"' % (self.property)) -- elif child.name == Constants.TAG_PROPERTY: -- if child.count() == 1: -- if self.property: -- raise WebdavError('Property for principal has already been set: old "%s", new "%s"' \ -- % (self.property, child.pop().name)) -- elif self.principalURL: -- raise WebdavError('Principal cannot contain a URL and "%s"' % (self.property)) -- else: -- self.property = child.pop().name -- else: -- raise WebdavError("There should be only one value in the property for a principal, we have: %s" \ -- % child.name) -- else: -- if self.property: -- raise WebdavError('Property for principal has already been set: old "%s", new "%s"' \ -- % (self.property, child.name)) -- else: -- self.property = child.name -- if self.principalURL and self.property in self._TAG_LIST_STATUS: -- raise WebdavError('Principal cannot contain a URL and "%s"' % (self.property)) -- else: # This shouldn't happen, something's wrong with the DOM tree -- raise WebdavError('Non-valid tag in principal DOM tree for constructor: %s' % child.name) -- elif displayname == None or principalURL == None: -- if displayname: -- self.displayname = displayname -- if principalURL: -- self.principalURL = principalURL -- else: -- # This shouldn't happen, someone screwed up with the params ... -- raise WebdavError('Non-valid parameters handed to Principal constructor.') -- -- def __cmp__(self, other): -- if not isinstance(other, Principal): -- return 1 -- if self.displayname == other.displayname \ -- and self.principalURL == other.principalURL \ -- and self.property == other.property: -- return 0 -- else: -- return 1 -- -- def __repr__(self): -- return '<class Principal: displayname: "%s", principalURL: "%s", property: "%s">' \ -- % (self.displayname, self.principalURL, self.property) -- -- def copy(self, other): -- """Copy Principal object. -- -- @param other: Another principal to copy. -- @type other: L{Principal} object -- -- @raise WebdavError: When an object that is not a L{Principal} is passed -- a L{WebdavError} is raised. -- """ -- if not isinstance(other, Principal): -- raise WebdavError('Non-Principal object passed to copy method: ' % other.__class__) -- self.displayname = other.displayname -- self.principalURL = other.principalURL -- self.property = other.property -- -- def isValid(self): -- """ -- Checks whether necessarry props for principal are set. -- -- @return: Validity of principal. -- @rtype: C{bool} -- """ -- return (self.displayname and -- (self.principalURL or self.property) and -- not (self.principalURL and self.property)) -- -- def toXML(self, invert=False, displayname=False, defaultNameSpace=None): -- """Returns string of Principal content in valid XML as described in WebDAV ACP. -- -- @param defaultNameSpace: Name space (default: None). -- @type defaultNameSpace: C(string) -- @param invert: True if principal should be inverted (default: False). -- @type invert: C{bool} -- @param displayname: True if displayname should be in output (default: False). -- @type displayname: C{bool} -- """ -- # this check is needed for setting principals only: -- # assert self.isValid(), "principal is not initialized or does not contain valid content!" -- -- PRINCIPAL = 'D:' + Constants.TAG_PRINCIPAL -- res = '' -- if self.principalURL: -- res += '<D:%s>%s</D:%s>' % (Constants.TAG_HREF, self.principalURL, Constants.TAG_HREF) -- elif self.property in self._TAG_LIST_STATUS \ -- or self.property == Constants.TAG_SELF: -- res += '<D:%s/>' % (self.property) -- elif self.property: -- res += '<D:%s><D:%s/></D:%s>' \ -- % (Constants.TAG_PROPERTY, self.property, Constants.TAG_PROPERTY) -- if self.displayname and displayname: -- res += '<D:%s><D:%s>%s</D:%s></D:%s>' \ -- % (Constants.TAG_PROP, Constants.PROP_DISPLAY_NAME, -- self.displayname, -- Constants.PROP_DISPLAY_NAME, Constants.TAG_PROP) -- if invert: -- res = '<D:invert>%s</D:invert>' % (res) -- return '<%s>%s</%s>' % (PRINCIPAL, res, PRINCIPAL) -diff --git a/src/webdav/acp/Privilege.py b/src/webdav/acp/Privilege.py -deleted file mode 100644 -index abfdcf9..0000000 ---- a/src/webdav/acp/Privilege.py -+++ /dev/null -@@ -1,125 +0,0 @@ --# Copyright 2008 German Aerospace Center (DLR) --# --# Licensed under the Apache License, Version 2.0 (the "License"); --# you may not use this file except in compliance with the License. --# You may obtain a copy of the License at --# --# http://www.apache.org/licenses/LICENSE-2.0 --# --# Unless required by applicable law or agreed to in writing, software --# distributed under the License is distributed on an "AS IS" BASIS, --# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. --# See the License for the specific language governing permissions and --# limitations under the License. -- -- --""" --Handling for privileges for grant and deny clauses in ACEs --according to WebDAV ACP specification. --""" -- -- --from webdav import Constants --from webdav.Connection import WebdavError -- -- --__version__ = "$LastChangedRevision$" -- -- --class Privilege(object): -- """This class provides functionality for handling privileges for ACEs. -- -- @ivar name: Name of the privilege. -- @type name: C{string} -- -- @cvar __privileges: List of allowed XML tags for privileges. -- @type __privileges: C{tuple} of C{string}s -- """ -- -- -- __privileges = list() -- -- -- def __init__(self, privilege=None, domroot=None): -- """ -- Constructor should be called with either no parameters (create blank Privilege), -- one parameter (a DOM tree or privilege name to initialize it directly). -- -- @param domroot: A DOM tree (default: None). -- @type domroot: L{webdav.WebdavResponse.Element} object -- @param privilege: The valid name of a privilege (default: None). -- @type privilege: C{string} -- -- @raise WebdavError: When non-valid parameters or sets of parameters are -- passed a L{WebdavError} is raised. -- """ -- -- self.name = None -- -- if domroot: -- if len(domroot.children) != 1: -- raise WebdavError('Wrong number of elements for Privilege constructor, we have: %i' \ -- % (len(domroot.children))) -- else: -- child = domroot.children[0] -- if child.ns == Constants.NS_DAV and child.name in self.__privileges: -- self.name = child.name -- else: -- raise WebdavError('Not a valid privilege tag, we have: %s%s' \ -- % (child.ns, child.name)) -- elif privilege: -- if privilege in self.__privileges: -- self.name = privilege -- else: -- raise WebdavError('Not a valid privilege tag, we have: %s.' % str(privilege)) -- -- @classmethod -- def registerPrivileges(cls, privileges): -- """ -- Registers supported privilege tags. -- -- @param privileges: List of privilege tags. -- @type privileges: C{list} of C{unicode} -- """ -- -- for privilege in privileges: -- cls.__privileges.append(privilege) -- -- def __cmp__(self, other): -- """ Compares two Privilege instances. """ -- if not isinstance(other, Privilege): -- return 1 -- if self.name != other.name: -- return 1 -- else: -- return 0 -- -- def __repr__(self): -- """ Returns the string representation of an instance. """ -- return '<class Privilege: name: "%s">' % (self.name) -- -- def copy(self, other): -- """ -- Copy Privilege object. -- -- @param other: Another privilege to copy. -- @type other: L{Privilege} object -- -- @raise WebdavError: When an object that is not a L{Privilege} is passed -- a L{WebdavError} is raised. -- """ -- if not isinstance(other, Privilege): -- raise WebdavError('Non-Privilege object passed to copy method: %s' % other.__class__) -- self.name = other.name -- -- def toXML(self): -- """ -- Returns privilege content as string in valid XML as described in WebDAV ACP. -- -- @param defaultNameSpace: Name space (default: None). -- @type defaultNameSpace: C(string) -- """ -- assert self.name != None, "privilege is not initialized or does not contain valid content!" -- -- privilege = 'D:' + Constants.TAG_PRIVILEGE -- return '<%s><D:%s/></%s>' % (privilege, self.name, privilege) -diff --git a/src/webdav/acp/__init__.py b/src/webdav/acp/__init__.py -deleted file mode 100644 -index b5af299..0000000 ---- a/src/webdav/acp/__init__.py -+++ /dev/null -@@ -1,33 +0,0 @@ --# Copyright 2008 German Aerospace Center (DLR) --# --# Licensed under the Apache License, Version 2.0 (the "License"); --# you may not use this file except in compliance with the License. --# You may obtain a copy of the License at --# --# http://www.apache.org/licenses/LICENSE-2.0 --# --# Unless required by applicable law or agreed to in writing, software --# distributed under the License is distributed on an "AS IS" BASIS, --# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. --# See the License for the specific language governing permissions and --# limitations under the License. -- -- --from webdav import Constants --from webdav.acp.Acl import ACL --from webdav.acp.Ace import ACE --from webdav.acp.GrantDeny import GrantDeny --from webdav.acp.Privilege import Privilege --from webdav.acp.Principal import Principal -- -- --__version__ = "$LastChangedRevision$" -- -- --privileges = [Constants.TAG_READ, Constants.TAG_WRITE, Constants.TAG_WRITE_PROPERTIES, -- Constants.TAG_WRITE_CONTENT, Constants.TAG_UNLOCK, Constants.TAG_READ_ACL, -- Constants.TAG_READ_CURRENT_USER_PRIVILEGE_SET, Constants.TAG_WRITE_ACL, Constants.TAG_ALL, -- Constants.TAG_BIND, Constants.TAG_UNBIND, Constants.TAG_TAMINO_SECURITY, -- Constants.TAG_BIND_COLLECTION, Constants.TAG_UNBIND_COLLECTION, Constants.TAG_READ_PRIVATE_PROPERTIES, -- Constants.TAG_WRITE_PRIVATE_PROPERTIES] --Privilege.registerPrivileges(privileges) -diff --git a/src/webdav/davlib.py b/src/webdav/davlib.py -deleted file mode 100644 -index f4dac91..0000000 ---- a/src/webdav/davlib.py -+++ /dev/null -@@ -1,336 +0,0 @@ --# pylint: disable-msg=W0402,W0231,W0141,R0903,C0321,W0701,R0904,C0103,W0201,W0102,R0913,W0622,E1101,C0111,C0121,R0901 --# DAV client library --# --# Copyright (C) 1998-2000 Guido van Rossum. All Rights Reserved. --# Written by Greg Stein. Given to Guido. Licensed using the Python license. --# --# This module is maintained by Greg and is available at: --# http://www.lyra.org/greg/python/davlib.py --# --# Since this isn't in the Python distribution yet, we'll use the CVS ID --# for tracking: --# $Id: davlib.py 3182 2008-02-22 15:57:55 +0000 (Fr, 22 Feb 2008) schlauch $ --# -- --import httplib --import urllib --import string --import types --import mimetypes --import qp_xml -- -- --INFINITY = 'infinity' --XML_DOC_HEADER = '<?xml version="1.0" encoding="utf-8"?>' --XML_CONTENT_TYPE = 'text/xml; charset="utf-8"' -- --# block size for copying files up to the server --BLOCKSIZE = 16384 -- -- --class HTTPProtocolChooser(httplib.HTTPSConnection): -- def __init__(self, *args, **kw): -- self.protocol = kw.pop('protocol') -- if self.protocol == "https": -- self.default_port = 443 -- else: -- self.default_port = 80 -- -- apply(httplib.HTTPSConnection.__init__, (self,) + args, kw) -- -- def connect(self): -- if self.protocol == "https": -- httplib.HTTPSConnection.connect(self) -- else: -- httplib.HTTPConnection.connect(self) -- -- --class HTTPConnectionAuth(HTTPProtocolChooser): -- def __init__(self, *args, **kw): -- apply(HTTPProtocolChooser.__init__, (self,) + args, kw) -- -- self.__username = None -- self.__password = None -- self.__nonce = None -- self.__opaque = None -- -- def setauth(self, username, password): -- self.__username = username -- self.__password = password -- -- --def _parse_status(elem): -- text = elem.textof() -- idx1 = string.find(text, ' ') -- idx2 = string.find(text, ' ', idx1+1) -- return int(text[idx1:idx2]), text[idx2+1:] -- --class _blank: -- def __init__(self, **kw): -- self.__dict__.update(kw) --class _propstat(_blank): pass --class _response(_blank): pass --class _multistatus(_blank): pass -- --def _extract_propstat(elem): -- ps = _propstat(prop={}, status=None, responsedescription=None) -- for child in elem.children: -- if child.ns != 'DAV:': -- continue -- if child.name == 'prop': -- for prop in child.children: -- ps.prop[(prop.ns, prop.name)] = prop -- elif child.name == 'status': -- ps.status = _parse_status(child) -- elif child.name == 'responsedescription': -- ps.responsedescription = child.textof() -- ### unknown element name -- -- return ps -- --def _extract_response(elem): -- resp = _response(href=[], status=None, responsedescription=None, propstat=[]) -- for child in elem.children: -- if child.ns != 'DAV:': -- continue -- if child.name == 'href': -- resp.href.append(child.textof()) -- elif child.name == 'status': -- resp.status = _parse_status(child) -- elif child.name == 'responsedescription': -- resp.responsedescription = child.textof() -- elif child.name == 'propstat': -- resp.propstat.append(_extract_propstat(child)) -- ### unknown child element -- -- return resp -- --def _extract_msr(root): -- if root.ns != 'DAV:' or root.name != 'multistatus': -- raise 'invalid response: <DAV:multistatus> expected' -- -- msr = _multistatus(responses=[ ], responsedescription=None) -- -- for child in root.children: -- if child.ns != 'DAV:': -- continue -- if child.name == 'responsedescription': -- msr.responsedescription = child.textof() -- elif child.name == 'response': -- msr.responses.append(_extract_response(child)) -- ### unknown child element -- -- return msr -- --def _extract_locktoken(root): -- if root.ns != 'DAV:' or root.name != 'prop': -- raise 'invalid response: <DAV:prop> expected' -- elem = root.find('lockdiscovery', 'DAV:') -- if not elem: -- raise 'invalid response: <DAV:lockdiscovery> expected' -- elem = elem.find('activelock', 'DAV:') -- if not elem: -- raise 'invalid response: <DAV:activelock> expected' -- elem = elem.find('locktoken', 'DAV:') -- if not elem: -- raise 'invalid response: <DAV:locktoken> expected' -- elem = elem.find('href', 'DAV:') -- if not elem: -- raise 'invalid response: <DAV:href> expected' -- return elem.textof() -- -- --class DAVResponse(httplib.HTTPResponse): -- def parse_multistatus(self): -- self.root = qp_xml.Parser().parse(self) -- self.msr = _extract_msr(self.root) -- -- def parse_lock_response(self): -- self.root = qp_xml.Parser().parse(self) -- self.locktoken = _extract_locktoken(self.root) -- -- --class DAV(HTTPConnectionAuth): -- -- response_class = DAVResponse -- -- def get(self, url, extra_hdrs={ }): -- return self._request('GET', url, extra_hdrs=extra_hdrs) -- -- def head(self, url, extra_hdrs={ }): -- return self._request('HEAD', url, extra_hdrs=extra_hdrs) -- -- def post(self, url, data={ }, body=None, extra_hdrs={ }): -- headers = extra_hdrs.copy() -- -- assert body or data, "body or data must be supplied" -- assert not (body and data), "cannot supply both body and data" -- if data: -- body = '' -- for key, value in data.items(): -- if isinstance(value, types.ListType): -- for item in value: -- body = body + '&' + key + '=' + urllib.quote(str(item)) -- else: -- body = body + '&' + key + '=' + urllib.quote(str(value)) -- body = body[1:] -- headers['Content-Type'] = 'application/x-www-form-urlencoded' -- -- return self._request('POST', url, body, headers) -- -- def options(self, url='*', extra_hdrs={ }): -- return self._request('OPTIONS', url, extra_hdrs=extra_hdrs) -- -- def trace(self, url, extra_hdrs={ }): -- return self._request('TRACE', url, extra_hdrs=extra_hdrs) -- -- def put(self, url, contents, -- content_type=None, content_enc=None, extra_hdrs={ }): -- -- if not content_type: -- content_type, content_enc = mimetypes.guess_type(url) -- -- headers = extra_hdrs.copy() -- if content_type: -- headers['Content-Type'] = content_type -- if content_enc: -- headers['Content-Encoding'] = content_enc -- return self._request('PUT', url, contents, headers) -- -- def delete(self, url, extra_hdrs={ }): -- return self._request('DELETE', url, extra_hdrs=extra_hdrs) -- -- def propfind(self, url, body=None, depth=None, extra_hdrs={ }): -- headers = extra_hdrs.copy() -- headers['Content-Type'] = XML_CONTENT_TYPE -- if depth is not None: -- headers['Depth'] = str(depth) -- return self._request('PROPFIND', url, body, headers) -- -- def proppatch(self, url, body, extra_hdrs={ }): -- headers = extra_hdrs.copy() -- headers['Content-Type'] = XML_CONTENT_TYPE -- return self._request('PROPPATCH', url, body, headers) -- -- def mkcol(self, url, extra_hdrs={ }): -- return self._request('MKCOL', url, extra_hdrs=extra_hdrs) -- -- def move(self, src, dst, extra_hdrs={ }): -- headers = extra_hdrs.copy() -- headers['Destination'] = dst -- return self._request('MOVE', src, extra_hdrs=headers) -- -- def copy(self, src, dst, depth=None, extra_hdrs={ }): -- headers = extra_hdrs.copy() -- headers['Destination'] = dst -- if depth is not None: -- headers['Depth'] = str(depth) -- return self._request('COPY', src, extra_hdrs=headers) -- -- def lock(self, url, owner='', timeout=None, depth=None, -- scope='exclusive', type='write', extra_hdrs={ }): -- headers = extra_hdrs.copy() -- headers['Content-Type'] = XML_CONTENT_TYPE -- if depth is not None: -- headers['Depth'] = str(depth) -- if timeout is not None: -- headers['Timeout'] = timeout -- body = XML_DOC_HEADER + \ -- '<DAV:lockinfo xmlns:DAV="DAV:">' + \ -- '<DAV:lockscope><DAV:%s/></DAV:lockscope>' % scope + \ -- '<DAV:locktype><DAV:%s/></DAV:locktype>' % type + \ -- '<DAV:owner>' + owner + '</DAV:owner>' + \ -- '</DAV:lockinfo>' -- return self._request('LOCK', url, body, extra_hdrs=headers) -- -- def unlock(self, url, locktoken, extra_hdrs={ }): -- headers = extra_hdrs.copy() -- if locktoken[0] != '<': -- locktoken = '<' + locktoken + '>' -- headers['Lock-Token'] = locktoken -- return self._request('UNLOCK', url, extra_hdrs=headers) -- -- def _request(self, method, url, body=None, extra_hdrs={}): -- "Internal method for sending a request." -- -- self.request(method, url, body, extra_hdrs) -- return self.getresponse() -- -- -- # -- # Higher-level methods for typical client use -- # -- -- def allprops(self, url, depth=None): -- body = XML_DOC_HEADER + \ -- '<DAV:propfind xmlns:DAV="DAV:"><DAV:allprop/></DAV:propfind>' -- return self.propfind(url, body, depth=depth) -- -- def propnames(self, url, depth=None): -- body = XML_DOC_HEADER + \ -- '<DAV:propfind xmlns:DAV="DAV:"><DAV:propname/></DAV:propfind>' -- return self.propfind(url, body, depth) -- -- def getprops(self, url, *names, **kw): -- assert names, 'at least one property name must be provided' -- if kw.has_key('ns'): -- xmlns = ' xmlns:NS="' + kw['ns'] + '"' -- ns = 'NS:' -- del kw['ns'] -- else: -- xmlns = ns = '' -- if kw.has_key('depth'): -- depth = kw['depth'] -- del kw['depth'] -- else: -- depth = 0 -- assert not kw, 'unknown arguments' -- body = XML_DOC_HEADER + \ -- '<DAV:propfind xmlns:DAV="DAV:"' + xmlns + '><DAV:prop><' + ns + \ -- string.joinfields(names, '/><' + ns) + \ -- '/></DAV:prop></DAV:propfind>' -- return self.propfind(url, body, depth) -- -- def delprops(self, url, *names, **kw): -- assert names, 'at least one property name must be provided' -- if kw.has_key('ns'): -- xmlns = ' xmlns:NS="' + kw['ns'] + '"' -- ns = 'NS:' -- del kw['ns'] -- else: -- xmlns = ns = '' -- assert not kw, 'unknown arguments' -- body = XML_DOC_HEADER + \ -- '<DAV:propertyupdate xmlns:DAV="DAV:"' + xmlns + \ -- '><DAV:remove><DAV:prop><' + ns + \ -- string.joinfields(names, '/><' + ns) + \ -- '/></DAV:prop></DAV:remove></DAV:propertyupdate>' -- return self.proppatch(url, body) -- -- def setprops(self, url, *xmlprops, **props): -- assert xmlprops or props, 'at least one property must be provided' -- xmlprops = list(xmlprops) -- if props.has_key('ns'): -- xmlns = ' xmlns:NS="' + props['ns'] + '"' -- ns = 'NS:' -- del props['ns'] -- else: -- xmlns = ns = '' -- for key, value in props.items(): -- if value: -- xmlprops.append('<%s%s>%s</%s%s>' % (ns, key, value, ns, key)) -- else: -- xmlprops.append('<%s%s/>' % (ns, key)) -- elems = string.joinfields(xmlprops, '') -- body = XML_DOC_HEADER + \ -- '<DAV:propertyupdate xmlns:DAV="DAV:"' + xmlns + \ -- '><DAV:set><DAV:prop>' + \ -- elems + \ -- '</DAV:prop></DAV:set></DAV:propertyupdate>' -- return self.proppatch(url, body) -- -- def get_lock(self, url, owner='', timeout=None, depth=None): -- response = self.lock(url, owner, timeout, depth) -- response.parse_lock_response() -- return response.locktoken -- -\ No newline at end of file -diff --git a/src/webdav/logger.py b/src/webdav/logger.py -deleted file mode 100644 -index d2538ef..0000000 ---- a/src/webdav/logger.py -+++ /dev/null -@@ -1,51 +0,0 @@ --# Copyright 2008 German Aerospace Center (DLR) --# --# Licensed under the Apache License, Version 2.0 (the "License"); --# you may not use this file except in compliance with the License. --# You may obtain a copy of the License at --# --# http://www.apache.org/licenses/LICENSE-2.0 --# --# Unless required by applicable law or agreed to in writing, software --# distributed under the License is distributed on an "AS IS" BASIS, --# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. --# See the License for the specific language governing permissions and --# limitations under the License. -- -- --"""" --Module provides access to a configured logger instance. --The logger writes C{sys.stdout}. --""" -- -- --import logging --import sys -- -- --__version__ = "$LastChangedRevision$"[11:-2] -- -- --_defaultLoggerName = "webdavLogger" --_fileLogFormat = "%(asctime)s: %(levelname)s: %(message)s" -- -- --def getDefaultLogger(handler=None): -- """ -- Returns a configured logger object. -- -- @return: Logger instance. -- @rtype: C{logging.Logger} -- """ -- -- myLogger = logging.getLogger(_defaultLoggerName) -- if len(myLogger.handlers) == 0: -- myLogger.level = logging.DEBUG -- formatter = logging.Formatter(_fileLogFormat) -- if handler is None: -- stdoutHandler = logging.StreamHandler(sys.stdout) -- stdoutHandler.setFormatter(formatter) -- myLogger.addHandler(stdoutHandler) -- else: -- myLogger.addHandler(handler) -- return myLogger -diff --git a/src/webdav/qp_xml.py b/src/webdav/qp_xml.py -deleted file mode 100644 -index f167e1b..0000000 ---- a/src/webdav/qp_xml.py -+++ /dev/null -@@ -1,240 +0,0 @@ --# pylint: disable-msg=W0311,E1101,E1103,W0201,C0103,W0622,W0402,W0706,R0911,W0613,W0612,R0912,W0141,C0111,C0121 -- --# qp_xml: Quick Parsing for XML --# --# Written by Greg Stein. Public Domain. --# No Copyright, no Rights Reserved, and no Warranties. --# --# This module is maintained by Greg and is available as part of the XML-SIG --# distribution. This module and its changelog can be fetched at: --# http://www.lyra.org/cgi-bin/viewcvs.cgi/xml/xml/utils/qp_xml.py --# --# Additional information can be found on Greg's Python page at: --# http://www.lyra.org/greg/python/ --# --# This module was added to the XML-SIG distribution on February 14, 2000. --# As part of that distribution, it falls under the XML distribution license. --# -- --import string --from xml.parsers import expat -- -- --error = __name__ + '.error' -- -- --# --# The parsing class. Instantiate and pass a string/file to .parse() --# --class Parser: -- def __init__(self): -- self.reset() -- -- def reset(self): -- self.root = None -- self.cur_elem = None -- -- def find_prefix(self, prefix): -- elem = self.cur_elem -- while elem: -- if elem.ns_scope.has_key(prefix): -- return elem.ns_scope[prefix] -- elem = elem.parent -- -- if prefix == '': -- return '' # empty URL for "no namespace" -- -- return None -- -- def process_prefix(self, name, use_default): -- idx = string.find(name, ':') -- if idx == -1: -- if use_default: -- return self.find_prefix(''), name -- return '', name # no namespace -- -- if string.lower(name[:3]) == 'xml': -- return '', name # name is reserved by XML. don't break out a NS. -- -- ns = self.find_prefix(name[:idx]) -- if ns is None: -- raise error, 'namespace prefix ("%s") not found' % name[:idx] -- -- return ns, name[idx+1:] -- -- def start(self, name, attrs): -- elem = _element(name=name, lang=None, parent=None, -- children=[], ns_scope={}, attrs={}, -- first_cdata='', following_cdata='') -- -- if self.cur_elem: -- elem.parent = self.cur_elem -- elem.parent.children.append(elem) -- self.cur_elem = elem -- else: -- self.cur_elem = self.root = elem -- -- work_attrs = [ ] -- -- # scan for namespace declarations (and xml:lang while we're at it) -- for name, value in attrs.items(): -- if name == 'xmlns': -- elem.ns_scope[''] = value -- elif name[:6] == 'xmlns:': -- elem.ns_scope[name[6:]] = value -- elif name == 'xml:lang': -- elem.lang = value -- else: -- work_attrs.append((name, value)) -- -- # inherit xml:lang from parent -- if elem.lang is None and elem.parent: -- elem.lang = elem.parent.lang -- -- # process prefix of the element name -- elem.ns, elem.name = self.process_prefix(elem.name, 1) -- -- # process attributes' namespace prefixes -- for name, value in work_attrs: -- elem.attrs[self.process_prefix(name, 0)] = value -- -- def end(self, name): -- parent = self.cur_elem.parent -- -- del self.cur_elem.ns_scope -- del self.cur_elem.parent -- -- self.cur_elem = parent -- -- def cdata(self, data): -- elem = self.cur_elem -- if elem.children: -- last = elem.children[-1] -- last.following_cdata = last.following_cdata + data -- else: -- elem.first_cdata = elem.first_cdata + data -- -- def parse(self, input): -- self.reset() -- -- p = expat.ParserCreate() -- p.StartElementHandler = self.start -- p.EndElementHandler = self.end -- p.CharacterDataHandler = self.cdata -- -- try: -- if type(input) == type(''): -- p.Parse(input, 1) -- else: -- while 1: -- s = input.read(_BLOCKSIZE) -- if not s: -- p.Parse('', 1) -- break -- -- p.Parse(s, 0) -- -- finally: -- if self.root: -- _clean_tree(self.root) -- -- return self.root -- -- --# --# handy function for dumping a tree that is returned by Parser --# --def dump(f, root): -- f.write('<?xml version="1.0"?>\n') -- namespaces = _collect_ns(root) -- _dump_recurse(f, root, namespaces, dump_ns=1) -- f.write('\n') -- -- --# --# This function returns the element's CDATA. Note: this is not recursive -- --# it only returns the CDATA immediately within the element, excluding the --# CDATA in child elements. --# --def textof(elem): -- return elem.textof() -- -- --######################################################################### --# --# private stuff for qp_xml --# -- --_BLOCKSIZE = 16384 # chunk size for parsing input -- --class _element: -- def __init__(self, **kw): -- self.__dict__.update(kw) -- -- def textof(self): -- '''Return the CDATA of this element. -- -- Note: this is not recursive -- it only returns the CDATA immediately -- within the element, excluding the CDATA in child elements. -- ''' -- s = self.first_cdata -- for child in self.children: -- s = s + child.following_cdata -- return s -- -- def find(self, name, ns=''): -- for elem in self.children: -- if elem.name == name and elem.ns == ns: -- return elem -- return None -- -- --def _clean_tree(elem): -- elem.parent = None -- del elem.parent -- map(_clean_tree, elem.children) -- -- --def _collect_recurse(elem, dict): -- dict[elem.ns] = None -- for ns, name in elem.attrs.keys(): -- dict[ns] = None -- for child in elem.children: -- _collect_recurse(child, dict) -- --def _collect_ns(elem): -- "Collect all namespaces into a NAMESPACE -> PREFIX mapping." -- d = { '' : None } -- _collect_recurse(elem, d) -- del d[''] # make sure we don't pick up no-namespace entries -- keys = d.keys() -- for i in range(len(keys)): -- d[keys[i]] = i -- return d -- --def _dump_recurse(f, elem, namespaces, lang=None, dump_ns=0): -- if elem.ns: -- f.write('<ns%d:%s' % (namespaces[elem.ns], elem.name)) -- else: -- f.write('<' + elem.name) -- for (ns, name), value in elem.attrs.items(): -- if ns: -- f.write(' ns%d:%s="%s"' % (namespaces[ns], name, value)) -- else: -- f.write(' %s="%s"' % (name, value)) -- if dump_ns: -- for ns, id in namespaces.items(): -- f.write(' xmlns:ns%d="%s"' % (id, ns)) -- if elem.lang != lang: -- f.write(' xml:lang="%s"' % elem.lang) -- if elem.children or elem.first_cdata: -- f.write('>' + elem.first_cdata) -- for child in elem.children: -- _dump_recurse(f, child, namespaces, elem.lang) -- f.write(child.following_cdata) -- if elem.ns: -- f.write('</ns%d:%s>' % (namespaces[elem.ns], elem.name)) -- else: -- f.write('</%s>' % elem.name) -- else: -- f.write('/>') -diff --git a/src/webdav/uuid_.py b/src/webdav/uuid_.py -deleted file mode 100644 -index 3b590e8..0000000 ---- a/src/webdav/uuid_.py -+++ /dev/null -@@ -1,476 +0,0 @@ --r"""UUID objects (universally unique identifiers) according to RFC 4122. -- --This module provides immutable UUID objects (class UUID) and the functions --uuid1(), uuid3(), uuid4(), uuid5() for generating version 1, 3, 4, and 5 --UUIDs as specified in RFC 4122. -- --If all you want is a unique ID, you should probably call uuid1() or uuid4(). --Note that uuid1() may compromise privacy since it creates a UUID containing --the computer's network address. uuid4() creates a random UUID. -- --Typical usage: -- -- >>> import uuid -- -- # make a UUID based on the host ID and current time -- >>> uuid.uuid1() -- UUID('a8098c1a-f86e-11da-bd1a-00112444be1e') -- -- # make a UUID using an MD5 hash of a namespace UUID and a name -- >>> uuid.uuid3(uuid.NAMESPACE_DNS, 'python.org') -- UUID('6fa459ea-ee8a-3ca4-894e-db77e160355e') -- -- # make a random UUID -- >>> uuid.uuid4() -- UUID('16fd2706-8baf-433b-82eb-8c7fada847da') -- -- # make a UUID using a SHA-1 hash of a namespace UUID and a name -- >>> uuid.uuid5(uuid.NAMESPACE_DNS, 'python.org') -- UUID('886313e1-3b8a-5372-9b90-0c9aee199e5d') -- -- # make a UUID from a string of hex digits (braces and hyphens ignored) -- >>> x = uuid.UUID('{00010203-0405-0607-0809-0a0b0c0d0e0f}') -- -- # convert a UUID to a string of hex digits in standard form -- >>> str(x) -- '00010203-0405-0607-0809-0a0b0c0d0e0f' -- -- # get the raw 16 bytes of the UUID -- >>> x.bytes -- '\x00\x01\x02\x03\x04\x05\x06\x07\x08\t\n\x0b\x0c\r\x0e\x0f' -- -- # make a UUID from a 16-byte string -- >>> uuid.UUID(bytes=x.bytes) -- UUID('00010203-0405-0607-0809-0a0b0c0d0e0f') --""" -- --__author__ = 'Ka-Ping Yee <ping@zesty.ca>' --__date__ = '$Date: 2006/06/12 23:15:40 $'.split()[1].replace('/', '-') --__version__ = '$Revision: 1.30 $'.split()[1] -- --RESERVED_NCS, RFC_4122, RESERVED_MICROSOFT, RESERVED_FUTURE = [ -- 'reserved for NCS compatibility', 'specified in RFC 4122', -- 'reserved for Microsoft compatibility', 'reserved for future definition'] -- --class UUID(object): -- """Instances of the UUID class represent UUIDs as specified in RFC 4122. -- UUID objects are immutable, hashable, and usable as dictionary keys. -- Converting a UUID to a string with str() yields something in the form -- '12345678-1234-1234-1234-123456789abc'. The UUID constructor accepts -- four possible forms: a similar string of hexadecimal digits, or a -- string of 16 raw bytes as an argument named 'bytes', or a tuple of -- six integer fields (with 32-bit, 16-bit, 16-bit, 8-bit, 8-bit, and -- 48-bit values respectively) as an argument named 'fields', or a single -- 128-bit integer as an argument named 'int'. -- -- UUIDs have these read-only attributes: -- -- bytes the UUID as a 16-byte string -- -- fields a tuple of the six integer fields of the UUID, -- which are also available as six individual attributes -- and two derived attributes: -- -- time_low the first 32 bits of the UUID -- time_mid the next 16 bits of the UUID -- time_hi_version the next 16 bits of the UUID -- clock_seq_hi_variant the next 8 bits of the UUID -- clock_seq_low the next 8 bits of the UUID -- node the last 48 bits of the UUID -- -- time the 60-bit timestamp -- clock_seq the 14-bit sequence number -- -- hex the UUID as a 32-character hexadecimal string -- -- int the UUID as a 128-bit integer -- -- urn the UUID as a URN as specified in RFC 4122 -- -- variant the UUID variant (one of the constants RESERVED_NCS, -- RFC_4122, RESERVED_MICROSOFT, or RESERVED_FUTURE) -- -- version the UUID version number (1 through 5, meaningful only -- when the variant is RFC_4122) -- """ -- -- def __init__(self, hex=None, bytes=None, fields=None, int=None, -- version=None): -- r"""Create a UUID from either a string of 32 hexadecimal digits, -- a string of 16 bytes as the 'bytes' argument, a tuple of six -- integers (32-bit time_low, 16-bit time_mid, 16-bit time_hi_version, -- 8-bit clock_seq_hi_variant, 8-bit clock_seq_low, 48-bit node) as -- the 'fields' argument, or a single 128-bit integer as the 'int' -- argument. When a string of hex digits is given, curly braces, -- hyphens, and a URN prefix are all optional. For example, these -- expressions all yield the same UUID: -- -- UUID('{12345678-1234-5678-1234-567812345678}') -- UUID('12345678123456781234567812345678') -- UUID('urn:uuid:12345678-1234-5678-1234-567812345678') -- UUID(bytes='\x12\x34\x56\x78'*4) -- UUID(fields=(0x12345678, 0x1234, 0x5678, 0x12, 0x34, 0x567812345678)) -- UUID(int=0x12345678123456781234567812345678) -- -- Exactly one of 'hex', 'bytes', 'fields', or 'int' must be given. -- The 'version' argument is optional; if given, the resulting UUID -- will have its variant and version number set according to RFC 4122, -- overriding bits in the given 'hex', 'bytes', 'fields', or 'int'. -- """ -- -- if [hex, bytes, fields, int].count(None) != 3: -- raise TypeError('need just one of hex, bytes, fields, or int') -- if hex is not None: -- hex = hex.replace('urn:', '').replace('uuid:', '') -- hex = hex.strip('{}').replace('-', '') -- if len(hex) != 32: -- raise ValueError('badly formed hexadecimal UUID string') -- int = long(hex, 16) -- if bytes is not None: -- if len(bytes) != 16: -- raise ValueError('bytes is not a 16-char string') -- int = long(('%02x'*16) % tuple(map(ord, bytes)), 16) -- if fields is not None: -- if len(fields) != 6: -- raise ValueError('fields is not a 6-tuple') -- (time_low, time_mid, time_hi_version, -- clock_seq_hi_variant, clock_seq_low, node) = fields -- if not 0 <= time_low < 1<<32L: -- raise ValueError('field 1 out of range (need a 32-bit value)') -- if not 0 <= time_mid < 1<<16L: -- raise ValueError('field 2 out of range (need a 16-bit value)') -- if not 0 <= time_hi_version < 1<<16L: -- raise ValueError('field 3 out of range (need a 16-bit value)') -- if not 0 <= clock_seq_hi_variant < 1<<8L: -- raise ValueError('field 4 out of range (need an 8-bit value)') -- if not 0 <= clock_seq_low < 1<<8L: -- raise ValueError('field 5 out of range (need an 8-bit value)') -- if not 0 <= node < 1<<48L: -- raise ValueError('field 6 out of range (need a 48-bit value)') -- clock_seq = (clock_seq_hi_variant << 8L) | clock_seq_low -- int = ((time_low << 96L) | (time_mid << 80L) | -- (time_hi_version << 64L) | (clock_seq << 48L) | node) -- if int is not None: -- if not 0 <= int < 1<<128L: -- raise ValueError('int is out of range (need a 128-bit value)') -- if version is not None: -- if not 1 <= version <= 5: -- raise ValueError('illegal version number') -- # Set the variant to RFC 4122. -- int &= ~(0xc000 << 48L) -- int |= 0x8000 << 48L -- # Set the version number. -- int &= ~(0xf000 << 64L) -- int |= version << 76L -- self.__dict__['int'] = int -- -- def __cmp__(self, other): -- if isinstance(other, UUID): -- return cmp(self.int, other.int) -- return NotImplemented -- -- def __hash__(self): -- return hash(self.int) -- -- def __int__(self): -- return self.int -- -- def __repr__(self): -- return 'UUID(%r)' % str(self) -- -- def __setattr__(self, name, value): -- raise TypeError('UUID objects are immutable') -- -- def __str__(self): -- hex = '%032x' % self.int -- return '%s-%s-%s-%s-%s' % ( -- hex[:8], hex[8:12], hex[12:16], hex[16:20], hex[20:]) -- -- def get_bytes(self): -- bytes = '' -- for shift in range(0, 128, 8): -- bytes = chr((self.int >> shift) & 0xff) + bytes -- return bytes -- -- bytes = property(get_bytes) -- -- def get_fields(self): -- return (self.time_low, self.time_mid, self.time_hi_version, -- self.clock_seq_hi_variant, self.clock_seq_low, self.node) -- -- fields = property(get_fields) -- -- def get_time_low(self): -- return self.int >> 96L -- -- time_low = property(get_time_low) -- -- def get_time_mid(self): -- return (self.int >> 80L) & 0xffff -- -- time_mid = property(get_time_mid) -- -- def get_time_hi_version(self): -- return (self.int >> 64L) & 0xffff -- -- time_hi_version = property(get_time_hi_version) -- -- def get_clock_seq_hi_variant(self): -- return (self.int >> 56L) & 0xff -- -- clock_seq_hi_variant = property(get_clock_seq_hi_variant) -- -- def get_clock_seq_low(self): -- return (self.int >> 48L) & 0xff -- -- clock_seq_low = property(get_clock_seq_low) -- -- def get_time(self): -- return (((self.time_hi_version & 0x0fffL) << 48L) | -- (self.time_mid << 32L) | self.time_low) -- -- time = property(get_time) -- -- def get_clock_seq(self): -- return (((self.clock_seq_hi_variant & 0x3fL) << 8L) | -- self.clock_seq_low) -- -- clock_seq = property(get_clock_seq) -- -- def get_node(self): -- return self.int & 0xffffffffffff -- -- node = property(get_node) -- -- def get_hex(self): -- return '%032x' % self.int -- -- hex = property(get_hex) -- -- def get_urn(self): -- return 'urn:uuid:' + str(self) -- -- urn = property(get_urn) -- -- def get_variant(self): -- if not self.int & (0x8000 << 48L): -- return RESERVED_NCS -- elif not self.int & (0x4000 << 48L): -- return RFC_4122 -- elif not self.int & (0x2000 << 48L): -- return RESERVED_MICROSOFT -- else: -- return RESERVED_FUTURE -- -- variant = property(get_variant) -- -- def get_version(self): -- # The version bits are only meaningful for RFC 4122 UUIDs. -- if self.variant == RFC_4122: -- return int((self.int >> 76L) & 0xf) -- -- version = property(get_version) -- --def _ifconfig_getnode(): -- """Get the hardware address on Unix by running ifconfig.""" -- import os -- for dir in ['', '/sbin/', '/usr/sbin']: -- try: -- pipe = os.popen(os.path.join(dir, 'ifconfig')) -- except IOError: -- continue -- for line in pipe: -- words = line.lower().split() -- for i in range(len(words)): -- if words[i] in ['hwaddr', 'ether']: -- return int(words[i + 1].replace(':', ''), 16) -- --def _ipconfig_getnode(): -- """Get the hardware address on Windows by running ipconfig.exe.""" -- import os, re -- dirs = ['', r'c:\windows\system32', r'c:\winnt\system32'] -- try: -- import ctypes -- buffer = ctypes.create_string_buffer(300) -- ctypes.windll.kernel32.GetSystemDirectoryA(buffer, 300) -- dirs.insert(0, buffer.value.decode('mbcs')) -- except: -- pass -- for dir in dirs: -- try: -- pipe = os.popen(os.path.join(dir, 'ipconfig') + ' /all') -- except IOError: -- continue -- for line in pipe: -- value = line.split(':')[-1].strip().lower() -- if re.match('([0-9a-f][0-9a-f]-){5}[0-9a-f][0-9a-f]', value): -- return int(value.replace('-', ''), 16) -- --def _netbios_getnode(): -- """Get the hardware address on Windows using NetBIOS calls. -- See http://support.microsoft.com/kb/118623 for details.""" -- import win32wnet, netbios -- ncb = netbios.NCB() -- ncb.Command = netbios.NCBENUM -- ncb.Buffer = adapters = netbios.LANA_ENUM() -- adapters._pack() -- if win32wnet.Netbios(ncb) != 0: -- return -- adapters._unpack() -- for i in range(adapters.length): -- ncb.Reset() -- ncb.Command = netbios.NCBRESET -- ncb.Lana_num = ord(adapters.lana[i]) -- if win32wnet.Netbios(ncb) != 0: -- continue -- ncb.Reset() -- ncb.Command = netbios.NCBASTAT -- ncb.Lana_num = ord(adapters.lana[i]) -- ncb.Callname = '*'.ljust(16) -- ncb.Buffer = status = netbios.ADAPTER_STATUS() -- if win32wnet.Netbios(ncb) != 0: -- continue -- status._unpack() -- bytes = map(ord, status.adapter_address) -- return ((bytes[0]<<40L) + (bytes[1]<<32L) + (bytes[2]<<24L) + -- (bytes[3]<<16L) + (bytes[4]<<8L) + bytes[5]) -- --# Thanks to Thomas Heller for ctypes and for his help with its use here. -- --# If ctypes is available, use it to find system routines for UUID generation. --_uuid_generate_random = _uuid_generate_time = _UuidCreate = None --try: -- import ctypes, ctypes.util -- _buffer = ctypes.create_string_buffer(16) -- -- # The uuid_generate_* routines are provided by libuuid on at least -- # Linux and FreeBSD, and provided by libc on Mac OS X. -- for libname in ['uuid', 'c']: -- try: -- lib = ctypes.CDLL(ctypes.util.find_library(libname)) -- except: -- continue -- if hasattr(lib, 'uuid_generate_random'): -- _uuid_generate_random = lib.uuid_generate_random -- if hasattr(lib, 'uuid_generate_time'): -- _uuid_generate_time = lib.uuid_generate_time -- -- # On Windows prior to 2000, UuidCreate gives a UUID containing the -- # hardware address. On Windows 2000 and later, UuidCreate makes a -- # random UUID and UuidCreateSequential gives a UUID containing the -- # hardware address. These routines are provided by the RPC runtime. -- try: -- lib = ctypes.windll.rpcrt4 -- except: -- lib = None -- _UuidCreate = getattr(lib, 'UuidCreateSequential', -- getattr(lib, 'UuidCreate', None)) --except: -- pass -- --def _unixdll_getnode(): -- """Get the hardware address on Unix using ctypes.""" -- _uuid_generate_time(_buffer) -- return UUID(bytes=_buffer.raw).node -- --def _windll_getnode(): -- """Get the hardware address on Windows using ctypes.""" -- if _UuidCreate(_buffer) == 0: -- return UUID(bytes=_buffer.raw).node -- --def _random_getnode(): -- """Get a random node ID, with eighth bit set as suggested by RFC 4122.""" -- import random -- return random.randrange(0, 1<<48L) | 0x010000000000L -- --_node = None -- --def getnode(): -- """Get the hardware address as a 48-bit integer. The first time this -- runs, it may launch a separate program, which could be quite slow. If -- all attempts to obtain the hardware address fail, we choose a random -- 48-bit number with its eighth bit set to 1 as recommended in RFC 4122.""" -- -- global _node -- if _node is not None: -- return _node -- -- import sys -- if sys.platform == 'win32': -- getters = [_windll_getnode, _netbios_getnode, _ipconfig_getnode] -- else: -- getters = [_unixdll_getnode, _ifconfig_getnode] -- -- for getter in getters + [_random_getnode]: -- try: -- _node = getter() -- except: -- continue -- if _node is not None: -- return _node -- --def uuid1(node=None, clock_seq=None): -- """Generate a UUID from a host ID, sequence number, and the current time. -- If 'node' is not given, getnode() is used to obtain the hardware -- address. If 'clock_seq' is given, it is used as the sequence number; -- otherwise a random 14-bit sequence number is chosen.""" -- -- # When the system provides a version-1 UUID generator, use it (but don't -- # use UuidCreate here because its UUIDs don't conform to RFC 4122). -- if _uuid_generate_time and node is clock_seq is None: -- _uuid_generate_time(_buffer) -- return UUID(bytes=_buffer.raw) -- -- import time -- nanoseconds = int(time.time() * 1e9) -- # 0x01b21dd213814000 is the number of 100-ns intervals between the -- # UUID epoch 1582-10-15 00:00:00 and the Unix epoch 1970-01-01 00:00:00. -- timestamp = int(nanoseconds/100) + 0x01b21dd213814000L -- if clock_seq is None: -- import random -- clock_seq = random.randrange(1<<14L) # instead of stable storage -- time_low = timestamp & 0xffffffffL -- time_mid = (timestamp >> 32L) & 0xffffL -- time_hi_version = (timestamp >> 48L) & 0x0fffL -- clock_seq_low = clock_seq & 0xffL -- clock_seq_hi_variant = (clock_seq >> 8L) & 0x3fL -- if node is None: -- node = getnode() -- return UUID(fields=(time_low, time_mid, time_hi_version, -- clock_seq_hi_variant, clock_seq_low, node), version=1) -- --def uuid3(namespace, name): -- """Generate a UUID from the MD5 hash of a namespace UUID and a name.""" -- import md5 -- hash = md5.md5(namespace.bytes + name).digest() -- return UUID(bytes=hash[:16], version=3) -- --def uuid4(): -- """Generate a random UUID.""" -- -- # When the system provides a version-4 UUID generator, use it. -- if _uuid_generate_random: -- _uuid_generate_random(_buffer) -- return UUID(bytes=_buffer.raw) -- -- # Otherwise, get randomness from urandom or the 'random' module. -- try: -- import os -- return UUID(bytes=os.urandom(16), version=4) -- except: -- import random -- bytes = [chr(random.randrange(256)) for i in range(16)] -- return UUID(bytes=bytes, version=4) -- --def uuid5(namespace, name): -- """Generate a UUID from the SHA-1 hash of a namespace UUID and a name.""" -- import sha -- hash = sha.sha(namespace.bytes + name).digest() -- return UUID(bytes=hash[:16], version=5) -- --# The following standard UUIDs are for use with uuid3() or uuid5(). -- --NAMESPACE_DNS = UUID('6ba7b810-9dad-11d1-80b4-00c04fd430c8') --NAMESPACE_URL = UUID('6ba7b811-9dad-11d1-80b4-00c04fd430c8') --NAMESPACE_OID = UUID('6ba7b812-9dad-11d1-80b4-00c04fd430c8') --NAMESPACE_X500 = UUID('6ba7b814-9dad-11d1-80b4-00c04fd430c8') --- -1.7.4.4 - |