Advertisement
Gistrec

SFTP Sublime decompile

Aug 13th, 2018
1,633
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 341.33 KB | None | 0 0
  1. # uncompyle6 version 3.2.3
  2. # Python bytecode 3.3 (3230)
  3. # Decompiled from: Python 2.7.12 (default, Dec  4 2017, 14:50:18)
  4. # [GCC 5.4.0 20160609]
  5. # Embedded file name: /Users/wbond/Library/Application Support/Sublime Text 3/Packages/SFTP/sftp/commands.py
  6. # Compiled at: 2015-11-05 01:05:02
  7. # Size of source mod 2**32: 126303 bytes
  8. import sublime, sublime_plugin, os, subprocess, re, sys, hmac, binascii, time, traceback, tempfile, shutil, difflib, codecs
  9. from itertools import cycle
  10. try:
  11.     from itertools import izip
  12.     str_cls = unicode
  13.     str_clses = ['unicode', 'str']
  14.     st_version = 2
  15. except ImportError:
  16.     izip = zip
  17.     xrange = range
  18.     str_cls = str
  19.     str_clses = ['str', 'bytes']
  20.     st_version = 3
  21.  
  22. if os.name != 'nt':
  23.     import unicodedata
  24. from .errors import AuthenticationError, BinaryMissingError, ConnectionError, DisconnectionError, encoding_error, handle_exception, NotFoundError, PermissionError
  25. from .panel_printer import PanelPrinter, ProgressThread
  26. from .threads import HookedThread, SyncThread, ThreadActivity, ThreadTracker, unset_current_thread
  27. from .debug import debug_print, get_debug
  28. from .times import time_diff
  29. from .views import get_view_by_group_index
  30. 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
  31. from .config import build_config, find_config, get_default_config, get_server_config_folder, load_config, parse_config, prepare_server_config, setup_tmp_dir
  32. from .sftp_transport import SFTP
  33. from .ftp_transport import FTP
  34. from .vcs import Hg, Git, SVN
  35. transports = {'SFTP': SFTP,
  36.  'FTP': FTP}
  37. if 'ssl' in sys.modules:
  38.     from .ftps_transport import FTPS
  39.     transports['FTPS'] = FTPS
  40.  
  41. def show_qp(window, choices, on_done):
  42.  
  43.     def show_timeout():
  44.         window.show_quick_panel(choices, on_done)
  45.  
  46.     sublime.set_timeout(show_timeout, 10)
  47.  
  48.  
  49. class SftpCommand(object):
  50.     connections = {}
  51.     identifiers = {}
  52.     usage = {}
  53.     remote_roots = {}
  54.     remote_time_offsets = {}
  55.  
  56.     @classmethod
  57.     def setup_elements(cls, config):
  58.         if not hasattr(SftpCommand, 'elements'):
  59.             SftpCommand.elements = [0,
  60.              config.get('email'),
  61.              'sftp_flags',
  62.              'ssh_key_file',
  63.              'psftp']
  64.             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.')
  65.             SftpCommand.elements.append(config.get('product_key'))
  66.             key = SftpCommand.elements[1]
  67.             if isinstance(key, str_cls):
  68.                 key = key.encode('utf-8')
  69.             for element in SftpCommand.elements[2:-2]:
  70.                 key = hmac.new(element.encode('utf-8'), key).digest()
  71.  
  72.             SftpCommand.elements[1] = key
  73.  
  74.     def create_default_config(self, file):
  75.         handle = open(file, 'w')
  76.         handle.write(get_default_config(True))
  77.         handle.close()
  78.  
  79.     def first_path(self, paths):
  80.         if paths is None or paths == []:
  81.             return
  82.         else:
  83.             return paths[0]
  84.  
  85.     def get_path(self, paths=None, allow_multiple=False, view=None):
  86.         type_ = type(paths).__name__
  87.         if (paths is None or paths == []) and type_ not in str_clses:
  88.             if view is None:
  89.                 if not hasattr(self, 'window'):
  90.                     return
  91.                 view = self.window.active_view()
  92.                 if view is None:
  93.                     if st_version == 3:
  94.                         return self.window.extract_variables().get('file')
  95.                     return
  96.             return view.file_name()
  97.         elif allow_multiple or type_ in str_clses:
  98.             return paths
  99.         else:
  100.             return paths[0]
  101.  
  102.     def has_config(self, path):
  103.         return bool(find_config(path, True))
  104.  
  105.     def get_config(self, paths=None, quiet=False):
  106.         path = self.get_path(paths)
  107.         config, config_file = load_config(path)
  108.         if config is None:
  109.             return
  110.         else:
  111.             config_dir = dirname(config_file)
  112.             return build_config(config, config_dir, config_file, quiet)
  113.  
  114.     def save_files(self, files=[]):
  115.         if not isinstance(files, list):
  116.             files = [
  117.              files]
  118.         window = self.window if hasattr(self, 'window') else self.view.window()
  119.         original_view = window.active_view()
  120.         for view in window.views():
  121.             file = view.file_name()
  122.             if not (files and file not in files):
  123.                 if not file:
  124.                     continue
  125.                 if find_config(file, True) and view.is_dirty():
  126.                     window.focus_view(view)
  127.                     view_settings = view.settings()
  128.                     view_settings.set('sftp_auto_save', True)
  129.                     view.run_command('save')
  130.                 continue
  131.  
  132.         if original_view:
  133.             window.focus_view(original_view)
  134.  
  135.  
  136. class SftpThread(HookedThread):
  137.  
  138.     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):
  139.         self.window_id = window_id
  140.         self.printer = PanelPrinter.get(window_id)
  141.         self.config = config
  142.         self.file = file
  143.         self.action = action
  144.         self.quiet = quiet
  145.         self.result = None
  146.         self.connection = None
  147.         self.increment = increment
  148.         self.should_join = should_join
  149.         self.reset_lcd = reset_lcd
  150.         self.on_connect = on_connect
  151.         self.on_fail = on_fail
  152.         self.on_complete = on_complete
  153.         self.connection_id = str(window_id) + '-' + self.config['user'] + '@' + self.config['host'] + ':' + self.config['port']
  154.         self.hide = hide
  155.         self.failed = False
  156.         self.skip_ignore = skip_ignore
  157.         self.skip_symlinks = skip_symlinks
  158.         self.mode = mode
  159.         self.config_file_cancel = False
  160.         if self.action == 'put' and not self.config['allow_config_upload']:
  161.             files = isinstance(file, list)[file]file
  162.             regexp = re.compile('(^|/|\\\\)(sftp-config(-alt\\d?)?\\.json|sftp-settings\\.json)$')
  163.             for f in files:
  164.                 if re.search(regexp, f) is not None:
  165.                     self.config_file_cancel = True
  166.                     break
  167.  
  168.         SftpCommand.setup_elements(self.config)
  169.         super(SftpThread, self).__init__()
  170.         return
  171.  
  172.     @classmethod
  173.     def cleanup(cls):
  174.         for connection_id in SftpCommand.connections:
  175.             connection = SftpCommand.connections[connection_id]
  176.             connection.close()
  177.  
  178.     def close_connection(self, identifier, dir, window_id, disconnected=False):
  179.         if not SftpCommand.usage.get(identifier):
  180.             return
  181.         SftpCommand.usage[identifier] -= 1
  182.         if SftpCommand.usage[identifier] and not disconnected:
  183.             return
  184.         debug_print('SFTP: Closing unused connection ' + identifier, 2)
  185.         if SftpCommand.connections.get(identifier):
  186.             SftpCommand.connections[identifier].close(disconnected)
  187.             debug_print('SFTP: Closed unused connection ' + identifier, 2)
  188.             del SftpCommand.connections[identifier]
  189.         if SftpCommand.identifiers.get(str(window_id) + '-' + dir):
  190.             del SftpCommand.identifiers[str(window_id) + '-' + dir]
  191.  
  192.     def kill(self):
  193.         debug_print('SFTP: Killing connection ' + self.connection_id, 2)
  194.         self.connection.close()
  195.         dir = self.config['local_dir']
  196.         dir = str(self.window_id) + '-' + dir
  197.         identifier = SftpCommand.identifiers.get(dir, None)
  198.         if SftpCommand.identifiers.get(dir):
  199.             del SftpCommand.identifiers[dir]
  200.         if identifier:
  201.             del SftpCommand.connections[identifier]
  202.             del SftpCommand.usage[identifier]
  203.         ThreadTracker.set_current(self.window_id, None)
  204.         return
  205.  
  206.     @unset_current_thread
  207.     def run(self):
  208.         self.start = time.time()
  209.         if self.should_join:
  210.             last_thread = ThreadTracker.get_last_added(self.window_id)
  211.             ThreadTracker.add(self)
  212.             if last_thread is not None:
  213.                 debug_print('SFTP: Waiting for previous thread', 2)
  214.                 last_thread.join()
  215.         if self.config_file_cancel:
  216.             self.printer.write('\nCanceled since payload included one or more Sublime SFTP configuration files and the "allow_config_upload" is not set to true')
  217.  
  218.             def do_config_file_cancel():
  219.                 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.'
  220.                 sublime.error_message(message)
  221.  
  222.             sublime.set_timeout(do_config_file_cancel, 10)
  223.             return
  224.         else:
  225.             debug_print('SFTP: Beginning file transfer thread', 2)
  226.             ThreadActivity(self, self.printer, 'SFTP Working')
  227.             self.printer.reset_hide()
  228.             ThreadTracker.set_current(self.window_id, self)
  229.             try:
  230.                 try:
  231.                     if not self.make_connection():
  232.                         return
  233.                     debug_print('SFTP: Checking license key', 2)
  234.                     SftpCommand.setup_elements(self.config)
  235.                     do_show = False
  236.                     if self.action not in (u'list', u'listr', u'llist', u'llistr',
  237.                                            u'cwd'):
  238.                         SftpCommand.elements[0] += 1
  239.  
  240.                         def zip_to_i(element):
  241.                             if isinstance(element, int):
  242.                                 return element
  243.                             return ord(element)
  244.  
  245.                         if sys.version_info >= (3, ):
  246.                             key_prefix = bytes([zip_to_i(x) ^ zip_to_i(y) for x, y in izip(SftpCommand.elements[1], cycle('22'))])
  247.                         else:
  248.                             key_prefix = ('').join([chr(zip_to_i(x) ^ zip_to_i(y)) for x, y in izip(SftpCommand.elements[1], cycle('22'))])
  249.                         key_prefix = binascii.hexlify(key_prefix)[:30]
  250.                         clen = 6
  251.                         chunks = [key_prefix[(i - 1) * clen:i * clen] for i in xrange(1, int(len(key_prefix) / clen + 1))]
  252.                         key_prefix = ('-').join(chunks).decode('utf-8')
  253.                         if SftpCommand.elements[0] > 0 and SftpCommand.elements[0] % 10 == 0 and key_prefix != SftpCommand.elements[-1]:
  254.  
  255.                             def reg():
  256.                                 if int(sublime.version()) >= 2190:
  257.                                     if sublime.ok_cancel_dialog(SftpCommand.elements[-2], 'Buy Now'):
  258.                                         sublime.active_window().run_command('open_url', {'url': 'http://wbond.net/sublime_packages/sftp/buy'})
  259.                                 else:
  260.                                     sublime.error_message(SftpCommand.elements[-2])
  261.  
  262.                             sublime.set_timeout(reg, 1)
  263.                             do_show = True
  264.                     self.do_operation(do_show)
  265.                 except (OSError, BinaryMissingError) as e:
  266.                     if self.on_fail:
  267.                         self.on_fail(e)
  268.                     self.close_connection(self.connection_id, self.config['local_dir'], self.window_id, isinstance(e, DisconnectionError))
  269.                     if isinstance(e, DisconnectionError):
  270.                         try:
  271.                             debug_print('SFTP: Reconnecting after disconnection', 2)
  272.                             if not self.make_connection():
  273.                                 return
  274.                             self.do_operation()
  275.                         except OSError:
  276.                             self.printer.write('\nMultiple disconnection errors, giving up')
  277.  
  278.             except UnicodeDecodeError as e:
  279.                 encoding_error(e)
  280.             except LookupError as e:
  281.                 encoding_error(e)
  282.  
  283.             return
  284.  
  285.     def make_connection(self):
  286.         self.failed = True
  287.         old_identifier = SftpCommand.identifiers.get(str(self.window_id) + '-' + self.config['local_dir'], None)
  288.         if old_identifier is not None and self.connection_id != old_identifier:
  289.             self.close_connection(old_identifier, self.config['local_dir'], self.window_id)
  290.         self.connection = SftpCommand.connections.get(self.connection_id, None)
  291.         if self.connection and self.connection.closed:
  292.             self.close_connection(self.connection_id, self.config['local_dir'], self.window_id)
  293.             self.connection = None
  294.         if self.connection is None:
  295.             class_name = self.config['type'].upper()
  296.             class_object = transports[class_name]
  297.             debug_print('SFTP: Creating file transfer object', 2)
  298.             merged_config = self.config.copy()
  299.             merged_config['remote_time_offset'] = SftpCommand.remote_time_offsets.get(self.connection_id)
  300.             debug_print('SFTP: Configuration\n    %s' % repr(merged_config), 2)
  301.             self.connection = class_object(self.printer, **merged_config)
  302.             try:
  303.                 debug_print('SFTP: Starting connection', 2)
  304.                 self.connection.connect()
  305.                 debug_print('SFTP: Successful connected', 2)
  306.                 SftpCommand.connections[self.connection_id] = self.connection
  307.             except (AuthenticationError, ConnectionError, BinaryMissingError) as e:
  308.                 self.printer.error(e)
  309.                 return False
  310.             except OSError as e:
  311.                 self.close_connection(self.connection_id, self.config['local_dir'], self.window_id)
  312.                 return False
  313.  
  314.         if str(self.window_id) + '-' + self.config['local_dir'] not in SftpCommand.identifiers:
  315.             SftpCommand.identifiers[str(self.window_id) + '-' + self.config['local_dir']] = self.connection_id
  316.             if self.connection_id not in SftpCommand.usage:
  317.                 SftpCommand.usage[self.connection_id] = 0
  318.             SftpCommand.usage[self.connection_id] += 1
  319.         if self.connection_id not in SftpCommand.remote_roots:
  320.             SftpCommand.remote_roots[self.connection_id] = []
  321.         if self.config['remote_dir'] not in SftpCommand.remote_roots[self.connection_id]:
  322.             try:
  323.                 progress = ProgressThread(self.printer, '\nValidating remote folder "%s"' % self.config['initial_remote_dir'])
  324.                 initial_dir = self.connection.pwd()
  325.                 self.connection.cd(self.config['initial_remote_dir'])
  326.                 self.connection.ls(self.config['path_map'], config=self.config)
  327.                 SftpCommand.remote_roots[self.connection_id].append(self.config['remote_dir'])
  328.             except (NotFoundError, PermissionError) as e:
  329.                 progress.stop('failure (' + str(e) + ')')
  330.                 progress.join()
  331.                 self.connection.remote_time_offset = 0
  332.                 output = '\nInitial folder and contents:'
  333.                 try:
  334.                     for path in self.connection.ls(self.config['path_map'], config=self.config):
  335.                         if path[0] == '.':
  336.                             path[0] = ''
  337.                         output += '\n  "%s%s"' % (initial_dir, path[0])
  338.  
  339.                     self.printer.write(output)
  340.                 except (ConnectionError, NotFoundError) as e2:
  341.                     self.close_connection(self.connection_id, self.config['local_dir'], self.window_id)
  342.                     self.printer.write('\nListing initial folder "%s" ... failure (%s)' % (initial_dir, str(e2)))
  343.  
  344.                 self.printer.error(e)
  345.                 return False
  346.             except ConnectionError as e:
  347.                 progress.stop('failure (%s)' % str(e))
  348.                 progress.join()
  349.                 self.close_connection(self.connection_id, self.config['local_dir'], self.window_id, True)
  350.                 self.printer.error(e)
  351.                 return False
  352.             except OSError as e:
  353.                 is_ftp = self.config['type'] == 'ftp'
  354.                 is_pasv = self.config.get('ftp_passive_mode') is not False
  355.                 if isinstance(e, DisconnectionError) and is_ftp and is_pasv:
  356.                     e = OSError('Disconnected - possible PASV mode error, try setting ftp_passive_mode to false in sftp-config.json')
  357.                 self.close_connection(self.connection_id, self.config['local_dir'], self.window_id)
  358.                 progress.stop('failure (%s)' % str(e))
  359.                 progress.join()
  360.                 raise e
  361.             except (AttributeError, EOFError) as e:
  362.                 backtrace = traceback.format_exc()
  363.                 handle_exception('Unknown Error', backtrace)
  364.                 self.close_connection(self.connection_id, self.config['local_dir'], self.window_id)
  365.                 progress.stop('failure (Unknown Error)')
  366.                 progress.join()
  367.                 return False
  368.             except UnicodeDecodeError:
  369.                 self.close_connection(self.connection_id, self.config['local_dir'], self.window_id)
  370.                 progress.stop('failure (Encoding error)')
  371.                 progress.join()
  372.                 raise
  373.  
  374.             progress.stop('success')
  375.             progress.join()
  376.         if self.connection_id not in SftpCommand.remote_time_offsets:
  377.             SftpCommand.remote_time_offsets[self.connection_id] = self.connection.remote_time_offset
  378.         if self.on_connect:
  379.             self.on_connect()
  380.         self.failed = False
  381.         return True
  382.  
  383.     def do_operation(self, show=False):
  384.         self.failed = True
  385.         self.connection.debug(get_debug())
  386.         debug_print('SFTP: Starting operation ' + self.action, 2)
  387.         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)
  388.         if show:
  389.  
  390.             def do_print():
  391.                 self.printer.write('\nUNREGISTERED: Please visit http://sublime.wbond.net/sftp')
  392.  
  393.             sublime.set_timeout(do_print, 1)
  394.         debug_print('SFTP: Finished operation ' + self.action, 2)
  395.         if self.hide:
  396.             sublime.set_timeout(lambda : self.printer.hide(self), 1)
  397.         if self.reset_lcd:
  398.             reset_lcd = self.reset_lcd
  399.             last_reset_lcd = None
  400.             while reset_lcd != last_reset_lcd:
  401.                 last_reset_lcd = reset_lcd
  402.                 try:
  403.                     self.connection.lcd(reset_lcd)
  404.                 except NotFoundError:
  405.                     reset_lcd = os.path.dirname(reset_lcd)
  406.  
  407.         if self.on_fail and not success:
  408.             self.on_fail(result)
  409.         if self.on_complete and success:
  410.             sublime.set_timeout(self.on_complete, 1)
  411.         self.result = result
  412.         if success:
  413.             self.failed = False
  414.         return
  415.  
  416.  
  417. class SftpWritePanelCommand(sublime_plugin.TextCommand):
  418.  
  419.     def run(self, edit):
  420.         printer = PanelPrinter.get(self.view.settings().get('window_id'))
  421.         read_only = self.view.is_read_only()
  422.         if read_only:
  423.             self.view.set_read_only(False)
  424.         keys_to_erase = []
  425.         for key in list(printer.queue):
  426.             while len(printer.strings[key]):
  427.                 string = printer.strings[key].pop(0)
  428.                 if string is None:
  429.                     self.view.erase_regions(key)
  430.                     keys_to_erase.append(key)
  431.                     continue
  432.                 if key == 'sublime_sftp':
  433.                     point = self.view.size()
  434.                 else:
  435.                     regions = self.view.get_regions(key)
  436.                     if not len(regions):
  437.                         point = self.view.size()
  438.                     else:
  439.                         region = regions[0]
  440.                         point = region.b + 1
  441.                     if point == 0 and string[0] == '\n':
  442.                         string = string[1:]
  443.                     self.view.insert(edit, point, string)
  444.                 if key != 'sublime_sftp':
  445.                     point = point + len(string) - 1
  446.                     region = sublime.Region(point, point)
  447.                     self.view.add_regions(key, [region], '')
  448.                     continue
  449.  
  450.         for key in keys_to_erase:
  451.             if key in printer.strings:
  452.                 del printer.strings[key]
  453.             try:
  454.                 printer.queue.remove(key)
  455.             except ValueError:
  456.                 pass
  457.  
  458.         if read_only:
  459.             self.view.set_read_only(True)
  460.         return
  461.  
  462.  
  463. class SftpInsertViewCommand(sublime_plugin.TextCommand):
  464.  
  465.     def run(self, edit, position=0, string=''):
  466.         self.view.insert(edit, position, string)
  467.  
  468.  
  469. class SftpReplaceViewCommand(sublime_plugin.TextCommand):
  470.  
  471.     def run(self, edit, start=0, end=0, string=''):
  472.         self.view.replace(edit, sublime.Region(start, end), string)
  473.  
  474.  
  475. class SftpShowPanelCommand(sublime_plugin.WindowCommand, SftpCommand):
  476.  
  477.     def run(self):
  478.         PanelPrinter.get(self.window.id()).show(True)
  479.  
  480.  
  481. class SftpCreateServerCommand(sublime_plugin.WindowCommand):
  482.  
  483.     def run(self):
  484.         config_dir = get_server_config_folder()
  485.         self.window.run_command('new_file_at', {'dirs': [config_dir]})
  486.         view_settings = self.window.active_view().settings()
  487.         view_settings.set('syntax', 'Packages/JavaScript/JSON.tmLanguage')
  488.         view_settings.set('sftp_new_server', True)
  489.         snippet = get_default_config(remove_settings=[
  490.          'save_before_upload',
  491.          'upload_on_save',
  492.          'confirm_sync',
  493.          'ignore_regex',
  494.          'ignore_regexes',
  495.          'confirm_overwrite_newer',
  496.          'sync_skip_deletes',
  497.          'confirm_downloads'], force_settings={'sync_down_on_open': True})
  498.         self.window.active_view().run_command('insert_snippet', {'contents': snippet})
  499.  
  500.  
  501. class SftpLastServerCommand(sublime_plugin.WindowCommand):
  502.  
  503.     def run(self):
  504.         name = BrowsePathThread.get_last()
  505.         if not name:
  506.             return
  507.         self.window.run_command('sftp_browse_server', {'name': name})
  508.  
  509.  
  510. class SftpBrowseServerCommand(sublime_plugin.WindowCommand):
  511.  
  512.     def run(self, name=None):
  513.         config_dir = get_server_config_folder()
  514.         self.config_dir = config_dir
  515.         servers = []
  516.         for filename in os.listdir(config_dir):
  517.             if filename in (u'.DS_Store', u'Thumbs.db', u'desktop.ini'):
  518.                 continue
  519.             if os.path.isdir(os.path.join(config_dir, filename)):
  520.                 continue
  521.             config = prepare_server_config(filename)
  522.             if config:
  523.                 servers.append(config)
  524.                 continue
  525.  
  526.         self.servers = servers
  527.         choices = [['  ' + r['name'], '  ' + r['desc'] + ' ' + r.get('remote_path', '')] for r in servers]
  528.         choices.insert(0, ['Add New Server...', 'Set up a new SFTP or FTP server to browse'])
  529.         self.window_id = self.window.id()
  530.         self.printer = PanelPrinter.get(self.window_id)
  531.         names = [s['name'] for s in servers]
  532.         if name and name in names:
  533.             self.on_done(names.index(name) + 1)
  534.             return
  535.         show_qp(self.window, choices, self.on_done)
  536.  
  537.     def on_done(self, index):
  538.         if index == -1:
  539.             return
  540.         elif index == 0:
  541.             self.window.run_command('sftp_create_server')
  542.             return
  543.         else:
  544.             index -= 1
  545.             raw_config = self.servers[index]
  546.             if raw_config is None:
  547.                 return
  548.             tmp_dir = setup_tmp_dir(raw_config)
  549.             config = build_config(raw_config, tmp_dir, raw_config['file_path'])
  550.             if not config:
  551.                 return
  552.             config['is_tmp'] = True
  553.             config['tmp_dir'] = tmp_dir
  554.             debug_print('SFTP: Starting Browse Path Thread from List', 2)
  555.             BrowsePathThread(config, self.printer, self.window_id, self.window, None).start()
  556.             return
  557.  
  558.  
  559. class SftpEditServerCommand(sublime_plugin.WindowCommand):
  560.  
  561.     def run(self):
  562.         config_dir = get_server_config_folder()
  563.         self.config_dir = config_dir
  564.         servers = []
  565.         for filename in os.listdir(config_dir):
  566.             if filename in (u'.DS_Store', u'Thumbs.db', u'desktop.ini'):
  567.                 continue
  568.             if os.path.isdir(os.path.join(config_dir, filename)):
  569.                 continue
  570.             config = prepare_server_config(filename)
  571.             if config:
  572.                 servers.append(config)
  573.                 continue
  574.  
  575.         self.servers = servers
  576.         choices = [['  ' + r['name'], '  ' + r['desc'] + ' ' + r.get('remote_path', '')] for r in servers]
  577.         show_qp(self.window, choices, self.on_done)
  578.  
  579.     def on_done(self, index):
  580.         if index == -1:
  581.             return
  582.         remote = self.servers[index]
  583.         config_path = os.path.join(self.config_dir, remote['name'])
  584.  
  585.         def open_file():
  586.             self.window.run_command('open_file', {'file': fix_windows_path(config_path)})
  587.             self.window.active_view().settings().set('syntax', 'Packages/JavaScript/JSON.tmLanguage')
  588.  
  589.         sublime.set_timeout(open_file, 1)
  590.  
  591.  
  592. class SftpDeleteServerCommand(sublime_plugin.WindowCommand):
  593.  
  594.     def run(self):
  595.         config_dir = get_server_config_folder()
  596.         self.config_dir = config_dir
  597.         servers = []
  598.         for filename in os.listdir(config_dir):
  599.             if filename in (u'.DS_Store', u'Thumbs.db', u'desktop.ini'):
  600.                 continue
  601.             if os.path.isdir(os.path.join(config_dir, filename)):
  602.                 continue
  603.             config = prepare_server_config(filename)
  604.             if config:
  605.                 servers.append(config)
  606.                 continue
  607.  
  608.         self.servers = servers
  609.         choices = [['  ' + r['name'], '  ' + r['desc'] + ' ' + r.get('remote_path', '')] for r in servers]
  610.         show_qp(self.window, choices, self.on_done)
  611.  
  612.     def on_done(self, index):
  613.         if index == -1:
  614.             return
  615.         remote = self.servers[index]
  616.         self.config_path = os.path.join(self.config_dir, remote['name'])
  617.         choices = [
  618.          [
  619.           'Yes', 'Delete %s server' % remote['name']],
  620.          [
  621.           'No', 'Do not delete %s server' % remote['name']]]
  622.         show_qp(self.window, choices, self.confirm_delete)
  623.  
  624.     def confirm_delete(self, index):
  625.         if index == -1 or index == 1:
  626.             return
  627.         os.unlink(self.config_path)
  628.  
  629.  
  630. class QuickPanelBrowser(object):
  631.  
  632.     def cleanup(self):
  633.         if 'is_tmp' not in self.config:
  634.             return
  635.         tmp_dir = dirname(self.config['local_dir'])
  636.         if os.path.exists(tmp_dir):
  637.             shutil.rmtree(tmp_dir)
  638.  
  639.  
  640. class BrowsePathThread(HookedThread, QuickPanelBrowser):
  641.     last_name = None
  642.  
  643.     @classmethod
  644.     def get_last(cls):
  645.         return cls.last_name
  646.  
  647.     def __init__(self, config, printer, window_id, window, remote_path, second_time=False):
  648.         self.config = config
  649.         self.printer = printer
  650.         self.window_id = window_id
  651.         self.window = window
  652.         self.remote_path = remote_path
  653.         super(BrowsePathThread, self).__init__()
  654.  
  655.     def run(self):
  656.         BrowsePathThread.last_name = self.config['name']
  657.         if self.remote_path is None:
  658.             pwd_thread = SftpThread(self.window_id, self.config, None, 'cwd', hide=False)
  659.             pwd_thread.start()
  660.             pwd_thread.join()
  661.             if pwd_thread.failed:
  662.                 sublime.set_timeout(self.printer.show, 1)
  663.                 self.cleanup()
  664.                 return
  665.             self.remote_path = pwd_thread.result
  666.         reset_lcd = None
  667.         if self.config.get('is_tmp'):
  668.             reset_lcd = dirname(dirname(self.config['local_dir']))
  669.         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)
  670.         list_dir_thread.start()
  671.         list_dir_thread.join()
  672.         if list_dir_thread.failed and list_dir_thread.result in (u'File not found',
  673.                                                                  u'Folder not found',
  674.                                                                  u'Permission denied'):
  675.             debug_print('SFTP: Starting Browse Thread after Error', 2)
  676.             BrowsePathThread(self.config, self.printer, self.window_id, self.window, dirname(self.remote_path), second_time=True).start()
  677.             return
  678.         elif list_dir_thread.failed:
  679.             sublime.set_timeout(self.printer.show, 1)
  680.             self.cleanup()
  681.             return
  682.         else:
  683.             self.files = []
  684.             self.entries = []
  685.             listing = [p[0] for p in list_dir_thread.result if is_dir(p[0])]
  686.             listing.extend([p[0] for p in list_dir_thread.result if not is_dir(p[0]) and p[0] != '.'])
  687.             if list_dir_thread.result:
  688.                 self.files.extend([p for p in listing])
  689.                 self.entries.extend(['    ' + p for p in listing if is_dir(p)])
  690.                 self.entries.extend(['    ' + p for p in listing if not is_dir(p)])
  691.             self.files.insert(0, '')
  692.             self.entries.insert(0, '' + self.config['host'] + ':' + self.remote_path)
  693.             if not is_root(self.remote_path):
  694.                 self.files.insert(1, '..')
  695.                 self.entries.insert(1, '  Up a folder')
  696.             self.files.insert(1, '.')
  697.             self.entries.insert(1, '  Folder actions')
  698.             self.existing_files = [
  699.              '.', '..']
  700.             if list_dir_thread.result:
  701.                 self.existing_files.extend([p[0].rstrip('/\\') for p in list_dir_thread.result])
  702.             sublime.set_timeout(self.show_files, 1)
  703.             return
  704.  
  705.     def show_files(self):
  706.         show_qp(self.window, self.entries, self.show_files_action)
  707.  
  708.     def custom_path(self):
  709.         self.window.show_input_panel('Browse to', self.remote_path, self.browse_to, None, self.show_files)
  710.         return
  711.  
  712.     def browse_to(self, input):
  713.         if len(input) == 0:
  714.             input = '/'
  715.         input = canonicalize(input, 'remote')
  716.         debug_print('SFTP: Starting Custom Browse Path Thread', 2)
  717.         BrowsePathThread(self.config, self.printer, self.window_id, self.window, input).start()
  718.  
  719.     def show_files_action(self, index):
  720.         if index == -1:
  721.             self.cleanup()
  722.             return
  723.         else:
  724.             selected = self.files[index]
  725.             new_path = None
  726.             if selected == '':
  727.                 self.custom_path()
  728.                 return
  729.             if selected == '..':
  730.                 new_path = dirname(self.remote_path)
  731.             else:
  732.                 if selected == '.':
  733.                     self.selected_path = self.remote_path
  734.                     self.modify_dir()
  735.                     return
  736.                 if not is_dir(selected):
  737.                     self.selected_path = os.path.join(self.remote_path, selected)
  738.                     self.modify_file()
  739.                     return
  740.                 new_path = os.path.join(self.remote_path, selected)
  741.             debug_print('SFTP: Starting Browse Sub-path Thread', 2)
  742.             BrowsePathThread(self.config, self.printer, self.window_id, self.window, new_path).start()
  743.             return
  744.  
  745.     def modify_dir(self):
  746.         actions = [
  747.          '' + self.config['host'] + ':' + self.selected_path,
  748.          '  Back to list',
  749.          '  New file',
  750.          '  New folder',
  751.          '  Rename',
  752.          '  Chmod',
  753.          '  Delete']
  754.         if not self.config.get('is_tmp'):
  755.             actions.insert(4, '  Download')
  756.         show_qp(self.window, actions, self.modify_dir_action)
  757.  
  758.     def modify_dir_action(self, index):
  759.         if index == -1:
  760.             self.cleanup()
  761.             return
  762.         elif index == 0 or index == 1:
  763.             self.show_files()
  764.             return
  765.         else:
  766.             is_tmp = self.config.get('is_tmp')
  767.             if index == 2:
  768.                 self.window.show_input_panel('New file name', '', self.new_file, None, self.modify_dir)
  769.             if index == 3:
  770.                 self.window.show_input_panel('New folder name', '', self.new_folder, None, self.modify_dir)
  771.             if index == 4 and not is_tmp:
  772.                 debug_print('SFTP: Starting Download Folder Thread', 2)
  773.                 DownloadPathThread(self.config, self.printer, self.window_id, self.window, self.selected_path).start()
  774.             if not is_tmp and index == 5 or is_tmp and index == 4:
  775.                 self.window.show_input_panel('New name', os.path.basename(self.selected_path.rstrip('/\\')), self.rename_folder, None, self.modify_dir)
  776.             if not is_tmp and index == 6 or is_tmp and index == 5:
  777.                 self.window.show_input_panel('New permissions', '755', self.chmod_folder, None, self.modify_dir)
  778.             if not is_tmp and index == 7 or is_tmp and index == 6:
  779.                 name = canonicalize(os.path.basename(self.selected_path.rstrip('/\\')), 'remote')
  780.                 choices = [
  781.                  [
  782.                   'Yes', 'Delete %s and all children' % name],
  783.                  [
  784.                   'No', 'Do not delete %s' % name]]
  785.                 show_qp(self.window, choices, self.confirm_delete_dir)
  786.             return
  787.  
  788.     def new_file(self, new_name):
  789.         if new_name.find('\\') != -1 or new_name.find('/') != -1:
  790.  
  791.             def try_again_slash():
  792.                 sublime.error_message('Sublime SFTP\n\nFile name may not contain slashes')
  793.                 self.window.show_input_panel('New file name', '', self.new_file, None, self.modify_dir)
  794.                 return
  795.  
  796.             sublime.set_timeout(try_again_slash, 1)
  797.             return
  798.         if new_name in self.existing_files:
  799.  
  800.             def try_again_existing():
  801.                 sublime.error_message('Sublime SFTP\n\nA file or folder with that name specified already exists')
  802.                 self.window.show_input_panel('New file name', '', self.new_file, None, self.modify_dir)
  803.                 return
  804.  
  805.             sublime.set_timeout(try_again_existing, 1)
  806.             return
  807.         new_path = os.path.join(self.selected_path, new_name)
  808.         debug_print('SFTP: Starting New File Thread', 2)
  809.         NewFileThread(self.config, self.printer, self.window_id, self.window, new_path).start()
  810.  
  811.     def new_folder(self, new_name):
  812.         if new_name.find('\\') != -1 or new_name.find('/') != -1:
  813.  
  814.             def try_again_slash():
  815.                 sublime.error_message('Sublime SFTP\n\nFolder name may not contain slashes')
  816.                 self.window.show_input_panel('New folder name', '', self.new_folder, None, self.modify_dir)
  817.                 return
  818.  
  819.             sublime.set_timeout(try_again_slash, 1)
  820.             return
  821.         if new_name in self.existing_files:
  822.  
  823.             def try_again_existing():
  824.                 sublime.error_message('Sublime SFTP\n\nA file or folder with that name specified already exists')
  825.                 self.window.show_input_panel('New folder name', '', self.new_folder, None, self.modify_dir)
  826.                 return
  827.  
  828.             sublime.set_timeout(try_again_existing, 1)
  829.             return
  830.         new_path = os.path.join(self.selected_path, new_name)
  831.         debug_print('SFTP: Starting New Folder Thread', 2)
  832.         NewFolderThread(self.config, self.printer, self.window_id, self.window, new_path).start()
  833.  
  834.     def rename_folder(self, new_name):
  835.         if new_name == os.path.basename(self.selected_path.rstrip('\\/')):
  836.             self.cleanup()
  837.             return
  838.         if new_name.find('\\') != -1 or new_name.find('/') != -1:
  839.  
  840.             def try_again():
  841.                 sublime.error_message('Sublime SFTP\n\nFolder name may not contain slashes')
  842.                 self.window.show_input_panel('New name', os.path.basename(self.selected_path.rstrip('/\\')), self.rename_folder, None, self.modify_dir)
  843.                 return
  844.  
  845.             sublime.set_timeout(try_again, 1)
  846.             return
  847.         new_path = os.path.join(dirname(self.selected_path), new_name)
  848.         new_path = canonicalize(new_path, 'remote')
  849.         debug_print('SFTP: Starting Rename Folder Thread', 2)
  850.         RenamePathThread(self.config, self.printer, self.window_id, self.window, self.selected_path, new_path).start()
  851.  
  852.     def chmod_folder(self, mode):
  853.         debug_print('SFTP: Starting Chmod Folder Thread', 2)
  854.         ChmodPathThread(self.config, self.printer, self.window_id, self.window, self.selected_path, mode).start()
  855.  
  856.     def confirm_delete_dir(self, index):
  857.         if index == -1 or index == 1:
  858.             self.modify_dir()
  859.             return
  860.         DeletePathThread(self.config, self.printer, self.window_id, self.window, self.selected_path).start()
  861.  
  862.     def modify_file(self):
  863.         actions = [
  864.          '' + self.config['host'] + ':' + self.selected_path,
  865.          '  Back to list',
  866.          '  Edit',
  867.          '  Rename',
  868.          '  Chmod',
  869.          '  Delete']
  870.         if not self.config.get('is_tmp'):
  871.             actions[2] += ' (remote version)'
  872.             actions.insert(3, '  Download')
  873.         show_qp(self.window, actions, self.modify_file_action)
  874.  
  875.     def modify_file_action(self, index):
  876.         if index == -1:
  877.             self.cleanup()
  878.             return
  879.         else:
  880.             is_tmp = self.config.get('is_tmp')
  881.             if index == 0 or index == 1:
  882.                 self.show_files()
  883.                 return
  884.             if index == 2:
  885.                 debug_print('SFTP: Starting Edit File Thread', 2)
  886.                 EditFileThread(self.config, self.printer, self.window_id, self.window, self.selected_path).start()
  887.                 return
  888.             if index == 3 and not is_tmp:
  889.                 debug_print('SFTP: Starting Download File Thread', 2)
  890.                 DownloadPathThread(self.config, self.printer, self.window_id, self.window, self.selected_path).start()
  891.             if not is_tmp and index == 4 or is_tmp and index == 3:
  892.                 self.window.show_input_panel('New name', os.path.basename(self.selected_path.rstrip('/\\')), self.rename_file, None, self.modify_file)
  893.             if not is_tmp and index == 5 or is_tmp and index == 4:
  894.                 self.window.show_input_panel('New permissions', '644', self.chmod_file, None, self.modify_file)
  895.             if not is_tmp and index == 6 or is_tmp and index == 5:
  896.                 name = os.path.basename(self.selected_path)
  897.                 choices = [
  898.                  [
  899.                   'Yes', 'Delete %s' % name],
  900.                  [
  901.                   'No', 'Do not delete %s' % name]]
  902.                 show_qp(self.window, choices, self.confirm_delete_file)
  903.             return
  904.  
  905.     def rename_file(self, new_name):
  906.         if new_name == os.path.basename(self.selected_path.rstrip('\\/')):
  907.             self.cleanup()
  908.             return
  909.         if new_name.find('\\') != -1 or new_name.find('/') != -1:
  910.  
  911.             def try_again():
  912.                 sublime.error_message('Sublime SFTP\n\nFile name may not contain slashes')
  913.                 self.window.show_input_panel('New name', os.path.basename(self.selected_path.rstrip('/\\')), self.rename_path, None, self.modify_file)
  914.                 return
  915.  
  916.             sublime.set_timeout(try_again, 1)
  917.             return
  918.         new_path = os.path.join(dirname(self.selected_path), new_name)
  919.         debug_print('SFTP: Starting Rename File Thread', 2)
  920.         RenamePathThread(self.config, self.printer, self.window_id, self.window, self.selected_path, new_path).start()
  921.  
  922.     def chmod_file(self, mode):
  923.         debug_print('SFTP: Starting Chmod File Thread', 2)
  924.         ChmodPathThread(self.config, self.printer, self.window_id, self.window, self.selected_path, mode).start()
  925.  
  926.     def confirm_delete_file(self, index):
  927.         if index == -1 or index == 1:
  928.             self.modify_file()
  929.             return
  930.         debug_print('SFTP: Starting Delete File Thread', 2)
  931.         DeletePathThread(self.config, self.printer, self.window_id, self.window, self.selected_path).start()
  932.  
  933.  
  934. class DownloadPathThread(HookedThread):
  935.  
  936.     def __init__(self, config, printer, window_id, window, remote_path, on_complete=None):
  937.         self.config = config
  938.         self.printer = printer
  939.         self.window_id = window_id
  940.         self.window = window
  941.         self.remote_path = remote_path
  942.         self.on_complete = on_complete
  943.         super(DownloadPathThread, self).__init__()
  944.  
  945.     def run(self):
  946.         sublime.set_timeout(self.printer.show, 1)
  947.         if not is_dir(self.remote_path):
  948.             download_thread = SftpThread(self.window_id, self.config, self.remote_path, 'get', should_join=False)
  949.             download_thread.start()
  950.             download_thread.join()
  951.             local_path = remote_to_local(self.remote_path, self.config['path_map'])
  952.             if not download_thread.failed:
  953.  
  954.                 def open_file():
  955.                     self.window.run_command('open_file', {'file': fix_windows_path(local_path)})
  956.                     if self.on_complete:
  957.                         self.on_complete()
  958.  
  959.                 sublime.set_timeout(open_file, 1)
  960.             else:
  961.                 BrowsePathThread(self.config, self.printer, self.window_id, self.window, dirname(self.remote_path)).start()
  962.         else:
  963.             download_thread = DownloadFolderThread(self.window_id, self.config, self.remote_path)
  964.             download_thread.start()
  965.             download_thread.join()
  966.             if download_thread.error:
  967.                 sublime.set_timeout(self.printer.show, 1)
  968.             BrowsePathThread(self.config, self.printer, self.window_id, self.window, self.remote_path).start()
  969.  
  970.  
  971. class EditFileThread(HookedThread):
  972.  
  973.     def __init__(self, config, printer, window_id, window, remote_path):
  974.         self.config = config
  975.         self.printer = printer
  976.         self.window_id = window_id
  977.         self.window = window
  978.         self.remote_path = remote_path
  979.         self.local_path = None
  980.         if not config.get('tmp_dir'):
  981.             self.local_path = remote_to_local(remote_path, config.get('path_map'))
  982.             raw_config, config_file = load_config(self.config['local_dir'])
  983.             tmp_dir = setup_tmp_dir(raw_config)
  984.             raw_config, config_file = load_config(tmp_dir)
  985.             new_config = build_config(raw_config, tmp_dir, config_file)
  986.             new_config['tmp_dir'] = tmp_dir
  987.             self.config = new_config
  988.         super(EditFileThread, self).__init__()
  989.         return
  990.  
  991.     def run(self):
  992.         config = self.config.copy()
  993.         config['path_map'] = {config['tmp_dir']: config['remote_dir']}
  994.         local_path = remote_to_local(self.remote_path, config['path_map'])
  995.         sublime.set_timeout(self.printer.show, 1)
  996.         download_thread = SftpThread(self.window_id, config, self.remote_path, 'get', should_join=False, reset_lcd=dirname(dirname(config['local_dir'])))
  997.         download_thread.start()
  998.         download_thread.join()
  999.         if not download_thread.failed:
  1000.  
  1001.             def open_file():
  1002.                 self.window.run_command('open_file', {'file': fix_windows_path(local_path)})
  1003.                 view = self.window.active_view()
  1004.                 if self.local_path:
  1005.                     view.settings().set('local_path', self.local_path)
  1006.                 view.settings().set('remote_loading', True)
  1007.                 view.settings().set('synced', True)
  1008.                 view.settings().set('is_remote', True)
  1009.                 view.settings().set('tmp_dir', self.config['tmp_dir'])
  1010.  
  1011.             sublime.set_timeout(open_file, 1)
  1012.         else:
  1013.             BrowsePathThread(self.config, self.printer, self.window_id, self.window, dirname(self.remote_path)).start()
  1014.  
  1015.  
  1016. class NewFileThread(HookedThread):
  1017.  
  1018.     def __init__(self, config, printer, window_id, window, remote_path):
  1019.         self.config = config
  1020.         self.printer = printer
  1021.         self.window_id = window_id
  1022.         self.window = window
  1023.         self.remote_path = remote_path
  1024.         super(NewFileThread, self).__init__()
  1025.  
  1026.     def run(self):
  1027.         local_path = remote_to_local(self.remote_path, self.config['path_map'])
  1028.         if not os.path.exists(dirname(local_path)):
  1029.             os.makedirs(dirname(local_path))
  1030.         open(local_path, 'a').close()
  1031.         sublime.set_timeout(self.printer.show, 1)
  1032.         reset_lcd = None
  1033.         if self.config.get('is_tmp'):
  1034.             reset_lcd = dirname(dirname(self.config['local_dir']))
  1035.         mv_thread = SftpThread(self.window_id, self.config, local_path, 'put', reset_lcd=reset_lcd)
  1036.         mv_thread.start()
  1037.         mv_thread.join()
  1038.         if not mv_thread.failed:
  1039.  
  1040.             def status():
  1041.                 sublime.status_message('%s successfully created' % path_type(self.remote_path, True))
  1042.                 self.window.run_command('open_file', {'file': fix_windows_path(local_path)})
  1043.                 view = self.window.active_view()
  1044.                 if 'is_tmp' in self.config:
  1045.                     view.settings().set('is_remote', True)
  1046.                     view.settings().set('tmp_dir', self.config['local_dir'])
  1047.  
  1048.             sublime.set_timeout(status, 1)
  1049.         else:
  1050.             BrowsePathThread(self.config, self.printer, self.window_id, self.window, dirname(self.remote_path)).start()
  1051.         return
  1052.  
  1053.  
  1054. class NewFolderThread(HookedThread):
  1055.  
  1056.     def __init__(self, config, printer, window_id, window, remote_path):
  1057.         self.config = config
  1058.         self.printer = printer
  1059.         self.window_id = window_id
  1060.         self.window = window
  1061.         self.remote_path = remote_path
  1062.         super(NewFolderThread, self).__init__()
  1063.  
  1064.     def run(self):
  1065.         local_path = remote_to_local(self.remote_path, self.config['path_map'])
  1066.         os.makedirs(local_path)
  1067.         sublime.set_timeout(self.printer.show, 1)
  1068.         reset_lcd = None
  1069.         if self.config.get('is_tmp'):
  1070.             reset_lcd = dirname(dirname(self.config['local_dir']))
  1071.         mv_thread = SftpThread(self.window_id, self.config, local_path, 'put', reset_lcd=reset_lcd)
  1072.         mv_thread.start()
  1073.         mv_thread.join()
  1074.         if not mv_thread.failed:
  1075.  
  1076.             def status():
  1077.                 sublime.status_message('Folder successfully created')
  1078.  
  1079.             sublime.set_timeout(status, 1)
  1080.         BrowsePathThread(self.config, self.printer, self.window_id, self.window, dirname(self.remote_path)).start()
  1081.         return
  1082.  
  1083.  
  1084. class ChmodPathThread(HookedThread):
  1085.  
  1086.     def __init__(self, config, printer, window_id, window, remote_path, mode):
  1087.         self.config = config
  1088.         self.printer = printer
  1089.         self.window_id = window_id
  1090.         self.window = window
  1091.         self.remote_path = remote_path
  1092.         self.mode = mode
  1093.         super(ChmodPathThread, self).__init__()
  1094.  
  1095.     def run(self):
  1096.         sublime.set_timeout(self.printer.show, 1)
  1097.         reset_lcd = None
  1098.         if self.config.get('is_tmp'):
  1099.             reset_lcd = dirname(dirname(self.config['local_dir']))
  1100.         chmod_thread = SftpThread(self.window_id, self.config, self.remote_path, 'chmod', reset_lcd=reset_lcd, mode=self.mode)
  1101.         chmod_thread.start()
  1102.         chmod_thread.join()
  1103.         if not chmod_thread.failed:
  1104.  
  1105.             def status():
  1106.                 sublime.status_message('%s successfully chmodded' % path_type(self.remote_path, True))
  1107.  
  1108.             sublime.set_timeout(status, 1)
  1109.         BrowsePathThread(self.config, self.printer, self.window_id, self.window, dirname(self.remote_path)).start()
  1110.         return
  1111.  
  1112.  
  1113. class RenamePathThread(HookedThread):
  1114.  
  1115.     def __init__(self, config, printer, window_id, window, remote_path, new_remote_path):
  1116.         self.config = config
  1117.         self.printer = printer
  1118.         self.window_id = window_id
  1119.         self.window = window
  1120.         self.remote_path = remote_path
  1121.         self.new_remote_path = new_remote_path
  1122.         super(RenamePathThread, self).__init__()
  1123.  
  1124.     def run(self):
  1125.         sublime.set_timeout(self.printer.show, 1)
  1126.         reset_lcd = None
  1127.         if self.config.get('is_tmp'):
  1128.             reset_lcd = dirname(dirname(self.config['local_dir']))
  1129.         mv_thread = SftpThread(self.window_id, self.config, [
  1130.          self.remote_path, self.new_remote_path], 'mv', reset_lcd=reset_lcd)
  1131.         mv_thread.start()
  1132.         mv_thread.join()
  1133.         if not mv_thread.failed:
  1134.  
  1135.             def status():
  1136.                 sublime.status_message('%s successfully renamed' % path_type(self.remote_path, True))
  1137.  
  1138.             sublime.set_timeout(status, 1)
  1139.         BrowsePathThread(self.config, self.printer, self.window_id, self.window, dirname(self.remote_path)).start()
  1140.         return
  1141.  
  1142.  
  1143. class DeletePathThread(HookedThread):
  1144.  
  1145.     def __init__(self, config, printer, window_id, window, remote_path):
  1146.         self.config = config
  1147.         self.printer = printer
  1148.         self.window_id = window_id
  1149.         self.window = window
  1150.         self.remote_path = remote_path
  1151.         super(DeletePathThread, self).__init__()
  1152.  
  1153.     def run(self):
  1154.         if is_dir(self.remote_path):
  1155.             reset_lcd = None
  1156.             if self.config.get('is_tmp'):
  1157.                 reset_lcd = dirname(dirname(self.config['local_dir']))
  1158.             list_thread = SftpThread(self.window_id, self.config, self.remote_path, 'listr', should_join=False, reset_lcd=reset_lcd, hide=False, skip_symlinks='file')
  1159.             list_thread.start()
  1160.             list_thread.join()
  1161.             if list_thread.failed or not list_thread.result:
  1162.                 return
  1163.             remote_paths = [p[0] for p in list_thread.result[::-1]]
  1164.         else:
  1165.             remote_paths = self.remote_path
  1166.         sublime.set_timeout(self.printer.show, 1)
  1167.         reset_lcd = None
  1168.         if self.config.get('is_tmp'):
  1169.             reset_lcd = dirname(dirname(self.config['local_dir']))
  1170.         rm_thread = SftpThread(self.window_id, self.config, remote_paths, 'rm', reset_lcd=reset_lcd)
  1171.         rm_thread.start()
  1172.         rm_thread.join()
  1173.         if not rm_thread.failed:
  1174.  
  1175.             def status():
  1176.                 sublime.status_message('%s successfully deleted' % path_type(self.remote_path, True))
  1177.  
  1178.             sublime.set_timeout(status, 1)
  1179.         BrowsePathThread(self.config, self.printer, self.window_id, self.window, dirname(self.remote_path)).start()
  1180.         return
  1181.  
  1182.  
  1183. class SftpBrowseCommand(sublime_plugin.WindowCommand, SftpCommand):
  1184.  
  1185.     def run(self, paths=None, group=None, index=None, reset_lcd=None):
  1186.         config = self.get_config(paths)
  1187.         if not config:
  1188.             return
  1189.         else:
  1190.             if paths is None:
  1191.                 active_view = self.window.active_view()
  1192.                 if active_view and active_view.settings().get('is_remote') and config.get('is_server'):
  1193.                     self.window.run_command('sftp_browse_server', {'name': config.get('name')})
  1194.                     return
  1195.             printer = PanelPrinter.get(self.window.id())
  1196.             path = self.get_path(paths)
  1197.             if path is None:
  1198.                 return
  1199.             if not os.path.isdir(path):
  1200.                 path = os.path.dirname(path)
  1201.             path = canonicalize(path, 'local')
  1202.             remote_path = local_to_remote(path, config['path_map'])
  1203.             debug_print('SFTP: Starting Browse Thread', 2)
  1204.             BrowsePathThread(config, printer, self.window.id(), self.window, remote_path).start()
  1205.             return
  1206.  
  1207.     def is_visible(self, paths=None):
  1208.         path = self.get_path(paths)
  1209.         if not path:
  1210.             return False
  1211.         return self.has_config(path)
  1212.  
  1213.  
  1214. class SftpUploadFileCommand(sublime_plugin.WindowCommand, SftpCommand):
  1215.  
  1216.     def run(self, paths=None, group=None, index=None, reset_lcd=None):
  1217.         if paths is None and group is not None and index is not None:
  1218.             selected_view = get_view_by_group_index(self.window, group, index)
  1219.             paths = [selected_view.file_name()]
  1220.         config = self.get_config(paths)
  1221.         if not config:
  1222.             return
  1223.         files = self.get_path(paths, True)
  1224.         filtered_files = []
  1225.  
  1226.         def filter_file(file_):
  1227.             if os.path.exists(file_):
  1228.                 filtered_files.append(file_)
  1229.             else:
  1230.                 debug_print('SFTP: Skipping File Not Present on Disk', 2)
  1231.  
  1232.         if isinstance(files, list):
  1233.             for file_ in files:
  1234.                 filter_file(file_)
  1235.  
  1236.         else:
  1237.             filter_file(files)
  1238.             if filtered_files:
  1239.                 filtered_files = filtered_files[0]
  1240.             files = filtered_files
  1241.             if not files:
  1242.                 sublime.error_message('Sublime SFTP\n\nUnable to upload file since it is not present on disk')
  1243.                 return
  1244.             if config['save_before_upload']:
  1245.                 self.save_files(files)
  1246.             printer = PanelPrinter.get(self.window.id())
  1247.             printer.show()
  1248.             if config['confirm_overwrite_newer']:
  1249.                 debug_print('SFTP: Starting Confirm Overwrite File Thread', 2)
  1250.                 ConfirmOverwriteFileThread(self.window, printer, config, files, reset_lcd=reset_lcd).start()
  1251.             else:
  1252.                 debug_print('SFTP: Starting Upload File Thread', 2)
  1253.                 SftpThread(self.window.id(), config, files, 'put', reset_lcd=reset_lcd).start()
  1254.             return
  1255.  
  1256.     def is_visible(self, paths=None):
  1257.         path = self.get_path(paths)
  1258.         if not path or os.path.isdir(path):
  1259.             return False
  1260.         return self.has_config(path)
  1261.  
  1262.  
  1263. class ConfirmOverwriteFileThread(HookedThread, SyncThread):
  1264.  
  1265.     def __init__(self, window, printer, config, files, reset_lcd):
  1266.         self.window = window
  1267.         self.window_id = window.id()
  1268.         self.printer = printer
  1269.         self.config = config
  1270.         if not isinstance(files, list):
  1271.             files = [
  1272.              files]
  1273.         self.files = files
  1274.         self.reset_lcd = reset_lcd
  1275.         super(ConfirmOverwriteFileThread, self).__init__()
  1276.  
  1277.     def run(self):
  1278.         last_thread = ThreadTracker.get_last_added(self.window_id)
  1279.         ThreadTracker.add(self)
  1280.         if last_thread is not None:
  1281.             last_thread.join()
  1282.         path_map = self.config['path_map']
  1283.         for path in self.files:
  1284.             remote_path = local_to_remote(path, path_map, self.config['remote_encoding'])
  1285.             nonlocal_ = {'progress': None}
  1286.  
  1287.             def on_connect():
  1288.                 nonlocal_['progress'] = ProgressThread(self.printer, '\nChecking modification date of "%s"' % remote_path)
  1289.  
  1290.             def on_fail(e):
  1291.                 if not nonlocal_['progress']:
  1292.                     return
  1293.                 nonlocal_['progress'].stop('failure (%s)' % str(e))
  1294.                 nonlocal_['progress'].join()
  1295.  
  1296.             dir = canonicalize(dirname(path), 'local')
  1297.             remote_dir = local_to_remote(dir, path_map, self.config['remote_encoding'])
  1298.             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')
  1299.             list_dir_thread.start()
  1300.             list_dir_thread.join()
  1301.  
  1302.             def upload_file():
  1303.                 debug_print('SFTP: Starting Confirmed Upload File Thread', 2)
  1304.                 put_thread = SftpThread(self.window_id, self.config, path, 'put', should_join=False, hide=False, reset_lcd=self.reset_lcd)
  1305.                 put_thread.start()
  1306.                 put_thread.join()
  1307.  
  1308.             if list_dir_thread.failed and list_dir_thread.result in (u'File not found',
  1309.                                                                      u'Folder not found'):
  1310.                 upload_file()
  1311.                 continue
  1312.             if list_dir_thread.failed:
  1313.                 return
  1314.             if nonlocal_['progress']:
  1315.                 nonlocal_['progress'].stop('success')
  1316.                 nonlocal_['progress'].join()
  1317.             remote_paths = list_dir_thread.result
  1318.             if list_dir_thread.failed or remote_paths is None:
  1319.                 remote_paths = []
  1320.             remote_dict = dict(remote_paths)
  1321.             remote_mod_time = remote_dict.get(os.path.basename(remote_path), None)
  1322.             local_mod_time = int(os.lstat(path)[8])
  1323.             if not remote_mod_time or local_mod_time >= remote_mod_time:
  1324.                 upload_file()
  1325.                 continue
  1326.             nonlocal_ = {'confirmed': None}
  1327.  
  1328.             def on_confirm():
  1329.                 nonlocal_['confirmed'] = True
  1330.  
  1331.             def on_reject():
  1332.                 nonlocal_['confirmed'] = False
  1333.  
  1334.             local_time = self.make_time(local_mod_time)
  1335.             remote_time = self.make_time(remote_mod_time)
  1336.             operations = [
  1337.              'Upload local "%s" (%s) over remote "%s" [%s vs. %s]' % (
  1338.               self.strip(path, dir, 'local'),
  1339.               time_diff(local_mod_time, remote_mod_time),
  1340.               self.strip(remote_path, remote_dir, 'remote'),
  1341.               local_time, remote_time)]
  1342.             self.confirm(operations, on_confirm, on_reject)
  1343.             while True:
  1344.                 if nonlocal_['confirmed'] is not None:
  1345.                     break
  1346.                 time.sleep(0.01)
  1347.  
  1348.             if nonlocal_['confirmed']:
  1349.                 upload_file()
  1350.                 continue
  1351.  
  1352.         def do_hide():
  1353.             self.printer.hide()
  1354.  
  1355.         sublime.set_timeout(do_hide, 1)
  1356.         return
  1357.  
  1358.  
  1359. class SftpMonitorFileCommand(sublime_plugin.WindowCommand, SftpCommand):
  1360.  
  1361.     def run(self, paths=None):
  1362.         config = self.get_config(paths)
  1363.         if not config:
  1364.             return
  1365.         printer = PanelPrinter.get(self.window.id())
  1366.         path = self.get_path(paths)
  1367.         if not path:
  1368.             return
  1369.         view = self.window.active_view()
  1370.         if view.id() not in [v.id for v in self.window.views()]:
  1371.             file_name = fix_windows_path(view.file_name())
  1372.             self.window.run_command('open_file', {'file': file_name})
  1373.         if not view.get_status('sftp_monitor'):
  1374.             debug_print('SFTP: Starting File Monitoring', 2)
  1375.             sftp_settings = sublime.load_settings('SFTP.sublime-settings')
  1376.             frequency = sftp_settings.get('monitoring_frequency', 200)
  1377.             frequency = float(frequency) / 1000.0
  1378.             delay = sftp_settings.get('monitoring_upload_delay', 500)
  1379.             delay = float(delay) / 1000.0
  1380.             view.set_status('sftp_monitor', 'SFTP: Monitoring')
  1381.             MonitorFileThread(self.window, view, printer, config, path, frequency, delay).start()
  1382.         else:
  1383.             view.erase_status('sftp_monitor')
  1384.  
  1385.     def is_visible(self, paths=None):
  1386.         if paths is None:
  1387.             active_view = self.window.active_view()
  1388.             if active_view and active_view.settings().get('is_remote'):
  1389.                 return False
  1390.         path = self.get_path(paths)
  1391.         if not path or os.path.isdir(path):
  1392.             return False
  1393.         else:
  1394.             return self.has_config(path)
  1395.  
  1396.  
  1397. class MonitorFileThread(HookedThread):
  1398.  
  1399.     def __init__(self, window, view, printer, config, path, frequency, delay):
  1400.         self.window = window
  1401.         self.window_id = window.id()
  1402.         self.view = view
  1403.         self.printer = printer
  1404.         self.config = config
  1405.         self.path = path
  1406.         self.frequency = frequency
  1407.         self.delay = delay
  1408.         super(MonitorFileThread, self).__init__()
  1409.  
  1410.     def run(self):
  1411.         nonlocal_ = {'cont': True}
  1412.         try:
  1413.             uploaded_mod_time = int(os.lstat(self.path)[8])
  1414.         except OSError as e:
  1415.             debug_print('SFTP: Monitoring Error - %s' % str(e), 2)
  1416.             nonlocal_['cont'] = False
  1417.  
  1418.         num = 0
  1419.         while 1:
  1420.             if nonlocal_['cont']:
  1421.                 try:
  1422.                     mod_time = int(os.lstat(self.path)[8])
  1423.                 except OSError as e:
  1424.                     debug_print('SFTP: Monitoring Error - %s' % str(e), 2)
  1425.                     break
  1426.  
  1427.                 if mod_time > uploaded_mod_time:
  1428.                     time.sleep(self.delay)
  1429.  
  1430.                     def show():
  1431.                         self.printer.show()
  1432.  
  1433.                     sublime.set_timeout(show, 1)
  1434.                     debug_print('SFTP: Starting Monitored Upload File Thread', 2)
  1435.                     SftpThread(self.window_id, self.config, self.path, 'put').start()
  1436.                     uploaded_mod_time = mod_time
  1437.                 num = (num + 1) % 5
  1438.                 if num == 4:
  1439.  
  1440.                     def check_view():
  1441.                         if self.view is None or not self.view.get_status('sftp_monitor'):
  1442.                             nonlocal_['cont'] = False
  1443.                         return
  1444.  
  1445.                     sublime.set_timeout(check_view, 1)
  1446.                 time.sleep(self.frequency)
  1447.  
  1448.         def stop_monitoring():
  1449.             debug_print('SFTP: Stoping File Monitoring', 2)
  1450.             self.view.erase_status('sftp_monitor')
  1451.  
  1452.         sublime.set_timeout(stop_monitoring, 1)
  1453.  
  1454.  
  1455. class SftpUploadOpenFilesCommand(sublime_plugin.WindowCommand, SftpCommand):
  1456.  
  1457.     def run(self, paths=None):
  1458.         debug_print('SFTP: Starting Upload Open Files Command', 2)
  1459.         for view in self.window.views():
  1460.             path = view.file_name()
  1461.             if not path:
  1462.                 debug_print('SFTP: Skipping View Without File Name', 2)
  1463.                 continue
  1464.             if not os.path.exists(path):
  1465.                 debug_print('SFTP: Skipping File Not Present on Disk', 2)
  1466.                 continue
  1467.             paths = [
  1468.              path]
  1469.             if not self.has_config(path):
  1470.                 debug_print('SFTP: Skipping View Without Remote', 2)
  1471.                 continue
  1472.             config = self.get_config(paths)
  1473.             if not config:
  1474.                 continue
  1475.             PanelPrinter.get(self.window.id()).show()
  1476.             files = self.get_path(paths, True)
  1477.             if config['save_before_upload']:
  1478.                 self.save_files(files)
  1479.             debug_print('SFTP: Starting Upload File Thread', 2)
  1480.             SftpThread(self.window.id(), config, files, 'put').start()
  1481.  
  1482.     def is_visible(self, paths=None):
  1483.         if paths is None:
  1484.             active_view = self.window.active_view()
  1485.             if active_view and active_view.settings().get('is_remote'):
  1486.                 return False
  1487.         path = self.get_path(paths)
  1488.         if not path or os.path.isdir(path):
  1489.             return False
  1490.         else:
  1491.             return self.has_config(path)
  1492.  
  1493.  
  1494. class SftpDiffRemoteFileCommand(sublime_plugin.WindowCommand, SftpCommand):
  1495.  
  1496.     def run(self, paths=None):
  1497.         config = self.get_config(paths)
  1498.         if not config:
  1499.             return
  1500.         printer = PanelPrinter.get(self.window.id())
  1501.         printer.show()
  1502.         file = self.get_path(paths)
  1503.         remote_file = local_to_remote(file, config['path_map'])
  1504.         local_path = list(config['path_map'].keys())[0]
  1505.         remote_path = config['path_map'][local_path]
  1506.         tmp_dir = os.path.join(tempfile.gettempdir(), 'sublime-sftp-diff-') + str(int(time.time()))
  1507.         if not os.path.exists(tmp_dir):
  1508.             os.makedirs(tmp_dir)
  1509.         config['path_map'] = {tmp_dir: remote_path}
  1510.         tmp_file = remote_to_local(remote_file, config['path_map'])
  1511.         tmp_file_direct_parent = dirname(tmp_file)
  1512.         if not os.path.exists(tmp_file_direct_parent):
  1513.             os.makedirs(tmp_file_direct_parent)
  1514.         debug_print('SFTP: Starting Diff Remote Thread', 2)
  1515.         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()
  1516.  
  1517.     def complete(self, printer, file, remote_file, tmp_dir, tmp_file):
  1518.         printer.show()
  1519.         progress = ProgressThread(printer, '\nDiffing "%s" with "%s"' % (file, remote_file))
  1520.         sftp_settings = sublime.load_settings('SFTP.sublime-settings')
  1521.         if sftp_settings.get('diff_command'):
  1522.             diff_command_args = sftp_settings.get('diff_command')
  1523.             diff_thread = DiffCommandThread(diff_command_args, file, tmp_file, tmp_dir, sftp_settings.get('delete_temp_diff_folder', True))
  1524.             debug_print('SFTP: Starting Diff Command Thread', 2)
  1525.             diff_thread.start()
  1526.             progress.stop('success (external diff launched)')
  1527.             progress.join()
  1528.         else:
  1529.             settings = sublime.load_settings('Base File.sublime-settings')
  1530.             fallback_encoding = settings.get('fallback_encoding')
  1531.             fallback_encoding = re.sub('^[a-zA-Z ]*\\((.*)\\)$', '\\1', fallback_encoding)
  1532.             try:
  1533.                 file_lines = codecs.open(file, 'r', 'utf-8').read().splitlines()
  1534.             except UnicodeDecodeError:
  1535.                 debug_print('SFTP: Using fallback encoding "%s" when reading local file for diff' % fallback_encoding, 2)
  1536.                 file_lines = codecs.open(file, 'r', fallback_encoding).read().splitlines()
  1537.  
  1538.             try:
  1539.                 tmp_file_lines = codecs.open(tmp_file, 'r', 'utf-8').read().splitlines()
  1540.             except UnicodeDecodeError:
  1541.                 debug_print('SFTP: Using fallback encoding "%s" when reading remote file for diff' % fallback_encoding, 2)
  1542.                 tmp_file_lines = codecs.open(tmp_file, 'r', fallback_encoding).read().splitlines()
  1543.  
  1544.             file_date = time.ctime(os.stat(file).st_mtime)
  1545.             tmp_file_date = time.ctime(os.stat(tmp_file).st_mtime)
  1546.             diff = difflib.unified_diff(file_lines, tmp_file_lines, file, tmp_file, file_date, tmp_file_date, lineterm='')
  1547.             diff = ('\n').join([line for line in diff])
  1548.             if diff == '':
  1549.                 progress.stop('success (no changes)')
  1550.                 printer.reset_hide()
  1551.             else:
  1552.                 name = os.path.basename(file)
  1553.                 new_view = self.window.new_file()
  1554.                 new_view.set_name('(local) ' + name + ' -> (remote) ' + name)
  1555.                 new_view.set_scratch(True)
  1556.                 new_view.set_syntax_file('Packages/Diff/Diff.tmLanguage')
  1557.                 new_view.run_command('sftp_insert_view', {'position': 0,  'string': diff})
  1558.                 progress.stop('success')
  1559.             progress.join()
  1560.         if os.path.exists(tmp_dir):
  1561.             try:
  1562.                 shutil.rmtree(tmp_dir)
  1563.             except WindowsError:
  1564.                 pass
  1565.  
  1566.     def is_visible(self, paths=None):
  1567.         if paths is None:
  1568.             active_view = self.window.active_view()
  1569.             if active_view and active_view.settings().get('is_remote'):
  1570.                 return False
  1571.         path = self.get_path(paths)
  1572.         if not path or os.path.isdir(path):
  1573.             return False
  1574.         else:
  1575.             return self.has_config(path)
  1576.  
  1577.  
  1578. class DiffCommandThread(HookedThread):
  1579.  
  1580.     def __init__(self, args, file, tmp_file, tmp_dir, delete_tmp_dir):
  1581.         self.args = args
  1582.         self.file = file
  1583.         self.tmp_file = tmp_file
  1584.         self.tmp_dir = tmp_dir
  1585.         self.delete_tmp_dir = delete_tmp_dir
  1586.         super(DiffCommandThread, self).__init__()
  1587.  
  1588.     def run(self):
  1589.         args = []
  1590.         for arg in self.args:
  1591.             if arg == '%1$s':
  1592.                 arg = self.file
  1593.             else:
  1594.                 if arg == '%2$s':
  1595.                     arg = self.tmp_file
  1596.                 args.append(arg)
  1597.  
  1598.         proc = subprocess.Popen(args)
  1599.         proc.wait()
  1600.         if self.delete_tmp_dir and os.path.exists(self.tmp_dir):
  1601.             try:
  1602.                 shutil.rmtree(self.tmp_dir)
  1603.             except WindowsError:
  1604.                 pass
  1605.  
  1606.  
  1607. class SftpRenameLocalAndRemotePathsCommand(sublime_plugin.WindowCommand, SftpCommand):
  1608.  
  1609.     def run(self, paths=None, files=None, dirs=None):
  1610.         if paths is None and files is not None:
  1611.             paths = files
  1612.         if paths is None and dirs is not None:
  1613.             paths = dirs
  1614.         config = self.get_config(paths)
  1615.         if not config:
  1616.             return
  1617.         else:
  1618.             printer = PanelPrinter.get(self.window.id())
  1619.             path = self.get_path(paths)
  1620.             remote_path = local_to_remote(path, config['path_map'])
  1621.  
  1622.             def prompt_new_name():
  1623.                 self.window.show_input_panel('New Name', os.path.basename(path.rstrip('/\\')), on_done, None, None)
  1624.                 return
  1625.  
  1626.             def on_done(new_name):
  1627.                 if new_name.find('\\') != -1 or new_name.find('/') != -1:
  1628.                     sublime.error_message('Sublime SFTP\n\n%s name may not contain slashes' % path_type(path))
  1629.                     prompt_new_name()
  1630.                     return
  1631.                 new_path = dirname(path) + new_name
  1632.                 if os.path.exists(new_path):
  1633.                     sublime.error_message('Sublime SFTP\n\nA file or folder with that name specified already exists')
  1634.                     prompt_new_name()
  1635.                     return
  1636.                 new_remote_path = dirname(remote_path) + new_name
  1637.                 printer.show()
  1638.                 RenameLocalAndRemotePathThread(self.window, config, path, new_path, remote_path, new_remote_path).start()
  1639.  
  1640.             prompt_new_name()
  1641.             return
  1642.  
  1643.     def is_visible(self, paths=None, files=None, dirs=None):
  1644.         if paths is None and files is None and dirs is None:
  1645.             active_view = self.window.active_view()
  1646.             if active_view and active_view.settings().get('is_remote'):
  1647.                 return False
  1648.         if dirs == [] or files == []:
  1649.             return False
  1650.         if paths is None and files is not None:
  1651.             paths = files
  1652.         if paths is None and dirs is not None:
  1653.             paths = dirs
  1654.         path = self.get_path(paths)
  1655.         if not path:
  1656.             return False
  1657.         else:
  1658.             return self.has_config(path)
  1659.  
  1660.  
  1661. class RenameLocalAndRemotePathThread(HookedThread):
  1662.  
  1663.     def __init__(self, window, config, path, new_path, remote_path, new_remote_path):
  1664.         self.window = window
  1665.         self.window_id = window.id()
  1666.         self.config = config
  1667.         self.path = path
  1668.         self.new_path = new_path
  1669.         self.remote_path = remote_path
  1670.         self.new_remote_path = new_remote_path
  1671.         super(RenameLocalAndRemotePathThread, self).__init__()
  1672.  
  1673.     def run(self):
  1674.         last_thread = ThreadTracker.get_last_added(self.window_id)
  1675.         ThreadTracker.add(self)
  1676.         if last_thread is not None:
  1677.             last_thread.join()
  1678.         mv_thread = SftpThread(self.window_id, self.config, [
  1679.          self.remote_path, self.new_remote_path], 'mv', should_join=False, reset_lcd=dirname(self.config['local_dir']))
  1680.         mv_thread.start()
  1681.         mv_thread.join()
  1682.         if mv_thread.failed:
  1683.             return
  1684.         else:
  1685.             os.rename(self.path, self.new_path)
  1686.             if int(sublime.version()) < 2178:
  1687.                 return
  1688.  
  1689.             def do_retarget():
  1690.                 view = self.window.find_open_file(self.path)
  1691.                 if view:
  1692.                     view.retarget(self.new_path)
  1693.  
  1694.             sublime.set_timeout(do_retarget, 1)
  1695.             return
  1696.  
  1697.  
  1698. class SftpDeleteLocalAndRemotePathsCommand(sublime_plugin.WindowCommand, SftpCommand):
  1699.  
  1700.     def run(self, paths=None, files=None, dirs=None):
  1701.         if paths is None and files is not None:
  1702.             paths = files
  1703.         if paths is None and dirs is not None:
  1704.             paths = dirs
  1705.         config = self.get_config(paths)
  1706.         if not config:
  1707.             return
  1708.         else:
  1709.             printer = PanelPrinter.get(self.window.id())
  1710.             path = self.get_path(paths)
  1711.             if os.path.isdir(path):
  1712.                 path = canonicalize(path, 'local')
  1713.             remote_path = local_to_remote(path, config['path_map'])
  1714.             type_ = path_type(remote_path)
  1715.  
  1716.             def on_done(index):
  1717.                 if index == -1 or index == 1:
  1718.                     return
  1719.                 printer.show()
  1720.                 DeleteLocalAndRemotePathThread(self.window, config, path, remote_path).start()
  1721.  
  1722.             choices = [
  1723.              [
  1724.               'Yes', 'Delete local and remote %ss %s' % (type_, os.path.basename(remote_path.rstrip('/\\')))],
  1725.              [
  1726.               'No', 'Do not delete local and remote %ss %s' % (type_, os.path.basename(remote_path.rstrip('/\\')))]]
  1727.             show_qp(self.window, choices, on_done)
  1728.             return
  1729.  
  1730.     def is_visible(self, paths=None, files=None, dirs=None):
  1731.         if paths is None and files is None and dirs is None:
  1732.             active_view = self.window.active_view()
  1733.             if active_view and active_view.settings().get('is_remote'):
  1734.                 return False
  1735.         if dirs == [] or files == []:
  1736.             return False
  1737.         if paths is None and files is not None:
  1738.             paths = files
  1739.         if paths is None and dirs is not None:
  1740.             paths = dirs
  1741.         path = self.get_path(paths)
  1742.         if not path:
  1743.             return False
  1744.         else:
  1745.             return self.has_config(path)
  1746.  
  1747.  
  1748. class DeleteLocalAndRemotePathThread(HookedThread):
  1749.  
  1750.     def __init__(self, window, config, path, remote_path):
  1751.         self.window_id = window.id()
  1752.         self.config = config
  1753.         self.path = path
  1754.         self.remote_path = remote_path
  1755.         super(DeleteLocalAndRemotePathThread, self).__init__()
  1756.  
  1757.     def run(self):
  1758.         last_thread = ThreadTracker.get_last_added(self.window_id)
  1759.         ThreadTracker.add(self)
  1760.         if last_thread is not None:
  1761.             last_thread.join()
  1762.         if is_dir(self.remote_path):
  1763.             list_thread = SftpThread(self.window_id, self.config, self.remote_path, 'listr', should_join=False, hide=False, skip_symlinks='file')
  1764.             list_thread.start()
  1765.             list_thread.join()
  1766.             if list_thread.failed and list_thread.result in (u'File not found', u'Folder not found'):
  1767.                 remote_paths = []
  1768.             else:
  1769.                 if list_thread.failed or not list_thread.result:
  1770.                     return
  1771.                 remote_paths = [p[0] for p in list_thread.result[::-1]]
  1772.         else:
  1773.             remote_paths = self.remote_path
  1774.         if remote_paths:
  1775.             rm_thread = SftpThread(self.window_id, self.config, remote_paths, 'rm', should_join=False)
  1776.             rm_thread.start()
  1777.             rm_thread.join()
  1778.             if rm_thread.failed and rm_thread.result not in (u'File not found', u'Folder not found'):
  1779.                 return
  1780.         if is_dir(self.path):
  1781.             shutil.rmtree(self.path)
  1782.         else:
  1783.             os.remove(self.path)
  1784.         return
  1785.  
  1786.  
  1787. class SftpDeleteRemotePathCommand(sublime_plugin.WindowCommand, SftpCommand):
  1788.  
  1789.     def run(self, paths=None, files=None, dirs=None):
  1790.         if paths is None and files is not None:
  1791.             paths = files
  1792.         if paths is None and dirs is not None:
  1793.             paths = dirs
  1794.         config = self.get_config(paths)
  1795.         if not config:
  1796.             return
  1797.         else:
  1798.             printer = PanelPrinter.get(self.window.id())
  1799.             path = self.get_path(paths)
  1800.             remote_path = local_to_remote(path, config['path_map'])
  1801.             type_ = path_type(remote_path)
  1802.  
  1803.             def on_done(index):
  1804.                 if index == -1 or index == 1:
  1805.                     return
  1806.                 printer.show()
  1807.                 DeleteRemotePathThread(self.window, config, remote_path).start()
  1808.  
  1809.             choices = [
  1810.              [
  1811.               'Yes', 'Delete remote %s %s' % (type_, os.path.basename(remote_path.rstrip('/\\')))],
  1812.              [
  1813.               'No', 'Do not delete remote %s %s' % (type_, os.path.basename(remote_path.rstrip('/\\')))]]
  1814.             show_qp(self.window, choices, on_done)
  1815.             return
  1816.  
  1817.     def is_visible(self, paths=None, files=None, dirs=None):
  1818.         if paths is None and files is None and dirs is None:
  1819.             active_view = self.window.active_view()
  1820.             if active_view and active_view.settings().get('is_remote'):
  1821.                 return False
  1822.         if dirs == [] or files == []:
  1823.             return False
  1824.         if paths is None and files is not None:
  1825.             paths = files
  1826.         if paths is None and dirs is not None:
  1827.             paths = dirs
  1828.         path = self.get_path(paths)
  1829.         if not path:
  1830.             return False
  1831.         else:
  1832.             return self.has_config(path)
  1833.  
  1834.  
  1835. class DeleteRemotePathThread(HookedThread):
  1836.  
  1837.     def __init__(self, window, config, remote_path):
  1838.         self.window_id = window.id()
  1839.         self.config = config
  1840.         self.remote_path = remote_path
  1841.         super(DeleteRemotePathThread, self).__init__()
  1842.  
  1843.     def run(self):
  1844.         last_thread = ThreadTracker.get_last_added(self.window_id)
  1845.         ThreadTracker.add(self)
  1846.         if last_thread is not None:
  1847.             last_thread.join()
  1848.         if is_dir(self.remote_path):
  1849.             list_thread = SftpThread(self.window_id, self.config, self.remote_path, 'listr', should_join=False, hide=False, skip_symlinks='file')
  1850.             list_thread.start()
  1851.             list_thread.join()
  1852.             if list_thread.failed or not list_thread.result:
  1853.                 return
  1854.             remote_paths = [p[0] for p in list_thread.result[::-1]]
  1855.         else:
  1856.             remote_paths = self.remote_path
  1857.         rm_thread = SftpThread(self.window_id, self.config, remote_paths, 'rm', should_join=False)
  1858.         rm_thread.start()
  1859.         rm_thread.join()
  1860.         return
  1861.  
  1862.  
  1863. class SftpDownloadFileCommand(sublime_plugin.WindowCommand, SftpCommand):
  1864.  
  1865.     def run(self, paths=None):
  1866.         config = self.get_config(paths)
  1867.         if not config:
  1868.             return
  1869.         else:
  1870.             self.printer = PanelPrinter.get(self.window.id())
  1871.             file = self.get_path(paths)
  1872.             view = self.window.active_view()
  1873.             self.on_complete = None
  1874.             if view and view.file_name() == file:
  1875.                 self.on_complete = lambda : view.run_command('revert')
  1876.             self.remote_file = local_to_remote(file, config['path_map'])
  1877.             self.config = config
  1878.             if not config['confirm_downloads']:
  1879.                 self.do_download()
  1880.                 return
  1881.             debug_print('SFTP: Starting Confirm Download File Thread', 2)
  1882.             basename = os.path.basename(file)
  1883.             if os.name != 'nt':
  1884.                 basename = unicodedata.normalize('NFC', basename)
  1885.             choices = [['Yes', 'Download the file %s' % basename],
  1886.              [
  1887.               'No', 'Do not download the file %s' % basename]]
  1888.  
  1889.             def on_choose(index):
  1890.                 if index == -1 or index == 1:
  1891.                     return
  1892.                 self.do_download()
  1893.  
  1894.             show_qp(self.window, choices, on_choose)
  1895.             return
  1896.  
  1897.     def do_download(self):
  1898.         self.printer.show()
  1899.         debug_print('SFTP: Starting Download File Thread', 2)
  1900.         SftpThread(self.window.id(), self.config, self.remote_file, 'get', on_complete=self.on_complete).start()
  1901.  
  1902.     def is_visible(self, paths=None):
  1903.         path = self.get_path(paths)
  1904.         if not path or os.path.isdir(path):
  1905.             return False
  1906.         return self.has_config(path)
  1907.  
  1908.  
  1909. class SftpUploadFolderCommand(sublime_plugin.WindowCommand, SftpCommand):
  1910.  
  1911.     def run(self, paths=None):
  1912.         config = self.get_config(paths)
  1913.         if not config:
  1914.             return
  1915.         dir = self.get_path(paths)
  1916.         if not os.path.isdir(dir):
  1917.             dir = dirname(dir)
  1918.         dir = canonicalize(dir, 'local')
  1919.         debug_print('SFTP: Starting Upload Folder Command Thread', 2)
  1920.         UploadFolderThread(self.window, config, dir, self.save_files).start()
  1921.  
  1922.     def is_visible(self, paths=None):
  1923.         if paths is None:
  1924.             active_view = self.window.active_view()
  1925.             if active_view and active_view.settings().get('is_remote'):
  1926.                 return False
  1927.         path = self.get_path(paths)
  1928.         if not path:
  1929.             return False
  1930.         else:
  1931.             return self.has_config(path)
  1932.  
  1933.  
  1934. class UploadFolderThread(HookedThread):
  1935.  
  1936.     def __init__(self, window, config, dir, save_files):
  1937.         self.config = config
  1938.         self.printer = PanelPrinter.get(window.id())
  1939.         self.window_id = window.id()
  1940.         self.dir = dir
  1941.         self.save_files = save_files
  1942.         super(UploadFolderThread, self).__init__()
  1943.  
  1944.     def run(self):
  1945.         last_thread = ThreadTracker.get_last_added(self.window_id)
  1946.         ThreadTracker.add(self)
  1947.         if last_thread is not None:
  1948.             last_thread.join()
  1949.  
  1950.         def do_run():
  1951.             self.printer.show()
  1952.             basename = os.path.basename(self.dir.rstrip('/\\'))
  1953.             progress = ProgressThread(self.printer, '\nUploading folder "%s"' % basename)
  1954.             paths = [
  1955.              self.dir]
  1956.             for root, dirs, files in os.walk(self.dir):
  1957.                 paths.extend([os.path.join(root, path) for path in dirs])
  1958.                 paths.extend([os.path.join(root, path) for path in files])
  1959.  
  1960.             paths, to_upload, ignored = ignore_paths(paths, self.config)
  1961.             paths.sort()
  1962.             message = '%d %s to upload' % (to_upload, 'files' if to_upload != 1 else 'file')
  1963.             if ignored:
  1964.                 message += ', %d ignored' % ignored
  1965.             progress.stop(message)
  1966.             progress.join()
  1967.             if paths:
  1968.                 if self.config['save_before_upload']:
  1969.                     self.save_files(paths)
  1970.                 debug_print('SFTP: Starting Upload Folder Thread', 2)
  1971.                 SftpThread(self.window_id, self.config, paths, 'put').start()
  1972.             else:
  1973.                 self.printer.hide()
  1974.  
  1975.         sublime.set_timeout(do_run, 10)
  1976.         return
  1977.  
  1978.  
  1979. class SftpSyncUpCommand(sublime_plugin.WindowCommand, SftpCommand):
  1980.  
  1981.     def run(self, paths=None, ignore_delete=False, on_complete=None):
  1982.         config = self.get_config(paths)
  1983.         if not config:
  1984.             return
  1985.         printer = PanelPrinter.get(self.window.id())
  1986.         printer.show()
  1987.         path = self.get_path(paths)
  1988.         if os.path.isdir(path):
  1989.             path = canonicalize(path, 'local')
  1990.         if os.name != 'nt':
  1991.             path = unicodedata.normalize('NFC', path)
  1992.         if config.get('sync_skip_deletes'):
  1993.             ignore_delete = True
  1994.         debug_print('SFTP: Starting Sync Up Command Thread', 2)
  1995.         SyncUpThread(self.window, config, path, self.save_files, ignore_delete, on_complete).start()
  1996.  
  1997.     def is_visible(self, paths=None):
  1998.         if paths is None:
  1999.             active_view = self.window.active_view()
  2000.             if active_view and active_view.settings().get('is_remote'):
  2001.                 return False
  2002.         path = self.get_path(paths)
  2003.         if not path:
  2004.             return False
  2005.         else:
  2006.             return self.has_config(path)
  2007.  
  2008.  
  2009. class SyncUpThread(HookedThread, SyncThread):
  2010.  
  2011.     def __init__(self, window, config, path, save_files, ignore_delete, on_complete):
  2012.         self.config = config
  2013.         self.printer = PanelPrinter.get(window.id())
  2014.         self.window = window
  2015.         self.window_id = window.id()
  2016.         self.path = path
  2017.         self.save_files = save_files
  2018.         self.ignore_delete = ignore_delete
  2019.         self.on_complete = on_complete
  2020.         super(SyncUpThread, self).__init__()
  2021.  
  2022.     def run(self):
  2023.         last_thread = ThreadTracker.get_last_added(self.window_id)
  2024.         ThreadTracker.add(self)
  2025.         if last_thread is not None:
  2026.             last_thread.join()
  2027.         path_map = self.config['path_map']
  2028.         path = self.path
  2029.         remote_path = local_to_remote(path, path_map, self.config['remote_encoding'])
  2030.         list_operation = 'listr' if is_dir(path) else 'list'
  2031.         nonlocal_ = {'progress': None}
  2032.  
  2033.         def on_connect():
  2034.             nonlocal_['progress'] = ProgressThread(self.printer, '\nDetermining operations to sync local path "%s" up to remote path "%s"' % (path, remote_path))
  2035.  
  2036.         def on_fail(e):
  2037.             if nonlocal_['progress']:
  2038.                 nonlocal_['progress'].stop('failure (%s)' % str(e))
  2039.                 nonlocal_['progress'].join()
  2040.  
  2041.         dir = path if is_dir(path) else dirname(path)
  2042.         remote_dir = local_to_remote(dir, path_map, self.config['remote_encoding'])
  2043.         try:
  2044.             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)
  2045.             list_dir_thread.start()
  2046.             list_dir_thread.join()
  2047.         except UnicodeDecodeError as e:
  2048.             if nonlocal_['progress']:
  2049.                 nonlocal_['progress'].stop('failure (Encoding error)')
  2050.                 nonlocal_['progress'].join()
  2051.             encoding_error(e)
  2052.             return
  2053.  
  2054.         if list_dir_thread.failed and list_dir_thread.result in (u'File not found',
  2055.                                                                  u'Folder not found'):
  2056.             remote_paths = []
  2057.         else:
  2058.             if list_dir_thread.failed:
  2059.                 return
  2060.             remote_paths = list_dir_thread.result
  2061.         list_dir_thread = SftpThread(self.window_id, self.config, dir, 'l' + list_operation, should_join=False, hide=False)
  2062.         list_dir_thread.start()
  2063.         list_dir_thread.join()
  2064.         if list_dir_thread.failed and list_dir_thread.result in (u'File not found',
  2065.                                                                  u'Folder not found'):
  2066.             local_paths = []
  2067.         else:
  2068.             if list_dir_thread.failed:
  2069.                 return
  2070.             local_paths = list_dir_thread.result
  2071.         if not is_dir(path):
  2072.             local_paths = [[os.path.join(dir, _path[0]), _path[1]] for _path in local_paths if os.path.join(dir, _path[0]) == path]
  2073.             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]
  2074.         local_dict = dict(local_paths)
  2075.         remote_dict = dict(remote_paths)
  2076.         debug_print('SFTP: Sync Up Local Files\n    %s' % repr(local_paths), 2)
  2077.         debug_print('SFTP: Sync Up Remote Files\n    %s' % repr(remote_paths), 2)
  2078.         to_rm = set([path_[0] for path_ in remote_paths])
  2079.         to_put = []
  2080.         to_put_new = []
  2081.         to_put_overwrite = []
  2082.         for local_path, local_time in local_paths:
  2083.             remote_path = local_to_remote(local_path, path_map)
  2084.             remote_time = remote_dict.get(remote_path)
  2085.             if remote_time is None:
  2086.                 to_put_new.append(local_path)
  2087.                 to_put.append(local_path)
  2088.             else:
  2089.                 if remote_time <= local_time and not is_dir(local_path):
  2090.                     to_put_overwrite.append(local_path)
  2091.                     to_put.append(local_path)
  2092.             to_rm = to_rm - set([remote_path])
  2093.  
  2094.         to_rm = list(to_rm)
  2095.         to_rm = sorted(to_rm, key=lambda s: s.lower())
  2096.         to_put, num_to_put, num_to_put_ignored = ignore_paths(to_put, self.config)
  2097.         to_put_new, num_to_put, num_to_put_ignored = ignore_paths(to_put_new, self.config)
  2098.         to_put_overwrite, num_to_put, num_to_put_ignored = ignore_paths(to_put_overwrite, self.config)
  2099.         to_rm, num_to_rm, num_to_rm_ignored = ignore_rm_paths(to_rm, self.config, 'remote')
  2100.         if self.ignore_delete:
  2101.             to_rm = []
  2102.         operations = []
  2103.         for local_path in to_put:
  2104.             local_time = self.make_time(local_dict.get(local_path))
  2105.             remote_path = local_to_remote(local_path, path_map)
  2106.             remote_time = self.make_time(remote_dict.get(remote_path))
  2107.             if is_dir(remote_path):
  2108.                 operation = 'Create remote "%s"' % self.strip(remote_path, remote_dir, 'remote')
  2109.             else:
  2110.                 if remote_time == 'None':
  2111.                     operation = 'Upload local "%s" to remote "%s"' % (
  2112.                      self.strip(local_path, dir, 'local'),
  2113.                      self.strip(remote_path, remote_dir, 'remote'))
  2114.                 else:
  2115.                     diff = time_diff(local_dict.get(local_path), remote_dict.get(remote_path))
  2116.                     if diff == 'same age' and not self.config['sync_same_age']:
  2117.                         to_put_overwrite.remove(local_path)
  2118.                         continue
  2119.                     operation = 'Upload local "%s" (%s) over remote "%s" [%s vs. %s]' % (
  2120.                      self.strip(local_path, dir, 'local'),
  2121.                      diff, self.strip(remote_path, remote_dir, 'remote'),
  2122.                      local_time, remote_time)
  2123.                 operations.append(operation)
  2124.  
  2125.         operations += ['Delete remote path "%s"' % self.strip(path_, remote_dir, 'remote') for path_ in to_rm]
  2126.         if operations:
  2127.             if nonlocal_['progress']:
  2128.                 nonlocal_['progress'].stop('success')
  2129.                 nonlocal_['progress'].join()
  2130.  
  2131.             def handle_yes():
  2132.                 failed = False
  2133.                 if to_put_new:
  2134.                     put_thread = SftpThread(self.window_id, self.config, to_put_new, 'put', should_join=False)
  2135.                     put_thread.start()
  2136.                     put_thread.join()
  2137.                     failed = failed or put_thread.failed
  2138.                 if to_put_overwrite:
  2139.                     put_thread = SftpThread(self.window_id, self.config, to_put_overwrite, 'put', should_join=False)
  2140.                     put_thread.start()
  2141.                     put_thread.join()
  2142.                     failed = failed or put_thread.failed
  2143.                 if to_rm:
  2144.                     to_rm.reverse()
  2145.                     rm_thread = SftpThread(self.window_id, self.config, to_rm, 'rm', should_join=False)
  2146.                     rm_thread.start()
  2147.                     rm_thread.join()
  2148.                     failed = failed or rm_thread.failed
  2149.                 if not failed and self.on_complete:
  2150.                     self.on_complete()
  2151.                 if not failed:
  2152.                     sublime.set_timeout(lambda : self.printer.hide(), 1)
  2153.  
  2154.             if self.config.get('confirm_sync'):
  2155.  
  2156.                 def handle_no():
  2157.                     sublime.set_timeout(lambda : self.printer.hide(), 1)
  2158.  
  2159.                 self.confirm(operations, handle_yes, handle_no, should_join=True)
  2160.             else:
  2161.                 handle_yes()
  2162.         else:
  2163.             if nonlocal_['progress']:
  2164.                 nonlocal_['progress'].stop('success (No operations to perform)')
  2165.                 nonlocal_['progress'].join()
  2166.             sublime.set_timeout(lambda : self.printer.hide(), 1)
  2167.         return
  2168.  
  2169.  
  2170. class SftpSyncDownCommand(sublime_plugin.WindowCommand, SftpCommand):
  2171.  
  2172.     def run(self, paths=None, ignore_delete=False, on_complete=None, reset_lcd=None, synchronous=False):
  2173.         config = self.get_config(paths)
  2174.         if not config:
  2175.             return
  2176.         printer = PanelPrinter.get(self.window.id())
  2177.         printer.show()
  2178.         path = self.get_path(paths)
  2179.         if os.path.isdir(path):
  2180.             path = canonicalize(path, 'local')
  2181.         if os.name != 'nt':
  2182.             path = unicodedata.normalize('NFC', path)
  2183.         if config.get('sync_skip_deletes'):
  2184.             ignore_delete = True
  2185.         debug_print('SFTP: Starting Sync Down Command Thread', 2)
  2186.         SyncDownThread(self.window, config, path, self.save_files, ignore_delete, on_complete, reset_lcd, synchronous).start()
  2187.  
  2188.     def is_visible(self, paths=None):
  2189.         if paths is None:
  2190.             active_view = self.window.active_view()
  2191.             if active_view and active_view.settings().get('is_remote'):
  2192.                 return False
  2193.         path = self.get_path(paths)
  2194.         if not path:
  2195.             return False
  2196.         else:
  2197.             return self.has_config(path)
  2198.  
  2199.  
  2200. class SyncDownThread(HookedThread, SyncThread):
  2201.  
  2202.     def __init__(self, window, config, path, save_files, ignore_delete, on_complete, reset_lcd, synchronous):
  2203.         self.config = config
  2204.         self.printer = PanelPrinter.get(window.id())
  2205.         self.window = window
  2206.         self.window_id = window.id()
  2207.         self.path = path
  2208.         self.save_files = save_files
  2209.         self.ignore_delete = ignore_delete
  2210.         self.on_complete = on_complete
  2211.         self.reset_lcd = reset_lcd
  2212.         self.synchronous = synchronous
  2213.         super(SyncDownThread, self).__init__()
  2214.  
  2215.     def run(self):
  2216.         last_thread = ThreadTracker.get_last_added(self.window_id)
  2217.         ThreadTracker.add(self)
  2218.         if last_thread is not None:
  2219.             last_thread.join()
  2220.         path_map = self.config['path_map']
  2221.         path = self.path
  2222.         remote_path = local_to_remote(path, path_map, self.config['remote_encoding'])
  2223.         list_operation = 'listr' if is_dir(path) else 'list'
  2224.         nonlocal_ = {'progress': None}
  2225.  
  2226.         def on_connect():
  2227.             nonlocal_['progress'] = ProgressThread(self.printer, '\nDetermining operations to sync remote path "%s" down to local path "%s"' % (remote_path, path))
  2228.  
  2229.         def on_fail(e):
  2230.             if nonlocal_['progress']:
  2231.                 nonlocal_['progress'].stop('failure (%s)' % str(e))
  2232.                 nonlocal_['progress'].join()
  2233.  
  2234.         dir = path if is_dir(path) else canonicalize(dirname(path), 'local')
  2235.         remote_dir = local_to_remote(dir, path_map, self.config['remote_encoding'])
  2236.         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)
  2237.         list_dir_thread.start()
  2238.         list_dir_thread.join()
  2239.         if list_dir_thread.failed and list_dir_thread.result in (u'File not found',
  2240.                                                                  u'Folder not found'):
  2241.             remote_paths = []
  2242.         else:
  2243.             if list_dir_thread.failed:
  2244.                 return
  2245.             remote_paths = list_dir_thread.result
  2246.         list_dir_thread = SftpThread(self.window_id, self.config, dir, 'l' + list_operation, should_join=False, hide=False)
  2247.         list_dir_thread.start()
  2248.         list_dir_thread.join()
  2249.         if list_dir_thread.failed and list_dir_thread.result in (u'File not found',
  2250.                                                                  u'Folder not found'):
  2251.             local_paths = []
  2252.         else:
  2253.             if list_dir_thread.failed:
  2254.                 return
  2255.             local_paths = list_dir_thread.result
  2256.         if not is_dir(path):
  2257.             local_paths = [[os.path.join(dir, _path[0]), _path[1]] for _path in local_paths if os.path.join(dir, _path[0]) == path]
  2258.             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]
  2259.         local_dict = dict(local_paths)
  2260.         remote_dict = dict(remote_paths)
  2261.         debug_print('SFTP: Sync Down Local Files\n    %s' % repr(local_paths), 2)
  2262.         debug_print('SFTP: Sync Down Remote Files\n    %s' % repr(remote_paths), 2)
  2263.         to_rm = set([path_[0] for path_ in local_paths])
  2264.         to_get = []
  2265.         to_get_new = []
  2266.         to_get_overwrite = []
  2267.         for remote_path, remote_time in remote_paths:
  2268.             local_path = remote_to_local(remote_path, path_map)
  2269.             local_time = local_dict.get(local_path)
  2270.             if local_time is None:
  2271.                 to_get_new.append(remote_path)
  2272.                 to_get.append(remote_path)
  2273.             else:
  2274.                 if local_time <= remote_time and not is_dir(local_path):
  2275.                     to_get_overwrite.append(remote_path)
  2276.                     to_get.append(remote_path)
  2277.             to_rm = to_rm - set([local_path])
  2278.  
  2279.         to_rm = list(to_rm)
  2280.         to_rm = sorted(to_rm, key=lambda s: s.lower())
  2281.         to_get, num_to_get, num_to_get_ignored = ignore_paths(to_get, self.config)
  2282.         to_get_new, num_to_get, num_to_get_ignored = ignore_paths(to_get_new, self.config)
  2283.         to_get_overwrite, num_to_get, num_to_get_ignored = ignore_paths(to_get_overwrite, self.config)
  2284.         to_rm, num_to_rm, num_to_rm_ignored = ignore_rm_paths(to_rm, self.config, 'local')
  2285.         if self.ignore_delete:
  2286.             to_rm = []
  2287.         operations = []
  2288.         for remote_path in to_get:
  2289.             remote_time = self.make_time(remote_dict.get(remote_path))
  2290.             local_path = remote_to_local(remote_path, path_map)
  2291.             local_time = self.make_time(local_dict.get(local_path))
  2292.             if is_dir(local_path):
  2293.                 operation = 'Create local "%s"' % self.strip(local_path, dir, 'local')
  2294.             else:
  2295.                 if local_time == 'None':
  2296.                     operation = 'Download remote "%s" to "%s"' % (
  2297.                      self.strip(remote_path, remote_dir, 'remote'),
  2298.                      self.strip(local_path, dir, 'local'))
  2299.                 else:
  2300.                     diff = time_diff(remote_dict.get(remote_path), local_dict.get(local_path))
  2301.                     if diff == 'same age' and not self.config['sync_same_age']:
  2302.                         to_get_overwrite.remove(remote_path)
  2303.                         continue
  2304.                     operation = 'Download remote "%s" (%s) over local "%s" [%s vs. %s]' % (
  2305.                      self.strip(remote_path, remote_dir, 'remote'),
  2306.                      diff, self.strip(local_path, dir, 'local'),
  2307.                      remote_time, local_time)
  2308.                 operations.append(operation)
  2309.  
  2310.         operations += ['Delete local "%s"' % self.strip(path_, dir, 'local') for path_ in to_rm]
  2311.         if operations:
  2312.             if nonlocal_['progress']:
  2313.                 nonlocal_['progress'].stop('success')
  2314.                 nonlocal_['progress'].join()
  2315.             nonlocal_['done'] = False
  2316.  
  2317.             def handle_yes():
  2318.                 failed = False
  2319.                 if to_get_new:
  2320.                     get_thread = SftpThread(self.window_id, self.config, to_get_new, 'get', should_join=False, reset_lcd=self.reset_lcd)
  2321.                     get_thread.start()
  2322.                     get_thread.join()
  2323.                     failed = failed or get_thread.failed
  2324.                 if to_get_overwrite:
  2325.                     get_thread = SftpThread(self.window_id, self.config, to_get_overwrite, 'get', should_join=False, reset_lcd=self.reset_lcd)
  2326.                     get_thread.start()
  2327.                     get_thread.join()
  2328.                     failed = failed or get_thread.failed
  2329.                 if to_rm:
  2330.                     to_rm.reverse()
  2331.                     rm_thread = SftpThread(self.window_id, self.config, to_rm, 'lrm', should_join=False)
  2332.                     rm_thread.start()
  2333.                     rm_thread.join()
  2334.                     failed = failed or rm_thread.failed
  2335.                 if not failed and self.on_complete:
  2336.                     if self.on_complete == 'open_refresh':
  2337.  
  2338.                         def do_refresh():
  2339.                             if not to_get:
  2340.                                 return
  2341.                             local_path = remote_to_local(to_get[0], path_map)
  2342.                             self.window.run_command('open_file', {'file': fix_windows_path(local_path)})
  2343.                             self.window.active_view().run_command('revert')
  2344.  
  2345.                         sublime.set_timeout(do_refresh, 1)
  2346.                     else:
  2347.                         self.on_complete()
  2348.                 if not failed:
  2349.                     sublime.set_timeout(lambda : self.printer.hide(), 1)
  2350.                 nonlocal_['done'] = True
  2351.  
  2352.             if self.config.get('confirm_sync'):
  2353.  
  2354.                 def handle_no():
  2355.                     sublime.set_timeout(lambda : self.printer.hide(), 1)
  2356.                     nonlocal_['done'] = True
  2357.  
  2358.                 self.confirm(operations, handle_yes, handle_no, should_join=not self.synchronous)
  2359.                 if self.synchronous:
  2360.                     while True:
  2361.                         if nonlocal_['done']:
  2362.                             break
  2363.                         time.sleep(0.01)
  2364.  
  2365.             else:
  2366.                 handle_yes()
  2367.         else:
  2368.             if nonlocal_['progress']:
  2369.                 nonlocal_['progress'].stop('success (No operations to perform)')
  2370.                 nonlocal_['progress'].join()
  2371.             sublime.set_timeout(lambda : self.printer.hide(), 1)
  2372.         return
  2373.  
  2374.  
  2375. class SftpSyncBothCommand(sublime_plugin.WindowCommand, SftpCommand):
  2376.  
  2377.     def run(self, paths=None, on_complete=None):
  2378.         config = self.get_config(paths)
  2379.         if not config:
  2380.             return
  2381.         printer = PanelPrinter.get(self.window.id())
  2382.         printer.show()
  2383.         path = self.get_path(paths)
  2384.         if os.path.isdir(path):
  2385.             path = canonicalize(path, 'local')
  2386.         if os.name != 'nt':
  2387.             path = unicodedata.normalize('NFC', path)
  2388.         debug_print('SFTP: Starting Sync Both Command Thread', 2)
  2389.         SyncBothThread(self.window, config, path, self.save_files, on_complete).start()
  2390.  
  2391.     def is_visible(self, paths=None):
  2392.         if paths is None:
  2393.             active_view = self.window.active_view()
  2394.             if active_view and active_view.settings().get('is_remote'):
  2395.                 return False
  2396.         path = self.get_path(paths)
  2397.         if not path:
  2398.             return False
  2399.         else:
  2400.             return self.has_config(path)
  2401.  
  2402.  
  2403. class SyncBothThread(HookedThread, SyncThread):
  2404.  
  2405.     def __init__(self, window, config, path, save_files, on_complete):
  2406.         self.config = config
  2407.         self.printer = PanelPrinter.get(window.id())
  2408.         self.window = window
  2409.         self.window_id = window.id()
  2410.         self.path = path
  2411.         self.save_files = save_files
  2412.         self.on_complete = on_complete
  2413.         super(SyncBothThread, self).__init__()
  2414.  
  2415.     def run(self):
  2416.         last_thread = ThreadTracker.get_last_added(self.window_id)
  2417.         ThreadTracker.add(self)
  2418.         if last_thread is not None:
  2419.             last_thread.join()
  2420.         path_map = self.config['path_map']
  2421.         path = self.path
  2422.         remote_path = local_to_remote(path, path_map, self.config['remote_encoding'])
  2423.         nonlocal_ = {'progress': None}
  2424.  
  2425.         def on_connect():
  2426.             nonlocal_['progress'] = ProgressThread(self.printer, '\nDetermining operations to sync local path "%s" both directions with remote path "%s"' % (path, remote_path))
  2427.  
  2428.         def on_fail(e):
  2429.             if nonlocal_['progress']:
  2430.                 nonlocal_['progress'].stop('failure (%s)' % str(e))
  2431.                 nonlocal_['progress'].join()
  2432.  
  2433.         dir = path if is_dir(path) else dirname(path)
  2434.         remote_dir = local_to_remote(dir, path_map, self.config['remote_encoding'])
  2435.         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)
  2436.         list_dir_thread.start()
  2437.         list_dir_thread.join()
  2438.         if list_dir_thread.failed and list_dir_thread.result in (u'File not found',
  2439.                                                                  u'Folder not found'):
  2440.             remote_paths = []
  2441.         else:
  2442.             if list_dir_thread.failed:
  2443.                 return
  2444.             remote_paths = list_dir_thread.result
  2445.         list_dir_thread = SftpThread(self.window_id, self.config, dir, 'llistr', should_join=False, hide=False)
  2446.         list_dir_thread.start()
  2447.         list_dir_thread.join()
  2448.         if list_dir_thread.failed and list_dir_thread.result in (u'File not found',
  2449.                                                                  u'Folder not found'):
  2450.             local_paths = []
  2451.         else:
  2452.             if list_dir_thread.failed:
  2453.                 return
  2454.             local_paths = list_dir_thread.result
  2455.         if not is_dir(path):
  2456.             local_paths = [_path for _path in local_paths if _path[0] == path]
  2457.             remote_paths = [_path for _path in remote_paths if _path[0] == remote_path]
  2458.         local_dict = dict(local_paths)
  2459.         remote_dict = dict(remote_paths)
  2460.         debug_print('SFTP: Sync Both Local Files\n    %s' % repr(local_paths), 2)
  2461.         debug_print('SFTP: Sync Both Remote Files\n    %s' % repr(remote_paths), 2)
  2462.         to_put = []
  2463.         to_put_new = []
  2464.         to_put_overwrite = []
  2465.         to_get = []
  2466.         to_get_new = []
  2467.         to_get_overwrite = []
  2468.         for local_path, local_time in local_paths:
  2469.             remote_path = local_to_remote(local_path, path_map)
  2470.             remote_time = remote_dict.get(remote_path)
  2471.             if remote_time is None:
  2472.                 to_put_new.append(local_path)
  2473.                 to_put.append(local_path)
  2474.             elif remote_time <= local_time and not is_dir(local_path):
  2475.                 to_put_overwrite.append(local_path)
  2476.                 to_put.append(local_path)
  2477.                 continue
  2478.  
  2479.         for remote_path, remote_time in remote_paths:
  2480.             local_path = remote_to_local(remote_path, path_map)
  2481.             local_time = local_dict.get(local_path)
  2482.             if local_time is None:
  2483.                 to_get_new.append(remote_path)
  2484.                 to_get.append(remote_path)
  2485.             elif local_time < remote_time and not is_dir(remote_path):
  2486.                 to_get_overwrite.append(remote_path)
  2487.                 to_get.append(remote_path)
  2488.                 continue
  2489.  
  2490.         to_put, num_to_put, num_to_put_ignored = ignore_paths(to_put, self.config)
  2491.         to_put_new, num_to_put, num_to_put_ignored = ignore_paths(to_put_new, self.config)
  2492.         to_put_overwrite, num_to_put, num_to_put_ignored = ignore_paths(to_put_overwrite, self.config)
  2493.         to_get, num_to_get, num_to_get_ignored = ignore_paths(to_get, self.config)
  2494.         to_get_new, num_to_get, num_to_get_ignored = ignore_paths(to_get_new, self.config)
  2495.         to_get_overwrite, num_to_get, num_to_get_ignored = ignore_paths(to_get_overwrite, self.config)
  2496.         operations = []
  2497.         for local_path in to_put:
  2498.             local_time = self.make_time(local_dict.get(local_path))
  2499.             remote_path = local_to_remote(local_path, path_map)
  2500.             remote_time = self.make_time(remote_dict.get(remote_path))
  2501.             if is_dir(remote_path):
  2502.                 operation = 'Create remote "%s"' % self.strip(remote_path, remote_dir, 'remote')
  2503.             else:
  2504.                 if remote_time == 'None':
  2505.                     operation = 'Upload local "%s" to remote "%s"' % (
  2506.                      self.strip(local_path, dir, 'local'),
  2507.                      self.strip(remote_path, remote_dir, 'remote'))
  2508.                 else:
  2509.                     diff = time_diff(local_dict.get(local_path), remote_dict.get(remote_path))
  2510.                     if diff == 'same age' and not self.config['sync_same_age']:
  2511.                         to_put_overwrite.remove(local_path)
  2512.                         continue
  2513.                     operation = 'Upload local "%s" (%s) over remote "%s" [%s vs. %s]' % (
  2514.                      self.strip(local_path, dir, 'local'),
  2515.                      diff, self.strip(remote_path, remote_dir, 'remote'),
  2516.                      local_time, remote_time)
  2517.                 operations.append(operation)
  2518.  
  2519.         for remote_path in to_get:
  2520.             remote_time = self.make_time(remote_dict.get(remote_path))
  2521.             local_path = remote_to_local(remote_path, path_map)
  2522.             local_time = self.make_time(local_dict.get(local_path))
  2523.             if is_dir(local_path):
  2524.                 operation = 'Create local "%s"' % self.strip(local_path, dir, 'local')
  2525.             else:
  2526.                 if local_time == 'None':
  2527.                     operation = 'Download remote "%s" to "%s"' % (
  2528.                      self.strip(remote_path, remote_dir, 'remote'),
  2529.                      self.strip(local_path, dir, 'local'))
  2530.                 else:
  2531.                     diff = time_diff(remote_dict.get(remote_path), local_dict.get(local_path))
  2532.                     if diff == 'same age' and not self.config['sync_same_age']:
  2533.                         to_get_overwrite.remove(remote_path)
  2534.                         continue
  2535.                     operation = 'Download remote "%s" (%s) over local "%s" [%s vs. %s]' % (
  2536.                      self.strip(remote_path, remote_dir, 'remote'),
  2537.                      diff, self.strip(local_path, dir, 'local'),
  2538.                      remote_time, local_time)
  2539.                 operations.append(operation)
  2540.  
  2541.         if operations:
  2542.             if nonlocal_['progress']:
  2543.                 nonlocal_['progress'].stop('success')
  2544.                 nonlocal_['progress'].join()
  2545.  
  2546.             def handle_yes():
  2547.                 failed = False
  2548.                 if to_put_new:
  2549.                     put_thread = SftpThread(self.window_id, self.config, to_put_new, 'put', should_join=False)
  2550.                     put_thread.start()
  2551.                     put_thread.join()
  2552.                     failed = failed or put_thread.failed
  2553.                 if to_put_overwrite:
  2554.                     put_thread = SftpThread(self.window_id, self.config, to_put_overwrite, 'put', should_join=False)
  2555.                     put_thread.start()
  2556.                     put_thread.join()
  2557.                     failed = failed or put_thread.failed
  2558.                 if to_get_new:
  2559.                     get_thread = SftpThread(self.window_id, self.config, to_get_new, 'get', should_join=False)
  2560.                     get_thread.start()
  2561.                     get_thread.join()
  2562.                     failed = failed or get_thread.failed
  2563.                 if to_get_overwrite:
  2564.                     get_thread = SftpThread(self.window_id, self.config, to_get_overwrite, 'get', should_join=False)
  2565.                     get_thread.start()
  2566.                     get_thread.join()
  2567.                     failed = failed or get_thread.failed
  2568.                 if not failed and self.on_complete:
  2569.                     self.on_complete()
  2570.                 if not failed:
  2571.                     sublime.set_timeout(lambda : self.printer.hide(), 1)
  2572.  
  2573.             if self.config.get('confirm_sync'):
  2574.  
  2575.                 def handle_no():
  2576.                     sublime.set_timeout(lambda : self.printer.hide(), 1)
  2577.  
  2578.                 self.confirm(operations, handle_yes, handle_no, should_join=True)
  2579.             else:
  2580.                 handle_yes()
  2581.         else:
  2582.             if nonlocal_['progress']:
  2583.                 nonlocal_['progress'].stop('success (No operations to perform)')
  2584.                 nonlocal_['progress'].join()
  2585.             sublime.set_timeout(lambda : self.printer.hide(), 1)
  2586.         return
  2587.  
  2588.  
  2589. class SftpDownloadFolderCommand(sublime_plugin.WindowCommand, SftpCommand):
  2590.  
  2591.     def run(self, paths=None):
  2592.         config = self.get_config(paths)
  2593.         if not config:
  2594.             return
  2595.         self.printer = PanelPrinter.get(self.window.id())
  2596.         dir = self.get_path(paths)
  2597.         if not os.path.isdir(dir):
  2598.             dir = dirname(dir)
  2599.         self.remote_dir = local_to_remote(dir, config['path_map'])
  2600.         self.config = config
  2601.         if not config['confirm_downloads']:
  2602.             self.do_download()
  2603.             return
  2604.         debug_print('SFTP: Starting Confirm Download Folder Thread', 2)
  2605.         basename = os.path.basename(dir)
  2606.         if os.name != 'nt':
  2607.             basename = unicodedata.normalize('NFC', basename)
  2608.         choices = [['Yes', 'Download the folder %s' % basename],
  2609.          [
  2610.           'No', 'Do not download the folder %s' % basename]]
  2611.  
  2612.         def on_choose(index):
  2613.             if index == -1 or index == 1:
  2614.                 return
  2615.             self.do_download()
  2616.  
  2617.         show_qp(self.window, choices, on_choose)
  2618.  
  2619.     def do_download(self):
  2620.         self.printer.show()
  2621.         debug_print('SFTP: Starting Download Folder Command Thread', 2)
  2622.         DownloadFolderThread(self.window.id(), self.config, self.remote_dir).start()
  2623.  
  2624.     def is_visible(self, paths=None):
  2625.         if paths is None:
  2626.             active_view = self.window.active_view()
  2627.             if active_view and active_view.settings().get('is_remote'):
  2628.                 return False
  2629.         path = self.get_path(paths)
  2630.         if not path:
  2631.             return False
  2632.         else:
  2633.             return self.has_config(path)
  2634.  
  2635.  
  2636. class DownloadFolderThread(HookedThread):
  2637.  
  2638.     def __init__(self, window_id, config, remote_dir):
  2639.         self.config = config
  2640.         self.printer = PanelPrinter.get(window_id)
  2641.         self.window_id = window_id
  2642.         self.remote_dir = remote_dir
  2643.         self.error = False
  2644.         super(DownloadFolderThread, self).__init__()
  2645.  
  2646.     def run(self):
  2647.         last_thread = ThreadTracker.get_last_added(self.window_id)
  2648.         ThreadTracker.add(self)
  2649.         if last_thread is not None:
  2650.             last_thread.join()
  2651.         nonlocal_ = {'progress': None}
  2652.  
  2653.         def on_connect():
  2654.             nonlocal_['progress'] = ProgressThread(self.printer, '\nDownloading folder "%s"' % self.remote_dir)
  2655.  
  2656.         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)
  2657.         list_dir_thread.start()
  2658.         list_dir_thread.join()
  2659.         remote_paths = list_dir_thread.result
  2660.         if remote_paths is not None:
  2661.             remote_paths = [path[0] for path in remote_paths]
  2662.             remote_paths, to_download, ignored = ignore_paths(remote_paths, self.config)
  2663.             message = ' %d %s to download' % (to_download, 'files' if to_download != 1 else 'file')
  2664.             if ignored:
  2665.                 message += ', %d ignored' % ignored
  2666.         else:
  2667.             message = 'failure (Error)'
  2668.         if nonlocal_['progress']:
  2669.             nonlocal_['progress'].stop(message)
  2670.             nonlocal_['progress'].join()
  2671.         if remote_paths:
  2672.             debug_print('SFTP: Starting Download Folder Thread', 2)
  2673.             download_thread = SftpThread(self.window_id, self.config, remote_paths, 'get', should_join=False)
  2674.             download_thread.start()
  2675.             download_thread.join()
  2676.             self.error = download_thread.failed
  2677.         else:
  2678.             sublime.set_timeout(lambda : self.printer.hide(), 1)
  2679.         return
  2680.  
  2681.  
  2682. class SftpVcsChangedFilesCommand(sublime_plugin.WindowCommand, SftpCommand):
  2683.  
  2684.     def run(self):
  2685.         config = self.get_config()
  2686.         if not config:
  2687.             return
  2688.         else:
  2689.             printer = PanelPrinter.get(self.window.id())
  2690.             printer.show()
  2691.             vcs = None
  2692.             path = self.get_path()
  2693.             settings = sublime.load_settings('SFTP.sublime-settings')
  2694.             vcses = [
  2695.              (
  2696.               SVN, settings.get('svn_binary_path')),
  2697.              (
  2698.               Git, settings.get('git_binary_path')),
  2699.              (
  2700.               Hg, settings.get('hg_binary_path'))]
  2701.             for vcs_class, binary_path in vcses:
  2702.                 try:
  2703.                     vcs = vcs_class(path, binary_path)
  2704.                 except NotFoundError:
  2705.                     pass
  2706.  
  2707.             if vcs is None:
  2708.                 printer.write('\nLooking for changed files ... failure')
  2709.                 printer.error('The current file does not appear to be inside of an SVN, Git or Mercurial working copy')
  2710.                 return
  2711.             if config['save_before_upload']:
  2712.                 self.save_files()
  2713.             debug_print('SFTP: Starting VCS Command Thread', 2)
  2714.             VcsThread(self.window, config, vcs).start()
  2715.             return
  2716.  
  2717.     def is_visible(self, paths=None):
  2718.         path = self.get_path(paths)
  2719.         if not path or os.path.isdir(path):
  2720.             return False
  2721.         return self.has_config(path)
  2722.  
  2723.  
  2724. class VcsThread(HookedThread):
  2725.  
  2726.     def __init__(self, window, config, vcs):
  2727.         self.window_id = window.id()
  2728.         self.printer = PanelPrinter.get(window.id())
  2729.         self.config = config
  2730.         self.vcs = vcs
  2731.         super(VcsThread, self).__init__()
  2732.  
  2733.     def run(self):
  2734.         last_thread = ThreadTracker.get_last_added(self.window_id)
  2735.         ThreadTracker.add(self)
  2736.         if last_thread is not None:
  2737.             last_thread.join()
  2738.         progress = ProgressThread(self.printer, '\nLooking for changed files')
  2739.         try:
  2740.             files = self.vcs.list_changed_files()
  2741.             files = [file for file in files if re.match(re.escape(self.config['local_dir']), file) is not None]
  2742.         except NotFoundError as e:
  2743.             progress.stop('failure\n' + str(e))
  2744.             progress.join()
  2745.             self.printer.error(e)
  2746.             return
  2747.  
  2748.         to_upload = len(files)
  2749.         ignored = 0
  2750.         if 'ignore_regex' in self.config:
  2751.             files = [file for file in files if not re.search(self.config['ignore_regex'], file)]
  2752.             ignored = to_upload - len(files)
  2753.             to_upload = len(files)
  2754.         message = '%d %s to upload' % (to_upload, 'files' if to_upload != 1 else ' file')
  2755.         if ignored:
  2756.             message += ', %d ignored' % ignored
  2757.         progress.stop(message)
  2758.         progress.join()
  2759.         if files:
  2760.             debug_print('SFTP: Starting VCS Thread', 2)
  2761.             SftpThread(self.window_id, self.config, files, 'put').start()
  2762.         else:
  2763.             sublime.set_timeout(self.printer.hide, 10)
  2764.         return
  2765.  
  2766.  
  2767. class SftpCancelUploadCommand(sublime_plugin.WindowCommand, SftpCommand):
  2768.  
  2769.     def run(self):
  2770.         current_thread = ThreadTracker.get_current(self.window.id())
  2771.         if current_thread:
  2772.             current_thread.kill()
  2773.  
  2774.     def is_visible(self, paths=None):
  2775.         return ThreadTracker.get_current(self.window.id()) is not None
  2776.  
  2777.  
  2778. class SftpEditConfigCommand(sublime_plugin.WindowCommand, SftpCommand):
  2779.  
  2780.     def run(self, paths=None):
  2781.         path = self.get_path(paths)
  2782.         if not path:
  2783.             return
  2784.         if not os.path.isdir(path):
  2785.             path = os.path.dirname(path)
  2786.         file = find_config(path, True)
  2787.         file = fix_windows_path(file)
  2788.         self.window.run_command('open_file', {'file': file})
  2789.  
  2790.     def is_visible(self, paths=None):
  2791.         path = self.get_path(paths)
  2792.         if not path:
  2793.             return False
  2794.         if not os.path.isdir(path):
  2795.             path = os.path.dirname(path)
  2796.         config_file = find_config(path, True)
  2797.         return config_file is not False
  2798.  
  2799.     def is_enabled(self, paths=None):
  2800.         path = self.get_path(paths)
  2801.         if not path:
  2802.             return False
  2803.         if not os.path.isdir(path):
  2804.             path = os.path.dirname(path)
  2805.         config_file = find_config(path, True)
  2806.         return config_file is not False
  2807.  
  2808.  
  2809. class SftpCreateConfigCommand(sublime_plugin.WindowCommand, SftpCommand):
  2810.  
  2811.     def run(self, paths=None):
  2812.         path = self.get_path(paths)
  2813.         if not os.path.isdir(path):
  2814.             path = os.path.dirname(path)
  2815.         file = os.path.join(path, 'sftp-config.json')
  2816.         self.create_default_config(file)
  2817.  
  2818.         def open_file():
  2819.             self.window.run_command('open_file', {'file': fix_windows_path(file)})
  2820.  
  2821.             def run_snippet():
  2822.                 snippet = get_default_config()
  2823.                 view = self.window.active_view()
  2824.                 view.sel().add(sublime.Region(0, view.size()))
  2825.                 view.run_command('insert_snippet', {'contents': snippet})
  2826.  
  2827.             sublime.set_timeout(run_snippet, 350)
  2828.  
  2829.         sublime.set_timeout(open_file, 350)
  2830.  
  2831.     def is_visible(self, paths=None):
  2832.         path = self.get_path(paths)
  2833.         if not path:
  2834.             return False
  2835.         if not os.path.isdir(path):
  2836.             path = os.path.dirname(path)
  2837.         return find_config(path, True) is False
  2838.  
  2839.     def is_enabled(self, paths=None):
  2840.         path = self.get_path(paths)
  2841.         if not path:
  2842.             return False
  2843.         if not os.path.isdir(path):
  2844.             path = os.path.dirname(path)
  2845.         return find_config(path, True) is False
  2846.  
  2847.  
  2848. class SftpSwitchConfigCommand(sublime_plugin.WindowCommand, SftpCommand):
  2849.  
  2850.     def run(self, paths=None):
  2851.         path = self.get_path(paths)
  2852.         if not path:
  2853.             return
  2854.         config_file = find_config(path, True)
  2855.         if config_file is False:
  2856.             return
  2857.         self.config_dir = os.path.dirname(config_file)
  2858.  
  2859.         def add_desc(config):
  2860.             remote_type = config.get('type', 'sftp').upper()
  2861.             user = config.get('user')
  2862.             host = config.get('host')
  2863.             remote_path = config.get('remote_path')
  2864.             port = ':' + str(config.get('port')) if config.get('port') else ''
  2865.             config['desc'] = '%s %s@%s%s %s' % (
  2866.              remote_type, user, host, port, remote_path)
  2867.             return config
  2868.  
  2869.         alt_config_files = []
  2870.         alt_configs = {}
  2871.         for num in ['', '2', '3', '4', '5', '6', '7', '8', '9']:
  2872.             alt_config_file = config_file.replace('sftp-settings.json', 'sftp-config.json')[0:-5] + '-alt' + num + '.json'
  2873.             if os.path.exists(alt_config_file):
  2874.                 alt_config_files.append(alt_config_file)
  2875.                 alt_configs[alt_config_file] = add_desc(parse_config(alt_config_file))
  2876.                 continue
  2877.  
  2878.         config = add_desc(parse_config(config_file))
  2879.         self.choices = [
  2880.          [
  2881.           config['desc'], os.path.basename(config_file)]]
  2882.         for alt_config_file in alt_config_files:
  2883.             self.choices.append([alt_configs[alt_config_file]['desc'], os.path.basename(alt_config_file)])
  2884.  
  2885.         self.window_id = self.window.id()
  2886.         self.printer = PanelPrinter.get(self.window_id)
  2887.         show_qp(self.window, self.choices, self.on_done)
  2888.  
  2889.     def on_done(self, index):
  2890.         if index == -1:
  2891.             return
  2892.         if index == 0:
  2893.             return
  2894.         main_config_file = os.path.join(self.config_dir, self.choices[0][1])
  2895.         alt_config_file = os.path.join(self.config_dir, self.choices[index][1])
  2896.         original_config = ''
  2897.         with open(main_config_file, 'rb') as (f):
  2898.             original_config = f.read()
  2899.         new_config = ''
  2900.         with open(alt_config_file, 'rb') as (f):
  2901.             new_config = f.read()
  2902.         with open(main_config_file, 'wb') as (f):
  2903.             f.write(new_config)
  2904.         with open(alt_config_file, 'wb') as (f):
  2905.             f.write(original_config)
  2906.  
  2907.     def is_visible(self, paths=None):
  2908.         path = self.get_path(paths)
  2909.         if not path:
  2910.             return False
  2911.         config_file = find_config(path, True)
  2912.         if config_file is False:
  2913.             return False
  2914.         alt_config_file = config_file.replace('sftp-settings.json', 'sftp-config.json')[0:-5] + '-alt.json'
  2915.         if os.path.exists(alt_config_file):
  2916.             return True
  2917.         return False
  2918.  
  2919.  
  2920. class SftpCreateAltConfigCommand(sublime_plugin.WindowCommand, SftpCommand):
  2921.  
  2922.     def run(self, paths=None):
  2923.         path = self.get_path(paths)
  2924.         if not path:
  2925.             return
  2926.         else:
  2927.             config_file = find_config(path, True)
  2928.             if config_file is False:
  2929.                 return
  2930.             new_config_file = None
  2931.             for num in ['', '2', '3', '4', '5', '6', '7', '8', '9']:
  2932.                 alt_config_file = config_file.replace('sftp-settings.json', 'sftp-config.json')[0:-5] + '-alt' + num + '.json'
  2933.                 if not os.path.exists(alt_config_file):
  2934.                     new_config_file = alt_config_file
  2935.                     break
  2936.  
  2937.             if not new_config_file:
  2938.                 sublime.error_message('Sublime SFTP\n\nThere can not be more than 9 alternate configs')
  2939.                 return False
  2940.             original_config = ''
  2941.             with open(config_file, 'rb') as (f):
  2942.                 original_config = f.read()
  2943.             with open(new_config_file, 'wb') as (f):
  2944.                 f.write(original_config)
  2945.             self.window.run_command('open_file', {'file': fix_windows_path(new_config_file)})
  2946.             return
  2947.  
  2948.     def is_visible(self, paths=None):
  2949.         path = self.get_path(paths)
  2950.         if not path:
  2951.             return False
  2952.         config_file = find_config(path, True)
  2953.         if config_file is False:
  2954.             return False
  2955.         return True
  2956.  
  2957.  
  2958. class SftpCreateSubConfigCommand(sublime_plugin.WindowCommand, SftpCommand):
  2959.  
  2960.     def run(self, dirs):
  2961.         file = os.path.join(dirs[0], 'sftp-config.json')
  2962.         self.create_default_config(file)
  2963.         file = fix_windows_path(file)
  2964.         self.window.run_command('open_file', {'file': file})
  2965.  
  2966.     def is_visible(self, dirs):
  2967.         path = self.first_path(dirs)
  2968.         config_file = find_config(path, True)
  2969.         return config_file is not False and os.path.dirname(config_file) != path
  2970.  
  2971.     def is_enabled(self, dirs):
  2972.         path = self.first_path(dirs)
  2973.         config_file = find_config(path, True)
  2974.         return config_file is not False and os.path.dirname(config_file) != path
  2975. # okay decompiling /home/manager/.config/sublime-text-3/Packages/SFTP/sftp/commands.pyc
  2976. # uncompyle6 version 3.2.3
  2977. # Python bytecode 3.3 (3230)
  2978. # Decompiled from: Python 2.7.12 (default, Dec  4 2017, 14:50:18)
  2979. # [GCC 5.4.0 20160609]
  2980. # Embedded file name: /Users/wbond/Library/Application Support/Sublime Text 3/Packages/SFTP/sftp/config.py
  2981. # Compiled at: 2015-11-05 01:05:02
  2982. # Size of source mod 2**32: 15538 bytes
  2983. import sublime, os, sys, re, json, tempfile, time
  2984. if os.name != 'nt':
  2985.     import unicodedata
  2986. try:
  2987.     str_cls = unicode
  2988. except NameError:
  2989.     str_cls = str
  2990.  
  2991. from .errors import NotFoundError
  2992. from .debug import set_debug, set_debug_log_file
  2993. from .proc import find_binary
  2994. from .paths import canonicalize, dirname, fix_windows_path
  2995.  
  2996. def normalize_path(path):
  2997.     if os.name == 'nt':
  2998.         return path
  2999.     return unicodedata.normalize('NFC', path)
  3000.  
  3001.  
  3002. shown_regex_errors = {}
  3003.  
  3004. def get_default_config(use_defaults=False, remove_settings=[], force_settings=None):
  3005.     user_path = os.path.join(sublime.packages_path(), 'User', 'SFTP.default-config')
  3006.     if os.path.exists(user_path):
  3007.         with open(user_path, 'r') as (f):
  3008.             string = f.read()
  3009.     else:
  3010.         package_path = os.path.join(sublime.packages_path(), 'SFTP', 'SFTP.default-config')
  3011.         with open(package_path, 'r') as (f):
  3012.             string = f.read()
  3013.     if use_defaults:
  3014.         string = re.sub('\\$\\{\\d+:([^}]+|\\\\})+}', '\\1', string)
  3015.         string = string.replace('\\\\\\', '\\\\')
  3016.     else:
  3017.         if remove_settings or force_settings:
  3018.             for setting in remove_settings:
  3019.                 if setting == 'ignore_regexes':
  3020.                     regex = re.compile('\r?\n[^\n]*"' + setting + '"\\s*:\\s*\\[.*?\\]\\s*,[ \t]*', re.S)
  3021.                     string = re.sub(regex, '', string)
  3022.                 else:
  3023.                     string = re.sub('\r?\n[^\n]*"' + setting + '"\\s*:[^\n]*', '', string)
  3024.  
  3025.             for setting in force_settings:
  3026.                 if string.find('"' + setting + '"') == -1:
  3027.                     string = re.sub('("type"[^\n]+)\n', '\\1\n\t"' + setting + '": ${1:' + json.dumps(force_settings[setting]) + '},\n', string)
  3028.                 else:
  3029.                     string = re.sub('("' + setting + '"[^\n]+)\n', '"' + setting + '": ${1:' + json.dumps(force_settings[setting]) + '},\n', string)
  3030.  
  3031.             placeholder = 1
  3032.             for match in re.finditer('\\$\\{\\d+:([^}]+|\\\\})+}', string):
  3033.                 replacement = '${' + str(placeholder) + ':' + match.group(1).replace('\\', '\\\\') + '}'
  3034.                 string = string.replace(match.group(0), replacement)
  3035.                 placeholder += 1
  3036.  
  3037.     return string
  3038.  
  3039.  
  3040. def find_config(start, return_not_found=False):
  3041.     if start is None:
  3042.         if return_not_found:
  3043.             return False
  3044.         raise NotFoundError()
  3045.     last_dir = ''
  3046.     if os.path.isdir(start):
  3047.         orig_dir = start
  3048.     else:
  3049.         orig_dir = dirname(start)
  3050.     local_dir = orig_dir
  3051.     while local_dir != last_dir:
  3052.         config_file = os.path.join(local_dir, 'sftp-settings.json')
  3053.         if os.path.exists(config_file):
  3054.             return normalize_path(config_file)
  3055.         config_file = os.path.join(local_dir, 'sftp-config.json')
  3056.         if os.path.exists(config_file):
  3057.             return normalize_path(config_file)
  3058.         last_dir = local_dir
  3059.         if local_dir == '/':
  3060.             break
  3061.         local_dir = dirname(local_dir)
  3062.  
  3063.     if return_not_found:
  3064.         return False
  3065.     else:
  3066.         raise NotFoundError()
  3067.         return
  3068.  
  3069.  
  3070. def load_config(path):
  3071.     if path is None:
  3072.  
  3073.         def show_error_3():
  3074.             sublime.error_message('Sublime SFTP\n\nThe current file does not appear to be saved to disk and thus can not be uploaded')
  3075.  
  3076.         sublime.set_timeout(show_error_3, 1)
  3077.         return (None, None)
  3078.     else:
  3079.         try:
  3080.             config_file = find_config(path)
  3081.             config = parse_config(config_file)
  3082.             if 'remote_path' in config and len(config['remote_path']) and config['remote_path'][-1] != '/':
  3083.                 config['remote_path'] += '/'
  3084.         except ValueError as e:
  3085.             exception = str(e)
  3086.  
  3087.             def show_error():
  3088.                 sublime.error_message('Sublime SFTP\n\nError parsing sftp-config.json file:\n%s' % exception)
  3089.  
  3090.             sublime.set_timeout(show_error, 1)
  3091.             return (None, None)
  3092.         except NotFoundError:
  3093.  
  3094.             def show_error_2():
  3095.                 current_dir = dirname(path) if path and os.path.isfile(path) else path
  3096.                 sublime.error_message('Sublime SFTP\n\nA sftp-config.json file was not found in "%s" or any parent folders' % current_dir)
  3097.  
  3098.             sublime.set_timeout(show_error_2, 1)
  3099.             return (None, None)
  3100.  
  3101.         return (
  3102.          config, config_file)
  3103.  
  3104.  
  3105. def parse_config(path):
  3106.     with open(path, 'r') as (handle):
  3107.         regex = re.compile('^[ \t]*//.*\r?', re.M)
  3108.         json_string = handle.read()
  3109.         match = re.search(regex, json_string)
  3110.         while match is not None:
  3111.             s = match.start()
  3112.             e = match.end()
  3113.             json_string = json_string[:s] + ' ' * (e - s) + json_string[e:]
  3114.             match = re.search(regex, json_string)
  3115.  
  3116.         json_string = re.sub(',([ \r\n\t]+}\\s*)$', ' \\1', json_string)
  3117.         return json.loads(json_string)
  3118.     return
  3119.  
  3120.  
  3121. def fix_server_folder():
  3122.     old_path = os.path.join(sublime.packages_path(), 'User', 'sftp_remotes')
  3123.     if os.path.exists(old_path):
  3124.         new_path = os.path.join(sublime.packages_path(), 'User', 'sftp_servers')
  3125.         os.rename(old_path, new_path)
  3126.  
  3127.  
  3128. def get_server_config_folder():
  3129.     fix_server_folder()
  3130.     config_folder = os.path.join(sublime.packages_path(), 'User', 'sftp_servers')
  3131.     if not os.path.exists(config_folder):
  3132.         os.mkdir(config_folder)
  3133.     return normalize_path(config_folder)
  3134.  
  3135.  
  3136. def prepare_server_config(filename):
  3137.     config_dir = get_server_config_folder()
  3138.     path = os.path.join(config_dir, filename)
  3139.     try:
  3140.         config = parse_config(path)
  3141.         config['file_path'] = path
  3142.         config['name'] = filename
  3143.  
  3144.         def open_config(message):
  3145.             sublime.active_window().run_command('open_file', {'file': fix_windows_path(path)})
  3146.  
  3147.             def show_error():
  3148.                 sublime.error_message(message)
  3149.  
  3150.             sublime.set_timeout(show_error, 200)
  3151.  
  3152.         for setting in ['remote_path', 'user', 'host']:
  3153.             if setting not in config or not len(config[setting]):
  3154.                 message = 'Sublime SFTP\n\nThe "%s" setting is not specified in %s.' % (setting, path)
  3155.                 sublime.set_timeout(lambda : open_config(message), 10)
  3156.                 return
  3157.  
  3158.         remote_type = config.get('type', 'sftp').upper()
  3159.         user = config.get('user')
  3160.         host = config.get('host')
  3161.         port = ':' + str(config.get('port')) if config.get('port') else ''
  3162.         config['desc'] = '%s %s@%s%s' % (
  3163.          remote_type, user, host, port)
  3164.         config['upload_on_save'] = True
  3165.         config['save_before_upload'] = True
  3166.         return config
  3167.     except ValueError as e:
  3168.         exception = str(e)
  3169.  
  3170.         def show_error():
  3171.             sublime.error_message('Sublime SFTP\n\nError parsing server config file %s:\n%s' % (filename, exception))
  3172.  
  3173.         sublime.set_timeout(show_error, 1)
  3174.         return
  3175.  
  3176.     return
  3177.  
  3178.  
  3179. def setup_tmp_dir(config, tmp_dir=None):
  3180.     if tmp_dir is None:
  3181.         if 'name' in config:
  3182.             tmp_dir = os.path.join(tempfile.gettempdir(), 'sublime-sftp-browse-' + str(int(time.time())), config['name'])
  3183.         else:
  3184.             tmp_dir = os.path.join(tempfile.gettempdir(), 'sublime-sftp-browse-' + str(int(time.time())), 'mapped')
  3185.     if not os.path.exists(tmp_dir):
  3186.         os.makedirs(tmp_dir)
  3187.     tmp_dir = canonicalize(tmp_dir, 'local')
  3188.     config['upload_on_save'] = True
  3189.     config['save_before_upload'] = True
  3190.     config['confirm_sync'] = False
  3191.     remote_path_matches = re.match('(/|[a-zA-Z]:\\\\)(.*)$', config.get('remote_path', ''))
  3192.     if remote_path_matches:
  3193.         initial_tmp_dir = os.path.join(tmp_dir, remote_path_matches.group(2))
  3194.         if not os.path.exists(initial_tmp_dir):
  3195.             os.makedirs(initial_tmp_dir)
  3196.         config['initial_remote_path'] = config['remote_path']
  3197.         config['remote_path'] = remote_path_matches.group(1)
  3198.     config_path = os.path.join(tmp_dir, 'sftp-config.json')
  3199.     with open(config_path, 'w') as (handle):
  3200.         handle.write(json.dumps(config, sort_keys=True, indent=4))
  3201.     return tmp_dir
  3202.  
  3203.  
  3204. def build_config(config, local_dir, config_file, quiet=False, skip_settings=False):
  3205.     if not skip_settings:
  3206.         settings = sublime.load_settings('SFTP.sublime-settings')
  3207.         set_debug(settings.get('debug', False))
  3208.         set_debug_log_file(settings.get('debug_log_file', False))
  3209.     local_dir = canonicalize(local_dir, 'local')
  3210.  
  3211.     def open_config(message):
  3212.         sublime.active_window().run_command('open_file', {'file': fix_windows_path(config_file)})
  3213.  
  3214.         def show_error():
  3215.             sublime.error_message(message)
  3216.  
  3217.         sublime.set_timeout(show_error, 200)
  3218.  
  3219.     if os.name == 'nt':
  3220.         sftp_path = find_binary('psftp.exe')
  3221.     else:
  3222.         sftp_path = find_binary('sftp')
  3223.     for setting in ['remote_path', 'user', 'host']:
  3224.         if setting not in config or not len(config[setting]):
  3225.             if not quiet:
  3226.                 message = 'Sublime SFTP\n\nThe "%s" setting is not specified in %s.' % (setting, config_file)
  3227.                 sublime.set_timeout(lambda : open_config(message), 10)
  3228.             return
  3229.  
  3230.     if 'remote_path' in config and not re.match('/|[a-zA-Z]:[/\\\\]', config['remote_path']):
  3231.         if not quiet:
  3232.             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
  3233.             sublime.set_timeout(lambda : open_config(message), 10)
  3234.         return
  3235.     else:
  3236.         parsed_config = {'name': config.get('name'),
  3237.          'is_server': True if config.get('file_path') else False,
  3238.          'allow_config_upload': config.get('allow_config_upload', False),
  3239.          'type': config.get('type', 'sftp'),
  3240.          'binary': sftp_path,
  3241.          'user': config['user'],
  3242.          'host': config['host'],
  3243.          'path_map': {normalize_path(local_dir): config['remote_path']},  'local_dir': normalize_path(local_dir),
  3244.          'remote_dir': config['remote_path'],
  3245.          'initial_remote_dir': config.get('initial_remote_path', config['remote_path']),
  3246.          'timeout': int(config.get('connect_timeout', 30)),
  3247.          'keepalive': int(config.get('keepalive', 0)),
  3248.          'remote_locale': config.get('remote_locale', 'C'),
  3249.          'remote_encoding': config.get('remote_encoding', 'utf-8'),
  3250.          'upload_on_save': config.get('upload_on_save', False),
  3251.          'save_before_upload': config.get('save_before_upload', True),
  3252.          'confirm_downloads': config.get('confirm_downloads', False),
  3253.          'confirm_sync': config.get('confirm_sync', True),
  3254.          'sync_same_age': config.get('sync_same_age', True),
  3255.          'sync_down_on_open': config.get('sync_down_on_open', False),
  3256.          'sync_skip_deletes': config.get('sync_skip_deletes', False),
  3257.          'extra_list_connections': int(config.get('extra_list_connections', 0)),
  3258.          'confirm_overwrite_newer': config.get('confirm_overwrite_newer', False),
  3259.          'preserve_modification_times': config.get('preserve_modification_times', False)}
  3260.         types = [
  3261.          'ftp', 'sftp']
  3262.         if 'ssl' in sys.modules:
  3263.             types.append('ftps')
  3264.         if parsed_config['type'] not in types:
  3265.             if not quiet:
  3266.                 message = 'Sublime SFTP\n\nThe type specified, %s, is invalid. Must be one of: %s. Please fix the type setting in %s.' % (
  3267.                  parsed_config['type'], (', ').join(types), config_file)
  3268.                 if sublime.platform() == 'linux':
  3269.                     settings_path = os.path.join(sublime.packages_path(), 'User', 'SFTP.sublime-settings')
  3270.                     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
  3271.                 sublime.set_timeout(lambda : open_config(message), 10)
  3272.             return
  3273.         if not skip_settings:
  3274.             for element in ['email', 'product_key']:
  3275.                 value = settings.get(element)
  3276.                 if isinstance(value, str):
  3277.                     value = value.lower()
  3278.                 parsed_config[element] = value
  3279.  
  3280.         if parsed_config['type'] == 'sftp':
  3281.             default_port = '22'
  3282.         else:
  3283.             default_port = '21'
  3284.         parsed_config['port'] = str(config.get('port', default_port))
  3285.         keys_to_copy = set([
  3286.          'password',
  3287.          'ignore_regex',
  3288.          'file_permissions',
  3289.          'ignore_regexes',
  3290.          'dir_permissions',
  3291.          'ssh_key_file',
  3292.          'sftp_flags',
  3293.          'ftp_passive_mode',
  3294.          'ftp_passive_host_fix',
  3295.          'remote_time_offset_in_hours'])
  3296.         for key in keys_to_copy:
  3297.             if key in config:
  3298.                 parsed_config[key] = config[key]
  3299.                 continue
  3300.  
  3301.         if 'ssh_key_file' in parsed_config:
  3302.             if re.match('\\.\\.?[/\\\\]', parsed_config['ssh_key_file']) is not None:
  3303.                 tmp_path = os.path.join(parsed_config['local_dir'], parsed_config['ssh_key_file'])
  3304.                 parsed_config['ssh_key_file'] = os.path.realpath(tmp_path)
  3305.         if 'ignore_regexes' not in parsed_config and 'ignore_regex' not in parsed_config:
  3306.             parsed_config['ignore_regexes'] = ['\\\\.sublime-(project|workspace)',
  3307.              'sftp-config(-alt\\d?)?\\.json',
  3308.              'sftp-settings\\.json',
  3309.              '/venv/',
  3310.              '\\.svn/',
  3311.              '\\.hg/',
  3312.              '\\.git/',
  3313.              '\\.bzr',
  3314.              '_darcs',
  3315.              'CVS',
  3316.              '\\.DS_Store',
  3317.              'Thumbs\\.db',
  3318.              'desktop\\.ini']
  3319.         if 'ignore_regexes' in parsed_config and isinstance(parsed_config['ignore_regexes'], list):
  3320.             parsed_config['ignore_regex'] = '(' + ('|').join(parsed_config['ignore_regexes']) + ')'
  3321.         if 'ignore_regex' in parsed_config and parsed_config['ignore_regex']:
  3322.             try:
  3323.                 re.compile(parsed_config['ignore_regex'])
  3324.             except re.error as e:
  3325.                 local_dir = parsed_config['local_dir']
  3326.                 if local_dir not in shown_regex_errors or shown_regex_errors[local_dir] < time.time():
  3327.                     shown_regex_errors[local_dir] = time.time() + 10
  3328.                     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' % (
  3329.                      config_file, str_cls(e))
  3330.                     sublime.set_timeout(lambda : open_config(message), 10)
  3331.                 return
  3332.  
  3333.         return parsed_config
  3334. # okay decompiling /home/manager/.config/sublime-text-3/Packages/SFTP/sftp/config.pyc
  3335. # uncompyle6 version 3.2.3
  3336. # Python bytecode 3.3 (3230)
  3337. # Decompiled from: Python 2.7.12 (default, Dec  4 2017, 14:50:18)
  3338. # [GCC 5.4.0 20160609]
  3339. # Embedded file name: /Users/wbond/Library/Application Support/Sublime Text 3/Packages/SFTP/sftp/debug.py
  3340. # Compiled at: 2015-11-05 00:17:09
  3341. # Size of source mod 2**32: 2114 bytes
  3342. import sublime, locale, time
  3343. try:
  3344.     str_cls = unicode
  3345.     str_cls_name = 'unicode'
  3346. except NameError:
  3347.     str_cls = str
  3348.     str_cls_name = 'str'
  3349.  
  3350. from .times import timestamp_to_string
  3351. status = {'debug': False,
  3352.  'debug_log_file': None,
  3353.  'inited': False}
  3354.  
  3355. def debug_print(message, level=1, force=False):
  3356.     debug = status['debug']
  3357.     debug_log_file = status['debug_log_file']
  3358.     if not status['inited']:
  3359.  
  3360.         def init():
  3361.             settings = sublime.load_settings('SFTP.sublime-settings')
  3362.             debug_print('SFTP Email: ' + str(settings.get('email')))
  3363.             debug_print('SFTP Key: ' + str(settings.get('product_key')))
  3364.  
  3365.         sublime.set_timeout(init, 1)
  3366.         status['inited'] = True
  3367.     if (not debug or int(debug) < level) and not force:
  3368.         return
  3369.     typeof = type(message).__name__
  3370.     is_string = typeof == 'str' or typeof == str_cls_name
  3371.     if is_string and message[-1:] == '\n':
  3372.         message = message[0:-1]
  3373.     else:
  3374.         if not is_string:
  3375.             message = str(message)
  3376.         if type(message).__name__ != str_cls_name:
  3377.             try:
  3378.                 message = str_cls(message, 'utf-8', errors='strict')
  3379.             except UnicodeDecodeError:
  3380.                 encoding = locale.getpreferredencoding(do_setlocale=True)
  3381.                 try:
  3382.                     message = str_cls(message, encoding)
  3383.                 except UnicodeDecodeError:
  3384.                     message = str_cls(message, encoding, 'replace')
  3385.  
  3386.         message = str_cls(timestamp_to_string(time.time(), '%Y-%m-%d %H:%M:%S, ')) + message
  3387.         message = message.replace('\r\n', '\n')
  3388.     if debug and debug_log_file:
  3389.         with open(debug_log_file, 'ab') as (f):
  3390.             f.write(message.encode('utf-8') + '\n')
  3391.     else:
  3392.         if int(sublime.version()) < 3000:
  3393.             if isinstance(message, str_cls):
  3394.                 message = message.encode('UTF-8')
  3395.         print(message)
  3396.  
  3397.  
  3398. def set_debug(enabled):
  3399.     status['debug'] = enabled
  3400.  
  3401.  
  3402. def set_debug_log_file(path):
  3403.     status['debug_log_file'] = path
  3404.  
  3405.  
  3406. def get_debug():
  3407.     return status['debug']
  3408.  
  3409.  
  3410. def get_debug_log_file():
  3411.     return status['debug_log_file']
  3412. # okay decompiling /home/manager/.config/sublime-text-3/Packages/SFTP/sftp/debug.pyc
  3413. # uncompyle6 version 3.2.3
  3414. # Python bytecode 3.3 (3230)
  3415. # Decompiled from: Python 2.7.12 (default, Dec  4 2017, 14:50:18)
  3416. # [GCC 5.4.0 20160609]
  3417. # Embedded file name: /Users/wbond/Library/Application Support/Sublime Text 3/Packages/SFTP/sftp/errors.py
  3418. # Compiled at: 2015-11-05 00:17:46
  3419. # Size of source mod 2**32: 2020 bytes
  3420. import sublime, sys
  3421. from .debug import debug_print
  3422. if sys.version_info < (3, ):
  3423.     str_cls = unicode
  3424. else:
  3425.     str_cls = str
  3426.  
  3427. class ConnectionError(Exception):
  3428.     pass
  3429.  
  3430.  
  3431. class NotFoundError(Exception):
  3432.     pass
  3433.  
  3434.  
  3435. class OperationError(Exception):
  3436.     pass
  3437.  
  3438.  
  3439. class PermissionError(Exception):
  3440.     pass
  3441.  
  3442.  
  3443. class AuthenticationError(Exception):
  3444.     pass
  3445.  
  3446.  
  3447. class BinaryMissingError(Exception):
  3448.     pass
  3449.  
  3450.  
  3451. class DisconnectionError(OSError):
  3452.     pass
  3453.  
  3454.  
  3455. class CancelledError(OSError):
  3456.     pass
  3457.  
  3458.  
  3459. def handle_exception(heading, backtrace):
  3460.     divider = '-' * 60
  3461.     lines = [
  3462.      'SFTP %s:' % heading,
  3463.      divider]
  3464.     lines.extend(backtrace.split('\n'))
  3465.     lines.append(divider)
  3466.     debug_print(('\n    ').join(lines), force=True)
  3467.  
  3468.  
  3469. def encoding_error(e):
  3470.     debug_print('SFTP: Encoding error - ' + str_cls(e), 1)
  3471.  
  3472.     def show_encoding_helper():
  3473.         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'
  3474.         sublime.error_message('Sublime SFTP' + message)
  3475.  
  3476.     sublime.set_timeout(show_encoding_helper, 1)
  3477. # okay decompiling /home/manager/.config/sublime-text-3/Packages/SFTP/sftp/errors.pyc
  3478. # uncompyle6 version 3.2.3
  3479. # Python bytecode 3.3 (3230)
  3480. # Decompiled from: Python 2.7.12 (default, Dec  4 2017, 14:50:18)
  3481. # [GCC 5.4.0 20160609]
  3482. # Embedded file name: /Users/wbond/Library/Application Support/Sublime Text 3/Packages/SFTP/sftp/file_transfer.py
  3483. # Compiled at: 2015-11-05 01:18:03
  3484. # Size of source mod 2**32: 33345 bytes
  3485. import sublime, os, tempfile, time, datetime, shutil, re, json
  3486. try:
  3487.     basestring
  3488.     str_cls = unicode
  3489. except NameError:
  3490.     basestring = str
  3491.     str_cls = str
  3492.  
  3493. if os.name != 'nt':
  3494.     import unicodedata
  3495. from .debug import debug_print
  3496. from .errors import CancelledError, ConnectionError, DisconnectionError, NotFoundError, PermissionError
  3497. from .paths import canonicalize, dirname, fix_windows_path, ignore_paths, is_dir, local_to_remote
  3498. from .panel_printer import ProgressThread
  3499. from .times import timestamp_to_string
  3500. from .threads import HookedThread
  3501.  
  3502. def keepaliveize(fn):
  3503.  
  3504.     def handler(self, *args, **kwargs):
  3505.         set_keepalive = self.clear_keepalive()
  3506.         try:
  3507.             result = fn(self, *args, **kwargs)
  3508.         except Exception:
  3509.             if set_keepalive:
  3510.                 self.set_keepalive()
  3511.             raise
  3512.  
  3513.         if set_keepalive:
  3514.             self.set_keepalive()
  3515.         return result
  3516.  
  3517.     return handler
  3518.  
  3519.  
  3520. class CloneThread(HookedThread):
  3521.  
  3522.     def __init__(self, parent):
  3523.         self.parent = parent
  3524.         self.clone = None
  3525.         self.end = False
  3526.         super(CloneThread, self).__init__()
  3527.         return
  3528.  
  3529.     def run(self):
  3530.         try:
  3531.             args = []
  3532.             self.clone = self.parent.clone()
  3533.             while not self.end:
  3534.                 args = self.parent.pop_list_queue()
  3535.                 if args:
  3536.                     success, paths = self.clone.list(*args)
  3537.                     self.parent.push_list_result(args[0], success, paths)
  3538.                     self.parent.clear_list_running(args[0])
  3539.                 else:
  3540.                     time.sleep(0.1)
  3541.  
  3542.         except ConnectionError as e:
  3543.             pass
  3544.         except OSError as e:
  3545.             if isinstance(e, (CancelledError, DisconnectionError)):
  3546.                 if args:
  3547.                     self.parent.clear_list_running(args[0])
  3548.                     self.parent.list_queue.append(args)
  3549.                 return
  3550.             raise
  3551.  
  3552.  
  3553. class FileTransfer(object):
  3554.  
  3555.     def __init__(self, printer, user=None, host=None, port=None, password=None, remote_time_offset=None, **kwargs):
  3556.         self.ibm_ftp = False
  3557.         self.connected = False
  3558.         self.dir = None
  3559.         self.local_dir = None
  3560.         self.printer = printer
  3561.         self.shown_locale_set_error = False
  3562.         self.port = port
  3563.         self.host = host
  3564.         self.user = user
  3565.         self.password = password
  3566.         self.timeout = kwargs['timeout']
  3567.         self.remote_locale = kwargs['remote_locale']
  3568.         self.month_info = {'C': {'jan': 1,
  3569.                'feb': 2,
  3570.                'mar': 3,
  3571.                'apr': 4,
  3572.                'may': 5,
  3573.                'jun': 6,
  3574.                'jul': 7,
  3575.                'aug': 8,
  3576.                'sep': 9,
  3577.                'oct': 10,
  3578.                'nov': 11,
  3579.                'dec': 12}}
  3580.         self.clock_info = {'C': {'am': 0,
  3581.                'pm': 12}}
  3582.         if self.remote_locale != 'C':
  3583.             try:
  3584.                 months_file = os.path.join(sublime.packages_path(), 'SFTP', 'lang', 'months.json')
  3585.                 with open(months_file, 'r') as (f):
  3586.                     self.month_info = json.load(f)
  3587.                 if self.remote_locale not in self.month_info:
  3588.                     remote_locale = self.remote_locale
  3589.  
  3590.                     def show_locale_error():
  3591.                         sublime.error_message('Sublime SFTP\n\nThe remote_locale "%s" is not defined in %s' % (
  3592.                          remote_locale, months_file))
  3593.  
  3594.                     sublime.set_timeout(show_locale_error, 1)
  3595.                     self.remote_locale = 'C'
  3596.             except ValueError as e:
  3597.                 exception = str(e)
  3598.  
  3599.                 def show_parse_error():
  3600.                     sublime.error_message('Sublime SFTP\n\nError parsing %s:\n%s' % (
  3601.                      months_file, exception))
  3602.  
  3603.                 sublime.set_timeout(show_parse_error, 1)
  3604.  
  3605.             try:
  3606.                 clock_file = os.path.join(sublime.packages_path(), 'SFTP', 'lang', 'clock.json')
  3607.                 with open(clock_file, 'r') as (f):
  3608.                     self.clock_info = json.load(f)
  3609.             except ValueError as e:
  3610.                 exception = str(e)
  3611.  
  3612.                 def show_parse_error2():
  3613.                     sublime.error_message('Sublime SFTP\n\nError parsing %s:\n%s' % (
  3614.                      clock_file, exception))
  3615.  
  3616.                 sublime.set_timeout(show_parse_error2, 1)
  3617.  
  3618.         self.remote_encoding = kwargs['remote_encoding']
  3619.         self.keepalive = 0
  3620.         if kwargs.get('keepalive'):
  3621.             self.keepalive = kwargs['keepalive']
  3622.         self.keepalive_at = 0
  3623.         offset = kwargs.get('remote_time_offset_in_hours')
  3624.         if isinstance(offset, basestring):
  3625.             offset = int(offset)
  3626.         if offset is not None:
  3627.             offset *= -3600
  3628.         if offset is None and remote_time_offset is not None:
  3629.             offset = remote_time_offset
  3630.         self.remote_time_offset = offset
  3631.         self.preserve_modification_times = kwargs.get('preserve_modification_times', False)
  3632.         self.extra_list_connections = kwargs.get('extra_list_connections')
  3633.         self.clones = []
  3634.         self.clone_threads = []
  3635.         self.list_queue = []
  3636.         self.list_results = []
  3637.         self.list_args = []
  3638.         self.list_paths = []
  3639.         self.list_running_paths = []
  3640.         return
  3641.  
  3642.     def clone(self):
  3643.         kwargs = self.kwargs.copy()
  3644.         kwargs['keepalive'] = 0
  3645.         debug_print('SFTP: Cloning connection for recursive remote listing', 2)
  3646.         clone = self.__class__(self.printer, self.user, self.host, self.port, self.password, self.remote_time_offset, **kwargs)
  3647.         self.clones.append(clone)
  3648.         clone.connect(quiet=True)
  3649.         return clone
  3650.  
  3651.     def debug(self, debug):
  3652.         pass
  3653.  
  3654.     def clear_keepalive(self):
  3655.         if not self.keepalive:
  3656.             return
  3657.         should_set = False
  3658.         if self.keepalive_at < int(time.time()) + 1000000 - self.keepalive - 5:
  3659.             should_set = True
  3660.             debug_print('SFTP: Clearing keepalive', 2)
  3661.         self.keepalive_at = int(time.time()) + 1000000
  3662.         return should_set
  3663.  
  3664.     def set_keepalive(self):
  3665.         if not self.keepalive:
  3666.             return
  3667.         keepalive_at = int(time.time()) + self.keepalive - 1
  3668.         debug_print('SFTP: Setting keepalive to ' + str(keepalive_at), 2)
  3669.         self.keepalive_at = keepalive_at
  3670.  
  3671.         def perform_keepalive():
  3672.             if int(time.time()) < self.keepalive_at:
  3673.                 return
  3674.             try:
  3675.                 self.do_keepalive()
  3676.                 self.set_keepalive()
  3677.             except OSError:
  3678.                 self.close(True)
  3679.  
  3680.         sublime.set_timeout(perform_keepalive, self.keepalive * 1000)
  3681.  
  3682.     def do_keepalive(self):
  3683.         pass
  3684.  
  3685.     def connect(self):
  3686.         pass
  3687.  
  3688.     def close(self, disconnected=False):
  3689.         pass
  3690.  
  3691.     def decode(self, string):
  3692.         if isinstance(string, str_cls):
  3693.             return string
  3694.         try:
  3695.             output = str_cls(string, self.remote_encoding, errors='strict')
  3696.         except UnicodeDecodeError:
  3697.             self.remote_encoding = 'cp1252'
  3698.             output = str_cls(string, self.remote_encoding)
  3699.  
  3700.         return output
  3701.  
  3702.     def encode(self, string):
  3703.         if not isinstance(string, str_cls):
  3704.             return string
  3705.         return string.encode(self.remote_encoding)
  3706.  
  3707.     def determine_time_offset(self, path_map, config):
  3708.         if self.remote_time_offset is not None:
  3709.             if self.remote_time_offset is False:
  3710.                 return 0
  3711.             return self.remote_time_offset
  3712.         else:
  3713.             old_pwd = self.pwd()
  3714.             local_root = list(path_map.keys())[0]
  3715.             remote_root = config.get('initial_remote_dir')
  3716.             self.cd(remote_root)
  3717.             tmp_dir = os.path.join(tempfile.gettempdir(), 'sublime-sftp-offset-') + str(int(time.time()))
  3718.             tmp_dir = canonicalize(tmp_dir, 'local')
  3719.             if not os.path.exists(tmp_dir):
  3720.                 os.makedirs(tmp_dir)
  3721.             tmp_file = canonicalize(tmp_dir, 'local') + '__sublime_sftp_offset'
  3722.             path_map = {tmp_dir: remote_root}
  3723.             f = open(tmp_file, 'w')
  3724.             f.write('Sublime SFTP Offset Detection Dummy File')
  3725.             f.close()
  3726.             self.remote_time_offset = False
  3727.             try:
  3728.                 success, result = self.put(tmp_file, path_map, quiet=True)
  3729.                 if not success:
  3730.                     raise PermissionError('Permission denied')
  3731.                 files = self.ls(path_map, config=config)
  3732.                 remote_tmp_file = local_to_remote(tmp_file, path_map, self.remote_encoding)
  3733.                 self.rm(remote_tmp_file, path_map, quiet=True)
  3734.                 for file in files:
  3735.                     if file[0] == '__sublime_sftp_offset':
  3736.                         self.remote_time_offset = int(time.time() - file[1])
  3737.                         break
  3738.  
  3739.                 debug_print('SFTP Remote time offset: %s' % str(self.remote_time_offset))
  3740.             except PermissionError:
  3741.                 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)
  3742.                 self.remote_time_offset = 0
  3743.  
  3744.             self.cd(old_pwd)
  3745.             self.lcd(local_root)
  3746.             if os.path.exists(tmp_dir):
  3747.                 shutil.rmtree(tmp_dir)
  3748.             return self.remote_time_offset
  3749.  
  3750.     def cd(self, dir):
  3751.         pass
  3752.  
  3753.     def lcd(self, dir):
  3754.         pass
  3755.  
  3756.     def cwd(self, *args, **kwargs):
  3757.         return [
  3758.          True, self.pwd()]
  3759.  
  3760.     def get(self, remote_files, path_map, quiet=False, **kwargs):
  3761.         pass
  3762.  
  3763.     def handle_get_dirs(self, file, remote_file, single_file):
  3764.         if not is_dir(file):
  3765.             file_dir = dirname(file)
  3766.             remote_file_dir = dirname(remote_file)
  3767.         else:
  3768.             file_dir = file
  3769.             remote_file_dir = remote_file
  3770.         local_dir = canonicalize(file_dir, 'local')
  3771.         remote_dir = canonicalize(remote_file_dir, 'remote')
  3772.         if local_dir != self.lpwd():
  3773.             try:
  3774.                 self.lcd(local_dir)
  3775.                 if file_dir == file:
  3776.                     self.printer.write('\nFolder "%s" already exists' % local_dir)
  3777.             except NotFoundError as e:
  3778.                 os.makedirs(local_dir)
  3779.                 self.printer.write('\nCreating folder "%s" ... success' % local_dir)
  3780.                 self.lcd(local_dir)
  3781.  
  3782.         else:
  3783.             if file_dir == file:
  3784.                 self.printer.write('\nFolder "%s" already exists' % local_dir)
  3785.             if remote_dir != self.pwd():
  3786.                 try:
  3787.                     self.cd(remote_dir)
  3788.                 except (PermissionError, NotFoundError) as e:
  3789.                     self.printer.write('\nChanging to remote folder "%s" ... failure (%s)' % (remote_dir, str(e)))
  3790.                     if single_file:
  3791.                         self.printer.error(e)
  3792.                     return (True, True)
  3793.  
  3794.             if remote_dir == remote_file:
  3795.                 return (False, True)
  3796.         return (False, False)
  3797.  
  3798.     def handle_put_dirs(self, file, remote_file, chmod_dirs, single_file):
  3799.         if os.path.isdir(file):
  3800.             try:
  3801.                 self.mkdir(remote_file, chmod_dirs)
  3802.                 return (False, True)
  3803.             except PermissionError as e:
  3804.                 self.printer.write('\nCreating remote folder "%s" ... failure (%s)' % (remote_file, str(e)))
  3805.                 if single_file:
  3806.                     self.printer.error(e)
  3807.                 return (True, True)
  3808.  
  3809.         local_dir = canonicalize(dirname(file), 'local')
  3810.         remote_dir = canonicalize(dirname(remote_file), 'remote')
  3811.         if local_dir != self.lpwd():
  3812.             self.lcd(local_dir)
  3813.         if remote_dir != self.pwd():
  3814.             try:
  3815.                 try:
  3816.                     self.cd(remote_dir)
  3817.                 except NotFoundError:
  3818.                     self.mkdir(remote_dir, chmod_dirs)
  3819.                     self.cd(remote_dir)
  3820.  
  3821.             except PermissionError as e:
  3822.                 self.printer.write('\nChanging to remote folder "%s" ... failure (%s)' % (remote_dir, str(e)))
  3823.                 if single_file:
  3824.                     self.printer.error(e)
  3825.                 return (True, True)
  3826.  
  3827.         return (False, False)
  3828.  
  3829.     def handle_rm_dirs(self, file, remote_file, single_file):
  3830.         remote_dir = canonicalize(dirname(remote_file), 'remote')
  3831.         if remote_dir != self.pwd():
  3832.             try:
  3833.                 self.cd(remote_dir)
  3834.             except NotFoundError:
  3835.                 return (False, True)
  3836.             except PermissionError:
  3837.                 self.printer.write('\nChanging to remote folder "%s" ... failure' % remote_dir)
  3838.                 return (True, True)
  3839.  
  3840.         return (False, False)
  3841.  
  3842.     def pop_list_queue(self):
  3843.         try:
  3844.             self.list_running_paths.append(self.list_queue[0][0])
  3845.             return self.list_queue.pop(0)
  3846.         except IndexError:
  3847.             return
  3848.  
  3849.         return
  3850.  
  3851.     def clear_list_running(self, path):
  3852.         try:
  3853.             self.list_running_paths.remove(path)
  3854.         except ValueError:
  3855.             pass
  3856.  
  3857.     def push_list_result(self, remote_dir, success, paths, include_remote_dir=False):
  3858.         self.list_results.append([remote_dir, success, paths])
  3859.         if not success:
  3860.             return
  3861.         for path in paths:
  3862.             if path[0] == '.':
  3863.                 path[0] = ''
  3864.             full_dir_path = ('/').join([remote_dir, path[0]]).replace('//', '/')
  3865.             if not include_remote_dir and full_dir_path == remote_dir:
  3866.                 continue
  3867.             self.list_paths.append([full_dir_path, path[1]])
  3868.             if full_dir_path == remote_dir:
  3869.                 continue
  3870.             if full_dir_path[-1] == '/':
  3871.                 args = [
  3872.                  full_dir_path]
  3873.                 args.extend(self.list_args)
  3874.                 self.list_queue.append(args)
  3875.                 continue
  3876.  
  3877.     @keepaliveize
  3878.     def list(self, remote_dir, path_map, include_self=True, ignore_regex=None, quiet=False, config=None, skip_symlinks=True, **kwargs):
  3879.         remote_dir = canonicalize(remote_dir, 'remote')
  3880.         if remote_dir != self.pwd():
  3881.             try:
  3882.                 self.cd(remote_dir)
  3883.             except (PermissionError, NotFoundError) as e:
  3884.                 if not quiet:
  3885.                     self.printer.write('\nChanging to remote folder "%s" ... failure (%s)' % (remote_dir, str(e)))
  3886.                     self.printer.error(e)
  3887.                 return [False, str(e)]
  3888.  
  3889.         try:
  3890.             paths = self.ls(path_map, include_self, config=config, skip_symlinks=skip_symlinks)
  3891.         except (PermissionError, NotFoundError) as e:
  3892.             if not quiet:
  3893.                 self.printer.write('\nListing remote folder "%s" ... failure (%s)' % (remote_dir, str(e)))
  3894.                 self.printer.error(e)
  3895.             return [False, str(e)]
  3896.         except UnicodeDecodeError:
  3897.             if not quiet:
  3898.                 self.printer.write('\nListing remote folder "%s" ... failure (Encoding error)' % remote_dir)
  3899.                 self.printer.error('Encoding error')
  3900.             raise
  3901.  
  3902.         paths, unignored, ignored = ignore_paths(paths, {'ignore_regex': ignore_regex})
  3903.         return [
  3904.          True, paths]
  3905.  
  3906.     @keepaliveize
  3907.     def listr(self, remote_dir, path_map, include_self=True, ignore_regex=None, quiet=False, skip_symlinks=True, **kwargs):
  3908.         remote_dir = canonicalize(remote_dir, 'remote')
  3909.         for i in range(self.extra_list_connections):
  3910.             clone_thread = CloneThread(self)
  3911.             self.clone_threads.append(clone_thread)
  3912.             clone_thread.start()
  3913.  
  3914.         success, result = self.list(remote_dir, path_map, include_self=include_self, ignore_regex=ignore_regex, quiet=quiet, skip_symlinks=skip_symlinks)
  3915.         if not success:
  3916.             return [success, result]
  3917.         self.list_args = [path_map, False, ignore_regex, quiet, skip_symlinks]
  3918.         self.push_list_result(remote_dir, success, result, True)
  3919.         while True:
  3920.             if not len(self.list_queue) and not len(self.list_running_paths):
  3921.                 break
  3922.             args = self.pop_list_queue()
  3923.             if args:
  3924.                 success, paths = self.list(*args)
  3925.                 self.push_list_result(args[0], success, paths)
  3926.                 self.clear_list_running(args[0])
  3927.             else:
  3928.                 time.sleep(0.1)
  3929.  
  3930.         for result in self.list_results:
  3931.             if not result[1]:
  3932.                 self.printer.write('\nListing remote folder "%s" ... failure (%s)' % (result[0], result[2]))
  3933.                 continue
  3934.  
  3935.         output = sorted(self.list_paths)
  3936.         for thread in self.clone_threads:
  3937.             thread.end = True
  3938.  
  3939.         for clone in self.clones:
  3940.             debug_print('SFTP: Closing cloned connection', 2)
  3941.             clone.close()
  3942.  
  3943.         self.clones = []
  3944.         self.clone_threads = []
  3945.         self.list_queue = []
  3946.         self.list_results = []
  3947.         self.list_args = []
  3948.         self.list_paths = []
  3949.         self.list_running_paths = []
  3950.         return [
  3951.          True, output]
  3952.  
  3953.     def llist(self, local_dir, path_map, include_self=True, ignore_regex=None, quiet=False, **kwargs):
  3954.         try:
  3955.             local_dir = canonicalize(local_dir, 'local')
  3956.             paths = self.lls(local_dir, include_self)
  3957.             paths, unignored, ignored = ignore_paths(paths, {'ignore_regex': ignore_regex})
  3958.             return [
  3959.              True, paths]
  3960.         except NotFoundError as e:
  3961.             return [
  3962.              False, str(e)]
  3963.  
  3964.     def llistr(self, local_dir, path_map, include_self=True, ignore_regex=None, quiet=False, **kwargs):
  3965.         local_dir = canonicalize(local_dir, 'local')
  3966.         success, result = self.llist(local_dir, path_map, include_self=include_self, ignore_regex=ignore_regex, quiet=quiet)
  3967.         if not success:
  3968.             return [success, result]
  3969.         output = []
  3970.         for path in result:
  3971.             if path[0] == '.':
  3972.                 path[0] = ''
  3973.             full_dir_path = os.path.join(local_dir, path[0])
  3974.             output.append([full_dir_path, path[1]])
  3975.             if full_dir_path == local_dir:
  3976.                 continue
  3977.             if path[0][-1] == '/' or path[0][-1] == '\\':
  3978.                 success, result = self.llistr(full_dir_path, path_map, include_self=False, ignore_regex=ignore_regex, quiet=quiet)
  3979.                 if success:
  3980.                     output.extend(result)
  3981.                 elif quiet:
  3982.                     self.printer.write('\nListing local folder "%s" ... failure (%s)' % (full_dir_path, result))
  3983.                 else:
  3984.                     continue
  3985.  
  3986.         return [
  3987.          True, output]
  3988.  
  3989.     def ls(self, path_map, include_self=True, thread=None, config=None):
  3990.         pass
  3991.  
  3992.     def lls(self, local_dir, include_self=True):
  3993.         files = []
  3994.         if not os.path.exists(local_dir):
  3995.             raise NotFoundError('Folder not found')
  3996.         if include_self:
  3997.             files.append(['.', int(os.lstat(local_dir)[8])])
  3998.         for file in os.listdir(local_dir):
  3999.             if os.name != 'nt':
  4000.                 file = unicodedata.normalize('NFC', file)
  4001.             full_path = os.path.join(local_dir, file)
  4002.             path = file
  4003.             if os.path.isdir(full_path):
  4004.                 path = canonicalize(path, 'local')
  4005.             timestamp = int(os.lstat(full_path)[8])
  4006.             files.append([path, timestamp])
  4007.  
  4008.         files = sorted(files, key=lambda ar: ar[0].lower())
  4009.         return files
  4010.  
  4011.     def make_absolute_dir(self, dir, type):
  4012.         dir_prefix = self.pwd() if type == 'remote' else self.lpwd()
  4013.         if type == 'local' and os.name == 'nt':
  4014.             if not re.match('[A-Za-z]:\\\\|\\\\\\\\', dir):
  4015.                 dir = dir_prefix + dir
  4016.         else:
  4017.             if dir[0] != '/' and re.match('[A-Za-z]:\\\\', dir) is None:
  4018.                 dir = dir_prefix + dir
  4019.             return canonicalize(dir, type)
  4020.  
  4021.     def mkdir(self, dir, chmod_dirs=None, **kwargs):
  4022.         pass
  4023.  
  4024.     def mv(self, names, quiet=False, **kwargs):
  4025.         pass
  4026.  
  4027.     def put(self, files, path_map, chmod_files=None, chmod_dirs=None, quiet=False, **kwargs):
  4028.         pass
  4029.  
  4030.     def pwd(self):
  4031.         pass
  4032.  
  4033.     def lpwd(self):
  4034.         pass
  4035.  
  4036.     def rm(self, remote_files, path_map, quiet=False, **kwargs):
  4037.         pass
  4038.  
  4039.     def lrm(self, local_files, path_map, quiet=False, **kwargs):
  4040.         if not isinstance(local_files, list):
  4041.             local_files = [
  4042.              local_files]
  4043.         error = False
  4044.         single_file = len(local_files) == 1
  4045.         for local_file in local_files:
  4046.             if os.path.isdir(local_file):
  4047.                 sub_files = [os.path.join(local_file, sub_file) for sub_file in os.listdir(local_file)]
  4048.                 self.lrm(sub_files, path_map)
  4049.             if not quiet:
  4050.                 progress = ProgressThread(self.printer, '\nDeleting local "%s"' % local_file)
  4051.             try:
  4052.                 if os.path.isdir(local_file):
  4053.                     os.rmdir(local_file)
  4054.                 else:
  4055.                     os.unlink(local_file)
  4056.             except OSError:
  4057.                 message = 'Permission denied'
  4058.                 if not quiet:
  4059.                     progress.stop('failure (%s)' % message)
  4060.                     progress.join()
  4061.                 if single_file:
  4062.                     self.printer.error(message)
  4063.                 error = True
  4064.                 continue
  4065.  
  4066.             if not quiet:
  4067.                 progress.stop('success')
  4068.                 progress.join()
  4069.                 continue
  4070.  
  4071.         result = None
  4072.         if error and not quiet and not single_file:
  4073.             string = 'One or more errors occured while removing files'
  4074.             result = string
  4075.             self.printer.write('\n' + string)
  4076.             self.printer.error(string)
  4077.         return [
  4078.          not error, result]
  4079.  
  4080.     def encourage_report(self, popup_message, log_message):
  4081.         timestamp = timestamp_to_string(time.time(), '%Y-%m-%d %H:%M:%S\n')
  4082.         log_file_path = os.path.join(sublime.packages_path(), 'User', 'SFTP.errors.log')
  4083.         send_log_path = log_file_path
  4084.         with open(log_file_path, 'ab') as (f):
  4085.             f.write(timestamp.encode('utf-8'))
  4086.             f.write(log_message.encode('utf-8'))
  4087.  
  4088.         def notify_parse_error():
  4089.             sublime.error_message('Sublime SFTP\n\n%s, please send the file %s to support@wbond.net' % (
  4090.              popup_message, send_log_path))
  4091.             sublime.active_window().run_command('open_file', {'file': fix_windows_path(send_log_path)})
  4092.  
  4093.         sublime.set_timeout(notify_parse_error, 1)
  4094.  
  4095.     def parse_month(self, month, lines):
  4096.         try:
  4097.             if not re.match('^\\d+$', month):
  4098.                 month = month.strip(' \t.').lower()
  4099.                 if month not in self.month_info[self.remote_locale]:
  4100.                     raise ValueError('')
  4101.                 month = self.month_info[self.remote_locale][month]
  4102.             return str(month)
  4103.         except ValueError:
  4104.             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' % (
  4105.              month, self.remote_locale)
  4106.             indented_lines = ['  ' + l for l in lines]
  4107.             log_message = '%s\n%s\n' % (popup_message, ('\n').join(indented_lines))
  4108.             self.encourage_report(popup_message, log_message)
  4109.             raise ConnectionError('Error parsing remote folder listing')
  4110.  
  4111.     def parse_date(self, date, lines, format='%Y-%m-%d %H:%M'):
  4112.         try:
  4113.             timestamp = datetime.datetime.strptime(date, format)
  4114.         except ValueError:
  4115.             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' % (
  4116.              date, self.remote_locale)
  4117.             indented_lines = ['  ' + l for l in lines]
  4118.             log_message = '%s\n%s\n' % (popup_message, ('\n').join(indented_lines))
  4119.             self.encourage_report(popup_message, log_message)
  4120.             raise ConnectionError('Error parsing remote folder listing')
  4121.  
  4122.         return timestamp
  4123.  
  4124.     def parse_ls(self, lines, offset, include_self, skip_symlinks):
  4125.         output = []
  4126.         vshell_sftp_regex = '^(?:\\s*\\d+\\s+([^ ]+)\\s+(\\d+),\\s+(\\d+)\\s+(\\d{2}:\\d{2})\\s+(.*)|\\s*0\\s+([A-Z]/))$'
  4127.         data_set = None
  4128.         for line in lines:
  4129.             line = line.lstrip()
  4130.             line = line.rstrip('\n\r')
  4131.             if not line == 'ls':
  4132.                 if line == 'ls -la':
  4133.                     continue
  4134.                 if self.ibm_ftp:
  4135.                     if data_set is None:
  4136.                         if line.find('Dsname') != -1:
  4137.                             data_set = False
  4138.                         else:
  4139.                             data_set = True
  4140.                         continue
  4141.                     if data_set:
  4142.                         parts = re.split('\\s+', line, maxsplit=9)
  4143.                         file = parts[0]
  4144.                         if len(parts) > 1:
  4145.                             date = ('%s %s' % (parts[3], parts[4])).replace('/', '-')
  4146.                             timestamp = self.parse_date(date, lines)
  4147.                         else:
  4148.                             timestamp = int(time.time())
  4149.                             output.append([file, timestamp])
  4150.                             continue
  4151.                     else:
  4152.                         parts = re.split('\\s+', line, maxsplit=8)
  4153.                         file = parts[7]
  4154.                         if file.find('.') != -1:
  4155.                             prefix = file[:file.find('.')]
  4156.                             for entry in output:
  4157.                                 if entry[0] == prefix:
  4158.                                     continue
  4159.                                     continue
  4160.  
  4161.                             file = prefix
  4162.                         date = parts[3].replace('/', '-') + ' 00:00'
  4163.                         timestamp = self.parse_date(date, lines)
  4164.                 else:
  4165.                     if re.search(vshell_sftp_regex, line) is not None:
  4166.                         match = re.search(vshell_sftp_regex, line)
  4167.                         parts = list(match.groups())
  4168.                         file = parts[4]
  4169.                         if parts[0] is None and parts[5] is not None:
  4170.                             continue
  4171.                         if not file == '../':
  4172.                             if not include_self and file == './':
  4173.                                 continue
  4174.                             if file == './':
  4175.                                 file = '.'
  4176.                             month = parts[0]
  4177.                             day = parts[1]
  4178.                             year = parts[2]
  4179.                             _time = parts[3]
  4180.                             if len(day) == 1:
  4181.                                 day = '0' + day
  4182.                             if len(_time) == 4:
  4183.                                 _time = '0' + _time
  4184.                             month = self.parse_month(month, lines)
  4185.                             date = '%s-%s-%s %s' % (year, month, day, _time)
  4186.                             timestamp = self.parse_date(date, lines)
  4187.                     else:
  4188.                         if re.match('[0-9]', line) is not None:
  4189.                             parts = re.split('\\s+', line, maxsplit=3)
  4190.                             file = parts[3]
  4191.                             if parts[2] == '<DIR>':
  4192.                                 file += '/'
  4193.                             parts[1] = parts[1].replace('.', '').lower()
  4194.                             if self.remote_locale in self.clock_info:
  4195.                                 for suffix in self.clock_info[self.remote_locale]:
  4196.                                     new_part = parts[1].replace(suffix, '')
  4197.                                     if new_part != parts[1]:
  4198.                                         hours, minutes = new_part.split(':')
  4199.                                         adjustment = self.clock_info[self.remote_locale][suffix]
  4200.                                         if hours == '12':
  4201.                                             hours = str(adjustment)
  4202.                                         else:
  4203.                                             hours = str(int(hours) + int(adjustment))
  4204.                                         if len(hours) < 2:
  4205.                                             hours = '0' + str(hours)
  4206.                                         parts[1] = '%s:%s' % (hours, minutes)
  4207.                                         continue
  4208.  
  4209.                             month, day, year = parts[0].split('-')
  4210.                             if len(str(year)) == 2:
  4211.                                 if int(year) > int(datetime.datetime.now().strftime('%y')):
  4212.                                     year = '19' + year
  4213.                                 else:
  4214.                                     year = '20' + year
  4215.                             date = '%s-%s-%s %s' % (year, month, day, parts[1])
  4216.                             timestamp = self.parse_date(date, lines)
  4217.                         else:
  4218.                             if re.match('total \\d+', line, re.I) is not None:
  4219.                                 continue
  4220.                 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)
  4221.                 if match is None:
  4222.                     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)
  4223.                 if match is None:
  4224.                     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)
  4225.                 if match is None:
  4226.                     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)
  4227.                 if match is None:
  4228.                     match = re.match('^([^ ]+)(\\s+)(folder)(\\s+)(0)\\s+([^\\d\\s]+)\\s+(\\d+)\\s+((?:19|20)\\d{2}|[012]?\\d:\\d{2})\\s(.*)$', line)
  4229.                 if match is None:
  4230.                     match = re.match('^([^ ]+)\\s+(\\d+)\\s+(.*?)\\s+(.*?)\\s+(\\d+)\\s+(\\.\\.)$', line)
  4231.                 if match is None:
  4232.                     popup_message = 'There was an error parsing the remote folder listing'
  4233.                     indented_lines = ['  ' + l for l in lines]
  4234.                     log_message = 'Error parsing remote folder listing:\n' + ('\n').join(indented_lines) + '\n'
  4235.                     self.encourage_report(popup_message, log_message)
  4236.                     raise ConnectionError('Error parsing remote folder listing')
  4237.                 parts = list(match.groups())
  4238.                 if len(parts) == 6:
  4239.                     continue
  4240.                 file = parts[8]
  4241.                 if not file == '..':
  4242.                     if not include_self and file == '.':
  4243.                         continue
  4244.                     if line[0] == 'l':
  4245.                         if skip_symlinks is False:
  4246.                             file = self.check_symlink(file)
  4247.                         elif skip_symlinks == 'file':
  4248.                             file = re.sub(' -> [^ ].*$', '', file)
  4249.                         else:
  4250.                             continue
  4251.                     if parts[0][0] == 'd' and file != '.':
  4252.                         file += '/'
  4253.                     if re.match('^\\d+$', parts[5]):
  4254.                         day = parts[5]
  4255.                         month = parts[6]
  4256.                     else:
  4257.                         month = parts[5]
  4258.                         day = parts[6]
  4259.                     month = self.parse_month(month, lines)
  4260.                     month = str(month)
  4261.                     if len(str(day)) == 1:
  4262.                         day = '0' + str(day)
  4263.                     if str(parts[7]).find(':') == -1:
  4264.                         year = str(parts[7]).strip()
  4265.                         _time = '00:00'
  4266.                     else:
  4267.                         year = datetime.datetime.now().strftime('%Y')
  4268.                         _time = str(parts[7])
  4269.                         if len(_time) == 4:
  4270.                             _time = '0' + _time
  4271.                         current_month = datetime.datetime.now().strftime('%m')
  4272.                         if int(month) > int(current_month):
  4273.                             year = str(int(year) - 1)
  4274.                         date = '%s-%s-%s %s' % (year, month, day, _time)
  4275.                         timestamp = self.parse_date(date, lines)
  4276.                     try:
  4277.                         timestamp = int(time.mktime(timestamp.timetuple())) + offset
  4278.                     except OverflowError:
  4279.                         if int(timestamp.strftime('%Y')) <= 1970:
  4280.                             timestamp = 0
  4281.                         else:
  4282.                             timestamp = int(time.time())
  4283.  
  4284.                     output.append([file, timestamp])
  4285.  
  4286.         return output
  4287. # okay decompiling /home/manager/.config/sublime-text-3/Packages/SFTP/sftp/file_transfer.pyc
  4288. # uncompyle6 version 3.2.3
  4289. # Python bytecode 3.3 (3230)
  4290. # Decompiled from: Python 2.7.12 (default, Dec  4 2017, 14:50:18)
  4291. # [GCC 5.4.0 20160609]
  4292. # Embedded file name: /Users/wbond/Library/Application Support/Sublime Text 3/Packages/SFTP/sftp/ftplib2.py
  4293. # Compiled at: 2015-11-05 00:18:29
  4294. # Size of source mod 2**32: 37337 bytes
  4295. import sys, re
  4296. try:
  4297.     import SOCKS
  4298.     socket = SOCKS
  4299.     del SOCKS
  4300.     from socket import getfqdn
  4301.     socket.getfqdn = getfqdn
  4302.     del getfqdn
  4303. except ImportError:
  4304.     import socket
  4305.  
  4306. from socket import _GLOBAL_DEFAULT_TIMEOUT
  4307. __all__ = [
  4308.  'FTP', 'FTP_TLS']
  4309. MSG_OOB = 1
  4310. FTP_PORT = 21
  4311.  
  4312. class Error(Exception):
  4313.     pass
  4314.  
  4315.  
  4316. class error_reply(Error):
  4317.     pass
  4318.  
  4319.  
  4320. class error_temp(Error):
  4321.     pass
  4322.  
  4323.  
  4324. class error_perm(Error):
  4325.     pass
  4326.  
  4327.  
  4328. class error_proto(Error):
  4329.     pass
  4330.  
  4331.  
  4332. class error_fixed_host(Error):
  4333.     pass
  4334.  
  4335.  
  4336. all_errors = (
  4337.  Error, IOError, EOFError)
  4338. SSLError = Error
  4339. CRLF = '\r\n'
  4340. B_CRLF = '\r\n'
  4341. if sys.version_info < (3,):
  4342.     str_cls = unicode
  4343.     str_clses = (unicode, str)
  4344. else:
  4345.     str_cls = str
  4346.     str_clses = (str, bytes)
  4347.  
  4348. class FTP:
  4349.     debugging = 0
  4350.     debug_callback = None
  4351.     host = ''
  4352.     ip_addr = ''
  4353.     port = FTP_PORT
  4354.     sock = None
  4355.     file = None
  4356.     welcome = None
  4357.     passiveserver = 1
  4358.     obey_passive_host = False
  4359.     creating_connection = False
  4360.     closed_during_connection = False
  4361.     encoding = 'cp1252'
  4362.     was_125 = False
  4363.  
  4364.     def __init__(self, host='', user='', passwd='', acct='', timeout=_GLOBAL_DEFAULT_TIMEOUT):
  4365.         self.timeout = timeout
  4366.         if host:
  4367.             self.connect(host)
  4368.             if user:
  4369.                 self.login(user, passwd, acct)
  4370.  
  4371.     def connect(self, host='', port=0, timeout=-999):
  4372.         if host != '':
  4373.             self.host = host
  4374.         if int(port) > 0:
  4375.             self.port = int(port)
  4376.         if timeout != -999:
  4377.             self.timeout = timeout
  4378.         self.creating_connection = True
  4379.         try:
  4380.             self.ip_addr = socket.gethostbyname(self.host)
  4381.         except (socket.gaierror, TypeError):
  4382.             self.ip_addr = None
  4383.  
  4384.         self.sock = socket.create_connection((self.host, self.port), self.timeout)
  4385.         self.check_closed_during_connection()
  4386.         self.af = self.sock.family
  4387.         if sys.version_info < (3, ):
  4388.             self.file = self.sock.makefile('rb')
  4389.         else:
  4390.             self.file = self.sock.makefile('r', encoding=self.encoding)
  4391.         self.welcome = self.getresp()
  4392.         return self.welcome
  4393.  
  4394.     def encode(self, *args):
  4395.         output = ''
  4396.         for arg in args:
  4397.             if isinstance(arg, str_cls):
  4398.                 arg = arg.encode(self.encoding)
  4399.             output += arg
  4400.  
  4401.         return output
  4402.  
  4403.     def check_closed_during_connection(self):
  4404.         self.creating_connection = False
  4405.         if self.closed_during_connection:
  4406.             raise socket.error('Closed')
  4407.  
  4408.     def getwelcome(self):
  4409.         if self.debugging:
  4410.             self.debug_print('*welcome* ' + self.sanitize(self.welcome))
  4411.         return self.welcome
  4412.  
  4413.     def set_debuglevel(self, level, callback=None):
  4414.         self.debugging = level
  4415.         self.debug_callback = callback
  4416.  
  4417.     debug = set_debuglevel
  4418.  
  4419.     def debug_print(self, string):
  4420.         if self.debug_callback:
  4421.             self.debug_callback(string)
  4422.         else:
  4423.             print(string)
  4424.  
  4425.     def set_pasv(self, val, obey_passive_host=False):
  4426.         self.passiveserver = val
  4427.         self.obey_passive_host = obey_passive_host
  4428.  
  4429.     def sanitize(self, s):
  4430.         if s[:5] == 'pass ' or s[:5] == 'PASS ':
  4431.             i = len(s)
  4432.             while i > 5 and s[i - 1] in '\r\n':
  4433.                 i = i - 1
  4434.  
  4435.             s = s[:5] + '*' * (i - 5) + s[i:]
  4436.         return repr(s)
  4437.  
  4438.     def putline(self, line):
  4439.         if isinstance(line, str_cls):
  4440.             line = line + CRLF
  4441.         else:
  4442.             line = line + B_CRLF
  4443.         if self.debugging > 1:
  4444.             self.debug_print('*put* ' + self.sanitize(line))
  4445.         if isinstance(line, str_cls):
  4446.             line = line.encode(self.encoding)
  4447.         self.sock.sendall(line)
  4448.  
  4449.     def putcmd(self, line):
  4450.         if self.debugging:
  4451.             self.debug_print('*cmd* ' + self.sanitize(line))
  4452.         self.putline(line)
  4453.  
  4454.     def getline(self):
  4455.         line = self.file.readline()
  4456.         if self.debugging > 1:
  4457.             self.debug_print('*get* ' + self.sanitize(line))
  4458.         if not line:
  4459.             raise EOFError
  4460.         if line[-2:] == CRLF:
  4461.             line = line[:-2]
  4462.         else:
  4463.             if line[-1:] in CRLF:
  4464.                 line = line[:-1]
  4465.         return line
  4466.  
  4467.     def getmultiline(self):
  4468.         line = self.getline()
  4469.         if line[3:4] == '-':
  4470.             code = line[:3]
  4471.             while True:
  4472.                 nextline = self.getline()
  4473.                 line = line + ('\n' + nextline)
  4474.                 if nextline[:3] == code and nextline[3:4] != '-':
  4475.                     break
  4476.  
  4477.         return line
  4478.  
  4479.     def getresp(self):
  4480.         resp = self.getmultiline()
  4481.         if self.debugging:
  4482.             self.debug_print('*resp* ' + self.sanitize(resp))
  4483.         self.lastresp = resp[:3]
  4484.         c = resp[:1]
  4485.         if c in (u'1', u'2', u'3'):
  4486.             return resp
  4487.         if c == '4':
  4488.             raise error_temp(resp)
  4489.         if c == '5':
  4490.             raise error_perm(resp)
  4491.         raise error_proto(resp)
  4492.  
  4493.     def voidresp(self):
  4494.         resp = self.getresp()
  4495.         if resp[:1] != '2':
  4496.             raise error_reply(resp)
  4497.         return resp
  4498.  
  4499.     def abort(self):
  4500.         if 'ssl' in sys.modules and isinstance(self.sock, ssl.SSLSocket):
  4501.             return
  4502.         line = 'ABOR' + CRLF
  4503.         if isinstance(line, str_cls):
  4504.             line = line.encode(self.encoding)
  4505.         self.sock.sendall(line, MSG_OOB)
  4506.         resp = self.getmultiline()
  4507.         if resp[:3] not in (u'426', u'225', u'226'):
  4508.             raise error_proto(resp)
  4509.  
  4510.     def sendcmd(self, cmd):
  4511.         self.putcmd(cmd)
  4512.         return self.getresp()
  4513.  
  4514.     def voidcmd(self, cmd):
  4515.         self.putcmd(cmd)
  4516.         return self.voidresp()
  4517.  
  4518.     def sendport(self, host, port):
  4519.         hbytes = host.split('.')
  4520.         pbytes = [repr(port // 256), repr(port % 256)]
  4521.         bytes = hbytes + pbytes
  4522.         cmd = 'PORT ' + (',').join(bytes)
  4523.         return self.voidcmd(cmd)
  4524.  
  4525.     def sendeprt(self, host, port):
  4526.         af = 0
  4527.         if self.af == socket.AF_INET:
  4528.             af = 1
  4529.         if self.af == socket.AF_INET6:
  4530.             af = 2
  4531.         if af == 0:
  4532.             raise error_proto('unsupported address family')
  4533.         fields = [
  4534.          '', repr(af), host, repr(port), '']
  4535.         cmd = 'EPRT ' + ('|').join(fields)
  4536.         return self.voidcmd(cmd)
  4537.  
  4538.     def makeport(self):
  4539.         msg = 'getaddrinfo returns an empty list'
  4540.         sock = None
  4541.         for res in socket.getaddrinfo(None, 0, self.af, socket.SOCK_STREAM, 0, socket.AI_PASSIVE):
  4542.             af, socktype, proto, canonname, sa = res
  4543.             try:
  4544.                 sock = socket.socket(af, socktype, proto)
  4545.                 sock.bind(sa)
  4546.             except socket.error as msg:
  4547.                 if sock:
  4548.                     sock.close()
  4549.                 sock = None
  4550.                 continue
  4551.  
  4552.             break
  4553.  
  4554.         if not sock:
  4555.             raise socket.error(msg)
  4556.         sock.listen(1)
  4557.         port = sock.getsockname()[1]
  4558.         host = self.sock.getsockname()[0]
  4559.         if self.af == socket.AF_INET:
  4560.             self.sendport(host, port)
  4561.         else:
  4562.             self.sendeprt(host, port)
  4563.         if self.timeout is not _GLOBAL_DEFAULT_TIMEOUT:
  4564.             sock.settimeout(self.timeout)
  4565.         return sock
  4566.  
  4567.     def makepasv(self):
  4568.         if self.af == socket.AF_INET:
  4569.             host, port = parse227(self.sendcmd('PASV'))
  4570.         else:
  4571.             host, port = parse229(self.sendcmd('EPSV'), self.sock.getpeername())
  4572.         return (host, port)
  4573.  
  4574.     def ntransfercmd(self, cmd, rest=None):
  4575.         self.was_125 = False
  4576.         size = None
  4577.         if self.passiveserver:
  4578.             host, port = self.makepasv()
  4579.             original_host = host
  4580.             is_192 = host[0:8] == '192.168.'
  4581.             is_10 = host[0:3] == '10.'
  4582.             is_172 = re.match('172\\.(1[6-9]|2[0-9]|3[01])\\.', host) is not None
  4583.             is_private = is_192 or is_10 or is_172
  4584.             different_ip = self.ip_addr != host
  4585.             if different_ip and is_private and not self.obey_passive_host:
  4586.                 if self.debugging:
  4587.                     self.debug_print('*warn* Connecting to %s instead of %s since %s seems likely unroutable' % (
  4588.                      self.host, host, host))
  4589.                 host = self.host
  4590.             self.creating_connection = True
  4591.             try:
  4592.                 conn = socket.create_connection((host, port), self.timeout)
  4593.             except socket.timeout as e:
  4594.                 if different_ip and is_private and not self.obey_passive_host:
  4595.                     raise error_fixed_host(original_host)
  4596.                 raise e
  4597.  
  4598.             self.check_closed_during_connection()
  4599.             if rest is not None:
  4600.                 self.sendcmd('REST %s' % rest)
  4601.             resp = self.sendcmd(cmd)
  4602.             if resp[0] == '2':
  4603.                 resp = self.getresp()
  4604.             if resp[0] != '1':
  4605.                 raise error_reply(resp)
  4606.         else:
  4607.             sock = self.makeport()
  4608.             if rest is not None:
  4609.                 self.sendcmd('REST %s' % rest)
  4610.             resp = self.sendcmd(cmd)
  4611.             if resp[0] == '2':
  4612.                 resp = self.getresp()
  4613.             if resp[0] != '1':
  4614.                 raise error_reply(resp)
  4615.             conn, sockaddr = sock.accept()
  4616.             if self.timeout is not _GLOBAL_DEFAULT_TIMEOUT:
  4617.                 conn.settimeout(self.timeout)
  4618.             if resp[:3] == '150':
  4619.                 size = parse150(resp)
  4620.             if resp[:3] == '125':
  4621.                 self.was_125 = True
  4622.             return (conn, size)
  4623.  
  4624.     def transfercmd(self, cmd, rest=None):
  4625.         return self.ntransfercmd(cmd, rest)[0]
  4626.  
  4627.     def login(self, user='', passwd='', acct=''):
  4628.         if not user:
  4629.             user = 'anonymous'
  4630.         if not passwd:
  4631.             passwd = ''
  4632.         if not acct:
  4633.             acct = ''
  4634.         if user == 'anonymous' and passwd in (u'', u'-'):
  4635.             passwd = passwd + 'anonymous@'
  4636.         resp = self.sendcmd(self.encode('USER ', user))
  4637.         if resp[0] == '3':
  4638.             resp = self.sendcmd(self.encode('PASS ', passwd))
  4639.         if resp[0] == '3':
  4640.             resp = self.sendcmd(self.encode('ACCT ', acct))
  4641.         if resp[0] != '2':
  4642.             raise error_reply(resp)
  4643.         return resp
  4644.  
  4645.     def retrbinary(self, cmd, callback, blocksize=8192, rest=None):
  4646.         self.voidcmd('TYPE I')
  4647.         conn = self.transfercmd(cmd, rest)
  4648.         while True:
  4649.             data = conn.recv(blocksize)
  4650.             if not data:
  4651.                 break
  4652.             callback(data)
  4653.  
  4654.         conn.close()
  4655.         return self.voidresp()
  4656.  
  4657.     def retrlines(self, cmd, callback=None):
  4658.         if callback is None:
  4659.             callback = print_line
  4660.         self.sendcmd('TYPE A')
  4661.         conn = self.transfercmd(cmd)
  4662.         if sys.version_info < (3, ):
  4663.             fp = conn.makefile('rb')
  4664.         else:
  4665.             fp = conn.makefile('r', encoding=self.encoding)
  4666.         debug_lines = []
  4667.         while True:
  4668.             line = fp.readline()
  4669.             if self.debugging > 2:
  4670.                 debug_lines.append('*retr* ' + repr(line))
  4671.             if not line:
  4672.                 break
  4673.             if line[-2:] == CRLF:
  4674.                 line = line[:-2]
  4675.             else:
  4676.                 if line[-1:] == '\n':
  4677.                     line = line[:-1]
  4678.                 callback(line)
  4679.  
  4680.         if self.debugging > 2:
  4681.             self.debug_print(('\n').join(debug_lines))
  4682.         fp.close()
  4683.         conn.close()
  4684.         return self.voidresp()
  4685.  
  4686.     def storbinary(self, cmd, fp, blocksize=8192, callback=None, rest=None):
  4687.         self.voidcmd('TYPE I')
  4688.         conn = self.transfercmd(cmd, rest)
  4689.         while 1:
  4690.             buf = fp.read(blocksize)
  4691.             if not buf:
  4692.                 break
  4693.             conn.sendall(buf)
  4694.             if callback:
  4695.                 callback(buf)
  4696.                 continue
  4697.  
  4698.         conn.close()
  4699.         return self.voidresp()
  4700.  
  4701.     def storlines(self, cmd, fp, callback=None):
  4702.         self.voidcmd('TYPE A')
  4703.         conn = self.transfercmd(cmd)
  4704.         while 1:
  4705.             buf = fp.readline()
  4706.             if not buf:
  4707.                 break
  4708.             if buf[-2:] != CRLF:
  4709.                 if buf[-1] in CRLF:
  4710.                     buf = buf[:-1]
  4711.                 buf = buf + CRLF
  4712.             conn.sendall(buf)
  4713.             if callback:
  4714.                 callback(buf)
  4715.                 continue
  4716.  
  4717.         conn.close()
  4718.         return self.voidresp()
  4719.  
  4720.     def acct(self, password):
  4721.         cmd = self.encode('ACCT ', password)
  4722.         return self.voidcmd(cmd)
  4723.  
  4724.     def nlst(self, *args):
  4725.         cmd = 'NLST'
  4726.         for arg in args:
  4727.             cmd = cmd + self.encode(' ', arg)
  4728.  
  4729.         files = []
  4730.         self.retrlines(cmd, files.append)
  4731.         return files
  4732.  
  4733.     def dir(self, *args):
  4734.         cmd = 'LIST'
  4735.         func = None
  4736.         if args[-1:] and not isinstance(args[-1], str_clses):
  4737.             args, func = args[:-1], args[-1]
  4738.         for arg in args:
  4739.             if arg:
  4740.                 cmd = cmd + self.encode(' ', arg)
  4741.                 continue
  4742.  
  4743.         self.retrlines(cmd, func)
  4744.         return
  4745.  
  4746.     def rename(self, fromname, toname):
  4747.         resp = self.sendcmd(self.encode('RNFR ', fromname))
  4748.         if resp[0] != '3':
  4749.             raise error_reply(resp)
  4750.         return self.voidcmd(self.encode('RNTO ', toname))
  4751.  
  4752.     def delete(self, filename):
  4753.         resp = self.sendcmd(self.encode('DELE ', filename))
  4754.         if resp[:3] in (u'250', u'200'):
  4755.             return resp
  4756.         raise error_reply(resp)
  4757.  
  4758.     def cwd(self, dirname):
  4759.         if dirname == '..':
  4760.             try:
  4761.                 return self.voidcmd('CDUP')
  4762.             except error_perm as msg:
  4763.                 if msg.args[0][:3] != '500':
  4764.                     raise
  4765.  
  4766.         else:
  4767.             if dirname == '':
  4768.                 dirname = '.'
  4769.             cmd = self.encode('CWD ', dirname)
  4770.         return self.voidcmd(cmd)
  4771.  
  4772.     def size(self, filename):
  4773.         resp = self.sendcmd(self.encode('SIZE ', filename))
  4774.         if resp[:3] == '213':
  4775.             s = resp[3:].strip()
  4776.             try:
  4777.                 return int(s)
  4778.             except (OverflowError, ValueError):
  4779.                 return long(s)
  4780.  
  4781.     def mkd(self, dirname):
  4782.         resp = self.sendcmd(self.encode('MKD ', dirname))
  4783.         return parse257(resp)
  4784.  
  4785.     def rmd(self, dirname):
  4786.         return self.voidcmd(self.encode('RMD ', dirname))
  4787.  
  4788.     def pwd(self):
  4789.         resp = self.sendcmd('PWD')
  4790.         return parse257(resp)
  4791.  
  4792.     def quit(self):
  4793.         resp = self.voidcmd('QUIT')
  4794.         self.close()
  4795.         return resp
  4796.  
  4797.     def close(self):
  4798.         if self.creating_connection:
  4799.             self.closed_during_connection = True
  4800.         if self.file:
  4801.             self.file.close()
  4802.             self.sock.close()
  4803.             self.file = self.sock = None
  4804.         return
  4805.  
  4806.  
  4807. try:
  4808.     import ssl
  4809. except ImportError:
  4810.     pass
  4811. else:
  4812.  
  4813.     class FTP_TLS(FTP):
  4814.         ssl_version = ssl.PROTOCOL_TLSv1
  4815.  
  4816.         def __init__(self, host='', user='', passwd='', acct='', keyfile=None, certfile=None, timeout=_GLOBAL_DEFAULT_TIMEOUT):
  4817.             self.keyfile = keyfile
  4818.             self.certfile = certfile
  4819.             self._prot_p = False
  4820.             self.implicit_ssl = False
  4821.             FTP.__init__(self, host, user, passwd, acct, timeout)
  4822.  
  4823.         def login(self, user='', passwd='', acct='', secure=True):
  4824.             if secure and not isinstance(self.sock, ssl.SSLSocket):
  4825.                 self.auth()
  4826.             return FTP.login(self, user, passwd, acct)
  4827.  
  4828.         def auth(self):
  4829.             if isinstance(self.sock, ssl.SSLSocket):
  4830.                 raise ValueError('Already using TLS')
  4831.             if self.ssl_version == ssl.PROTOCOL_TLSv1:
  4832.                 resp = self.voidcmd('AUTH TLS')
  4833.             else:
  4834.                 resp = self.voidcmd('AUTH SSL')
  4835.             self.sock = ssl.wrap_socket(self.sock, self.keyfile, self.certfile, ssl_version=self.ssl_version)
  4836.             if sys.version_info < (3, ):
  4837.                 self.file = self.sock.makefile(mode='rb')
  4838.             else:
  4839.                 self.file = self.sock.makefile(mode='r', encoding=self.encoding)
  4840.             return resp
  4841.  
  4842.         def connect(self, host='', port=0, timeout=-999):
  4843.             if host != '':
  4844.                 self.host = host
  4845.             if int(port) > 0:
  4846.                 self.port = int(port)
  4847.             if timeout != -999:
  4848.                 self.timeout = timeout
  4849.             self.creating_connection = True
  4850.             try:
  4851.                 self.ip_addr = socket.gethostbyname(self.host)
  4852.             except (socket.gaierror, TypeError):
  4853.                 self.ip_addr = None
  4854.  
  4855.             self.sock = socket.create_connection((self.host, self.port), self.timeout)
  4856.             self.check_closed_during_connection()
  4857.             if int(port) == 990:
  4858.                 try:
  4859.                     self.sock = ssl.wrap_socket(self.sock, self.keyfile, self.certfile, ssl_version=self.ssl_version)
  4860.                     self.implicit_ssl = True
  4861.                 except ssl.SSLError:
  4862.                     self.creating_connection = True
  4863.                     self.sock = socket.create_connection((self.host, self.port), self.timeout)
  4864.                     self.check_closed_during_connection()
  4865.  
  4866.             self.af = self.sock.family
  4867.             if sys.version_info < (3, ):
  4868.                 self.file = self.sock.makefile('rb')
  4869.             else:
  4870.                 self.file = self.sock.makefile('r', encoding=self.encoding)
  4871.             self.welcome = self.getresp()
  4872.             return self.welcome
  4873.  
  4874.         def prot_p(self):
  4875.             self.voidcmd('PBSZ 0')
  4876.             resp = self.voidcmd('PROT P')
  4877.             self._prot_p = True
  4878.             return resp
  4879.  
  4880.         def prot_c(self):
  4881.             resp = self.voidcmd('PROT C')
  4882.             self._prot_p = False
  4883.             return resp
  4884.  
  4885.         def ntransfercmd(self, cmd, rest=None):
  4886.             conn, size = FTP.ntransfercmd(self, cmd, rest)
  4887.             if self._prot_p:
  4888.                 conn = ssl.wrap_socket(conn, self.keyfile, self.certfile, ssl_version=self.ssl_version)
  4889.             return (conn, size)
  4890.  
  4891.         def retrbinary(self, cmd, callback, blocksize=8192, rest=None):
  4892.             self.voidcmd('TYPE I')
  4893.             conn = self.transfercmd(cmd, rest)
  4894.             try:
  4895.                 while True:
  4896.                     data = conn.recv(blocksize)
  4897.                     if not data:
  4898.                         break
  4899.                     callback(data)
  4900.  
  4901.                 if not self.was_125 and isinstance(conn, ssl.SSLSocket):
  4902.                     conn.unwrap()
  4903.             finally:
  4904.                 conn.close()
  4905.  
  4906.             return self.voidresp()
  4907.  
  4908.         def retrlines(self, cmd, callback=None):
  4909.             if callback is None:
  4910.                 callback = print_line
  4911.             self.sendcmd('TYPE A')
  4912.             conn = self.transfercmd(cmd)
  4913.             if sys.version_info < (3, ):
  4914.                 fp = conn.makefile('rb')
  4915.             else:
  4916.                 fp = conn.makefile('r', encoding=self.encoding)
  4917.             try:
  4918.                 while True:
  4919.                     line = fp.readline()
  4920.                     if self.debugging > 2:
  4921.                         self.debug_print('*retr* ' + repr(line))
  4922.                     if not line:
  4923.                         break
  4924.                     if line[-2:] == CRLF:
  4925.                         line = line[:-2]
  4926.                     else:
  4927.                         if line[-1:] == '\n':
  4928.                             line = line[:-1]
  4929.                         callback(line)
  4930.  
  4931.                 if not self.was_125 and isinstance(conn, ssl.SSLSocket):
  4932.                     conn.unwrap()
  4933.             finally:
  4934.                 fp.close()
  4935.                 conn.close()
  4936.  
  4937.             return self.voidresp()
  4938.  
  4939.         def storbinary(self, cmd, fp, blocksize=8192, callback=None, rest=None):
  4940.             self.voidcmd('TYPE I')
  4941.             conn = self.transfercmd(cmd, rest)
  4942.             try:
  4943.                 while 1:
  4944.                     buf = fp.read(blocksize)
  4945.                     if not buf:
  4946.                         break
  4947.                     conn.sendall(buf)
  4948.                     if callback:
  4949.                         callback(buf)
  4950.                         continue
  4951.  
  4952.                 if not self.was_125 and isinstance(conn, ssl.SSLSocket):
  4953.                     conn.unwrap()
  4954.             finally:
  4955.                 conn.close()
  4956.  
  4957.             return self.voidresp()
  4958.  
  4959.         def storlines(self, cmd, fp, callback=None):
  4960.             self.voidcmd('TYPE A')
  4961.             conn = self.transfercmd(cmd)
  4962.             try:
  4963.                 while 1:
  4964.                     buf = fp.readline()
  4965.                     if not buf:
  4966.                         break
  4967.                     if buf[-2:] != CRLF:
  4968.                         if buf[-1] in CRLF:
  4969.                             buf = buf[:-1]
  4970.                         buf = buf + CRLF
  4971.                     conn.sendall(buf)
  4972.                     if callback:
  4973.                         callback(buf)
  4974.                         continue
  4975.  
  4976.                 if not self.was_125 and isinstance(conn, ssl.SSLSocket):
  4977.                     conn.unwrap()
  4978.             finally:
  4979.                 conn.close()
  4980.  
  4981.             return self.voidresp()
  4982.  
  4983.  
  4984.     __all__.append('FTP_TLS')
  4985.     SSLError = ssl.SSLError
  4986.     all_errors = (Error, IOError, EOFError, ssl.SSLError)
  4987.  
  4988. _150_re = None
  4989.  
  4990. def parse150(resp):
  4991.     global _150_re
  4992.     if resp[:3] != '150':
  4993.         raise error_reply(resp)
  4994.     if _150_re is None:
  4995.         import re
  4996.         _150_re = re.compile('150 .* \\((\\d+) bytes\\)', re.IGNORECASE)
  4997.     m = _150_re.match(resp)
  4998.     if not m:
  4999.         return
  5000.     else:
  5001.         s = m.group(1)
  5002.         try:
  5003.             return int(s)
  5004.         except (OverflowError, ValueError):
  5005.             return long(s)
  5006.  
  5007.         return
  5008.  
  5009.  
  5010. _227_re = None
  5011.  
  5012. def parse227(resp):
  5013.     global _227_re
  5014.     if resp[:3] != '227':
  5015.         raise error_reply(resp)
  5016.     if _227_re is None:
  5017.         import re
  5018.         _227_re = re.compile('(\\d+),(\\d+),(\\d+),(\\d+),(\\d+),(\\d+)')
  5019.     m = _227_re.search(resp)
  5020.     if not m:
  5021.         raise error_proto(resp)
  5022.     numbers = m.groups()
  5023.     host = ('.').join(numbers[:4])
  5024.     port = (int(numbers[4]) << 8) + int(numbers[5])
  5025.     return (
  5026.      host, port)
  5027.  
  5028.  
  5029. def parse229(resp, peer):
  5030.     if resp[:3] != '229':
  5031.         raise error_reply(resp)
  5032.     left = resp.find('(')
  5033.     if left < 0:
  5034.         raise error_proto(resp)
  5035.     right = resp.find(')', left + 1)
  5036.     if right < 0:
  5037.         raise error_proto(resp)
  5038.     if resp[left + 1] != resp[right - 1]:
  5039.         raise error_proto(resp)
  5040.     parts = resp[left + 1:right].split(resp[left + 1])
  5041.     if len(parts) != 5:
  5042.         raise error_proto(resp)
  5043.     host = peer[0]
  5044.     port = int(parts[3])
  5045.     return (
  5046.      host, port)
  5047.  
  5048.  
  5049. def parse257(resp):
  5050.     if resp[:3] != '257':
  5051.         raise error_reply(resp)
  5052.     if len(resp) > 26 and resp[3:26] == ' Current directory is "':
  5053.         resp = '257 "' + resp[26:]
  5054.     if resp[3:5] != ' "':
  5055.         return ''
  5056.     dirname = ''
  5057.     i = 5
  5058.     n = len(resp)
  5059.     while i < n:
  5060.         c = resp[i]
  5061.         i = i + 1
  5062.         if c == '"':
  5063.             if i >= n or resp[i] != '"':
  5064.                 break
  5065.             i = i + 1
  5066.         dirname = dirname + c
  5067.  
  5068.     return dirname
  5069.  
  5070.  
  5071. def print_line(line):
  5072.     print(line)
  5073.  
  5074.  
  5075. def ftpcp(source, sourcename, target, targetname='', type='I'):
  5076.     if not targetname:
  5077.         targetname = sourcename
  5078.     type = 'TYPE ' + type
  5079.     source.voidcmd(type)
  5080.     target.voidcmd(type)
  5081.     sourcehost, sourceport = parse227(source.sendcmd('PASV'))
  5082.     target.sendport(sourcehost, sourceport)
  5083.     treply = target.sendcmd('STOR ' + targetname)
  5084.     if treply[:3] not in (u'125', u'150'):
  5085.         raise error_proto
  5086.     sreply = source.sendcmd('RETR ' + sourcename)
  5087.     if sreply[:3] not in (u'125', u'150'):
  5088.         raise error_proto
  5089.     source.voidresp()
  5090.     target.voidresp()
  5091. # okay decompiling /home/manager/.config/sublime-text-3/Packages/SFTP/sftp/ftplib2.pyc
  5092. # uncompyle6 version 3.2.3
  5093. # Python bytecode 3.3 (3230)
  5094. # Decompiled from: Python 2.7.12 (default, Dec  4 2017, 14:50:18)
  5095. # [GCC 5.4.0 20160609]
  5096. # Embedded file name: /Users/wbond/Library/Application Support/Sublime Text 3/Packages/SFTP/sftp/ftps_transport.py
  5097. # Compiled at: 2015-11-05 00:18:32
  5098. # Size of source mod 2**32: 1762 bytes
  5099. import socket, sys
  5100. from . import ftplib2
  5101. from .ftp_transport import FTP
  5102.  
  5103. class FTPS(FTP):
  5104.  
  5105.     def create(self):
  5106.         ftp = ftplib2.FTP_TLS()
  5107.         if sys.version_info >= (3, ):
  5108.             ftp.encoding = self.remote_encoding
  5109.         return ftp
  5110.  
  5111.     def set_options(self):
  5112.         try:
  5113.             self.ftp.prot_p()
  5114.         except ftplib2.error_perm:
  5115.             try:
  5116.                 self.ftp.prot_c()
  5117.             except ftplib2.error_perm as e:
  5118.                 if not self.ftp.implicit_ssl:
  5119.                     raise e
  5120.  
  5121.     def check_disconnect(self):
  5122.         if not self.ftp:
  5123.             return
  5124.         old_timeout = self.ftp.sock.gettimeout()
  5125.         try:
  5126.             if sys.version_info < (3, ):
  5127.                 self.ftp.sock.settimeout(1e-05)
  5128.             else:
  5129.                 self.ftp.sock.settimeout(0.0)
  5130.             self.ftp.getresp()
  5131.         except EOFError:
  5132.             self.ftp.sock.settimeout(old_timeout)
  5133.         except socket.error as e:
  5134.             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:
  5135.                 raise e
  5136.             self.ftp.sock.settimeout(old_timeout)
  5137. # okay decompiling /home/manager/.config/sublime-text-3/Packages/SFTP/sftp/ftps_transport.pyc
  5138. # uncompyle6 version 3.2.3
  5139. # Python bytecode 3.3 (3230)
  5140. # Decompiled from: Python 2.7.12 (default, Dec  4 2017, 14:50:18)
  5141. # [GCC 5.4.0 20160609]
  5142. # Embedded file name: /Users/wbond/Library/Application Support/Sublime Text 3/Packages/SFTP/sftp/ftp_transport.py
  5143. # Compiled at: 2015-11-05 00:18:24
  5144. # Size of source mod 2**32: 44384 bytes
  5145. import sublime, os, sys, re, socket, traceback, time, datetime
  5146. if sys.version_info < (3, ):
  5147.     str_cls = unicode
  5148.     FileNotFoundError = IOError
  5149. else:
  5150.     str_cls = str
  5151. from . import ftplib2
  5152. from .secure_input import SecureInputThread
  5153. from .file_transfer import FileTransfer, keepaliveize
  5154. from .panel_printer import ProgressThread
  5155. from .debug import get_debug, debug_print
  5156. from .paths import canonicalize, dirname, is_dir, local_to_remote, remote_to_local
  5157. from .errors import AuthenticationError, CancelledError, ConnectionError, DisconnectionError, handle_exception, NotFoundError, OperationError, PermissionError
  5158.  
  5159. class FTP(FileTransfer):
  5160.  
  5161.     def __init__(self, printer, user=None, host=None, port=22, password=None, remote_time_offset=None, **kwargs):
  5162.         self.ftp = None
  5163.         self.in_progress = False
  5164.         self.closed = False
  5165.         self.supports_mfmt = False
  5166.         self.supports_utime = False
  5167.         self.supports_utime_utc = False
  5168.         self.last_command = ''
  5169.         super(FTP, self).__init__(printer, user, host, port, password, remote_time_offset, **kwargs)
  5170.         self.passive_mode = True
  5171.         if kwargs.get('ftp_passive_mode') is not None:
  5172.             self.passive_mode = kwargs['ftp_passive_mode']
  5173.         self.obey_passive_host = False
  5174.         if kwargs.get('ftp_obey_passive_host') is not None:
  5175.             self.obey_passive_host = kwargs['ftp_obey_passive_host']
  5176.         self.kwargs = kwargs
  5177.         return
  5178.  
  5179.     def debug(self, debug):
  5180.         if not self.ftp:
  5181.             return
  5182.         self.ftp.set_debuglevel(3 if debug else 0, self.handle_debug)
  5183.  
  5184.     def create(self):
  5185.         ftp = ftplib2.FTP()
  5186.         if sys.version_info >= (3, ):
  5187.             ftp.encoding = self.remote_encoding
  5188.         return ftp
  5189.  
  5190.     def set_options(self):
  5191.         pass
  5192.  
  5193.     def do_keepalive(self):
  5194.         if not self.ftp:
  5195.             return
  5196.         debug_print('SFTP: Doing keepalive', 2)
  5197.  
  5198.         def do_noop():
  5199.             self.last_command = 'NOOP'
  5200.             self.ftp.sendcmd('NOOP')
  5201.  
  5202.         self.handle_ftp_error(do_noop)
  5203.  
  5204.     def connect(self, quiet=False):
  5205.         if not quiet:
  5206.             progress = ProgressThread(self.printer, '\nConnecting to %s server "%s" as "%s"' % (
  5207.              self.__class__.__name__, self.host, self.user))
  5208.         try:
  5209.  
  5210.             def do_connect():
  5211.                 self.ftp = self.create()
  5212.                 self.debug(get_debug())
  5213.                 type = self.ftp.connect(self.host, self.port, self.timeout)
  5214.                 if re.search('IBM FTP', type, re.I) is not None and re.search('V1R', type) is not None:
  5215.                     self.ibm_ftp = True
  5216.                 if self.password is None:
  5217.                     was_visible = self.printer.visible
  5218.                     tries = 0
  5219.                     while True:
  5220.                         try:
  5221.                             if tries > 2:
  5222.                                 raise AuthenticationError('Invalid login/password specified')
  5223.                             input_thread = SecureInputThread("%s@%s's password" % (self.user, self.host))
  5224.                             input_thread.start()
  5225.                             input_thread.join()
  5226.                             if was_visible:
  5227.  
  5228.                                 def show():
  5229.                                     self.printer.show()
  5230.  
  5231.                                 sublime.set_timeout(show, 1)
  5232.                             tries += 1
  5233.                             if input_thread.password is None:
  5234.                                 raise CancelledError('Cancelled')
  5235.                             self.ftp.login(self.encode(self.user), self.encode(input_thread.password))
  5236.                             break
  5237.                         except ftplib2.error_perm as e:
  5238.                             if re.match('530', str(e)) is None or tries > 2:
  5239.                                 raise e
  5240.  
  5241.                 else:
  5242.                     self.ftp.login(self.user, self.password)
  5243.                 self.ftp.set_pasv(bool(self.passive_mode), self.obey_passive_host)
  5244.                 self.set_options()
  5245.                 if self.preserve_modification_times is True:
  5246.                     try:
  5247.  
  5248.                         def do_feat():
  5249.                             self.last_command = 'FEAT'
  5250.                             response = self.ftp.sendcmd('FEAT')
  5251.                             if response.find('MFMT') != -1:
  5252.                                 self.supports_mfmt = True
  5253.  
  5254.                         self.handle_ftp_error(do_feat)
  5255.                     except Exception as e:
  5256.                         pass
  5257.  
  5258.                     if not self.supports_mfmt:
  5259.                         try:
  5260.  
  5261.                             def do_site():
  5262.                                 self.last_command = 'UTIME'
  5263.                                 self.ftp.sendcmd('SITE UTIME')
  5264.  
  5265.                             self.handle_ftp_error(do_site)
  5266.                             self.supports_utime = True
  5267.                         except Exception as e:
  5268.                             pass
  5269.  
  5270.                     if not self.supports_mfmt and not self.supports_utime:
  5271.                         extra = ''
  5272.                         if os.name != 'nt':
  5273.                             extra = ' SFTP connections do not have this limitation.'
  5274.                         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)
  5275.                 return
  5276.  
  5277.             self.handle_ftp_error(do_connect, True)
  5278.             self.pwd()
  5279.             self.lpwd()
  5280.             if not quiet:
  5281.                 progress.stop('success')
  5282.                 progress.join()
  5283.         except (OSError, PermissionError, ConnectionError, AuthenticationError, NotFoundError) as e:
  5284.             if not quiet:
  5285.                 progress.stop('failure (%s)' % str(e))
  5286.                 progress.join
  5287.             raise e
  5288.         except UnicodeDecodeError:
  5289.             if not quiet:
  5290.                 progress.stop('failure (Encoding error)')
  5291.                 progress.join
  5292.             raise
  5293.  
  5294.     def check_disconnect(self):
  5295.         if not self.ftp:
  5296.             return
  5297.         old_timeout = self.ftp.sock.gettimeout()
  5298.         try:
  5299.             self.ftp.sock.setblocking(0)
  5300.             self.ftp.getresp()
  5301.         except EOFError:
  5302.             self.ftp.sock.settimeout(old_timeout)
  5303.         except socket.error as e:
  5304.             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:
  5305.                 raise e
  5306.             self.ftp.sock.settimeout(old_timeout)
  5307.  
  5308.     def handle_ftp_error(self, fn, initial_connection=False):
  5309.         try:
  5310.             if self.closed:
  5311.                 raise CancelledError('Cancelled')
  5312.             self.check_disconnect()
  5313.             result = fn()
  5314.             return result
  5315.         except socket.gaierror as e:
  5316.             raise ConnectionError('Host does not exist')
  5317.         except EOFError as e:
  5318.             error = DisconnectionError('Disconnected')
  5319.             raise error
  5320.         except ftplib2.error_fixed_host as e:
  5321.             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.')
  5322.         except ftplib2.error_perm as e:
  5323.             if re.match('530', str(e)) is not None and re.search('TLS|SSL|encryption', str(e), re.I) is not None:
  5324.                 error = AuthenticationError('Server requires FTPS connection')
  5325.             else:
  5326.                 if re.match('552 disk full', str(e), re.I) is not None:
  5327.                     error = PermissionError('Disk full or quota reached')
  5328.                 else:
  5329.                     if re.match('502', str(e)) is not None:
  5330.                         error = AuthenticationError('Server does not support FTPS')
  5331.                     else:
  5332.                         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):
  5333.                             error = AuthenticationError('Server does not support FTPS')
  5334.                         else:
  5335.                             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:
  5336.                                 error = AuthenticationError('Server does not support FTPS')
  5337.                             else:
  5338.                                 if re.match('530', str(e)) is not None:
  5339.                                     error = AuthenticationError('Invalid login/password specified')
  5340.                                 else:
  5341.                                     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):
  5342.                                         error = NotFoundError('File not found')
  5343.                                     else:
  5344.                                         if re.match('550 ([^:]+):', str(e)) is not None and re.search('exists', str(e), re.I) is not None:
  5345.                                             match = re.match('550 ([^:]+):', str(e))
  5346.                                             error = PermissionError('%s already exists' % match.group(1))
  5347.                                         else:
  5348.                                             if re.match('550', str(e)) is not None and str(e).find('directory not found') != -1:
  5349.                                                 error = NotFoundError('Folder not found')
  5350.                                             else:
  5351.                                                 if re.match('550 Failed to change', str(e)) is not None:
  5352.                                                     error = NotFoundError('Folder not found')
  5353.                                                 else:
  5354.                                                     if re.match('550', str(e)) is not None and re.search('allowed from your ip', str(e), re.I) is not None:
  5355.                                                         error = ConnectionError('No connections allowed from your IP')
  5356.                                                     else:
  5357.                                                         if re.match('501', str(e)) is not None and re.search('Directory could not be opened', str(e), re.I) is not None:
  5358.                                                             error = OperationError('Not a directory')
  5359.                                                         else:
  5360.                                                             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:
  5361.                                                                 error = PermissionError('Permission denied')
  5362.                                                             else:
  5363.                                                                 if re.match('550', str(e)) is not None and re.search('Requested action not taken', str(e), re.I) is not None:
  5364.                                                                     error = PermissionError('Permission denied')
  5365.                                                                 else:
  5366.                                                                     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:
  5367.                                                                         error = PermissionError('File in use by another process')
  5368.                                                                     else:
  5369.                                                                         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:
  5370.                                                                             error = PermissionError('Permission denied')
  5371.                                                                         else:
  5372.                                                                             if re.match('500 \\?', str(e)) is not None:
  5373.                                                                                 error = PermissionError('Unknown 500 error')
  5374.                                                                             else:
  5375.                                                                                 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:
  5376.                                                                                     error = ConnectionError('PASV mode error, please set ftp_passive_mode to false in sftp-config.json')
  5377.                                                                                 else:
  5378.                                                                                     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:
  5379.                                                                                         error = PermissionError('Server does not support chmod operations')
  5380.                                                                                     else:
  5381.                                                                                         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:
  5382.                                                                                             error = PermissionError('Server does not support chmod operations')
  5383.                                                                                         else:
  5384.                                                                                             if self.last_command == 'ECHMOD' and re.match('504', str(e)) is not None and str(e).find('not implemented') != -1:
  5385.                                                                                                 error = PermissionError('Server does not support chmod operations')
  5386.                                                                                             else:
  5387.                                                                                                 if self.last_command == 'ECHMOD' and re.match('500', str(e)) is not None and str(e).find('Unknown SITE command') != -1:
  5388.                                                                                                     error = PermissionError('Server does not support chmod operations')
  5389.                                                                                                 else:
  5390.                                                                                                     if re.match('500', str(e)) is not None and str(e).find('not understood') != -1 and str(e).find('CHMOD') != -1:
  5391.                                                                                                         error = PermissionError('Server does not support chmod, please comment out file_permissions and dir_permissions in sftp-config.json')
  5392.                                                                                                     else:
  5393.                                                                                                         if re.match('550', str(e)) is not None and str(e).find('not permitted') != -1 and str(e).find('CHMOD') != -1:
  5394.                                                                                                             error = PermissionError('Server does not support chmod, please comment out file_permissions and dir_permissions in sftp-config.json')
  5395.                                                                                                         else:
  5396.                                                                                                             if re.match('504', str(e)) is not None and str(e).find('not implemented') != -1 and self.last_command == 'CHMOD':
  5397.                                                                                                                 error = PermissionError('Server does not support chmod, please comment out file_permissions and dir_permissions in sftp-config.json')
  5398.                                                                                                             else:
  5399.                                                                                                                 if re.match('500', str(e)) is not None and str(e).find('Unknown SITE command') != -1 and self.last_command == 'CHMOD':
  5400.                                                                                                                     error = PermissionError('Server does not support chmod, please comment out file_permissions and dir_permissions in sftp-config.json')
  5401.                                                                                                                 else:
  5402.                                                                                                                     if re.match('504', str(e)) is not None and str(e).find('not implemented') != -1 and self.last_command == 'UTIME':
  5403.                                                                                                                         error = PermissionError('Server does not support preserving modification times, please set preserve_modification_times to false or "download_only" in sftp-config.json')
  5404.                                                                                                                     else:
  5405.                                                                                                                         if re.match('500', str(e)) is not None and str(e).find('not understood') != -1 and str(e).find('UTIME') != -1:
  5406.                                                                                                                             error = PermissionError('Server does not support preserving modification times, please set preserve_modification_times to false or "download_only" in sftp-config.json')
  5407.                                                                                                                         else:
  5408.                                                                                                                             if re.match('500', str(e)) is not None and str(e).find('Unknown SITE command') != -1 and self.last_command == 'UTIME':
  5409.                                                                                                                                 error = PermissionError('Server does not support preserving modification times, please set preserve_modification_times to false or "download_only" in sftp-config.json')
  5410.                                                                                                                             else:
  5411.                                                                                                                                 if re.match('500', str(e)) is not None and str(e).find('UTC Only') != -1:
  5412.                                                                                                                                     error = PermissionError('Server requires UTC SITE UTIME command')
  5413.                                                                                                                                 else:
  5414.                                                                                                                                     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':
  5415.                                                                                                                                         return
  5416.                                                                                                                                     if not self.ibm_ftp and re.match('501', str(e)) is not None and self.last_command == 'UTIME':
  5417.                                                                                                                                         return
  5418.                                                                                                                                     if re.match('550', str(e)) is not None and self.last_command == 'CWD':
  5419.                                                                                                                                         error = NotFoundError('FTP does not use drive letters in path names')
  5420.                                                                                                                                     else:
  5421.                                                                                                                                         if self.ibm_ftp and re.match('501', str(e)) is not None and str(e).find('Dsname') != -1:
  5422.                                                                                                                                             error = PermissionError('Invalid data set name')
  5423.                                                                                                                                         else:
  5424.                                                                                                                                             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:
  5425.                                                                                                                                                 error = ConnectionError('PASV mode error, please set ftp_passive_mode to false in sftp-config.json')
  5426.                                                                                                                                             else:
  5427.                                                                                                                                                 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:
  5428.                                                                                                                                                     error = ConnectionError('Active mode error, please set ftp_passive_mode to true in sftp-config.json')
  5429.                                                                                                                                                 else:
  5430.                                                                                                                                                     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:
  5431.                                                                                                                                                         error = ConnectionError('Active mode error, please set ftp_passive_mode to true in sftp-config.json')
  5432.                                                                                                                                                     else:
  5433.                                                                                                                                                         if re.match('522', str(e), re.I) is not None and re.search('session reuse required', str(e), re.I) is not None:
  5434.                                                                                                                                                             error = ConnectionError('Server requires SSL session reuse, which is not currently implemented')
  5435.                                                                                                                                                         else:
  5436.                                                                                                                                                             backtrace = traceback.format_exc()
  5437.                                                                                                                                                             handle_exception('Unknown Error', backtrace)
  5438.                                                                                                                                                             error = OSError('Unknown Error')
  5439.                                                                                                                                     raise error
  5440.         except ftplib2.error_temp as e:
  5441.             if self.closed:
  5442.                 error = CancelledError('Cancelled')
  5443.             else:
  5444.                 if re.match('421', str(e), re.I) is not None and re.search('terminating connection', str(e), re.I) is not None:
  5445.                     error = DisconnectionError('Disconnected')
  5446.                 else:
  5447.                     if re.match('421', str(e), re.I) is not None and re.search('(temporarily banned)', str(e), re.I) is not None:
  5448.                         error = ConnectionError('Temporarily banned')
  5449.                     else:
  5450.                         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:
  5451.                             error = DisconnectionError('Disconnected')
  5452.                         else:
  5453.                             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:
  5454.                                 error = ConnectionError('PASV mode error, please set ftp_passive_mode to false in sftp-config.json')
  5455.                             else:
  5456.                                 if re.match('426', str(e)) is not None and re.search('error creating file', str(e), re.I) is not None:
  5457.                                     error = PermissionError('Permission denied')
  5458.                                 else:
  5459.                                     if re.match('426', str(e), re.I) is not None:
  5460.                                         error = DisconnectionError('Disconnected')
  5461.                                     else:
  5462.                                         if str(e).rstrip(' ') == '421':
  5463.                                             error = ConnectionError('Too many open connections')
  5464.                                         else:
  5465.                                             if re.match('425', str(e), re.I) is not None:
  5466.                                                 error = ConnectionError('Active mode error, please set ftp_passive_mode to true in sftp-config.json')
  5467.                                             else:
  5468.                                                 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:
  5469.                                                     error = DisconnectionError('Server link to file server lost')
  5470.                                                 else:
  5471.                                                     if re.match('450', str(e)) is not None and re.search('Link to file server lost', str(e), re.I) is not None:
  5472.                                                         error = DisconnectionError('Server link to file server lost')
  5473.                                                     else:
  5474.                                                         if re.match('450', str(e)) is not None and re.search('no such', str(e), re.I) is not None:
  5475.                                                             error = NotFoundError('File not found')
  5476.                                                         else:
  5477.                                                             if re.match('450', str(e)) is not None and re.search('not a directory', str(e), re.I) is not None:
  5478.                                                                 error = OperationError('Not a directory')
  5479.                                                             else:
  5480.                                                                 if re.match('450', str(e)) is not None and re.search('permission denied', str(e), re.I) is not None:
  5481.                                                                     error = PermissionError('Permission denied')
  5482.                                                                 else:
  5483.                                                                     backtrace = traceback.format_exc()
  5484.                                                                     handle_exception('Unknown Error', backtrace)
  5485.                                                                     error = OSError('Unknown Error')
  5486.                                                                 raise error
  5487.         except ftplib2.error_reply as e:
  5488.             if self.closed:
  5489.                 error = CancelledError('Cancelled')
  5490.             else:
  5491.                 if re.match('221', str(e)) is not None and re.search('idle', str(e), re.I) is not None:
  5492.                     error = DisconnectionError('Disconnected')
  5493.                 else:
  5494.                     backtrace = traceback.format_exc()
  5495.                     handle_exception('Unknown Error', backtrace)
  5496.                     error = OSError('Unknown Error')
  5497.                 raise error
  5498.         except socket.error as e:
  5499.             if str(e).find('Errno 101]') != -1 or str(e) == 'timed out':
  5500.                 if initial_connection:
  5501.                     error = ConnectionError('Connection timeout')
  5502.                 else:
  5503.                     error = DisconnectionError('Disconnected')
  5504.             else:
  5505.                 if str(e).find('Errno 111]') != -1:
  5506.                     error = ConnectionError('Connection refused')
  5507.                 else:
  5508.                     if str(e).find('Errno 61]') != -1:
  5509.                         error = ConnectionError('Connection refused')
  5510.                     else:
  5511.                         if self.closed:
  5512.                             error = CancelledError('Cancelled')
  5513.                         else:
  5514.                             error = DisconnectionError('Disconnected')
  5515.                         raise error
  5516.         except (ftplib2.Error, ftplib2.SSLError, IOError, EOFError, AttributeError) as e:
  5517.             if self.closed:
  5518.                 error = CancelledError('Cancelled')
  5519.             else:
  5520.                 backtrace = traceback.format_exc()
  5521.                 handle_exception('Unknown Error', backtrace)
  5522.                 error = OSError('Unknown Error')
  5523.             raise error
  5524.  
  5525.         return
  5526.  
  5527.     def handle_debug(self, string):
  5528.         string = string.replace('\\n', '\n').replace('\\r', '\r')
  5529.         string = string.replace('\r', '')
  5530.         if string == '\n' or string == '':
  5531.             return
  5532.         message = ''
  5533.         lines = string.strip('\n').split('\n')
  5534.         last = ''
  5535.         for line in lines:
  5536.             sep = '\n'
  5537.             prefix = '    '
  5538.             if not len(line):
  5539.                 continue
  5540.             if line[0:5] == '*put*':
  5541.                 current = 'SFTP Write'
  5542.                 start = 8 if line[6] in (u'u', u'b') else 7
  5543.                 cleaned_line = line[start:]
  5544.             else:
  5545.                 if line[0:12] == '*put urgent*':
  5546.                     current = 'SFTP Write'
  5547.                     start = 15 if line[13] in (u'u', u'b') else 14
  5548.                     cleaned_line = line[start:]
  5549.                 else:
  5550.                     if line[0:5] == '*get*':
  5551.                         current = 'SFTP Read'
  5552.                         start = 8 if line[6] in (u'u', u'b') else 7
  5553.                         cleaned_line = line[start:]
  5554.                     else:
  5555.                         if line[0:6] == '*retr*':
  5556.                             current = 'SFTP Read'
  5557.                             start = 9 if line[7] in (u'u', u'b') else 8
  5558.                             cleaned_line = line[start:]
  5559.                         else:
  5560.                             if line[0:6] == '*warn*':
  5561.                                 current = 'SFTP Warning'
  5562.                                 cleaned_line = line[7:]
  5563.                                 sep = ' '
  5564.                                 prefix = ''
  5565.                             else:
  5566.                                 continue
  5567.             if cleaned_line == "'":
  5568.                 continue
  5569.             if current != last:
  5570.                 if message:
  5571.                     debug_print(message.rstrip('\n'))
  5572.                 message = current + ':' + sep
  5573.             message += prefix + cleaned_line + '\n'
  5574.             last = current
  5575.  
  5576.         if len(message):
  5577.             debug_print(message.rstrip('\n'))
  5578.  
  5579.     def close(self, disconnected=False):
  5580.         ftp = self.ftp
  5581.         in_progress = self.in_progress
  5582.         self.closed = True
  5583.         self.ftp = None
  5584.         self.in_progress = False
  5585.         if ftp is not None:
  5586.             if disconnected:
  5587.                 ftp.close()
  5588.             elif ftp.creating_connection:
  5589.                 ftp.close()
  5590.             else:
  5591.                 try:
  5592.                     if in_progress:
  5593.                         ftp.abort()
  5594.                     ftp.quit()
  5595.                 except (ftplib2.Error, ftplib2.SSLError, IOError, EOFError):
  5596.                     ftp.close()
  5597.  
  5598.         return
  5599.  
  5600.     @keepaliveize
  5601.     def chmod(self, file, mode=None, quiet=False, **kwargs):
  5602.         if not quiet:
  5603.             progress = ProgressThread(self.printer, '\nChmoding "%s" to "%s"' % (file, mode))
  5604.         try:
  5605.  
  5606.             def do_chmod():
  5607.                 self.last_command = 'CHMOD' if quiet is False else 'ECHMOD'
  5608.                 self.ftp.voidcmd('SITE CHMOD ' + self.encode(mode) + ' ' + self.encode(file))
  5609.  
  5610.             self.handle_ftp_error(do_chmod)
  5611.             if not quiet:
  5612.                 progress.stop('success')
  5613.                 progress.join()
  5614.         except (OSError, PermissionError, ConnectionError, AuthenticationError, NotFoundError) as e:
  5615.             if not quiet:
  5616.                 progress.stop('failure (%s)' % str(e))
  5617.                 progress.join()
  5618.             return [False, str_cls(e)]
  5619.         except UnicodeDecodeError:
  5620.             if not quiet:
  5621.                 progress.stop('failure (Encoding error)')
  5622.                 progress.join
  5623.             raise
  5624.  
  5625.         return [True, None]
  5626.  
  5627.     def cd(self, dir):
  5628.         dir = self.make_absolute_dir(dir, 'remote')
  5629.         if self.dir == dir:
  5630.             return
  5631.         try:
  5632.  
  5633.             def do_cd():
  5634.                 stripped_dir = dir
  5635.                 if dir != '/':
  5636.                     stripped_dir = dir.rstrip('/')
  5637.                 if self.ibm_ftp:
  5638.                     stripped_dir = "'%s'" % dir.lstrip('/').replace('/', '.')
  5639.                 self.last_command = 'CWD'
  5640.                 self.ftp.cwd(self.encode(stripped_dir))
  5641.  
  5642.             self.handle_ftp_error(do_cd)
  5643.         except (OSError, PermissionError, ConnectionError, AuthenticationError, NotFoundError) as e:
  5644.             if str(e) == 'File not found':
  5645.                 e = NotFoundError('Folder not found')
  5646.             raise e
  5647.  
  5648.         self.dir = dir
  5649.  
  5650.     def lcd(self, dir):
  5651.         dir = self.make_absolute_dir(dir, 'local')
  5652.         if dir == self.local_dir:
  5653.             return
  5654.         if not os.path.exists(dir):
  5655.             raise NotFoundError('Folder not found')
  5656.         self.local_dir = dir
  5657.  
  5658.     @keepaliveize
  5659.     def get(self, remote_files, path_map, quiet=False, **kwargs):
  5660.         if not isinstance(remote_files, list):
  5661.             remote_files = [
  5662.              remote_files]
  5663.         error = False
  5664.         single_file = len(remote_files) == 1
  5665.         list_cache = {}
  5666.         for remote_file in remote_files:
  5667.             file = remote_to_local(remote_file, path_map, self.remote_encoding)
  5668.             try:
  5669.                 dir_error, cont = self.handle_get_dirs(file, remote_file, single_file)
  5670.                 error = error or dir_error
  5671.                 if cont:
  5672.                     continue
  5673.             except (OSError, PermissionError, ConnectionError, AuthenticationError, NotFoundError) as e:
  5674.                 if not quiet:
  5675.                     self.printer.write('\nDownloading "%s" to "%s" ... failure (%s)' % (remote_file, file, str(e)))
  5676.                 raise e
  5677.  
  5678.             try:
  5679.                 if not quiet:
  5680.                     progress = ProgressThread(self.printer, '\nDownloading "%s" to "%s"' % (remote_file, file))
  5681.  
  5682.                 def do_get():
  5683.                     nonlocal_ = {'handle': None}
  5684.  
  5685.                     def download_handler(data):
  5686.                         if not nonlocal_['handle']:
  5687.                             nonlocal_['handle'] = open(file, 'wb')
  5688.                         nonlocal_['handle'].write(data)
  5689.  
  5690.                     basename = os.path.basename(remote_file)
  5691.                     if self.ibm_ftp:
  5692.                         data_set = dirname(remote_file).strip('/').replace('/', '.')
  5693.                         basename = "'%s(%s)'" % (data_set, basename)
  5694.                     self.in_progress = True
  5695.                     self.last_command = 'RETR'
  5696.                     self.ftp.retrbinary('RETR ' + self.encode(basename), download_handler)
  5697.                     self.in_progress = False
  5698.                     if nonlocal_['handle'] is None:
  5699.                         nonlocal_['handle'] = open(file, 'wb')
  5700.                     nonlocal_['handle'].close()
  5701.                     return
  5702.  
  5703.                 self.handle_ftp_error(do_get)
  5704.                 if self.preserve_modification_times:
  5705.                     if self.dir not in list_cache:
  5706.                         success, result = self.list(self.dir, path_map, quiet=True, skip_symlinks=False)
  5707.                         if success:
  5708.                             list_cache[self.dir] = dict(result)
  5709.                         else:
  5710.                             list_cache[self.dir] = None
  5711.                     if isinstance(list_cache[self.dir], dict):
  5712.                         atime = list_cache[self.dir][os.path.basename(remote_file)]
  5713.                         mtime = atime
  5714.                         os.utime(file, (atime, mtime))
  5715.                 if not quiet:
  5716.                     progress.stop('success')
  5717.                     progress.join()
  5718.             except (OSError, PermissionError, ConnectionError, AuthenticationError, NotFoundError) as e:
  5719.                 if not quiet:
  5720.                     progress.stop('failure (%s)' % str(e))
  5721.                     progress.join()
  5722.                 if not isinstance(e, (CancelledError, DisconnectionError)):
  5723.                     if single_file and not quiet:
  5724.                         self.printer.error(str(e))
  5725.                     error = True
  5726.                     continue
  5727.                 raise e
  5728.             except UnicodeDecodeError:
  5729.                 if not quiet:
  5730.                     progress.stop('failure (Encoding error)')
  5731.                     progress.join
  5732.                 raise
  5733.  
  5734.         result = None
  5735.         if error and not quiet and not single_file:
  5736.             string = 'One or more errors occured while downloading files'
  5737.             result = string
  5738.             self.printer.write('\n' + string)
  5739.             self.printer.error(string)
  5740.         return [
  5741.          not error, result]
  5742.  
  5743.     def ls(self, path_map, include_self=True, config=None, skip_symlinks=True):
  5744.         try:
  5745.             offset = self.determine_time_offset(path_map, config)
  5746.  
  5747.             def do_ls():
  5748.                 files = []
  5749.  
  5750.                 def ls_handler(string):
  5751.                     files.append(self.decode(string))
  5752.  
  5753.                 self.last_command = 'LIST'
  5754.                 self.ftp.retrlines('LIST -a', ls_handler)
  5755.                 return files
  5756.  
  5757.             ls_res = self.handle_ftp_error(do_ls)
  5758.             files = self.parse_ls(ls_res, offset, include_self, skip_symlinks)
  5759.             if include_self:
  5760.                 found_cur_dir = False
  5761.                 if files:
  5762.                     for file_ in files:
  5763.                         if file_[0] == '.':
  5764.                             found_cur_dir = True
  5765.                             break
  5766.  
  5767.                 if not found_cur_dir:
  5768.                     timestamp = int(time.time())
  5769.                     files.insert(0, ['.', timestamp])
  5770.             files = sorted(files, key=lambda ar: ar[0].lower())
  5771.             return files
  5772.         except (OSError, PermissionError, ConnectionError, AuthenticationError, NotFoundError) as e:
  5773.             if str(e) == 'Permission denied':
  5774.                 e = ConnectionError('PASV mode disallowed, please set ftp_passive_mode to false in sftp-config.json')
  5775.             else:
  5776.                 if str(e) == 'Connection timeout':
  5777.                     if self.passive_mode:
  5778.                         e = ConnectionError('PASV mode error, please set ftp_passive_mode to false in sftp-config.json')
  5779.                     else:
  5780.                         e = ConnectionError('Active mode error, please set ftp_passive_mode to true in sftp-config.json')
  5781.                 raise e
  5782.  
  5783.     def check_symlink(self, basename):
  5784.         basename = re.sub(' -> .*$', '', basename)
  5785.         try:
  5786.  
  5787.             def do_ls():
  5788.                 files = []
  5789.  
  5790.                 def ls_handler(string):
  5791.                     files.append(self.decode(string))
  5792.  
  5793.                 self.last_command = 'LIST'
  5794.                 self.ftp.retrlines('LIST -a ' + self.encode(basename + '/.'), ls_handler)
  5795.                 return files
  5796.  
  5797.             ls_res = self.handle_ftp_error(do_ls)
  5798.             if ls_res is None or len(ls_res) == 0:
  5799.                 return basename
  5800.             return canonicalize(basename, 'remote')
  5801.         except OperationError:
  5802.             return basename
  5803.         except (NotFoundError, PermissionError):
  5804.             return canonicalize(basename, 'remote')
  5805.  
  5806.         return
  5807.  
  5808.     @keepaliveize
  5809.     def mkdir(self, dir, chmod_dirs=None, **kwargs):
  5810.         try:
  5811.             self.cd(dir)
  5812.             self.printer.write('\nFolder "%s" already exists' % dir, key='sftp_mkdir', finish=True)
  5813.             if chmod_dirs:
  5814.                 self.chmod('.', chmod_dirs, quiet=True)
  5815.             return
  5816.         except NotFoundError:
  5817.             pass
  5818.         except PermissionError:
  5819.             self.printer.write('\nChmoding folder "%s" to "%s" ... failure (Permission denied)' % (dir, chmod_dirs), key='sftp_mkdir', finish=True)
  5820.             return
  5821.  
  5822.         if dir[0] == '/':
  5823.             parent_dir = dirname(dir) + '/'
  5824.             if parent_dir == '//':
  5825.                 parent_dir = '/'
  5826.             try:
  5827.                 if self.dir != parent_dir:
  5828.                     self.cd(parent_dir)
  5829.             except NotFoundError:
  5830.                 self.mkdir(parent_dir, chmod_dirs)
  5831.                 self.cd(parent_dir)
  5832.  
  5833.         dir = canonicalize(dir, 'remote')
  5834.         progress = ProgressThread(self.printer, '\nCreating folder "%s"' % dir)
  5835.         try:
  5836.             basename = os.path.basename(dir.rstrip('/'))
  5837.  
  5838.             def do_mkdir():
  5839.                 self.last_command = 'MKD'
  5840.                 self.ftp.mkd(self.encode(basename))
  5841.  
  5842.             self.handle_ftp_error(do_mkdir)
  5843.             try:
  5844.                 chmod_error = False
  5845.                 if chmod_dirs:
  5846.                     self.chmod(basename, chmod_dirs, quiet=True)
  5847.             except PermissionError:
  5848.                 chmod_error = True
  5849.  
  5850.             progress.stop('success')
  5851.             progress.join()
  5852.             if chmod_error:
  5853.                 self.printer.write('\nChmoding folder "%s" to "%s" ... failure (Permission denied)' % (dir, chmod_dirs), key='sftp_mkdir', finish=True)
  5854.         except (OSError, PermissionError, ConnectionError, AuthenticationError, NotFoundError) as e:
  5855.             progress.stop('failure (%s)' % str(e))
  5856.             progress.join()
  5857.             raise e
  5858.         except UnicodeDecodeError:
  5859.             progress.stop('failure (Encoding error)')
  5860.             progress.join
  5861.             raise
  5862.  
  5863.     @keepaliveize
  5864.     def mv(self, names, quiet=False, **kwargs):
  5865.         old_filename = os.path.basename(names[0].rstrip('\\/'))
  5866.         new_filename = os.path.basename(names[1].rstrip('\\/'))
  5867.         dir = dirname(names[0])
  5868.         dir = canonicalize(dir, 'remote')
  5869.         try:
  5870.             self.cd(dir)
  5871.         except NotFoundError:
  5872.             if not quiet:
  5873.                 self.printer.write('\nChanging to folder "%s" ... failure (Folder not found)' % dir)
  5874.                 self.printer.error('Folder not found')
  5875.             return [False, 'Folder not found']
  5876.  
  5877.         if not quiet:
  5878.             progress = ProgressThread(self.printer, '\nRenaming "%s" to "%s"' % (names[0], names[1]))
  5879.         try:
  5880.  
  5881.             def do_mv():
  5882.                 self.last_command = 'RENAME'
  5883.                 self.ftp.rename(self.encode(old_filename), self.encode(new_filename))
  5884.  
  5885.             self.handle_ftp_error(do_mv)
  5886.             if not quiet:
  5887.                 progress.stop('success')
  5888.                 progress.join()
  5889.         except (PermissionError, NotFoundError) as e:
  5890.             if not quiet:
  5891.                 progress.stop('failure (%s)' % str(e))
  5892.                 progress.join()
  5893.                 self.printer.error(str(e))
  5894.             return [False, str(e)]
  5895.         except (OSError, ConnectionError, AuthenticationError) as e:
  5896.             if not quiet:
  5897.                 progress.stop('failure (%s)' % str(e))
  5898.                 progress.join()
  5899.             raise e
  5900.         except UnicodeDecodeError:
  5901.             if not quiet:
  5902.                 progress.stop('failure (Encoding error)')
  5903.                 progress.join
  5904.             raise
  5905.  
  5906.         return [True, None]
  5907.  
  5908.     @keepaliveize
  5909.     def put(self, files, path_map, chmod_files=None, chmod_dirs=None, quiet=False, **kwargs):
  5910.         if not isinstance(files, list):
  5911.             files = [
  5912.              files]
  5913.         error = False
  5914.         single_file = len(files) == 1
  5915.         for file in files:
  5916.             remote_file = local_to_remote(file, path_map, self.remote_encoding)
  5917.             try:
  5918.                 dir_error, cont = self.handle_put_dirs(file, remote_file, chmod_dirs, single_file)
  5919.                 error = error or dir_error
  5920.                 if cont:
  5921.                     continue
  5922.             except (OSError, PermissionError, ConnectionError, AuthenticationError, NotFoundError) as e:
  5923.                 if not quiet:
  5924.                     self.printer.write('\nUploading "%s" to "%s" ... failure (%s)' % (file, remote_file, str(e)))
  5925.                 if single_file and not isinstance(e, (CancelledError, DisconnectionError)) and not quiet:
  5926.                     self.printer.error(e)
  5927.                 raise e
  5928.  
  5929.             try:
  5930.                 if not quiet:
  5931.                     progress = ProgressThread(self.printer, '\nUploading "%s" to "%s"' % (file, remote_file))
  5932.                 basename = os.path.basename(remote_file)
  5933.  
  5934.                 def do_put():
  5935.                     local_basename = basename
  5936.                     handle = open(file, 'rb')
  5937.                     self.in_progress = True
  5938.                     if self.ibm_ftp:
  5939.                         data_set = dirname(remote_file).strip('/').replace('/', '.')
  5940.                         local_basename = "'%s(%s)'" % (data_set, local_basename)
  5941.                     self.last_command = 'STOR'
  5942.                     self.ftp.storbinary('STOR ' + self.encode(local_basename), handle)
  5943.                     self.in_progress = False
  5944.                     handle.close()
  5945.                     mod_date = int(os.lstat(file)[8])
  5946.                     if self.supports_utime:
  5947.                         mod_datetime = datetime.datetime.fromtimestamp(mod_date + self.remote_time_offset).strftime('%Y%m%d%H%M%S')
  5948.                         try:
  5949.  
  5950.                             def try_utime():
  5951.                                 self.last_command = 'UTIME'
  5952.                                 self.ftp.sendcmd('SITE UTIME ' + self.encode(mod_datetime) + ' ' + self.encode(basename))
  5953.  
  5954.                             self.handle_ftp_error(try_utime)
  5955.                         except:
  5956.                             self.supports_utime = False
  5957.                             self.supports_utime_utc = True
  5958.  
  5959.                     if self.supports_utime_utc:
  5960.                         utc_mod_datetime = datetime.datetime.fromtimestamp(mod_date).strftime('%Y%m%d%H%M%S')
  5961.                         self.last_command = 'UTIME'
  5962.                         self.ftp.sendcmd('SITE UTIME ' + self.encode(basename) + ' ' + self.encode(utc_mod_datetime) + ' ' + self.encode(utc_mod_datetime) + ' ' + self.encode(utc_mod_datetime) + ' UTC')
  5963.                     if self.supports_mfmt:
  5964.                         mod_datetime = datetime.datetime.utcfromtimestamp(mod_date + self.remote_time_offset).strftime('%Y%m%d%H%M%S')
  5965.                         self.last_command = 'MFMT'
  5966.                         self.ftp.sendcmd('MFMT ' + self.encode(mod_datetime) + ' ' + self.encode(basename))
  5967.  
  5968.                 self.handle_ftp_error(do_put)
  5969.                 try:
  5970.                     chmod_error = False
  5971.                     if chmod_files:
  5972.                         self.chmod(basename, chmod_files, quiet=True)
  5973.                 except PermissionError:
  5974.                     chmod_error = True
  5975.  
  5976.                 if not quiet:
  5977.                     progress.stop('success')
  5978.                     progress.join()
  5979.                 if chmod_error:
  5980.                     self.printer.write('\nChmoding folder "%s" to "%s" ... failure (Permission denied)' % (remote_file, chmod_files), key='sftp_put_chmod', finish=True)
  5981.             except (PermissionError, AuthenticationError, NotFoundError) as e:
  5982.                 if not quiet:
  5983.                     progress.stop('failure (%s)' % str(e))
  5984.                     progress.join()
  5985.                 if not isinstance(e, (CancelledError, DisconnectionError)):
  5986.                     if single_file and not quiet:
  5987.                         self.printer.error(str(e))
  5988.                     error = True
  5989.                     continue
  5990.                 raise e
  5991.             except (OSError, ConnectionError) as e:
  5992.                 if not quiet:
  5993.                     progress.stop('failure (%s)' % str(e))
  5994.                     progress.join()
  5995.                 raise
  5996.             except UnicodeDecodeError:
  5997.                 if not quiet:
  5998.                     progress.stop('failure (Encoding error)')
  5999.                     progress.join
  6000.                 raise
  6001.  
  6002.         result = None
  6003.         if error and not quiet and not single_file:
  6004.             string = 'One or more errors occured while uploading files'
  6005.             result = string
  6006.             self.printer.write('\n' + string)
  6007.             self.printer.error(string)
  6008.         return [
  6009.          not error, result]
  6010.  
  6011.     def pwd(self):
  6012.         if self.dir is None:
  6013.  
  6014.             def do_pwd():
  6015.                 self.last_command = 'PWD'
  6016.                 self.dir = self.decode(self.ftp.pwd())
  6017.                 if self.ibm_ftp:
  6018.                     self.dir = '/' + self.dir.strip("'").replace('.', '/')
  6019.  
  6020.             self.handle_ftp_error(do_pwd)
  6021.             self.dir = canonicalize(self.dir, 'remote')
  6022.         return self.dir
  6023.  
  6024.     def lpwd(self):
  6025.         if self.local_dir is None:
  6026.             try:
  6027.                 self.local_dir = os.getcwd()
  6028.             except FileNotFoundError:
  6029.                 os.chdir(os.path.expanduser('~'))
  6030.                 self.local_dir = os.getcwd()
  6031.  
  6032.             self.local_dir = canonicalize(self.local_dir, 'local')
  6033.         return self.local_dir
  6034.  
  6035.     @keepaliveize
  6036.     def rm(self, remote_files, path_map, quiet=False, **kwargs):
  6037.         if not isinstance(remote_files, list):
  6038.             remote_files = [
  6039.              remote_files]
  6040.         error = False
  6041.         single_file = len(remote_files) == 1
  6042.         for remote_file in remote_files:
  6043.             file = remote_to_local(remote_file, path_map, self.remote_encoding)
  6044.             dir_error, cont = self.handle_rm_dirs(file, remote_file, single_file)
  6045.             error = error or dir_error
  6046.             if cont:
  6047.                 continue
  6048.             try:
  6049.                 if not quiet:
  6050.                     progress = ProgressThread(self.printer, '\nDeleting "%s"' % remote_file)
  6051.  
  6052.                 def do_rm():
  6053.                     if is_dir(remote_file):
  6054.                         file = remote_file.rstrip('/\\')
  6055.                         file = os.path.basename(file)
  6056.                         if self.ibm_ftp:
  6057.                             file = remote_file.strip('/').replace('/', '.')
  6058.                             file = "'%s'" % file
  6059.                         self.last_command = 'RMD'
  6060.                         self.ftp.rmd(self.encode(file))
  6061.                     else:
  6062.                         file = os.path.basename(remote_file)
  6063.                         if self.ibm_ftp:
  6064.                             data_set = dirname(remote_file).strip('/').replace('/', '.')
  6065.                             file = "'%s(%s)'" % (data_set, file)
  6066.                         self.last_command = 'DELE'
  6067.                         self.ftp.delete(self.encode(file))
  6068.  
  6069.                 self.handle_ftp_error(do_rm)
  6070.                 if not quiet:
  6071.                     progress.stop('success')
  6072.                     progress.join()
  6073.             except (OSError, PermissionError, ConnectionError, AuthenticationError, NotFoundError) as e:
  6074.                 if not quiet:
  6075.                     progress.stop('failure (%s)' % str(e))
  6076.                     progress.join()
  6077.                 if not isinstance(e, (CancelledError, DisconnectionError)):
  6078.                     if single_file and not quiet:
  6079.                         self.printer.error(str(e))
  6080.                     error = True
  6081.                     continue
  6082.                 raise e
  6083.             except UnicodeDecodeError:
  6084.                 if not quiet:
  6085.                     progress.stop('failure (Encoding error)')
  6086.                     progress.join
  6087.                 raise
  6088.  
  6089.         result = None
  6090.         if error and not quiet and not single_file:
  6091.             string = 'One or more errors occured while removing files'
  6092.             result = string
  6093.             self.printer.write('\n' + string)
  6094.             self.printer.error(string)
  6095.         return [
  6096.          not error, result]
  6097. # okay decompiling /home/manager/.config/sublime-text-3/Packages/SFTP/sftp/ftp_transport.pyc
  6098. # uncompyle6 version 3.2.3
  6099. # Python bytecode 3.3 (3230)
  6100. # Decompiled from: Python 2.7.12 (default, Dec  4 2017, 14:50:18)
  6101. # [GCC 5.4.0 20160609]
  6102. # Embedded file name: /Users/wbond/Library/Application Support/Sublime Text 3/Packages/SFTP/sftp/__init__.py
  6103. # Compiled at: 2015-11-05 00:16:24
  6104. pass
  6105. # okay decompiling /home/manager/.config/sublime-text-3/Packages/SFTP/sftp/__init__.pyc
  6106. # uncompyle6 version 3.2.3
  6107. # Python bytecode 3.3 (3230)
  6108. # Decompiled from: Python 2.7.12 (default, Dec  4 2017, 14:50:18)
  6109. # [GCC 5.4.0 20160609]
  6110. # Embedded file name: /Users/wbond/Library/Application Support/Sublime Text 3/Packages/SFTP/sftp/listeners.py
  6111. # Compiled at: 2015-11-05 00:18:34
  6112. # Size of source mod 2**32: 13010 bytes
  6113. import sublime, sublime_plugin, shutil, re, time, os
  6114. from .config import build_config, find_config, load_config, prepare_server_config, setup_tmp_dir
  6115. from .debug import debug_print
  6116. from .panel_printer import PanelPrinter
  6117. from .commands import SftpCommand
  6118. from .paths import dirname
  6119. from .views import get_all_views
  6120. from .threads import ThreadTracker, HookedThread
  6121.  
  6122. def find_window(callback, view):
  6123.     window = view.window()
  6124.     if window is None:
  6125.         windows = sublime.windows()
  6126.         for w in windows:
  6127.             for v in w.views():
  6128.                 if v.id() == view.id():
  6129.                     window = w
  6130.                     break
  6131.  
  6132.             if window:
  6133.                 break
  6134.  
  6135.         if not window:
  6136.             if view.size() == 0 and view.name() == '' and view.file_name() is None:
  6137.                 return
  6138.             sublime.set_timeout(lambda : find_window(callback, view), 200)
  6139.             return
  6140.     return callback(window)
  6141.  
  6142.  
  6143. class SftpCloseListener(sublime_plugin.EventListener):
  6144.  
  6145.     def on_close(self, view):
  6146.         if view is None or isinstance(view, bool):
  6147.             return
  6148.         else:
  6149.             view.erase_status('sftp_monitor')
  6150.             return
  6151.  
  6152.  
  6153. class SftpLoadListener(sublime_plugin.EventListener, SftpCommand):
  6154.  
  6155.     def on_load(self, view):
  6156.         if view is None or isinstance(view, bool):
  6157.             return
  6158.         else:
  6159.             settings = view.settings()
  6160.             if settings.get('is_widget'):
  6161.                 return
  6162.             was_synced = settings.get('synced')
  6163.             remote_loading = settings.get('remote_loading')
  6164.             settings.set('remote_loading', False)
  6165.             if not remote_loading:
  6166.                 settings.set('synced', False)
  6167.             if was_synced and not remote_loading:
  6168.                 settings.set('incomplete_sync', None)
  6169.                 DoSyncThread(self.extract_settings(settings), view, view.file_name()).start()
  6170.             return
  6171.  
  6172.     def on_modified(self, view):
  6173.         if view is None or isinstance(view, bool):
  6174.             return
  6175.         else:
  6176.             settings = view.settings()
  6177.             if settings.get('is_widget'):
  6178.                 return
  6179.             if not settings.get('incomplete_sync'):
  6180.                 return
  6181.             settings.set('incomplete_sync', None)
  6182.             if settings.get('synced'):
  6183.                 return
  6184.             DoSyncThread(self.extract_settings(settings), view, view.file_name()).start()
  6185.             return
  6186.  
  6187.     def on_activated(self, view):
  6188.         if view is None or isinstance(view, bool):
  6189.             return
  6190.         elif view.is_loading():
  6191.             return
  6192.         else:
  6193.             settings = view.settings()
  6194.             if settings.get('is_widget'):
  6195.                 return
  6196.  
  6197.             def try_sync(window):
  6198.                 ids = [v.id() for v in window.views()]
  6199.                 if view.id() not in ids:
  6200.                     settings.set('incomplete_sync', True)
  6201.                     return
  6202.                 else:
  6203.                     settings.set('incomplete_sync', None)
  6204.                     DoSyncThread(self.extract_settings(settings), view, view.file_name()).start()
  6205.                     return
  6206.  
  6207.             def do_find_window():
  6208.                 find_window(try_sync, view)
  6209.  
  6210.             if sublime.platform() == 'osx':
  6211.                 svn_settings = sublime.load_settings('SFTP.sublime-settings')
  6212.                 sync_down_on_open_delay = int(svn_settings.get('osx_sync_down_on_open_delay', 500))
  6213.                 sublime.set_timeout(do_find_window, sync_down_on_open_delay)
  6214.             else:
  6215.                 do_find_window()
  6216.             return
  6217.  
  6218.     def extract_settings(self, settings):
  6219.         output = {}
  6220.         for key in ['is_remote', 'tmp_dir', 'local_path', 'synced']:
  6221.             output[key] = settings.get(key)
  6222.  
  6223.         return output
  6224.  
  6225.  
  6226. class DoSyncThread(HookedThread):
  6227.  
  6228.     def __init__(self, settings, view, path):
  6229.         self.settings = settings
  6230.         self.view = view
  6231.         self.path = path
  6232.         super(DoSyncThread, self).__init__()
  6233.  
  6234.     def run(self):
  6235.         settings = self.settings
  6236.         view = self.view
  6237.         path = self.path
  6238.         has_config = bool(find_config(path, True))
  6239.         if settings.get('is_remote') and not has_config:
  6240.             tmp_dir = settings.get('tmp_dir')
  6241.             local_path = settings.get('local_path')
  6242.             try:
  6243.                 if os.name == 'nt' and len(tmp_dir) and tmp_dir == '/':
  6244.                     raise OSError('Switched platforms')
  6245.                 if os.name != 'nt' and len(tmp_dir) and re.match('^[a-z]:\\\\', tmp_dir, re.I) is not None:
  6246.                     raise OSError('Switched platforms')
  6247.                 if not local_path:
  6248.                     if not os.path.exists(tmp_dir):
  6249.                         os.makedirs(tmp_dir)
  6250.                     remote_name = os.path.basename(tmp_dir.rstrip('/\\'))
  6251.                     if remote_name == 'mapped':
  6252.                         return
  6253.                     raw_config = prepare_server_config(remote_name)
  6254.                     config_file = raw_config['file_path']
  6255.                 else:
  6256.                     raw_config, config_file = load_config(local_path)
  6257.             except (OSError, IOError):
  6258.                 raw_config = None
  6259.  
  6260.             if raw_config is None:
  6261.  
  6262.                 def show_error():
  6263.                     sublime.error_message('Sublime SFTP\n\nError loading config for remote file - please close and reopen the file')
  6264.                     view.settings().set('is_remote', None)
  6265.                     return
  6266.  
  6267.                 sublime.set_timeout(show_error, 1)
  6268.                 return
  6269.             tmp_dir = setup_tmp_dir(raw_config, tmp_dir)
  6270.             has_config = True
  6271.         if not has_config:
  6272.             return
  6273.         else:
  6274.             try:
  6275.                 config, config_file = load_config(path)
  6276.             except IOError:
  6277.                 config = None
  6278.  
  6279.             if config is None:
  6280.                 return
  6281.             config_dir = dirname(config_file)
  6282.             config = build_config(config, config_dir, config_file, True, skip_settings=True)
  6283.             if not config or not config.get('sync_down_on_open'):
  6284.                 return
  6285.             if 'ignore_regex' in config and re.search(config['ignore_regex'], path) is not None:
  6286.                 return
  6287.             if config.get('name') and not settings.get('is_remote'):
  6288.                 return
  6289.             if settings.get('synced'):
  6290.                 return
  6291.             params = {'paths': [
  6292.                        path],
  6293.              'ignore_delete': True,
  6294.              'on_complete': 'open_refresh',
  6295.              'reset_lcd': dirname(dirname(config.get('local_dir'))),
  6296.              'synchronous': True}
  6297.  
  6298.             def execute_sync():
  6299.                 if view.settings().get('synced'):
  6300.                     return
  6301.  
  6302.                 def do_real_sync(window):
  6303.                     debug_print('SFTP: Starting Sync Down on Open', 2)
  6304.                     window.run_command('sftp_sync_down', params)
  6305.  
  6306.                 find_window(do_real_sync, view)
  6307.                 view.settings().set('synced', True)
  6308.  
  6309.             sublime.set_timeout(execute_sync, 300)
  6310.             return
  6311.  
  6312.  
  6313. class DelayedDeleteThread(HookedThread):
  6314.  
  6315.     def __init__(self, tmp_dir, window_id):
  6316.         self.window_id = window_id
  6317.         self.tmp_dir = tmp_dir
  6318.         super(DelayedDeleteThread, self).__init__()
  6319.  
  6320.     def run(self):
  6321.         last_thread = ThreadTracker.get_last_added(self.window_id)
  6322.         if last_thread:
  6323.             last_thread.join()
  6324.         if os.path.exists(self.tmp_dir):
  6325.             try:
  6326.                 shutil.rmtree(self.tmp_dir)
  6327.             except WindowsError:
  6328.                 pass
  6329.  
  6330.  
  6331. class SftpAutoUploadListener(sublime_plugin.EventListener, SftpCommand):
  6332.  
  6333.     def on_close(self, view):
  6334.         if view is None or isinstance(view, bool):
  6335.             return
  6336.         else:
  6337.             settings = view.settings()
  6338.             if settings.get('is_remote') and settings.get('tmp_dir'):
  6339.                 tmp_dir = settings.get('tmp_dir')
  6340.                 for _view in get_all_views(sublime.active_window()):
  6341.                     if _view.id() == view.id():
  6342.                         continue
  6343.                     if _view.settings().get('tmp_dir') == tmp_dir:
  6344.                         return
  6345.  
  6346.                 DelayedDeleteThread(dirname(tmp_dir), sublime.active_window().id()).start()
  6347.             return
  6348.  
  6349.     def on_post_save(self, view):
  6350.         view_settings = view.settings()
  6351.         if view_settings.get('sftp_auto_save'):
  6352.             view_settings.erase('sftp_auto_save')
  6353.             return
  6354.  
  6355.         def do_upload():
  6356.             initial_dupes = view_settings.get('sftp_duplicate_views', 0) or 0
  6357.             if initial_dupes > 0:
  6358.                 if (view_settings.get('sftp_duplicate_views', 0) or 0) > 0:
  6359.                     view_settings.set('sftp_duplicate_views', view_settings.get('sftp_duplicate_views') - 1)
  6360.                     return
  6361.             else:
  6362.                 window = view.window()
  6363.                 if window is None:
  6364.                     window = sublime.active_window()
  6365.                 if window is not None:
  6366.                     for other_view in window.views():
  6367.                         if other_view.id() == view.id():
  6368.                             continue
  6369.                         if other_view.file_name() == view.file_name():
  6370.                             view_settings.set('sftp_duplicate_views', (view_settings.get('sftp_duplicate_views', 0) or 0) + 1)
  6371.                             continue
  6372.  
  6373.                 path = self.get_path(view=view)
  6374.                 if not path:
  6375.                     return
  6376.                 if not self.has_config(path):
  6377.                     return
  6378.                 quiet = True
  6379.                 if os.path.basename(view.file_name()) in (u'sftp-config.json', u'sftp-settings.json'):
  6380.                     quiet = False
  6381.                 config = self.get_config(path, quiet=quiet)
  6382.                 if not config or not config.get('upload_on_save'):
  6383.                     return
  6384.                 if config.get('ignore_regex') and re.search(config.get('ignore_regex'), path) is not None:
  6385.                     debug_print('SFTP: Ignoring file save event due to ignore_regex', 2)
  6386.                     return
  6387.                 params = {'paths': [path]}
  6388.                 if view_settings.get('is_remote') and view_settings.get('tmp_dir'):
  6389.                     params['reset_lcd'] = dirname(dirname(view_settings.get('tmp_dir')))
  6390.  
  6391.                 def execute_upload(window):
  6392.                     window.run_command('sftp_upload_file', params)
  6393.  
  6394.                 find_window(execute_upload, view)
  6395.                 return
  6396.  
  6397.         sublime.set_timeout(do_upload, 10)
  6398.  
  6399.  
  6400. class SftpAutoConnectListener(sublime_plugin.EventListener, SftpCommand):
  6401.  
  6402.     def on_post_save(self, view):
  6403.         view_settings = view.settings()
  6404.         if not view_settings.get('sftp_new_server'):
  6405.             return
  6406.         else:
  6407.             view_settings.set('sftp_new_server', None)
  6408.  
  6409.             def connect():
  6410.                 if view.window():
  6411.                     view.window().run_command('sftp_browse_server', {'name': os.path.basename(view.file_name())})
  6412.  
  6413.             sublime.set_timeout(connect, 500)
  6414.             return
  6415.  
  6416.  
  6417. class SftpFocusListener(sublime_plugin.EventListener, SftpCommand):
  6418.  
  6419.     def __init__(self):
  6420.         self.keys = []
  6421.         self.last_query = 0
  6422.  
  6423.     def check_keys(self, window):
  6424.         if window is None:
  6425.             return
  6426.         else:
  6427.             if self.keys[-1] == 'panel_visible':
  6428.                 PanelPrinter.get(window.id()).visible = False
  6429.             return
  6430.  
  6431.     def on_query_context(self, view, key, operator, operand, match_all):
  6432.         if view is None or isinstance(view, bool) or view.window() is None:
  6433.             return
  6434.         else:
  6435.             printer = PanelPrinter.get(view.window().id())
  6436.             if not hasattr(printer, 'panel') or not printer.panel:
  6437.                 return
  6438.             if time.time() <= self.last_query + 1:
  6439.                 self.keys.append(key)
  6440.             else:
  6441.                 sublime.set_timeout(lambda : self.check_keys(view.window()), 100)
  6442.                 self.keys = [key]
  6443.             self.last_query = time.time()
  6444.             return
  6445.  
  6446.     def on_modified(self, view):
  6447.         if view is None or view.window() is None:
  6448.             return
  6449.         else:
  6450.             printer = PanelPrinter.get(view.window().id())
  6451.             if not printer.visible:
  6452.                 return
  6453.             if hasattr(printer, 'panel') and printer.panel and printer.panel.id() == view.id():
  6454.                 printer.visible = True
  6455.                 return
  6456.             if view.settings().get('is_widget') and view.buffer_id() >= 3 and view.buffer_id() <= 19:
  6457.                 printer.visible = False
  6458.             return
  6459.  
  6460.     def on_activated(self, view):
  6461.         if view is None or view.window() is None:
  6462.             return
  6463.         else:
  6464.             printer = PanelPrinter.get(view.window().id())
  6465.             if not printer.visible:
  6466.                 return
  6467.             if hasattr(printer, 'panel') and printer.panel and printer.panel.id() == view.id():
  6468.                 printer.visible = True
  6469.                 return
  6470.             if view.settings().get('is_widget') and view.buffer_id() >= 3 and view.buffer_id() <= 19:
  6471.                 printer.visible = False
  6472.             return
  6473. # okay decompiling /home/manager/.config/sublime-text-3/Packages/SFTP/sftp/listeners.pyc
  6474. # uncompyle6 version 3.2.3
  6475. # Python bytecode 3.3 (3230)
  6476. # Decompiled from: Python 2.7.12 (default, Dec  4 2017, 14:50:18)
  6477. # [GCC 5.4.0 20160609]
  6478. # Embedded file name: /Users/wbond/Library/Application Support/Sublime Text 3/Packages/SFTP/sftp/panel_printer.py
  6479. # Compiled at: 2015-11-05 00:18:37
  6480. # Size of source mod 2**32: 7066 bytes
  6481. import sublime, os, random, time
  6482. try:
  6483.     str_cls = unicode
  6484.     st_version = 2
  6485. except NameError:
  6486.     str_cls = str
  6487.     st_version = 3
  6488.  
  6489. if os.name != 'nt':
  6490.     import unicodedata
  6491. from .threads import HookedThread, ThreadTracker
  6492.  
  6493. class PanelPrinter(object):
  6494.     printers = {}
  6495.  
  6496.     def __init__(self):
  6497.         self.name = 'sftp'
  6498.         self.visible = False
  6499.         self.hide_time = 0
  6500.         self.queue = []
  6501.         self.strings = {}
  6502.         self.just_error = False
  6503.         self.capture = False
  6504.         self.input = None
  6505.         self.input_start = None
  6506.         self.on_input_complete = None
  6507.         self.original_view = None
  6508.         return
  6509.  
  6510.     @classmethod
  6511.     def get(cls, window_id):
  6512.         printer = cls.printers.get(window_id)
  6513.         if not printer:
  6514.             printer = PanelPrinter()
  6515.             printer.window_id = window_id
  6516.             printer.init()
  6517.             cls.printers[window_id] = printer
  6518.         return printer
  6519.  
  6520.     def adjust_color_scheme(self):
  6521.         current_color_scheme = sublime.load_settings('Base File.sublime-settings').get('color_scheme')
  6522.         if current_color_scheme:
  6523.             current_color_scheme = current_color_scheme.replace('Packages/', '')
  6524.             sftp_color_scheme = current_color_scheme.replace('Color Scheme - Default/', 'SFTP/schemes/').replace('.tmTheme', '.sftpTheme')
  6525.             sftp_color_scheme_path = os.path.join(sublime.packages_path(), sftp_color_scheme)
  6526.             current_panel_scheme = self.panel.settings().get('color_scheme')
  6527.             if current_panel_scheme:
  6528.                 current_panel_scheme = current_panel_scheme.replace('Packages/', '')
  6529.             if os.path.exists(sftp_color_scheme_path):
  6530.                 if sftp_color_scheme != current_panel_scheme:
  6531.                     self.panel.settings().set('color_scheme', 'Packages/' + sftp_color_scheme)
  6532.                 if self.panel.settings().get('syntax') != 'Packages/SFTP/schemes/Output.hidden-tmLanguage':
  6533.                     self.panel.settings().set('syntax', 'Packages/SFTP/schemes/Output.hidden-tmLanguage')
  6534.                 return
  6535.             if current_color_scheme != current_panel_scheme:
  6536.                 self.panel.settings().set('color_scheme', 'Packages/' + current_color_scheme)
  6537.         if self.panel.settings().get('syntax') != 'Packages/SFTP/schemes/Custom Output.hidden-tmLanguage':
  6538.             self.panel.settings().set('syntax', 'Packages/SFTP/schemes/Custom Output.hidden-tmLanguage')
  6539.  
  6540.     def error(self, string):
  6541.         callback = lambda : self.error_callback(string)
  6542.         sublime.set_timeout(callback, 1)
  6543.  
  6544.     def error_callback(self, string):
  6545.         string = str(string)
  6546.         self.reset_hide()
  6547.         self.just_error = True
  6548.         sublime.error_message('Sublime SFTP\n\n' + string)
  6549.  
  6550.     def hide(self, thread=None):
  6551.         settings = sublime.load_settings('SFTP.sublime-settings')
  6552.         hide = settings.get('hide_output_panel', 1)
  6553.         try:
  6554.             if hide and not isinstance(hide, bool):
  6555.                 hide_time = time.time() + float(hide)
  6556.                 self.hide_time = hide_time
  6557.                 sublime.set_timeout(lambda : self.hide_callback(hide_time, thread), int(hide * 1000))
  6558.         except ValueError:
  6559.             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'
  6560.             sublime.set_timeout(lambda : sublime.error_message(message), 10)
  6561.  
  6562.     def hide_callback(self, hide_time, thread):
  6563.         if thread:
  6564.             last_added = ThreadTracker.get_last_added(self.window_id)
  6565.             if thread != last_added:
  6566.                 return
  6567.         if self.visible and self.hide_time and hide_time == self.hide_time:
  6568.             if not self.just_error:
  6569.                 self.window.run_command('hide_panel')
  6570.             self.just_error = False
  6571.  
  6572.     def init(self):
  6573.         if not hasattr(self, 'panel'):
  6574.             self.window = sublime.active_window()
  6575.             if st_version == 2:
  6576.                 self.panel = self.window.get_output_panel(self.name)
  6577.             else:
  6578.                 self.panel = self.window.create_output_panel(self.name)
  6579.             self.panel.set_read_only(True)
  6580.             self.panel.settings().set('syntax', 'Packages/SFTP/schemes/Output.hidden-tmLanguage')
  6581.             self.panel.settings().set('word_wrap', True)
  6582.             self.panel.settings().set('window_id', self.window.id())
  6583.             self.adjust_color_scheme()
  6584.             sublime.load_settings('Base File.sublime-settings').add_on_change('color_scheme', self.adjust_color_scheme)
  6585.  
  6586.     def reset_hide(self):
  6587.         self.hide_time = None
  6588.         return
  6589.  
  6590.     def show(self, force=False):
  6591.         self.init()
  6592.         settings = sublime.load_settings('SFTP.sublime-settings')
  6593.         hide = settings.get('hide_output_panel', 1)
  6594.         if force or hide is not True or not isinstance(hide, bool):
  6595.             self.visible = True
  6596.             self.window.run_command('show_panel', {'panel': 'output.' + self.name})
  6597.  
  6598.     def write(self, string, key='sublime_sftp', finish=False):
  6599.         if not len(string) and not finish:
  6600.             return
  6601.         else:
  6602.             if key not in self.strings:
  6603.                 self.strings[key] = []
  6604.                 self.queue.append(key)
  6605.             if len(string):
  6606.                 if not isinstance(string, str_cls):
  6607.                     string = str_cls(string, 'UTF-8', errors='strict')
  6608.                 if os.name != 'nt':
  6609.                     string = unicodedata.normalize('NFC', string)
  6610.                 self.strings[key].append(string)
  6611.             if finish:
  6612.                 self.strings[key].append(None)
  6613.             sublime.set_timeout(self.write_callback, 0)
  6614.             return key
  6615.  
  6616.     def write_callback(self):
  6617.         found = False
  6618.         for key in self.strings.keys():
  6619.             if len(self.strings[key]):
  6620.                 found = True
  6621.                 continue
  6622.  
  6623.         if not found:
  6624.             return
  6625.         self.panel.run_command('sftp_write_panel')
  6626.         size = self.panel.size()
  6627.         sublime.set_timeout(lambda : self.panel.show(size, True), 2)
  6628.  
  6629.  
  6630. class ProgressThread(HookedThread):
  6631.  
  6632.     def __init__(self, printer, beginning=''):
  6633.         self.printer = printer
  6634.         self.cont = True
  6635.         self.beginning = beginning
  6636.         self.ending = ''
  6637.         self.key = 'sublime_sftp_' + str(random.randint(1, 1000))
  6638.         self.printer.write(self.beginning + ' .', self.key)
  6639.         super(ProgressThread, self).__init__()
  6640.         self.start()
  6641.  
  6642.     def get_string(self):
  6643.         if self.cont:
  6644.             return '.'
  6645.         if self.ending:
  6646.             string = '. ' + self.ending
  6647.             self.ending = ''
  6648.             return string
  6649.         return ''
  6650.  
  6651.     def run(self):
  6652.         while self.cont:
  6653.             time.sleep(0.15)
  6654.             self.printer.write(self.get_string(), self.key)
  6655.  
  6656.         self.printer.write(self.get_string(), self.key, True)
  6657.  
  6658.     def stop(self, string):
  6659.         self.cont = False
  6660.         self.ending = string
  6661. # okay decompiling /home/manager/.config/sublime-text-3/Packages/SFTP/sftp/panel_printer.pyc
  6662. # uncompyle6 version 3.2.3
  6663. # Python bytecode 3.3 (3230)
  6664. # Decompiled from: Python 2.7.12 (default, Dec  4 2017, 14:50:18)
  6665. # [GCC 5.4.0 20160609]
  6666. # Embedded file name: /Users/wbond/Library/Application Support/Sublime Text 3/Packages/SFTP/sftp/paths.py
  6667. # Compiled at: 2015-11-05 00:18:40
  6668. # Size of source mod 2**32: 4730 bytes
  6669. import locale, re, os
  6670. if os.name != 'nt':
  6671.     import unicodedata
  6672. try:
  6673.     str_cls = unicode
  6674.     str_cls_name = 'unicode'
  6675. except NameError:
  6676.     str_cls = str
  6677.     str_cls_name = 'str'
  6678.  
  6679. def canonicalize(dir, _type):
  6680.     if _type == 'local' and os.name == 'nt':
  6681.         if dir[-1] != '\\':
  6682.             dir += '\\'
  6683.     else:
  6684.         if dir[-1] != '/':
  6685.             dir += '/'
  6686.     if os.name != 'nt':
  6687.         if type(dir).__name__ != str_cls_name:
  6688.             dir = str_cls(dir, 'utf-8', errors='strict')
  6689.         dir = unicodedata.normalize('NFC', dir)
  6690.     return dir
  6691.  
  6692.  
  6693. def local_to_remote(path, path_map, encoding=None):
  6694.     if type(path).__name__ != str_cls_name:
  6695.         try:
  6696.             path = str_cls(path, 'utf-8', errors='strict')
  6697.         except UnicodeDecodeError:
  6698.             if encoding == 'utf-8' or encoding is None:
  6699.                 encoding = locale.getpreferredencoding(do_setlocale=True)
  6700.             path = str_cls(path, encoding)
  6701.  
  6702.     if os.name != 'nt':
  6703.         path = unicodedata.normalize('NFC', path)
  6704.     key = list(path_map.keys())[0]
  6705.     local_prefix = canonicalize(key, 'local')
  6706.     remote_prefix = canonicalize(path_map[key], 'remote')
  6707.     if os.path.isdir(path):
  6708.         path = canonicalize(path, 'local')
  6709.     remote_path = re.sub('^' + re.escape(local_prefix), remote_prefix.replace('\\', '\\\\'), path)
  6710.     if os.name == 'nt':
  6711.         remote_path = remote_path.replace('\\', '/')
  6712.     if os.path.isdir(path):
  6713.         remote_path = canonicalize(remote_path, 'remote')
  6714.     if os.name != 'nt':
  6715.         return unicodedata.normalize('NFC', remote_path)
  6716.     else:
  6717.         return remote_path
  6718.  
  6719.  
  6720. def remote_to_local(remote_path, path_map, encoding=None):
  6721.     if type(remote_path).__name__ != str_cls_name:
  6722.         try:
  6723.             remote_path = str_cls(remote_path, 'utf-8', errors='strict')
  6724.         except UnicodeDecodeError:
  6725.             if encoding == 'utf-8' or encoding is None:
  6726.                 encoding = 'cp1252'
  6727.             remote_path = str_cls(remote_path, encoding)
  6728.  
  6729.     key = list(path_map.keys())[0]
  6730.     local_prefix = canonicalize(key, 'local')
  6731.     remote_prefix = canonicalize(path_map[key], 'remote')
  6732.     if remote_path[-1] == '/':
  6733.         remote_path = canonicalize(remote_path, 'remote')
  6734.     path = re.sub('^' + re.escape(remote_prefix), local_prefix.replace('\\', '\\\\'), remote_path)
  6735.     if os.name == 'nt':
  6736.         path = path.replace('/', '\\')
  6737.     if remote_path[-1] == '/':
  6738.         path = canonicalize(path, 'local')
  6739.     return path
  6740.  
  6741.  
  6742. def is_dir(path):
  6743.     return path[-1] in (u'/', u'\\')
  6744.  
  6745.  
  6746. def is_root(path):
  6747.     if path == '/':
  6748.         return True
  6749.     elif re.search('^[A-Za-z]:\\\\$', path) is not None:
  6750.         return True
  6751.     elif re.search('^\\\\\\\\[^\\\\]+\\\\?$', path) is not None:
  6752.         return True
  6753.     else:
  6754.         return False
  6755.  
  6756.  
  6757. def dirname(path):
  6758.     path = path.rstrip('/\\')
  6759.     path = re.sub('([/\\\\])[^/\\\\]+$', '\\1', path)
  6760.     return path
  6761.  
  6762.  
  6763. def path_type(path, capitalize=False):
  6764.     type_ = 'folder' if is_dir(path) else 'file'
  6765.     if capitalize:
  6766.         type_ = 'F' + type_[1:]
  6767.     return type_
  6768.  
  6769.  
  6770. def fix_windows_path(file):
  6771.     if os.name == 'nt':
  6772.         file = file.replace('\\', '/')
  6773.         file = re.sub('^([A-Za-z]):', '/\\1', file)
  6774.         file = file[0:2].upper() + file[2:]
  6775.     return file
  6776.  
  6777.  
  6778. def ignore_paths(paths, config):
  6779.     unignored = len(paths)
  6780.     ignored = 0
  6781.     if 'ignore_regex' in config and config['ignore_regex']:
  6782.         new_paths = []
  6783.         for path in paths:
  6784.             test_path = path[0] if isinstance(path, list) else path
  6785.             if re.search(config['ignore_regex'], test_path):
  6786.                 continue
  6787.             if test_path.replace('/', '\\') != test_path:
  6788.                 if re.search(config['ignore_regex'], test_path.replace('/', '\\')):
  6789.                     continue
  6790.             if test_path.replace('\\', '/') != test_path:
  6791.                 if re.search(config['ignore_regex'], test_path.replace('\\', '/')):
  6792.                     continue
  6793.             new_paths.append(path)
  6794.  
  6795.         paths = new_paths
  6796.         ignored = unignored - len(paths)
  6797.         unignored = len(paths)
  6798.     return (paths, unignored, ignored)
  6799.  
  6800.  
  6801. def ignore_rm_paths(paths, config, type):
  6802.     new_to_rm, num_to_rm, num_rm_ignored = ignore_paths(paths, config)
  6803.     rm_ignored = list(set(paths) - set(new_to_rm))
  6804.     for rm_path in rm_ignored:
  6805.         while not is_root(rm_path):
  6806.             try:
  6807.                 new_to_rm.remove(rm_path)
  6808.             except ValueError:
  6809.                 pass
  6810.  
  6811.             rm_path = canonicalize(dirname(rm_path), type)
  6812.  
  6813.     return (
  6814.      sorted(new_to_rm, key=lambda s: s.lower()), len(new_to_rm), len(paths) - len(new_to_rm))
  6815. # okay decompiling /home/manager/.config/sublime-text-3/Packages/SFTP/sftp/paths.pyc
  6816. # uncompyle6 version 3.2.3
  6817. # Python bytecode 3.3 (3230)
  6818. # Decompiled from: Python 2.7.12 (default, Dec  4 2017, 14:50:18)
  6819. # [GCC 5.4.0 20160609]
  6820. # Embedded file name: /Users/wbond/Library/Application Support/Sublime Text 3/Packages/SFTP/sftp/proc.py
  6821. # Compiled at: 2015-11-05 00:19:18
  6822. # Size of source mod 2**32: 9847 bytes
  6823. import sublime, os, subprocess, re, time, sys
  6824. try:
  6825.     str_cls = unicode
  6826. except NameError:
  6827.     str_cls = str
  6828.  
  6829. if os.name != 'nt':
  6830.     import pty, tty, select
  6831. from .debug import debug_print
  6832. from .errors import CancelledError, DisconnectionError
  6833.  
  6834. def find_binary(name):
  6835.     if os.name == 'nt':
  6836.         sftp_package_dir = os.path.join(sublime.packages_path(), 'SFTP', 'bin')
  6837.         dirs = [
  6838.          sftp_package_dir,
  6839.          'C:\\Program Files\\Git\\bin',
  6840.          'C:\\Program Files (x86)\\Git\\bin',
  6841.          'C:\\Program Files\\Mercurial',
  6842.          'C:\\Program Files (x86)\\Mercurial',
  6843.          'C:\\Program Files (x86)\\TortoiseHg',
  6844.          'C:\\Program Files\\TortoiseHg']
  6845.         dirs.extend(os.environ['PATH'].split(os.pathsep))
  6846.     else:
  6847.         dirs = os.environ['PATH'].split(os.pathsep)
  6848.     for dir in dirs:
  6849.         path = os.path.join(dir, name)
  6850.         if os.path.exists(path):
  6851.             return path
  6852.  
  6853.     return
  6854.  
  6855.  
  6856. class NonInteractiveProcess(object):
  6857.  
  6858.     def __init__(self, cwd, *args):
  6859.         self.args = args
  6860.         self.cwd = cwd
  6861.  
  6862.     def run(self):
  6863.         if os.name == 'nt' and not hasattr(NonInteractiveProcess, 'acp'):
  6864.             startupinfo = subprocess.STARTUPINFO()
  6865.             startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW
  6866.             proc = subprocess.Popen([
  6867.              'chcp'], stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, startupinfo=startupinfo, cwd=self.cwd, shell=True)
  6868.             stdout, stderr = proc.communicate()
  6869.             NonInteractiveProcess.acp = re.sub('^[^\\d]*(\\d+).*$', '\\1', stdout.decode('utf-8'))
  6870.         startupinfo = None
  6871.         if os.name == 'nt':
  6872.             startupinfo = subprocess.STARTUPINFO()
  6873.             startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW
  6874.         encoded_args = []
  6875.         encoding = sys.getfilesystemencoding()
  6876.         for arg in self.args:
  6877.             if sys.version_info >= (3, ):
  6878.                 encoded_args.append(arg)
  6879.             else:
  6880.                 encoded_args.append(arg.encode(encoding))
  6881.  
  6882.         proc = subprocess.Popen(encoded_args, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, startupinfo=startupinfo, cwd=self.cwd)
  6883.         output = proc.stdout.read()
  6884.         try:
  6885.             if os.name == 'nt':
  6886.                 output = str_cls(output, 'cp' + NonInteractiveProcess.acp, errors='strict')
  6887.             else:
  6888.                 output = str_cls(output, 'utf-8', errors='strict')
  6889.         except UnicodeDecodeError:
  6890.             output = str_cls(output, 'cp1252')
  6891.  
  6892.         return output.replace('\r\n', '\n').rstrip(' \n\r')
  6893.  
  6894.  
  6895. class InteractiveProcess(object):
  6896.  
  6897.     def __init__(self, type, *args):
  6898.         self.args = args
  6899.         self.proc = None
  6900.         self.returncode = None
  6901.         self.opened = False
  6902.         self.closed = False
  6903.         self.debug = False
  6904.         self.open()
  6905.         return
  6906.  
  6907.     def open(self):
  6908.         if self.opened:
  6909.             return
  6910.         encoded_args = []
  6911.         encoding = sys.getfilesystemencoding()
  6912.         debug_print('SFTP Connection Args: ' + repr(self.args), 2)
  6913.         for arg in self.args:
  6914.             if sys.version_info >= (3, ):
  6915.                 encoded_args.append(arg)
  6916.             else:
  6917.                 encoded_args.append(arg.encode(encoding))
  6918.  
  6919.         if os.name == 'nt':
  6920.             startupinfo = subprocess.STARTUPINFO()
  6921.             startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW
  6922.             self.proc = subprocess.Popen(encoded_args, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, startupinfo=startupinfo)
  6923.             self.stdout = self.proc.stdout.fileno()
  6924.             self.stdin = self.proc.stdin.fileno()
  6925.         else:
  6926.             self.proc, child_fd = pty.fork()
  6927.             if not self.proc:
  6928.                 os.execv(encoded_args[0], list(encoded_args))
  6929.             self.stdout = child_fd
  6930.             self.stdin = child_fd
  6931.         self.opened = True
  6932.  
  6933.     def write(self, command, encoding='utf-8'):
  6934.         self.open()
  6935.         ending = '\r\n' if os.name == 'nt' else '\n'
  6936.         debug_print('SFTP Write:' + ending + '    ' + command)
  6937.         try:
  6938.             os.write(self.stdin, command.encode(encoding) + ending.encode(encoding))
  6939.         except Exception:
  6940.             if self.closed:
  6941.                 raise CancelledError('Cancelled')
  6942.             else:
  6943.                 raise DisconnectionError('Disconnected')
  6944.  
  6945.     def read(self, until='sftp>\\s?$', timeout=1, encoding='utf-8', remove_prompt=True):
  6946.         self.open()
  6947.         output = ''
  6948.         start = time.time()
  6949.         try:
  6950.             while 1:
  6951.                 if timeout and output == '' and time.time() > start + timeout:
  6952.                     break
  6953.                 if os.name != 'nt' and self.proc:
  6954.                     pid, exit_status = os.waitpid(self.proc, os.WNOHANG)
  6955.                     if pid != 0:
  6956.                         if self.closed:
  6957.                             e = CancelledError('Cancelled')
  6958.                         else:
  6959.                             e = DisconnectionError('Disconnected')
  6960.                         e.returncode = exit_status >> 8
  6961.                         raise e
  6962.                 if os.name != 'nt' and self.proc is None:
  6963.                     if self.closed:
  6964.                         raise CancelledError('Cancelled')
  6965.                     else:
  6966.                         raise DisconnectionError('Disconnected')
  6967.                 else:
  6968.                     if os.name == 'nt' and (self.proc is None or self.proc.poll() is not None):
  6969.                         if self.closed:
  6970.                             raise CancelledError('Cancelled')
  6971.                         else:
  6972.                             raise DisconnectionError('Disconnected')
  6973.                 output += str_cls(os.read(self.stdout, 32768), encoding, errors='strict')
  6974.                 if not until or re.findall(until, output):
  6975.                     break
  6976.  
  6977.             if output.find('\x07') != -1:
  6978.                 self.close()
  6979.  
  6980.                 def error_message():
  6981.                     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.')
  6982.  
  6983.                 sublime.set_timeout(error_message, 1)
  6984.                 raise CancelledError('Cancelled')
  6985.             if self.debug:
  6986.                 self.print_read(output)
  6987.         except OSError as e:
  6988.             if self.debug:
  6989.                 self.print_read(output)
  6990.             if os.name != 'nt' and not hasattr(e, 'returncode') and not self.returncode:
  6991.                 pid, bytes = os.wait()
  6992.             if not isinstance(e, (CancelledError, DisconnectionError)):
  6993.                 if self.closed:
  6994.                     e = CancelledError('Cancelled')
  6995.                 else:
  6996.                     e = DisconnectionError('Disconnected')
  6997.             raise e
  6998.  
  6999.         output = output.replace('\r\n', '\n')
  7000.         output = re.sub('.\x08', '', output)
  7001.         if remove_prompt:
  7002.             output = re.sub('\\s+p?sftp>\\s*$', '', output)
  7003.         return output.strip()
  7004.  
  7005.     def print_read(self, string):
  7006.         output = 'SFTP Read:\n'
  7007.         for line in re.sub('\n+', '\n', string.replace('\r', '\n')).split('\n'):
  7008.             output += '    ' + line + '\n'
  7009.  
  7010.         debug_print(output)
  7011.  
  7012.     def close(self, try_num=0):
  7013.         if not self.proc:
  7014.             return
  7015.         else:
  7016.             self.closed = True
  7017.             if type(self.proc).__name__ == 'int':
  7018.                 try:
  7019.                     if os is None:
  7020.                         return
  7021.                     os.kill(self.proc, 15)
  7022.                     try:
  7023.                         os.close(self.stdout)
  7024.                     except OSError:
  7025.                         pass
  7026.  
  7027.                     pid, exit_status = os.waitpid(self.proc, os.WNOHANG)
  7028.                     if exit_status:
  7029.                         self.returncode = 1
  7030.                     os.kill(self.proc, 0)
  7031.                     if try_num > 4:
  7032.                         os.kill(self.proc, 9)
  7033.                         os.kill(self.proc, 0)
  7034.                     if try_num < 10:
  7035.                         sublime.set_timeout(lambda : self.close(try_num + 1), 20)
  7036.                     return
  7037.                 except TypeError:
  7038.                     pass
  7039.                 except OSError:
  7040.                     pass
  7041.  
  7042.                 self.proc = None
  7043.             else:
  7044.                 if self.proc.returncode is None:
  7045.                     try:
  7046.                         self.proc.kill()
  7047.                         if self.proc.returncode is None and try_num < 10:
  7048.                             sublime.set_timeout(lambda : self.close(try_num + 1), 20)
  7049.                     except WindowsError:
  7050.                         pass
  7051.  
  7052.             return
  7053. # okay decompiling /home/manager/.config/sublime-text-3/Packages/SFTP/sftp/proc.pyc
  7054. # uncompyle6 version 3.2.3
  7055. # Python bytecode 3.3 (3230)
  7056. # Decompiled from: Python 2.7.12 (default, Dec  4 2017, 14:50:18)
  7057. # [GCC 5.4.0 20160609]
  7058. # Embedded file name: /Users/wbond/Library/Application Support/Sublime Text 3/Packages/SFTP/sftp/secure_input.py
  7059. # Compiled at: 2015-11-05 00:19:23
  7060. # Size of source mod 2**32: 1890 bytes
  7061. import sublime, time
  7062. from .threads import HookedThread
  7063.  
  7064. class SecureInput(object):
  7065.  
  7066.     def __init__(self, prompt, on_done, on_cancel):
  7067.         self.prompt = prompt
  7068.         self.on_done_callback = on_done
  7069.         self.on_cancel = on_cancel
  7070.         self.input_view = None
  7071.         self.input = ''
  7072.         self.length = 0
  7073.         sublime.set_timeout(self.show_input, 1)
  7074.         sublime.set_timeout(self.on_cancel, 30000)
  7075.         return
  7076.  
  7077.     def capture_char(self, string):
  7078.         if len(string) == self.length:
  7079.             return
  7080.         if len(string) < self.length:
  7081.             self.input = self.input[0:len(string)]
  7082.             self.length = len(string)
  7083.             return
  7084.         if not self.input_view:
  7085.             return
  7086.         new_length = len(string) - len(self.input)
  7087.         self.input += string[len(self.input):]
  7088.         self.length = len(string)
  7089.         pos = len(string) - new_length
  7090.         self.input_view.run_command('sftp_replace_view', {'start': pos,
  7091.          'end': pos + new_length,
  7092.          'string': '*' * new_length})
  7093.  
  7094.     def on_done(self, string):
  7095.         self.on_done_callback(self.input)
  7096.  
  7097.     def show_input(self):
  7098.         self.input_view = sublime.active_window().show_input_panel(self.prompt, '', self.on_done, self.capture_char, self.on_cancel)
  7099.  
  7100.  
  7101. class SecureInputThread(HookedThread):
  7102.  
  7103.     def __init__(self, prompt):
  7104.         self.prompt = prompt
  7105.         self.password = False
  7106.         super(SecureInputThread, self).__init__()
  7107.  
  7108.     def run(self):
  7109.         SecureInput(self.prompt, self.get_password, self.cancel_password)
  7110.         while self.password is False:
  7111.             time.sleep(0.01)
  7112.  
  7113.     def get_password(self, password):
  7114.         self.password = password
  7115.  
  7116.     def cancel_password(self):
  7117.         self.password = None
  7118.         return
  7119. # okay decompiling /home/manager/.config/sublime-text-3/Packages/SFTP/sftp/secure_input.pyc
  7120. # uncompyle6 version 3.2.3
  7121. # Python bytecode 3.3 (3230)
  7122. # Decompiled from: Python 2.7.12 (default, Dec  4 2017, 14:50:18)
  7123. # [GCC 5.4.0 20160609]
  7124. # Embedded file name: /Users/wbond/Library/Application Support/Sublime Text 3/Packages/SFTP/sftp/sftp_transport.py
  7125. # Compiled at: 2015-11-10 11:29:17
  7126. # Size of source mod 2**32: 33185 bytes
  7127. import sublime, os, re, traceback, locale, time
  7128. if os.name == 'nt':
  7129.     import ctypes
  7130. try:
  7131.     str_cls = unicode
  7132. except NameError:
  7133.     str_cls = str
  7134.  
  7135. from .file_transfer import FileTransfer, keepaliveize
  7136. from .panel_printer import ProgressThread
  7137. from .proc import InteractiveProcess
  7138. from .paths import canonicalize, dirname, is_dir, local_to_remote, path_type, remote_to_local
  7139. from .debug import get_debug, debug_print
  7140. from .errors import AuthenticationError, BinaryMissingError, CancelledError, ConnectionError, DisconnectionError, handle_exception, NotFoundError, PermissionError
  7141. from .secure_input import SecureInput
  7142.  
  7143. class SFTP(FileTransfer):
  7144.  
  7145.     def __init__(self, printer, user=None, host=None, port=22, password=None, remote_time_offset=None, **kwargs):
  7146.         self.closed = False
  7147.         super(SFTP, self).__init__(printer, user, host, port, password, remote_time_offset, **kwargs)
  7148.         args = [
  7149.          kwargs['binary']]
  7150.         if os.name == 'nt':
  7151.             self.type = 'psftp'
  7152.             args.extend(['-P', str(port)])
  7153.             if password is not None:
  7154.                 args.extend(['-pw', str(password)])
  7155.         else:
  7156.             timeout = int(kwargs['timeout'])
  7157.             alive_interval = timeout
  7158.             if kwargs.get('keepalive'):
  7159.                 keepalive = int(kwargs['keepalive'])
  7160.                 if keepalive < alive_interval:
  7161.                     alive_interval = keepalive
  7162.             self.type = 'sftp'
  7163.             args.append('-C')
  7164.             args.append('-oPort=%s' % port)
  7165.             args.append('-oConnectTimeout=%s' % str(timeout))
  7166.             args.append('-oServerAliveCountMax=1')
  7167.             args.append('-oServerAliveInterval=%s' % str(alive_interval))
  7168.             args.append('-oTCPKeepAlive=no')
  7169.         if kwargs.get('ssh_key_file'):
  7170.             if os.name == 'nt':
  7171.                 args.extend(['-i', kwargs['ssh_key_file']])
  7172.             else:
  7173.                 args.append('-oIdentityFile=' + kwargs['ssh_key_file'])
  7174.         if kwargs.get('sftp_flags'):
  7175.             if isinstance(kwargs['sftp_flags'], list):
  7176.                 args.extend(kwargs['sftp_flags'])
  7177.             else:
  7178.                 args.append(kwargs['sftp_flags'])
  7179.         args.append('%s@%s' % (user, host))
  7180.         self.args = args
  7181.         self.kwargs = kwargs
  7182.         return
  7183.  
  7184.     def debug(self, debug):
  7185.         self.proc.debug = debug
  7186.  
  7187.     def do_keepalive(self):
  7188.         debug_print('SFTP: Doing keepalive', 2)
  7189.         self.proc.write('cd .', self.remote_encoding)
  7190.         self.proc.read(encoding=self.remote_encoding)
  7191.  
  7192.     def connect(self, quiet=False):
  7193.         if not quiet:
  7194.             progress = ProgressThread(self.printer, '\nConnecting to SFTP server "%s" as "%s"' % (self.host, self.user))
  7195.         if self.args[0] is None:
  7196.             if os.name == 'nt':
  7197.                 message = 'psftp.exe could not be found'
  7198.             else:
  7199.                 message = 'sftp command line executable could not be found'
  7200.             progress.stop('failure (%s)' % message)
  7201.             progress.join()
  7202.             raise BinaryMissingError(message)
  7203.         try:
  7204.             if os.name == 'nt':
  7205.  
  7206.                 def connect_timeout():
  7207.                     if not self.connected and self.proc.proc is not None:
  7208.                         ctypes.windll.kernel32.TerminateProcess(int(self.proc.proc._handle), -1)
  7209.                     return
  7210.  
  7211.                 sublime.set_timeout(connect_timeout, int(self.timeout) * 1000)
  7212.             self.proc = InteractiveProcess(self.type, *self.args)
  7213.             self.debug(get_debug())
  7214.             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)
  7215.             self.connected = True
  7216.             if result == '' or re.search('Broken pipe', result):
  7217.                 if not quiet:
  7218.                     progress.stop('failure (Connection timeout)')
  7219.                     progress.join()
  7220.                 raise ConnectionError('Connection timeout')
  7221.             if re.search('UNPROTECTED PRIVATE KEY FILE', result):
  7222.                 if not quiet:
  7223.                     progress.stop('failure (Unprotected private key file)')
  7224.                     progress.join()
  7225.                 command = (' ').join(self.args)
  7226.                 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
  7227.                 sublime.set_timeout(lambda : self.printer.write(message), 200)
  7228.                 raise ConnectionError('Unprotected private key file')
  7229.             if re.search('Host key verification failed|Update cached key\\?', result):
  7230.                 if not quiet:
  7231.                     progress.stop('failure (Host key verification failed)')
  7232.                     progress.join()
  7233.                 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'
  7234.                 if os.name == 'nt':
  7235.                     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' % (
  7236.                      str(self.port), self.host)
  7237.                 else:
  7238.                     known_hosts_file = os.path.expanduser('~/.ssh/known_hosts')
  7239.                     if str(self.port) == '22':
  7240.                         host_file_beginning = self.host
  7241.                     else:
  7242.                         host_file_beginning = '[' + self.host + ']:' + str(self.port)
  7243.                     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'
  7244.                 sublime.set_timeout(lambda : self.printer.write(message), 200)
  7245.                 raise ConnectionError('Host key verification failed')
  7246.             if re.search('Connection (refused|reset)', result):
  7247.                 if not quiet:
  7248.                     progress.stop('failure (Connection refused)')
  7249.                     progress.join()
  7250.                 raise ConnectionError('Connection refused')
  7251.             if re.search('No route to host', result):
  7252.                 if not quiet:
  7253.                     progress.stop('failure (No route to host)')
  7254.                     progress.join()
  7255.                 raise ConnectionError('No route to host')
  7256.             if re.search('Host does not exist|Could not resolve hostname', result) is not None:
  7257.                 if not quiet:
  7258.                     progress.stop('failure (Host does not exist)')
  7259.                     progress.join()
  7260.                 raise ConnectionError('Host does not exist')
  7261.             if os.name != 'nt' and re.search('continue connecting', result) is not None:
  7262.                 self.proc.write('yes')
  7263.                 result = self.proc.read('sftp>|[pP]assword:|PASSWORD:|[pP]assphrase for key', remove_prompt=False)
  7264.             if os.name == 'nt' and re.search('Store key in cache', result) is not None:
  7265.                 self.proc.write('y')
  7266.                 result = self.proc.read('sftp>|[pP]assword:|PASSWORD:|Password for [^@]+@[^:]+:|[pP]assphrase for key', remove_prompt=False)
  7267.             if os.name != 'nt' and self.password is not None and re.search('([pP]assword|PASSWORD):', result) is not None:
  7268.                 self.proc.write(self.password)
  7269.                 result = self.proc.read('sftp>|[pP]assword:|PASSWORD:', remove_prompt=False)
  7270.             was_visible = self.printer.visible
  7271.             password_state = {'cancelled': False,  'tries': 0,  'success': False}
  7272.  
  7273.             def cancel_password():
  7274.                 if password_state['success']:
  7275.                     return
  7276.                 try:
  7277.                     self.proc.write('')
  7278.                 except OSError:
  7279.                     pass
  7280.  
  7281.                 if was_visible:
  7282.                     self.printer.show()
  7283.                 password_state['cancelled'] = True
  7284.  
  7285.             def write_password(password):
  7286.                 password_state['tries'] += 1
  7287.                 if password_state['tries'] > 2:
  7288.                     password_state['cancelled'] = True
  7289.                 try:
  7290.                     self.proc.write(password)
  7291.                 except OSError:
  7292.                     pass
  7293.  
  7294.                 if was_visible:
  7295.                     self.printer.show()
  7296.  
  7297.             pass_prompt_regex = '([pP]assword|PASSWORD|Password for [^@]+@[^:]+):\\Z'
  7298.             while 1:
  7299.                 if re.search('[Pp]assphrase for key [\'"](.*)[\'"]:', result) is not None and not password_state['cancelled']:
  7300.                     match = re.search('[Pp](assphrase for key [\'"].*[\'"]):', result)
  7301.                     SecureInput('P%s' % match.group(1), write_password, cancel_password)
  7302.                     result = self.proc.read('sftp>|[pP]assword:|PASSWORD:|Password for [^@]+@[^:]+:|[pP]assphrase for key|[Pp]ermission denied', remove_prompt=False)
  7303.  
  7304.             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']:
  7305.                 message = 'Invalid SSH key passphrase specified'
  7306.                 if not quiet:
  7307.                     progress.stop('failure (%s)' % message)
  7308.                     progress.join()
  7309.                 raise AuthenticationError(message)
  7310.             while 1:
  7311.                 if re.search(pass_prompt_regex, result) is not None and not password_state['cancelled']:
  7312.                     SecureInput("%s@%s's password" % (self.user, self.host), write_password, cancel_password)
  7313.                     result = self.proc.read('sftp>|[pP]assword:|PASSWORD:|Password for [^@]+@[^:]+:|[Pp]ermission denied', remove_prompt=False)
  7314.  
  7315.             if re.search(pass_prompt_regex, result) is not None:
  7316.                 message = 'Invalid login/password specified'
  7317.                 if not quiet:
  7318.                     progress.stop('failure (%s)' % message)
  7319.                     progress.join()
  7320.                 raise AuthenticationError(message)
  7321.             if re.search('[Pp]ermission denied', result) is not None:
  7322.                 message = 'Invalid login credentials'
  7323.                 if not quiet:
  7324.                     progress.stop('failure (%s)' % message)
  7325.                     progress.join()
  7326.                 raise AuthenticationError(message)
  7327.             password_state['success'] = True
  7328.         except AuthenticationError as e:
  7329.             raise e
  7330.         except OSError as e:
  7331.             if isinstance(e, DisconnectionError):
  7332.                 error = 'Connection timeout'
  7333.                 e = ConnectionError(error)
  7334.             else:
  7335.                 if isinstance(e, CancelledError):
  7336.                     error = 'Unknown error'
  7337.                     backtrace = traceback.format_exc()
  7338.                     handle_exception('Unknown Error', backtrace)
  7339.                 else:
  7340.                     error = str(e)
  7341.                 if not quiet:
  7342.                     progress.stop('failure (' + error + ')')
  7343.                     progress.join()
  7344.                 raise e
  7345.         except UnicodeDecodeError:
  7346.             if not quiet:
  7347.                 progress.stop('failure (Encoding error)')
  7348.                 progress.join()
  7349.             raise
  7350.  
  7351.         if re.search('sftp>', result) is None:
  7352.             result = self.proc.read(remove_prompt=False)
  7353.         if not quiet:
  7354.             progress.stop('success')
  7355.             progress.join()
  7356.         if os.name != 'nt':
  7357.             self.proc.write('progress off')
  7358.             self.proc.read()
  7359.         self.pwd()
  7360.         self.lpwd()
  7361.         return
  7362.  
  7363.     def close(self, disconnected=False):
  7364.         self.proc.close()
  7365.  
  7366.     @keepaliveize
  7367.     def chmod(self, file, mode=None, quiet=False, **kwargs):
  7368.         if not quiet:
  7369.             progress = ProgressThread(self.printer, '\nChmoding "%s" to "%s"' % (file, mode))
  7370.         try:
  7371.             self.proc.write('chmod %s "%s"' % (mode, file), self.remote_encoding)
  7372.             result = self.proc.read(encoding=self.remote_encoding)
  7373.             if re.search('no such file or directory', result, re.I) is not None:
  7374.                 raise NotFoundError('Folder not found')
  7375.             if re.search('permission denied', result, re.I) is not None:
  7376.                 raise PermissionError('Permission denied')
  7377.             if not quiet:
  7378.                 progress.stop('success')
  7379.                 progress.join()
  7380.         except (NotFoundError, PermissionError) as e:
  7381.             if not quiet:
  7382.                 progress.stop('failure (%s)' % str(e))
  7383.                 progress.join()
  7384.             return [False, str(e)]
  7385.         except UnicodeDecodeError:
  7386.             if not quiet:
  7387.                 progress.stop('failure (Encoding error)')
  7388.                 progress.join()
  7389.             raise
  7390.  
  7391.         return [True, None]
  7392.  
  7393.     def cd(self, dir):
  7394.         dir = self.make_absolute_dir(dir, 'remote')
  7395.         if dir == self.dir:
  7396.             return
  7397.         else:
  7398.             self.proc.write('cd "%s"' % dir, self.remote_encoding)
  7399.             result = self.proc.read(encoding=self.remote_encoding)
  7400.             if re.search('no such file or directory', result, re.I) is not None:
  7401.                 raise NotFoundError('Folder not found')
  7402.             if re.search('permission denied', result, re.I) is not None:
  7403.                 raise PermissionError('Permission denied')
  7404.             self.dir = dir
  7405.             return
  7406.  
  7407.     def lcd(self, dir):
  7408.         dir = self.make_absolute_dir(dir, 'local')
  7409.         if self.local_dir == dir:
  7410.             return
  7411.         else:
  7412.             if not os.path.exists(dir):
  7413.                 raise NotFoundError('Folder not found')
  7414.             if not os.access(dir, os.X_OK):
  7415.                 raise PermissionError('Permission denied')
  7416.             encoding = 'utf-8'
  7417.             if os.name == 'nt':
  7418.                 encoding = locale.getpreferredencoding(do_setlocale=True)
  7419.             self.proc.write('lcd "%s"' % dir, encoding)
  7420.             result = self.proc.read(encoding=encoding)
  7421.             if re.search('(no such file or directory|cannot find the (file|path) specified\\.)', result, re.I) is not None:
  7422.                 raise NotFoundError('Folder not found')
  7423.             self.local_dir = dir
  7424.             return
  7425.  
  7426.     @keepaliveize
  7427.     def get(self, remote_files, path_map, quiet=False, **kwargs):
  7428.         if not isinstance(remote_files, list):
  7429.             remote_files = [
  7430.              remote_files]
  7431.         error = False
  7432.         single_file = len(remote_files) == 1
  7433.         list_cache = {}
  7434.         for remote_file in remote_files:
  7435.             file = remote_to_local(remote_file, path_map, self.remote_encoding)
  7436.             try:
  7437.                 dir_error, cont = self.handle_get_dirs(file, remote_file, single_file)
  7438.                 error = error or dir_error
  7439.                 if cont:
  7440.                     continue
  7441.             except OSError as e:
  7442.                 if not quiet:
  7443.                     self.printer.write('\nDownloading "%s" to "%s" ... failure (%s)' % (remote_file, file, str(e)))
  7444.                 raise e
  7445.  
  7446.             if not quiet:
  7447.                 progress = ProgressThread(self.printer, '\nDownloading "%s" to "%s"' % (remote_file, file))
  7448.             try:
  7449.                 basename = os.path.basename(file)
  7450.                 if os.name == 'nt':
  7451.                     local_encoding = locale.getpreferredencoding(do_setlocale=True)
  7452.                     remote_basename = str_cls(basename.encode(self.remote_encoding), local_encoding)
  7453.                     self.proc.write('get "%s" "%s"' % (remote_basename, basename), local_encoding)
  7454.                     result = self.proc.read(encoding=local_encoding)
  7455.                 else:
  7456.                     self.proc.write('get "%s"' % basename, self.remote_encoding)
  7457.                     result = self.proc.read(encoding=self.remote_encoding)
  7458.                 if re.search('permission denied', result, re.I) is not None:
  7459.                     raise PermissionError('Permission denied')
  7460.                 if re.search('(no such file or directory|cannot find the file specified)', result, re.I) is not None:
  7461.                     raise NotFoundError('File not found')
  7462.                 if self.preserve_modification_times:
  7463.                     if self.dir not in list_cache:
  7464.                         success, result = self.list(self.dir, path_map, quiet=True, skip_symlinks=False)
  7465.                         if success:
  7466.                             list_cache[self.dir] = dict(result)
  7467.                         else:
  7468.                             list_cache[self.dir] = None
  7469.                     if isinstance(list_cache[self.dir], dict):
  7470.                         atime = list_cache[self.dir][basename]
  7471.                         mtime = atime
  7472.                         os.utime(file, (atime, mtime))
  7473.                 if not quiet:
  7474.                     progress.stop('success')
  7475.                     progress.join()
  7476.             except (PermissionError, NotFoundError) as e:
  7477.                 if not quiet:
  7478.                     progress.stop('failure (%s)' % str(e))
  7479.                     progress.join()
  7480.                 if single_file and not quiet:
  7481.                     self.printer.error(e)
  7482.                 error = True
  7483.                 continue
  7484.             except OSError as e:
  7485.                 if not quiet:
  7486.                     progress.stop('failure (%s)' % str(e))
  7487.                     progress.join()
  7488.                 raise e
  7489.             except UnicodeDecodeError:
  7490.                 if not quiet:
  7491.                     progress.stop('failure (Encoding error)')
  7492.                     progress.join()
  7493.                 raise
  7494.  
  7495.         result = None
  7496.         if error and not quiet and not single_file:
  7497.             string = 'One or more errors occured while downloading files'
  7498.             result = string
  7499.             self.printer.write('\n' + string)
  7500.             self.printer.error(string)
  7501.         return [
  7502.          not error, result]
  7503.  
  7504.     def ls(self, path_map, include_self=True, config=None, skip_symlinks=True):
  7505.         offset = self.determine_time_offset(path_map, config)
  7506.         self.proc.write('ls' if os.name == 'nt' else 'ls -la')
  7507.         result = self.proc.read(encoding=self.remote_encoding)
  7508.         if re.search('permission denied', result, re.I) is not None:
  7509.             raise PermissionError('Permission denied')
  7510.         if re.search('no such file or directory|cannot find the file specified', result, re.I) is not None:
  7511.             raise NotFoundError('File not found')
  7512.         entries = result.split('\n')[1:]
  7513.         files = self.parse_ls(entries, offset, include_self, skip_symlinks)
  7514.         if include_self:
  7515.             found_cur_dir = False
  7516.             if files:
  7517.                 for file_ in files:
  7518.                     if file_[0] == '.':
  7519.                         found_cur_dir = True
  7520.                         break
  7521.  
  7522.             if not found_cur_dir:
  7523.                 timestamp = int(time.time())
  7524.                 files.insert(0, ['.', timestamp])
  7525.         files = sorted(files, key=lambda ar: ar[0].lower())
  7526.         return files
  7527.  
  7528.     def check_symlink(self, basename):
  7529.         if os.name == 'nt':
  7530.             command = 'ls %s' % basename
  7531.         else:
  7532.             command = 'ls -la %s' % basename
  7533.         self.proc.write(command, self.remote_encoding)
  7534.         result = self.proc.read(encoding=self.remote_encoding)
  7535.         if re.search('permission denied', result, re.I) is not None:
  7536.             return canonicalize(basename, 'remote')
  7537.         elif os.name == 'nt':
  7538.             if re.search('no such file', result, re.I) is not None:
  7539.                 return basename
  7540.             return canonicalize(basename, 'remote')
  7541.         lines = result.split('\n')[1:]
  7542.         if len(lines) == 1:
  7543.             return basename
  7544.         else:
  7545.             return canonicalize(basename, 'remote')
  7546.  
  7547.     @keepaliveize
  7548.     def mkdir(self, dir, chmod_dirs=None, **kwargs):
  7549.         try:
  7550.             self.cd(dir)
  7551.             self.printer.write('\nFolder "%s" already exists' % dir, key='sftp_mkdir', finish=True)
  7552.             if chmod_dirs:
  7553.                 try:
  7554.                     self.chmod(dir, chmod_dirs, quiet=True)
  7555.                 except PermissionError:
  7556.                     self.printer.write('\nChmoding folder "%s" to "%s" ... failure (Permission denied)' % (dir, chmod_dirs), key='sftp_mkdir', finish=True)
  7557.                     return
  7558.  
  7559.             return
  7560.         except NotFoundError:
  7561.             pass
  7562.  
  7563.         if dir[0] == '/':
  7564.             parent_dir = dirname(dir)
  7565.             try:
  7566.                 self.cd(parent_dir)
  7567.             except NotFoundError:
  7568.                 self.mkdir(parent_dir, chmod_dirs)
  7569.                 self.cd(parent_dir)
  7570.  
  7571.         dir = canonicalize(dir, 'remote')
  7572.         progress = ProgressThread(self.printer, '\nCreating folder "%s"' % dir)
  7573.         try:
  7574.             self.proc.write('mkdir "%s"' % dir, self.remote_encoding)
  7575.             result = self.proc.read(encoding=self.remote_encoding)
  7576.         except UnicodeDecodeError:
  7577.             progress.stop('failure (Encoding error)')
  7578.             progress.join()
  7579.             raise
  7580.  
  7581.         if re.search('permission denied', result, re.I) is not None:
  7582.             progress.stop('failure (Permission denied)')
  7583.             progress.join()
  7584.             raise PermissionError('Permission denied')
  7585.         try:
  7586.             chmod_error = False
  7587.             if chmod_dirs:
  7588.                 self.chmod(dir, chmod_dirs, quiet=True)
  7589.         except PermissionError:
  7590.             chmod_error = True
  7591.  
  7592.         progress.stop('success')
  7593.         progress.join()
  7594.         if chmod_error:
  7595.             self.printer.write('\nChmoding folder "%s" to "%s" ... failure (Permission denied)' % (dir, chmod_dirs), key='sftp_mkdir', finish=True)
  7596.         return
  7597.  
  7598.     @keepaliveize
  7599.     def mv(self, names, quiet=False, **kwargs):
  7600.         old_filename = os.path.basename(names[0].rstrip('\\/'))
  7601.         new_filename = os.path.basename(names[1].rstrip('\\/'))
  7602.         dir = dirname(names[0])
  7603.         dir = canonicalize(dir, 'remote')
  7604.         try:
  7605.             self.cd(dir)
  7606.         except NotFoundError:
  7607.             if not quiet:
  7608.                 self.printer.write('\nChanging to folder "%s" ... failure (Folder not found)' % dir)
  7609.                 self.printer.error('Folder not found')
  7610.             return [False, 'Folder not found']
  7611.  
  7612.         if not quiet:
  7613.             progress = ProgressThread(self.printer, '\nRenaming "%s" to "%s"' % (names[0], names[1]))
  7614.         try:
  7615.             self.proc.write('rename "%s" "%s"' % (old_filename, new_filename), self.remote_encoding)
  7616.             result = self.proc.read(encoding=self.remote_encoding)
  7617.         except UnicodeDecodeError:
  7618.             if not quiet:
  7619.                 progress.stop('failure (Encoding error)')
  7620.                 progress.join()
  7621.             raise
  7622.  
  7623.         if re.search('permission denied', result, re.I) is not None:
  7624.             if not quiet:
  7625.                 progress.stop('failure (Permission denied)')
  7626.                 progress.join()
  7627.                 self.printer.error('Permission denied')
  7628.             return [False, 'Permission denied']
  7629.         elif re.search('(no such file or directory|cannot find the file specified)', result, re.I) is not None:
  7630.             message = '%s not found' % path_type(names[0], True)
  7631.             if not quiet:
  7632.                 progress.stop('failure (%s)' % message)
  7633.                 progress.join()
  7634.                 self.printer.error(message)
  7635.             return [False, message]
  7636.         elif re.search('failure', result, re.I) is not None:
  7637.             message = '%s already exists' % new_filename
  7638.             if not quiet:
  7639.                 progress.stop('failure (%s)' % message)
  7640.                 progress.join()
  7641.                 self.printer.error(message)
  7642.             return [False, message]
  7643.         else:
  7644.             if not quiet:
  7645.                 progress.stop('success')
  7646.                 progress.join()
  7647.             return [
  7648.              True, None]
  7649.  
  7650.     @keepaliveize
  7651.     def put(self, files, path_map, chmod_files=None, chmod_dirs=None, quiet=False, **kwargs):
  7652.         if not isinstance(files, list):
  7653.             files = [
  7654.              files]
  7655.         error = False
  7656.         single_file = len(files) == 1
  7657.         for file in files:
  7658.             remote_file = local_to_remote(file, path_map, self.remote_encoding)
  7659.             try:
  7660.                 dir_error, cont = self.handle_put_dirs(file, remote_file, chmod_dirs, single_file)
  7661.                 error = error or dir_error
  7662.                 if cont:
  7663.                     continue
  7664.             except OSError as e:
  7665.                 if not quiet:
  7666.                     self.printer.write('\nUploading "%s" to "%s" ... failure (%s)' % (file, remote_file, str(e)))
  7667.                 raise e
  7668.  
  7669.             if not quiet:
  7670.                 progress = ProgressThread(self.printer, '\nUploading "%s" to "%s"' % (file, remote_file))
  7671.             try:
  7672.                 basename = os.path.basename(file)
  7673.                 if os.name == 'nt':
  7674.                     local_encoding = locale.getpreferredencoding(do_setlocale=True)
  7675.                     remote_basename = str_cls(basename.encode(self.remote_encoding), local_encoding)
  7676.                     command = 'put -- "%s" "%s"'
  7677.                     if self.preserve_modification_times is True:
  7678.                         command = 'put -P -- "%s" "%s"'
  7679.                     self.proc.write(command % (basename, remote_basename), local_encoding)
  7680.                     result = self.proc.read(encoding=local_encoding)
  7681.                 else:
  7682.                     command = 'put -- "%s"'
  7683.                     if self.preserve_modification_times is True:
  7684.                         command = 'put -P -- "%s"'
  7685.                     self.proc.write(command % basename, self.remote_encoding)
  7686.                     result = self.proc.read(encoding=self.remote_encoding)
  7687.                 if re.search("Couldn't fsetstat", result, re.I) is not None:
  7688.                     raise PermissionError('Server does not support preserving modification times, please set preserve_modification_times to false or "download_only" in sftp-config.json')
  7689.                 if re.search('permission denied', result, re.I) is not None:
  7690.                     raise PermissionError('Permission denied')
  7691.                 try:
  7692.                     chmod_error = False
  7693.                     if chmod_files:
  7694.                         self.chmod(remote_file, chmod_files, quiet=True)
  7695.                 except PermissionError:
  7696.                     chmod_error = True
  7697.  
  7698.                 if not quiet:
  7699.                     progress.stop('success')
  7700.                     progress.join()
  7701.                 if chmod_error:
  7702.                     self.printer.write('\nChmoding folder "%s" to "%s" ... failure (Permission denied)' % (remote_file, chmod_files), key='sftp_put_chmod', finish=True)
  7703.             except PermissionError as e:
  7704.                 if not quiet:
  7705.                     progress.stop('failure (%s)' % str(e))
  7706.                     progress.join()
  7707.                 if single_file and not quiet:
  7708.                     self.printer.error(e)
  7709.                 error = True
  7710.                 continue
  7711.             except OSError as e:
  7712.                 if not quiet:
  7713.                     progress.stop('failure (%s)' % str(e))
  7714.                     progress.join()
  7715.                 raise e
  7716.             except UnicodeDecodeError:
  7717.                 if not quiet:
  7718.                     progress.stop('failure (Encoding error)')
  7719.                     progress.join()
  7720.                 raise
  7721.  
  7722.         result = None
  7723.         if error and not quiet and not single_file:
  7724.             string = 'One or more errors occured while uploading files'
  7725.             result = string
  7726.             self.printer.write('\n' + string)
  7727.             self.printer.error(string)
  7728.         return [
  7729.          not error, result]
  7730.  
  7731.     def pwd(self):
  7732.         if self.dir is None:
  7733.             self.proc.write('pwd')
  7734.             result = self.proc.read(encoding=self.remote_encoding)
  7735.             self.dir = re.sub('^[^/]+', '', result.split('\n')[-1])
  7736.             self.dir = canonicalize(self.dir, 'remote')
  7737.         return self.dir
  7738.  
  7739.     def lpwd(self):
  7740.         if self.local_dir is None:
  7741.             self.proc.write('lpwd')
  7742.             encoding = 'utf-8'
  7743.             if os.name == 'nt':
  7744.                 encoding = locale.getpreferredencoding(do_setlocale=True)
  7745.             result = self.proc.read(encoding=encoding)
  7746.             if os.name == 'nt':
  7747.                 self.local_dir = re.sub('^Current local directory is ', '', result.split('\n')[-1])
  7748.             else:
  7749.                 if re.search("Couldn't get local cwd", result, re.I) is not None:
  7750.                     self.proc.write('lcd "%s"' % os.path.expanduser('~'), encoding)
  7751.                     result = self.proc.read(encoding=encoding)
  7752.                     self.proc.write('lpwd')
  7753.                     result = self.proc.read(encoding=encoding)
  7754.                 self.local_dir = re.sub('^[^/]+', '', result.split('\n')[-1])
  7755.             self.local_dir = canonicalize(self.local_dir, 'local')
  7756.         return self.local_dir
  7757.  
  7758.     @keepaliveize
  7759.     def rm(self, remote_files, path_map, quiet=False, **kwargs):
  7760.         if not isinstance(remote_files, list):
  7761.             remote_files = [
  7762.              remote_files]
  7763.         error = False
  7764.         single_file = len(remote_files) == 1
  7765.         for remote_file in remote_files:
  7766.             file = remote_to_local(remote_file, path_map, self.remote_encoding)
  7767.             try:
  7768.                 dir_error, cont = self.handle_rm_dirs(file, remote_file, single_file)
  7769.                 error = error or dir_error
  7770.                 if cont:
  7771.                     continue
  7772.             except OSError as e:
  7773.                 if not quiet:
  7774.                     self.printer.write('\nDeleting "%s" ... failure (%s)' % (remote_file, str(e)))
  7775.                 raise e
  7776.  
  7777.             if not quiet:
  7778.                 progress = ProgressThread(self.printer, '\nDeleting "%s"' % remote_file)
  7779.             try:
  7780.                 if is_dir(remote_file):
  7781.                     command = 'rmdir'
  7782.                     rm_path = os.path.basename(remote_file.rstrip('/\\'))
  7783.                 else:
  7784.                     command = 'rm'
  7785.                     rm_path = os.path.basename(remote_file)
  7786.                 self.proc.write('%s "%s"' % (command, rm_path), self.remote_encoding)
  7787.                 result = self.proc.read(encoding=self.remote_encoding)
  7788.                 if re.search('permission denied', result, re.I) is not None:
  7789.                     raise PermissionError('Permission denied')
  7790.                 if re.search('(no such file or directory|cannot find the file specified)', result, re.I) is not None:
  7791.                     raise NotFoundError('File not found')
  7792.                 if not quiet:
  7793.                     progress.stop('success')
  7794.                     progress.join()
  7795.             except (PermissionError, NotFoundError) as e:
  7796.                 if not quiet:
  7797.                     progress.stop('failure (%s)' % str(e))
  7798.                     progress.join()
  7799.                 if single_file and not quiet:
  7800.                     self.printer.error(e)
  7801.                 error = True
  7802.                 continue
  7803.             except OSError as e:
  7804.                 if not quiet:
  7805.                     progress.stop('failure (%s)' % str(e))
  7806.                     progress.join()
  7807.                 raise e
  7808.             except UnicodeDecodeError:
  7809.                 if not quiet:
  7810.                     progress.stop('failure (Encoding error)')
  7811.                     progress.join()
  7812.                 raise
  7813.  
  7814.         result = None
  7815.         if error and not quiet and not single_file:
  7816.             string = 'One or more errors occured while removing files'
  7817.             result = string
  7818.             self.printer.write('\n' + string)
  7819.             self.printer.error(string)
  7820.         return [
  7821.          not error, result]
  7822. # okay decompiling /home/manager/.config/sublime-text-3/Packages/SFTP/sftp/sftp_transport.pyc
  7823. # uncompyle6 version 3.2.3
  7824. # Python bytecode 3.3 (3230)
  7825. # Decompiled from: Python 2.7.12 (default, Dec  4 2017, 14:50:18)
  7826. # [GCC 5.4.0 20160609]
  7827. # Embedded file name: /Users/wbond/Library/Application Support/Sublime Text 3/Packages/SFTP/sftp/threads.py
  7828. # Compiled at: 2015-11-05 00:19:26
  7829. # Size of source mod 2**32: 5597 bytes
  7830. import sublime, threading, sys
  7831. from .errors import encoding_error
  7832. from .times import timestamp_to_string
  7833. from .views import get_all_views
  7834.  
  7835. class HookedThread(threading.Thread):
  7836.  
  7837.     def __init__(self):
  7838.         run_old = self.run
  7839.  
  7840.         def run_with_except_hook(*args, **kw):
  7841.             try:
  7842.                 run_old(*args, **kw)
  7843.             except (KeyboardInterrupt, SystemExit):
  7844.                 raise
  7845.             except UnicodeDecodeError as e:
  7846.                 encoding_error(e)
  7847.             except LookupError as e:
  7848.                 if e.message == 'unknown encoding: idna':
  7849.  
  7850.                     def show_2pdf_error():
  7851.                         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.')
  7852.  
  7853.                     sublime.set_timeout(show_2pdf_error, 10)
  7854.                 else:
  7855.                     encoding_error(e)
  7856.             except:
  7857.                 sys.excepthook(*sys.exc_info())
  7858.  
  7859.         self.run = run_with_except_hook
  7860.         threading.Thread.__init__(self)
  7861.  
  7862.  
  7863. class SyncThread(object):
  7864.  
  7865.     def confirm(self, operations, on_confirm, on_reject=None, should_join=False):
  7866.         prompt = '\n\n Do you wish to perform the following operations?'
  7867.         for op in operations:
  7868.             prompt += '\n   ' + op
  7869.  
  7870.         prompt += '\n (Select via quick panel) '
  7871.         self.printer.write(prompt, key='sync_confirm')
  7872.         options = [['Yes', 'Perform the listed operations (see SFTP panel)'], ['No', 'Do nothing']]
  7873.  
  7874.         def handler(index):
  7875.             if index == -1 or index == 1:
  7876.                 self.printer.write('No\n', key='sync_confirm', finish=True)
  7877.                 if on_reject:
  7878.                     threading.Thread(target=on_reject).start()
  7879.                 return
  7880.             self.printer.write('Yes\n', key='sync_confirm', finish=True)
  7881.             SyncThreadConfirm(on_confirm, should_join, self.window_id).start()
  7882.  
  7883.         def show():
  7884.             self.printer.show(force=True)
  7885.             self.window.show_quick_panel(options, handler)
  7886.  
  7887.         sublime.set_timeout(show, 10)
  7888.  
  7889.     def make_time(self, time):
  7890.         if time:
  7891.             return timestamp_to_string(time, '%-I:%M%p %-m/%-d/%y').lower()
  7892.         else:
  7893.             return 'None'
  7894.  
  7895.     def strip(self, path, dir, type):
  7896.         new_path = path[len(dir):]
  7897.         if new_path == '':
  7898.             new_path = path
  7899.         return new_path
  7900.  
  7901.  
  7902. class SyncThreadConfirm(HookedThread):
  7903.  
  7904.     def __init__(self, callback, should_join, window_id):
  7905.         self.callback = callback
  7906.         self.should_join = should_join
  7907.         self.window_id = window_id
  7908.         super(SyncThreadConfirm, self).__init__()
  7909.  
  7910.     def run(self):
  7911.         if self.should_join:
  7912.             last_thread = ThreadTracker.get_last_added(self.window_id)
  7913.             ThreadTracker.add(self)
  7914.             if last_thread is not None:
  7915.                 last_thread.join()
  7916.         self.callback()
  7917.         return
  7918.  
  7919.  
  7920. class ThreadTracker(object):
  7921.     pending_threads = {}
  7922.     current_thread = {}
  7923.  
  7924.     @classmethod
  7925.     def add(cls, thread):
  7926.         cls.pending_threads[thread.window_id] = thread
  7927.  
  7928.     @classmethod
  7929.     def get_last_added(cls, window_id):
  7930.         return cls.pending_threads.get(window_id)
  7931.  
  7932.     @classmethod
  7933.     def set_current(cls, window_id, thread):
  7934.         cls.current_thread[window_id] = thread
  7935.  
  7936.     @classmethod
  7937.     def get_current(cls, window_id):
  7938.         return cls.current_thread.get(window_id)
  7939.  
  7940.  
  7941. def unset_current_thread(fn):
  7942.  
  7943.     def handler(self, *args, **kwargs):
  7944.         result = fn(self, *args, **kwargs)
  7945.         ThreadTracker.set_current(self.window_id, None)
  7946.         return result
  7947.  
  7948.     return handler
  7949.  
  7950.  
  7951. class ThreadActivity(object):
  7952.  
  7953.     def __init__(self, thread, printer, message):
  7954.         self.thread = thread
  7955.         self.printer = printer
  7956.         self.message = message
  7957.         self.addend = 1
  7958.         self.size = 8
  7959.         self.last_wrote = None
  7960.         sublime.set_timeout(self.set_window, 1)
  7961.         return
  7962.  
  7963.     def set_window(self):
  7964.         for window in sublime.windows():
  7965.             if window.id() == self.thread.window_id:
  7966.                 self.window = window
  7967.                 continue
  7968.  
  7969.         sublime.set_timeout(lambda : self.run(0), 100)
  7970.  
  7971.     def run(self, i):
  7972.         if not self.thread.is_alive():
  7973.             self.write('')
  7974.             return
  7975.         if self.printer.visible:
  7976.             if self.last_wrote != '':
  7977.                 self.write('')
  7978.             sublime.set_timeout(lambda : self.run(i), 100)
  7979.             return
  7980.         before = i % self.size
  7981.         after = self.size - 1 - before
  7982.         if not self.write('%s [%s=%s]' % (self.message, ' ' * before, ' ' * after)):
  7983.             return
  7984.         if not after:
  7985.             self.addend = -1
  7986.         if not before:
  7987.             self.addend = 1
  7988.         i += self.addend
  7989.         sublime.set_timeout(lambda : self.run(i), 100)
  7990.  
  7991.     def write(self, value):
  7992.         if not hasattr(self, 'window'):
  7993.             return False
  7994.         self.last_wrote = value
  7995.         if value == '':
  7996.             [view.erase_status('sftp') for view in get_all_views(self.window)]
  7997.         else:
  7998.             view = self.window.active_view()
  7999.         if view:
  8000.             view.set_status('sftp', value)
  8001.         return True
  8002. # okay decompiling /home/manager/.config/sublime-text-3/Packages/SFTP/sftp/threads.pyc
  8003. # uncompyle6 version 3.2.3
  8004. # Python bytecode 3.3 (3230)
  8005. # Decompiled from: Python 2.7.12 (default, Dec  4 2017, 14:50:18)
  8006. # [GCC 5.4.0 20160609]
  8007. # Embedded file name: /Users/wbond/Library/Application Support/Sublime Text 3/Packages/SFTP/sftp/times.py
  8008. # Compiled at: 2015-11-05 00:19:28
  8009. # Size of source mod 2**32: 1330 bytes
  8010. import datetime, re, math
  8011.  
  8012. def timestamp_to_string(timestamp, format):
  8013.     date = datetime.datetime.fromtimestamp(timestamp)
  8014.     return good_strftime(date, format)
  8015.  
  8016.  
  8017. def good_strftime(timestamp, format):
  8018.     output = ''
  8019.     for part in filter(len, re.split('(%-\\w)', format)):
  8020.         if part[0:2] == '%-':
  8021.             output += timestamp.strftime('%' + part[2:]).lstrip('0')
  8022.         else:
  8023.             output += timestamp.strftime(part)
  8024.  
  8025.     return output
  8026.  
  8027.  
  8028. def time_diff(first_timestamp, second_timestamp):
  8029.     diff = first_timestamp - second_timestamp
  8030.     if math.floor(first_timestamp / 60) == math.floor(second_timestamp / 60):
  8031.         return 'same age'
  8032.     break_points = [
  8033.      [
  8034.       3600, 60, 'minute', 'minutes'],
  8035.      [
  8036.       86400, 3600, 'hour', 'hours'],
  8037.      [
  8038.       604800, 86400, 'day', 'days'],
  8039.      [
  8040.       2592000, 604800, 'week', 'weeks'],
  8041.      [
  8042.       31536000, 2592000, 'month', 'months'],
  8043.      [
  8044.       2147483647, 31536000, 'year', 'years']]
  8045.     for unit_info in break_points:
  8046.         if abs(diff) >= unit_info[0]:
  8047.             continue
  8048.         unit_diff = round(float(abs(diff)) / float(unit_info[1]))
  8049.         units = unit_info[2] if unit_diff == 1 else unit_info[3]
  8050.         break
  8051.  
  8052.     suffix = ' newer' if first_timestamp > second_timestamp else ' older'
  8053.     return str(int(unit_diff)) + ' ' + units + suffix
  8054. # okay decompiling /home/manager/.config/sublime-text-3/Packages/SFTP/sftp/times.pyc
  8055. # uncompyle6 version 3.2.3
  8056. # Python bytecode 3.3 (3230)
  8057. # Decompiled from: Python 2.7.12 (default, Dec  4 2017, 14:50:18)
  8058. # [GCC 5.4.0 20160609]
  8059. # Embedded file name: /Users/wbond/Library/Application Support/Sublime Text 3/Packages/SFTP/sftp/vcs.py
  8060. # Compiled at: 2015-11-05 00:19:30
  8061. # Size of source mod 2**32: 4628 bytes
  8062. import sublime, os, re
  8063. from .proc import find_binary, NonInteractiveProcess
  8064. from .errors import NotFoundError
  8065. from .paths import dirname
  8066.  
  8067. class VCS(object):
  8068.  
  8069.     def add_file(self, files, line):
  8070.         line = os.path.join(self.root_dir, line)
  8071.         if os.path.isdir(line):
  8072.             for dir, subdirs, subfiles in os.walk(line):
  8073.                 files.append(dir)
  8074.                 for subfile in subfiles:
  8075.                     files.append(os.path.join(self.root_dir, dir, subfile))
  8076.  
  8077.         else:
  8078.             files.append(line)
  8079.         return files
  8080.  
  8081.     def exec_status(self, binary, type, extra_arg=''):
  8082.         if not self.binary_path:
  8083.             self.binary_path = find_binary(binary)
  8084.         if not self.binary_path:
  8085.             raise NotFoundError('The %s binary could not be found on your system. Please edit "%s" and set "%s_binary_path" or install %s.' % (
  8086.              binary,
  8087.              os.path.join(sublime.packages_path(), 'User', 'SFTP.sublime-settings'),
  8088.              type,
  8089.              type))
  8090.         try:
  8091.             args = [
  8092.              self.binary_path, 'status']
  8093.             if extra_arg:
  8094.                 args.append(extra_arg)
  8095.             args.append(self.root_dir)
  8096.             proc = NonInteractiveProcess(self.root_dir, *args)
  8097.             result = proc.run().split('\n')
  8098.         except OSError:
  8099.             raise NotFoundError('The binary "%s" does not exist or is not executable. Please check your settings in "%s".' % (
  8100.              self.binary_path,
  8101.              os.path.join(sublime.packages_path(), 'User', 'SFTP.sublime-settings')))
  8102.  
  8103.         return result
  8104.  
  8105.     def init(self, path, binary_path, type):
  8106.         root_dir = None
  8107.         last_dir = None
  8108.         cur_dir = dirname(path)
  8109.         while cur_dir != last_dir:
  8110.             if os.path.exists(os.path.join(cur_dir, '.' + type)):
  8111.                 root_dir = cur_dir
  8112.                 break
  8113.             last_dir = cur_dir
  8114.             cur_dir = dirname(cur_dir)
  8115.  
  8116.         if root_dir is None:
  8117.             raise NotFoundError('Unable to find .' + type + ' folder')
  8118.         self.root_dir = root_dir
  8119.         self.binary_path = binary_path
  8120.         return
  8121.  
  8122.  
  8123. class SVN(VCS):
  8124.  
  8125.     def __init__(self, path, binary_path):
  8126.         root_dir = None
  8127.         last_dir = None
  8128.         orig_dir = dirname(path)
  8129.         cur_dir = orig_dir
  8130.         while cur_dir != last_dir:
  8131.             if root_dir is not None and not os.path.exists(os.path.join(cur_dir, '.svn')):
  8132.                 break
  8133.             if os.path.exists(os.path.join(cur_dir, '.svn')):
  8134.                 root_dir = cur_dir
  8135.             last_dir = cur_dir
  8136.             cur_dir = dirname(cur_dir)
  8137.  
  8138.         if root_dir is None:
  8139.             raise NotFoundError('Unable to find .svn folder')
  8140.         self.root_dir = root_dir
  8141.         self.binary_path = binary_path
  8142.         return
  8143.  
  8144.     def list_changed_files(self):
  8145.         svn_binary = 'svn.exe' if os.name == 'nt' else 'svn'
  8146.         result = self.exec_status(svn_binary, 'svn')
  8147.         files = []
  8148.         for line in result:
  8149.             if not not len(line):
  8150.                 if line[0] not in (u'A', u'M', u'R', u'C', u'?'):
  8151.                     continue
  8152.                 line = re.sub('^[^ ]\\s+(\\+\\s+)?', '', line)
  8153.                 files = self.add_file(files, line)
  8154.  
  8155.         return files
  8156.  
  8157.  
  8158. class Git(VCS):
  8159.  
  8160.     def __init__(self, path, binary_path):
  8161.         self.init(path, binary_path, 'git')
  8162.  
  8163.     def list_changed_files(self):
  8164.         git_binary = 'git.exe' if os.name == 'nt' else 'git'
  8165.         result = self.exec_status(git_binary, 'git', '--porcelain')
  8166.         files = []
  8167.         for line in result:
  8168.             if re.search('^([AMRC][ M]|\\?| M|UA|UU)', line) is None:
  8169.                 continue
  8170.             if line[0] == 'R':
  8171.                 line = re.sub('^.*? -> ', '', line[3:])
  8172.             else:
  8173.                 line = line[3:]
  8174.             files = self.add_file(files, line)
  8175.  
  8176.         return files
  8177.  
  8178.  
  8179. class Hg(VCS):
  8180.  
  8181.     def __init__(self, path, binary_path):
  8182.         self.init(path, binary_path, 'hg')
  8183.  
  8184.     def list_changed_files(self):
  8185.         hg_binary = 'hg.exe' if os.name == 'nt' else 'hg'
  8186.         result = self.exec_status(hg_binary, 'hg')
  8187.         files = []
  8188.         for line in result:
  8189.             if not not len(line):
  8190.                 if line[0] not in (u'A', u'M', u'R', u'?'):
  8191.                     continue
  8192.                 line = line[2:]
  8193.                 files = self.add_file(files, line)
  8194.  
  8195.         return files
  8196. # okay decompiling /home/manager/.config/sublime-text-3/Packages/SFTP/sftp/vcs.pyc
  8197. # uncompyle6 version 3.2.3
  8198. # Python bytecode 3.3 (3230)
  8199. # Decompiled from: Python 2.7.12 (default, Dec  4 2017, 14:50:18)
  8200. # [GCC 5.4.0 20160609]
  8201. # Embedded file name: /Users/wbond/Library/Application Support/Sublime Text 3/Packages/SFTP/sftp/views.py
  8202. # Compiled at: 2015-11-05 00:19:32
  8203. # Size of source mod 2**32: 322 bytes
  8204.  
  8205.  
  8206. def get_view_by_group_index(window, group, index):
  8207.     return window.views_in_group(group)[index]
  8208.  
  8209.  
  8210. def get_all_views(window):
  8211.     views = window.views()
  8212.     active_view = window.active_view()
  8213.     if active_view and active_view.id() not in [view.id() for view in views]:
  8214.         views.append(active_view)
  8215.     return views
  8216. # okay decompiling /home/manager/.config/sublime-text-3/Packages/SFTP/sftp/views.pyc
  8217. # decompiled 19 files: 19 okay, 0 failed
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement