Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- # uncompyle6 version 3.2.3
- # Python bytecode 3.3 (3230)
- # Decompiled from: Python 2.7.12 (default, Dec 4 2017, 14:50:18)
- # [GCC 5.4.0 20160609]
- # Embedded file name: /Users/wbond/Library/Application Support/Sublime Text 3/Packages/SFTP/sftp/commands.py
- # Compiled at: 2015-11-05 01:05:02
- # Size of source mod 2**32: 126303 bytes
- import sublime, sublime_plugin, os, subprocess, re, sys, hmac, binascii, time, traceback, tempfile, shutil, difflib, codecs
- from itertools import cycle
- try:
- from itertools import izip
- str_cls = unicode
- str_clses = ['unicode', 'str']
- st_version = 2
- except ImportError:
- izip = zip
- xrange = range
- str_cls = str
- str_clses = ['str', 'bytes']
- st_version = 3
- if os.name != 'nt':
- import unicodedata
- from .errors import AuthenticationError, BinaryMissingError, ConnectionError, DisconnectionError, encoding_error, handle_exception, NotFoundError, PermissionError
- from .panel_printer import PanelPrinter, ProgressThread
- from .threads import HookedThread, SyncThread, ThreadActivity, ThreadTracker, unset_current_thread
- from .debug import debug_print, get_debug
- from .times import time_diff
- from .views import get_view_by_group_index
- from .paths import canonicalize, dirname, fix_windows_path, ignore_paths, ignore_rm_paths, is_dir, is_root, local_to_remote, path_type, remote_to_local
- from .config import build_config, find_config, get_default_config, get_server_config_folder, load_config, parse_config, prepare_server_config, setup_tmp_dir
- from .sftp_transport import SFTP
- from .ftp_transport import FTP
- from .vcs import Hg, Git, SVN
- transports = {'SFTP': SFTP,
- 'FTP': FTP}
- if 'ssl' in sys.modules:
- from .ftps_transport import FTPS
- transports['FTPS'] = FTPS
- def show_qp(window, choices, on_done):
- def show_timeout():
- window.show_quick_panel(choices, on_done)
- sublime.set_timeout(show_timeout, 10)
- class SftpCommand(object):
- connections = {}
- identifiers = {}
- usage = {}
- remote_roots = {}
- remote_time_offsets = {}
- @classmethod
- def setup_elements(cls, config):
- if not hasattr(SftpCommand, 'elements'):
- SftpCommand.elements = [0,
- config.get('email'),
- 'sftp_flags',
- 'ssh_key_file',
- 'psftp']
- SftpCommand.elements.append('Sublime SFTP\n\nThanks for trying out Sublime SFTP. It is free to try, but a license must be purchased for continued use.\n\nPlease visit http://sublime.wbond.net/sftp for details.')
- SftpCommand.elements.append(config.get('product_key'))
- key = SftpCommand.elements[1]
- if isinstance(key, str_cls):
- key = key.encode('utf-8')
- for element in SftpCommand.elements[2:-2]:
- key = hmac.new(element.encode('utf-8'), key).digest()
- SftpCommand.elements[1] = key
- def create_default_config(self, file):
- handle = open(file, 'w')
- handle.write(get_default_config(True))
- handle.close()
- def first_path(self, paths):
- if paths is None or paths == []:
- return
- else:
- return paths[0]
- def get_path(self, paths=None, allow_multiple=False, view=None):
- type_ = type(paths).__name__
- if (paths is None or paths == []) and type_ not in str_clses:
- if view is None:
- if not hasattr(self, 'window'):
- return
- view = self.window.active_view()
- if view is None:
- if st_version == 3:
- return self.window.extract_variables().get('file')
- return
- return view.file_name()
- elif allow_multiple or type_ in str_clses:
- return paths
- else:
- return paths[0]
- def has_config(self, path):
- return bool(find_config(path, True))
- def get_config(self, paths=None, quiet=False):
- path = self.get_path(paths)
- config, config_file = load_config(path)
- if config is None:
- return
- else:
- config_dir = dirname(config_file)
- return build_config(config, config_dir, config_file, quiet)
- def save_files(self, files=[]):
- if not isinstance(files, list):
- files = [
- files]
- window = self.window if hasattr(self, 'window') else self.view.window()
- original_view = window.active_view()
- for view in window.views():
- file = view.file_name()
- if not (files and file not in files):
- if not file:
- continue
- if find_config(file, True) and view.is_dirty():
- window.focus_view(view)
- view_settings = view.settings()
- view_settings.set('sftp_auto_save', True)
- view.run_command('save')
- continue
- if original_view:
- window.focus_view(original_view)
- class SftpThread(HookedThread):
- def __init__(self, window_id, config, file, action, should_join=True, quiet=False, increment=True, reset_lcd=None, on_connect=None, on_fail=None, on_complete=None, hide=True, skip_ignore=False, skip_symlinks=True, mode=None):
- self.window_id = window_id
- self.printer = PanelPrinter.get(window_id)
- self.config = config
- self.file = file
- self.action = action
- self.quiet = quiet
- self.result = None
- self.connection = None
- self.increment = increment
- self.should_join = should_join
- self.reset_lcd = reset_lcd
- self.on_connect = on_connect
- self.on_fail = on_fail
- self.on_complete = on_complete
- self.connection_id = str(window_id) + '-' + self.config['user'] + '@' + self.config['host'] + ':' + self.config['port']
- self.hide = hide
- self.failed = False
- self.skip_ignore = skip_ignore
- self.skip_symlinks = skip_symlinks
- self.mode = mode
- self.config_file_cancel = False
- if self.action == 'put' and not self.config['allow_config_upload']:
- files = isinstance(file, list)[file]file
- regexp = re.compile('(^|/|\\\\)(sftp-config(-alt\\d?)?\\.json|sftp-settings\\.json)$')
- for f in files:
- if re.search(regexp, f) is not None:
- self.config_file_cancel = True
- break
- SftpCommand.setup_elements(self.config)
- super(SftpThread, self).__init__()
- return
- @classmethod
- def cleanup(cls):
- for connection_id in SftpCommand.connections:
- connection = SftpCommand.connections[connection_id]
- connection.close()
- def close_connection(self, identifier, dir, window_id, disconnected=False):
- if not SftpCommand.usage.get(identifier):
- return
- SftpCommand.usage[identifier] -= 1
- if SftpCommand.usage[identifier] and not disconnected:
- return
- debug_print('SFTP: Closing unused connection ' + identifier, 2)
- if SftpCommand.connections.get(identifier):
- SftpCommand.connections[identifier].close(disconnected)
- debug_print('SFTP: Closed unused connection ' + identifier, 2)
- del SftpCommand.connections[identifier]
- if SftpCommand.identifiers.get(str(window_id) + '-' + dir):
- del SftpCommand.identifiers[str(window_id) + '-' + dir]
- def kill(self):
- debug_print('SFTP: Killing connection ' + self.connection_id, 2)
- self.connection.close()
- dir = self.config['local_dir']
- dir = str(self.window_id) + '-' + dir
- identifier = SftpCommand.identifiers.get(dir, None)
- if SftpCommand.identifiers.get(dir):
- del SftpCommand.identifiers[dir]
- if identifier:
- del SftpCommand.connections[identifier]
- del SftpCommand.usage[identifier]
- ThreadTracker.set_current(self.window_id, None)
- return
- @unset_current_thread
- def run(self):
- self.start = time.time()
- if self.should_join:
- last_thread = ThreadTracker.get_last_added(self.window_id)
- ThreadTracker.add(self)
- if last_thread is not None:
- debug_print('SFTP: Waiting for previous thread', 2)
- last_thread.join()
- if self.config_file_cancel:
- self.printer.write('\nCanceled since payload included one or more Sublime SFTP configuration files and the "allow_config_upload" is not set to true')
- def do_config_file_cancel():
- message = 'Sublime SFTP\n\nIt appears you are attempting to upload one or more Sublime SFTP config files that may contain sensitive information. To do so, you must set the "allow_config_upload" setting to true.'
- sublime.error_message(message)
- sublime.set_timeout(do_config_file_cancel, 10)
- return
- else:
- debug_print('SFTP: Beginning file transfer thread', 2)
- ThreadActivity(self, self.printer, 'SFTP Working')
- self.printer.reset_hide()
- ThreadTracker.set_current(self.window_id, self)
- try:
- try:
- if not self.make_connection():
- return
- debug_print('SFTP: Checking license key', 2)
- SftpCommand.setup_elements(self.config)
- do_show = False
- if self.action not in (u'list', u'listr', u'llist', u'llistr',
- u'cwd'):
- SftpCommand.elements[0] += 1
- def zip_to_i(element):
- if isinstance(element, int):
- return element
- return ord(element)
- if sys.version_info >= (3, ):
- key_prefix = bytes([zip_to_i(x) ^ zip_to_i(y) for x, y in izip(SftpCommand.elements[1], cycle('22'))])
- else:
- key_prefix = ('').join([chr(zip_to_i(x) ^ zip_to_i(y)) for x, y in izip(SftpCommand.elements[1], cycle('22'))])
- key_prefix = binascii.hexlify(key_prefix)[:30]
- clen = 6
- chunks = [key_prefix[(i - 1) * clen:i * clen] for i in xrange(1, int(len(key_prefix) / clen + 1))]
- key_prefix = ('-').join(chunks).decode('utf-8')
- if SftpCommand.elements[0] > 0 and SftpCommand.elements[0] % 10 == 0 and key_prefix != SftpCommand.elements[-1]:
- def reg():
- if int(sublime.version()) >= 2190:
- if sublime.ok_cancel_dialog(SftpCommand.elements[-2], 'Buy Now'):
- sublime.active_window().run_command('open_url', {'url': 'http://wbond.net/sublime_packages/sftp/buy'})
- else:
- sublime.error_message(SftpCommand.elements[-2])
- sublime.set_timeout(reg, 1)
- do_show = True
- self.do_operation(do_show)
- except (OSError, BinaryMissingError) as e:
- if self.on_fail:
- self.on_fail(e)
- self.close_connection(self.connection_id, self.config['local_dir'], self.window_id, isinstance(e, DisconnectionError))
- if isinstance(e, DisconnectionError):
- try:
- debug_print('SFTP: Reconnecting after disconnection', 2)
- if not self.make_connection():
- return
- self.do_operation()
- except OSError:
- self.printer.write('\nMultiple disconnection errors, giving up')
- except UnicodeDecodeError as e:
- encoding_error(e)
- except LookupError as e:
- encoding_error(e)
- return
- def make_connection(self):
- self.failed = True
- old_identifier = SftpCommand.identifiers.get(str(self.window_id) + '-' + self.config['local_dir'], None)
- if old_identifier is not None and self.connection_id != old_identifier:
- self.close_connection(old_identifier, self.config['local_dir'], self.window_id)
- self.connection = SftpCommand.connections.get(self.connection_id, None)
- if self.connection and self.connection.closed:
- self.close_connection(self.connection_id, self.config['local_dir'], self.window_id)
- self.connection = None
- if self.connection is None:
- class_name = self.config['type'].upper()
- class_object = transports[class_name]
- debug_print('SFTP: Creating file transfer object', 2)
- merged_config = self.config.copy()
- merged_config['remote_time_offset'] = SftpCommand.remote_time_offsets.get(self.connection_id)
- debug_print('SFTP: Configuration\n %s' % repr(merged_config), 2)
- self.connection = class_object(self.printer, **merged_config)
- try:
- debug_print('SFTP: Starting connection', 2)
- self.connection.connect()
- debug_print('SFTP: Successful connected', 2)
- SftpCommand.connections[self.connection_id] = self.connection
- except (AuthenticationError, ConnectionError, BinaryMissingError) as e:
- self.printer.error(e)
- return False
- except OSError as e:
- self.close_connection(self.connection_id, self.config['local_dir'], self.window_id)
- return False
- if str(self.window_id) + '-' + self.config['local_dir'] not in SftpCommand.identifiers:
- SftpCommand.identifiers[str(self.window_id) + '-' + self.config['local_dir']] = self.connection_id
- if self.connection_id not in SftpCommand.usage:
- SftpCommand.usage[self.connection_id] = 0
- SftpCommand.usage[self.connection_id] += 1
- if self.connection_id not in SftpCommand.remote_roots:
- SftpCommand.remote_roots[self.connection_id] = []
- if self.config['remote_dir'] not in SftpCommand.remote_roots[self.connection_id]:
- try:
- progress = ProgressThread(self.printer, '\nValidating remote folder "%s"' % self.config['initial_remote_dir'])
- initial_dir = self.connection.pwd()
- self.connection.cd(self.config['initial_remote_dir'])
- self.connection.ls(self.config['path_map'], config=self.config)
- SftpCommand.remote_roots[self.connection_id].append(self.config['remote_dir'])
- except (NotFoundError, PermissionError) as e:
- progress.stop('failure (' + str(e) + ')')
- progress.join()
- self.connection.remote_time_offset = 0
- output = '\nInitial folder and contents:'
- try:
- for path in self.connection.ls(self.config['path_map'], config=self.config):
- if path[0] == '.':
- path[0] = ''
- output += '\n "%s%s"' % (initial_dir, path[0])
- self.printer.write(output)
- except (ConnectionError, NotFoundError) as e2:
- self.close_connection(self.connection_id, self.config['local_dir'], self.window_id)
- self.printer.write('\nListing initial folder "%s" ... failure (%s)' % (initial_dir, str(e2)))
- self.printer.error(e)
- return False
- except ConnectionError as e:
- progress.stop('failure (%s)' % str(e))
- progress.join()
- self.close_connection(self.connection_id, self.config['local_dir'], self.window_id, True)
- self.printer.error(e)
- return False
- except OSError as e:
- is_ftp = self.config['type'] == 'ftp'
- is_pasv = self.config.get('ftp_passive_mode') is not False
- if isinstance(e, DisconnectionError) and is_ftp and is_pasv:
- e = OSError('Disconnected - possible PASV mode error, try setting ftp_passive_mode to false in sftp-config.json')
- self.close_connection(self.connection_id, self.config['local_dir'], self.window_id)
- progress.stop('failure (%s)' % str(e))
- progress.join()
- raise e
- except (AttributeError, EOFError) as e:
- backtrace = traceback.format_exc()
- handle_exception('Unknown Error', backtrace)
- self.close_connection(self.connection_id, self.config['local_dir'], self.window_id)
- progress.stop('failure (Unknown Error)')
- progress.join()
- return False
- except UnicodeDecodeError:
- self.close_connection(self.connection_id, self.config['local_dir'], self.window_id)
- progress.stop('failure (Encoding error)')
- progress.join()
- raise
- progress.stop('success')
- progress.join()
- if self.connection_id not in SftpCommand.remote_time_offsets:
- SftpCommand.remote_time_offsets[self.connection_id] = self.connection.remote_time_offset
- if self.on_connect:
- self.on_connect()
- self.failed = False
- return True
- def do_operation(self, show=False):
- self.failed = True
- self.connection.debug(get_debug())
- debug_print('SFTP: Starting operation ' + self.action, 2)
- success, result = getattr(self.connection, self.action)(self.file, path_map=self.config['path_map'], chmod_dirs=self.config.get('dir_permissions'), chmod_files=self.config.get('file_permissions'), ignore_regex=self.skip_ignoreself.config.get('ignore_regex')None, quiet=self.quiet, config=self.config, skip_symlinks=self.skip_symlinks, mode=self.mode)
- if show:
- def do_print():
- self.printer.write('\nUNREGISTERED: Please visit http://sublime.wbond.net/sftp')
- sublime.set_timeout(do_print, 1)
- debug_print('SFTP: Finished operation ' + self.action, 2)
- if self.hide:
- sublime.set_timeout(lambda : self.printer.hide(self), 1)
- if self.reset_lcd:
- reset_lcd = self.reset_lcd
- last_reset_lcd = None
- while reset_lcd != last_reset_lcd:
- last_reset_lcd = reset_lcd
- try:
- self.connection.lcd(reset_lcd)
- except NotFoundError:
- reset_lcd = os.path.dirname(reset_lcd)
- if self.on_fail and not success:
- self.on_fail(result)
- if self.on_complete and success:
- sublime.set_timeout(self.on_complete, 1)
- self.result = result
- if success:
- self.failed = False
- return
- class SftpWritePanelCommand(sublime_plugin.TextCommand):
- def run(self, edit):
- printer = PanelPrinter.get(self.view.settings().get('window_id'))
- read_only = self.view.is_read_only()
- if read_only:
- self.view.set_read_only(False)
- keys_to_erase = []
- for key in list(printer.queue):
- while len(printer.strings[key]):
- string = printer.strings[key].pop(0)
- if string is None:
- self.view.erase_regions(key)
- keys_to_erase.append(key)
- continue
- if key == 'sublime_sftp':
- point = self.view.size()
- else:
- regions = self.view.get_regions(key)
- if not len(regions):
- point = self.view.size()
- else:
- region = regions[0]
- point = region.b + 1
- if point == 0 and string[0] == '\n':
- string = string[1:]
- self.view.insert(edit, point, string)
- if key != 'sublime_sftp':
- point = point + len(string) - 1
- region = sublime.Region(point, point)
- self.view.add_regions(key, [region], '')
- continue
- for key in keys_to_erase:
- if key in printer.strings:
- del printer.strings[key]
- try:
- printer.queue.remove(key)
- except ValueError:
- pass
- if read_only:
- self.view.set_read_only(True)
- return
- class SftpInsertViewCommand(sublime_plugin.TextCommand):
- def run(self, edit, position=0, string=''):
- self.view.insert(edit, position, string)
- class SftpReplaceViewCommand(sublime_plugin.TextCommand):
- def run(self, edit, start=0, end=0, string=''):
- self.view.replace(edit, sublime.Region(start, end), string)
- class SftpShowPanelCommand(sublime_plugin.WindowCommand, SftpCommand):
- def run(self):
- PanelPrinter.get(self.window.id()).show(True)
- class SftpCreateServerCommand(sublime_plugin.WindowCommand):
- def run(self):
- config_dir = get_server_config_folder()
- self.window.run_command('new_file_at', {'dirs': [config_dir]})
- view_settings = self.window.active_view().settings()
- view_settings.set('syntax', 'Packages/JavaScript/JSON.tmLanguage')
- view_settings.set('sftp_new_server', True)
- snippet = get_default_config(remove_settings=[
- 'save_before_upload',
- 'upload_on_save',
- 'confirm_sync',
- 'ignore_regex',
- 'ignore_regexes',
- 'confirm_overwrite_newer',
- 'sync_skip_deletes',
- 'confirm_downloads'], force_settings={'sync_down_on_open': True})
- self.window.active_view().run_command('insert_snippet', {'contents': snippet})
- class SftpLastServerCommand(sublime_plugin.WindowCommand):
- def run(self):
- name = BrowsePathThread.get_last()
- if not name:
- return
- self.window.run_command('sftp_browse_server', {'name': name})
- class SftpBrowseServerCommand(sublime_plugin.WindowCommand):
- def run(self, name=None):
- config_dir = get_server_config_folder()
- self.config_dir = config_dir
- servers = []
- for filename in os.listdir(config_dir):
- if filename in (u'.DS_Store', u'Thumbs.db', u'desktop.ini'):
- continue
- if os.path.isdir(os.path.join(config_dir, filename)):
- continue
- config = prepare_server_config(filename)
- if config:
- servers.append(config)
- continue
- self.servers = servers
- choices = [[' ' + r['name'], ' ' + r['desc'] + ' ' + r.get('remote_path', '')] for r in servers]
- choices.insert(0, ['Add New Server...', 'Set up a new SFTP or FTP server to browse'])
- self.window_id = self.window.id()
- self.printer = PanelPrinter.get(self.window_id)
- names = [s['name'] for s in servers]
- if name and name in names:
- self.on_done(names.index(name) + 1)
- return
- show_qp(self.window, choices, self.on_done)
- def on_done(self, index):
- if index == -1:
- return
- elif index == 0:
- self.window.run_command('sftp_create_server')
- return
- else:
- index -= 1
- raw_config = self.servers[index]
- if raw_config is None:
- return
- tmp_dir = setup_tmp_dir(raw_config)
- config = build_config(raw_config, tmp_dir, raw_config['file_path'])
- if not config:
- return
- config['is_tmp'] = True
- config['tmp_dir'] = tmp_dir
- debug_print('SFTP: Starting Browse Path Thread from List', 2)
- BrowsePathThread(config, self.printer, self.window_id, self.window, None).start()
- return
- class SftpEditServerCommand(sublime_plugin.WindowCommand):
- def run(self):
- config_dir = get_server_config_folder()
- self.config_dir = config_dir
- servers = []
- for filename in os.listdir(config_dir):
- if filename in (u'.DS_Store', u'Thumbs.db', u'desktop.ini'):
- continue
- if os.path.isdir(os.path.join(config_dir, filename)):
- continue
- config = prepare_server_config(filename)
- if config:
- servers.append(config)
- continue
- self.servers = servers
- choices = [[' ' + r['name'], ' ' + r['desc'] + ' ' + r.get('remote_path', '')] for r in servers]
- show_qp(self.window, choices, self.on_done)
- def on_done(self, index):
- if index == -1:
- return
- remote = self.servers[index]
- config_path = os.path.join(self.config_dir, remote['name'])
- def open_file():
- self.window.run_command('open_file', {'file': fix_windows_path(config_path)})
- self.window.active_view().settings().set('syntax', 'Packages/JavaScript/JSON.tmLanguage')
- sublime.set_timeout(open_file, 1)
- class SftpDeleteServerCommand(sublime_plugin.WindowCommand):
- def run(self):
- config_dir = get_server_config_folder()
- self.config_dir = config_dir
- servers = []
- for filename in os.listdir(config_dir):
- if filename in (u'.DS_Store', u'Thumbs.db', u'desktop.ini'):
- continue
- if os.path.isdir(os.path.join(config_dir, filename)):
- continue
- config = prepare_server_config(filename)
- if config:
- servers.append(config)
- continue
- self.servers = servers
- choices = [[' ' + r['name'], ' ' + r['desc'] + ' ' + r.get('remote_path', '')] for r in servers]
- show_qp(self.window, choices, self.on_done)
- def on_done(self, index):
- if index == -1:
- return
- remote = self.servers[index]
- self.config_path = os.path.join(self.config_dir, remote['name'])
- choices = [
- [
- 'Yes', 'Delete %s server' % remote['name']],
- [
- 'No', 'Do not delete %s server' % remote['name']]]
- show_qp(self.window, choices, self.confirm_delete)
- def confirm_delete(self, index):
- if index == -1 or index == 1:
- return
- os.unlink(self.config_path)
- class QuickPanelBrowser(object):
- def cleanup(self):
- if 'is_tmp' not in self.config:
- return
- tmp_dir = dirname(self.config['local_dir'])
- if os.path.exists(tmp_dir):
- shutil.rmtree(tmp_dir)
- class BrowsePathThread(HookedThread, QuickPanelBrowser):
- last_name = None
- @classmethod
- def get_last(cls):
- return cls.last_name
- def __init__(self, config, printer, window_id, window, remote_path, second_time=False):
- self.config = config
- self.printer = printer
- self.window_id = window_id
- self.window = window
- self.remote_path = remote_path
- super(BrowsePathThread, self).__init__()
- def run(self):
- BrowsePathThread.last_name = self.config['name']
- if self.remote_path is None:
- pwd_thread = SftpThread(self.window_id, self.config, None, 'cwd', hide=False)
- pwd_thread.start()
- pwd_thread.join()
- if pwd_thread.failed:
- sublime.set_timeout(self.printer.show, 1)
- self.cleanup()
- return
- self.remote_path = pwd_thread.result
- reset_lcd = None
- if self.config.get('is_tmp'):
- reset_lcd = dirname(dirname(self.config['local_dir']))
- list_dir_thread = SftpThread(self.window_id, self.config, self.remote_path, 'list', reset_lcd=reset_lcd, hide=False, skip_ignore=True, skip_symlinks=False)
- list_dir_thread.start()
- list_dir_thread.join()
- if list_dir_thread.failed and list_dir_thread.result in (u'File not found',
- u'Folder not found',
- u'Permission denied'):
- debug_print('SFTP: Starting Browse Thread after Error', 2)
- BrowsePathThread(self.config, self.printer, self.window_id, self.window, dirname(self.remote_path), second_time=True).start()
- return
- elif list_dir_thread.failed:
- sublime.set_timeout(self.printer.show, 1)
- self.cleanup()
- return
- else:
- self.files = []
- self.entries = []
- listing = [p[0] for p in list_dir_thread.result if is_dir(p[0])]
- listing.extend([p[0] for p in list_dir_thread.result if not is_dir(p[0]) and p[0] != '.'])
- if list_dir_thread.result:
- self.files.extend([p for p in listing])
- self.entries.extend([' ' + p for p in listing if is_dir(p)])
- self.entries.extend([' ' + p for p in listing if not is_dir(p)])
- self.files.insert(0, '')
- self.entries.insert(0, '' + self.config['host'] + ':' + self.remote_path)
- if not is_root(self.remote_path):
- self.files.insert(1, '..')
- self.entries.insert(1, ' Up a folder')
- self.files.insert(1, '.')
- self.entries.insert(1, ' Folder actions')
- self.existing_files = [
- '.', '..']
- if list_dir_thread.result:
- self.existing_files.extend([p[0].rstrip('/\\') for p in list_dir_thread.result])
- sublime.set_timeout(self.show_files, 1)
- return
- def show_files(self):
- show_qp(self.window, self.entries, self.show_files_action)
- def custom_path(self):
- self.window.show_input_panel('Browse to', self.remote_path, self.browse_to, None, self.show_files)
- return
- def browse_to(self, input):
- if len(input) == 0:
- input = '/'
- input = canonicalize(input, 'remote')
- debug_print('SFTP: Starting Custom Browse Path Thread', 2)
- BrowsePathThread(self.config, self.printer, self.window_id, self.window, input).start()
- def show_files_action(self, index):
- if index == -1:
- self.cleanup()
- return
- else:
- selected = self.files[index]
- new_path = None
- if selected == '':
- self.custom_path()
- return
- if selected == '..':
- new_path = dirname(self.remote_path)
- else:
- if selected == '.':
- self.selected_path = self.remote_path
- self.modify_dir()
- return
- if not is_dir(selected):
- self.selected_path = os.path.join(self.remote_path, selected)
- self.modify_file()
- return
- new_path = os.path.join(self.remote_path, selected)
- debug_print('SFTP: Starting Browse Sub-path Thread', 2)
- BrowsePathThread(self.config, self.printer, self.window_id, self.window, new_path).start()
- return
- def modify_dir(self):
- actions = [
- '' + self.config['host'] + ':' + self.selected_path,
- ' Back to list',
- ' New file',
- ' New folder',
- ' Rename',
- ' Chmod',
- ' Delete']
- if not self.config.get('is_tmp'):
- actions.insert(4, ' Download')
- show_qp(self.window, actions, self.modify_dir_action)
- def modify_dir_action(self, index):
- if index == -1:
- self.cleanup()
- return
- elif index == 0 or index == 1:
- self.show_files()
- return
- else:
- is_tmp = self.config.get('is_tmp')
- if index == 2:
- self.window.show_input_panel('New file name', '', self.new_file, None, self.modify_dir)
- if index == 3:
- self.window.show_input_panel('New folder name', '', self.new_folder, None, self.modify_dir)
- if index == 4 and not is_tmp:
- debug_print('SFTP: Starting Download Folder Thread', 2)
- DownloadPathThread(self.config, self.printer, self.window_id, self.window, self.selected_path).start()
- if not is_tmp and index == 5 or is_tmp and index == 4:
- self.window.show_input_panel('New name', os.path.basename(self.selected_path.rstrip('/\\')), self.rename_folder, None, self.modify_dir)
- if not is_tmp and index == 6 or is_tmp and index == 5:
- self.window.show_input_panel('New permissions', '755', self.chmod_folder, None, self.modify_dir)
- if not is_tmp and index == 7 or is_tmp and index == 6:
- name = canonicalize(os.path.basename(self.selected_path.rstrip('/\\')), 'remote')
- choices = [
- [
- 'Yes', 'Delete %s and all children' % name],
- [
- 'No', 'Do not delete %s' % name]]
- show_qp(self.window, choices, self.confirm_delete_dir)
- return
- def new_file(self, new_name):
- if new_name.find('\\') != -1 or new_name.find('/') != -1:
- def try_again_slash():
- sublime.error_message('Sublime SFTP\n\nFile name may not contain slashes')
- self.window.show_input_panel('New file name', '', self.new_file, None, self.modify_dir)
- return
- sublime.set_timeout(try_again_slash, 1)
- return
- if new_name in self.existing_files:
- def try_again_existing():
- sublime.error_message('Sublime SFTP\n\nA file or folder with that name specified already exists')
- self.window.show_input_panel('New file name', '', self.new_file, None, self.modify_dir)
- return
- sublime.set_timeout(try_again_existing, 1)
- return
- new_path = os.path.join(self.selected_path, new_name)
- debug_print('SFTP: Starting New File Thread', 2)
- NewFileThread(self.config, self.printer, self.window_id, self.window, new_path).start()
- def new_folder(self, new_name):
- if new_name.find('\\') != -1 or new_name.find('/') != -1:
- def try_again_slash():
- sublime.error_message('Sublime SFTP\n\nFolder name may not contain slashes')
- self.window.show_input_panel('New folder name', '', self.new_folder, None, self.modify_dir)
- return
- sublime.set_timeout(try_again_slash, 1)
- return
- if new_name in self.existing_files:
- def try_again_existing():
- sublime.error_message('Sublime SFTP\n\nA file or folder with that name specified already exists')
- self.window.show_input_panel('New folder name', '', self.new_folder, None, self.modify_dir)
- return
- sublime.set_timeout(try_again_existing, 1)
- return
- new_path = os.path.join(self.selected_path, new_name)
- debug_print('SFTP: Starting New Folder Thread', 2)
- NewFolderThread(self.config, self.printer, self.window_id, self.window, new_path).start()
- def rename_folder(self, new_name):
- if new_name == os.path.basename(self.selected_path.rstrip('\\/')):
- self.cleanup()
- return
- if new_name.find('\\') != -1 or new_name.find('/') != -1:
- def try_again():
- sublime.error_message('Sublime SFTP\n\nFolder name may not contain slashes')
- self.window.show_input_panel('New name', os.path.basename(self.selected_path.rstrip('/\\')), self.rename_folder, None, self.modify_dir)
- return
- sublime.set_timeout(try_again, 1)
- return
- new_path = os.path.join(dirname(self.selected_path), new_name)
- new_path = canonicalize(new_path, 'remote')
- debug_print('SFTP: Starting Rename Folder Thread', 2)
- RenamePathThread(self.config, self.printer, self.window_id, self.window, self.selected_path, new_path).start()
- def chmod_folder(self, mode):
- debug_print('SFTP: Starting Chmod Folder Thread', 2)
- ChmodPathThread(self.config, self.printer, self.window_id, self.window, self.selected_path, mode).start()
- def confirm_delete_dir(self, index):
- if index == -1 or index == 1:
- self.modify_dir()
- return
- DeletePathThread(self.config, self.printer, self.window_id, self.window, self.selected_path).start()
- def modify_file(self):
- actions = [
- '' + self.config['host'] + ':' + self.selected_path,
- ' Back to list',
- ' Edit',
- ' Rename',
- ' Chmod',
- ' Delete']
- if not self.config.get('is_tmp'):
- actions[2] += ' (remote version)'
- actions.insert(3, ' Download')
- show_qp(self.window, actions, self.modify_file_action)
- def modify_file_action(self, index):
- if index == -1:
- self.cleanup()
- return
- else:
- is_tmp = self.config.get('is_tmp')
- if index == 0 or index == 1:
- self.show_files()
- return
- if index == 2:
- debug_print('SFTP: Starting Edit File Thread', 2)
- EditFileThread(self.config, self.printer, self.window_id, self.window, self.selected_path).start()
- return
- if index == 3 and not is_tmp:
- debug_print('SFTP: Starting Download File Thread', 2)
- DownloadPathThread(self.config, self.printer, self.window_id, self.window, self.selected_path).start()
- if not is_tmp and index == 4 or is_tmp and index == 3:
- self.window.show_input_panel('New name', os.path.basename(self.selected_path.rstrip('/\\')), self.rename_file, None, self.modify_file)
- if not is_tmp and index == 5 or is_tmp and index == 4:
- self.window.show_input_panel('New permissions', '644', self.chmod_file, None, self.modify_file)
- if not is_tmp and index == 6 or is_tmp and index == 5:
- name = os.path.basename(self.selected_path)
- choices = [
- [
- 'Yes', 'Delete %s' % name],
- [
- 'No', 'Do not delete %s' % name]]
- show_qp(self.window, choices, self.confirm_delete_file)
- return
- def rename_file(self, new_name):
- if new_name == os.path.basename(self.selected_path.rstrip('\\/')):
- self.cleanup()
- return
- if new_name.find('\\') != -1 or new_name.find('/') != -1:
- def try_again():
- sublime.error_message('Sublime SFTP\n\nFile name may not contain slashes')
- self.window.show_input_panel('New name', os.path.basename(self.selected_path.rstrip('/\\')), self.rename_path, None, self.modify_file)
- return
- sublime.set_timeout(try_again, 1)
- return
- new_path = os.path.join(dirname(self.selected_path), new_name)
- debug_print('SFTP: Starting Rename File Thread', 2)
- RenamePathThread(self.config, self.printer, self.window_id, self.window, self.selected_path, new_path).start()
- def chmod_file(self, mode):
- debug_print('SFTP: Starting Chmod File Thread', 2)
- ChmodPathThread(self.config, self.printer, self.window_id, self.window, self.selected_path, mode).start()
- def confirm_delete_file(self, index):
- if index == -1 or index == 1:
- self.modify_file()
- return
- debug_print('SFTP: Starting Delete File Thread', 2)
- DeletePathThread(self.config, self.printer, self.window_id, self.window, self.selected_path).start()
- class DownloadPathThread(HookedThread):
- def __init__(self, config, printer, window_id, window, remote_path, on_complete=None):
- self.config = config
- self.printer = printer
- self.window_id = window_id
- self.window = window
- self.remote_path = remote_path
- self.on_complete = on_complete
- super(DownloadPathThread, self).__init__()
- def run(self):
- sublime.set_timeout(self.printer.show, 1)
- if not is_dir(self.remote_path):
- download_thread = SftpThread(self.window_id, self.config, self.remote_path, 'get', should_join=False)
- download_thread.start()
- download_thread.join()
- local_path = remote_to_local(self.remote_path, self.config['path_map'])
- if not download_thread.failed:
- def open_file():
- self.window.run_command('open_file', {'file': fix_windows_path(local_path)})
- if self.on_complete:
- self.on_complete()
- sublime.set_timeout(open_file, 1)
- else:
- BrowsePathThread(self.config, self.printer, self.window_id, self.window, dirname(self.remote_path)).start()
- else:
- download_thread = DownloadFolderThread(self.window_id, self.config, self.remote_path)
- download_thread.start()
- download_thread.join()
- if download_thread.error:
- sublime.set_timeout(self.printer.show, 1)
- BrowsePathThread(self.config, self.printer, self.window_id, self.window, self.remote_path).start()
- class EditFileThread(HookedThread):
- def __init__(self, config, printer, window_id, window, remote_path):
- self.config = config
- self.printer = printer
- self.window_id = window_id
- self.window = window
- self.remote_path = remote_path
- self.local_path = None
- if not config.get('tmp_dir'):
- self.local_path = remote_to_local(remote_path, config.get('path_map'))
- raw_config, config_file = load_config(self.config['local_dir'])
- tmp_dir = setup_tmp_dir(raw_config)
- raw_config, config_file = load_config(tmp_dir)
- new_config = build_config(raw_config, tmp_dir, config_file)
- new_config['tmp_dir'] = tmp_dir
- self.config = new_config
- super(EditFileThread, self).__init__()
- return
- def run(self):
- config = self.config.copy()
- config['path_map'] = {config['tmp_dir']: config['remote_dir']}
- local_path = remote_to_local(self.remote_path, config['path_map'])
- sublime.set_timeout(self.printer.show, 1)
- download_thread = SftpThread(self.window_id, config, self.remote_path, 'get', should_join=False, reset_lcd=dirname(dirname(config['local_dir'])))
- download_thread.start()
- download_thread.join()
- if not download_thread.failed:
- def open_file():
- self.window.run_command('open_file', {'file': fix_windows_path(local_path)})
- view = self.window.active_view()
- if self.local_path:
- view.settings().set('local_path', self.local_path)
- view.settings().set('remote_loading', True)
- view.settings().set('synced', True)
- view.settings().set('is_remote', True)
- view.settings().set('tmp_dir', self.config['tmp_dir'])
- sublime.set_timeout(open_file, 1)
- else:
- BrowsePathThread(self.config, self.printer, self.window_id, self.window, dirname(self.remote_path)).start()
- class NewFileThread(HookedThread):
- def __init__(self, config, printer, window_id, window, remote_path):
- self.config = config
- self.printer = printer
- self.window_id = window_id
- self.window = window
- self.remote_path = remote_path
- super(NewFileThread, self).__init__()
- def run(self):
- local_path = remote_to_local(self.remote_path, self.config['path_map'])
- if not os.path.exists(dirname(local_path)):
- os.makedirs(dirname(local_path))
- open(local_path, 'a').close()
- sublime.set_timeout(self.printer.show, 1)
- reset_lcd = None
- if self.config.get('is_tmp'):
- reset_lcd = dirname(dirname(self.config['local_dir']))
- mv_thread = SftpThread(self.window_id, self.config, local_path, 'put', reset_lcd=reset_lcd)
- mv_thread.start()
- mv_thread.join()
- if not mv_thread.failed:
- def status():
- sublime.status_message('%s successfully created' % path_type(self.remote_path, True))
- self.window.run_command('open_file', {'file': fix_windows_path(local_path)})
- view = self.window.active_view()
- if 'is_tmp' in self.config:
- view.settings().set('is_remote', True)
- view.settings().set('tmp_dir', self.config['local_dir'])
- sublime.set_timeout(status, 1)
- else:
- BrowsePathThread(self.config, self.printer, self.window_id, self.window, dirname(self.remote_path)).start()
- return
- class NewFolderThread(HookedThread):
- def __init__(self, config, printer, window_id, window, remote_path):
- self.config = config
- self.printer = printer
- self.window_id = window_id
- self.window = window
- self.remote_path = remote_path
- super(NewFolderThread, self).__init__()
- def run(self):
- local_path = remote_to_local(self.remote_path, self.config['path_map'])
- os.makedirs(local_path)
- sublime.set_timeout(self.printer.show, 1)
- reset_lcd = None
- if self.config.get('is_tmp'):
- reset_lcd = dirname(dirname(self.config['local_dir']))
- mv_thread = SftpThread(self.window_id, self.config, local_path, 'put', reset_lcd=reset_lcd)
- mv_thread.start()
- mv_thread.join()
- if not mv_thread.failed:
- def status():
- sublime.status_message('Folder successfully created')
- sublime.set_timeout(status, 1)
- BrowsePathThread(self.config, self.printer, self.window_id, self.window, dirname(self.remote_path)).start()
- return
- class ChmodPathThread(HookedThread):
- def __init__(self, config, printer, window_id, window, remote_path, mode):
- self.config = config
- self.printer = printer
- self.window_id = window_id
- self.window = window
- self.remote_path = remote_path
- self.mode = mode
- super(ChmodPathThread, self).__init__()
- def run(self):
- sublime.set_timeout(self.printer.show, 1)
- reset_lcd = None
- if self.config.get('is_tmp'):
- reset_lcd = dirname(dirname(self.config['local_dir']))
- chmod_thread = SftpThread(self.window_id, self.config, self.remote_path, 'chmod', reset_lcd=reset_lcd, mode=self.mode)
- chmod_thread.start()
- chmod_thread.join()
- if not chmod_thread.failed:
- def status():
- sublime.status_message('%s successfully chmodded' % path_type(self.remote_path, True))
- sublime.set_timeout(status, 1)
- BrowsePathThread(self.config, self.printer, self.window_id, self.window, dirname(self.remote_path)).start()
- return
- class RenamePathThread(HookedThread):
- def __init__(self, config, printer, window_id, window, remote_path, new_remote_path):
- self.config = config
- self.printer = printer
- self.window_id = window_id
- self.window = window
- self.remote_path = remote_path
- self.new_remote_path = new_remote_path
- super(RenamePathThread, self).__init__()
- def run(self):
- sublime.set_timeout(self.printer.show, 1)
- reset_lcd = None
- if self.config.get('is_tmp'):
- reset_lcd = dirname(dirname(self.config['local_dir']))
- mv_thread = SftpThread(self.window_id, self.config, [
- self.remote_path, self.new_remote_path], 'mv', reset_lcd=reset_lcd)
- mv_thread.start()
- mv_thread.join()
- if not mv_thread.failed:
- def status():
- sublime.status_message('%s successfully renamed' % path_type(self.remote_path, True))
- sublime.set_timeout(status, 1)
- BrowsePathThread(self.config, self.printer, self.window_id, self.window, dirname(self.remote_path)).start()
- return
- class DeletePathThread(HookedThread):
- def __init__(self, config, printer, window_id, window, remote_path):
- self.config = config
- self.printer = printer
- self.window_id = window_id
- self.window = window
- self.remote_path = remote_path
- super(DeletePathThread, self).__init__()
- def run(self):
- if is_dir(self.remote_path):
- reset_lcd = None
- if self.config.get('is_tmp'):
- reset_lcd = dirname(dirname(self.config['local_dir']))
- list_thread = SftpThread(self.window_id, self.config, self.remote_path, 'listr', should_join=False, reset_lcd=reset_lcd, hide=False, skip_symlinks='file')
- list_thread.start()
- list_thread.join()
- if list_thread.failed or not list_thread.result:
- return
- remote_paths = [p[0] for p in list_thread.result[::-1]]
- else:
- remote_paths = self.remote_path
- sublime.set_timeout(self.printer.show, 1)
- reset_lcd = None
- if self.config.get('is_tmp'):
- reset_lcd = dirname(dirname(self.config['local_dir']))
- rm_thread = SftpThread(self.window_id, self.config, remote_paths, 'rm', reset_lcd=reset_lcd)
- rm_thread.start()
- rm_thread.join()
- if not rm_thread.failed:
- def status():
- sublime.status_message('%s successfully deleted' % path_type(self.remote_path, True))
- sublime.set_timeout(status, 1)
- BrowsePathThread(self.config, self.printer, self.window_id, self.window, dirname(self.remote_path)).start()
- return
- class SftpBrowseCommand(sublime_plugin.WindowCommand, SftpCommand):
- def run(self, paths=None, group=None, index=None, reset_lcd=None):
- config = self.get_config(paths)
- if not config:
- return
- else:
- if paths is None:
- active_view = self.window.active_view()
- if active_view and active_view.settings().get('is_remote') and config.get('is_server'):
- self.window.run_command('sftp_browse_server', {'name': config.get('name')})
- return
- printer = PanelPrinter.get(self.window.id())
- path = self.get_path(paths)
- if path is None:
- return
- if not os.path.isdir(path):
- path = os.path.dirname(path)
- path = canonicalize(path, 'local')
- remote_path = local_to_remote(path, config['path_map'])
- debug_print('SFTP: Starting Browse Thread', 2)
- BrowsePathThread(config, printer, self.window.id(), self.window, remote_path).start()
- return
- def is_visible(self, paths=None):
- path = self.get_path(paths)
- if not path:
- return False
- return self.has_config(path)
- class SftpUploadFileCommand(sublime_plugin.WindowCommand, SftpCommand):
- def run(self, paths=None, group=None, index=None, reset_lcd=None):
- if paths is None and group is not None and index is not None:
- selected_view = get_view_by_group_index(self.window, group, index)
- paths = [selected_view.file_name()]
- config = self.get_config(paths)
- if not config:
- return
- files = self.get_path(paths, True)
- filtered_files = []
- def filter_file(file_):
- if os.path.exists(file_):
- filtered_files.append(file_)
- else:
- debug_print('SFTP: Skipping File Not Present on Disk', 2)
- if isinstance(files, list):
- for file_ in files:
- filter_file(file_)
- else:
- filter_file(files)
- if filtered_files:
- filtered_files = filtered_files[0]
- files = filtered_files
- if not files:
- sublime.error_message('Sublime SFTP\n\nUnable to upload file since it is not present on disk')
- return
- if config['save_before_upload']:
- self.save_files(files)
- printer = PanelPrinter.get(self.window.id())
- printer.show()
- if config['confirm_overwrite_newer']:
- debug_print('SFTP: Starting Confirm Overwrite File Thread', 2)
- ConfirmOverwriteFileThread(self.window, printer, config, files, reset_lcd=reset_lcd).start()
- else:
- debug_print('SFTP: Starting Upload File Thread', 2)
- SftpThread(self.window.id(), config, files, 'put', reset_lcd=reset_lcd).start()
- return
- def is_visible(self, paths=None):
- path = self.get_path(paths)
- if not path or os.path.isdir(path):
- return False
- return self.has_config(path)
- class ConfirmOverwriteFileThread(HookedThread, SyncThread):
- def __init__(self, window, printer, config, files, reset_lcd):
- self.window = window
- self.window_id = window.id()
- self.printer = printer
- self.config = config
- if not isinstance(files, list):
- files = [
- files]
- self.files = files
- self.reset_lcd = reset_lcd
- super(ConfirmOverwriteFileThread, self).__init__()
- def run(self):
- last_thread = ThreadTracker.get_last_added(self.window_id)
- ThreadTracker.add(self)
- if last_thread is not None:
- last_thread.join()
- path_map = self.config['path_map']
- for path in self.files:
- remote_path = local_to_remote(path, path_map, self.config['remote_encoding'])
- nonlocal_ = {'progress': None}
- def on_connect():
- nonlocal_['progress'] = ProgressThread(self.printer, '\nChecking modification date of "%s"' % remote_path)
- def on_fail(e):
- if not nonlocal_['progress']:
- return
- nonlocal_['progress'].stop('failure (%s)' % str(e))
- nonlocal_['progress'].join()
- dir = canonicalize(dirname(path), 'local')
- remote_dir = local_to_remote(dir, path_map, self.config['remote_encoding'])
- list_dir_thread = SftpThread(self.window_id, self.config, remote_dir, 'list', should_join=False, hide=False, on_connect=on_connect, on_fail=on_fail, skip_symlinks='file')
- list_dir_thread.start()
- list_dir_thread.join()
- def upload_file():
- debug_print('SFTP: Starting Confirmed Upload File Thread', 2)
- put_thread = SftpThread(self.window_id, self.config, path, 'put', should_join=False, hide=False, reset_lcd=self.reset_lcd)
- put_thread.start()
- put_thread.join()
- if list_dir_thread.failed and list_dir_thread.result in (u'File not found',
- u'Folder not found'):
- upload_file()
- continue
- if list_dir_thread.failed:
- return
- if nonlocal_['progress']:
- nonlocal_['progress'].stop('success')
- nonlocal_['progress'].join()
- remote_paths = list_dir_thread.result
- if list_dir_thread.failed or remote_paths is None:
- remote_paths = []
- remote_dict = dict(remote_paths)
- remote_mod_time = remote_dict.get(os.path.basename(remote_path), None)
- local_mod_time = int(os.lstat(path)[8])
- if not remote_mod_time or local_mod_time >= remote_mod_time:
- upload_file()
- continue
- nonlocal_ = {'confirmed': None}
- def on_confirm():
- nonlocal_['confirmed'] = True
- def on_reject():
- nonlocal_['confirmed'] = False
- local_time = self.make_time(local_mod_time)
- remote_time = self.make_time(remote_mod_time)
- operations = [
- 'Upload local "%s" (%s) over remote "%s" [%s vs. %s]' % (
- self.strip(path, dir, 'local'),
- time_diff(local_mod_time, remote_mod_time),
- self.strip(remote_path, remote_dir, 'remote'),
- local_time, remote_time)]
- self.confirm(operations, on_confirm, on_reject)
- while True:
- if nonlocal_['confirmed'] is not None:
- break
- time.sleep(0.01)
- if nonlocal_['confirmed']:
- upload_file()
- continue
- def do_hide():
- self.printer.hide()
- sublime.set_timeout(do_hide, 1)
- return
- class SftpMonitorFileCommand(sublime_plugin.WindowCommand, SftpCommand):
- def run(self, paths=None):
- config = self.get_config(paths)
- if not config:
- return
- printer = PanelPrinter.get(self.window.id())
- path = self.get_path(paths)
- if not path:
- return
- view = self.window.active_view()
- if view.id() not in [v.id for v in self.window.views()]:
- file_name = fix_windows_path(view.file_name())
- self.window.run_command('open_file', {'file': file_name})
- if not view.get_status('sftp_monitor'):
- debug_print('SFTP: Starting File Monitoring', 2)
- sftp_settings = sublime.load_settings('SFTP.sublime-settings')
- frequency = sftp_settings.get('monitoring_frequency', 200)
- frequency = float(frequency) / 1000.0
- delay = sftp_settings.get('monitoring_upload_delay', 500)
- delay = float(delay) / 1000.0
- view.set_status('sftp_monitor', 'SFTP: Monitoring')
- MonitorFileThread(self.window, view, printer, config, path, frequency, delay).start()
- else:
- view.erase_status('sftp_monitor')
- def is_visible(self, paths=None):
- if paths is None:
- active_view = self.window.active_view()
- if active_view and active_view.settings().get('is_remote'):
- return False
- path = self.get_path(paths)
- if not path or os.path.isdir(path):
- return False
- else:
- return self.has_config(path)
- class MonitorFileThread(HookedThread):
- def __init__(self, window, view, printer, config, path, frequency, delay):
- self.window = window
- self.window_id = window.id()
- self.view = view
- self.printer = printer
- self.config = config
- self.path = path
- self.frequency = frequency
- self.delay = delay
- super(MonitorFileThread, self).__init__()
- def run(self):
- nonlocal_ = {'cont': True}
- try:
- uploaded_mod_time = int(os.lstat(self.path)[8])
- except OSError as e:
- debug_print('SFTP: Monitoring Error - %s' % str(e), 2)
- nonlocal_['cont'] = False
- num = 0
- while 1:
- if nonlocal_['cont']:
- try:
- mod_time = int(os.lstat(self.path)[8])
- except OSError as e:
- debug_print('SFTP: Monitoring Error - %s' % str(e), 2)
- break
- if mod_time > uploaded_mod_time:
- time.sleep(self.delay)
- def show():
- self.printer.show()
- sublime.set_timeout(show, 1)
- debug_print('SFTP: Starting Monitored Upload File Thread', 2)
- SftpThread(self.window_id, self.config, self.path, 'put').start()
- uploaded_mod_time = mod_time
- num = (num + 1) % 5
- if num == 4:
- def check_view():
- if self.view is None or not self.view.get_status('sftp_monitor'):
- nonlocal_['cont'] = False
- return
- sublime.set_timeout(check_view, 1)
- time.sleep(self.frequency)
- def stop_monitoring():
- debug_print('SFTP: Stoping File Monitoring', 2)
- self.view.erase_status('sftp_monitor')
- sublime.set_timeout(stop_monitoring, 1)
- class SftpUploadOpenFilesCommand(sublime_plugin.WindowCommand, SftpCommand):
- def run(self, paths=None):
- debug_print('SFTP: Starting Upload Open Files Command', 2)
- for view in self.window.views():
- path = view.file_name()
- if not path:
- debug_print('SFTP: Skipping View Without File Name', 2)
- continue
- if not os.path.exists(path):
- debug_print('SFTP: Skipping File Not Present on Disk', 2)
- continue
- paths = [
- path]
- if not self.has_config(path):
- debug_print('SFTP: Skipping View Without Remote', 2)
- continue
- config = self.get_config(paths)
- if not config:
- continue
- PanelPrinter.get(self.window.id()).show()
- files = self.get_path(paths, True)
- if config['save_before_upload']:
- self.save_files(files)
- debug_print('SFTP: Starting Upload File Thread', 2)
- SftpThread(self.window.id(), config, files, 'put').start()
- def is_visible(self, paths=None):
- if paths is None:
- active_view = self.window.active_view()
- if active_view and active_view.settings().get('is_remote'):
- return False
- path = self.get_path(paths)
- if not path or os.path.isdir(path):
- return False
- else:
- return self.has_config(path)
- class SftpDiffRemoteFileCommand(sublime_plugin.WindowCommand, SftpCommand):
- def run(self, paths=None):
- config = self.get_config(paths)
- if not config:
- return
- printer = PanelPrinter.get(self.window.id())
- printer.show()
- file = self.get_path(paths)
- remote_file = local_to_remote(file, config['path_map'])
- local_path = list(config['path_map'].keys())[0]
- remote_path = config['path_map'][local_path]
- tmp_dir = os.path.join(tempfile.gettempdir(), 'sublime-sftp-diff-') + str(int(time.time()))
- if not os.path.exists(tmp_dir):
- os.makedirs(tmp_dir)
- config['path_map'] = {tmp_dir: remote_path}
- tmp_file = remote_to_local(remote_file, config['path_map'])
- tmp_file_direct_parent = dirname(tmp_file)
- if not os.path.exists(tmp_file_direct_parent):
- os.makedirs(tmp_file_direct_parent)
- debug_print('SFTP: Starting Diff Remote Thread', 2)
- SftpThread(self.window.id(), config, remote_file, 'get', reset_lcd=dirname(tmp_dir), on_complete=lambda : self.complete(printer, file, remote_file, tmp_dir, tmp_file)).start()
- def complete(self, printer, file, remote_file, tmp_dir, tmp_file):
- printer.show()
- progress = ProgressThread(printer, '\nDiffing "%s" with "%s"' % (file, remote_file))
- sftp_settings = sublime.load_settings('SFTP.sublime-settings')
- if sftp_settings.get('diff_command'):
- diff_command_args = sftp_settings.get('diff_command')
- diff_thread = DiffCommandThread(diff_command_args, file, tmp_file, tmp_dir, sftp_settings.get('delete_temp_diff_folder', True))
- debug_print('SFTP: Starting Diff Command Thread', 2)
- diff_thread.start()
- progress.stop('success (external diff launched)')
- progress.join()
- else:
- settings = sublime.load_settings('Base File.sublime-settings')
- fallback_encoding = settings.get('fallback_encoding')
- fallback_encoding = re.sub('^[a-zA-Z ]*\\((.*)\\)$', '\\1', fallback_encoding)
- try:
- file_lines = codecs.open(file, 'r', 'utf-8').read().splitlines()
- except UnicodeDecodeError:
- debug_print('SFTP: Using fallback encoding "%s" when reading local file for diff' % fallback_encoding, 2)
- file_lines = codecs.open(file, 'r', fallback_encoding).read().splitlines()
- try:
- tmp_file_lines = codecs.open(tmp_file, 'r', 'utf-8').read().splitlines()
- except UnicodeDecodeError:
- debug_print('SFTP: Using fallback encoding "%s" when reading remote file for diff' % fallback_encoding, 2)
- tmp_file_lines = codecs.open(tmp_file, 'r', fallback_encoding).read().splitlines()
- file_date = time.ctime(os.stat(file).st_mtime)
- tmp_file_date = time.ctime(os.stat(tmp_file).st_mtime)
- diff = difflib.unified_diff(file_lines, tmp_file_lines, file, tmp_file, file_date, tmp_file_date, lineterm='')
- diff = ('\n').join([line for line in diff])
- if diff == '':
- progress.stop('success (no changes)')
- printer.reset_hide()
- else:
- name = os.path.basename(file)
- new_view = self.window.new_file()
- new_view.set_name('(local) ' + name + ' -> (remote) ' + name)
- new_view.set_scratch(True)
- new_view.set_syntax_file('Packages/Diff/Diff.tmLanguage')
- new_view.run_command('sftp_insert_view', {'position': 0, 'string': diff})
- progress.stop('success')
- progress.join()
- if os.path.exists(tmp_dir):
- try:
- shutil.rmtree(tmp_dir)
- except WindowsError:
- pass
- def is_visible(self, paths=None):
- if paths is None:
- active_view = self.window.active_view()
- if active_view and active_view.settings().get('is_remote'):
- return False
- path = self.get_path(paths)
- if not path or os.path.isdir(path):
- return False
- else:
- return self.has_config(path)
- class DiffCommandThread(HookedThread):
- def __init__(self, args, file, tmp_file, tmp_dir, delete_tmp_dir):
- self.args = args
- self.file = file
- self.tmp_file = tmp_file
- self.tmp_dir = tmp_dir
- self.delete_tmp_dir = delete_tmp_dir
- super(DiffCommandThread, self).__init__()
- def run(self):
- args = []
- for arg in self.args:
- if arg == '%1$s':
- arg = self.file
- else:
- if arg == '%2$s':
- arg = self.tmp_file
- args.append(arg)
- proc = subprocess.Popen(args)
- proc.wait()
- if self.delete_tmp_dir and os.path.exists(self.tmp_dir):
- try:
- shutil.rmtree(self.tmp_dir)
- except WindowsError:
- pass
- class SftpRenameLocalAndRemotePathsCommand(sublime_plugin.WindowCommand, SftpCommand):
- def run(self, paths=None, files=None, dirs=None):
- if paths is None and files is not None:
- paths = files
- if paths is None and dirs is not None:
- paths = dirs
- config = self.get_config(paths)
- if not config:
- return
- else:
- printer = PanelPrinter.get(self.window.id())
- path = self.get_path(paths)
- remote_path = local_to_remote(path, config['path_map'])
- def prompt_new_name():
- self.window.show_input_panel('New Name', os.path.basename(path.rstrip('/\\')), on_done, None, None)
- return
- def on_done(new_name):
- if new_name.find('\\') != -1 or new_name.find('/') != -1:
- sublime.error_message('Sublime SFTP\n\n%s name may not contain slashes' % path_type(path))
- prompt_new_name()
- return
- new_path = dirname(path) + new_name
- if os.path.exists(new_path):
- sublime.error_message('Sublime SFTP\n\nA file or folder with that name specified already exists')
- prompt_new_name()
- return
- new_remote_path = dirname(remote_path) + new_name
- printer.show()
- RenameLocalAndRemotePathThread(self.window, config, path, new_path, remote_path, new_remote_path).start()
- prompt_new_name()
- return
- def is_visible(self, paths=None, files=None, dirs=None):
- if paths is None and files is None and dirs is None:
- active_view = self.window.active_view()
- if active_view and active_view.settings().get('is_remote'):
- return False
- if dirs == [] or files == []:
- return False
- if paths is None and files is not None:
- paths = files
- if paths is None and dirs is not None:
- paths = dirs
- path = self.get_path(paths)
- if not path:
- return False
- else:
- return self.has_config(path)
- class RenameLocalAndRemotePathThread(HookedThread):
- def __init__(self, window, config, path, new_path, remote_path, new_remote_path):
- self.window = window
- self.window_id = window.id()
- self.config = config
- self.path = path
- self.new_path = new_path
- self.remote_path = remote_path
- self.new_remote_path = new_remote_path
- super(RenameLocalAndRemotePathThread, self).__init__()
- def run(self):
- last_thread = ThreadTracker.get_last_added(self.window_id)
- ThreadTracker.add(self)
- if last_thread is not None:
- last_thread.join()
- mv_thread = SftpThread(self.window_id, self.config, [
- self.remote_path, self.new_remote_path], 'mv', should_join=False, reset_lcd=dirname(self.config['local_dir']))
- mv_thread.start()
- mv_thread.join()
- if mv_thread.failed:
- return
- else:
- os.rename(self.path, self.new_path)
- if int(sublime.version()) < 2178:
- return
- def do_retarget():
- view = self.window.find_open_file(self.path)
- if view:
- view.retarget(self.new_path)
- sublime.set_timeout(do_retarget, 1)
- return
- class SftpDeleteLocalAndRemotePathsCommand(sublime_plugin.WindowCommand, SftpCommand):
- def run(self, paths=None, files=None, dirs=None):
- if paths is None and files is not None:
- paths = files
- if paths is None and dirs is not None:
- paths = dirs
- config = self.get_config(paths)
- if not config:
- return
- else:
- printer = PanelPrinter.get(self.window.id())
- path = self.get_path(paths)
- if os.path.isdir(path):
- path = canonicalize(path, 'local')
- remote_path = local_to_remote(path, config['path_map'])
- type_ = path_type(remote_path)
- def on_done(index):
- if index == -1 or index == 1:
- return
- printer.show()
- DeleteLocalAndRemotePathThread(self.window, config, path, remote_path).start()
- choices = [
- [
- 'Yes', 'Delete local and remote %ss %s' % (type_, os.path.basename(remote_path.rstrip('/\\')))],
- [
- 'No', 'Do not delete local and remote %ss %s' % (type_, os.path.basename(remote_path.rstrip('/\\')))]]
- show_qp(self.window, choices, on_done)
- return
- def is_visible(self, paths=None, files=None, dirs=None):
- if paths is None and files is None and dirs is None:
- active_view = self.window.active_view()
- if active_view and active_view.settings().get('is_remote'):
- return False
- if dirs == [] or files == []:
- return False
- if paths is None and files is not None:
- paths = files
- if paths is None and dirs is not None:
- paths = dirs
- path = self.get_path(paths)
- if not path:
- return False
- else:
- return self.has_config(path)
- class DeleteLocalAndRemotePathThread(HookedThread):
- def __init__(self, window, config, path, remote_path):
- self.window_id = window.id()
- self.config = config
- self.path = path
- self.remote_path = remote_path
- super(DeleteLocalAndRemotePathThread, self).__init__()
- def run(self):
- last_thread = ThreadTracker.get_last_added(self.window_id)
- ThreadTracker.add(self)
- if last_thread is not None:
- last_thread.join()
- if is_dir(self.remote_path):
- list_thread = SftpThread(self.window_id, self.config, self.remote_path, 'listr', should_join=False, hide=False, skip_symlinks='file')
- list_thread.start()
- list_thread.join()
- if list_thread.failed and list_thread.result in (u'File not found', u'Folder not found'):
- remote_paths = []
- else:
- if list_thread.failed or not list_thread.result:
- return
- remote_paths = [p[0] for p in list_thread.result[::-1]]
- else:
- remote_paths = self.remote_path
- if remote_paths:
- rm_thread = SftpThread(self.window_id, self.config, remote_paths, 'rm', should_join=False)
- rm_thread.start()
- rm_thread.join()
- if rm_thread.failed and rm_thread.result not in (u'File not found', u'Folder not found'):
- return
- if is_dir(self.path):
- shutil.rmtree(self.path)
- else:
- os.remove(self.path)
- return
- class SftpDeleteRemotePathCommand(sublime_plugin.WindowCommand, SftpCommand):
- def run(self, paths=None, files=None, dirs=None):
- if paths is None and files is not None:
- paths = files
- if paths is None and dirs is not None:
- paths = dirs
- config = self.get_config(paths)
- if not config:
- return
- else:
- printer = PanelPrinter.get(self.window.id())
- path = self.get_path(paths)
- remote_path = local_to_remote(path, config['path_map'])
- type_ = path_type(remote_path)
- def on_done(index):
- if index == -1 or index == 1:
- return
- printer.show()
- DeleteRemotePathThread(self.window, config, remote_path).start()
- choices = [
- [
- 'Yes', 'Delete remote %s %s' % (type_, os.path.basename(remote_path.rstrip('/\\')))],
- [
- 'No', 'Do not delete remote %s %s' % (type_, os.path.basename(remote_path.rstrip('/\\')))]]
- show_qp(self.window, choices, on_done)
- return
- def is_visible(self, paths=None, files=None, dirs=None):
- if paths is None and files is None and dirs is None:
- active_view = self.window.active_view()
- if active_view and active_view.settings().get('is_remote'):
- return False
- if dirs == [] or files == []:
- return False
- if paths is None and files is not None:
- paths = files
- if paths is None and dirs is not None:
- paths = dirs
- path = self.get_path(paths)
- if not path:
- return False
- else:
- return self.has_config(path)
- class DeleteRemotePathThread(HookedThread):
- def __init__(self, window, config, remote_path):
- self.window_id = window.id()
- self.config = config
- self.remote_path = remote_path
- super(DeleteRemotePathThread, self).__init__()
- def run(self):
- last_thread = ThreadTracker.get_last_added(self.window_id)
- ThreadTracker.add(self)
- if last_thread is not None:
- last_thread.join()
- if is_dir(self.remote_path):
- list_thread = SftpThread(self.window_id, self.config, self.remote_path, 'listr', should_join=False, hide=False, skip_symlinks='file')
- list_thread.start()
- list_thread.join()
- if list_thread.failed or not list_thread.result:
- return
- remote_paths = [p[0] for p in list_thread.result[::-1]]
- else:
- remote_paths = self.remote_path
- rm_thread = SftpThread(self.window_id, self.config, remote_paths, 'rm', should_join=False)
- rm_thread.start()
- rm_thread.join()
- return
- class SftpDownloadFileCommand(sublime_plugin.WindowCommand, SftpCommand):
- def run(self, paths=None):
- config = self.get_config(paths)
- if not config:
- return
- else:
- self.printer = PanelPrinter.get(self.window.id())
- file = self.get_path(paths)
- view = self.window.active_view()
- self.on_complete = None
- if view and view.file_name() == file:
- self.on_complete = lambda : view.run_command('revert')
- self.remote_file = local_to_remote(file, config['path_map'])
- self.config = config
- if not config['confirm_downloads']:
- self.do_download()
- return
- debug_print('SFTP: Starting Confirm Download File Thread', 2)
- basename = os.path.basename(file)
- if os.name != 'nt':
- basename = unicodedata.normalize('NFC', basename)
- choices = [['Yes', 'Download the file %s' % basename],
- [
- 'No', 'Do not download the file %s' % basename]]
- def on_choose(index):
- if index == -1 or index == 1:
- return
- self.do_download()
- show_qp(self.window, choices, on_choose)
- return
- def do_download(self):
- self.printer.show()
- debug_print('SFTP: Starting Download File Thread', 2)
- SftpThread(self.window.id(), self.config, self.remote_file, 'get', on_complete=self.on_complete).start()
- def is_visible(self, paths=None):
- path = self.get_path(paths)
- if not path or os.path.isdir(path):
- return False
- return self.has_config(path)
- class SftpUploadFolderCommand(sublime_plugin.WindowCommand, SftpCommand):
- def run(self, paths=None):
- config = self.get_config(paths)
- if not config:
- return
- dir = self.get_path(paths)
- if not os.path.isdir(dir):
- dir = dirname(dir)
- dir = canonicalize(dir, 'local')
- debug_print('SFTP: Starting Upload Folder Command Thread', 2)
- UploadFolderThread(self.window, config, dir, self.save_files).start()
- def is_visible(self, paths=None):
- if paths is None:
- active_view = self.window.active_view()
- if active_view and active_view.settings().get('is_remote'):
- return False
- path = self.get_path(paths)
- if not path:
- return False
- else:
- return self.has_config(path)
- class UploadFolderThread(HookedThread):
- def __init__(self, window, config, dir, save_files):
- self.config = config
- self.printer = PanelPrinter.get(window.id())
- self.window_id = window.id()
- self.dir = dir
- self.save_files = save_files
- super(UploadFolderThread, self).__init__()
- def run(self):
- last_thread = ThreadTracker.get_last_added(self.window_id)
- ThreadTracker.add(self)
- if last_thread is not None:
- last_thread.join()
- def do_run():
- self.printer.show()
- basename = os.path.basename(self.dir.rstrip('/\\'))
- progress = ProgressThread(self.printer, '\nUploading folder "%s"' % basename)
- paths = [
- self.dir]
- for root, dirs, files in os.walk(self.dir):
- paths.extend([os.path.join(root, path) for path in dirs])
- paths.extend([os.path.join(root, path) for path in files])
- paths, to_upload, ignored = ignore_paths(paths, self.config)
- paths.sort()
- message = '%d %s to upload' % (to_upload, 'files' if to_upload != 1 else 'file')
- if ignored:
- message += ', %d ignored' % ignored
- progress.stop(message)
- progress.join()
- if paths:
- if self.config['save_before_upload']:
- self.save_files(paths)
- debug_print('SFTP: Starting Upload Folder Thread', 2)
- SftpThread(self.window_id, self.config, paths, 'put').start()
- else:
- self.printer.hide()
- sublime.set_timeout(do_run, 10)
- return
- class SftpSyncUpCommand(sublime_plugin.WindowCommand, SftpCommand):
- def run(self, paths=None, ignore_delete=False, on_complete=None):
- config = self.get_config(paths)
- if not config:
- return
- printer = PanelPrinter.get(self.window.id())
- printer.show()
- path = self.get_path(paths)
- if os.path.isdir(path):
- path = canonicalize(path, 'local')
- if os.name != 'nt':
- path = unicodedata.normalize('NFC', path)
- if config.get('sync_skip_deletes'):
- ignore_delete = True
- debug_print('SFTP: Starting Sync Up Command Thread', 2)
- SyncUpThread(self.window, config, path, self.save_files, ignore_delete, on_complete).start()
- def is_visible(self, paths=None):
- if paths is None:
- active_view = self.window.active_view()
- if active_view and active_view.settings().get('is_remote'):
- return False
- path = self.get_path(paths)
- if not path:
- return False
- else:
- return self.has_config(path)
- class SyncUpThread(HookedThread, SyncThread):
- def __init__(self, window, config, path, save_files, ignore_delete, on_complete):
- self.config = config
- self.printer = PanelPrinter.get(window.id())
- self.window = window
- self.window_id = window.id()
- self.path = path
- self.save_files = save_files
- self.ignore_delete = ignore_delete
- self.on_complete = on_complete
- super(SyncUpThread, self).__init__()
- def run(self):
- last_thread = ThreadTracker.get_last_added(self.window_id)
- ThreadTracker.add(self)
- if last_thread is not None:
- last_thread.join()
- path_map = self.config['path_map']
- path = self.path
- remote_path = local_to_remote(path, path_map, self.config['remote_encoding'])
- list_operation = 'listr' if is_dir(path) else 'list'
- nonlocal_ = {'progress': None}
- def on_connect():
- nonlocal_['progress'] = ProgressThread(self.printer, '\nDetermining operations to sync local path "%s" up to remote path "%s"' % (path, remote_path))
- def on_fail(e):
- if nonlocal_['progress']:
- nonlocal_['progress'].stop('failure (%s)' % str(e))
- nonlocal_['progress'].join()
- dir = path if is_dir(path) else dirname(path)
- remote_dir = local_to_remote(dir, path_map, self.config['remote_encoding'])
- try:
- list_dir_thread = SftpThread(self.window_id, self.config, remote_dir, list_operation, should_join=False, hide=False, on_connect=on_connect, on_fail=on_fail, quiet=True, skip_symlinks=False)
- list_dir_thread.start()
- list_dir_thread.join()
- except UnicodeDecodeError as e:
- if nonlocal_['progress']:
- nonlocal_['progress'].stop('failure (Encoding error)')
- nonlocal_['progress'].join()
- encoding_error(e)
- return
- if list_dir_thread.failed and list_dir_thread.result in (u'File not found',
- u'Folder not found'):
- remote_paths = []
- else:
- if list_dir_thread.failed:
- return
- remote_paths = list_dir_thread.result
- list_dir_thread = SftpThread(self.window_id, self.config, dir, 'l' + list_operation, should_join=False, hide=False)
- list_dir_thread.start()
- list_dir_thread.join()
- if list_dir_thread.failed and list_dir_thread.result in (u'File not found',
- u'Folder not found'):
- local_paths = []
- else:
- if list_dir_thread.failed:
- return
- local_paths = list_dir_thread.result
- if not is_dir(path):
- local_paths = [[os.path.join(dir, _path[0]), _path[1]] for _path in local_paths if os.path.join(dir, _path[0]) == path]
- remote_paths = [[os.path.join(remote_dir, _path[0]), _path[1]] for _path in remote_paths if os.path.join(remote_dir, _path[0]) == remote_path]
- local_dict = dict(local_paths)
- remote_dict = dict(remote_paths)
- debug_print('SFTP: Sync Up Local Files\n %s' % repr(local_paths), 2)
- debug_print('SFTP: Sync Up Remote Files\n %s' % repr(remote_paths), 2)
- to_rm = set([path_[0] for path_ in remote_paths])
- to_put = []
- to_put_new = []
- to_put_overwrite = []
- for local_path, local_time in local_paths:
- remote_path = local_to_remote(local_path, path_map)
- remote_time = remote_dict.get(remote_path)
- if remote_time is None:
- to_put_new.append(local_path)
- to_put.append(local_path)
- else:
- if remote_time <= local_time and not is_dir(local_path):
- to_put_overwrite.append(local_path)
- to_put.append(local_path)
- to_rm = to_rm - set([remote_path])
- to_rm = list(to_rm)
- to_rm = sorted(to_rm, key=lambda s: s.lower())
- to_put, num_to_put, num_to_put_ignored = ignore_paths(to_put, self.config)
- to_put_new, num_to_put, num_to_put_ignored = ignore_paths(to_put_new, self.config)
- to_put_overwrite, num_to_put, num_to_put_ignored = ignore_paths(to_put_overwrite, self.config)
- to_rm, num_to_rm, num_to_rm_ignored = ignore_rm_paths(to_rm, self.config, 'remote')
- if self.ignore_delete:
- to_rm = []
- operations = []
- for local_path in to_put:
- local_time = self.make_time(local_dict.get(local_path))
- remote_path = local_to_remote(local_path, path_map)
- remote_time = self.make_time(remote_dict.get(remote_path))
- if is_dir(remote_path):
- operation = 'Create remote "%s"' % self.strip(remote_path, remote_dir, 'remote')
- else:
- if remote_time == 'None':
- operation = 'Upload local "%s" to remote "%s"' % (
- self.strip(local_path, dir, 'local'),
- self.strip(remote_path, remote_dir, 'remote'))
- else:
- diff = time_diff(local_dict.get(local_path), remote_dict.get(remote_path))
- if diff == 'same age' and not self.config['sync_same_age']:
- to_put_overwrite.remove(local_path)
- continue
- operation = 'Upload local "%s" (%s) over remote "%s" [%s vs. %s]' % (
- self.strip(local_path, dir, 'local'),
- diff, self.strip(remote_path, remote_dir, 'remote'),
- local_time, remote_time)
- operations.append(operation)
- operations += ['Delete remote path "%s"' % self.strip(path_, remote_dir, 'remote') for path_ in to_rm]
- if operations:
- if nonlocal_['progress']:
- nonlocal_['progress'].stop('success')
- nonlocal_['progress'].join()
- def handle_yes():
- failed = False
- if to_put_new:
- put_thread = SftpThread(self.window_id, self.config, to_put_new, 'put', should_join=False)
- put_thread.start()
- put_thread.join()
- failed = failed or put_thread.failed
- if to_put_overwrite:
- put_thread = SftpThread(self.window_id, self.config, to_put_overwrite, 'put', should_join=False)
- put_thread.start()
- put_thread.join()
- failed = failed or put_thread.failed
- if to_rm:
- to_rm.reverse()
- rm_thread = SftpThread(self.window_id, self.config, to_rm, 'rm', should_join=False)
- rm_thread.start()
- rm_thread.join()
- failed = failed or rm_thread.failed
- if not failed and self.on_complete:
- self.on_complete()
- if not failed:
- sublime.set_timeout(lambda : self.printer.hide(), 1)
- if self.config.get('confirm_sync'):
- def handle_no():
- sublime.set_timeout(lambda : self.printer.hide(), 1)
- self.confirm(operations, handle_yes, handle_no, should_join=True)
- else:
- handle_yes()
- else:
- if nonlocal_['progress']:
- nonlocal_['progress'].stop('success (No operations to perform)')
- nonlocal_['progress'].join()
- sublime.set_timeout(lambda : self.printer.hide(), 1)
- return
- class SftpSyncDownCommand(sublime_plugin.WindowCommand, SftpCommand):
- def run(self, paths=None, ignore_delete=False, on_complete=None, reset_lcd=None, synchronous=False):
- config = self.get_config(paths)
- if not config:
- return
- printer = PanelPrinter.get(self.window.id())
- printer.show()
- path = self.get_path(paths)
- if os.path.isdir(path):
- path = canonicalize(path, 'local')
- if os.name != 'nt':
- path = unicodedata.normalize('NFC', path)
- if config.get('sync_skip_deletes'):
- ignore_delete = True
- debug_print('SFTP: Starting Sync Down Command Thread', 2)
- SyncDownThread(self.window, config, path, self.save_files, ignore_delete, on_complete, reset_lcd, synchronous).start()
- def is_visible(self, paths=None):
- if paths is None:
- active_view = self.window.active_view()
- if active_view and active_view.settings().get('is_remote'):
- return False
- path = self.get_path(paths)
- if not path:
- return False
- else:
- return self.has_config(path)
- class SyncDownThread(HookedThread, SyncThread):
- def __init__(self, window, config, path, save_files, ignore_delete, on_complete, reset_lcd, synchronous):
- self.config = config
- self.printer = PanelPrinter.get(window.id())
- self.window = window
- self.window_id = window.id()
- self.path = path
- self.save_files = save_files
- self.ignore_delete = ignore_delete
- self.on_complete = on_complete
- self.reset_lcd = reset_lcd
- self.synchronous = synchronous
- super(SyncDownThread, self).__init__()
- def run(self):
- last_thread = ThreadTracker.get_last_added(self.window_id)
- ThreadTracker.add(self)
- if last_thread is not None:
- last_thread.join()
- path_map = self.config['path_map']
- path = self.path
- remote_path = local_to_remote(path, path_map, self.config['remote_encoding'])
- list_operation = 'listr' if is_dir(path) else 'list'
- nonlocal_ = {'progress': None}
- def on_connect():
- nonlocal_['progress'] = ProgressThread(self.printer, '\nDetermining operations to sync remote path "%s" down to local path "%s"' % (remote_path, path))
- def on_fail(e):
- if nonlocal_['progress']:
- nonlocal_['progress'].stop('failure (%s)' % str(e))
- nonlocal_['progress'].join()
- dir = path if is_dir(path) else canonicalize(dirname(path), 'local')
- remote_dir = local_to_remote(dir, path_map, self.config['remote_encoding'])
- list_dir_thread = SftpThread(self.window_id, self.config, remote_dir, list_operation, should_join=False, hide=False, on_connect=on_connect, on_fail=on_fail, quiet=True, skip_symlinks=False)
- list_dir_thread.start()
- list_dir_thread.join()
- if list_dir_thread.failed and list_dir_thread.result in (u'File not found',
- u'Folder not found'):
- remote_paths = []
- else:
- if list_dir_thread.failed:
- return
- remote_paths = list_dir_thread.result
- list_dir_thread = SftpThread(self.window_id, self.config, dir, 'l' + list_operation, should_join=False, hide=False)
- list_dir_thread.start()
- list_dir_thread.join()
- if list_dir_thread.failed and list_dir_thread.result in (u'File not found',
- u'Folder not found'):
- local_paths = []
- else:
- if list_dir_thread.failed:
- return
- local_paths = list_dir_thread.result
- if not is_dir(path):
- local_paths = [[os.path.join(dir, _path[0]), _path[1]] for _path in local_paths if os.path.join(dir, _path[0]) == path]
- remote_paths = [[os.path.join(remote_dir, _path[0]), _path[1]] for _path in remote_paths if os.path.join(remote_dir, _path[0]) == remote_path]
- local_dict = dict(local_paths)
- remote_dict = dict(remote_paths)
- debug_print('SFTP: Sync Down Local Files\n %s' % repr(local_paths), 2)
- debug_print('SFTP: Sync Down Remote Files\n %s' % repr(remote_paths), 2)
- to_rm = set([path_[0] for path_ in local_paths])
- to_get = []
- to_get_new = []
- to_get_overwrite = []
- for remote_path, remote_time in remote_paths:
- local_path = remote_to_local(remote_path, path_map)
- local_time = local_dict.get(local_path)
- if local_time is None:
- to_get_new.append(remote_path)
- to_get.append(remote_path)
- else:
- if local_time <= remote_time and not is_dir(local_path):
- to_get_overwrite.append(remote_path)
- to_get.append(remote_path)
- to_rm = to_rm - set([local_path])
- to_rm = list(to_rm)
- to_rm = sorted(to_rm, key=lambda s: s.lower())
- to_get, num_to_get, num_to_get_ignored = ignore_paths(to_get, self.config)
- to_get_new, num_to_get, num_to_get_ignored = ignore_paths(to_get_new, self.config)
- to_get_overwrite, num_to_get, num_to_get_ignored = ignore_paths(to_get_overwrite, self.config)
- to_rm, num_to_rm, num_to_rm_ignored = ignore_rm_paths(to_rm, self.config, 'local')
- if self.ignore_delete:
- to_rm = []
- operations = []
- for remote_path in to_get:
- remote_time = self.make_time(remote_dict.get(remote_path))
- local_path = remote_to_local(remote_path, path_map)
- local_time = self.make_time(local_dict.get(local_path))
- if is_dir(local_path):
- operation = 'Create local "%s"' % self.strip(local_path, dir, 'local')
- else:
- if local_time == 'None':
- operation = 'Download remote "%s" to "%s"' % (
- self.strip(remote_path, remote_dir, 'remote'),
- self.strip(local_path, dir, 'local'))
- else:
- diff = time_diff(remote_dict.get(remote_path), local_dict.get(local_path))
- if diff == 'same age' and not self.config['sync_same_age']:
- to_get_overwrite.remove(remote_path)
- continue
- operation = 'Download remote "%s" (%s) over local "%s" [%s vs. %s]' % (
- self.strip(remote_path, remote_dir, 'remote'),
- diff, self.strip(local_path, dir, 'local'),
- remote_time, local_time)
- operations.append(operation)
- operations += ['Delete local "%s"' % self.strip(path_, dir, 'local') for path_ in to_rm]
- if operations:
- if nonlocal_['progress']:
- nonlocal_['progress'].stop('success')
- nonlocal_['progress'].join()
- nonlocal_['done'] = False
- def handle_yes():
- failed = False
- if to_get_new:
- get_thread = SftpThread(self.window_id, self.config, to_get_new, 'get', should_join=False, reset_lcd=self.reset_lcd)
- get_thread.start()
- get_thread.join()
- failed = failed or get_thread.failed
- if to_get_overwrite:
- get_thread = SftpThread(self.window_id, self.config, to_get_overwrite, 'get', should_join=False, reset_lcd=self.reset_lcd)
- get_thread.start()
- get_thread.join()
- failed = failed or get_thread.failed
- if to_rm:
- to_rm.reverse()
- rm_thread = SftpThread(self.window_id, self.config, to_rm, 'lrm', should_join=False)
- rm_thread.start()
- rm_thread.join()
- failed = failed or rm_thread.failed
- if not failed and self.on_complete:
- if self.on_complete == 'open_refresh':
- def do_refresh():
- if not to_get:
- return
- local_path = remote_to_local(to_get[0], path_map)
- self.window.run_command('open_file', {'file': fix_windows_path(local_path)})
- self.window.active_view().run_command('revert')
- sublime.set_timeout(do_refresh, 1)
- else:
- self.on_complete()
- if not failed:
- sublime.set_timeout(lambda : self.printer.hide(), 1)
- nonlocal_['done'] = True
- if self.config.get('confirm_sync'):
- def handle_no():
- sublime.set_timeout(lambda : self.printer.hide(), 1)
- nonlocal_['done'] = True
- self.confirm(operations, handle_yes, handle_no, should_join=not self.synchronous)
- if self.synchronous:
- while True:
- if nonlocal_['done']:
- break
- time.sleep(0.01)
- else:
- handle_yes()
- else:
- if nonlocal_['progress']:
- nonlocal_['progress'].stop('success (No operations to perform)')
- nonlocal_['progress'].join()
- sublime.set_timeout(lambda : self.printer.hide(), 1)
- return
- class SftpSyncBothCommand(sublime_plugin.WindowCommand, SftpCommand):
- def run(self, paths=None, on_complete=None):
- config = self.get_config(paths)
- if not config:
- return
- printer = PanelPrinter.get(self.window.id())
- printer.show()
- path = self.get_path(paths)
- if os.path.isdir(path):
- path = canonicalize(path, 'local')
- if os.name != 'nt':
- path = unicodedata.normalize('NFC', path)
- debug_print('SFTP: Starting Sync Both Command Thread', 2)
- SyncBothThread(self.window, config, path, self.save_files, on_complete).start()
- def is_visible(self, paths=None):
- if paths is None:
- active_view = self.window.active_view()
- if active_view and active_view.settings().get('is_remote'):
- return False
- path = self.get_path(paths)
- if not path:
- return False
- else:
- return self.has_config(path)
- class SyncBothThread(HookedThread, SyncThread):
- def __init__(self, window, config, path, save_files, on_complete):
- self.config = config
- self.printer = PanelPrinter.get(window.id())
- self.window = window
- self.window_id = window.id()
- self.path = path
- self.save_files = save_files
- self.on_complete = on_complete
- super(SyncBothThread, self).__init__()
- def run(self):
- last_thread = ThreadTracker.get_last_added(self.window_id)
- ThreadTracker.add(self)
- if last_thread is not None:
- last_thread.join()
- path_map = self.config['path_map']
- path = self.path
- remote_path = local_to_remote(path, path_map, self.config['remote_encoding'])
- nonlocal_ = {'progress': None}
- def on_connect():
- nonlocal_['progress'] = ProgressThread(self.printer, '\nDetermining operations to sync local path "%s" both directions with remote path "%s"' % (path, remote_path))
- def on_fail(e):
- if nonlocal_['progress']:
- nonlocal_['progress'].stop('failure (%s)' % str(e))
- nonlocal_['progress'].join()
- dir = path if is_dir(path) else dirname(path)
- remote_dir = local_to_remote(dir, path_map, self.config['remote_encoding'])
- list_dir_thread = SftpThread(self.window_id, self.config, remote_dir, 'listr', should_join=False, hide=False, on_connect=on_connect, on_fail=on_fail, quiet=True, skip_symlinks=False)
- list_dir_thread.start()
- list_dir_thread.join()
- if list_dir_thread.failed and list_dir_thread.result in (u'File not found',
- u'Folder not found'):
- remote_paths = []
- else:
- if list_dir_thread.failed:
- return
- remote_paths = list_dir_thread.result
- list_dir_thread = SftpThread(self.window_id, self.config, dir, 'llistr', should_join=False, hide=False)
- list_dir_thread.start()
- list_dir_thread.join()
- if list_dir_thread.failed and list_dir_thread.result in (u'File not found',
- u'Folder not found'):
- local_paths = []
- else:
- if list_dir_thread.failed:
- return
- local_paths = list_dir_thread.result
- if not is_dir(path):
- local_paths = [_path for _path in local_paths if _path[0] == path]
- remote_paths = [_path for _path in remote_paths if _path[0] == remote_path]
- local_dict = dict(local_paths)
- remote_dict = dict(remote_paths)
- debug_print('SFTP: Sync Both Local Files\n %s' % repr(local_paths), 2)
- debug_print('SFTP: Sync Both Remote Files\n %s' % repr(remote_paths), 2)
- to_put = []
- to_put_new = []
- to_put_overwrite = []
- to_get = []
- to_get_new = []
- to_get_overwrite = []
- for local_path, local_time in local_paths:
- remote_path = local_to_remote(local_path, path_map)
- remote_time = remote_dict.get(remote_path)
- if remote_time is None:
- to_put_new.append(local_path)
- to_put.append(local_path)
- elif remote_time <= local_time and not is_dir(local_path):
- to_put_overwrite.append(local_path)
- to_put.append(local_path)
- continue
- for remote_path, remote_time in remote_paths:
- local_path = remote_to_local(remote_path, path_map)
- local_time = local_dict.get(local_path)
- if local_time is None:
- to_get_new.append(remote_path)
- to_get.append(remote_path)
- elif local_time < remote_time and not is_dir(remote_path):
- to_get_overwrite.append(remote_path)
- to_get.append(remote_path)
- continue
- to_put, num_to_put, num_to_put_ignored = ignore_paths(to_put, self.config)
- to_put_new, num_to_put, num_to_put_ignored = ignore_paths(to_put_new, self.config)
- to_put_overwrite, num_to_put, num_to_put_ignored = ignore_paths(to_put_overwrite, self.config)
- to_get, num_to_get, num_to_get_ignored = ignore_paths(to_get, self.config)
- to_get_new, num_to_get, num_to_get_ignored = ignore_paths(to_get_new, self.config)
- to_get_overwrite, num_to_get, num_to_get_ignored = ignore_paths(to_get_overwrite, self.config)
- operations = []
- for local_path in to_put:
- local_time = self.make_time(local_dict.get(local_path))
- remote_path = local_to_remote(local_path, path_map)
- remote_time = self.make_time(remote_dict.get(remote_path))
- if is_dir(remote_path):
- operation = 'Create remote "%s"' % self.strip(remote_path, remote_dir, 'remote')
- else:
- if remote_time == 'None':
- operation = 'Upload local "%s" to remote "%s"' % (
- self.strip(local_path, dir, 'local'),
- self.strip(remote_path, remote_dir, 'remote'))
- else:
- diff = time_diff(local_dict.get(local_path), remote_dict.get(remote_path))
- if diff == 'same age' and not self.config['sync_same_age']:
- to_put_overwrite.remove(local_path)
- continue
- operation = 'Upload local "%s" (%s) over remote "%s" [%s vs. %s]' % (
- self.strip(local_path, dir, 'local'),
- diff, self.strip(remote_path, remote_dir, 'remote'),
- local_time, remote_time)
- operations.append(operation)
- for remote_path in to_get:
- remote_time = self.make_time(remote_dict.get(remote_path))
- local_path = remote_to_local(remote_path, path_map)
- local_time = self.make_time(local_dict.get(local_path))
- if is_dir(local_path):
- operation = 'Create local "%s"' % self.strip(local_path, dir, 'local')
- else:
- if local_time == 'None':
- operation = 'Download remote "%s" to "%s"' % (
- self.strip(remote_path, remote_dir, 'remote'),
- self.strip(local_path, dir, 'local'))
- else:
- diff = time_diff(remote_dict.get(remote_path), local_dict.get(local_path))
- if diff == 'same age' and not self.config['sync_same_age']:
- to_get_overwrite.remove(remote_path)
- continue
- operation = 'Download remote "%s" (%s) over local "%s" [%s vs. %s]' % (
- self.strip(remote_path, remote_dir, 'remote'),
- diff, self.strip(local_path, dir, 'local'),
- remote_time, local_time)
- operations.append(operation)
- if operations:
- if nonlocal_['progress']:
- nonlocal_['progress'].stop('success')
- nonlocal_['progress'].join()
- def handle_yes():
- failed = False
- if to_put_new:
- put_thread = SftpThread(self.window_id, self.config, to_put_new, 'put', should_join=False)
- put_thread.start()
- put_thread.join()
- failed = failed or put_thread.failed
- if to_put_overwrite:
- put_thread = SftpThread(self.window_id, self.config, to_put_overwrite, 'put', should_join=False)
- put_thread.start()
- put_thread.join()
- failed = failed or put_thread.failed
- if to_get_new:
- get_thread = SftpThread(self.window_id, self.config, to_get_new, 'get', should_join=False)
- get_thread.start()
- get_thread.join()
- failed = failed or get_thread.failed
- if to_get_overwrite:
- get_thread = SftpThread(self.window_id, self.config, to_get_overwrite, 'get', should_join=False)
- get_thread.start()
- get_thread.join()
- failed = failed or get_thread.failed
- if not failed and self.on_complete:
- self.on_complete()
- if not failed:
- sublime.set_timeout(lambda : self.printer.hide(), 1)
- if self.config.get('confirm_sync'):
- def handle_no():
- sublime.set_timeout(lambda : self.printer.hide(), 1)
- self.confirm(operations, handle_yes, handle_no, should_join=True)
- else:
- handle_yes()
- else:
- if nonlocal_['progress']:
- nonlocal_['progress'].stop('success (No operations to perform)')
- nonlocal_['progress'].join()
- sublime.set_timeout(lambda : self.printer.hide(), 1)
- return
- class SftpDownloadFolderCommand(sublime_plugin.WindowCommand, SftpCommand):
- def run(self, paths=None):
- config = self.get_config(paths)
- if not config:
- return
- self.printer = PanelPrinter.get(self.window.id())
- dir = self.get_path(paths)
- if not os.path.isdir(dir):
- dir = dirname(dir)
- self.remote_dir = local_to_remote(dir, config['path_map'])
- self.config = config
- if not config['confirm_downloads']:
- self.do_download()
- return
- debug_print('SFTP: Starting Confirm Download Folder Thread', 2)
- basename = os.path.basename(dir)
- if os.name != 'nt':
- basename = unicodedata.normalize('NFC', basename)
- choices = [['Yes', 'Download the folder %s' % basename],
- [
- 'No', 'Do not download the folder %s' % basename]]
- def on_choose(index):
- if index == -1 or index == 1:
- return
- self.do_download()
- show_qp(self.window, choices, on_choose)
- def do_download(self):
- self.printer.show()
- debug_print('SFTP: Starting Download Folder Command Thread', 2)
- DownloadFolderThread(self.window.id(), self.config, self.remote_dir).start()
- def is_visible(self, paths=None):
- if paths is None:
- active_view = self.window.active_view()
- if active_view and active_view.settings().get('is_remote'):
- return False
- path = self.get_path(paths)
- if not path:
- return False
- else:
- return self.has_config(path)
- class DownloadFolderThread(HookedThread):
- def __init__(self, window_id, config, remote_dir):
- self.config = config
- self.printer = PanelPrinter.get(window_id)
- self.window_id = window_id
- self.remote_dir = remote_dir
- self.error = False
- super(DownloadFolderThread, self).__init__()
- def run(self):
- last_thread = ThreadTracker.get_last_added(self.window_id)
- ThreadTracker.add(self)
- if last_thread is not None:
- last_thread.join()
- nonlocal_ = {'progress': None}
- def on_connect():
- nonlocal_['progress'] = ProgressThread(self.printer, '\nDownloading folder "%s"' % self.remote_dir)
- list_dir_thread = SftpThread(self.window_id, self.config, self.remote_dir, 'listr', should_join=False, on_connect=on_connect, hide=False, skip_symlinks=False)
- list_dir_thread.start()
- list_dir_thread.join()
- remote_paths = list_dir_thread.result
- if remote_paths is not None:
- remote_paths = [path[0] for path in remote_paths]
- remote_paths, to_download, ignored = ignore_paths(remote_paths, self.config)
- message = ' %d %s to download' % (to_download, 'files' if to_download != 1 else 'file')
- if ignored:
- message += ', %d ignored' % ignored
- else:
- message = 'failure (Error)'
- if nonlocal_['progress']:
- nonlocal_['progress'].stop(message)
- nonlocal_['progress'].join()
- if remote_paths:
- debug_print('SFTP: Starting Download Folder Thread', 2)
- download_thread = SftpThread(self.window_id, self.config, remote_paths, 'get', should_join=False)
- download_thread.start()
- download_thread.join()
- self.error = download_thread.failed
- else:
- sublime.set_timeout(lambda : self.printer.hide(), 1)
- return
- class SftpVcsChangedFilesCommand(sublime_plugin.WindowCommand, SftpCommand):
- def run(self):
- config = self.get_config()
- if not config:
- return
- else:
- printer = PanelPrinter.get(self.window.id())
- printer.show()
- vcs = None
- path = self.get_path()
- settings = sublime.load_settings('SFTP.sublime-settings')
- vcses = [
- (
- SVN, settings.get('svn_binary_path')),
- (
- Git, settings.get('git_binary_path')),
- (
- Hg, settings.get('hg_binary_path'))]
- for vcs_class, binary_path in vcses:
- try:
- vcs = vcs_class(path, binary_path)
- except NotFoundError:
- pass
- if vcs is None:
- printer.write('\nLooking for changed files ... failure')
- printer.error('The current file does not appear to be inside of an SVN, Git or Mercurial working copy')
- return
- if config['save_before_upload']:
- self.save_files()
- debug_print('SFTP: Starting VCS Command Thread', 2)
- VcsThread(self.window, config, vcs).start()
- return
- def is_visible(self, paths=None):
- path = self.get_path(paths)
- if not path or os.path.isdir(path):
- return False
- return self.has_config(path)
- class VcsThread(HookedThread):
- def __init__(self, window, config, vcs):
- self.window_id = window.id()
- self.printer = PanelPrinter.get(window.id())
- self.config = config
- self.vcs = vcs
- super(VcsThread, self).__init__()
- def run(self):
- last_thread = ThreadTracker.get_last_added(self.window_id)
- ThreadTracker.add(self)
- if last_thread is not None:
- last_thread.join()
- progress = ProgressThread(self.printer, '\nLooking for changed files')
- try:
- files = self.vcs.list_changed_files()
- files = [file for file in files if re.match(re.escape(self.config['local_dir']), file) is not None]
- except NotFoundError as e:
- progress.stop('failure\n' + str(e))
- progress.join()
- self.printer.error(e)
- return
- to_upload = len(files)
- ignored = 0
- if 'ignore_regex' in self.config:
- files = [file for file in files if not re.search(self.config['ignore_regex'], file)]
- ignored = to_upload - len(files)
- to_upload = len(files)
- message = '%d %s to upload' % (to_upload, 'files' if to_upload != 1 else ' file')
- if ignored:
- message += ', %d ignored' % ignored
- progress.stop(message)
- progress.join()
- if files:
- debug_print('SFTP: Starting VCS Thread', 2)
- SftpThread(self.window_id, self.config, files, 'put').start()
- else:
- sublime.set_timeout(self.printer.hide, 10)
- return
- class SftpCancelUploadCommand(sublime_plugin.WindowCommand, SftpCommand):
- def run(self):
- current_thread = ThreadTracker.get_current(self.window.id())
- if current_thread:
- current_thread.kill()
- def is_visible(self, paths=None):
- return ThreadTracker.get_current(self.window.id()) is not None
- class SftpEditConfigCommand(sublime_plugin.WindowCommand, SftpCommand):
- def run(self, paths=None):
- path = self.get_path(paths)
- if not path:
- return
- if not os.path.isdir(path):
- path = os.path.dirname(path)
- file = find_config(path, True)
- file = fix_windows_path(file)
- self.window.run_command('open_file', {'file': file})
- def is_visible(self, paths=None):
- path = self.get_path(paths)
- if not path:
- return False
- if not os.path.isdir(path):
- path = os.path.dirname(path)
- config_file = find_config(path, True)
- return config_file is not False
- def is_enabled(self, paths=None):
- path = self.get_path(paths)
- if not path:
- return False
- if not os.path.isdir(path):
- path = os.path.dirname(path)
- config_file = find_config(path, True)
- return config_file is not False
- class SftpCreateConfigCommand(sublime_plugin.WindowCommand, SftpCommand):
- def run(self, paths=None):
- path = self.get_path(paths)
- if not os.path.isdir(path):
- path = os.path.dirname(path)
- file = os.path.join(path, 'sftp-config.json')
- self.create_default_config(file)
- def open_file():
- self.window.run_command('open_file', {'file': fix_windows_path(file)})
- def run_snippet():
- snippet = get_default_config()
- view = self.window.active_view()
- view.sel().add(sublime.Region(0, view.size()))
- view.run_command('insert_snippet', {'contents': snippet})
- sublime.set_timeout(run_snippet, 350)
- sublime.set_timeout(open_file, 350)
- def is_visible(self, paths=None):
- path = self.get_path(paths)
- if not path:
- return False
- if not os.path.isdir(path):
- path = os.path.dirname(path)
- return find_config(path, True) is False
- def is_enabled(self, paths=None):
- path = self.get_path(paths)
- if not path:
- return False
- if not os.path.isdir(path):
- path = os.path.dirname(path)
- return find_config(path, True) is False
- class SftpSwitchConfigCommand(sublime_plugin.WindowCommand, SftpCommand):
- def run(self, paths=None):
- path = self.get_path(paths)
- if not path:
- return
- config_file = find_config(path, True)
- if config_file is False:
- return
- self.config_dir = os.path.dirname(config_file)
- def add_desc(config):
- remote_type = config.get('type', 'sftp').upper()
- user = config.get('user')
- host = config.get('host')
- remote_path = config.get('remote_path')
- port = ':' + str(config.get('port')) if config.get('port') else ''
- config['desc'] = '%s %s@%s%s %s' % (
- remote_type, user, host, port, remote_path)
- return config
- alt_config_files = []
- alt_configs = {}
- for num in ['', '2', '3', '4', '5', '6', '7', '8', '9']:
- alt_config_file = config_file.replace('sftp-settings.json', 'sftp-config.json')[0:-5] + '-alt' + num + '.json'
- if os.path.exists(alt_config_file):
- alt_config_files.append(alt_config_file)
- alt_configs[alt_config_file] = add_desc(parse_config(alt_config_file))
- continue
- config = add_desc(parse_config(config_file))
- self.choices = [
- [
- config['desc'], os.path.basename(config_file)]]
- for alt_config_file in alt_config_files:
- self.choices.append([alt_configs[alt_config_file]['desc'], os.path.basename(alt_config_file)])
- self.window_id = self.window.id()
- self.printer = PanelPrinter.get(self.window_id)
- show_qp(self.window, self.choices, self.on_done)
- def on_done(self, index):
- if index == -1:
- return
- if index == 0:
- return
- main_config_file = os.path.join(self.config_dir, self.choices[0][1])
- alt_config_file = os.path.join(self.config_dir, self.choices[index][1])
- original_config = ''
- with open(main_config_file, 'rb') as (f):
- original_config = f.read()
- new_config = ''
- with open(alt_config_file, 'rb') as (f):
- new_config = f.read()
- with open(main_config_file, 'wb') as (f):
- f.write(new_config)
- with open(alt_config_file, 'wb') as (f):
- f.write(original_config)
- def is_visible(self, paths=None):
- path = self.get_path(paths)
- if not path:
- return False
- config_file = find_config(path, True)
- if config_file is False:
- return False
- alt_config_file = config_file.replace('sftp-settings.json', 'sftp-config.json')[0:-5] + '-alt.json'
- if os.path.exists(alt_config_file):
- return True
- return False
- class SftpCreateAltConfigCommand(sublime_plugin.WindowCommand, SftpCommand):
- def run(self, paths=None):
- path = self.get_path(paths)
- if not path:
- return
- else:
- config_file = find_config(path, True)
- if config_file is False:
- return
- new_config_file = None
- for num in ['', '2', '3', '4', '5', '6', '7', '8', '9']:
- alt_config_file = config_file.replace('sftp-settings.json', 'sftp-config.json')[0:-5] + '-alt' + num + '.json'
- if not os.path.exists(alt_config_file):
- new_config_file = alt_config_file
- break
- if not new_config_file:
- sublime.error_message('Sublime SFTP\n\nThere can not be more than 9 alternate configs')
- return False
- original_config = ''
- with open(config_file, 'rb') as (f):
- original_config = f.read()
- with open(new_config_file, 'wb') as (f):
- f.write(original_config)
- self.window.run_command('open_file', {'file': fix_windows_path(new_config_file)})
- return
- def is_visible(self, paths=None):
- path = self.get_path(paths)
- if not path:
- return False
- config_file = find_config(path, True)
- if config_file is False:
- return False
- return True
- class SftpCreateSubConfigCommand(sublime_plugin.WindowCommand, SftpCommand):
- def run(self, dirs):
- file = os.path.join(dirs[0], 'sftp-config.json')
- self.create_default_config(file)
- file = fix_windows_path(file)
- self.window.run_command('open_file', {'file': file})
- def is_visible(self, dirs):
- path = self.first_path(dirs)
- config_file = find_config(path, True)
- return config_file is not False and os.path.dirname(config_file) != path
- def is_enabled(self, dirs):
- path = self.first_path(dirs)
- config_file = find_config(path, True)
- return config_file is not False and os.path.dirname(config_file) != path
- # okay decompiling /home/manager/.config/sublime-text-3/Packages/SFTP/sftp/commands.pyc
- # uncompyle6 version 3.2.3
- # Python bytecode 3.3 (3230)
- # Decompiled from: Python 2.7.12 (default, Dec 4 2017, 14:50:18)
- # [GCC 5.4.0 20160609]
- # Embedded file name: /Users/wbond/Library/Application Support/Sublime Text 3/Packages/SFTP/sftp/config.py
- # Compiled at: 2015-11-05 01:05:02
- # Size of source mod 2**32: 15538 bytes
- import sublime, os, sys, re, json, tempfile, time
- if os.name != 'nt':
- import unicodedata
- try:
- str_cls = unicode
- except NameError:
- str_cls = str
- from .errors import NotFoundError
- from .debug import set_debug, set_debug_log_file
- from .proc import find_binary
- from .paths import canonicalize, dirname, fix_windows_path
- def normalize_path(path):
- if os.name == 'nt':
- return path
- return unicodedata.normalize('NFC', path)
- shown_regex_errors = {}
- def get_default_config(use_defaults=False, remove_settings=[], force_settings=None):
- user_path = os.path.join(sublime.packages_path(), 'User', 'SFTP.default-config')
- if os.path.exists(user_path):
- with open(user_path, 'r') as (f):
- string = f.read()
- else:
- package_path = os.path.join(sublime.packages_path(), 'SFTP', 'SFTP.default-config')
- with open(package_path, 'r') as (f):
- string = f.read()
- if use_defaults:
- string = re.sub('\\$\\{\\d+:([^}]+|\\\\})+}', '\\1', string)
- string = string.replace('\\\\\\', '\\\\')
- else:
- if remove_settings or force_settings:
- for setting in remove_settings:
- if setting == 'ignore_regexes':
- regex = re.compile('\r?\n[^\n]*"' + setting + '"\\s*:\\s*\\[.*?\\]\\s*,[ \t]*', re.S)
- string = re.sub(regex, '', string)
- else:
- string = re.sub('\r?\n[^\n]*"' + setting + '"\\s*:[^\n]*', '', string)
- for setting in force_settings:
- if string.find('"' + setting + '"') == -1:
- string = re.sub('("type"[^\n]+)\n', '\\1\n\t"' + setting + '": ${1:' + json.dumps(force_settings[setting]) + '},\n', string)
- else:
- string = re.sub('("' + setting + '"[^\n]+)\n', '"' + setting + '": ${1:' + json.dumps(force_settings[setting]) + '},\n', string)
- placeholder = 1
- for match in re.finditer('\\$\\{\\d+:([^}]+|\\\\})+}', string):
- replacement = '${' + str(placeholder) + ':' + match.group(1).replace('\\', '\\\\') + '}'
- string = string.replace(match.group(0), replacement)
- placeholder += 1
- return string
- def find_config(start, return_not_found=False):
- if start is None:
- if return_not_found:
- return False
- raise NotFoundError()
- last_dir = ''
- if os.path.isdir(start):
- orig_dir = start
- else:
- orig_dir = dirname(start)
- local_dir = orig_dir
- while local_dir != last_dir:
- config_file = os.path.join(local_dir, 'sftp-settings.json')
- if os.path.exists(config_file):
- return normalize_path(config_file)
- config_file = os.path.join(local_dir, 'sftp-config.json')
- if os.path.exists(config_file):
- return normalize_path(config_file)
- last_dir = local_dir
- if local_dir == '/':
- break
- local_dir = dirname(local_dir)
- if return_not_found:
- return False
- else:
- raise NotFoundError()
- return
- def load_config(path):
- if path is None:
- def show_error_3():
- sublime.error_message('Sublime SFTP\n\nThe current file does not appear to be saved to disk and thus can not be uploaded')
- sublime.set_timeout(show_error_3, 1)
- return (None, None)
- else:
- try:
- config_file = find_config(path)
- config = parse_config(config_file)
- if 'remote_path' in config and len(config['remote_path']) and config['remote_path'][-1] != '/':
- config['remote_path'] += '/'
- except ValueError as e:
- exception = str(e)
- def show_error():
- sublime.error_message('Sublime SFTP\n\nError parsing sftp-config.json file:\n%s' % exception)
- sublime.set_timeout(show_error, 1)
- return (None, None)
- except NotFoundError:
- def show_error_2():
- current_dir = dirname(path) if path and os.path.isfile(path) else path
- sublime.error_message('Sublime SFTP\n\nA sftp-config.json file was not found in "%s" or any parent folders' % current_dir)
- sublime.set_timeout(show_error_2, 1)
- return (None, None)
- return (
- config, config_file)
- def parse_config(path):
- with open(path, 'r') as (handle):
- regex = re.compile('^[ \t]*//.*\r?', re.M)
- json_string = handle.read()
- match = re.search(regex, json_string)
- while match is not None:
- s = match.start()
- e = match.end()
- json_string = json_string[:s] + ' ' * (e - s) + json_string[e:]
- match = re.search(regex, json_string)
- json_string = re.sub(',([ \r\n\t]+}\\s*)$', ' \\1', json_string)
- return json.loads(json_string)
- return
- def fix_server_folder():
- old_path = os.path.join(sublime.packages_path(), 'User', 'sftp_remotes')
- if os.path.exists(old_path):
- new_path = os.path.join(sublime.packages_path(), 'User', 'sftp_servers')
- os.rename(old_path, new_path)
- def get_server_config_folder():
- fix_server_folder()
- config_folder = os.path.join(sublime.packages_path(), 'User', 'sftp_servers')
- if not os.path.exists(config_folder):
- os.mkdir(config_folder)
- return normalize_path(config_folder)
- def prepare_server_config(filename):
- config_dir = get_server_config_folder()
- path = os.path.join(config_dir, filename)
- try:
- config = parse_config(path)
- config['file_path'] = path
- config['name'] = filename
- def open_config(message):
- sublime.active_window().run_command('open_file', {'file': fix_windows_path(path)})
- def show_error():
- sublime.error_message(message)
- sublime.set_timeout(show_error, 200)
- for setting in ['remote_path', 'user', 'host']:
- if setting not in config or not len(config[setting]):
- message = 'Sublime SFTP\n\nThe "%s" setting is not specified in %s.' % (setting, path)
- sublime.set_timeout(lambda : open_config(message), 10)
- return
- remote_type = config.get('type', 'sftp').upper()
- user = config.get('user')
- host = config.get('host')
- port = ':' + str(config.get('port')) if config.get('port') else ''
- config['desc'] = '%s %s@%s%s' % (
- remote_type, user, host, port)
- config['upload_on_save'] = True
- config['save_before_upload'] = True
- return config
- except ValueError as e:
- exception = str(e)
- def show_error():
- sublime.error_message('Sublime SFTP\n\nError parsing server config file %s:\n%s' % (filename, exception))
- sublime.set_timeout(show_error, 1)
- return
- return
- def setup_tmp_dir(config, tmp_dir=None):
- if tmp_dir is None:
- if 'name' in config:
- tmp_dir = os.path.join(tempfile.gettempdir(), 'sublime-sftp-browse-' + str(int(time.time())), config['name'])
- else:
- tmp_dir = os.path.join(tempfile.gettempdir(), 'sublime-sftp-browse-' + str(int(time.time())), 'mapped')
- if not os.path.exists(tmp_dir):
- os.makedirs(tmp_dir)
- tmp_dir = canonicalize(tmp_dir, 'local')
- config['upload_on_save'] = True
- config['save_before_upload'] = True
- config['confirm_sync'] = False
- remote_path_matches = re.match('(/|[a-zA-Z]:\\\\)(.*)$', config.get('remote_path', ''))
- if remote_path_matches:
- initial_tmp_dir = os.path.join(tmp_dir, remote_path_matches.group(2))
- if not os.path.exists(initial_tmp_dir):
- os.makedirs(initial_tmp_dir)
- config['initial_remote_path'] = config['remote_path']
- config['remote_path'] = remote_path_matches.group(1)
- config_path = os.path.join(tmp_dir, 'sftp-config.json')
- with open(config_path, 'w') as (handle):
- handle.write(json.dumps(config, sort_keys=True, indent=4))
- return tmp_dir
- def build_config(config, local_dir, config_file, quiet=False, skip_settings=False):
- if not skip_settings:
- settings = sublime.load_settings('SFTP.sublime-settings')
- set_debug(settings.get('debug', False))
- set_debug_log_file(settings.get('debug_log_file', False))
- local_dir = canonicalize(local_dir, 'local')
- def open_config(message):
- sublime.active_window().run_command('open_file', {'file': fix_windows_path(config_file)})
- def show_error():
- sublime.error_message(message)
- sublime.set_timeout(show_error, 200)
- if os.name == 'nt':
- sftp_path = find_binary('psftp.exe')
- else:
- sftp_path = find_binary('sftp')
- for setting in ['remote_path', 'user', 'host']:
- if setting not in config or not len(config[setting]):
- if not quiet:
- message = 'Sublime SFTP\n\nThe "%s" setting is not specified in %s.' % (setting, config_file)
- sublime.set_timeout(lambda : open_config(message), 10)
- return
- if 'remote_path' in config and not re.match('/|[a-zA-Z]:[/\\\\]', config['remote_path']):
- if not quiet:
- message = 'Sublime SFTP\n\nThe "remote_path" setting in %s should be an absolute file path starting with a / (or in rarer cases, a Windows drive letter, e.g. C:\\ or C:/).\n\nIf you do not know the exact remote path, enter your best guess and try connecting. If the path you was not found, a list of valid paths will be printed in the SFTP output panel.' % config_file
- sublime.set_timeout(lambda : open_config(message), 10)
- return
- else:
- parsed_config = {'name': config.get('name'),
- 'is_server': True if config.get('file_path') else False,
- 'allow_config_upload': config.get('allow_config_upload', False),
- 'type': config.get('type', 'sftp'),
- 'binary': sftp_path,
- 'user': config['user'],
- 'host': config['host'],
- 'path_map': {normalize_path(local_dir): config['remote_path']}, 'local_dir': normalize_path(local_dir),
- 'remote_dir': config['remote_path'],
- 'initial_remote_dir': config.get('initial_remote_path', config['remote_path']),
- 'timeout': int(config.get('connect_timeout', 30)),
- 'keepalive': int(config.get('keepalive', 0)),
- 'remote_locale': config.get('remote_locale', 'C'),
- 'remote_encoding': config.get('remote_encoding', 'utf-8'),
- 'upload_on_save': config.get('upload_on_save', False),
- 'save_before_upload': config.get('save_before_upload', True),
- 'confirm_downloads': config.get('confirm_downloads', False),
- 'confirm_sync': config.get('confirm_sync', True),
- 'sync_same_age': config.get('sync_same_age', True),
- 'sync_down_on_open': config.get('sync_down_on_open', False),
- 'sync_skip_deletes': config.get('sync_skip_deletes', False),
- 'extra_list_connections': int(config.get('extra_list_connections', 0)),
- 'confirm_overwrite_newer': config.get('confirm_overwrite_newer', False),
- 'preserve_modification_times': config.get('preserve_modification_times', False)}
- types = [
- 'ftp', 'sftp']
- if 'ssl' in sys.modules:
- types.append('ftps')
- if parsed_config['type'] not in types:
- if not quiet:
- message = 'Sublime SFTP\n\nThe type specified, %s, is invalid. Must be one of: %s. Please fix the type setting in %s.' % (
- parsed_config['type'], (', ').join(types), config_file)
- if sublime.platform() == 'linux':
- settings_path = os.path.join(sublime.packages_path(), 'User', 'SFTP.sublime-settings')
- message += '\n\nPlease note that ftps is not available by default on Linux since Sublime Text does not include the Python ssl module. Sublime SFTP includes copies that may work. You can try using them by changing "linux_enable_ssl" to true in %s.' % settings_path
- sublime.set_timeout(lambda : open_config(message), 10)
- return
- if not skip_settings:
- for element in ['email', 'product_key']:
- value = settings.get(element)
- if isinstance(value, str):
- value = value.lower()
- parsed_config[element] = value
- if parsed_config['type'] == 'sftp':
- default_port = '22'
- else:
- default_port = '21'
- parsed_config['port'] = str(config.get('port', default_port))
- keys_to_copy = set([
- 'password',
- 'ignore_regex',
- 'file_permissions',
- 'ignore_regexes',
- 'dir_permissions',
- 'ssh_key_file',
- 'sftp_flags',
- 'ftp_passive_mode',
- 'ftp_passive_host_fix',
- 'remote_time_offset_in_hours'])
- for key in keys_to_copy:
- if key in config:
- parsed_config[key] = config[key]
- continue
- if 'ssh_key_file' in parsed_config:
- if re.match('\\.\\.?[/\\\\]', parsed_config['ssh_key_file']) is not None:
- tmp_path = os.path.join(parsed_config['local_dir'], parsed_config['ssh_key_file'])
- parsed_config['ssh_key_file'] = os.path.realpath(tmp_path)
- if 'ignore_regexes' not in parsed_config and 'ignore_regex' not in parsed_config:
- parsed_config['ignore_regexes'] = ['\\\\.sublime-(project|workspace)',
- 'sftp-config(-alt\\d?)?\\.json',
- 'sftp-settings\\.json',
- '/venv/',
- '\\.svn/',
- '\\.hg/',
- '\\.git/',
- '\\.bzr',
- '_darcs',
- 'CVS',
- '\\.DS_Store',
- 'Thumbs\\.db',
- 'desktop\\.ini']
- if 'ignore_regexes' in parsed_config and isinstance(parsed_config['ignore_regexes'], list):
- parsed_config['ignore_regex'] = '(' + ('|').join(parsed_config['ignore_regexes']) + ')'
- if 'ignore_regex' in parsed_config and parsed_config['ignore_regex']:
- try:
- re.compile(parsed_config['ignore_regex'])
- except re.error as e:
- local_dir = parsed_config['local_dir']
- if local_dir not in shown_regex_errors or shown_regex_errors[local_dir] < time.time():
- shown_regex_errors[local_dir] = time.time() + 10
- message = 'Sublime SFTP\n\nThe "ignore_regex" or "ignore_regexes" setting in %s contains an invalid regular expression with the error message:\n\n%s' % (
- config_file, str_cls(e))
- sublime.set_timeout(lambda : open_config(message), 10)
- return
- return parsed_config
- # okay decompiling /home/manager/.config/sublime-text-3/Packages/SFTP/sftp/config.pyc
- # uncompyle6 version 3.2.3
- # Python bytecode 3.3 (3230)
- # Decompiled from: Python 2.7.12 (default, Dec 4 2017, 14:50:18)
- # [GCC 5.4.0 20160609]
- # Embedded file name: /Users/wbond/Library/Application Support/Sublime Text 3/Packages/SFTP/sftp/debug.py
- # Compiled at: 2015-11-05 00:17:09
- # Size of source mod 2**32: 2114 bytes
- import sublime, locale, time
- try:
- str_cls = unicode
- str_cls_name = 'unicode'
- except NameError:
- str_cls = str
- str_cls_name = 'str'
- from .times import timestamp_to_string
- status = {'debug': False,
- 'debug_log_file': None,
- 'inited': False}
- def debug_print(message, level=1, force=False):
- debug = status['debug']
- debug_log_file = status['debug_log_file']
- if not status['inited']:
- def init():
- settings = sublime.load_settings('SFTP.sublime-settings')
- debug_print('SFTP Email: ' + str(settings.get('email')))
- debug_print('SFTP Key: ' + str(settings.get('product_key')))
- sublime.set_timeout(init, 1)
- status['inited'] = True
- if (not debug or int(debug) < level) and not force:
- return
- typeof = type(message).__name__
- is_string = typeof == 'str' or typeof == str_cls_name
- if is_string and message[-1:] == '\n':
- message = message[0:-1]
- else:
- if not is_string:
- message = str(message)
- if type(message).__name__ != str_cls_name:
- try:
- message = str_cls(message, 'utf-8', errors='strict')
- except UnicodeDecodeError:
- encoding = locale.getpreferredencoding(do_setlocale=True)
- try:
- message = str_cls(message, encoding)
- except UnicodeDecodeError:
- message = str_cls(message, encoding, 'replace')
- message = str_cls(timestamp_to_string(time.time(), '%Y-%m-%d %H:%M:%S, ')) + message
- message = message.replace('\r\n', '\n')
- if debug and debug_log_file:
- with open(debug_log_file, 'ab') as (f):
- f.write(message.encode('utf-8') + '\n')
- else:
- if int(sublime.version()) < 3000:
- if isinstance(message, str_cls):
- message = message.encode('UTF-8')
- print(message)
- def set_debug(enabled):
- status['debug'] = enabled
- def set_debug_log_file(path):
- status['debug_log_file'] = path
- def get_debug():
- return status['debug']
- def get_debug_log_file():
- return status['debug_log_file']
- # okay decompiling /home/manager/.config/sublime-text-3/Packages/SFTP/sftp/debug.pyc
- # uncompyle6 version 3.2.3
- # Python bytecode 3.3 (3230)
- # Decompiled from: Python 2.7.12 (default, Dec 4 2017, 14:50:18)
- # [GCC 5.4.0 20160609]
- # Embedded file name: /Users/wbond/Library/Application Support/Sublime Text 3/Packages/SFTP/sftp/errors.py
- # Compiled at: 2015-11-05 00:17:46
- # Size of source mod 2**32: 2020 bytes
- import sublime, sys
- from .debug import debug_print
- if sys.version_info < (3, ):
- str_cls = unicode
- else:
- str_cls = str
- class ConnectionError(Exception):
- pass
- class NotFoundError(Exception):
- pass
- class OperationError(Exception):
- pass
- class PermissionError(Exception):
- pass
- class AuthenticationError(Exception):
- pass
- class BinaryMissingError(Exception):
- pass
- class DisconnectionError(OSError):
- pass
- class CancelledError(OSError):
- pass
- def handle_exception(heading, backtrace):
- divider = '-' * 60
- lines = [
- 'SFTP %s:' % heading,
- divider]
- lines.extend(backtrace.split('\n'))
- lines.append(divider)
- debug_print(('\n ').join(lines), force=True)
- def encoding_error(e):
- debug_print('SFTP: Encoding error - ' + str_cls(e), 1)
- def show_encoding_helper():
- message = '\n\nAn encoding error was encountered trying to ' + 'read from the server. Please adjust the "remote_encoding" ' + 'setting in sftp-config.json.\n\nCommon encodings include:\n\n' + '"utf-8": Default and most common encoding\n\n' + '"cp1252" or "iso-8859-15": Danish, Dutch, English, French, ' + 'German, Icelandic, Italian, Norwegian, Spanish, Swedish and ' + 'Portuguese\n\n' + '"cp1250": Albanian, Bosnian, Croatian, Czech, Hungarian, ' + 'Polish, Romanian, Serbian (Latin Script), Slovak and ' + 'Slovene\n\n' + '"cp1251": Bulgarian, Russian and Serbian (Cyrillic)\n' + '"cp1253": Modern Greek\n' + '"cp1254": Turkish\n' + '"cp1255": Hebrew\n' + '"cp1256": Arabic, Persian and Urdu\n' + '"cp1257": Estonian, Latvian and Lithuanian\n' + '"cp1258": Vietnamese'
- sublime.error_message('Sublime SFTP' + message)
- sublime.set_timeout(show_encoding_helper, 1)
- # okay decompiling /home/manager/.config/sublime-text-3/Packages/SFTP/sftp/errors.pyc
- # uncompyle6 version 3.2.3
- # Python bytecode 3.3 (3230)
- # Decompiled from: Python 2.7.12 (default, Dec 4 2017, 14:50:18)
- # [GCC 5.4.0 20160609]
- # Embedded file name: /Users/wbond/Library/Application Support/Sublime Text 3/Packages/SFTP/sftp/file_transfer.py
- # Compiled at: 2015-11-05 01:18:03
- # Size of source mod 2**32: 33345 bytes
- import sublime, os, tempfile, time, datetime, shutil, re, json
- try:
- basestring
- str_cls = unicode
- except NameError:
- basestring = str
- str_cls = str
- if os.name != 'nt':
- import unicodedata
- from .debug import debug_print
- from .errors import CancelledError, ConnectionError, DisconnectionError, NotFoundError, PermissionError
- from .paths import canonicalize, dirname, fix_windows_path, ignore_paths, is_dir, local_to_remote
- from .panel_printer import ProgressThread
- from .times import timestamp_to_string
- from .threads import HookedThread
- def keepaliveize(fn):
- def handler(self, *args, **kwargs):
- set_keepalive = self.clear_keepalive()
- try:
- result = fn(self, *args, **kwargs)
- except Exception:
- if set_keepalive:
- self.set_keepalive()
- raise
- if set_keepalive:
- self.set_keepalive()
- return result
- return handler
- class CloneThread(HookedThread):
- def __init__(self, parent):
- self.parent = parent
- self.clone = None
- self.end = False
- super(CloneThread, self).__init__()
- return
- def run(self):
- try:
- args = []
- self.clone = self.parent.clone()
- while not self.end:
- args = self.parent.pop_list_queue()
- if args:
- success, paths = self.clone.list(*args)
- self.parent.push_list_result(args[0], success, paths)
- self.parent.clear_list_running(args[0])
- else:
- time.sleep(0.1)
- except ConnectionError as e:
- pass
- except OSError as e:
- if isinstance(e, (CancelledError, DisconnectionError)):
- if args:
- self.parent.clear_list_running(args[0])
- self.parent.list_queue.append(args)
- return
- raise
- class FileTransfer(object):
- def __init__(self, printer, user=None, host=None, port=None, password=None, remote_time_offset=None, **kwargs):
- self.ibm_ftp = False
- self.connected = False
- self.dir = None
- self.local_dir = None
- self.printer = printer
- self.shown_locale_set_error = False
- self.port = port
- self.host = host
- self.user = user
- self.password = password
- self.timeout = kwargs['timeout']
- self.remote_locale = kwargs['remote_locale']
- self.month_info = {'C': {'jan': 1,
- 'feb': 2,
- 'mar': 3,
- 'apr': 4,
- 'may': 5,
- 'jun': 6,
- 'jul': 7,
- 'aug': 8,
- 'sep': 9,
- 'oct': 10,
- 'nov': 11,
- 'dec': 12}}
- self.clock_info = {'C': {'am': 0,
- 'pm': 12}}
- if self.remote_locale != 'C':
- try:
- months_file = os.path.join(sublime.packages_path(), 'SFTP', 'lang', 'months.json')
- with open(months_file, 'r') as (f):
- self.month_info = json.load(f)
- if self.remote_locale not in self.month_info:
- remote_locale = self.remote_locale
- def show_locale_error():
- sublime.error_message('Sublime SFTP\n\nThe remote_locale "%s" is not defined in %s' % (
- remote_locale, months_file))
- sublime.set_timeout(show_locale_error, 1)
- self.remote_locale = 'C'
- except ValueError as e:
- exception = str(e)
- def show_parse_error():
- sublime.error_message('Sublime SFTP\n\nError parsing %s:\n%s' % (
- months_file, exception))
- sublime.set_timeout(show_parse_error, 1)
- try:
- clock_file = os.path.join(sublime.packages_path(), 'SFTP', 'lang', 'clock.json')
- with open(clock_file, 'r') as (f):
- self.clock_info = json.load(f)
- except ValueError as e:
- exception = str(e)
- def show_parse_error2():
- sublime.error_message('Sublime SFTP\n\nError parsing %s:\n%s' % (
- clock_file, exception))
- sublime.set_timeout(show_parse_error2, 1)
- self.remote_encoding = kwargs['remote_encoding']
- self.keepalive = 0
- if kwargs.get('keepalive'):
- self.keepalive = kwargs['keepalive']
- self.keepalive_at = 0
- offset = kwargs.get('remote_time_offset_in_hours')
- if isinstance(offset, basestring):
- offset = int(offset)
- if offset is not None:
- offset *= -3600
- if offset is None and remote_time_offset is not None:
- offset = remote_time_offset
- self.remote_time_offset = offset
- self.preserve_modification_times = kwargs.get('preserve_modification_times', False)
- self.extra_list_connections = kwargs.get('extra_list_connections')
- self.clones = []
- self.clone_threads = []
- self.list_queue = []
- self.list_results = []
- self.list_args = []
- self.list_paths = []
- self.list_running_paths = []
- return
- def clone(self):
- kwargs = self.kwargs.copy()
- kwargs['keepalive'] = 0
- debug_print('SFTP: Cloning connection for recursive remote listing', 2)
- clone = self.__class__(self.printer, self.user, self.host, self.port, self.password, self.remote_time_offset, **kwargs)
- self.clones.append(clone)
- clone.connect(quiet=True)
- return clone
- def debug(self, debug):
- pass
- def clear_keepalive(self):
- if not self.keepalive:
- return
- should_set = False
- if self.keepalive_at < int(time.time()) + 1000000 - self.keepalive - 5:
- should_set = True
- debug_print('SFTP: Clearing keepalive', 2)
- self.keepalive_at = int(time.time()) + 1000000
- return should_set
- def set_keepalive(self):
- if not self.keepalive:
- return
- keepalive_at = int(time.time()) + self.keepalive - 1
- debug_print('SFTP: Setting keepalive to ' + str(keepalive_at), 2)
- self.keepalive_at = keepalive_at
- def perform_keepalive():
- if int(time.time()) < self.keepalive_at:
- return
- try:
- self.do_keepalive()
- self.set_keepalive()
- except OSError:
- self.close(True)
- sublime.set_timeout(perform_keepalive, self.keepalive * 1000)
- def do_keepalive(self):
- pass
- def connect(self):
- pass
- def close(self, disconnected=False):
- pass
- def decode(self, string):
- if isinstance(string, str_cls):
- return string
- try:
- output = str_cls(string, self.remote_encoding, errors='strict')
- except UnicodeDecodeError:
- self.remote_encoding = 'cp1252'
- output = str_cls(string, self.remote_encoding)
- return output
- def encode(self, string):
- if not isinstance(string, str_cls):
- return string
- return string.encode(self.remote_encoding)
- def determine_time_offset(self, path_map, config):
- if self.remote_time_offset is not None:
- if self.remote_time_offset is False:
- return 0
- return self.remote_time_offset
- else:
- old_pwd = self.pwd()
- local_root = list(path_map.keys())[0]
- remote_root = config.get('initial_remote_dir')
- self.cd(remote_root)
- tmp_dir = os.path.join(tempfile.gettempdir(), 'sublime-sftp-offset-') + str(int(time.time()))
- tmp_dir = canonicalize(tmp_dir, 'local')
- if not os.path.exists(tmp_dir):
- os.makedirs(tmp_dir)
- tmp_file = canonicalize(tmp_dir, 'local') + '__sublime_sftp_offset'
- path_map = {tmp_dir: remote_root}
- f = open(tmp_file, 'w')
- f.write('Sublime SFTP Offset Detection Dummy File')
- f.close()
- self.remote_time_offset = False
- try:
- success, result = self.put(tmp_file, path_map, quiet=True)
- if not success:
- raise PermissionError('Permission denied')
- files = self.ls(path_map, config=config)
- remote_tmp_file = local_to_remote(tmp_file, path_map, self.remote_encoding)
- self.rm(remote_tmp_file, path_map, quiet=True)
- for file in files:
- if file[0] == '__sublime_sftp_offset':
- self.remote_time_offset = int(time.time() - file[1])
- break
- debug_print('SFTP Remote time offset: %s' % str(self.remote_time_offset))
- except PermissionError:
- self.printer.write('\nUnable to determine remote time offset since "%s" is not writable. Please set the "remote_time_offset_in_hours" setting in sftp-config.json for sync commands to work properly.' % remote_root, key='offset_priv', finish=True)
- self.remote_time_offset = 0
- self.cd(old_pwd)
- self.lcd(local_root)
- if os.path.exists(tmp_dir):
- shutil.rmtree(tmp_dir)
- return self.remote_time_offset
- def cd(self, dir):
- pass
- def lcd(self, dir):
- pass
- def cwd(self, *args, **kwargs):
- return [
- True, self.pwd()]
- def get(self, remote_files, path_map, quiet=False, **kwargs):
- pass
- def handle_get_dirs(self, file, remote_file, single_file):
- if not is_dir(file):
- file_dir = dirname(file)
- remote_file_dir = dirname(remote_file)
- else:
- file_dir = file
- remote_file_dir = remote_file
- local_dir = canonicalize(file_dir, 'local')
- remote_dir = canonicalize(remote_file_dir, 'remote')
- if local_dir != self.lpwd():
- try:
- self.lcd(local_dir)
- if file_dir == file:
- self.printer.write('\nFolder "%s" already exists' % local_dir)
- except NotFoundError as e:
- os.makedirs(local_dir)
- self.printer.write('\nCreating folder "%s" ... success' % local_dir)
- self.lcd(local_dir)
- else:
- if file_dir == file:
- self.printer.write('\nFolder "%s" already exists' % local_dir)
- if remote_dir != self.pwd():
- try:
- self.cd(remote_dir)
- except (PermissionError, NotFoundError) as e:
- self.printer.write('\nChanging to remote folder "%s" ... failure (%s)' % (remote_dir, str(e)))
- if single_file:
- self.printer.error(e)
- return (True, True)
- if remote_dir == remote_file:
- return (False, True)
- return (False, False)
- def handle_put_dirs(self, file, remote_file, chmod_dirs, single_file):
- if os.path.isdir(file):
- try:
- self.mkdir(remote_file, chmod_dirs)
- return (False, True)
- except PermissionError as e:
- self.printer.write('\nCreating remote folder "%s" ... failure (%s)' % (remote_file, str(e)))
- if single_file:
- self.printer.error(e)
- return (True, True)
- local_dir = canonicalize(dirname(file), 'local')
- remote_dir = canonicalize(dirname(remote_file), 'remote')
- if local_dir != self.lpwd():
- self.lcd(local_dir)
- if remote_dir != self.pwd():
- try:
- try:
- self.cd(remote_dir)
- except NotFoundError:
- self.mkdir(remote_dir, chmod_dirs)
- self.cd(remote_dir)
- except PermissionError as e:
- self.printer.write('\nChanging to remote folder "%s" ... failure (%s)' % (remote_dir, str(e)))
- if single_file:
- self.printer.error(e)
- return (True, True)
- return (False, False)
- def handle_rm_dirs(self, file, remote_file, single_file):
- remote_dir = canonicalize(dirname(remote_file), 'remote')
- if remote_dir != self.pwd():
- try:
- self.cd(remote_dir)
- except NotFoundError:
- return (False, True)
- except PermissionError:
- self.printer.write('\nChanging to remote folder "%s" ... failure' % remote_dir)
- return (True, True)
- return (False, False)
- def pop_list_queue(self):
- try:
- self.list_running_paths.append(self.list_queue[0][0])
- return self.list_queue.pop(0)
- except IndexError:
- return
- return
- def clear_list_running(self, path):
- try:
- self.list_running_paths.remove(path)
- except ValueError:
- pass
- def push_list_result(self, remote_dir, success, paths, include_remote_dir=False):
- self.list_results.append([remote_dir, success, paths])
- if not success:
- return
- for path in paths:
- if path[0] == '.':
- path[0] = ''
- full_dir_path = ('/').join([remote_dir, path[0]]).replace('//', '/')
- if not include_remote_dir and full_dir_path == remote_dir:
- continue
- self.list_paths.append([full_dir_path, path[1]])
- if full_dir_path == remote_dir:
- continue
- if full_dir_path[-1] == '/':
- args = [
- full_dir_path]
- args.extend(self.list_args)
- self.list_queue.append(args)
- continue
- @keepaliveize
- def list(self, remote_dir, path_map, include_self=True, ignore_regex=None, quiet=False, config=None, skip_symlinks=True, **kwargs):
- remote_dir = canonicalize(remote_dir, 'remote')
- if remote_dir != self.pwd():
- try:
- self.cd(remote_dir)
- except (PermissionError, NotFoundError) as e:
- if not quiet:
- self.printer.write('\nChanging to remote folder "%s" ... failure (%s)' % (remote_dir, str(e)))
- self.printer.error(e)
- return [False, str(e)]
- try:
- paths = self.ls(path_map, include_self, config=config, skip_symlinks=skip_symlinks)
- except (PermissionError, NotFoundError) as e:
- if not quiet:
- self.printer.write('\nListing remote folder "%s" ... failure (%s)' % (remote_dir, str(e)))
- self.printer.error(e)
- return [False, str(e)]
- except UnicodeDecodeError:
- if not quiet:
- self.printer.write('\nListing remote folder "%s" ... failure (Encoding error)' % remote_dir)
- self.printer.error('Encoding error')
- raise
- paths, unignored, ignored = ignore_paths(paths, {'ignore_regex': ignore_regex})
- return [
- True, paths]
- @keepaliveize
- def listr(self, remote_dir, path_map, include_self=True, ignore_regex=None, quiet=False, skip_symlinks=True, **kwargs):
- remote_dir = canonicalize(remote_dir, 'remote')
- for i in range(self.extra_list_connections):
- clone_thread = CloneThread(self)
- self.clone_threads.append(clone_thread)
- clone_thread.start()
- success, result = self.list(remote_dir, path_map, include_self=include_self, ignore_regex=ignore_regex, quiet=quiet, skip_symlinks=skip_symlinks)
- if not success:
- return [success, result]
- self.list_args = [path_map, False, ignore_regex, quiet, skip_symlinks]
- self.push_list_result(remote_dir, success, result, True)
- while True:
- if not len(self.list_queue) and not len(self.list_running_paths):
- break
- args = self.pop_list_queue()
- if args:
- success, paths = self.list(*args)
- self.push_list_result(args[0], success, paths)
- self.clear_list_running(args[0])
- else:
- time.sleep(0.1)
- for result in self.list_results:
- if not result[1]:
- self.printer.write('\nListing remote folder "%s" ... failure (%s)' % (result[0], result[2]))
- continue
- output = sorted(self.list_paths)
- for thread in self.clone_threads:
- thread.end = True
- for clone in self.clones:
- debug_print('SFTP: Closing cloned connection', 2)
- clone.close()
- self.clones = []
- self.clone_threads = []
- self.list_queue = []
- self.list_results = []
- self.list_args = []
- self.list_paths = []
- self.list_running_paths = []
- return [
- True, output]
- def llist(self, local_dir, path_map, include_self=True, ignore_regex=None, quiet=False, **kwargs):
- try:
- local_dir = canonicalize(local_dir, 'local')
- paths = self.lls(local_dir, include_self)
- paths, unignored, ignored = ignore_paths(paths, {'ignore_regex': ignore_regex})
- return [
- True, paths]
- except NotFoundError as e:
- return [
- False, str(e)]
- def llistr(self, local_dir, path_map, include_self=True, ignore_regex=None, quiet=False, **kwargs):
- local_dir = canonicalize(local_dir, 'local')
- success, result = self.llist(local_dir, path_map, include_self=include_self, ignore_regex=ignore_regex, quiet=quiet)
- if not success:
- return [success, result]
- output = []
- for path in result:
- if path[0] == '.':
- path[0] = ''
- full_dir_path = os.path.join(local_dir, path[0])
- output.append([full_dir_path, path[1]])
- if full_dir_path == local_dir:
- continue
- if path[0][-1] == '/' or path[0][-1] == '\\':
- success, result = self.llistr(full_dir_path, path_map, include_self=False, ignore_regex=ignore_regex, quiet=quiet)
- if success:
- output.extend(result)
- elif quiet:
- self.printer.write('\nListing local folder "%s" ... failure (%s)' % (full_dir_path, result))
- else:
- continue
- return [
- True, output]
- def ls(self, path_map, include_self=True, thread=None, config=None):
- pass
- def lls(self, local_dir, include_self=True):
- files = []
- if not os.path.exists(local_dir):
- raise NotFoundError('Folder not found')
- if include_self:
- files.append(['.', int(os.lstat(local_dir)[8])])
- for file in os.listdir(local_dir):
- if os.name != 'nt':
- file = unicodedata.normalize('NFC', file)
- full_path = os.path.join(local_dir, file)
- path = file
- if os.path.isdir(full_path):
- path = canonicalize(path, 'local')
- timestamp = int(os.lstat(full_path)[8])
- files.append([path, timestamp])
- files = sorted(files, key=lambda ar: ar[0].lower())
- return files
- def make_absolute_dir(self, dir, type):
- dir_prefix = self.pwd() if type == 'remote' else self.lpwd()
- if type == 'local' and os.name == 'nt':
- if not re.match('[A-Za-z]:\\\\|\\\\\\\\', dir):
- dir = dir_prefix + dir
- else:
- if dir[0] != '/' and re.match('[A-Za-z]:\\\\', dir) is None:
- dir = dir_prefix + dir
- return canonicalize(dir, type)
- def mkdir(self, dir, chmod_dirs=None, **kwargs):
- pass
- def mv(self, names, quiet=False, **kwargs):
- pass
- def put(self, files, path_map, chmod_files=None, chmod_dirs=None, quiet=False, **kwargs):
- pass
- def pwd(self):
- pass
- def lpwd(self):
- pass
- def rm(self, remote_files, path_map, quiet=False, **kwargs):
- pass
- def lrm(self, local_files, path_map, quiet=False, **kwargs):
- if not isinstance(local_files, list):
- local_files = [
- local_files]
- error = False
- single_file = len(local_files) == 1
- for local_file in local_files:
- if os.path.isdir(local_file):
- sub_files = [os.path.join(local_file, sub_file) for sub_file in os.listdir(local_file)]
- self.lrm(sub_files, path_map)
- if not quiet:
- progress = ProgressThread(self.printer, '\nDeleting local "%s"' % local_file)
- try:
- if os.path.isdir(local_file):
- os.rmdir(local_file)
- else:
- os.unlink(local_file)
- except OSError:
- message = 'Permission denied'
- if not quiet:
- progress.stop('failure (%s)' % message)
- progress.join()
- if single_file:
- self.printer.error(message)
- error = True
- continue
- if not quiet:
- progress.stop('success')
- progress.join()
- continue
- result = None
- if error and not quiet and not single_file:
- string = 'One or more errors occured while removing files'
- result = string
- self.printer.write('\n' + string)
- self.printer.error(string)
- return [
- not error, result]
- def encourage_report(self, popup_message, log_message):
- timestamp = timestamp_to_string(time.time(), '%Y-%m-%d %H:%M:%S\n')
- log_file_path = os.path.join(sublime.packages_path(), 'User', 'SFTP.errors.log')
- send_log_path = log_file_path
- with open(log_file_path, 'ab') as (f):
- f.write(timestamp.encode('utf-8'))
- f.write(log_message.encode('utf-8'))
- def notify_parse_error():
- sublime.error_message('Sublime SFTP\n\n%s, please send the file %s to support@wbond.net' % (
- popup_message, send_log_path))
- sublime.active_window().run_command('open_file', {'file': fix_windows_path(send_log_path)})
- sublime.set_timeout(notify_parse_error, 1)
- def parse_month(self, month, lines):
- try:
- if not re.match('^\\d+$', month):
- month = month.strip(' \t.').lower()
- if month not in self.month_info[self.remote_locale]:
- raise ValueError('')
- month = self.month_info[self.remote_locale][month]
- return str(month)
- except ValueError:
- popup_message = 'There was an error parsing the month "%s" with the remote_locale "%s". Please check your "remote_locale" setting.\n\nIf you continue to have trouble' % (
- month, self.remote_locale)
- indented_lines = [' ' + l for l in lines]
- log_message = '%s\n%s\n' % (popup_message, ('\n').join(indented_lines))
- self.encourage_report(popup_message, log_message)
- raise ConnectionError('Error parsing remote folder listing')
- def parse_date(self, date, lines, format='%Y-%m-%d %H:%M'):
- try:
- timestamp = datetime.datetime.strptime(date, format)
- except ValueError:
- popup_message = 'There was an error parsing the date "%s" with the remote_locale "%s". Please check your "remote_locale" setting.\n\nIf you continue to have trouble' % (
- date, self.remote_locale)
- indented_lines = [' ' + l for l in lines]
- log_message = '%s\n%s\n' % (popup_message, ('\n').join(indented_lines))
- self.encourage_report(popup_message, log_message)
- raise ConnectionError('Error parsing remote folder listing')
- return timestamp
- def parse_ls(self, lines, offset, include_self, skip_symlinks):
- output = []
- vshell_sftp_regex = '^(?:\\s*\\d+\\s+([^ ]+)\\s+(\\d+),\\s+(\\d+)\\s+(\\d{2}:\\d{2})\\s+(.*)|\\s*0\\s+([A-Z]/))$'
- data_set = None
- for line in lines:
- line = line.lstrip()
- line = line.rstrip('\n\r')
- if not line == 'ls':
- if line == 'ls -la':
- continue
- if self.ibm_ftp:
- if data_set is None:
- if line.find('Dsname') != -1:
- data_set = False
- else:
- data_set = True
- continue
- if data_set:
- parts = re.split('\\s+', line, maxsplit=9)
- file = parts[0]
- if len(parts) > 1:
- date = ('%s %s' % (parts[3], parts[4])).replace('/', '-')
- timestamp = self.parse_date(date, lines)
- else:
- timestamp = int(time.time())
- output.append([file, timestamp])
- continue
- else:
- parts = re.split('\\s+', line, maxsplit=8)
- file = parts[7]
- if file.find('.') != -1:
- prefix = file[:file.find('.')]
- for entry in output:
- if entry[0] == prefix:
- continue
- continue
- file = prefix
- date = parts[3].replace('/', '-') + ' 00:00'
- timestamp = self.parse_date(date, lines)
- else:
- if re.search(vshell_sftp_regex, line) is not None:
- match = re.search(vshell_sftp_regex, line)
- parts = list(match.groups())
- file = parts[4]
- if parts[0] is None and parts[5] is not None:
- continue
- if not file == '../':
- if not include_self and file == './':
- continue
- if file == './':
- file = '.'
- month = parts[0]
- day = parts[1]
- year = parts[2]
- _time = parts[3]
- if len(day) == 1:
- day = '0' + day
- if len(_time) == 4:
- _time = '0' + _time
- month = self.parse_month(month, lines)
- date = '%s-%s-%s %s' % (year, month, day, _time)
- timestamp = self.parse_date(date, lines)
- else:
- if re.match('[0-9]', line) is not None:
- parts = re.split('\\s+', line, maxsplit=3)
- file = parts[3]
- if parts[2] == '<DIR>':
- file += '/'
- parts[1] = parts[1].replace('.', '').lower()
- if self.remote_locale in self.clock_info:
- for suffix in self.clock_info[self.remote_locale]:
- new_part = parts[1].replace(suffix, '')
- if new_part != parts[1]:
- hours, minutes = new_part.split(':')
- adjustment = self.clock_info[self.remote_locale][suffix]
- if hours == '12':
- hours = str(adjustment)
- else:
- hours = str(int(hours) + int(adjustment))
- if len(hours) < 2:
- hours = '0' + str(hours)
- parts[1] = '%s:%s' % (hours, minutes)
- continue
- month, day, year = parts[0].split('-')
- if len(str(year)) == 2:
- if int(year) > int(datetime.datetime.now().strftime('%y')):
- year = '19' + year
- else:
- year = '20' + year
- date = '%s-%s-%s %s' % (year, month, day, parts[1])
- timestamp = self.parse_date(date, lines)
- else:
- if re.match('total \\d+', line, re.I) is not None:
- continue
- match = re.match('^([^ ]+)\\s+(\\d+)\\s+(.*?)\\s+(.*?)\\s+(\\d+)\\s+([^\\d\\s]+)\\s+(\\d+)\\s+((?:19|20)\\d{2}\\s?|[012]?\\d:\\d{2})\\s(.+)$', line)
- if match is None:
- match = re.match('^([^ ]+)\\s+(\\d+)\\s+(.*?)\\s+(.*?)\\s+(\\d+)\\s+(\\d+)\\s+([^\\d\\s]+)\\s+((?:19|20)\\d{2}\\s?|[012]?\\d:\\d{2})\\s(.+)$', line)
- if match is None:
- match = re.match('^([^ ]+)\\s+(\\d+)\\s+(.*?)\\s+(.*?)\\s+(\\d+)\\s+(\\d+)\\s+(\\d+)\\s+((?:19|20)\\d{2}\\s?|[012]?\\d:\\d{2})\\s(.+)$', line)
- if match is None:
- match = re.match('^([^ ]+)\\s+(\\d+)\\s+(.*?)(\\s+)(\\d+)\\s+([^\\d\\s]+)\\s+(\\d+)\\s+((?:19|20)\\d{2}\\s?|[012]?\\d:\\d{2})\\s(.*)$', line)
- if match is None:
- match = re.match('^([^ ]+)(\\s+)(folder)(\\s+)(0)\\s+([^\\d\\s]+)\\s+(\\d+)\\s+((?:19|20)\\d{2}|[012]?\\d:\\d{2})\\s(.*)$', line)
- if match is None:
- match = re.match('^([^ ]+)\\s+(\\d+)\\s+(.*?)\\s+(.*?)\\s+(\\d+)\\s+(\\.\\.)$', line)
- if match is None:
- popup_message = 'There was an error parsing the remote folder listing'
- indented_lines = [' ' + l for l in lines]
- log_message = 'Error parsing remote folder listing:\n' + ('\n').join(indented_lines) + '\n'
- self.encourage_report(popup_message, log_message)
- raise ConnectionError('Error parsing remote folder listing')
- parts = list(match.groups())
- if len(parts) == 6:
- continue
- file = parts[8]
- if not file == '..':
- if not include_self and file == '.':
- continue
- if line[0] == 'l':
- if skip_symlinks is False:
- file = self.check_symlink(file)
- elif skip_symlinks == 'file':
- file = re.sub(' -> [^ ].*$', '', file)
- else:
- continue
- if parts[0][0] == 'd' and file != '.':
- file += '/'
- if re.match('^\\d+$', parts[5]):
- day = parts[5]
- month = parts[6]
- else:
- month = parts[5]
- day = parts[6]
- month = self.parse_month(month, lines)
- month = str(month)
- if len(str(day)) == 1:
- day = '0' + str(day)
- if str(parts[7]).find(':') == -1:
- year = str(parts[7]).strip()
- _time = '00:00'
- else:
- year = datetime.datetime.now().strftime('%Y')
- _time = str(parts[7])
- if len(_time) == 4:
- _time = '0' + _time
- current_month = datetime.datetime.now().strftime('%m')
- if int(month) > int(current_month):
- year = str(int(year) - 1)
- date = '%s-%s-%s %s' % (year, month, day, _time)
- timestamp = self.parse_date(date, lines)
- try:
- timestamp = int(time.mktime(timestamp.timetuple())) + offset
- except OverflowError:
- if int(timestamp.strftime('%Y')) <= 1970:
- timestamp = 0
- else:
- timestamp = int(time.time())
- output.append([file, timestamp])
- return output
- # okay decompiling /home/manager/.config/sublime-text-3/Packages/SFTP/sftp/file_transfer.pyc
- # uncompyle6 version 3.2.3
- # Python bytecode 3.3 (3230)
- # Decompiled from: Python 2.7.12 (default, Dec 4 2017, 14:50:18)
- # [GCC 5.4.0 20160609]
- # Embedded file name: /Users/wbond/Library/Application Support/Sublime Text 3/Packages/SFTP/sftp/ftplib2.py
- # Compiled at: 2015-11-05 00:18:29
- # Size of source mod 2**32: 37337 bytes
- import sys, re
- try:
- import SOCKS
- socket = SOCKS
- del SOCKS
- from socket import getfqdn
- socket.getfqdn = getfqdn
- del getfqdn
- except ImportError:
- import socket
- from socket import _GLOBAL_DEFAULT_TIMEOUT
- __all__ = [
- 'FTP', 'FTP_TLS']
- MSG_OOB = 1
- FTP_PORT = 21
- class Error(Exception):
- pass
- class error_reply(Error):
- pass
- class error_temp(Error):
- pass
- class error_perm(Error):
- pass
- class error_proto(Error):
- pass
- class error_fixed_host(Error):
- pass
- all_errors = (
- Error, IOError, EOFError)
- SSLError = Error
- CRLF = '\r\n'
- B_CRLF = '\r\n'
- if sys.version_info < (3,):
- str_cls = unicode
- str_clses = (unicode, str)
- else:
- str_cls = str
- str_clses = (str, bytes)
- class FTP:
- debugging = 0
- debug_callback = None
- host = ''
- ip_addr = ''
- port = FTP_PORT
- sock = None
- file = None
- welcome = None
- passiveserver = 1
- obey_passive_host = False
- creating_connection = False
- closed_during_connection = False
- encoding = 'cp1252'
- was_125 = False
- def __init__(self, host='', user='', passwd='', acct='', timeout=_GLOBAL_DEFAULT_TIMEOUT):
- self.timeout = timeout
- if host:
- self.connect(host)
- if user:
- self.login(user, passwd, acct)
- def connect(self, host='', port=0, timeout=-999):
- if host != '':
- self.host = host
- if int(port) > 0:
- self.port = int(port)
- if timeout != -999:
- self.timeout = timeout
- self.creating_connection = True
- try:
- self.ip_addr = socket.gethostbyname(self.host)
- except (socket.gaierror, TypeError):
- self.ip_addr = None
- self.sock = socket.create_connection((self.host, self.port), self.timeout)
- self.check_closed_during_connection()
- self.af = self.sock.family
- if sys.version_info < (3, ):
- self.file = self.sock.makefile('rb')
- else:
- self.file = self.sock.makefile('r', encoding=self.encoding)
- self.welcome = self.getresp()
- return self.welcome
- def encode(self, *args):
- output = ''
- for arg in args:
- if isinstance(arg, str_cls):
- arg = arg.encode(self.encoding)
- output += arg
- return output
- def check_closed_during_connection(self):
- self.creating_connection = False
- if self.closed_during_connection:
- raise socket.error('Closed')
- def getwelcome(self):
- if self.debugging:
- self.debug_print('*welcome* ' + self.sanitize(self.welcome))
- return self.welcome
- def set_debuglevel(self, level, callback=None):
- self.debugging = level
- self.debug_callback = callback
- debug = set_debuglevel
- def debug_print(self, string):
- if self.debug_callback:
- self.debug_callback(string)
- else:
- print(string)
- def set_pasv(self, val, obey_passive_host=False):
- self.passiveserver = val
- self.obey_passive_host = obey_passive_host
- def sanitize(self, s):
- if s[:5] == 'pass ' or s[:5] == 'PASS ':
- i = len(s)
- while i > 5 and s[i - 1] in '\r\n':
- i = i - 1
- s = s[:5] + '*' * (i - 5) + s[i:]
- return repr(s)
- def putline(self, line):
- if isinstance(line, str_cls):
- line = line + CRLF
- else:
- line = line + B_CRLF
- if self.debugging > 1:
- self.debug_print('*put* ' + self.sanitize(line))
- if isinstance(line, str_cls):
- line = line.encode(self.encoding)
- self.sock.sendall(line)
- def putcmd(self, line):
- if self.debugging:
- self.debug_print('*cmd* ' + self.sanitize(line))
- self.putline(line)
- def getline(self):
- line = self.file.readline()
- if self.debugging > 1:
- self.debug_print('*get* ' + self.sanitize(line))
- if not line:
- raise EOFError
- if line[-2:] == CRLF:
- line = line[:-2]
- else:
- if line[-1:] in CRLF:
- line = line[:-1]
- return line
- def getmultiline(self):
- line = self.getline()
- if line[3:4] == '-':
- code = line[:3]
- while True:
- nextline = self.getline()
- line = line + ('\n' + nextline)
- if nextline[:3] == code and nextline[3:4] != '-':
- break
- return line
- def getresp(self):
- resp = self.getmultiline()
- if self.debugging:
- self.debug_print('*resp* ' + self.sanitize(resp))
- self.lastresp = resp[:3]
- c = resp[:1]
- if c in (u'1', u'2', u'3'):
- return resp
- if c == '4':
- raise error_temp(resp)
- if c == '5':
- raise error_perm(resp)
- raise error_proto(resp)
- def voidresp(self):
- resp = self.getresp()
- if resp[:1] != '2':
- raise error_reply(resp)
- return resp
- def abort(self):
- if 'ssl' in sys.modules and isinstance(self.sock, ssl.SSLSocket):
- return
- line = 'ABOR' + CRLF
- if isinstance(line, str_cls):
- line = line.encode(self.encoding)
- self.sock.sendall(line, MSG_OOB)
- resp = self.getmultiline()
- if resp[:3] not in (u'426', u'225', u'226'):
- raise error_proto(resp)
- def sendcmd(self, cmd):
- self.putcmd(cmd)
- return self.getresp()
- def voidcmd(self, cmd):
- self.putcmd(cmd)
- return self.voidresp()
- def sendport(self, host, port):
- hbytes = host.split('.')
- pbytes = [repr(port // 256), repr(port % 256)]
- bytes = hbytes + pbytes
- cmd = 'PORT ' + (',').join(bytes)
- return self.voidcmd(cmd)
- def sendeprt(self, host, port):
- af = 0
- if self.af == socket.AF_INET:
- af = 1
- if self.af == socket.AF_INET6:
- af = 2
- if af == 0:
- raise error_proto('unsupported address family')
- fields = [
- '', repr(af), host, repr(port), '']
- cmd = 'EPRT ' + ('|').join(fields)
- return self.voidcmd(cmd)
- def makeport(self):
- msg = 'getaddrinfo returns an empty list'
- sock = None
- for res in socket.getaddrinfo(None, 0, self.af, socket.SOCK_STREAM, 0, socket.AI_PASSIVE):
- af, socktype, proto, canonname, sa = res
- try:
- sock = socket.socket(af, socktype, proto)
- sock.bind(sa)
- except socket.error as msg:
- if sock:
- sock.close()
- sock = None
- continue
- break
- if not sock:
- raise socket.error(msg)
- sock.listen(1)
- port = sock.getsockname()[1]
- host = self.sock.getsockname()[0]
- if self.af == socket.AF_INET:
- self.sendport(host, port)
- else:
- self.sendeprt(host, port)
- if self.timeout is not _GLOBAL_DEFAULT_TIMEOUT:
- sock.settimeout(self.timeout)
- return sock
- def makepasv(self):
- if self.af == socket.AF_INET:
- host, port = parse227(self.sendcmd('PASV'))
- else:
- host, port = parse229(self.sendcmd('EPSV'), self.sock.getpeername())
- return (host, port)
- def ntransfercmd(self, cmd, rest=None):
- self.was_125 = False
- size = None
- if self.passiveserver:
- host, port = self.makepasv()
- original_host = host
- is_192 = host[0:8] == '192.168.'
- is_10 = host[0:3] == '10.'
- is_172 = re.match('172\\.(1[6-9]|2[0-9]|3[01])\\.', host) is not None
- is_private = is_192 or is_10 or is_172
- different_ip = self.ip_addr != host
- if different_ip and is_private and not self.obey_passive_host:
- if self.debugging:
- self.debug_print('*warn* Connecting to %s instead of %s since %s seems likely unroutable' % (
- self.host, host, host))
- host = self.host
- self.creating_connection = True
- try:
- conn = socket.create_connection((host, port), self.timeout)
- except socket.timeout as e:
- if different_ip and is_private and not self.obey_passive_host:
- raise error_fixed_host(original_host)
- raise e
- self.check_closed_during_connection()
- if rest is not None:
- self.sendcmd('REST %s' % rest)
- resp = self.sendcmd(cmd)
- if resp[0] == '2':
- resp = self.getresp()
- if resp[0] != '1':
- raise error_reply(resp)
- else:
- sock = self.makeport()
- if rest is not None:
- self.sendcmd('REST %s' % rest)
- resp = self.sendcmd(cmd)
- if resp[0] == '2':
- resp = self.getresp()
- if resp[0] != '1':
- raise error_reply(resp)
- conn, sockaddr = sock.accept()
- if self.timeout is not _GLOBAL_DEFAULT_TIMEOUT:
- conn.settimeout(self.timeout)
- if resp[:3] == '150':
- size = parse150(resp)
- if resp[:3] == '125':
- self.was_125 = True
- return (conn, size)
- def transfercmd(self, cmd, rest=None):
- return self.ntransfercmd(cmd, rest)[0]
- def login(self, user='', passwd='', acct=''):
- if not user:
- user = 'anonymous'
- if not passwd:
- passwd = ''
- if not acct:
- acct = ''
- if user == 'anonymous' and passwd in (u'', u'-'):
- passwd = passwd + 'anonymous@'
- resp = self.sendcmd(self.encode('USER ', user))
- if resp[0] == '3':
- resp = self.sendcmd(self.encode('PASS ', passwd))
- if resp[0] == '3':
- resp = self.sendcmd(self.encode('ACCT ', acct))
- if resp[0] != '2':
- raise error_reply(resp)
- return resp
- def retrbinary(self, cmd, callback, blocksize=8192, rest=None):
- self.voidcmd('TYPE I')
- conn = self.transfercmd(cmd, rest)
- while True:
- data = conn.recv(blocksize)
- if not data:
- break
- callback(data)
- conn.close()
- return self.voidresp()
- def retrlines(self, cmd, callback=None):
- if callback is None:
- callback = print_line
- self.sendcmd('TYPE A')
- conn = self.transfercmd(cmd)
- if sys.version_info < (3, ):
- fp = conn.makefile('rb')
- else:
- fp = conn.makefile('r', encoding=self.encoding)
- debug_lines = []
- while True:
- line = fp.readline()
- if self.debugging > 2:
- debug_lines.append('*retr* ' + repr(line))
- if not line:
- break
- if line[-2:] == CRLF:
- line = line[:-2]
- else:
- if line[-1:] == '\n':
- line = line[:-1]
- callback(line)
- if self.debugging > 2:
- self.debug_print(('\n').join(debug_lines))
- fp.close()
- conn.close()
- return self.voidresp()
- def storbinary(self, cmd, fp, blocksize=8192, callback=None, rest=None):
- self.voidcmd('TYPE I')
- conn = self.transfercmd(cmd, rest)
- while 1:
- buf = fp.read(blocksize)
- if not buf:
- break
- conn.sendall(buf)
- if callback:
- callback(buf)
- continue
- conn.close()
- return self.voidresp()
- def storlines(self, cmd, fp, callback=None):
- self.voidcmd('TYPE A')
- conn = self.transfercmd(cmd)
- while 1:
- buf = fp.readline()
- if not buf:
- break
- if buf[-2:] != CRLF:
- if buf[-1] in CRLF:
- buf = buf[:-1]
- buf = buf + CRLF
- conn.sendall(buf)
- if callback:
- callback(buf)
- continue
- conn.close()
- return self.voidresp()
- def acct(self, password):
- cmd = self.encode('ACCT ', password)
- return self.voidcmd(cmd)
- def nlst(self, *args):
- cmd = 'NLST'
- for arg in args:
- cmd = cmd + self.encode(' ', arg)
- files = []
- self.retrlines(cmd, files.append)
- return files
- def dir(self, *args):
- cmd = 'LIST'
- func = None
- if args[-1:] and not isinstance(args[-1], str_clses):
- args, func = args[:-1], args[-1]
- for arg in args:
- if arg:
- cmd = cmd + self.encode(' ', arg)
- continue
- self.retrlines(cmd, func)
- return
- def rename(self, fromname, toname):
- resp = self.sendcmd(self.encode('RNFR ', fromname))
- if resp[0] != '3':
- raise error_reply(resp)
- return self.voidcmd(self.encode('RNTO ', toname))
- def delete(self, filename):
- resp = self.sendcmd(self.encode('DELE ', filename))
- if resp[:3] in (u'250', u'200'):
- return resp
- raise error_reply(resp)
- def cwd(self, dirname):
- if dirname == '..':
- try:
- return self.voidcmd('CDUP')
- except error_perm as msg:
- if msg.args[0][:3] != '500':
- raise
- else:
- if dirname == '':
- dirname = '.'
- cmd = self.encode('CWD ', dirname)
- return self.voidcmd(cmd)
- def size(self, filename):
- resp = self.sendcmd(self.encode('SIZE ', filename))
- if resp[:3] == '213':
- s = resp[3:].strip()
- try:
- return int(s)
- except (OverflowError, ValueError):
- return long(s)
- def mkd(self, dirname):
- resp = self.sendcmd(self.encode('MKD ', dirname))
- return parse257(resp)
- def rmd(self, dirname):
- return self.voidcmd(self.encode('RMD ', dirname))
- def pwd(self):
- resp = self.sendcmd('PWD')
- return parse257(resp)
- def quit(self):
- resp = self.voidcmd('QUIT')
- self.close()
- return resp
- def close(self):
- if self.creating_connection:
- self.closed_during_connection = True
- if self.file:
- self.file.close()
- self.sock.close()
- self.file = self.sock = None
- return
- try:
- import ssl
- except ImportError:
- pass
- else:
- class FTP_TLS(FTP):
- ssl_version = ssl.PROTOCOL_TLSv1
- def __init__(self, host='', user='', passwd='', acct='', keyfile=None, certfile=None, timeout=_GLOBAL_DEFAULT_TIMEOUT):
- self.keyfile = keyfile
- self.certfile = certfile
- self._prot_p = False
- self.implicit_ssl = False
- FTP.__init__(self, host, user, passwd, acct, timeout)
- def login(self, user='', passwd='', acct='', secure=True):
- if secure and not isinstance(self.sock, ssl.SSLSocket):
- self.auth()
- return FTP.login(self, user, passwd, acct)
- def auth(self):
- if isinstance(self.sock, ssl.SSLSocket):
- raise ValueError('Already using TLS')
- if self.ssl_version == ssl.PROTOCOL_TLSv1:
- resp = self.voidcmd('AUTH TLS')
- else:
- resp = self.voidcmd('AUTH SSL')
- self.sock = ssl.wrap_socket(self.sock, self.keyfile, self.certfile, ssl_version=self.ssl_version)
- if sys.version_info < (3, ):
- self.file = self.sock.makefile(mode='rb')
- else:
- self.file = self.sock.makefile(mode='r', encoding=self.encoding)
- return resp
- def connect(self, host='', port=0, timeout=-999):
- if host != '':
- self.host = host
- if int(port) > 0:
- self.port = int(port)
- if timeout != -999:
- self.timeout = timeout
- self.creating_connection = True
- try:
- self.ip_addr = socket.gethostbyname(self.host)
- except (socket.gaierror, TypeError):
- self.ip_addr = None
- self.sock = socket.create_connection((self.host, self.port), self.timeout)
- self.check_closed_during_connection()
- if int(port) == 990:
- try:
- self.sock = ssl.wrap_socket(self.sock, self.keyfile, self.certfile, ssl_version=self.ssl_version)
- self.implicit_ssl = True
- except ssl.SSLError:
- self.creating_connection = True
- self.sock = socket.create_connection((self.host, self.port), self.timeout)
- self.check_closed_during_connection()
- self.af = self.sock.family
- if sys.version_info < (3, ):
- self.file = self.sock.makefile('rb')
- else:
- self.file = self.sock.makefile('r', encoding=self.encoding)
- self.welcome = self.getresp()
- return self.welcome
- def prot_p(self):
- self.voidcmd('PBSZ 0')
- resp = self.voidcmd('PROT P')
- self._prot_p = True
- return resp
- def prot_c(self):
- resp = self.voidcmd('PROT C')
- self._prot_p = False
- return resp
- def ntransfercmd(self, cmd, rest=None):
- conn, size = FTP.ntransfercmd(self, cmd, rest)
- if self._prot_p:
- conn = ssl.wrap_socket(conn, self.keyfile, self.certfile, ssl_version=self.ssl_version)
- return (conn, size)
- def retrbinary(self, cmd, callback, blocksize=8192, rest=None):
- self.voidcmd('TYPE I')
- conn = self.transfercmd(cmd, rest)
- try:
- while True:
- data = conn.recv(blocksize)
- if not data:
- break
- callback(data)
- if not self.was_125 and isinstance(conn, ssl.SSLSocket):
- conn.unwrap()
- finally:
- conn.close()
- return self.voidresp()
- def retrlines(self, cmd, callback=None):
- if callback is None:
- callback = print_line
- self.sendcmd('TYPE A')
- conn = self.transfercmd(cmd)
- if sys.version_info < (3, ):
- fp = conn.makefile('rb')
- else:
- fp = conn.makefile('r', encoding=self.encoding)
- try:
- while True:
- line = fp.readline()
- if self.debugging > 2:
- self.debug_print('*retr* ' + repr(line))
- if not line:
- break
- if line[-2:] == CRLF:
- line = line[:-2]
- else:
- if line[-1:] == '\n':
- line = line[:-1]
- callback(line)
- if not self.was_125 and isinstance(conn, ssl.SSLSocket):
- conn.unwrap()
- finally:
- fp.close()
- conn.close()
- return self.voidresp()
- def storbinary(self, cmd, fp, blocksize=8192, callback=None, rest=None):
- self.voidcmd('TYPE I')
- conn = self.transfercmd(cmd, rest)
- try:
- while 1:
- buf = fp.read(blocksize)
- if not buf:
- break
- conn.sendall(buf)
- if callback:
- callback(buf)
- continue
- if not self.was_125 and isinstance(conn, ssl.SSLSocket):
- conn.unwrap()
- finally:
- conn.close()
- return self.voidresp()
- def storlines(self, cmd, fp, callback=None):
- self.voidcmd('TYPE A')
- conn = self.transfercmd(cmd)
- try:
- while 1:
- buf = fp.readline()
- if not buf:
- break
- if buf[-2:] != CRLF:
- if buf[-1] in CRLF:
- buf = buf[:-1]
- buf = buf + CRLF
- conn.sendall(buf)
- if callback:
- callback(buf)
- continue
- if not self.was_125 and isinstance(conn, ssl.SSLSocket):
- conn.unwrap()
- finally:
- conn.close()
- return self.voidresp()
- __all__.append('FTP_TLS')
- SSLError = ssl.SSLError
- all_errors = (Error, IOError, EOFError, ssl.SSLError)
- _150_re = None
- def parse150(resp):
- global _150_re
- if resp[:3] != '150':
- raise error_reply(resp)
- if _150_re is None:
- import re
- _150_re = re.compile('150 .* \\((\\d+) bytes\\)', re.IGNORECASE)
- m = _150_re.match(resp)
- if not m:
- return
- else:
- s = m.group(1)
- try:
- return int(s)
- except (OverflowError, ValueError):
- return long(s)
- return
- _227_re = None
- def parse227(resp):
- global _227_re
- if resp[:3] != '227':
- raise error_reply(resp)
- if _227_re is None:
- import re
- _227_re = re.compile('(\\d+),(\\d+),(\\d+),(\\d+),(\\d+),(\\d+)')
- m = _227_re.search(resp)
- if not m:
- raise error_proto(resp)
- numbers = m.groups()
- host = ('.').join(numbers[:4])
- port = (int(numbers[4]) << 8) + int(numbers[5])
- return (
- host, port)
- def parse229(resp, peer):
- if resp[:3] != '229':
- raise error_reply(resp)
- left = resp.find('(')
- if left < 0:
- raise error_proto(resp)
- right = resp.find(')', left + 1)
- if right < 0:
- raise error_proto(resp)
- if resp[left + 1] != resp[right - 1]:
- raise error_proto(resp)
- parts = resp[left + 1:right].split(resp[left + 1])
- if len(parts) != 5:
- raise error_proto(resp)
- host = peer[0]
- port = int(parts[3])
- return (
- host, port)
- def parse257(resp):
- if resp[:3] != '257':
- raise error_reply(resp)
- if len(resp) > 26 and resp[3:26] == ' Current directory is "':
- resp = '257 "' + resp[26:]
- if resp[3:5] != ' "':
- return ''
- dirname = ''
- i = 5
- n = len(resp)
- while i < n:
- c = resp[i]
- i = i + 1
- if c == '"':
- if i >= n or resp[i] != '"':
- break
- i = i + 1
- dirname = dirname + c
- return dirname
- def print_line(line):
- print(line)
- def ftpcp(source, sourcename, target, targetname='', type='I'):
- if not targetname:
- targetname = sourcename
- type = 'TYPE ' + type
- source.voidcmd(type)
- target.voidcmd(type)
- sourcehost, sourceport = parse227(source.sendcmd('PASV'))
- target.sendport(sourcehost, sourceport)
- treply = target.sendcmd('STOR ' + targetname)
- if treply[:3] not in (u'125', u'150'):
- raise error_proto
- sreply = source.sendcmd('RETR ' + sourcename)
- if sreply[:3] not in (u'125', u'150'):
- raise error_proto
- source.voidresp()
- target.voidresp()
- # okay decompiling /home/manager/.config/sublime-text-3/Packages/SFTP/sftp/ftplib2.pyc
- # uncompyle6 version 3.2.3
- # Python bytecode 3.3 (3230)
- # Decompiled from: Python 2.7.12 (default, Dec 4 2017, 14:50:18)
- # [GCC 5.4.0 20160609]
- # Embedded file name: /Users/wbond/Library/Application Support/Sublime Text 3/Packages/SFTP/sftp/ftps_transport.py
- # Compiled at: 2015-11-05 00:18:32
- # Size of source mod 2**32: 1762 bytes
- import socket, sys
- from . import ftplib2
- from .ftp_transport import FTP
- class FTPS(FTP):
- def create(self):
- ftp = ftplib2.FTP_TLS()
- if sys.version_info >= (3, ):
- ftp.encoding = self.remote_encoding
- return ftp
- def set_options(self):
- try:
- self.ftp.prot_p()
- except ftplib2.error_perm:
- try:
- self.ftp.prot_c()
- except ftplib2.error_perm as e:
- if not self.ftp.implicit_ssl:
- raise e
- def check_disconnect(self):
- if not self.ftp:
- return
- old_timeout = self.ftp.sock.gettimeout()
- try:
- if sys.version_info < (3, ):
- self.ftp.sock.settimeout(1e-05)
- else:
- self.ftp.sock.settimeout(0.0)
- self.ftp.getresp()
- except EOFError:
- self.ftp.sock.settimeout(old_timeout)
- except socket.error as e:
- if str(e).find('Errno 11]') == -1 and str(e).find('Errno 35]') == -1 and str(e).find('Errno 10035]') == -1 and str(e).find('Errno 10011]') == -1 and str(e).find('The read operation timed out') == -1 and str(e).find('operation did not complete') == -1:
- raise e
- self.ftp.sock.settimeout(old_timeout)
- # okay decompiling /home/manager/.config/sublime-text-3/Packages/SFTP/sftp/ftps_transport.pyc
- # uncompyle6 version 3.2.3
- # Python bytecode 3.3 (3230)
- # Decompiled from: Python 2.7.12 (default, Dec 4 2017, 14:50:18)
- # [GCC 5.4.0 20160609]
- # Embedded file name: /Users/wbond/Library/Application Support/Sublime Text 3/Packages/SFTP/sftp/ftp_transport.py
- # Compiled at: 2015-11-05 00:18:24
- # Size of source mod 2**32: 44384 bytes
- import sublime, os, sys, re, socket, traceback, time, datetime
- if sys.version_info < (3, ):
- str_cls = unicode
- FileNotFoundError = IOError
- else:
- str_cls = str
- from . import ftplib2
- from .secure_input import SecureInputThread
- from .file_transfer import FileTransfer, keepaliveize
- from .panel_printer import ProgressThread
- from .debug import get_debug, debug_print
- from .paths import canonicalize, dirname, is_dir, local_to_remote, remote_to_local
- from .errors import AuthenticationError, CancelledError, ConnectionError, DisconnectionError, handle_exception, NotFoundError, OperationError, PermissionError
- class FTP(FileTransfer):
- def __init__(self, printer, user=None, host=None, port=22, password=None, remote_time_offset=None, **kwargs):
- self.ftp = None
- self.in_progress = False
- self.closed = False
- self.supports_mfmt = False
- self.supports_utime = False
- self.supports_utime_utc = False
- self.last_command = ''
- super(FTP, self).__init__(printer, user, host, port, password, remote_time_offset, **kwargs)
- self.passive_mode = True
- if kwargs.get('ftp_passive_mode') is not None:
- self.passive_mode = kwargs['ftp_passive_mode']
- self.obey_passive_host = False
- if kwargs.get('ftp_obey_passive_host') is not None:
- self.obey_passive_host = kwargs['ftp_obey_passive_host']
- self.kwargs = kwargs
- return
- def debug(self, debug):
- if not self.ftp:
- return
- self.ftp.set_debuglevel(3 if debug else 0, self.handle_debug)
- def create(self):
- ftp = ftplib2.FTP()
- if sys.version_info >= (3, ):
- ftp.encoding = self.remote_encoding
- return ftp
- def set_options(self):
- pass
- def do_keepalive(self):
- if not self.ftp:
- return
- debug_print('SFTP: Doing keepalive', 2)
- def do_noop():
- self.last_command = 'NOOP'
- self.ftp.sendcmd('NOOP')
- self.handle_ftp_error(do_noop)
- def connect(self, quiet=False):
- if not quiet:
- progress = ProgressThread(self.printer, '\nConnecting to %s server "%s" as "%s"' % (
- self.__class__.__name__, self.host, self.user))
- try:
- def do_connect():
- self.ftp = self.create()
- self.debug(get_debug())
- type = self.ftp.connect(self.host, self.port, self.timeout)
- if re.search('IBM FTP', type, re.I) is not None and re.search('V1R', type) is not None:
- self.ibm_ftp = True
- if self.password is None:
- was_visible = self.printer.visible
- tries = 0
- while True:
- try:
- if tries > 2:
- raise AuthenticationError('Invalid login/password specified')
- input_thread = SecureInputThread("%s@%s's password" % (self.user, self.host))
- input_thread.start()
- input_thread.join()
- if was_visible:
- def show():
- self.printer.show()
- sublime.set_timeout(show, 1)
- tries += 1
- if input_thread.password is None:
- raise CancelledError('Cancelled')
- self.ftp.login(self.encode(self.user), self.encode(input_thread.password))
- break
- except ftplib2.error_perm as e:
- if re.match('530', str(e)) is None or tries > 2:
- raise e
- else:
- self.ftp.login(self.user, self.password)
- self.ftp.set_pasv(bool(self.passive_mode), self.obey_passive_host)
- self.set_options()
- if self.preserve_modification_times is True:
- try:
- def do_feat():
- self.last_command = 'FEAT'
- response = self.ftp.sendcmd('FEAT')
- if response.find('MFMT') != -1:
- self.supports_mfmt = True
- self.handle_ftp_error(do_feat)
- except Exception as e:
- pass
- if not self.supports_mfmt:
- try:
- def do_site():
- self.last_command = 'UTIME'
- self.ftp.sendcmd('SITE UTIME')
- self.handle_ftp_error(do_site)
- self.supports_utime = True
- except Exception as e:
- pass
- if not self.supports_mfmt and not self.supports_utime:
- extra = ''
- if os.name != 'nt':
- extra = ' SFTP connections do not have this limitation.'
- self.printer.write('\nYou have the setting "preserve_modification_times" set to true, however the FTP server you are connecting to supports neither the "MFMT" or "SITE UTIME" commands, and thus modification times can notbe preserved when uploading.%s\nPlease set "preserve_modification_times" to "download_only" or false to hide this message.' % extra)
- return
- self.handle_ftp_error(do_connect, True)
- self.pwd()
- self.lpwd()
- if not quiet:
- progress.stop('success')
- progress.join()
- except (OSError, PermissionError, ConnectionError, AuthenticationError, NotFoundError) as e:
- if not quiet:
- progress.stop('failure (%s)' % str(e))
- progress.join
- raise e
- except UnicodeDecodeError:
- if not quiet:
- progress.stop('failure (Encoding error)')
- progress.join
- raise
- def check_disconnect(self):
- if not self.ftp:
- return
- old_timeout = self.ftp.sock.gettimeout()
- try:
- self.ftp.sock.setblocking(0)
- self.ftp.getresp()
- except EOFError:
- self.ftp.sock.settimeout(old_timeout)
- except socket.error as e:
- if str(e).find('Errno 11]') == -1 and str(e).find('Errno 35]') == -1 and str(e).find('Errno 10035]') == -1 and str(e).find('Errno 10011]') == -1:
- raise e
- self.ftp.sock.settimeout(old_timeout)
- def handle_ftp_error(self, fn, initial_connection=False):
- try:
- if self.closed:
- raise CancelledError('Cancelled')
- self.check_disconnect()
- result = fn()
- return result
- except socket.gaierror as e:
- raise ConnectionError('Host does not exist')
- except EOFError as e:
- error = DisconnectionError('Disconnected')
- raise error
- except ftplib2.error_fixed_host as e:
- raise ConnectionError('Server sent passive reply with seemingly unroutable private IP address, used original hostname instead, but connection still failed. Please set ftp_obey_passive_host to true in sftp-config.json to use seemingly unroutable private IP from server.')
- except ftplib2.error_perm as e:
- if re.match('530', str(e)) is not None and re.search('TLS|SSL|encryption', str(e), re.I) is not None:
- error = AuthenticationError('Server requires FTPS connection')
- else:
- if re.match('552 disk full', str(e), re.I) is not None:
- error = PermissionError('Disk full or quota reached')
- else:
- if re.match('502', str(e)) is not None:
- error = AuthenticationError('Server does not support FTPS')
- else:
- if re.match('500', str(e)) is not None and str(e).find('not understood') != -1 and (str(e).find('AUTH') != -1 or str(e).find('PROT C') != -1):
- error = AuthenticationError('Server does not support FTPS')
- else:
- if re.match('534', str(e), re.I) is not None and re.search('Local policy on server does not allow TLS secure connections', str(e), re.I) is not None:
- error = AuthenticationError('Server does not support FTPS')
- else:
- if re.match('530', str(e)) is not None:
- error = AuthenticationError('Invalid login/password specified')
- else:
- if re.match('550', str(e)) is not None and (str(e).find('not found') != -1 or str(e).find('cannot find') != -1 or re.search('no such', str(e), re.I) is not None):
- error = NotFoundError('File not found')
- else:
- if re.match('550 ([^:]+):', str(e)) is not None and re.search('exists', str(e), re.I) is not None:
- match = re.match('550 ([^:]+):', str(e))
- error = PermissionError('%s already exists' % match.group(1))
- else:
- if re.match('550', str(e)) is not None and str(e).find('directory not found') != -1:
- error = NotFoundError('Folder not found')
- else:
- if re.match('550 Failed to change', str(e)) is not None:
- error = NotFoundError('Folder not found')
- else:
- if re.match('550', str(e)) is not None and re.search('allowed from your ip', str(e), re.I) is not None:
- error = ConnectionError('No connections allowed from your IP')
- else:
- if re.match('501', str(e)) is not None and re.search('Directory could not be opened', str(e), re.I) is not None:
- error = OperationError('Not a directory')
- else:
- if re.match('550', str(e)) is not None and re.search('permission|authorized|error opening file|access is denied', str(e), re.I) is not None:
- error = PermissionError('Permission denied')
- else:
- if re.match('550', str(e)) is not None and re.search('Requested action not taken', str(e), re.I) is not None:
- error = PermissionError('Permission denied')
- else:
- if re.match('550', str(e)) is not None and re.search('it is being used by another process', str(e), re.I) is not None:
- error = PermissionError('File in use by another process')
- else:
- if re.match('553', str(e)) is not None and re.search('permission|authorized|could not create file', str(e), re.I) is not None:
- error = PermissionError('Permission denied')
- else:
- if re.match('500 \\?', str(e)) is not None:
- error = PermissionError('Unknown 500 error')
- else:
- if self.passive_mode and re.match('550', str(e), re.I) is not None and re.search("Couldn't start listener on any port", str(e), re.I) is not None:
- error = ConnectionError('PASV mode error, please set ftp_passive_mode to false in sftp-config.json')
- else:
- if self.last_command == 'ECHMOD' and re.match('500', str(e)) is not None and str(e).find('not understood') != -1 and str(e).find('CHMOD') != -1:
- error = PermissionError('Server does not support chmod operations')
- else:
- if self.last_command == 'ECHMOD' and re.match('550', str(e)) is not None and str(e).find('not permitted') != -1 and str(e).find('CHMOD') != -1:
- error = PermissionError('Server does not support chmod operations')
- else:
- if self.last_command == 'ECHMOD' and re.match('504', str(e)) is not None and str(e).find('not implemented') != -1:
- error = PermissionError('Server does not support chmod operations')
- else:
- if self.last_command == 'ECHMOD' and re.match('500', str(e)) is not None and str(e).find('Unknown SITE command') != -1:
- error = PermissionError('Server does not support chmod operations')
- else:
- if re.match('500', str(e)) is not None and str(e).find('not understood') != -1 and str(e).find('CHMOD') != -1:
- error = PermissionError('Server does not support chmod, please comment out file_permissions and dir_permissions in sftp-config.json')
- else:
- if re.match('550', str(e)) is not None and str(e).find('not permitted') != -1 and str(e).find('CHMOD') != -1:
- error = PermissionError('Server does not support chmod, please comment out file_permissions and dir_permissions in sftp-config.json')
- else:
- if re.match('504', str(e)) is not None and str(e).find('not implemented') != -1 and self.last_command == 'CHMOD':
- error = PermissionError('Server does not support chmod, please comment out file_permissions and dir_permissions in sftp-config.json')
- else:
- if re.match('500', str(e)) is not None and str(e).find('Unknown SITE command') != -1 and self.last_command == 'CHMOD':
- error = PermissionError('Server does not support chmod, please comment out file_permissions and dir_permissions in sftp-config.json')
- else:
- if re.match('504', str(e)) is not None and str(e).find('not implemented') != -1 and self.last_command == 'UTIME':
- error = PermissionError('Server does not support preserving modification times, please set preserve_modification_times to false or "download_only" in sftp-config.json')
- else:
- if re.match('500', str(e)) is not None and str(e).find('not understood') != -1 and str(e).find('UTIME') != -1:
- error = PermissionError('Server does not support preserving modification times, please set preserve_modification_times to false or "download_only" in sftp-config.json')
- else:
- if re.match('500', str(e)) is not None and str(e).find('Unknown SITE command') != -1 and self.last_command == 'UTIME':
- error = PermissionError('Server does not support preserving modification times, please set preserve_modification_times to false or "download_only" in sftp-config.json')
- else:
- if re.match('500', str(e)) is not None and str(e).find('UTC Only') != -1:
- error = PermissionError('Server requires UTC SITE UTIME command')
- else:
- if re.match('500', str(e)) is not None and str(e).find('not understood') != -1 and str(e).find('FEAT') != -1 and self.last_command == 'FEAT':
- return
- if not self.ibm_ftp and re.match('501', str(e)) is not None and self.last_command == 'UTIME':
- return
- if re.match('550', str(e)) is not None and self.last_command == 'CWD':
- error = NotFoundError('FTP does not use drive letters in path names')
- else:
- if self.ibm_ftp and re.match('501', str(e)) is not None and str(e).find('Dsname') != -1:
- error = PermissionError('Invalid data set name')
- else:
- if self.passive_mode and re.match('501', str(e), re.I) is not None and re.search('Server cannot accept argument', str(e), re.I) is not None:
- error = ConnectionError('PASV mode error, please set ftp_passive_mode to false in sftp-config.json')
- else:
- if not self.passive_mode and re.match('501', str(e), re.I) is not None and re.search('Server cannot accept argument', str(e), re.I) is not None:
- error = ConnectionError('Active mode error, please set ftp_passive_mode to true in sftp-config.json')
- else:
- if not self.passive_mode and re.match('500', str(e), re.I) is not None and re.search('Illegal PORT command', str(e), re.I) is not None:
- error = ConnectionError('Active mode error, please set ftp_passive_mode to true in sftp-config.json')
- else:
- if re.match('522', str(e), re.I) is not None and re.search('session reuse required', str(e), re.I) is not None:
- error = ConnectionError('Server requires SSL session reuse, which is not currently implemented')
- else:
- backtrace = traceback.format_exc()
- handle_exception('Unknown Error', backtrace)
- error = OSError('Unknown Error')
- raise error
- except ftplib2.error_temp as e:
- if self.closed:
- error = CancelledError('Cancelled')
- else:
- if re.match('421', str(e), re.I) is not None and re.search('terminating connection', str(e), re.I) is not None:
- error = DisconnectionError('Disconnected')
- else:
- if re.match('421', str(e), re.I) is not None and re.search('(temporarily banned)', str(e), re.I) is not None:
- error = ConnectionError('Temporarily banned')
- else:
- if re.match('421', str(e), re.I) is not None and re.search('(timeout|idle|timed out)', str(e), re.I) is not None:
- error = DisconnectionError('Disconnected')
- else:
- if self.passive_mode and re.match('421', str(e), re.I) is not None and re.search('(could not create socket)', str(e), re.I) is not None:
- error = ConnectionError('PASV mode error, please set ftp_passive_mode to false in sftp-config.json')
- else:
- if re.match('426', str(e)) is not None and re.search('error creating file', str(e), re.I) is not None:
- error = PermissionError('Permission denied')
- else:
- if re.match('426', str(e), re.I) is not None:
- error = DisconnectionError('Disconnected')
- else:
- if str(e).rstrip(' ') == '421':
- error = ConnectionError('Too many open connections')
- else:
- if re.match('425', str(e), re.I) is not None:
- error = ConnectionError('Active mode error, please set ftp_passive_mode to true in sftp-config.json')
- else:
- if re.match('450', str(e)) is not None and re.search('Rupture de la liaison avec le serveur de fichier', str(e), re.I) is not None:
- error = DisconnectionError('Server link to file server lost')
- else:
- if re.match('450', str(e)) is not None and re.search('Link to file server lost', str(e), re.I) is not None:
- error = DisconnectionError('Server link to file server lost')
- else:
- if re.match('450', str(e)) is not None and re.search('no such', str(e), re.I) is not None:
- error = NotFoundError('File not found')
- else:
- if re.match('450', str(e)) is not None and re.search('not a directory', str(e), re.I) is not None:
- error = OperationError('Not a directory')
- else:
- if re.match('450', str(e)) is not None and re.search('permission denied', str(e), re.I) is not None:
- error = PermissionError('Permission denied')
- else:
- backtrace = traceback.format_exc()
- handle_exception('Unknown Error', backtrace)
- error = OSError('Unknown Error')
- raise error
- except ftplib2.error_reply as e:
- if self.closed:
- error = CancelledError('Cancelled')
- else:
- if re.match('221', str(e)) is not None and re.search('idle', str(e), re.I) is not None:
- error = DisconnectionError('Disconnected')
- else:
- backtrace = traceback.format_exc()
- handle_exception('Unknown Error', backtrace)
- error = OSError('Unknown Error')
- raise error
- except socket.error as e:
- if str(e).find('Errno 101]') != -1 or str(e) == 'timed out':
- if initial_connection:
- error = ConnectionError('Connection timeout')
- else:
- error = DisconnectionError('Disconnected')
- else:
- if str(e).find('Errno 111]') != -1:
- error = ConnectionError('Connection refused')
- else:
- if str(e).find('Errno 61]') != -1:
- error = ConnectionError('Connection refused')
- else:
- if self.closed:
- error = CancelledError('Cancelled')
- else:
- error = DisconnectionError('Disconnected')
- raise error
- except (ftplib2.Error, ftplib2.SSLError, IOError, EOFError, AttributeError) as e:
- if self.closed:
- error = CancelledError('Cancelled')
- else:
- backtrace = traceback.format_exc()
- handle_exception('Unknown Error', backtrace)
- error = OSError('Unknown Error')
- raise error
- return
- def handle_debug(self, string):
- string = string.replace('\\n', '\n').replace('\\r', '\r')
- string = string.replace('\r', '')
- if string == '\n' or string == '':
- return
- message = ''
- lines = string.strip('\n').split('\n')
- last = ''
- for line in lines:
- sep = '\n'
- prefix = ' '
- if not len(line):
- continue
- if line[0:5] == '*put*':
- current = 'SFTP Write'
- start = 8 if line[6] in (u'u', u'b') else 7
- cleaned_line = line[start:]
- else:
- if line[0:12] == '*put urgent*':
- current = 'SFTP Write'
- start = 15 if line[13] in (u'u', u'b') else 14
- cleaned_line = line[start:]
- else:
- if line[0:5] == '*get*':
- current = 'SFTP Read'
- start = 8 if line[6] in (u'u', u'b') else 7
- cleaned_line = line[start:]
- else:
- if line[0:6] == '*retr*':
- current = 'SFTP Read'
- start = 9 if line[7] in (u'u', u'b') else 8
- cleaned_line = line[start:]
- else:
- if line[0:6] == '*warn*':
- current = 'SFTP Warning'
- cleaned_line = line[7:]
- sep = ' '
- prefix = ''
- else:
- continue
- if cleaned_line == "'":
- continue
- if current != last:
- if message:
- debug_print(message.rstrip('\n'))
- message = current + ':' + sep
- message += prefix + cleaned_line + '\n'
- last = current
- if len(message):
- debug_print(message.rstrip('\n'))
- def close(self, disconnected=False):
- ftp = self.ftp
- in_progress = self.in_progress
- self.closed = True
- self.ftp = None
- self.in_progress = False
- if ftp is not None:
- if disconnected:
- ftp.close()
- elif ftp.creating_connection:
- ftp.close()
- else:
- try:
- if in_progress:
- ftp.abort()
- ftp.quit()
- except (ftplib2.Error, ftplib2.SSLError, IOError, EOFError):
- ftp.close()
- return
- @keepaliveize
- def chmod(self, file, mode=None, quiet=False, **kwargs):
- if not quiet:
- progress = ProgressThread(self.printer, '\nChmoding "%s" to "%s"' % (file, mode))
- try:
- def do_chmod():
- self.last_command = 'CHMOD' if quiet is False else 'ECHMOD'
- self.ftp.voidcmd('SITE CHMOD ' + self.encode(mode) + ' ' + self.encode(file))
- self.handle_ftp_error(do_chmod)
- if not quiet:
- progress.stop('success')
- progress.join()
- except (OSError, PermissionError, ConnectionError, AuthenticationError, NotFoundError) as e:
- if not quiet:
- progress.stop('failure (%s)' % str(e))
- progress.join()
- return [False, str_cls(e)]
- except UnicodeDecodeError:
- if not quiet:
- progress.stop('failure (Encoding error)')
- progress.join
- raise
- return [True, None]
- def cd(self, dir):
- dir = self.make_absolute_dir(dir, 'remote')
- if self.dir == dir:
- return
- try:
- def do_cd():
- stripped_dir = dir
- if dir != '/':
- stripped_dir = dir.rstrip('/')
- if self.ibm_ftp:
- stripped_dir = "'%s'" % dir.lstrip('/').replace('/', '.')
- self.last_command = 'CWD'
- self.ftp.cwd(self.encode(stripped_dir))
- self.handle_ftp_error(do_cd)
- except (OSError, PermissionError, ConnectionError, AuthenticationError, NotFoundError) as e:
- if str(e) == 'File not found':
- e = NotFoundError('Folder not found')
- raise e
- self.dir = dir
- def lcd(self, dir):
- dir = self.make_absolute_dir(dir, 'local')
- if dir == self.local_dir:
- return
- if not os.path.exists(dir):
- raise NotFoundError('Folder not found')
- self.local_dir = dir
- @keepaliveize
- def get(self, remote_files, path_map, quiet=False, **kwargs):
- if not isinstance(remote_files, list):
- remote_files = [
- remote_files]
- error = False
- single_file = len(remote_files) == 1
- list_cache = {}
- for remote_file in remote_files:
- file = remote_to_local(remote_file, path_map, self.remote_encoding)
- try:
- dir_error, cont = self.handle_get_dirs(file, remote_file, single_file)
- error = error or dir_error
- if cont:
- continue
- except (OSError, PermissionError, ConnectionError, AuthenticationError, NotFoundError) as e:
- if not quiet:
- self.printer.write('\nDownloading "%s" to "%s" ... failure (%s)' % (remote_file, file, str(e)))
- raise e
- try:
- if not quiet:
- progress = ProgressThread(self.printer, '\nDownloading "%s" to "%s"' % (remote_file, file))
- def do_get():
- nonlocal_ = {'handle': None}
- def download_handler(data):
- if not nonlocal_['handle']:
- nonlocal_['handle'] = open(file, 'wb')
- nonlocal_['handle'].write(data)
- basename = os.path.basename(remote_file)
- if self.ibm_ftp:
- data_set = dirname(remote_file).strip('/').replace('/', '.')
- basename = "'%s(%s)'" % (data_set, basename)
- self.in_progress = True
- self.last_command = 'RETR'
- self.ftp.retrbinary('RETR ' + self.encode(basename), download_handler)
- self.in_progress = False
- if nonlocal_['handle'] is None:
- nonlocal_['handle'] = open(file, 'wb')
- nonlocal_['handle'].close()
- return
- self.handle_ftp_error(do_get)
- if self.preserve_modification_times:
- if self.dir not in list_cache:
- success, result = self.list(self.dir, path_map, quiet=True, skip_symlinks=False)
- if success:
- list_cache[self.dir] = dict(result)
- else:
- list_cache[self.dir] = None
- if isinstance(list_cache[self.dir], dict):
- atime = list_cache[self.dir][os.path.basename(remote_file)]
- mtime = atime
- os.utime(file, (atime, mtime))
- if not quiet:
- progress.stop('success')
- progress.join()
- except (OSError, PermissionError, ConnectionError, AuthenticationError, NotFoundError) as e:
- if not quiet:
- progress.stop('failure (%s)' % str(e))
- progress.join()
- if not isinstance(e, (CancelledError, DisconnectionError)):
- if single_file and not quiet:
- self.printer.error(str(e))
- error = True
- continue
- raise e
- except UnicodeDecodeError:
- if not quiet:
- progress.stop('failure (Encoding error)')
- progress.join
- raise
- result = None
- if error and not quiet and not single_file:
- string = 'One or more errors occured while downloading files'
- result = string
- self.printer.write('\n' + string)
- self.printer.error(string)
- return [
- not error, result]
- def ls(self, path_map, include_self=True, config=None, skip_symlinks=True):
- try:
- offset = self.determine_time_offset(path_map, config)
- def do_ls():
- files = []
- def ls_handler(string):
- files.append(self.decode(string))
- self.last_command = 'LIST'
- self.ftp.retrlines('LIST -a', ls_handler)
- return files
- ls_res = self.handle_ftp_error(do_ls)
- files = self.parse_ls(ls_res, offset, include_self, skip_symlinks)
- if include_self:
- found_cur_dir = False
- if files:
- for file_ in files:
- if file_[0] == '.':
- found_cur_dir = True
- break
- if not found_cur_dir:
- timestamp = int(time.time())
- files.insert(0, ['.', timestamp])
- files = sorted(files, key=lambda ar: ar[0].lower())
- return files
- except (OSError, PermissionError, ConnectionError, AuthenticationError, NotFoundError) as e:
- if str(e) == 'Permission denied':
- e = ConnectionError('PASV mode disallowed, please set ftp_passive_mode to false in sftp-config.json')
- else:
- if str(e) == 'Connection timeout':
- if self.passive_mode:
- e = ConnectionError('PASV mode error, please set ftp_passive_mode to false in sftp-config.json')
- else:
- e = ConnectionError('Active mode error, please set ftp_passive_mode to true in sftp-config.json')
- raise e
- def check_symlink(self, basename):
- basename = re.sub(' -> .*$', '', basename)
- try:
- def do_ls():
- files = []
- def ls_handler(string):
- files.append(self.decode(string))
- self.last_command = 'LIST'
- self.ftp.retrlines('LIST -a ' + self.encode(basename + '/.'), ls_handler)
- return files
- ls_res = self.handle_ftp_error(do_ls)
- if ls_res is None or len(ls_res) == 0:
- return basename
- return canonicalize(basename, 'remote')
- except OperationError:
- return basename
- except (NotFoundError, PermissionError):
- return canonicalize(basename, 'remote')
- return
- @keepaliveize
- def mkdir(self, dir, chmod_dirs=None, **kwargs):
- try:
- self.cd(dir)
- self.printer.write('\nFolder "%s" already exists' % dir, key='sftp_mkdir', finish=True)
- if chmod_dirs:
- self.chmod('.', chmod_dirs, quiet=True)
- return
- except NotFoundError:
- pass
- except PermissionError:
- self.printer.write('\nChmoding folder "%s" to "%s" ... failure (Permission denied)' % (dir, chmod_dirs), key='sftp_mkdir', finish=True)
- return
- if dir[0] == '/':
- parent_dir = dirname(dir) + '/'
- if parent_dir == '//':
- parent_dir = '/'
- try:
- if self.dir != parent_dir:
- self.cd(parent_dir)
- except NotFoundError:
- self.mkdir(parent_dir, chmod_dirs)
- self.cd(parent_dir)
- dir = canonicalize(dir, 'remote')
- progress = ProgressThread(self.printer, '\nCreating folder "%s"' % dir)
- try:
- basename = os.path.basename(dir.rstrip('/'))
- def do_mkdir():
- self.last_command = 'MKD'
- self.ftp.mkd(self.encode(basename))
- self.handle_ftp_error(do_mkdir)
- try:
- chmod_error = False
- if chmod_dirs:
- self.chmod(basename, chmod_dirs, quiet=True)
- except PermissionError:
- chmod_error = True
- progress.stop('success')
- progress.join()
- if chmod_error:
- self.printer.write('\nChmoding folder "%s" to "%s" ... failure (Permission denied)' % (dir, chmod_dirs), key='sftp_mkdir', finish=True)
- except (OSError, PermissionError, ConnectionError, AuthenticationError, NotFoundError) as e:
- progress.stop('failure (%s)' % str(e))
- progress.join()
- raise e
- except UnicodeDecodeError:
- progress.stop('failure (Encoding error)')
- progress.join
- raise
- @keepaliveize
- def mv(self, names, quiet=False, **kwargs):
- old_filename = os.path.basename(names[0].rstrip('\\/'))
- new_filename = os.path.basename(names[1].rstrip('\\/'))
- dir = dirname(names[0])
- dir = canonicalize(dir, 'remote')
- try:
- self.cd(dir)
- except NotFoundError:
- if not quiet:
- self.printer.write('\nChanging to folder "%s" ... failure (Folder not found)' % dir)
- self.printer.error('Folder not found')
- return [False, 'Folder not found']
- if not quiet:
- progress = ProgressThread(self.printer, '\nRenaming "%s" to "%s"' % (names[0], names[1]))
- try:
- def do_mv():
- self.last_command = 'RENAME'
- self.ftp.rename(self.encode(old_filename), self.encode(new_filename))
- self.handle_ftp_error(do_mv)
- if not quiet:
- progress.stop('success')
- progress.join()
- except (PermissionError, NotFoundError) as e:
- if not quiet:
- progress.stop('failure (%s)' % str(e))
- progress.join()
- self.printer.error(str(e))
- return [False, str(e)]
- except (OSError, ConnectionError, AuthenticationError) as e:
- if not quiet:
- progress.stop('failure (%s)' % str(e))
- progress.join()
- raise e
- except UnicodeDecodeError:
- if not quiet:
- progress.stop('failure (Encoding error)')
- progress.join
- raise
- return [True, None]
- @keepaliveize
- def put(self, files, path_map, chmod_files=None, chmod_dirs=None, quiet=False, **kwargs):
- if not isinstance(files, list):
- files = [
- files]
- error = False
- single_file = len(files) == 1
- for file in files:
- remote_file = local_to_remote(file, path_map, self.remote_encoding)
- try:
- dir_error, cont = self.handle_put_dirs(file, remote_file, chmod_dirs, single_file)
- error = error or dir_error
- if cont:
- continue
- except (OSError, PermissionError, ConnectionError, AuthenticationError, NotFoundError) as e:
- if not quiet:
- self.printer.write('\nUploading "%s" to "%s" ... failure (%s)' % (file, remote_file, str(e)))
- if single_file and not isinstance(e, (CancelledError, DisconnectionError)) and not quiet:
- self.printer.error(e)
- raise e
- try:
- if not quiet:
- progress = ProgressThread(self.printer, '\nUploading "%s" to "%s"' % (file, remote_file))
- basename = os.path.basename(remote_file)
- def do_put():
- local_basename = basename
- handle = open(file, 'rb')
- self.in_progress = True
- if self.ibm_ftp:
- data_set = dirname(remote_file).strip('/').replace('/', '.')
- local_basename = "'%s(%s)'" % (data_set, local_basename)
- self.last_command = 'STOR'
- self.ftp.storbinary('STOR ' + self.encode(local_basename), handle)
- self.in_progress = False
- handle.close()
- mod_date = int(os.lstat(file)[8])
- if self.supports_utime:
- mod_datetime = datetime.datetime.fromtimestamp(mod_date + self.remote_time_offset).strftime('%Y%m%d%H%M%S')
- try:
- def try_utime():
- self.last_command = 'UTIME'
- self.ftp.sendcmd('SITE UTIME ' + self.encode(mod_datetime) + ' ' + self.encode(basename))
- self.handle_ftp_error(try_utime)
- except:
- self.supports_utime = False
- self.supports_utime_utc = True
- if self.supports_utime_utc:
- utc_mod_datetime = datetime.datetime.fromtimestamp(mod_date).strftime('%Y%m%d%H%M%S')
- self.last_command = 'UTIME'
- self.ftp.sendcmd('SITE UTIME ' + self.encode(basename) + ' ' + self.encode(utc_mod_datetime) + ' ' + self.encode(utc_mod_datetime) + ' ' + self.encode(utc_mod_datetime) + ' UTC')
- if self.supports_mfmt:
- mod_datetime = datetime.datetime.utcfromtimestamp(mod_date + self.remote_time_offset).strftime('%Y%m%d%H%M%S')
- self.last_command = 'MFMT'
- self.ftp.sendcmd('MFMT ' + self.encode(mod_datetime) + ' ' + self.encode(basename))
- self.handle_ftp_error(do_put)
- try:
- chmod_error = False
- if chmod_files:
- self.chmod(basename, chmod_files, quiet=True)
- except PermissionError:
- chmod_error = True
- if not quiet:
- progress.stop('success')
- progress.join()
- if chmod_error:
- self.printer.write('\nChmoding folder "%s" to "%s" ... failure (Permission denied)' % (remote_file, chmod_files), key='sftp_put_chmod', finish=True)
- except (PermissionError, AuthenticationError, NotFoundError) as e:
- if not quiet:
- progress.stop('failure (%s)' % str(e))
- progress.join()
- if not isinstance(e, (CancelledError, DisconnectionError)):
- if single_file and not quiet:
- self.printer.error(str(e))
- error = True
- continue
- raise e
- except (OSError, ConnectionError) as e:
- if not quiet:
- progress.stop('failure (%s)' % str(e))
- progress.join()
- raise
- except UnicodeDecodeError:
- if not quiet:
- progress.stop('failure (Encoding error)')
- progress.join
- raise
- result = None
- if error and not quiet and not single_file:
- string = 'One or more errors occured while uploading files'
- result = string
- self.printer.write('\n' + string)
- self.printer.error(string)
- return [
- not error, result]
- def pwd(self):
- if self.dir is None:
- def do_pwd():
- self.last_command = 'PWD'
- self.dir = self.decode(self.ftp.pwd())
- if self.ibm_ftp:
- self.dir = '/' + self.dir.strip("'").replace('.', '/')
- self.handle_ftp_error(do_pwd)
- self.dir = canonicalize(self.dir, 'remote')
- return self.dir
- def lpwd(self):
- if self.local_dir is None:
- try:
- self.local_dir = os.getcwd()
- except FileNotFoundError:
- os.chdir(os.path.expanduser('~'))
- self.local_dir = os.getcwd()
- self.local_dir = canonicalize(self.local_dir, 'local')
- return self.local_dir
- @keepaliveize
- def rm(self, remote_files, path_map, quiet=False, **kwargs):
- if not isinstance(remote_files, list):
- remote_files = [
- remote_files]
- error = False
- single_file = len(remote_files) == 1
- for remote_file in remote_files:
- file = remote_to_local(remote_file, path_map, self.remote_encoding)
- dir_error, cont = self.handle_rm_dirs(file, remote_file, single_file)
- error = error or dir_error
- if cont:
- continue
- try:
- if not quiet:
- progress = ProgressThread(self.printer, '\nDeleting "%s"' % remote_file)
- def do_rm():
- if is_dir(remote_file):
- file = remote_file.rstrip('/\\')
- file = os.path.basename(file)
- if self.ibm_ftp:
- file = remote_file.strip('/').replace('/', '.')
- file = "'%s'" % file
- self.last_command = 'RMD'
- self.ftp.rmd(self.encode(file))
- else:
- file = os.path.basename(remote_file)
- if self.ibm_ftp:
- data_set = dirname(remote_file).strip('/').replace('/', '.')
- file = "'%s(%s)'" % (data_set, file)
- self.last_command = 'DELE'
- self.ftp.delete(self.encode(file))
- self.handle_ftp_error(do_rm)
- if not quiet:
- progress.stop('success')
- progress.join()
- except (OSError, PermissionError, ConnectionError, AuthenticationError, NotFoundError) as e:
- if not quiet:
- progress.stop('failure (%s)' % str(e))
- progress.join()
- if not isinstance(e, (CancelledError, DisconnectionError)):
- if single_file and not quiet:
- self.printer.error(str(e))
- error = True
- continue
- raise e
- except UnicodeDecodeError:
- if not quiet:
- progress.stop('failure (Encoding error)')
- progress.join
- raise
- result = None
- if error and not quiet and not single_file:
- string = 'One or more errors occured while removing files'
- result = string
- self.printer.write('\n' + string)
- self.printer.error(string)
- return [
- not error, result]
- # okay decompiling /home/manager/.config/sublime-text-3/Packages/SFTP/sftp/ftp_transport.pyc
- # uncompyle6 version 3.2.3
- # Python bytecode 3.3 (3230)
- # Decompiled from: Python 2.7.12 (default, Dec 4 2017, 14:50:18)
- # [GCC 5.4.0 20160609]
- # Embedded file name: /Users/wbond/Library/Application Support/Sublime Text 3/Packages/SFTP/sftp/__init__.py
- # Compiled at: 2015-11-05 00:16:24
- pass
- # okay decompiling /home/manager/.config/sublime-text-3/Packages/SFTP/sftp/__init__.pyc
- # uncompyle6 version 3.2.3
- # Python bytecode 3.3 (3230)
- # Decompiled from: Python 2.7.12 (default, Dec 4 2017, 14:50:18)
- # [GCC 5.4.0 20160609]
- # Embedded file name: /Users/wbond/Library/Application Support/Sublime Text 3/Packages/SFTP/sftp/listeners.py
- # Compiled at: 2015-11-05 00:18:34
- # Size of source mod 2**32: 13010 bytes
- import sublime, sublime_plugin, shutil, re, time, os
- from .config import build_config, find_config, load_config, prepare_server_config, setup_tmp_dir
- from .debug import debug_print
- from .panel_printer import PanelPrinter
- from .commands import SftpCommand
- from .paths import dirname
- from .views import get_all_views
- from .threads import ThreadTracker, HookedThread
- def find_window(callback, view):
- window = view.window()
- if window is None:
- windows = sublime.windows()
- for w in windows:
- for v in w.views():
- if v.id() == view.id():
- window = w
- break
- if window:
- break
- if not window:
- if view.size() == 0 and view.name() == '' and view.file_name() is None:
- return
- sublime.set_timeout(lambda : find_window(callback, view), 200)
- return
- return callback(window)
- class SftpCloseListener(sublime_plugin.EventListener):
- def on_close(self, view):
- if view is None or isinstance(view, bool):
- return
- else:
- view.erase_status('sftp_monitor')
- return
- class SftpLoadListener(sublime_plugin.EventListener, SftpCommand):
- def on_load(self, view):
- if view is None or isinstance(view, bool):
- return
- else:
- settings = view.settings()
- if settings.get('is_widget'):
- return
- was_synced = settings.get('synced')
- remote_loading = settings.get('remote_loading')
- settings.set('remote_loading', False)
- if not remote_loading:
- settings.set('synced', False)
- if was_synced and not remote_loading:
- settings.set('incomplete_sync', None)
- DoSyncThread(self.extract_settings(settings), view, view.file_name()).start()
- return
- def on_modified(self, view):
- if view is None or isinstance(view, bool):
- return
- else:
- settings = view.settings()
- if settings.get('is_widget'):
- return
- if not settings.get('incomplete_sync'):
- return
- settings.set('incomplete_sync', None)
- if settings.get('synced'):
- return
- DoSyncThread(self.extract_settings(settings), view, view.file_name()).start()
- return
- def on_activated(self, view):
- if view is None or isinstance(view, bool):
- return
- elif view.is_loading():
- return
- else:
- settings = view.settings()
- if settings.get('is_widget'):
- return
- def try_sync(window):
- ids = [v.id() for v in window.views()]
- if view.id() not in ids:
- settings.set('incomplete_sync', True)
- return
- else:
- settings.set('incomplete_sync', None)
- DoSyncThread(self.extract_settings(settings), view, view.file_name()).start()
- return
- def do_find_window():
- find_window(try_sync, view)
- if sublime.platform() == 'osx':
- svn_settings = sublime.load_settings('SFTP.sublime-settings')
- sync_down_on_open_delay = int(svn_settings.get('osx_sync_down_on_open_delay', 500))
- sublime.set_timeout(do_find_window, sync_down_on_open_delay)
- else:
- do_find_window()
- return
- def extract_settings(self, settings):
- output = {}
- for key in ['is_remote', 'tmp_dir', 'local_path', 'synced']:
- output[key] = settings.get(key)
- return output
- class DoSyncThread(HookedThread):
- def __init__(self, settings, view, path):
- self.settings = settings
- self.view = view
- self.path = path
- super(DoSyncThread, self).__init__()
- def run(self):
- settings = self.settings
- view = self.view
- path = self.path
- has_config = bool(find_config(path, True))
- if settings.get('is_remote') and not has_config:
- tmp_dir = settings.get('tmp_dir')
- local_path = settings.get('local_path')
- try:
- if os.name == 'nt' and len(tmp_dir) and tmp_dir == '/':
- raise OSError('Switched platforms')
- if os.name != 'nt' and len(tmp_dir) and re.match('^[a-z]:\\\\', tmp_dir, re.I) is not None:
- raise OSError('Switched platforms')
- if not local_path:
- if not os.path.exists(tmp_dir):
- os.makedirs(tmp_dir)
- remote_name = os.path.basename(tmp_dir.rstrip('/\\'))
- if remote_name == 'mapped':
- return
- raw_config = prepare_server_config(remote_name)
- config_file = raw_config['file_path']
- else:
- raw_config, config_file = load_config(local_path)
- except (OSError, IOError):
- raw_config = None
- if raw_config is None:
- def show_error():
- sublime.error_message('Sublime SFTP\n\nError loading config for remote file - please close and reopen the file')
- view.settings().set('is_remote', None)
- return
- sublime.set_timeout(show_error, 1)
- return
- tmp_dir = setup_tmp_dir(raw_config, tmp_dir)
- has_config = True
- if not has_config:
- return
- else:
- try:
- config, config_file = load_config(path)
- except IOError:
- config = None
- if config is None:
- return
- config_dir = dirname(config_file)
- config = build_config(config, config_dir, config_file, True, skip_settings=True)
- if not config or not config.get('sync_down_on_open'):
- return
- if 'ignore_regex' in config and re.search(config['ignore_regex'], path) is not None:
- return
- if config.get('name') and not settings.get('is_remote'):
- return
- if settings.get('synced'):
- return
- params = {'paths': [
- path],
- 'ignore_delete': True,
- 'on_complete': 'open_refresh',
- 'reset_lcd': dirname(dirname(config.get('local_dir'))),
- 'synchronous': True}
- def execute_sync():
- if view.settings().get('synced'):
- return
- def do_real_sync(window):
- debug_print('SFTP: Starting Sync Down on Open', 2)
- window.run_command('sftp_sync_down', params)
- find_window(do_real_sync, view)
- view.settings().set('synced', True)
- sublime.set_timeout(execute_sync, 300)
- return
- class DelayedDeleteThread(HookedThread):
- def __init__(self, tmp_dir, window_id):
- self.window_id = window_id
- self.tmp_dir = tmp_dir
- super(DelayedDeleteThread, self).__init__()
- def run(self):
- last_thread = ThreadTracker.get_last_added(self.window_id)
- if last_thread:
- last_thread.join()
- if os.path.exists(self.tmp_dir):
- try:
- shutil.rmtree(self.tmp_dir)
- except WindowsError:
- pass
- class SftpAutoUploadListener(sublime_plugin.EventListener, SftpCommand):
- def on_close(self, view):
- if view is None or isinstance(view, bool):
- return
- else:
- settings = view.settings()
- if settings.get('is_remote') and settings.get('tmp_dir'):
- tmp_dir = settings.get('tmp_dir')
- for _view in get_all_views(sublime.active_window()):
- if _view.id() == view.id():
- continue
- if _view.settings().get('tmp_dir') == tmp_dir:
- return
- DelayedDeleteThread(dirname(tmp_dir), sublime.active_window().id()).start()
- return
- def on_post_save(self, view):
- view_settings = view.settings()
- if view_settings.get('sftp_auto_save'):
- view_settings.erase('sftp_auto_save')
- return
- def do_upload():
- initial_dupes = view_settings.get('sftp_duplicate_views', 0) or 0
- if initial_dupes > 0:
- if (view_settings.get('sftp_duplicate_views', 0) or 0) > 0:
- view_settings.set('sftp_duplicate_views', view_settings.get('sftp_duplicate_views') - 1)
- return
- else:
- window = view.window()
- if window is None:
- window = sublime.active_window()
- if window is not None:
- for other_view in window.views():
- if other_view.id() == view.id():
- continue
- if other_view.file_name() == view.file_name():
- view_settings.set('sftp_duplicate_views', (view_settings.get('sftp_duplicate_views', 0) or 0) + 1)
- continue
- path = self.get_path(view=view)
- if not path:
- return
- if not self.has_config(path):
- return
- quiet = True
- if os.path.basename(view.file_name()) in (u'sftp-config.json', u'sftp-settings.json'):
- quiet = False
- config = self.get_config(path, quiet=quiet)
- if not config or not config.get('upload_on_save'):
- return
- if config.get('ignore_regex') and re.search(config.get('ignore_regex'), path) is not None:
- debug_print('SFTP: Ignoring file save event due to ignore_regex', 2)
- return
- params = {'paths': [path]}
- if view_settings.get('is_remote') and view_settings.get('tmp_dir'):
- params['reset_lcd'] = dirname(dirname(view_settings.get('tmp_dir')))
- def execute_upload(window):
- window.run_command('sftp_upload_file', params)
- find_window(execute_upload, view)
- return
- sublime.set_timeout(do_upload, 10)
- class SftpAutoConnectListener(sublime_plugin.EventListener, SftpCommand):
- def on_post_save(self, view):
- view_settings = view.settings()
- if not view_settings.get('sftp_new_server'):
- return
- else:
- view_settings.set('sftp_new_server', None)
- def connect():
- if view.window():
- view.window().run_command('sftp_browse_server', {'name': os.path.basename(view.file_name())})
- sublime.set_timeout(connect, 500)
- return
- class SftpFocusListener(sublime_plugin.EventListener, SftpCommand):
- def __init__(self):
- self.keys = []
- self.last_query = 0
- def check_keys(self, window):
- if window is None:
- return
- else:
- if self.keys[-1] == 'panel_visible':
- PanelPrinter.get(window.id()).visible = False
- return
- def on_query_context(self, view, key, operator, operand, match_all):
- if view is None or isinstance(view, bool) or view.window() is None:
- return
- else:
- printer = PanelPrinter.get(view.window().id())
- if not hasattr(printer, 'panel') or not printer.panel:
- return
- if time.time() <= self.last_query + 1:
- self.keys.append(key)
- else:
- sublime.set_timeout(lambda : self.check_keys(view.window()), 100)
- self.keys = [key]
- self.last_query = time.time()
- return
- def on_modified(self, view):
- if view is None or view.window() is None:
- return
- else:
- printer = PanelPrinter.get(view.window().id())
- if not printer.visible:
- return
- if hasattr(printer, 'panel') and printer.panel and printer.panel.id() == view.id():
- printer.visible = True
- return
- if view.settings().get('is_widget') and view.buffer_id() >= 3 and view.buffer_id() <= 19:
- printer.visible = False
- return
- def on_activated(self, view):
- if view is None or view.window() is None:
- return
- else:
- printer = PanelPrinter.get(view.window().id())
- if not printer.visible:
- return
- if hasattr(printer, 'panel') and printer.panel and printer.panel.id() == view.id():
- printer.visible = True
- return
- if view.settings().get('is_widget') and view.buffer_id() >= 3 and view.buffer_id() <= 19:
- printer.visible = False
- return
- # okay decompiling /home/manager/.config/sublime-text-3/Packages/SFTP/sftp/listeners.pyc
- # uncompyle6 version 3.2.3
- # Python bytecode 3.3 (3230)
- # Decompiled from: Python 2.7.12 (default, Dec 4 2017, 14:50:18)
- # [GCC 5.4.0 20160609]
- # Embedded file name: /Users/wbond/Library/Application Support/Sublime Text 3/Packages/SFTP/sftp/panel_printer.py
- # Compiled at: 2015-11-05 00:18:37
- # Size of source mod 2**32: 7066 bytes
- import sublime, os, random, time
- try:
- str_cls = unicode
- st_version = 2
- except NameError:
- str_cls = str
- st_version = 3
- if os.name != 'nt':
- import unicodedata
- from .threads import HookedThread, ThreadTracker
- class PanelPrinter(object):
- printers = {}
- def __init__(self):
- self.name = 'sftp'
- self.visible = False
- self.hide_time = 0
- self.queue = []
- self.strings = {}
- self.just_error = False
- self.capture = False
- self.input = None
- self.input_start = None
- self.on_input_complete = None
- self.original_view = None
- return
- @classmethod
- def get(cls, window_id):
- printer = cls.printers.get(window_id)
- if not printer:
- printer = PanelPrinter()
- printer.window_id = window_id
- printer.init()
- cls.printers[window_id] = printer
- return printer
- def adjust_color_scheme(self):
- current_color_scheme = sublime.load_settings('Base File.sublime-settings').get('color_scheme')
- if current_color_scheme:
- current_color_scheme = current_color_scheme.replace('Packages/', '')
- sftp_color_scheme = current_color_scheme.replace('Color Scheme - Default/', 'SFTP/schemes/').replace('.tmTheme', '.sftpTheme')
- sftp_color_scheme_path = os.path.join(sublime.packages_path(), sftp_color_scheme)
- current_panel_scheme = self.panel.settings().get('color_scheme')
- if current_panel_scheme:
- current_panel_scheme = current_panel_scheme.replace('Packages/', '')
- if os.path.exists(sftp_color_scheme_path):
- if sftp_color_scheme != current_panel_scheme:
- self.panel.settings().set('color_scheme', 'Packages/' + sftp_color_scheme)
- if self.panel.settings().get('syntax') != 'Packages/SFTP/schemes/Output.hidden-tmLanguage':
- self.panel.settings().set('syntax', 'Packages/SFTP/schemes/Output.hidden-tmLanguage')
- return
- if current_color_scheme != current_panel_scheme:
- self.panel.settings().set('color_scheme', 'Packages/' + current_color_scheme)
- if self.panel.settings().get('syntax') != 'Packages/SFTP/schemes/Custom Output.hidden-tmLanguage':
- self.panel.settings().set('syntax', 'Packages/SFTP/schemes/Custom Output.hidden-tmLanguage')
- def error(self, string):
- callback = lambda : self.error_callback(string)
- sublime.set_timeout(callback, 1)
- def error_callback(self, string):
- string = str(string)
- self.reset_hide()
- self.just_error = True
- sublime.error_message('Sublime SFTP\n\n' + string)
- def hide(self, thread=None):
- settings = sublime.load_settings('SFTP.sublime-settings')
- hide = settings.get('hide_output_panel', 1)
- try:
- if hide and not isinstance(hide, bool):
- hide_time = time.time() + float(hide)
- self.hide_time = hide_time
- sublime.set_timeout(lambda : self.hide_callback(hide_time, thread), int(hide * 1000))
- except ValueError:
- message = 'Sublime SFTP\n\nThe "hide_output_panel" setting in Preferences > Package Settings > SFTP > Settings - User must be a number, or one of:\n\ntrue\nfalse'
- sublime.set_timeout(lambda : sublime.error_message(message), 10)
- def hide_callback(self, hide_time, thread):
- if thread:
- last_added = ThreadTracker.get_last_added(self.window_id)
- if thread != last_added:
- return
- if self.visible and self.hide_time and hide_time == self.hide_time:
- if not self.just_error:
- self.window.run_command('hide_panel')
- self.just_error = False
- def init(self):
- if not hasattr(self, 'panel'):
- self.window = sublime.active_window()
- if st_version == 2:
- self.panel = self.window.get_output_panel(self.name)
- else:
- self.panel = self.window.create_output_panel(self.name)
- self.panel.set_read_only(True)
- self.panel.settings().set('syntax', 'Packages/SFTP/schemes/Output.hidden-tmLanguage')
- self.panel.settings().set('word_wrap', True)
- self.panel.settings().set('window_id', self.window.id())
- self.adjust_color_scheme()
- sublime.load_settings('Base File.sublime-settings').add_on_change('color_scheme', self.adjust_color_scheme)
- def reset_hide(self):
- self.hide_time = None
- return
- def show(self, force=False):
- self.init()
- settings = sublime.load_settings('SFTP.sublime-settings')
- hide = settings.get('hide_output_panel', 1)
- if force or hide is not True or not isinstance(hide, bool):
- self.visible = True
- self.window.run_command('show_panel', {'panel': 'output.' + self.name})
- def write(self, string, key='sublime_sftp', finish=False):
- if not len(string) and not finish:
- return
- else:
- if key not in self.strings:
- self.strings[key] = []
- self.queue.append(key)
- if len(string):
- if not isinstance(string, str_cls):
- string = str_cls(string, 'UTF-8', errors='strict')
- if os.name != 'nt':
- string = unicodedata.normalize('NFC', string)
- self.strings[key].append(string)
- if finish:
- self.strings[key].append(None)
- sublime.set_timeout(self.write_callback, 0)
- return key
- def write_callback(self):
- found = False
- for key in self.strings.keys():
- if len(self.strings[key]):
- found = True
- continue
- if not found:
- return
- self.panel.run_command('sftp_write_panel')
- size = self.panel.size()
- sublime.set_timeout(lambda : self.panel.show(size, True), 2)
- class ProgressThread(HookedThread):
- def __init__(self, printer, beginning=''):
- self.printer = printer
- self.cont = True
- self.beginning = beginning
- self.ending = ''
- self.key = 'sublime_sftp_' + str(random.randint(1, 1000))
- self.printer.write(self.beginning + ' .', self.key)
- super(ProgressThread, self).__init__()
- self.start()
- def get_string(self):
- if self.cont:
- return '.'
- if self.ending:
- string = '. ' + self.ending
- self.ending = ''
- return string
- return ''
- def run(self):
- while self.cont:
- time.sleep(0.15)
- self.printer.write(self.get_string(), self.key)
- self.printer.write(self.get_string(), self.key, True)
- def stop(self, string):
- self.cont = False
- self.ending = string
- # okay decompiling /home/manager/.config/sublime-text-3/Packages/SFTP/sftp/panel_printer.pyc
- # uncompyle6 version 3.2.3
- # Python bytecode 3.3 (3230)
- # Decompiled from: Python 2.7.12 (default, Dec 4 2017, 14:50:18)
- # [GCC 5.4.0 20160609]
- # Embedded file name: /Users/wbond/Library/Application Support/Sublime Text 3/Packages/SFTP/sftp/paths.py
- # Compiled at: 2015-11-05 00:18:40
- # Size of source mod 2**32: 4730 bytes
- import locale, re, os
- if os.name != 'nt':
- import unicodedata
- try:
- str_cls = unicode
- str_cls_name = 'unicode'
- except NameError:
- str_cls = str
- str_cls_name = 'str'
- def canonicalize(dir, _type):
- if _type == 'local' and os.name == 'nt':
- if dir[-1] != '\\':
- dir += '\\'
- else:
- if dir[-1] != '/':
- dir += '/'
- if os.name != 'nt':
- if type(dir).__name__ != str_cls_name:
- dir = str_cls(dir, 'utf-8', errors='strict')
- dir = unicodedata.normalize('NFC', dir)
- return dir
- def local_to_remote(path, path_map, encoding=None):
- if type(path).__name__ != str_cls_name:
- try:
- path = str_cls(path, 'utf-8', errors='strict')
- except UnicodeDecodeError:
- if encoding == 'utf-8' or encoding is None:
- encoding = locale.getpreferredencoding(do_setlocale=True)
- path = str_cls(path, encoding)
- if os.name != 'nt':
- path = unicodedata.normalize('NFC', path)
- key = list(path_map.keys())[0]
- local_prefix = canonicalize(key, 'local')
- remote_prefix = canonicalize(path_map[key], 'remote')
- if os.path.isdir(path):
- path = canonicalize(path, 'local')
- remote_path = re.sub('^' + re.escape(local_prefix), remote_prefix.replace('\\', '\\\\'), path)
- if os.name == 'nt':
- remote_path = remote_path.replace('\\', '/')
- if os.path.isdir(path):
- remote_path = canonicalize(remote_path, 'remote')
- if os.name != 'nt':
- return unicodedata.normalize('NFC', remote_path)
- else:
- return remote_path
- def remote_to_local(remote_path, path_map, encoding=None):
- if type(remote_path).__name__ != str_cls_name:
- try:
- remote_path = str_cls(remote_path, 'utf-8', errors='strict')
- except UnicodeDecodeError:
- if encoding == 'utf-8' or encoding is None:
- encoding = 'cp1252'
- remote_path = str_cls(remote_path, encoding)
- key = list(path_map.keys())[0]
- local_prefix = canonicalize(key, 'local')
- remote_prefix = canonicalize(path_map[key], 'remote')
- if remote_path[-1] == '/':
- remote_path = canonicalize(remote_path, 'remote')
- path = re.sub('^' + re.escape(remote_prefix), local_prefix.replace('\\', '\\\\'), remote_path)
- if os.name == 'nt':
- path = path.replace('/', '\\')
- if remote_path[-1] == '/':
- path = canonicalize(path, 'local')
- return path
- def is_dir(path):
- return path[-1] in (u'/', u'\\')
- def is_root(path):
- if path == '/':
- return True
- elif re.search('^[A-Za-z]:\\\\$', path) is not None:
- return True
- elif re.search('^\\\\\\\\[^\\\\]+\\\\?$', path) is not None:
- return True
- else:
- return False
- def dirname(path):
- path = path.rstrip('/\\')
- path = re.sub('([/\\\\])[^/\\\\]+$', '\\1', path)
- return path
- def path_type(path, capitalize=False):
- type_ = 'folder' if is_dir(path) else 'file'
- if capitalize:
- type_ = 'F' + type_[1:]
- return type_
- def fix_windows_path(file):
- if os.name == 'nt':
- file = file.replace('\\', '/')
- file = re.sub('^([A-Za-z]):', '/\\1', file)
- file = file[0:2].upper() + file[2:]
- return file
- def ignore_paths(paths, config):
- unignored = len(paths)
- ignored = 0
- if 'ignore_regex' in config and config['ignore_regex']:
- new_paths = []
- for path in paths:
- test_path = path[0] if isinstance(path, list) else path
- if re.search(config['ignore_regex'], test_path):
- continue
- if test_path.replace('/', '\\') != test_path:
- if re.search(config['ignore_regex'], test_path.replace('/', '\\')):
- continue
- if test_path.replace('\\', '/') != test_path:
- if re.search(config['ignore_regex'], test_path.replace('\\', '/')):
- continue
- new_paths.append(path)
- paths = new_paths
- ignored = unignored - len(paths)
- unignored = len(paths)
- return (paths, unignored, ignored)
- def ignore_rm_paths(paths, config, type):
- new_to_rm, num_to_rm, num_rm_ignored = ignore_paths(paths, config)
- rm_ignored = list(set(paths) - set(new_to_rm))
- for rm_path in rm_ignored:
- while not is_root(rm_path):
- try:
- new_to_rm.remove(rm_path)
- except ValueError:
- pass
- rm_path = canonicalize(dirname(rm_path), type)
- return (
- sorted(new_to_rm, key=lambda s: s.lower()), len(new_to_rm), len(paths) - len(new_to_rm))
- # okay decompiling /home/manager/.config/sublime-text-3/Packages/SFTP/sftp/paths.pyc
- # uncompyle6 version 3.2.3
- # Python bytecode 3.3 (3230)
- # Decompiled from: Python 2.7.12 (default, Dec 4 2017, 14:50:18)
- # [GCC 5.4.0 20160609]
- # Embedded file name: /Users/wbond/Library/Application Support/Sublime Text 3/Packages/SFTP/sftp/proc.py
- # Compiled at: 2015-11-05 00:19:18
- # Size of source mod 2**32: 9847 bytes
- import sublime, os, subprocess, re, time, sys
- try:
- str_cls = unicode
- except NameError:
- str_cls = str
- if os.name != 'nt':
- import pty, tty, select
- from .debug import debug_print
- from .errors import CancelledError, DisconnectionError
- def find_binary(name):
- if os.name == 'nt':
- sftp_package_dir = os.path.join(sublime.packages_path(), 'SFTP', 'bin')
- dirs = [
- sftp_package_dir,
- 'C:\\Program Files\\Git\\bin',
- 'C:\\Program Files (x86)\\Git\\bin',
- 'C:\\Program Files\\Mercurial',
- 'C:\\Program Files (x86)\\Mercurial',
- 'C:\\Program Files (x86)\\TortoiseHg',
- 'C:\\Program Files\\TortoiseHg']
- dirs.extend(os.environ['PATH'].split(os.pathsep))
- else:
- dirs = os.environ['PATH'].split(os.pathsep)
- for dir in dirs:
- path = os.path.join(dir, name)
- if os.path.exists(path):
- return path
- return
- class NonInteractiveProcess(object):
- def __init__(self, cwd, *args):
- self.args = args
- self.cwd = cwd
- def run(self):
- if os.name == 'nt' and not hasattr(NonInteractiveProcess, 'acp'):
- startupinfo = subprocess.STARTUPINFO()
- startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW
- proc = subprocess.Popen([
- 'chcp'], stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, startupinfo=startupinfo, cwd=self.cwd, shell=True)
- stdout, stderr = proc.communicate()
- NonInteractiveProcess.acp = re.sub('^[^\\d]*(\\d+).*$', '\\1', stdout.decode('utf-8'))
- startupinfo = None
- if os.name == 'nt':
- startupinfo = subprocess.STARTUPINFO()
- startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW
- encoded_args = []
- encoding = sys.getfilesystemencoding()
- for arg in self.args:
- if sys.version_info >= (3, ):
- encoded_args.append(arg)
- else:
- encoded_args.append(arg.encode(encoding))
- proc = subprocess.Popen(encoded_args, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, startupinfo=startupinfo, cwd=self.cwd)
- output = proc.stdout.read()
- try:
- if os.name == 'nt':
- output = str_cls(output, 'cp' + NonInteractiveProcess.acp, errors='strict')
- else:
- output = str_cls(output, 'utf-8', errors='strict')
- except UnicodeDecodeError:
- output = str_cls(output, 'cp1252')
- return output.replace('\r\n', '\n').rstrip(' \n\r')
- class InteractiveProcess(object):
- def __init__(self, type, *args):
- self.args = args
- self.proc = None
- self.returncode = None
- self.opened = False
- self.closed = False
- self.debug = False
- self.open()
- return
- def open(self):
- if self.opened:
- return
- encoded_args = []
- encoding = sys.getfilesystemencoding()
- debug_print('SFTP Connection Args: ' + repr(self.args), 2)
- for arg in self.args:
- if sys.version_info >= (3, ):
- encoded_args.append(arg)
- else:
- encoded_args.append(arg.encode(encoding))
- if os.name == 'nt':
- startupinfo = subprocess.STARTUPINFO()
- startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW
- self.proc = subprocess.Popen(encoded_args, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, startupinfo=startupinfo)
- self.stdout = self.proc.stdout.fileno()
- self.stdin = self.proc.stdin.fileno()
- else:
- self.proc, child_fd = pty.fork()
- if not self.proc:
- os.execv(encoded_args[0], list(encoded_args))
- self.stdout = child_fd
- self.stdin = child_fd
- self.opened = True
- def write(self, command, encoding='utf-8'):
- self.open()
- ending = '\r\n' if os.name == 'nt' else '\n'
- debug_print('SFTP Write:' + ending + ' ' + command)
- try:
- os.write(self.stdin, command.encode(encoding) + ending.encode(encoding))
- except Exception:
- if self.closed:
- raise CancelledError('Cancelled')
- else:
- raise DisconnectionError('Disconnected')
- def read(self, until='sftp>\\s?$', timeout=1, encoding='utf-8', remove_prompt=True):
- self.open()
- output = ''
- start = time.time()
- try:
- while 1:
- if timeout and output == '' and time.time() > start + timeout:
- break
- if os.name != 'nt' and self.proc:
- pid, exit_status = os.waitpid(self.proc, os.WNOHANG)
- if pid != 0:
- if self.closed:
- e = CancelledError('Cancelled')
- else:
- e = DisconnectionError('Disconnected')
- e.returncode = exit_status >> 8
- raise e
- if os.name != 'nt' and self.proc is None:
- if self.closed:
- raise CancelledError('Cancelled')
- else:
- raise DisconnectionError('Disconnected')
- else:
- if os.name == 'nt' and (self.proc is None or self.proc.poll() is not None):
- if self.closed:
- raise CancelledError('Cancelled')
- else:
- raise DisconnectionError('Disconnected')
- output += str_cls(os.read(self.stdout, 32768), encoding, errors='strict')
- if not until or re.findall(until, output):
- break
- if output.find('\x07') != -1:
- self.close()
- def error_message():
- sublime.error_message('Sublime SFTP\n\nYour installation of OpenSSH seems to have a broken version of sftp that can not handle non-ASCII filenames. You can: use ftp instead, only work with ASCII filenames, or contact support@wbond.net for help in obtaining a non-broken binary.')
- sublime.set_timeout(error_message, 1)
- raise CancelledError('Cancelled')
- if self.debug:
- self.print_read(output)
- except OSError as e:
- if self.debug:
- self.print_read(output)
- if os.name != 'nt' and not hasattr(e, 'returncode') and not self.returncode:
- pid, bytes = os.wait()
- if not isinstance(e, (CancelledError, DisconnectionError)):
- if self.closed:
- e = CancelledError('Cancelled')
- else:
- e = DisconnectionError('Disconnected')
- raise e
- output = output.replace('\r\n', '\n')
- output = re.sub('.\x08', '', output)
- if remove_prompt:
- output = re.sub('\\s+p?sftp>\\s*$', '', output)
- return output.strip()
- def print_read(self, string):
- output = 'SFTP Read:\n'
- for line in re.sub('\n+', '\n', string.replace('\r', '\n')).split('\n'):
- output += ' ' + line + '\n'
- debug_print(output)
- def close(self, try_num=0):
- if not self.proc:
- return
- else:
- self.closed = True
- if type(self.proc).__name__ == 'int':
- try:
- if os is None:
- return
- os.kill(self.proc, 15)
- try:
- os.close(self.stdout)
- except OSError:
- pass
- pid, exit_status = os.waitpid(self.proc, os.WNOHANG)
- if exit_status:
- self.returncode = 1
- os.kill(self.proc, 0)
- if try_num > 4:
- os.kill(self.proc, 9)
- os.kill(self.proc, 0)
- if try_num < 10:
- sublime.set_timeout(lambda : self.close(try_num + 1), 20)
- return
- except TypeError:
- pass
- except OSError:
- pass
- self.proc = None
- else:
- if self.proc.returncode is None:
- try:
- self.proc.kill()
- if self.proc.returncode is None and try_num < 10:
- sublime.set_timeout(lambda : self.close(try_num + 1), 20)
- except WindowsError:
- pass
- return
- # okay decompiling /home/manager/.config/sublime-text-3/Packages/SFTP/sftp/proc.pyc
- # uncompyle6 version 3.2.3
- # Python bytecode 3.3 (3230)
- # Decompiled from: Python 2.7.12 (default, Dec 4 2017, 14:50:18)
- # [GCC 5.4.0 20160609]
- # Embedded file name: /Users/wbond/Library/Application Support/Sublime Text 3/Packages/SFTP/sftp/secure_input.py
- # Compiled at: 2015-11-05 00:19:23
- # Size of source mod 2**32: 1890 bytes
- import sublime, time
- from .threads import HookedThread
- class SecureInput(object):
- def __init__(self, prompt, on_done, on_cancel):
- self.prompt = prompt
- self.on_done_callback = on_done
- self.on_cancel = on_cancel
- self.input_view = None
- self.input = ''
- self.length = 0
- sublime.set_timeout(self.show_input, 1)
- sublime.set_timeout(self.on_cancel, 30000)
- return
- def capture_char(self, string):
- if len(string) == self.length:
- return
- if len(string) < self.length:
- self.input = self.input[0:len(string)]
- self.length = len(string)
- return
- if not self.input_view:
- return
- new_length = len(string) - len(self.input)
- self.input += string[len(self.input):]
- self.length = len(string)
- pos = len(string) - new_length
- self.input_view.run_command('sftp_replace_view', {'start': pos,
- 'end': pos + new_length,
- 'string': '*' * new_length})
- def on_done(self, string):
- self.on_done_callback(self.input)
- def show_input(self):
- self.input_view = sublime.active_window().show_input_panel(self.prompt, '', self.on_done, self.capture_char, self.on_cancel)
- class SecureInputThread(HookedThread):
- def __init__(self, prompt):
- self.prompt = prompt
- self.password = False
- super(SecureInputThread, self).__init__()
- def run(self):
- SecureInput(self.prompt, self.get_password, self.cancel_password)
- while self.password is False:
- time.sleep(0.01)
- def get_password(self, password):
- self.password = password
- def cancel_password(self):
- self.password = None
- return
- # okay decompiling /home/manager/.config/sublime-text-3/Packages/SFTP/sftp/secure_input.pyc
- # uncompyle6 version 3.2.3
- # Python bytecode 3.3 (3230)
- # Decompiled from: Python 2.7.12 (default, Dec 4 2017, 14:50:18)
- # [GCC 5.4.0 20160609]
- # Embedded file name: /Users/wbond/Library/Application Support/Sublime Text 3/Packages/SFTP/sftp/sftp_transport.py
- # Compiled at: 2015-11-10 11:29:17
- # Size of source mod 2**32: 33185 bytes
- import sublime, os, re, traceback, locale, time
- if os.name == 'nt':
- import ctypes
- try:
- str_cls = unicode
- except NameError:
- str_cls = str
- from .file_transfer import FileTransfer, keepaliveize
- from .panel_printer import ProgressThread
- from .proc import InteractiveProcess
- from .paths import canonicalize, dirname, is_dir, local_to_remote, path_type, remote_to_local
- from .debug import get_debug, debug_print
- from .errors import AuthenticationError, BinaryMissingError, CancelledError, ConnectionError, DisconnectionError, handle_exception, NotFoundError, PermissionError
- from .secure_input import SecureInput
- class SFTP(FileTransfer):
- def __init__(self, printer, user=None, host=None, port=22, password=None, remote_time_offset=None, **kwargs):
- self.closed = False
- super(SFTP, self).__init__(printer, user, host, port, password, remote_time_offset, **kwargs)
- args = [
- kwargs['binary']]
- if os.name == 'nt':
- self.type = 'psftp'
- args.extend(['-P', str(port)])
- if password is not None:
- args.extend(['-pw', str(password)])
- else:
- timeout = int(kwargs['timeout'])
- alive_interval = timeout
- if kwargs.get('keepalive'):
- keepalive = int(kwargs['keepalive'])
- if keepalive < alive_interval:
- alive_interval = keepalive
- self.type = 'sftp'
- args.append('-C')
- args.append('-oPort=%s' % port)
- args.append('-oConnectTimeout=%s' % str(timeout))
- args.append('-oServerAliveCountMax=1')
- args.append('-oServerAliveInterval=%s' % str(alive_interval))
- args.append('-oTCPKeepAlive=no')
- if kwargs.get('ssh_key_file'):
- if os.name == 'nt':
- args.extend(['-i', kwargs['ssh_key_file']])
- else:
- args.append('-oIdentityFile=' + kwargs['ssh_key_file'])
- if kwargs.get('sftp_flags'):
- if isinstance(kwargs['sftp_flags'], list):
- args.extend(kwargs['sftp_flags'])
- else:
- args.append(kwargs['sftp_flags'])
- args.append('%s@%s' % (user, host))
- self.args = args
- self.kwargs = kwargs
- return
- def debug(self, debug):
- self.proc.debug = debug
- def do_keepalive(self):
- debug_print('SFTP: Doing keepalive', 2)
- self.proc.write('cd .', self.remote_encoding)
- self.proc.read(encoding=self.remote_encoding)
- def connect(self, quiet=False):
- if not quiet:
- progress = ProgressThread(self.printer, '\nConnecting to SFTP server "%s" as "%s"' % (self.host, self.user))
- if self.args[0] is None:
- if os.name == 'nt':
- message = 'psftp.exe could not be found'
- else:
- message = 'sftp command line executable could not be found'
- progress.stop('failure (%s)' % message)
- progress.join()
- raise BinaryMissingError(message)
- try:
- if os.name == 'nt':
- def connect_timeout():
- if not self.connected and self.proc.proc is not None:
- ctypes.windll.kernel32.TerminateProcess(int(self.proc.proc._handle), -1)
- return
- sublime.set_timeout(connect_timeout, int(self.timeout) * 1000)
- self.proc = InteractiveProcess(self.type, *self.args)
- self.debug(get_debug())
- result = self.proc.read('sftp>|Broken pipe|[pP]assword:|Password for [^@]+@[^:]+:|PASSWORD:|continue connecting|[Pp]assphrase for key|Store key in cache|Host key verification failed|Update cached key\\?|UNPROTECTED PRIVATE KEY FILE|No route to host|Host does not exist|Could not resolve hostname|Connection (refused|reset)', timeout=self.timeout, remove_prompt=False)
- self.connected = True
- if result == '' or re.search('Broken pipe', result):
- if not quiet:
- progress.stop('failure (Connection timeout)')
- progress.join()
- raise ConnectionError('Connection timeout')
- if re.search('UNPROTECTED PRIVATE KEY FILE', result):
- if not quiet:
- progress.stop('failure (Unprotected private key file)')
- progress.join()
- command = (' ').join(self.args)
- message = '\nThe permissions on your SSH private key allow it to be read by other users and consequently the "sftp" command line program will not use it.\nPlease change the permissions so the file is not world readable. For more detailed help, open your terminal and execute the following:\n%s' % command
- sublime.set_timeout(lambda : self.printer.write(message), 200)
- raise ConnectionError('Unprotected private key file')
- if re.search('Host key verification failed|Update cached key\\?', result):
- if not quiet:
- progress.stop('failure (Host key verification failed)')
- progress.join()
- main_message = '\nThe SSH host key has changed. This could indicate a potential security breach, or that the domain you are connecting to recently moved servers.\nIf you are confident this is not a security breach you can delete the old host key and try again.\n'
- if os.name == 'nt':
- message = main_message + ' 1. Win XP: Start > Run > regedit.exe\n Win Vista/7: Start > regedit\n 2. Expand to HKEY_CURRENT_USER\\Software\\SimonTatham\\PuTTY\\SshHostKeys and delete the entry including @%s:%s' % (
- str(self.port), self.host)
- else:
- known_hosts_file = os.path.expanduser('~/.ssh/known_hosts')
- if str(self.port) == '22':
- host_file_beginning = self.host
- else:
- host_file_beginning = '[' + self.host + ']:' + str(self.port)
- message = main_message + ' 1. Open the file ' + known_hosts_file + '\n 2. Delete the line starting ' + 'with ' + host_file_beginning + '\n 3. Save your changes'
- sublime.set_timeout(lambda : self.printer.write(message), 200)
- raise ConnectionError('Host key verification failed')
- if re.search('Connection (refused|reset)', result):
- if not quiet:
- progress.stop('failure (Connection refused)')
- progress.join()
- raise ConnectionError('Connection refused')
- if re.search('No route to host', result):
- if not quiet:
- progress.stop('failure (No route to host)')
- progress.join()
- raise ConnectionError('No route to host')
- if re.search('Host does not exist|Could not resolve hostname', result) is not None:
- if not quiet:
- progress.stop('failure (Host does not exist)')
- progress.join()
- raise ConnectionError('Host does not exist')
- if os.name != 'nt' and re.search('continue connecting', result) is not None:
- self.proc.write('yes')
- result = self.proc.read('sftp>|[pP]assword:|PASSWORD:|[pP]assphrase for key', remove_prompt=False)
- if os.name == 'nt' and re.search('Store key in cache', result) is not None:
- self.proc.write('y')
- result = self.proc.read('sftp>|[pP]assword:|PASSWORD:|Password for [^@]+@[^:]+:|[pP]assphrase for key', remove_prompt=False)
- if os.name != 'nt' and self.password is not None and re.search('([pP]assword|PASSWORD):', result) is not None:
- self.proc.write(self.password)
- result = self.proc.read('sftp>|[pP]assword:|PASSWORD:', remove_prompt=False)
- was_visible = self.printer.visible
- password_state = {'cancelled': False, 'tries': 0, 'success': False}
- def cancel_password():
- if password_state['success']:
- return
- try:
- self.proc.write('')
- except OSError:
- pass
- if was_visible:
- self.printer.show()
- password_state['cancelled'] = True
- def write_password(password):
- password_state['tries'] += 1
- if password_state['tries'] > 2:
- password_state['cancelled'] = True
- try:
- self.proc.write(password)
- except OSError:
- pass
- if was_visible:
- self.printer.show()
- pass_prompt_regex = '([pP]assword|PASSWORD|Password for [^@]+@[^:]+):\\Z'
- while 1:
- if re.search('[Pp]assphrase for key [\'"](.*)[\'"]:', result) is not None and not password_state['cancelled']:
- match = re.search('[Pp](assphrase for key [\'"].*[\'"]):', result)
- SecureInput('P%s' % match.group(1), write_password, cancel_password)
- result = self.proc.read('sftp>|[pP]assword:|PASSWORD:|Password for [^@]+@[^:]+:|[pP]assphrase for key|[Pp]ermission denied', remove_prompt=False)
- if re.search('[Pp]assphrase for key', result) is not None or re.search(pass_prompt_regex, result) is not None and password_state['cancelled']:
- message = 'Invalid SSH key passphrase specified'
- if not quiet:
- progress.stop('failure (%s)' % message)
- progress.join()
- raise AuthenticationError(message)
- while 1:
- if re.search(pass_prompt_regex, result) is not None and not password_state['cancelled']:
- SecureInput("%s@%s's password" % (self.user, self.host), write_password, cancel_password)
- result = self.proc.read('sftp>|[pP]assword:|PASSWORD:|Password for [^@]+@[^:]+:|[Pp]ermission denied', remove_prompt=False)
- if re.search(pass_prompt_regex, result) is not None:
- message = 'Invalid login/password specified'
- if not quiet:
- progress.stop('failure (%s)' % message)
- progress.join()
- raise AuthenticationError(message)
- if re.search('[Pp]ermission denied', result) is not None:
- message = 'Invalid login credentials'
- if not quiet:
- progress.stop('failure (%s)' % message)
- progress.join()
- raise AuthenticationError(message)
- password_state['success'] = True
- except AuthenticationError as e:
- raise e
- except OSError as e:
- if isinstance(e, DisconnectionError):
- error = 'Connection timeout'
- e = ConnectionError(error)
- else:
- if isinstance(e, CancelledError):
- error = 'Unknown error'
- backtrace = traceback.format_exc()
- handle_exception('Unknown Error', backtrace)
- else:
- error = str(e)
- if not quiet:
- progress.stop('failure (' + error + ')')
- progress.join()
- raise e
- except UnicodeDecodeError:
- if not quiet:
- progress.stop('failure (Encoding error)')
- progress.join()
- raise
- if re.search('sftp>', result) is None:
- result = self.proc.read(remove_prompt=False)
- if not quiet:
- progress.stop('success')
- progress.join()
- if os.name != 'nt':
- self.proc.write('progress off')
- self.proc.read()
- self.pwd()
- self.lpwd()
- return
- def close(self, disconnected=False):
- self.proc.close()
- @keepaliveize
- def chmod(self, file, mode=None, quiet=False, **kwargs):
- if not quiet:
- progress = ProgressThread(self.printer, '\nChmoding "%s" to "%s"' % (file, mode))
- try:
- self.proc.write('chmod %s "%s"' % (mode, file), self.remote_encoding)
- result = self.proc.read(encoding=self.remote_encoding)
- if re.search('no such file or directory', result, re.I) is not None:
- raise NotFoundError('Folder not found')
- if re.search('permission denied', result, re.I) is not None:
- raise PermissionError('Permission denied')
- if not quiet:
- progress.stop('success')
- progress.join()
- except (NotFoundError, PermissionError) as e:
- if not quiet:
- progress.stop('failure (%s)' % str(e))
- progress.join()
- return [False, str(e)]
- except UnicodeDecodeError:
- if not quiet:
- progress.stop('failure (Encoding error)')
- progress.join()
- raise
- return [True, None]
- def cd(self, dir):
- dir = self.make_absolute_dir(dir, 'remote')
- if dir == self.dir:
- return
- else:
- self.proc.write('cd "%s"' % dir, self.remote_encoding)
- result = self.proc.read(encoding=self.remote_encoding)
- if re.search('no such file or directory', result, re.I) is not None:
- raise NotFoundError('Folder not found')
- if re.search('permission denied', result, re.I) is not None:
- raise PermissionError('Permission denied')
- self.dir = dir
- return
- def lcd(self, dir):
- dir = self.make_absolute_dir(dir, 'local')
- if self.local_dir == dir:
- return
- else:
- if not os.path.exists(dir):
- raise NotFoundError('Folder not found')
- if not os.access(dir, os.X_OK):
- raise PermissionError('Permission denied')
- encoding = 'utf-8'
- if os.name == 'nt':
- encoding = locale.getpreferredencoding(do_setlocale=True)
- self.proc.write('lcd "%s"' % dir, encoding)
- result = self.proc.read(encoding=encoding)
- if re.search('(no such file or directory|cannot find the (file|path) specified\\.)', result, re.I) is not None:
- raise NotFoundError('Folder not found')
- self.local_dir = dir
- return
- @keepaliveize
- def get(self, remote_files, path_map, quiet=False, **kwargs):
- if not isinstance(remote_files, list):
- remote_files = [
- remote_files]
- error = False
- single_file = len(remote_files) == 1
- list_cache = {}
- for remote_file in remote_files:
- file = remote_to_local(remote_file, path_map, self.remote_encoding)
- try:
- dir_error, cont = self.handle_get_dirs(file, remote_file, single_file)
- error = error or dir_error
- if cont:
- continue
- except OSError as e:
- if not quiet:
- self.printer.write('\nDownloading "%s" to "%s" ... failure (%s)' % (remote_file, file, str(e)))
- raise e
- if not quiet:
- progress = ProgressThread(self.printer, '\nDownloading "%s" to "%s"' % (remote_file, file))
- try:
- basename = os.path.basename(file)
- if os.name == 'nt':
- local_encoding = locale.getpreferredencoding(do_setlocale=True)
- remote_basename = str_cls(basename.encode(self.remote_encoding), local_encoding)
- self.proc.write('get "%s" "%s"' % (remote_basename, basename), local_encoding)
- result = self.proc.read(encoding=local_encoding)
- else:
- self.proc.write('get "%s"' % basename, self.remote_encoding)
- result = self.proc.read(encoding=self.remote_encoding)
- if re.search('permission denied', result, re.I) is not None:
- raise PermissionError('Permission denied')
- if re.search('(no such file or directory|cannot find the file specified)', result, re.I) is not None:
- raise NotFoundError('File not found')
- if self.preserve_modification_times:
- if self.dir not in list_cache:
- success, result = self.list(self.dir, path_map, quiet=True, skip_symlinks=False)
- if success:
- list_cache[self.dir] = dict(result)
- else:
- list_cache[self.dir] = None
- if isinstance(list_cache[self.dir], dict):
- atime = list_cache[self.dir][basename]
- mtime = atime
- os.utime(file, (atime, mtime))
- if not quiet:
- progress.stop('success')
- progress.join()
- except (PermissionError, NotFoundError) as e:
- if not quiet:
- progress.stop('failure (%s)' % str(e))
- progress.join()
- if single_file and not quiet:
- self.printer.error(e)
- error = True
- continue
- except OSError as e:
- if not quiet:
- progress.stop('failure (%s)' % str(e))
- progress.join()
- raise e
- except UnicodeDecodeError:
- if not quiet:
- progress.stop('failure (Encoding error)')
- progress.join()
- raise
- result = None
- if error and not quiet and not single_file:
- string = 'One or more errors occured while downloading files'
- result = string
- self.printer.write('\n' + string)
- self.printer.error(string)
- return [
- not error, result]
- def ls(self, path_map, include_self=True, config=None, skip_symlinks=True):
- offset = self.determine_time_offset(path_map, config)
- self.proc.write('ls' if os.name == 'nt' else 'ls -la')
- result = self.proc.read(encoding=self.remote_encoding)
- if re.search('permission denied', result, re.I) is not None:
- raise PermissionError('Permission denied')
- if re.search('no such file or directory|cannot find the file specified', result, re.I) is not None:
- raise NotFoundError('File not found')
- entries = result.split('\n')[1:]
- files = self.parse_ls(entries, offset, include_self, skip_symlinks)
- if include_self:
- found_cur_dir = False
- if files:
- for file_ in files:
- if file_[0] == '.':
- found_cur_dir = True
- break
- if not found_cur_dir:
- timestamp = int(time.time())
- files.insert(0, ['.', timestamp])
- files = sorted(files, key=lambda ar: ar[0].lower())
- return files
- def check_symlink(self, basename):
- if os.name == 'nt':
- command = 'ls %s' % basename
- else:
- command = 'ls -la %s' % basename
- self.proc.write(command, self.remote_encoding)
- result = self.proc.read(encoding=self.remote_encoding)
- if re.search('permission denied', result, re.I) is not None:
- return canonicalize(basename, 'remote')
- elif os.name == 'nt':
- if re.search('no such file', result, re.I) is not None:
- return basename
- return canonicalize(basename, 'remote')
- lines = result.split('\n')[1:]
- if len(lines) == 1:
- return basename
- else:
- return canonicalize(basename, 'remote')
- @keepaliveize
- def mkdir(self, dir, chmod_dirs=None, **kwargs):
- try:
- self.cd(dir)
- self.printer.write('\nFolder "%s" already exists' % dir, key='sftp_mkdir', finish=True)
- if chmod_dirs:
- try:
- self.chmod(dir, chmod_dirs, quiet=True)
- except PermissionError:
- self.printer.write('\nChmoding folder "%s" to "%s" ... failure (Permission denied)' % (dir, chmod_dirs), key='sftp_mkdir', finish=True)
- return
- return
- except NotFoundError:
- pass
- if dir[0] == '/':
- parent_dir = dirname(dir)
- try:
- self.cd(parent_dir)
- except NotFoundError:
- self.mkdir(parent_dir, chmod_dirs)
- self.cd(parent_dir)
- dir = canonicalize(dir, 'remote')
- progress = ProgressThread(self.printer, '\nCreating folder "%s"' % dir)
- try:
- self.proc.write('mkdir "%s"' % dir, self.remote_encoding)
- result = self.proc.read(encoding=self.remote_encoding)
- except UnicodeDecodeError:
- progress.stop('failure (Encoding error)')
- progress.join()
- raise
- if re.search('permission denied', result, re.I) is not None:
- progress.stop('failure (Permission denied)')
- progress.join()
- raise PermissionError('Permission denied')
- try:
- chmod_error = False
- if chmod_dirs:
- self.chmod(dir, chmod_dirs, quiet=True)
- except PermissionError:
- chmod_error = True
- progress.stop('success')
- progress.join()
- if chmod_error:
- self.printer.write('\nChmoding folder "%s" to "%s" ... failure (Permission denied)' % (dir, chmod_dirs), key='sftp_mkdir', finish=True)
- return
- @keepaliveize
- def mv(self, names, quiet=False, **kwargs):
- old_filename = os.path.basename(names[0].rstrip('\\/'))
- new_filename = os.path.basename(names[1].rstrip('\\/'))
- dir = dirname(names[0])
- dir = canonicalize(dir, 'remote')
- try:
- self.cd(dir)
- except NotFoundError:
- if not quiet:
- self.printer.write('\nChanging to folder "%s" ... failure (Folder not found)' % dir)
- self.printer.error('Folder not found')
- return [False, 'Folder not found']
- if not quiet:
- progress = ProgressThread(self.printer, '\nRenaming "%s" to "%s"' % (names[0], names[1]))
- try:
- self.proc.write('rename "%s" "%s"' % (old_filename, new_filename), self.remote_encoding)
- result = self.proc.read(encoding=self.remote_encoding)
- except UnicodeDecodeError:
- if not quiet:
- progress.stop('failure (Encoding error)')
- progress.join()
- raise
- if re.search('permission denied', result, re.I) is not None:
- if not quiet:
- progress.stop('failure (Permission denied)')
- progress.join()
- self.printer.error('Permission denied')
- return [False, 'Permission denied']
- elif re.search('(no such file or directory|cannot find the file specified)', result, re.I) is not None:
- message = '%s not found' % path_type(names[0], True)
- if not quiet:
- progress.stop('failure (%s)' % message)
- progress.join()
- self.printer.error(message)
- return [False, message]
- elif re.search('failure', result, re.I) is not None:
- message = '%s already exists' % new_filename
- if not quiet:
- progress.stop('failure (%s)' % message)
- progress.join()
- self.printer.error(message)
- return [False, message]
- else:
- if not quiet:
- progress.stop('success')
- progress.join()
- return [
- True, None]
- @keepaliveize
- def put(self, files, path_map, chmod_files=None, chmod_dirs=None, quiet=False, **kwargs):
- if not isinstance(files, list):
- files = [
- files]
- error = False
- single_file = len(files) == 1
- for file in files:
- remote_file = local_to_remote(file, path_map, self.remote_encoding)
- try:
- dir_error, cont = self.handle_put_dirs(file, remote_file, chmod_dirs, single_file)
- error = error or dir_error
- if cont:
- continue
- except OSError as e:
- if not quiet:
- self.printer.write('\nUploading "%s" to "%s" ... failure (%s)' % (file, remote_file, str(e)))
- raise e
- if not quiet:
- progress = ProgressThread(self.printer, '\nUploading "%s" to "%s"' % (file, remote_file))
- try:
- basename = os.path.basename(file)
- if os.name == 'nt':
- local_encoding = locale.getpreferredencoding(do_setlocale=True)
- remote_basename = str_cls(basename.encode(self.remote_encoding), local_encoding)
- command = 'put -- "%s" "%s"'
- if self.preserve_modification_times is True:
- command = 'put -P -- "%s" "%s"'
- self.proc.write(command % (basename, remote_basename), local_encoding)
- result = self.proc.read(encoding=local_encoding)
- else:
- command = 'put -- "%s"'
- if self.preserve_modification_times is True:
- command = 'put -P -- "%s"'
- self.proc.write(command % basename, self.remote_encoding)
- result = self.proc.read(encoding=self.remote_encoding)
- if re.search("Couldn't fsetstat", result, re.I) is not None:
- raise PermissionError('Server does not support preserving modification times, please set preserve_modification_times to false or "download_only" in sftp-config.json')
- if re.search('permission denied', result, re.I) is not None:
- raise PermissionError('Permission denied')
- try:
- chmod_error = False
- if chmod_files:
- self.chmod(remote_file, chmod_files, quiet=True)
- except PermissionError:
- chmod_error = True
- if not quiet:
- progress.stop('success')
- progress.join()
- if chmod_error:
- self.printer.write('\nChmoding folder "%s" to "%s" ... failure (Permission denied)' % (remote_file, chmod_files), key='sftp_put_chmod', finish=True)
- except PermissionError as e:
- if not quiet:
- progress.stop('failure (%s)' % str(e))
- progress.join()
- if single_file and not quiet:
- self.printer.error(e)
- error = True
- continue
- except OSError as e:
- if not quiet:
- progress.stop('failure (%s)' % str(e))
- progress.join()
- raise e
- except UnicodeDecodeError:
- if not quiet:
- progress.stop('failure (Encoding error)')
- progress.join()
- raise
- result = None
- if error and not quiet and not single_file:
- string = 'One or more errors occured while uploading files'
- result = string
- self.printer.write('\n' + string)
- self.printer.error(string)
- return [
- not error, result]
- def pwd(self):
- if self.dir is None:
- self.proc.write('pwd')
- result = self.proc.read(encoding=self.remote_encoding)
- self.dir = re.sub('^[^/]+', '', result.split('\n')[-1])
- self.dir = canonicalize(self.dir, 'remote')
- return self.dir
- def lpwd(self):
- if self.local_dir is None:
- self.proc.write('lpwd')
- encoding = 'utf-8'
- if os.name == 'nt':
- encoding = locale.getpreferredencoding(do_setlocale=True)
- result = self.proc.read(encoding=encoding)
- if os.name == 'nt':
- self.local_dir = re.sub('^Current local directory is ', '', result.split('\n')[-1])
- else:
- if re.search("Couldn't get local cwd", result, re.I) is not None:
- self.proc.write('lcd "%s"' % os.path.expanduser('~'), encoding)
- result = self.proc.read(encoding=encoding)
- self.proc.write('lpwd')
- result = self.proc.read(encoding=encoding)
- self.local_dir = re.sub('^[^/]+', '', result.split('\n')[-1])
- self.local_dir = canonicalize(self.local_dir, 'local')
- return self.local_dir
- @keepaliveize
- def rm(self, remote_files, path_map, quiet=False, **kwargs):
- if not isinstance(remote_files, list):
- remote_files = [
- remote_files]
- error = False
- single_file = len(remote_files) == 1
- for remote_file in remote_files:
- file = remote_to_local(remote_file, path_map, self.remote_encoding)
- try:
- dir_error, cont = self.handle_rm_dirs(file, remote_file, single_file)
- error = error or dir_error
- if cont:
- continue
- except OSError as e:
- if not quiet:
- self.printer.write('\nDeleting "%s" ... failure (%s)' % (remote_file, str(e)))
- raise e
- if not quiet:
- progress = ProgressThread(self.printer, '\nDeleting "%s"' % remote_file)
- try:
- if is_dir(remote_file):
- command = 'rmdir'
- rm_path = os.path.basename(remote_file.rstrip('/\\'))
- else:
- command = 'rm'
- rm_path = os.path.basename(remote_file)
- self.proc.write('%s "%s"' % (command, rm_path), self.remote_encoding)
- result = self.proc.read(encoding=self.remote_encoding)
- if re.search('permission denied', result, re.I) is not None:
- raise PermissionError('Permission denied')
- if re.search('(no such file or directory|cannot find the file specified)', result, re.I) is not None:
- raise NotFoundError('File not found')
- if not quiet:
- progress.stop('success')
- progress.join()
- except (PermissionError, NotFoundError) as e:
- if not quiet:
- progress.stop('failure (%s)' % str(e))
- progress.join()
- if single_file and not quiet:
- self.printer.error(e)
- error = True
- continue
- except OSError as e:
- if not quiet:
- progress.stop('failure (%s)' % str(e))
- progress.join()
- raise e
- except UnicodeDecodeError:
- if not quiet:
- progress.stop('failure (Encoding error)')
- progress.join()
- raise
- result = None
- if error and not quiet and not single_file:
- string = 'One or more errors occured while removing files'
- result = string
- self.printer.write('\n' + string)
- self.printer.error(string)
- return [
- not error, result]
- # okay decompiling /home/manager/.config/sublime-text-3/Packages/SFTP/sftp/sftp_transport.pyc
- # uncompyle6 version 3.2.3
- # Python bytecode 3.3 (3230)
- # Decompiled from: Python 2.7.12 (default, Dec 4 2017, 14:50:18)
- # [GCC 5.4.0 20160609]
- # Embedded file name: /Users/wbond/Library/Application Support/Sublime Text 3/Packages/SFTP/sftp/threads.py
- # Compiled at: 2015-11-05 00:19:26
- # Size of source mod 2**32: 5597 bytes
- import sublime, threading, sys
- from .errors import encoding_error
- from .times import timestamp_to_string
- from .views import get_all_views
- class HookedThread(threading.Thread):
- def __init__(self):
- run_old = self.run
- def run_with_except_hook(*args, **kw):
- try:
- run_old(*args, **kw)
- except (KeyboardInterrupt, SystemExit):
- raise
- except UnicodeDecodeError as e:
- encoding_error(e)
- except LookupError as e:
- if e.message == 'unknown encoding: idna':
- def show_2pdf_error():
- sublime.error_message('Sublime SFTP\n\nIt appears you have the 2pdf package installed. Unfortunately that package messes with the Python encodings and breaks the SFTP package.\n\nPlease use the command palette to run "Remove Package" and select "2pdf". Once removed, the SFTP package should work again, properly.')
- sublime.set_timeout(show_2pdf_error, 10)
- else:
- encoding_error(e)
- except:
- sys.excepthook(*sys.exc_info())
- self.run = run_with_except_hook
- threading.Thread.__init__(self)
- class SyncThread(object):
- def confirm(self, operations, on_confirm, on_reject=None, should_join=False):
- prompt = '\n\n Do you wish to perform the following operations?'
- for op in operations:
- prompt += '\n ' + op
- prompt += '\n (Select via quick panel) '
- self.printer.write(prompt, key='sync_confirm')
- options = [['Yes', 'Perform the listed operations (see SFTP panel)'], ['No', 'Do nothing']]
- def handler(index):
- if index == -1 or index == 1:
- self.printer.write('No\n', key='sync_confirm', finish=True)
- if on_reject:
- threading.Thread(target=on_reject).start()
- return
- self.printer.write('Yes\n', key='sync_confirm', finish=True)
- SyncThreadConfirm(on_confirm, should_join, self.window_id).start()
- def show():
- self.printer.show(force=True)
- self.window.show_quick_panel(options, handler)
- sublime.set_timeout(show, 10)
- def make_time(self, time):
- if time:
- return timestamp_to_string(time, '%-I:%M%p %-m/%-d/%y').lower()
- else:
- return 'None'
- def strip(self, path, dir, type):
- new_path = path[len(dir):]
- if new_path == '':
- new_path = path
- return new_path
- class SyncThreadConfirm(HookedThread):
- def __init__(self, callback, should_join, window_id):
- self.callback = callback
- self.should_join = should_join
- self.window_id = window_id
- super(SyncThreadConfirm, self).__init__()
- def run(self):
- if self.should_join:
- last_thread = ThreadTracker.get_last_added(self.window_id)
- ThreadTracker.add(self)
- if last_thread is not None:
- last_thread.join()
- self.callback()
- return
- class ThreadTracker(object):
- pending_threads = {}
- current_thread = {}
- @classmethod
- def add(cls, thread):
- cls.pending_threads[thread.window_id] = thread
- @classmethod
- def get_last_added(cls, window_id):
- return cls.pending_threads.get(window_id)
- @classmethod
- def set_current(cls, window_id, thread):
- cls.current_thread[window_id] = thread
- @classmethod
- def get_current(cls, window_id):
- return cls.current_thread.get(window_id)
- def unset_current_thread(fn):
- def handler(self, *args, **kwargs):
- result = fn(self, *args, **kwargs)
- ThreadTracker.set_current(self.window_id, None)
- return result
- return handler
- class ThreadActivity(object):
- def __init__(self, thread, printer, message):
- self.thread = thread
- self.printer = printer
- self.message = message
- self.addend = 1
- self.size = 8
- self.last_wrote = None
- sublime.set_timeout(self.set_window, 1)
- return
- def set_window(self):
- for window in sublime.windows():
- if window.id() == self.thread.window_id:
- self.window = window
- continue
- sublime.set_timeout(lambda : self.run(0), 100)
- def run(self, i):
- if not self.thread.is_alive():
- self.write('')
- return
- if self.printer.visible:
- if self.last_wrote != '':
- self.write('')
- sublime.set_timeout(lambda : self.run(i), 100)
- return
- before = i % self.size
- after = self.size - 1 - before
- if not self.write('%s [%s=%s]' % (self.message, ' ' * before, ' ' * after)):
- return
- if not after:
- self.addend = -1
- if not before:
- self.addend = 1
- i += self.addend
- sublime.set_timeout(lambda : self.run(i), 100)
- def write(self, value):
- if not hasattr(self, 'window'):
- return False
- self.last_wrote = value
- if value == '':
- [view.erase_status('sftp') for view in get_all_views(self.window)]
- else:
- view = self.window.active_view()
- if view:
- view.set_status('sftp', value)
- return True
- # okay decompiling /home/manager/.config/sublime-text-3/Packages/SFTP/sftp/threads.pyc
- # uncompyle6 version 3.2.3
- # Python bytecode 3.3 (3230)
- # Decompiled from: Python 2.7.12 (default, Dec 4 2017, 14:50:18)
- # [GCC 5.4.0 20160609]
- # Embedded file name: /Users/wbond/Library/Application Support/Sublime Text 3/Packages/SFTP/sftp/times.py
- # Compiled at: 2015-11-05 00:19:28
- # Size of source mod 2**32: 1330 bytes
- import datetime, re, math
- def timestamp_to_string(timestamp, format):
- date = datetime.datetime.fromtimestamp(timestamp)
- return good_strftime(date, format)
- def good_strftime(timestamp, format):
- output = ''
- for part in filter(len, re.split('(%-\\w)', format)):
- if part[0:2] == '%-':
- output += timestamp.strftime('%' + part[2:]).lstrip('0')
- else:
- output += timestamp.strftime(part)
- return output
- def time_diff(first_timestamp, second_timestamp):
- diff = first_timestamp - second_timestamp
- if math.floor(first_timestamp / 60) == math.floor(second_timestamp / 60):
- return 'same age'
- break_points = [
- [
- 3600, 60, 'minute', 'minutes'],
- [
- 86400, 3600, 'hour', 'hours'],
- [
- 604800, 86400, 'day', 'days'],
- [
- 2592000, 604800, 'week', 'weeks'],
- [
- 31536000, 2592000, 'month', 'months'],
- [
- 2147483647, 31536000, 'year', 'years']]
- for unit_info in break_points:
- if abs(diff) >= unit_info[0]:
- continue
- unit_diff = round(float(abs(diff)) / float(unit_info[1]))
- units = unit_info[2] if unit_diff == 1 else unit_info[3]
- break
- suffix = ' newer' if first_timestamp > second_timestamp else ' older'
- return str(int(unit_diff)) + ' ' + units + suffix
- # okay decompiling /home/manager/.config/sublime-text-3/Packages/SFTP/sftp/times.pyc
- # uncompyle6 version 3.2.3
- # Python bytecode 3.3 (3230)
- # Decompiled from: Python 2.7.12 (default, Dec 4 2017, 14:50:18)
- # [GCC 5.4.0 20160609]
- # Embedded file name: /Users/wbond/Library/Application Support/Sublime Text 3/Packages/SFTP/sftp/vcs.py
- # Compiled at: 2015-11-05 00:19:30
- # Size of source mod 2**32: 4628 bytes
- import sublime, os, re
- from .proc import find_binary, NonInteractiveProcess
- from .errors import NotFoundError
- from .paths import dirname
- class VCS(object):
- def add_file(self, files, line):
- line = os.path.join(self.root_dir, line)
- if os.path.isdir(line):
- for dir, subdirs, subfiles in os.walk(line):
- files.append(dir)
- for subfile in subfiles:
- files.append(os.path.join(self.root_dir, dir, subfile))
- else:
- files.append(line)
- return files
- def exec_status(self, binary, type, extra_arg=''):
- if not self.binary_path:
- self.binary_path = find_binary(binary)
- if not self.binary_path:
- raise NotFoundError('The %s binary could not be found on your system. Please edit "%s" and set "%s_binary_path" or install %s.' % (
- binary,
- os.path.join(sublime.packages_path(), 'User', 'SFTP.sublime-settings'),
- type,
- type))
- try:
- args = [
- self.binary_path, 'status']
- if extra_arg:
- args.append(extra_arg)
- args.append(self.root_dir)
- proc = NonInteractiveProcess(self.root_dir, *args)
- result = proc.run().split('\n')
- except OSError:
- raise NotFoundError('The binary "%s" does not exist or is not executable. Please check your settings in "%s".' % (
- self.binary_path,
- os.path.join(sublime.packages_path(), 'User', 'SFTP.sublime-settings')))
- return result
- def init(self, path, binary_path, type):
- root_dir = None
- last_dir = None
- cur_dir = dirname(path)
- while cur_dir != last_dir:
- if os.path.exists(os.path.join(cur_dir, '.' + type)):
- root_dir = cur_dir
- break
- last_dir = cur_dir
- cur_dir = dirname(cur_dir)
- if root_dir is None:
- raise NotFoundError('Unable to find .' + type + ' folder')
- self.root_dir = root_dir
- self.binary_path = binary_path
- return
- class SVN(VCS):
- def __init__(self, path, binary_path):
- root_dir = None
- last_dir = None
- orig_dir = dirname(path)
- cur_dir = orig_dir
- while cur_dir != last_dir:
- if root_dir is not None and not os.path.exists(os.path.join(cur_dir, '.svn')):
- break
- if os.path.exists(os.path.join(cur_dir, '.svn')):
- root_dir = cur_dir
- last_dir = cur_dir
- cur_dir = dirname(cur_dir)
- if root_dir is None:
- raise NotFoundError('Unable to find .svn folder')
- self.root_dir = root_dir
- self.binary_path = binary_path
- return
- def list_changed_files(self):
- svn_binary = 'svn.exe' if os.name == 'nt' else 'svn'
- result = self.exec_status(svn_binary, 'svn')
- files = []
- for line in result:
- if not not len(line):
- if line[0] not in (u'A', u'M', u'R', u'C', u'?'):
- continue
- line = re.sub('^[^ ]\\s+(\\+\\s+)?', '', line)
- files = self.add_file(files, line)
- return files
- class Git(VCS):
- def __init__(self, path, binary_path):
- self.init(path, binary_path, 'git')
- def list_changed_files(self):
- git_binary = 'git.exe' if os.name == 'nt' else 'git'
- result = self.exec_status(git_binary, 'git', '--porcelain')
- files = []
- for line in result:
- if re.search('^([AMRC][ M]|\\?| M|UA|UU)', line) is None:
- continue
- if line[0] == 'R':
- line = re.sub('^.*? -> ', '', line[3:])
- else:
- line = line[3:]
- files = self.add_file(files, line)
- return files
- class Hg(VCS):
- def __init__(self, path, binary_path):
- self.init(path, binary_path, 'hg')
- def list_changed_files(self):
- hg_binary = 'hg.exe' if os.name == 'nt' else 'hg'
- result = self.exec_status(hg_binary, 'hg')
- files = []
- for line in result:
- if not not len(line):
- if line[0] not in (u'A', u'M', u'R', u'?'):
- continue
- line = line[2:]
- files = self.add_file(files, line)
- return files
- # okay decompiling /home/manager/.config/sublime-text-3/Packages/SFTP/sftp/vcs.pyc
- # uncompyle6 version 3.2.3
- # Python bytecode 3.3 (3230)
- # Decompiled from: Python 2.7.12 (default, Dec 4 2017, 14:50:18)
- # [GCC 5.4.0 20160609]
- # Embedded file name: /Users/wbond/Library/Application Support/Sublime Text 3/Packages/SFTP/sftp/views.py
- # Compiled at: 2015-11-05 00:19:32
- # Size of source mod 2**32: 322 bytes
- def get_view_by_group_index(window, group, index):
- return window.views_in_group(group)[index]
- def get_all_views(window):
- views = window.views()
- active_view = window.active_view()
- if active_view and active_view.id() not in [view.id() for view in views]:
- views.append(active_view)
- return views
- # okay decompiling /home/manager/.config/sublime-text-3/Packages/SFTP/sftp/views.pyc
- # decompiled 19 files: 19 okay, 0 failed
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement